"Fossies" - the Fresh Open Source Software Archive

Member "libgd-2.3.3/tests/gdtest/gdtest.c" (11 Sep 2021, 15782 Bytes) of package /linux/www/libgd-2.3.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "gdtest.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.3.2_vs_2.3.3.

    1 #ifdef HAVE_CONFIG_H
    2 #include <config.h>
    3 #endif
    4 #include <assert.h>
    5 #include <setjmp.h>
    6 #include <stdlib.h>
    7 #include <stdio.h>
    8 #include <string.h>
    9 #include <math.h>
   10 #include <limits.h>
   11 #include <time.h>
   12 
   13 #ifdef HAVE_DIRENT_H
   14 #include <dirent.h>
   15 #endif
   16 #ifdef HAVE_UNISTD_H
   17 #include <unistd.h>
   18 #endif
   19 #ifdef HAVE_SYS_STAT_H
   20 #include <sys/stat.h>
   21 #endif
   22 #ifdef HAVE_SYS_TYPES_H
   23 #include <sys/types.h>
   24 #endif
   25 
   26 #if defined(_WIN32) && !defined(__MINGW32__) &&  !defined(__MINGW64__)
   27 #include "readdir.h"
   28 #include <errno.h>
   29 #endif
   30 #if defined(__MINGW32__) ||  defined(__MINGW64__)
   31 # define lstat stat
   32 #endif
   33 #include "gd_intern.h"
   34 
   35 /* GDTEST_TOP_DIR is defined in other compile ways except msys
   36  * test_config.h is created by windows/msys/run_test.sh*/
   37 #ifndef GDTEST_TOP_DIR
   38 #include <test_config.h>
   39 #endif
   40 
   41 #include "gd.h"
   42 
   43 #include "gdtest.h"
   44 
   45 /* max is already defined in windows/msvc */
   46 #ifndef max
   47     static inline int max(int a, int b) {return a > b ? a : b;}
   48 #endif
   49 
   50 void gdSilence(int priority, const char *format, va_list args)
   51 {
   52     (void)priority;
   53     (void)format;
   54     (void)args;
   55 }
   56 
   57 gdImagePtr gdTestImageFromPng(const char *filename)
   58 {
   59     gdImagePtr image;
   60     FILE *fp;
   61 
   62     /* If the path is relative, then assume it's in the tests/ dir. */
   63     if (filename[0] == '/' || filename[0] == '.'
   64 #ifdef _WIN32
   65     || filename[1] == ':'
   66 #endif
   67     ) {
   68         fp = fopen(filename, "rb");
   69     } else {
   70         fp = gdTestFileOpen(filename);
   71     }
   72 
   73     if (fp == NULL) {
   74         return NULL;
   75     }
   76 
   77     image = gdImageCreateFromPng(fp);
   78     fclose(fp);
   79     return image;
   80 }
   81 
   82 static char *tmpdir_base;
   83 int gdTestIsDir(char *path) {
   84 #if defined(_WIN32) && !defined(__MINGW32__) &&  !defined(__MINGW64__)
   85     WIN32_FILE_ATTRIBUTE_DATA data;
   86 
   87     if (!GetFileAttributesEx(path, GetFileExInfoStandard, &data)) {
   88         return 0;
   89     }
   90     if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
   91         return 0;
   92     } else {
   93         return 1;
   94     }
   95 #else
   96     struct stat st;
   97     if (lstat(path, &st) != 0)
   98 
   99     if (S_ISDIR(st.st_mode))
  100         return 1;
  101     return 0;
  102 #endif
  103 }
  104 /* This is kind of hacky, but it's meant to be simple. */
  105 static void _clean_dir(const char *dir)
  106 {
  107     DIR *d;
  108     struct dirent *de;
  109 
  110     d = opendir(dir);
  111     if (d == NULL)
  112         return;
  113 
  114     if (chdir(dir) != 0)
  115         goto done;
  116 
  117     while ((de = readdir(d)) != NULL) {
  118         struct stat st;
  119 
  120         if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
  121             continue;
  122 #if defined(_WIN32) && !defined(__MINGW32__) &&  !defined(__MINGW64__)
  123     {
  124         WIN32_FILE_ATTRIBUTE_DATA data;
  125 
  126         if (!GetFileAttributesEx(de->d_name, GetFileExInfoStandard, &data)) {
  127             continue;
  128         }
  129         if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  130             _clean_dir(de->d_name);
  131         } else {
  132             unlink(de->d_name);
  133         }
  134     }
  135 #else
  136         if (lstat(de->d_name, &st) != 0)
  137             continue;
  138 
  139         if (S_ISDIR(st.st_mode))
  140             _clean_dir(de->d_name);
  141         else
  142             unlink(de->d_name);
  143 #endif
  144     }
  145 
  146     if (chdir("..")) {
  147         /* Ignore. */;
  148     }
  149 
  150  done:
  151     closedir(d);
  152     rmdir(dir);
  153 }
  154 
  155 static void tmpdir_cleanup(void)
  156 {
  157     _clean_dir(tmpdir_base);
  158     free(tmpdir_base);
  159 }
  160 
  161 #if defined(_WIN32) && !defined(__MINGW32__) &&  !defined(__MINGW64__)
  162 
  163 
  164 typedef VOID (WINAPI *MyGetSystemTimeAsFileTime)(LPFILETIME lpSystemTimeAsFileTime);
  165 
  166 static MyGetSystemTimeAsFileTime get_time_func(void)
  167 {
  168     MyGetSystemTimeAsFileTime timefunc = NULL;
  169     HMODULE hMod = GetModuleHandle("kernel32.dll");
  170 
  171     if (hMod) {
  172         /* Max possible resolution <1us, win8/server2012 */
  173         timefunc = (MyGetSystemTimeAsFileTime)GetProcAddress(hMod, "GetSystemTimePreciseAsFileTime");
  174 
  175         if(!timefunc) {
  176             /* 100ns blocks since 01-Jan-1641 */
  177             timefunc = (MyGetSystemTimeAsFileTime)GetProcAddress(hMod, "GetSystemTimeAsFileTime");
  178         }
  179     }
  180 
  181     return timefunc;
  182 }
  183 static MyGetSystemTimeAsFileTime timefunc = NULL;
  184 static int getfilesystemtime(struct timeval *tv)
  185 {
  186     FILETIME ft;
  187     unsigned __int64 ff = 0;
  188     ULARGE_INTEGER fft;
  189 
  190     if (timefunc == NULL) {
  191         timefunc = get_time_func();
  192     }
  193     timefunc(&ft);
  194 
  195     /*
  196      * Do not cast a pointer to a FILETIME structure to either a
  197      * ULARGE_INTEGER* or __int64* value because it can cause alignment faults on 64-bit Windows.
  198      * via  http://technet.microsoft.com/en-us/library/ms724284(v=vs.85).aspx
  199      */
  200     fft.HighPart = ft.dwHighDateTime;
  201     fft.LowPart = ft.dwLowDateTime;
  202     ff = fft.QuadPart;
  203 
  204     ff /= 10ULL; /* convert to microseconds */
  205     ff -= 11644473600000000ULL; /* convert to unix epoch */
  206 
  207     tv->tv_sec = (long)(ff / 1000000ULL);
  208     tv->tv_usec = (long)(ff % 1000000ULL);
  209 
  210     return 0;
  211 }
  212 #endif
  213 #if defined(_WIN32)
  214 
  215 static void randtemplate(char *template, size_t l) {
  216     // just to avoid calls within the same second 
  217     srand(time (NULL) + (unsigned int)template);
  218     for (size_t i = l - 6; i < l; i++) {
  219         int r = rand();
  220         if ((r / (RAND_MAX + 1)) > ((RAND_MAX + 1) / 2))
  221             template[i] = 'A' + (double) rand () / (RAND_MAX + 1) * ('Z' - 'A');
  222         else
  223             template[i] = 'a' + (double) rand () / (RAND_MAX + 1) * ('z' - 'a');
  224     }
  225 }
  226 
  227 char* strrstr (char* haystack, char* needle)
  228 {
  229   int needle_length = strlen(needle);
  230   char * haystack_end = haystack + strlen(haystack) - needle_length;
  231   char * p;
  232   int i;
  233 
  234   for(p = haystack_end; p >= haystack; --p)
  235   {
  236     for(i = 0; i < needle_length; ++i) {
  237       if(p[i] != needle[i])
  238         goto next;
  239     }
  240     return p;
  241 
  242     next:;
  243   }
  244   return 0;
  245 }
  246 
  247 
  248 static char *
  249 mkdtemp (char *tmpl)
  250 {
  251     size_t l;
  252     char attempts = 8;
  253     int res = 0;
  254 
  255     if (tmpl == NULL) {
  256         errno = EINVAL;
  257         return NULL;
  258     }
  259 
  260     l = strlen (tmpl);
  261     if (l < 6 || strcmp (&tmpl[l - 6], "XXXXXX") != 0) {
  262         errno = EINVAL;
  263         return NULL;
  264     }
  265     do {
  266         randtemplate (tmpl, l);
  267         res = mkdir(tmpl);
  268         attempts--;
  269     } while (attempts > 0 && res != 0 );
  270     
  271     if (res == 0) {
  272         return tmpl;
  273     }
  274     if (errno != EEXIST) {
  275         printf("Failed to create tmp dir, last attempt %s.", tmpl);
  276         return NULL;
  277     }
  278 
  279     /* We got out of the loop because we ran out of combinations to try.  */
  280     errno = EEXIST;
  281     return NULL;
  282 }
  283 #endif
  284 
  285 const char *gdTestTempDir(void)
  286 {
  287     if (tmpdir_base == NULL) {
  288         char *tmpdir;
  289 #if defined(_WIN32)  && !defined(__MINGW32__) &&  !defined(__MINGW64__)
  290         char tmpdir_root[MAXPATHLEN];
  291         size_t tmpdir_root_len = GetTempPath(MAX_PATH, tmpdir_root);
  292         if ((tmpdir_root_len + 30 > MAX_PATH) || (tmpdir_root_len == 0)) {
  293             printf("Tmp dir path too long or 0 length <%s>\n", tmpdir_root);
  294             return NULL;
  295         }
  296 #else
  297         char *tmpdir_root;
  298         tmpdir_root = getenv("TMPDIR");
  299         if (tmpdir_root == NULL) {
  300             // Mingw defines it
  301             tmpdir_root = getenv("TMP");
  302             if (tmpdir_root == NULL) {
  303                 // Fall back here.
  304                 tmpdir_root = "/tmp";
  305                 if (!gdTestIsDir(tmpdir_root)) {
  306                     printf("tmpdir failed to be used or initialized (%s).", tmpdir_root);
  307                     exit(2);
  308                 }
  309             }
  310         }
  311 #endif
  312 
  313         /* The constant here is a lazy over-estimate. */
  314         tmpdir = malloc(strlen(tmpdir_root) + 30);
  315         if (tmpdir == NULL) {
  316             printf("cannot alloc tmpdir path.");
  317             return NULL;
  318         }
  319 
  320 #if defined(_WIN32)
  321         sprintf(tmpdir, "%sgdtest.XXXXXX", tmpdir_root);
  322 #else
  323         sprintf(tmpdir, "%s/gdtest.XXXXXX", tmpdir_root);
  324 #endif
  325 
  326         tmpdir_base = mkdtemp(tmpdir);
  327         if (tmpdir_base == NULL) {
  328             printf("failed to generate the tmp dir path (%s).", tmpdir);
  329             return NULL;
  330         }
  331 
  332         atexit(tmpdir_cleanup);
  333     }
  334 
  335     return tmpdir_base;
  336 }
  337 
  338 char *gdTestTempFile(const char *template)
  339 {
  340     const char *tempdir = gdTestTempDir();
  341     char *ret;
  342     if (tempdir == NULL) {
  343         return NULL;
  344     }
  345 #if defined(_WIN32) && !defined(__MINGW32__) &&  !defined(__MINGW64__)
  346     {
  347         char *tmpfilename;
  348         UINT error;
  349 
  350         ret = malloc(MAX_PATH);
  351         if (ret == NULL) {
  352             printf("Failed to alloc tmp path");
  353             return NULL;
  354         }
  355         if (template == NULL) {
  356             error = GetTempFileName(tempdir,
  357                                           "gdtest",
  358                                           0,
  359                                           ret);
  360             if (error = 0)  {
  361                 printf("GetTempFileName failed.");
  362                 gdFree(ret);
  363                 return NULL;
  364             }
  365         } else {
  366             sprintf(ret, "%s\\%s", tempdir, template);
  367         }
  368     }
  369 #else
  370     if (template == NULL) {
  371         template = "gdtemp.XXXXXX";
  372     }
  373     ret = malloc(strlen(tempdir) + 10 + strlen(template));
  374     if (ret == NULL) {
  375         printf("Failed to alloc tmp path");
  376         return NULL;
  377     }
  378     sprintf(ret, "%s/%s", tempdir, template);
  379 
  380     if (strstr(template, "XXXXXX") != NULL) {
  381         int fd = mkstemp(ret);
  382         if (fd == -1) {
  383             printf("mkstemp failed");
  384             gdFree(ret);
  385             return NULL;
  386         }
  387         close(fd);
  388     }
  389 #endif
  390     return ret;
  391 }
  392 
  393 FILE *gdTestTempFp(void)
  394 {
  395     char *file = gdTestTempFile(NULL);
  396     FILE *fp = fopen(file, "wb");
  397     if (fp == NULL) {
  398         printf("fail to open tmp file");
  399         return NULL;
  400     }
  401     free(file);
  402     return fp;
  403 }
  404 
  405 char *gdTestFilePathV(const char *path, va_list args)
  406 {
  407     size_t len;
  408     const char *p;
  409     char *file;
  410     va_list args_len;
  411 
  412     /* Figure out how much space we need. */
  413     va_copy(args_len, args);
  414     len = strlen(GDTEST_TOP_DIR) + 1;
  415     p = path;
  416     do {
  417         len += strlen(p) + 1;
  418     } while ((p = va_arg(args_len, const char *)) != NULL);
  419     va_end(args_len);
  420 
  421     /* Now build the path. */
  422     file = malloc(len);
  423     if (file == NULL) {
  424         printf("failed to alloc path.");
  425         return NULL;
  426     }
  427     strcpy(file, GDTEST_TOP_DIR);
  428     p = path;
  429     do {
  430 #if defined(_WIN32) && !defined(__MINGW32__) &&  !defined(__MINGW64__)
  431         strcat(file, "\\");
  432 #else
  433         strcat(file, "/");
  434 #endif
  435         strcat(file, p);
  436 
  437     } while ((p = va_arg(args, const char *)) != NULL);
  438     va_end(args);
  439 
  440     return file;
  441 }
  442 
  443 char *gdTestFilePathX(const char *path, ...)
  444 {
  445     va_list args;
  446     va_start(args, path);
  447     return gdTestFilePathV(path, args);
  448 }
  449 
  450 FILE *gdTestFileOpenX(const char *path, ...)
  451 {
  452     va_list args;
  453     FILE *fp;
  454     char *file;
  455 
  456     va_start(args, path);
  457     file = gdTestFilePathV(path, args);
  458     fp = fopen(file, "rb");
  459     if (fp == NULL) {
  460         printf("failed to open path (rb).");
  461         return NULL;
  462     }
  463     free(file);
  464     return fp;
  465 }
  466 
  467 /* Compare two buffers, returning the number of pixels that are
  468  * different and the maximum difference of any single color channel in
  469  * result_ret.
  470  *
  471  * This function should be rewritten to compare all formats supported by
  472  * cairo_format_t instead of taking a mask as a parameter.
  473  */
  474 void gdTestImageDiff(gdImagePtr buf_a, gdImagePtr buf_b,
  475                      gdImagePtr buf_diff, CuTestImageResult *result_ret)
  476 {
  477     int x, y;
  478     int c1, c2;
  479 #   define UP_DIFF(var) result_ret->max_diff = max((var), result_ret->max_diff)
  480 
  481     for (y = 0; y < gdImageSY(buf_a); y++) {
  482         for (x = 0; x < gdImageSX(buf_a); x++) {
  483             c1 = gdImageGetTrueColorPixel(buf_a, x, y);
  484             c2 = gdImageGetTrueColorPixel(buf_b, x, y);
  485 
  486             /* check if the pixels are the same */
  487             if (c1 != c2) {
  488                 int r1,b1,g1,a1,r2,b2,g2,a2;
  489                 unsigned int diff_a,diff_r,diff_g,diff_b;
  490 
  491                 a1 = gdTrueColorGetAlpha(c1);
  492                 a2 = gdTrueColorGetAlpha(c2);
  493                 diff_a = abs (a1 - a2);
  494                 diff_a *= 4;  /* emphasize */
  495 
  496                 if (diff_a) {
  497                     diff_a += 128; /* make sure it's visible */
  498                 }
  499                 if (diff_a > gdAlphaMax) {
  500                     diff_a = gdAlphaMax/2;
  501                 }
  502 
  503                 r1 = gdTrueColorGetRed(c1);
  504                 r2 = gdTrueColorGetRed(c2);
  505                 diff_r = abs (r1 - r2);
  506                 // diff_r *= 4;  /* emphasize */
  507                 if (diff_r) {
  508                     diff_r += gdRedMax/2; /* make sure it's visible */
  509                 }
  510                 if (diff_r > 255) {
  511                     diff_r = 255;
  512                 }
  513                 UP_DIFF(diff_r);
  514 
  515                 g1 = gdTrueColorGetGreen(c1);
  516                 g2 = gdTrueColorGetGreen(c2);
  517                 diff_g = abs (g1 - g2);
  518 
  519                 diff_g *= 4;  /* emphasize */
  520                 if (diff_g) {
  521                     diff_g += gdGreenMax/2; /* make sure it's visible */
  522                 }
  523                 if (diff_g > 255) {
  524                     diff_g = 255;
  525                 }
  526                 UP_DIFF(diff_g);
  527 
  528                 b1 = gdTrueColorGetBlue(c1);
  529                 b2 = gdTrueColorGetBlue(c2);
  530                 diff_b = abs (b1 - b2);
  531                 diff_b *= 4;  /* emphasize */
  532                 if (diff_b) {
  533                     diff_b += gdBlueMax/2; /* make sure it's visible */
  534                 }
  535                 if (diff_b > 255) {
  536                     diff_b = 255;
  537                 }
  538                 UP_DIFF(diff_b);
  539 
  540                 result_ret->pixels_changed++;
  541                 if (buf_diff) gdImageSetPixel(buf_diff, x,y, gdTrueColorAlpha(diff_r, diff_g, diff_b, diff_a));
  542             } else {
  543                 if (buf_diff) gdImageSetPixel(buf_diff, x,y, gdTrueColorAlpha(255,255,255,0));
  544             }
  545         }
  546     }
  547 #   undef UP_DIFF
  548 }
  549 
  550 
  551 /* Return the largest difference between two corresponding pixels and
  552  * channels. */
  553 unsigned int gdMaxPixelDiff(gdImagePtr a, gdImagePtr b)
  554 {
  555     int diff = 0;
  556     int x, y;
  557 
  558     if (a == NULL || b == NULL || a->sx != b->sx || a->sy != b->sy)
  559         return UINT_MAX;
  560 
  561     for (x = 0; x < a->sx; x++) {
  562         for (y = 0; y < a->sy; y++) {
  563             int c1, c2;
  564 
  565             c1 = gdImageGetTrueColorPixel(a, x, y);
  566             c2 = gdImageGetTrueColorPixel(b, x, y);
  567             if (c1 == c2) continue;
  568 
  569             diff = max(diff, abs(gdTrueColorGetAlpha(c1) - gdTrueColorGetAlpha(c2)));
  570             diff = max(diff, abs(gdTrueColorGetRed(c1)   - gdTrueColorGetRed(c2)));
  571             diff = max(diff, abs(gdTrueColorGetGreen(c1) - gdTrueColorGetGreen(c2)));
  572             diff = max(diff, abs(gdTrueColorGetBlue(c1)  - gdTrueColorGetBlue(c2)));
  573         }/* for */
  574     }/* for */
  575 
  576     return diff;
  577 }
  578 
  579 int gdTestImageCompareToImage(const char* file, unsigned int line, const char* message,
  580                               gdImagePtr expected, gdImagePtr actual)
  581 {
  582     unsigned int width_a, height_a;
  583     unsigned int width_b, height_b;
  584     gdImagePtr surface_diff = NULL;
  585     CuTestImageResult result = {0, 0};
  586 
  587     (void)message;
  588 
  589     if (!actual) {
  590         _gdTestErrorMsg(file, line, "Image is NULL\n");
  591         goto fail;
  592     }
  593 
  594     width_a  = gdImageSX(expected);
  595     height_a = gdImageSY(expected);
  596     width_b  = gdImageSX(actual);
  597     height_b = gdImageSY(actual);
  598 
  599     if (width_a  != width_b  || height_a != height_b) {
  600         _gdTestErrorMsg(file, line,
  601                 "Image size mismatch: (%ux%u) vs. (%ux%u)\n       for %s vs. buffer\n",
  602                 width_a, height_a,
  603                 width_b, height_b,
  604                 file);
  605         goto fail;
  606     }
  607 
  608     surface_diff = gdImageCreateTrueColor(width_a, height_a);
  609 
  610     gdTestImageDiff(expected, actual, surface_diff, &result);
  611     if (result.pixels_changed>0) {
  612         char file_diff[255];
  613         char file_out[1024];
  614         FILE *fp;
  615         int len, p;
  616 
  617         _gdTestErrorMsg(file, line,
  618                 "Total pixels changed: %d with a maximum channel difference of %d.\n",
  619                 result.pixels_changed,
  620                 result.max_diff
  621             );
  622 
  623         p = len = strlen(file);
  624         p--;
  625 
  626         /* Use only the filename (and store it in the bld dir not the src dir
  627          */
  628         while(p > 0 && (file[p] != '/' && file[p] != '\\')) {
  629             p--;
  630         }
  631         sprintf(file_diff, "%s_%u_diff.png", file + p  + 1, line);
  632         sprintf(file_out, "%s_%u_out.png", file + p  + 1, line);
  633 
  634         fp = fopen(file_diff, "wb");
  635         if (!fp) goto fail;
  636         gdImagePng(surface_diff,fp);
  637         fclose(fp);
  638         gdImageDestroy(surface_diff);
  639 
  640         fp = fopen(file_out, "wb");
  641         if (!fp) goto fail;
  642         gdImagePng(actual, fp);
  643         fclose(fp);
  644         return 0;
  645     } else {
  646         if (surface_diff) {
  647             gdImageDestroy(surface_diff);
  648         }
  649         return 1;
  650     }
  651 
  652 fail:
  653     if (surface_diff) {
  654         gdImageDestroy(surface_diff);
  655     }
  656     return 1;
  657 }
  658 
  659 int gdTestImageCompareToFile(const char* file, unsigned int line, const char* message,
  660                              const char *expected_file, gdImagePtr actual)
  661 {
  662     gdImagePtr expected = 0;
  663     int res = 1;
  664 
  665     expected = gdTestImageFromPng(expected_file);
  666 
  667     if (!expected) {
  668         _gdTestErrorMsg(file, line, "Cannot open PNG <%s>\n", expected_file);
  669         res = 0;
  670     } else {
  671         res = gdTestImageCompareToImage(file, line, message, expected, actual);
  672         gdImageDestroy(expected);
  673     }
  674     return res;
  675 }
  676 
  677 static int failureCount = 0;
  678 
  679 int gdNumFailures() {
  680     return failureCount;
  681 }
  682 
  683 int _gdTestAssert(const char* file, unsigned int line, int condition)
  684 {
  685     if (condition) return 1;
  686     _gdTestErrorMsg(file, line, "Assert failed in <%s:%i>\n", file, line);
  687 
  688     ++failureCount;
  689 
  690     return 0;
  691 }
  692 
  693 int _gdTestAssertMsg(const char* file, unsigned int line, int condition, const char* message, ...)
  694 {
  695     va_list args;
  696 
  697     if (condition) return 1;
  698 
  699     fprintf(stderr, "%s:%u: ", file, line);
  700     va_start(args, message);
  701     vfprintf(stderr, message, args);
  702     va_end(args);
  703 
  704     fflush(stderr);
  705 
  706     ++failureCount;
  707 
  708     return 0;
  709 }
  710 
  711 int _gdTestErrorMsg(const char* file, unsigned int line, const char* format, ...) /* {{{ */
  712 {
  713     va_list args;
  714 
  715     fprintf(stderr, "%s:%u: ", file, line);
  716     va_start(args, format);
  717     vfprintf(stderr, format, args);
  718     va_end(args);
  719     fflush(stderr);
  720 
  721     ++failureCount;
  722 
  723     return 0;
  724 }
  725 /* }}} */