ARB
arb_msg.cxx
Go to the documentation of this file.
1 // ================================================================ //
2 // //
3 // File : arb_msg.cxx //
4 // Purpose : //
5 // //
6 // Coded by Ralf Westram (coder@reallysoft.de) in November 2010 //
7 // Institute of Microbiology (Technical University Munich) //
8 // http://www.arb-home.de/ //
9 // //
10 // ================================================================ //
11 
12 #include <arb_msg_fwd.h>
13 #include <arb_string.h>
14 #include <arb_backtrace.h>
15 #include <smartptr.h>
16 #include <arb_handlers.h>
17 #include <arb_defs.h>
18 #include "arb_strbuf.h"
19 
20 // AISC_MKPT_PROMOTE:#ifndef _GLIBCXX_CSTDLIB
21 // AISC_MKPT_PROMOTE:#include <cstdlib>
22 // AISC_MKPT_PROMOTE:#endif
23 // AISC_MKPT_PROMOTE:#ifndef ARB_CORE_H
24 // AISC_MKPT_PROMOTE:#include "arb_core.h"
25 // AISC_MKPT_PROMOTE:#endif
26 // AISC_MKPT_PROMOTE:#ifndef ARB_ASSERT_H
27 // AISC_MKPT_PROMOTE:#include "arb_assert.h"
28 // AISC_MKPT_PROMOTE:#endif
29 // AISC_MKPT_PROMOTE:
30 // AISC_MKPT_PROMOTE:// return error and ensure none is exported
31 // AISC_MKPT_PROMOTE:#define RETURN_ERROR(err) arb_assert(!GB_have_error()); return (err)
32 // AISC_MKPT_PROMOTE:
33 
34 #if defined(DEBUG)
35 #if defined(DEVEL_RALF)
36 // #define TRACE_BUFFER_USAGE
37 #endif // DEBUG
38 #endif // DEVEL_RALF
39 
40 #define GLOBAL_STRING_BUFFERS 4
41 
42 static size_t last_global_string_size = 0;
43 #define GBS_GLOBAL_STRING_SIZE 64000
44 
45 // --------------------------------------------------------------------------------
46 
47 #ifdef LINUX
48 # define HAVE_VSNPRINTF
49 #endif
50 
51 #ifdef HAVE_VSNPRINTF
52 # define PRINT2BUFFER(buffer, bufsize, templat, parg) vsnprintf(buffer, bufsize, templat, parg)
53 #else
54 # define PRINT2BUFFER(buffer, bufsize, templat, parg) vsprintf(buffer, templat, parg)
55 #endif
56 
57 #define PRINT2BUFFER_CHECKED(printed, buffer, bufsize, templat, parg) \
58  (printed) = PRINT2BUFFER(buffer, bufsize, templat, parg); \
59  if ((printed) < 0 || (size_t)(printed) >= (bufsize)) { \
60  GBK_terminatef("Internal buffer overflow (size=%zu, used=%i)\n", \
61  (bufsize), (printed)); \
62  }
63 
64 // --------------------------------------------------------------------------------
65 
67  char buffer[GLOBAL_STRING_BUFFERS][GBS_GLOBAL_STRING_SIZE+2]; // several buffers - used alternately
68  int idx;
69  char lifetime[GLOBAL_STRING_BUFFERS];
70  char nextIdx[GLOBAL_STRING_BUFFERS];
71 
72 public:
74  : idx(0)
75  {
76  for (int i = 0; i<GLOBAL_STRING_BUFFERS; ++i) {
77  nextIdx[i] = 0;
78  lifetime[i] = 0;
79  }
80  }
81 
82  __ATTR__VFORMAT_MEMBER(1) const char *vstrf(const char *templat, va_list parg, int allow_reuse);
83 };
84 
86 
87 const char *GlobalStringBuffers::vstrf(const char *templat, va_list parg, int allow_reuse) {
88  int my_idx;
89  int psize;
90 
91  if (nextIdx[0] == 0) { // initialize nextIdx
92  for (my_idx = 0; my_idx<GLOBAL_STRING_BUFFERS; my_idx++) {
93  nextIdx[my_idx] = (my_idx+1)%GLOBAL_STRING_BUFFERS;
94  }
95  }
96 
97  if (allow_reuse == -1) { // called from GBS_reuse_buffer
98  // buffer to reuse is passed in 'templat'
99 
100  for (my_idx = 0; my_idx<GLOBAL_STRING_BUFFERS; my_idx++) {
101  if (buffer[my_idx] == templat) {
102  lifetime[my_idx] = 0;
103 #if defined(TRACE_BUFFER_USAGE)
104  printf("Reusing buffer #%i\n", my_idx);
105 #endif // TRACE_BUFFER_USAGE
106  if (nextIdx[my_idx] == idx) idx = my_idx;
107  return NULp;
108  }
109 #if defined(TRACE_BUFFER_USAGE)
110  else {
111  printf("(buffer to reuse is not buffer #%i (%p))\n", my_idx, buffer[my_idx]);
112  }
113 #endif // TRACE_BUFFER_USAGE
114  }
115  for (my_idx = 0; my_idx<GLOBAL_STRING_BUFFERS; my_idx++) {
116  printf("buffer[%i]=%p\n", my_idx, buffer[my_idx]);
117  }
118  arb_assert(0); // GBS_reuse_buffer called with illegal buffer
119  return NULp;
120  }
121 
122  if (lifetime[idx] == 0) {
123  my_idx = idx;
124  }
125  else {
126  for (my_idx = nextIdx[idx]; lifetime[my_idx]>0; my_idx = nextIdx[my_idx]) {
127 #if defined(TRACE_BUFFER_USAGE)
128  printf("decreasing lifetime[%i] (%i->%i)\n", my_idx, lifetime[my_idx], lifetime[my_idx]-1);
129 #endif // TRACE_BUFFER_USAGE
130  lifetime[my_idx]--;
131  }
132  }
133 
134  PRINT2BUFFER_CHECKED(psize, buffer[my_idx], (size_t)GBS_GLOBAL_STRING_SIZE, templat, parg);
135 
136 #if defined(TRACE_BUFFER_USAGE)
137  printf("Printed into global buffer #%i ('%s')\n", my_idx, buffer[my_idx]);
138 #endif // TRACE_BUFFER_USAGE
139 
140  last_global_string_size = psize;
141 
142  if (!allow_reuse) {
143  idx = my_idx;
144  lifetime[idx] = 1;
145  }
146 #if defined(TRACE_BUFFER_USAGE)
147  else {
148  printf("Allow reuse of buffer #%i\n", my_idx);
149  }
150 #endif // TRACE_BUFFER_USAGE
151 
152  return buffer[my_idx];
153 }
154 
158  return stored;
159 }
160 
162  globBuf = *saved;
163  delete saved;
164 }
165 
166 const char *GBS_vglobal_string(const char *templat, va_list parg) {
167  // goes to header: __ATTR__VFORMAT(1)
168  return globBuf.vstrf(templat, parg, 0);
169 }
170 
171 char *GBS_vglobal_string_copy(const char *templat, va_list parg) {
172  // goes to header: __ATTR__VFORMAT(1)
173  const char *gstr = globBuf.vstrf(templat, parg, 1);
175 }
176 
177 const char *GBS_global_string_to_buffer(char *buffer, size_t bufsize, const char *templat, ...) {
178  // goes to header: __ATTR__FORMAT(3)
179 
180 #if defined(WARN_TODO)
181 #warning search for '\b(sprintf)\b\s*\(' and replace by GBS_global_string_to_buffer
182 #endif
183 
184  va_list parg;
185  int psize;
186 
187  arb_assert(buffer);
188  va_start(parg, templat);
189  PRINT2BUFFER_CHECKED(psize, buffer, bufsize, templat, parg);
190  va_end(parg);
191 
192  return buffer;
193 }
194 
195 char *GBS_global_string_copy(const char *templat, ...) {
196  // goes to header: __ATTR__FORMAT(1)
197  va_list parg;
198  va_start(parg, templat);
199  char *result = GBS_vglobal_string_copy(templat, parg);
200  va_end(parg);
201  return result;
202 }
203 
204 const char *GBS_global_string(const char *templat, ...) {
205  // goes to header: __ATTR__FORMAT(1)
206  va_list parg;
207  va_start(parg, templat);
208  const char *result = globBuf.vstrf(templat, parg, 0);
209  va_end(parg);
210  return result;
211 }
212 
213 const char *GBS_static_string(const char *str) {
214  return GBS_global_string("%s", str);
215 }
216 
217 GB_ERROR GBK_assert_msg(const char *assertion, const char *file, int linenr) {
218 #define BUFSIZE 1000
219  static char *buffer = NULp;
220  const char *result = NULp;
221  int old_size = last_global_string_size;
222 
223  if (!buffer) ARB_alloc(buffer, BUFSIZE);
224  result = GBS_global_string_to_buffer(buffer, BUFSIZE, "assertion '%s' failed in %s #%i", assertion, file, linenr);
225 
226  last_global_string_size = old_size;
227 
228  return result;
229 #undef BUFSIZE
230 }
231 
232 // -------------------------
233 // Error "handling"
234 
235 
236 #if defined(WARN_TODO)
237 #warning redesign GB_export_error et al
238 /* To clearly distinguish between the two ways of error handling
239  * (which are: return GB_ERROR
240  * and: export the error)
241  *
242  * GB_export_error() shall only export, not return the error message.
243  * if only used for formatting GBS_global_string shall be used
244  * (most cases where GB_export_errorf is used are candidates for this.
245  * GB_export_error was generally misused for this, before
246  * GBS_global_string was added!)
247  *
248  * GB_export_IO_error() shall not export and be renamed into GB_IO_error()
249  *
250  * GB_export_error() shall fail if there is already an exported error
251  * (maybe always remember a stack trace of last error export (try whether copy of backtrace-array works))
252  *
253  * use GB_get_error() to import AND clear the error
254  */
255 #endif
256 
257 static char *GB_error_buffer = NULp;
258 
259 GB_ERROR GB_export_error(const char *error) { // just a temp hack around format-warnings
260  arb_assert(error);
261  return GB_export_errorf("%s", error);
262 }
263 
264 GB_ERROR GB_export_errorf(const char *templat, ...) {
265  /* goes to header:
266  * __ATTR__FORMAT(1)
267  * __ATTR__DEPRECATED_LATER("use GB_export_error(GBS_global_string(...))")
268  * because it's misused (where GBS_global_string should be used)
269  * old functionality will remain available via 'GB_export_error(GBS_global_string(...))'
270  */
271 
272  char buffer[GBS_GLOBAL_STRING_SIZE];
273  char *p = buffer;
274  va_list parg;
275 
276 #if defined(WARN_TODO)
277 #warning dont prepend ARB ERROR here
278 #endif
279 
280  p += sprintf(buffer, "ARB ERROR: ");
281  va_start(parg, templat);
282 
283  vsprintf(p, templat, parg);
284 
285  freedup(GB_error_buffer, buffer);
286  return GB_error_buffer;
287 }
288 
289 #if defined(WARN_TODO)
290 #warning replace GB_export_IO_error() by GB_IO_error() and then export it if really needed
291 #endif
292 
293 GB_ERROR GB_IO_error(const char *action, const char *filename) {
300  GB_ERROR io_error;
301  if (errno) {
302  io_error = strerror(errno);
303  }
304  else {
305  arb_assert(0); // unhandled error (which is NOT an IO-Error)
306  io_error =
307  "Some unhandled error occurred, but it was not an IO-Error. "
308  "Please send detailed information about how the error occurred to devel@arb-home.de "
309  "or ignore this error (if possible).";
310  }
311 
312  GB_ERROR error;
313  if (action) {
314  if (filename) error = GBS_global_string("While %s '%s': %s", action, filename, io_error);
315  else error = GBS_global_string("While %s <unknown file>: %s", action, io_error);
316  }
317  else {
318  if (filename) error = GBS_global_string("Concerning '%s': %s", filename, io_error);
319  else error = io_error;
320  }
321 
322  return error;
323 }
324 
325 GB_ERROR GB_export_IO_error(const char *action, const char *filename) {
326  // goes to header: __ATTR__DEPRECATED_TODO("use GB_export_error(GB_IO_error(...))")
327  return GB_export_error(GB_IO_error(action, filename));
328 }
329 
330 #if defined(WARN_TODO)
331 #warning reactivate deprecations below
332 #endif
333 
334 
336  // goes to header: __ATTR__DEPRECATED_TODO("will be removed completely")
337  if (GB_error_buffer) {
338  fflush(stdout);
339  fprintf(stderr, "%s\n", GB_error_buffer);
340  }
341  return GB_error_buffer;
342 }
343 
345  // goes to header: __ATTR__DEPRECATED_TODO("consider using either GB_await_error() or GB_incur_error()")
346  return GB_error_buffer;
347 }
348 
350  return GB_error_buffer;
351 }
352 
354  if (GB_error_buffer) {
355  static SmartCharPtr err;
356  err = GB_error_buffer;
357  GB_error_buffer = NULp;
358  return &*err;
359  }
360  arb_assert(0); // please correct error handling
361 
362  return "Program logic error: Something went wrong, but reason is unknown";
363 }
364 
365 void GB_clear_error() { // clears the error buffer
366  freenull(GB_error_buffer);
367 }
368 
369 // AISC_MKPT_PROMOTE:inline GB_ERROR GB_incur_error() {
370 // AISC_MKPT_PROMOTE: /*! Take over responsibility for any potential (exported) error.
371 // AISC_MKPT_PROMOTE: * @return NULp if no error was exported; the error otherwise
372 // AISC_MKPT_PROMOTE: * Postcondition: no error is exported
373 // AISC_MKPT_PROMOTE: */
374 // AISC_MKPT_PROMOTE: return GB_have_error() ? GB_await_error() : NULp;
375 // AISC_MKPT_PROMOTE:}
376 // AISC_MKPT_PROMOTE:inline GB_ERROR GB_incur_error_if(bool error_may_occur) {
377 // AISC_MKPT_PROMOTE: /*! similar to GB_incur_error.
378 // AISC_MKPT_PROMOTE: * Additionally asserts no error may occur if 'error_may_occur' is false!
379 // AISC_MKPT_PROMOTE: */
380 // AISC_MKPT_PROMOTE: arb_assert(implicated(!error_may_occur, !GB_have_error()));
381 // AISC_MKPT_PROMOTE: return error_may_occur ? GB_incur_error() : NULp;
382 // AISC_MKPT_PROMOTE:}
383 
384 #if defined(WARN_TODO)
385 #warning search for 'GBS_global_string.*error' and replace with GB_failedTo_error or GB_append_exportedError
386 #endif
387 GB_ERROR GB_failedTo_error(const char *do_something, const char *special, GB_ERROR error) {
388  if (error) {
389  if (special) {
390  error = GBS_global_string("Failed to %s '%s'.\n(Reason: %s)", do_something, special, error);
391  }
392  else {
393  error = GBS_global_string("Failed to %s.\n(Reason: %s)", do_something, error);
394  }
395  }
396  return error;
397 }
398 
400  // If an error has been exported, it gets appended as reason to given 'error'.
401  // If error is NULp, the exported error is returned (if any)
402  //
403  // This is e.g. useful if you search for SOMETHING in the DB w/o success (i.e. get NULp as result).
404  // In that case you can't be sure, whether SOMETHING just does not exist or whether it was not
405  // found because some other error occurred.
406 
407  if (GB_have_error()) {
408  if (error) return GBS_global_string("%s (Reason: %s)", error, GB_await_error());
409  return GB_await_error();
410  }
411  return error;
412 }
413 
414 // ---------------------
415 // Backtracing
416 
417 class BackTraceInfo *GBK_get_backtrace(size_t skipFramesAtBottom) { // only used ifdef TRACE_ALLOCS
418  return new BackTraceInfo(skipFramesAtBottom);
419 }
420 void GBK_dump_former_backtrace(class BackTraceInfo *trace, FILE *out, const char *message) { // only used ifdef TRACE_ALLOCS
421  demangle_backtrace(*trace, out, message);
422 }
423 
424 void GBK_free_backtrace(class BackTraceInfo *trace) { // only used ifdef TRACE_ALLOCS
425  delete trace;
426 }
427 
428 void GBK_dump_backtrace(FILE *out, const char *message) {
429  demangle_backtrace(BackTraceInfo(1), out ? out : stderr, message);
430 }
431 
432 // -------------------------------------------
433 // Error/notification functions
434 
435 void GB_internal_error(const char *message) {
436  /* Use GB_internal_error, when something goes badly wrong
437  * but you want to give the user a chance to save his database
438  *
439  * Note: it's NOT recommended to use this function!
440  */
441 
442  char *full_message = GBS_global_string_copy("Internal ARB Error: %s", message);
443  active_arb_handlers->show_error(full_message);
444  active_arb_handlers->show_error("ARB is most likely unstable now (due to this error).\n"
445  "If you've made changes to the database, consider to save it using a different name.\n"
446  "Try to fix the cause of the error and restart ARB.");
447 
448 #ifdef ASSERTION_USED
449  fputs(full_message, stderr);
450  arb_assert(0); // internal errors shall not happen, go fix it
451 #else
452  GBK_dump_backtrace(stderr, full_message);
453 #endif
454 
455  free(full_message);
456 }
457 
458 void GB_internal_errorf(const char *templat, ...) {
459  // goes to header: __ATTR__FORMAT(1)
461 }
462 
463 void GBK_terminate(const char *error) { // goes to header __ATTR__NORETURN
464  /* GBK_terminate is the emergency exit!
465  * only used if no other way to recover
466  */
467 
468  fprintf(stderr, "Error: '%s'\n", error);
469  fputs("Can't continue - terminating..\n", stderr);
470  GBK_dump_backtrace(stderr, "GBK_terminate (reason above) ");
471 
472  fflush(stderr);
473  ARB_SIGSEGV(0); // GBK_terminate shall not be called, fix reason for call (this will crash in RELEASE version)
474  exit(ARB_CRASH_CODE(0)); // should not be reached..just ensure ARB really terminates if somebody changes ARB_SIGSEGV
475 }
476 
477 void GBK_terminatef(const char *templat, ...) {
478  // goes to header: __ATTR__FORMAT(1) __ATTR__NORETURN
480 }
481 
482 // AISC_MKPT_PROMOTE:inline void GBK_terminate_on_error(const char *error) { if (error) GBK_terminatef("Fatal error: %s", error); }
483 
484 void GB_warning(const char *message) {
485  /* If program uses GUI, the message is printed via aw_message, otherwise it goes to stdout
486  * see also : GB_information
487  */
489 }
490 void GB_warningf(const char *templat, ...) {
491  // goes to header: __ATTR__FORMAT(1)
492  FORWARD_FORMATTED(GB_warning, templat);
493 }
494 
495 void GB_information(const char *message) {
496  /* this message is always printed to stdout (regardless whether program uses GUI or not)
497  * (if it is not redirected using ARB_redirect_handlers_to)
498  * see also : GB_warning
499  */
501 }
502 void GB_informationf(const char *templat, ...) {
503  // goes to header: __ATTR__FORMAT(1)
505 }
506 
507 
508 #pragma GCC diagnostic ignored "-Wmissing-format-attribute"
509 
510 void GBS_reuse_buffer(const char *global_buffer) {
511  // If you've just shortely used a buffer, you can put it back here
512  va_list empty;
513  globBuf.vstrf(global_buffer, empty, -1); // omg hax
514 }
515 
516 #if defined(WARN_TODO)
517 #warning search for '\b(system)\b\s*\(' and use GBK_system instead
518 #endif
519 GB_ERROR GBK_system(const char *system_command) {
520  // goes to header: __ATTR__USERESULT
521  fflush(stdout);
522  fprintf(stderr, "[Action: `%s`]\n", system_command); fflush(stderr);
523 
524  int res = system(system_command);
525 
526  fflush(stdout);
527  fflush(stderr);
528 
529  GB_ERROR error = NULp;
530  if (res) {
531  if (res == -1) {
532  error = GB_IO_error("forking", system_command);
533  error = GBS_global_string("System call failed (Reason: %s)", error);
534  }
535  else {
536  error = GBS_global_string("System call failed (result=%i)", res);
537  }
538 
539  error = GBS_global_string("%s\n"
540  "System call was `%s`%s",
541  error, system_command,
542  res == -1 ? "" : "\n(Note: console window may contain additional information)");
543  }
544  return error;
545 }
546 
547 char *GBK_singlequote(const char *arg) {
551  if (!arg[0]) return ARB_strdup("''");
552 
553  GBS_strstruct out(500);
554  const char *existing_quote = strchr(arg, '\'');
555 
556  while (existing_quote) {
557  if (existing_quote>arg) {
558  out.put('\'');
559  out.ncat(arg, existing_quote-arg);
560  out.put('\'');
561  }
562  out.put('\\');
563  out.put('\'');
564  arg = existing_quote+1;
565  existing_quote = strchr(arg, '\'');
566  }
567 
568  if (arg[0]) {
569  out.put('\'');
570  out.cat(arg);
571  out.put('\'');
572  }
573  return out.release();
574 }
575 
576 char *GBK_doublequote(const char *arg) {
581  const char *charsToEscape = "\"\\";
582  const char *escape = arg+strcspn(arg, charsToEscape);
583  GBS_strstruct out(500);
584 
585  out.put('"');
586  while (escape[0]) {
587  out.ncat(arg, escape-arg);
588  out.put('\\');
589  out.put(escape[0]);
590  arg = escape+1;
591  escape = arg+strcspn(arg, charsToEscape);
592  }
593  out.cat(arg);
594  out.put('"');
595  return out.release();
596 }
597 
598 // --------------------------------------------------------------------------------
599 
600 #ifdef UNIT_TESTS
601 #ifndef TEST_UNIT_H
602 #include <test_unit.h>
603 #endif
604 
605 #include "FileContent.h"
606 #include <unistd.h>
607 
608 #define TEST_EXPECT_SQUOTE(plain,squote_expected) do { \
609  char *plain_squoted = GBK_singlequote(plain); \
610  TEST_EXPECT_EQUAL(plain_squoted, squote_expected); \
611  free(plain_squoted); \
612  } while(0)
613 
614 #define TEST_EXPECT_DQUOTE(plain,dquote_expected) do { \
615  char *plain_dquoted = GBK_doublequote(plain); \
616  TEST_EXPECT_EQUAL(plain_dquoted, dquote_expected); \
617  free(plain_dquoted); \
618  } while(0)
619 
620 #define TEST_EXPECT_ECHOED_EQUALS(echoarg,expected_echo) do { \
621  char *cmd = GBS_global_string_copy("echo %s > %s", echoarg, tmpfile); \
622  TEST_EXPECT_NO_ERROR(GBK_system(cmd)); \
623  FileContent out(tmpfile); \
624  TEST_EXPECT_NO_ERROR(out.has_error()); \
625  TEST_EXPECT_EQUAL(out.lines()[0], expected_echo); \
626  free(cmd); \
627  } while(0)
628 
629 #define TEST_EXPECT_SQUOTE_IDENTITY(plain) do { \
630  char *plain_squoted = GBK_singlequote(plain); \
631  TEST_EXPECT_ECHOED_EQUALS(plain_squoted,plain); \
632  free(plain_squoted); \
633  } while(0)
634 
635 #define TEST_EXPECT_DQUOTE_IDENTITY(plain) do { \
636  char *plain_dquoted = GBK_doublequote(plain); \
637  TEST_EXPECT_ECHOED_EQUALS(plain_dquoted,plain); \
638  free(plain_dquoted); \
639  } while(0)
640 
641 void TEST_quoting() {
642  const char *tmpfile = "quoted.output";
643 
644  struct quoting {
645  const char *plain;
646  const char *squoted;
647  const char *dquoted;
648  } args[] = {
649  { "", "''", "\"\"" }, // empty
650  { " ", "' '", "\" \"" }, // a space
651  { "unquoted", "'unquoted'", "\"unquoted\"" },
652  { "part 'is' squoted", "'part '\\''is'\\'' squoted'", "\"part 'is' squoted\"" },
653  { "part \"is\" dquoted", "'part \"is\" dquoted'", "\"part \\\"is\\\" dquoted\"" },
654  { "'squoted'", "\\''squoted'\\'", "\"'squoted'\"" },
655  { "\"dquoted\"", "'\"dquoted\"'", "\"\\\"dquoted\\\"\"" },
656  { "'", "\\'", "\"'\"" }, // a single quote
657  { "\"", "'\"'", "\"\\\"\"" }, // a double quote
658  { "\\", "'\\'", "\"\\\\\"" }, // a backslash
659  { "'\"'\"", "\\''\"'\\''\"'", "\"'\\\"'\\\"\"" }, // interleaved quotes
660  { "`wc -c <min_ascii.arb | tr -d ' '`",
661  "'`wc -c <min_ascii.arb | tr -d '\\'' '\\''`'",
662  "\"`wc -c <min_ascii.arb | tr -d ' '`\"" }, // interleaved quotes
663  };
664 
665  for (unsigned a = 0; a<ARRAY_ELEMS(args); ++a) {
666  TEST_ANNOTATE(GBS_global_string("a=%i", a));
667  const quoting& arg = args[a];
668 
669  TEST_EXPECT_SQUOTE(arg.plain, arg.squoted);
670  TEST_EXPECT_SQUOTE_IDENTITY(arg.plain);
671 
672  TEST_EXPECT_DQUOTE(arg.plain, arg.dquoted);
673  if (a != 11) {
674  TEST_EXPECT_DQUOTE_IDENTITY(arg.plain);
675  }
676  else { // backticked wc call
677  char *dquoted = GBK_doublequote(arg.plain);
678  TEST_EXPECT_ECHOED_EQUALS(dquoted, "16"); // 16 is number of chars in min_ascii.arb
679  free(dquoted);
680  }
681  }
682 
683  TEST_EXPECT_EQUAL(unlink(tmpfile), 0);
684 }
685 
686 #endif // UNIT_TESTS
687 
688 // --------------------------------------------------------------------------------
689 
GB_ERROR GB_get_error()
Definition: arb_msg.cxx:344
#define arb_assert(cond)
Definition: arb_assert.h:245
const char * GB_ERROR
Definition: arb_core.h:25
string result
void GBS_restore_global_buffers(GlobalStringBuffers *saved)
Definition: arb_msg.cxx:161
void GB_warning(const char *message)
Definition: arb_msg.cxx:484
const char * GBS_vglobal_string(const char *templat, va_list parg)
Definition: arb_msg.cxx:166
gb_warning_func_type show_warning
Definition: arb_handlers.h:39
static char * GB_error_buffer
Definition: arb_msg.cxx:257
GB_ERROR GBK_system(const char *system_command)
Definition: arb_msg.cxx:519
static size_t last_global_string_size
Definition: arb_msg.cxx:42
gb_information_func_type show_message
Definition: arb_handlers.h:40
GB_ERROR GB_append_exportedError(GB_ERROR error)
Definition: arb_msg.cxx:399
void demangle_backtrace(const class BackTraceInfo &trace, FILE *out, const char *message)
Definition: arb_backtrace.h:95
const char * GBS_global_string_to_buffer(char *buffer, size_t bufsize, const char *templat,...)
Definition: arb_msg.cxx:177
char * GBK_doublequote(const char *arg)
Definition: arb_msg.cxx:576
GB_ERROR GB_IO_error(const char *action, const char *filename)
Definition: arb_msg.cxx:293
char * ARB_strdup(const char *str)
Definition: arb_string.h:27
#define ARB_CRASH_CODE(sig)
Definition: arb_backtrace.h:33
void GB_internal_errorf(const char *templat,...)
Definition: arb_msg.cxx:458
GB_ERROR GB_export_IO_error(const char *action, const char *filename)
Definition: arb_msg.cxx:325
const char * GBS_global_string(const char *templat,...)
Definition: arb_msg.cxx:204
#define FORWARD_FORMATTED(receiver, format)
Definition: arb_msg_fwd.h:19
bool GB_have_error()
Definition: arb_msg.cxx:349
void GBK_terminatef(const char *templat,...)
Definition: arb_msg.cxx:477
char * release()
Definition: arb_strbuf.h:80
#define ARB_SIGSEGV(backtrace)
Definition: arb_assert.h:174
void cat(const char *from)
Definition: arb_strbuf.h:158
char * GBS_vglobal_string_copy(const char *templat, va_list parg)
Definition: arb_msg.cxx:171
static GlobalStringBuffers globBuf
Definition: arb_msg.cxx:85
class BackTraceInfo * GBK_get_backtrace(size_t skipFramesAtBottom)
Definition: arb_msg.cxx:417
void GBK_dump_former_backtrace(class BackTraceInfo *trace, FILE *out, const char *message)
Definition: arb_msg.cxx:420
#define ARRAY_ELEMS(array)
Definition: arb_defs.h:19
gb_error_handler_type show_error
Definition: arb_handlers.h:38
GB_ERROR GB_export_error(const char *error)
Definition: arb_msg.cxx:259
GB_ERROR GB_await_error()
Definition: arb_msg.cxx:353
fflush(stdout)
char * ARB_strduplen(const char *p, unsigned len)
Definition: arb_string.h:33
void GB_warningf(const char *templat,...)
Definition: arb_msg.cxx:490
TYPE * ARB_alloc(size_t nelem)
Definition: arb_mem.h:56
arb_handlers * active_arb_handlers
void GB_clear_error()
Definition: arb_msg.cxx:365
void message(char *errortext)
static void error(const char *msg)
Definition: mkptypes.cxx:96
GB_ERROR GBK_assert_msg(const char *assertion, const char *file, int linenr)
Definition: arb_msg.cxx:217
__ATTR__VFORMAT_MEMBER(1) const char *vstrf(const char *templat
void GBS_reuse_buffer(const char *global_buffer)
Definition: arb_msg.cxx:510
#define BUFSIZE
va_end(argPtr)
GB_ERROR GB_print_error()
Definition: arb_msg.cxx:335
fputs(TRACE_PREFIX, stderr)
GB_ERROR GB_export_errorf(const char *templat,...)
Definition: arb_msg.cxx:264
void GB_internal_error(const char *message)
Definition: arb_msg.cxx:435
void ncat(const char *from, size_t count)
Definition: arb_strbuf.h:151
char * GBK_singlequote(const char *arg)
Definition: arb_msg.cxx:547
BackTraceInfo(size_t skipFramesAtBottom)
Definition: arb_backtrace.h:46
void GBK_free_backtrace(class BackTraceInfo *trace)
Definition: arb_msg.cxx:424
GB_ERROR GB_failedTo_error(const char *do_something, const char *special, GB_ERROR error)
Definition: arb_msg.cxx:387
va_start(argPtr, format)
const char * GBS_static_string(const char *str)
Definition: arb_msg.cxx:213
void GBK_dump_backtrace(FILE *out, const char *message)
Definition: arb_msg.cxx:428
#define PRINT2BUFFER_CHECKED(printed, buffer, bufsize, templat, parg)
Definition: arb_msg.cxx:57
GlobalStringBuffers * GBS_store_global_buffers()
Definition: arb_msg.cxx:155
#define NULp
Definition: cxxforward.h:97
void GB_information(const char *message)
Definition: arb_msg.cxx:495
void GBK_terminate(const char *error)
Definition: arb_msg.cxx:463
void GB_informationf(const char *templat,...)
Definition: arb_msg.cxx:502
#define FORWARD_FORMATTED_NORETURN(receiver, format)
Definition: arb_msg_fwd.h:29
va_list int allow_reuse
Definition: arb_msg.cxx:82
#define TEST_EXPECT_EQUAL(expr, want)
Definition: test_unit.h:1283
#define GBS_GLOBAL_STRING_SIZE
Definition: arb_msg.cxx:43
#define GLOBAL_STRING_BUFFERS
Definition: arb_msg.cxx:40
char * GBS_global_string_copy(const char *templat,...)
Definition: arb_msg.cxx:195
void put(char c)
Definition: arb_strbuf.h:138