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