pcb 4.1.1
An interactive printed circuit board layout editor.

pcb-printf.c

Go to the documentation of this file.
00001 
00039 #ifdef HAVE_CONFIG_H
00040 #include "config.h"
00041 #endif
00042 
00043 #include "global.h"
00044 
00045 #include "pcb-printf.h"
00046 
00047 /* Helper macros for tables */
00048 #define MM_TO_COORD3(a,b,c)             MM_TO_COORD (a), MM_TO_COORD (b), MM_TO_COORD (c)
00049 #define MIL_TO_COORD3(a,b,c)            MIL_TO_COORD (a), MIL_TO_COORD (b), MIL_TO_COORD (c)
00050 #define MM_TO_COORD5(a,b,c,d,e)         MM_TO_COORD (a), MM_TO_COORD (b), MM_TO_COORD (c),      \
00051                                         MM_TO_COORD (d), MM_TO_COORD (e)
00052 #define MIL_TO_COORD5(a,b,c,d,e)        MIL_TO_COORD (a), MIL_TO_COORD (b), MIL_TO_COORD (c),   \
00053                                         MIL_TO_COORD (d), MIL_TO_COORD (e)
00054 
00062 static Unit Units[] = {
00063   { 0, "km", NULL, 'k', 0.000001, METRIC, ALLOW_KM, 5,
00064              0.00005, 0.0005, 0.0025, 0.05, 0.25,
00065              { "" } },
00066   { 0, "m",  NULL, 'f', 0.001,    METRIC, ALLOW_M,  5,
00067              0.0005, 0.005, 0.025, 0.5, 2.5,
00068              { "" } },
00069   { 0, "cm", NULL, 'e', 0.1,      METRIC, ALLOW_CM, 5,
00070              0.005, 0.05, 0.25, 5, 25,
00071              { "" } },
00072   { 0, "mm", NULL, 'm', 1,        METRIC, ALLOW_MM, 4,
00073              0.005, 0.05, 0.25, 5, 25,
00074              { "" } },
00075   { 0, "um", NULL, 'u', 1000,     METRIC, ALLOW_UM, 2,
00076              0.005, 0.05, 0.25, 5, 25,
00077              { "" } },
00078   { 0, "nm", NULL, 'n', 1000000,  METRIC, ALLOW_NM, 0,
00079              5, 50, 2500, 5000, 25000,
00080              { "" } },
00081 /* Hack: Pixels get parsed like nanometers. If the value of the 
00082  * resulting integer is sufficiently small, the code interprets 
00083  * it a screen pixels. This affects rat thickness. */            
00084   { 0, "px", NULL, 'n', 1000000,  METRIC, ALLOW_NM, 0,
00085              5, 50, 2500, 5000, 25000,
00086              { "" } },
00087 
00088 
00089   { 0, "in",   NULL, 'i', 0.001, IMPERIAL, ALLOW_IN,   5,
00090                0.1, 1.0, 5.0, 25, 100,
00091                { "inch" } },
00092   { 0, "mil",  NULL, 'l', 1,     IMPERIAL, ALLOW_MIL,  2,
00093                0.1, 1.0, 10, 100, 1000,
00094                { "" } },
00095   { 0, "dmil", NULL, 't', 10,   IMPERIAL, ALLOW_DMIL, 0,
00096                1, 10, 100, 1000, 10000,
00097                { "" } },
00098   { 0, "cmil", NULL, 'c', 100,   IMPERIAL, ALLOW_CMIL, 0,
00099                1, 10, 100, 1000, 10000,
00100                { "pcb" } }
00101 };
00102 #define N_UNITS ((int) (sizeof Units / sizeof Units[0]))
00103 
00111 void initialize_units()
00112 {
00113   int i;
00114   for (i = 0; i < N_UNITS; ++i)
00115     {
00116       Units[i].index = i;
00117       Units[i].in_suffix = _(Units[i].suffix);
00118     }
00119 }
00120 
00134 enum e_allow set_allow_readable(enum e_allow new_mask)
00135 {
00136   static enum e_allow readable_mask = ALLOW_READABLE;
00137   if (new_mask != 0)
00138     readable_mask = new_mask;
00139   return readable_mask;
00140 }
00141 
00142 
00143 /* TABLE FORMAT   |  default  |  min  |  max
00144  *          grid  |           |       |
00145  *          size  |           |       |
00146  *          line  |           |       |
00147  *         clear  |           |       |
00148  */
00149 static Increments increments_metric = {
00150   "mm",
00151   MM_TO_COORD3 (0.1,  0.01,  1.0),
00152   MM_TO_COORD3 (0.2,  0.01,  0.5),
00153   MM_TO_COORD3 (0.1,  0.005, 0.5),
00154   MM_TO_COORD3 (0.05, 0.005, 0.5)
00155 };
00156 static Increments increments_imperial = {
00157   "mil",
00158   MIL_TO_COORD3 (5,  1,   25),
00159   MIL_TO_COORD3 (10, 1,   10),
00160   MIL_TO_COORD3 (5,  0.5, 10),
00161   MIL_TO_COORD3 (2,  0.5, 10)
00162 };
00163 
00175 const Unit *get_unit_struct (const char *const_suffix)
00176 {
00177   int i;
00178   int s_len = 0;
00179   /* Turn given suffix into something we can modify... */
00180   char *m_suffix = g_strdup (const_suffix);
00181   /* ...and store this in a pointer we can move. */
00182   char *suffix = m_suffix;
00183 
00184   /* Determine bounds */
00185   while (isspace (*suffix))
00186     ++suffix;
00187   while (isalnum (suffix[s_len]))
00188     ++s_len;
00189 
00190   /* Also understand plural suffixes: "inches", "mils" */
00191   if (s_len > 2)
00192     {
00193       if (suffix[s_len - 2] == 'e' && suffix[s_len - 1] == 's')
00194         suffix[s_len - 2] = 0;
00195       else if (suffix[s_len - 1] == 's')
00196         suffix[s_len - 1] = 0;
00197     }
00198 
00199   /* Do lookup */
00200   if (*suffix && s_len > 0)
00201     for (i = 0; i < N_UNITS; ++i)
00202       if (strncmp (suffix, Units[i].suffix, s_len) == 0 ||
00203           strncmp (suffix, Units[i].alias[0], s_len) == 0)
00204         {
00205           g_free (m_suffix);
00206           return &Units[i];
00207         }
00208   g_free (m_suffix);
00209   return NULL;
00210 }
00211 
00212 void copy_nonzero_increments (Increments *dst, const Increments *src)
00213 {
00214   if (src->grid >= dst->grid_min && src->grid <= dst->grid_max)
00215     dst->grid = src->grid;
00216   if (src->line >= dst->line_min && src->line <= dst->line_max)
00217     dst->line = src->line;
00218   if (src->size >= dst->size_min && src->size <= dst->size_max)
00219     dst->size = src->size;
00220   if (src->clear >= dst->clear_min && src->clear <= dst->clear_max)
00221     dst->clear = src->clear;
00222 }
00223 
00224 /* ACCESSORS */
00225 
00231 const Unit *get_unit_list (void)
00232 {
00233         return Units;
00234 }
00235 
00239 int get_n_units (void)
00240 {
00241         return N_UNITS;
00242 }
00243 
00251 Increments *get_increments_struct (enum e_family family)
00252 {
00253   switch (family)
00254     {
00255     case METRIC:
00256       return &increments_metric;
00257     case IMPERIAL:
00258       return &increments_imperial;
00259     }
00260   return NULL;
00261 }
00262 
00271 double coord_to_unit (const Unit *unit, Coord x)
00272 {
00273   double base;
00274   if (unit == NULL)
00275     return -1;
00276   base = unit->family == METRIC
00277            ? COORD_TO_MM (x)
00278            : COORD_TO_MIL (x);
00279   return unit->scale_factor * base;
00280 }
00281 
00290 Coord unit_to_coord (const Unit *unit, double x)
00291 {
00292   double base;
00293   if (unit == NULL)
00294     return -1;
00295   base = unit->family == METRIC
00296            ? MM_TO_COORD (x)
00297            : MIL_TO_COORD (x);
00298   return DOUBLE_TO_COORD (base / unit->scale_factor);
00299 }
00300 
00301 static int min_sig_figs(double d)
00302 {
00303   char buf[50];
00304   int rv;
00305 
00306   if(d == 0) return 0;
00307 
00308   /* Normalize to x.xxxx... form */
00309   if(d < 0)      d *= -1;
00310   while(d >= 10) d /= 10;
00311   while(d < 1)   d *= 10;
00312 
00313   rv = sprintf(buf, "%g", d);
00314   return rv;
00315 }
00316 
00334 static gchar *CoordsToString(Coord coord[], int n_coords, const char *printf_spec, enum e_allow allow, enum e_suffix suffix_type)
00335 {
00336   GString *buff;
00337   gchar *printf_buff;
00338   gchar filemode_buff[G_ASCII_DTOSTR_BUF_SIZE];
00339   enum e_family family;
00340   double *value;
00341   const char *suffix;
00342   int i, n;
00343 
00344   value = malloc (n_coords * sizeof *value);
00345   buff  = g_string_new ("");
00346 
00347   /* Sanity checks */
00348   if (buff == NULL || value == NULL)
00349     return NULL;
00350   if (allow == 0)
00351     allow = ALLOW_ALL;
00352   if (printf_spec == NULL)
00353     printf_spec = "";
00354 
00355   /* Check our freedom in choosing units */
00356   if ((allow & ALLOW_IMPERIAL) == 0)
00357     family = METRIC;
00358   else if ((allow & ALLOW_METRIC) == 0)
00359     family = IMPERIAL;
00360   else
00361     {
00362       int met_votes = 0,
00363           imp_votes = 0;
00364 
00365       for (i = 0; i < n_coords; ++i)
00366         if(min_sig_figs(COORD_TO_MIL(coord[i])) < min_sig_figs(COORD_TO_MM(coord[i])))
00367           ++imp_votes;
00368         else
00369           ++met_votes;
00370 
00371       if (imp_votes > met_votes)
00372         family = IMPERIAL;
00373       else
00374         family = METRIC;
00375     }
00376 
00377   /* Set base unit */
00378   for (i = 0; i < n_coords; ++i)
00379     {
00380       switch (family)
00381         {
00382         case METRIC:   value[i] = COORD_TO_MM (coord[i]); break;
00383         case IMPERIAL: value[i] = COORD_TO_MIL (coord[i]); break;
00384         }
00385     }
00386 
00387   /* Determine scale factor -- find smallest unit that brings
00388    * the whole group above unity */
00389   for (n = 0; n < N_UNITS; ++n)
00390     {
00391       if ((Units[n].allow & allow) != 0 && (Units[n].family == family))
00392         {
00393           int n_above_one = 0;
00394     
00395           for (i = 0; i < n_coords; ++i)
00396             if (fabs(value[i] * Units[n].scale_factor) > 1)
00397               ++n_above_one;
00398           if (n_above_one == n_coords)
00399             break;
00400         }
00401     }
00402   /* If nothing worked, wind back to the smallest allowable unit */
00403   if (n == N_UNITS)
00404     {
00405       do {
00406         --n;
00407       } while ((Units[n].allow & allow) == 0 || Units[n].family != family);
00408     }
00409 
00410   /* Apply scale factor */
00411   suffix = Units[n].suffix;
00412   for (i = 0; i < n_coords; ++i)
00413     value[i] = value[i] * Units[n].scale_factor;
00414 
00415   /* Create sprintf specifier, using default_prec no precision is given */
00416   i = 0;
00417   while (printf_spec[i] == '%' || isdigit(printf_spec[i]) ||
00418          printf_spec[i] == '-' || printf_spec[i] == '+' ||
00419          printf_spec[i] == '#')
00420     ++i;
00421   if (printf_spec[i] == '.')
00422     printf_buff = g_strdup_printf (", %sf", printf_spec);
00423   else
00424     printf_buff = g_strdup_printf (", %s.%df", printf_spec, Units[n].default_prec);
00425 
00426   /* Actually sprintf the values in place
00427    *  (+ 2 skips the ", " for first value) */
00428   if (n_coords > 1)
00429     g_string_append_c (buff, '(');
00430   if (suffix_type == FILE_MODE || suffix_type == FILE_MODE_NO_SUFFIX)
00431     {
00432       g_ascii_formatd (filemode_buff, sizeof filemode_buff, printf_buff + 2, value[0]);
00433       g_string_append_printf (buff, "%s", filemode_buff);
00434     }
00435   else
00436     g_string_append_printf (buff, printf_buff + 2, value[0]);
00437   for (i = 1; i < n_coords; ++i)
00438     {
00439       if (suffix_type == FILE_MODE || suffix_type == FILE_MODE_NO_SUFFIX)
00440         {
00441           g_ascii_formatd (filemode_buff, sizeof filemode_buff, printf_buff, value[i]);
00442           g_string_append_printf (buff, "%s", filemode_buff);
00443         }
00444       else
00445         g_string_append_printf (buff, printf_buff, value[i]);
00446     }
00447   if (n_coords > 1)
00448     g_string_append_c (buff, ')');
00449   /* Append suffix */
00450   if (value[0] != 0 || n_coords > 1)
00451     {
00452       switch (suffix_type)
00453         {
00454         case NO_SUFFIX:
00455         case FILE_MODE_NO_SUFFIX:
00456           break;
00457         case SUFFIX:
00458           g_string_append_printf (buff, " %s", suffix);
00459           break;
00460         case FILE_MODE:
00461           g_string_append_printf (buff, "%s", suffix);
00462           break;
00463         }
00464     }
00465 
00466   g_free (printf_buff);
00467   free (value);
00468   /* Return just the gchar* part of our string */
00469   return g_string_free (buff, FALSE);
00470 }
00471 
00484 gchar *pcb_vprintf(const char *fmt, va_list args)
00485 {
00486   GString *string = g_string_new ("");
00487   GString *spec   = g_string_new ("");
00488 
00489   enum e_allow mask = ALLOW_ALL;
00490 
00491   if (string == NULL || spec == NULL)
00492     return NULL;
00493 
00494   while(*fmt)
00495     {
00496       enum e_suffix suffix = NO_SUFFIX;
00497 
00498       if(*fmt == '%')
00499         {
00500           gchar *unit_str = NULL;
00501           const char *ext_unit = "";
00502           Coord value[10];
00503           int count, i, done;
00504 
00505           g_string_assign (spec, "%");
00506 
00507           done = 0;
00508           while ( ! done && fmt++ && *fmt)
00509             {
00510               switch (*fmt)
00511                 {
00512                 /* Our sub-specifiers */
00513                 case '#':
00514                   mask = ALLOW_CMIL;  /* This must be pcb's base unit */
00515                   break;
00516                 case '$':
00517                   suffix = (suffix == NO_SUFFIX) ? SUFFIX : FILE_MODE;
00518                   break;
00519                 case '`':
00520                   suffix = (suffix == SUFFIX) ? FILE_MODE : FILE_MODE_NO_SUFFIX;
00521                   break;
00522                 /* Printf sub-specifiers */
00523                 case '*':
00524                   g_string_append_printf (spec, "%d", va_arg (args, int));
00525                   break;
00526                 case '.':
00527                 case ' ':
00528                 //case '#': (duplicate)
00529                 case 'l':
00530                 case 'L':
00531                 case 'h':
00532                 case '+':
00533                 case '-':
00534                 case '0':
00535                 case '1':
00536                 case '2':
00537                 case '3':
00538                 case '4':
00539                 case '5':
00540                 case '6':
00541                 case '7':
00542                 case '8':
00543                 case '9':
00544                   g_string_append_c (spec, *fmt);
00545                   break;
00546                 default:
00547                   done = 1;
00548                 }
00549             }
00550 
00551           /* Tack full specifier onto specifier */
00552           if (*fmt != 'm')
00553             g_string_append_c (spec, *fmt);
00554           switch(*fmt)
00555             {
00556             /* Printf specs */
00557             case 'o': case 'i': case 'd':
00558             case 'u': case 'x': case 'X':
00559               if(strchr (spec->str, 'l'))
00560                 {
00561                   if(strchr (spec->str, 'l') != strrchr (spec->str, 'l'))
00562                     unit_str = g_strdup_printf (spec->str, va_arg(args, long long));
00563                   else
00564                     unit_str = g_strdup_printf (spec->str, va_arg(args, long));
00565                 }
00566               else
00567                 {
00568                   unit_str = g_strdup_printf (spec->str, va_arg(args, int));
00569                 }
00570               break;
00571             case 'e': case 'E':
00572             case 'f': case 'F':
00573             case 'g': case 'G':
00574               if(suffix == FILE_MODE || suffix == FILE_MODE_NO_SUFFIX)
00575                 {
00576                   gchar buffer[128];
00577                   g_ascii_formatd (buffer, 128, spec->str, va_arg(args, double));
00578                   unit_str = g_strdup_printf ("%s", buffer);
00579                 }
00580               else
00581                 unit_str = g_strdup_printf (spec->str, va_arg(args, double));
00582               break;
00583             case 'c':
00584               if(strchr (spec->str, 'l') && sizeof(int) <= sizeof(wchar_t))
00585                 unit_str = g_strdup_printf (spec->str, va_arg(args, wchar_t));
00586               else
00587                 unit_str = g_strdup_printf (spec->str, va_arg(args, int));
00588               break;
00589             case 's':
00590               if(strchr (spec->str, 'l'))
00591                 unit_str = g_strdup_printf (spec->str, va_arg(args, wchar_t *));
00592               else
00593                 unit_str = g_strdup_printf (spec->str, va_arg(args, char *));
00594               break;
00595             case 'n':
00596               /* Depending on gcc settings, this will probably break with
00597                *  some silly "can't put %n in writeable data space" message */
00598               unit_str = g_strdup_printf (spec->str, va_arg(args, int *));
00599               break;
00600             case 'p':
00601               unit_str = g_strdup_printf (spec->str, va_arg(args, void *));
00602               break;
00603             case '%':
00604               g_string_append_c (string, '%');
00605               break;
00606             /* Our specs */
00607             case 'm':
00608               ++fmt;
00609               if (*fmt == '*')
00610                 ext_unit = va_arg(args, const char *);
00611               if (*fmt != '+' && *fmt != 'a')
00612                 value[0] = va_arg(args, Coord);
00613               count = 1;
00614               switch(*fmt)
00615                 {
00616                 case 's': unit_str = CoordsToString(value, 1, spec->str, ALLOW_MM | ALLOW_MIL, suffix); break;
00617                 case 'S': unit_str = CoordsToString(value, 1, spec->str, mask & ALLOW_ALL, suffix); break;
00618                 case 'M': unit_str = CoordsToString(value, 1, spec->str, mask & ALLOW_METRIC, suffix); break;
00619                 case 'L': unit_str = CoordsToString(value, 1, spec->str, mask & ALLOW_IMPERIAL, suffix); break;
00620                 case 'r': unit_str = CoordsToString(value, 1, spec->str, set_allow_readable(0), FILE_MODE); break;
00621                 /* All these fallthroughs are deliberate */
00622                 case '9': value[count++] = va_arg(args, Coord);
00623                 case '8': value[count++] = va_arg(args, Coord);
00624                 case '7': value[count++] = va_arg(args, Coord);
00625                 case '6': value[count++] = va_arg(args, Coord);
00626                 case '5': value[count++] = va_arg(args, Coord);
00627                 case '4': value[count++] = va_arg(args, Coord);
00628                 case '3': value[count++] = va_arg(args, Coord);
00629                 case '2':
00630                 case 'D':
00631                   value[count++] = va_arg(args, Coord);
00632                   unit_str = CoordsToString(value, count, spec->str, mask & ALLOW_ALL, suffix);
00633                   break;
00634                 case 'd':
00635                   value[1] = va_arg(args, Coord);
00636                   unit_str = CoordsToString(value, 2, spec->str, ALLOW_MM | ALLOW_MIL, suffix);
00637                   break;
00638                 case '*':
00639                   for (i = 0; i < N_UNITS; ++i)
00640                     if (strcmp (ext_unit, Units[i].suffix) == 0)
00641                       unit_str = CoordsToString(value, 1, spec->str, Units[i].allow, suffix);
00642                   if (unit_str == NULL)
00643                     unit_str = CoordsToString(value, 1, spec->str, mask & ALLOW_ALL, suffix);
00644                   break;
00645                 case 'a':
00646                   g_string_append (spec, "f");
00647                   if (suffix == SUFFIX)
00648                     g_string_append (spec, " deg");
00649                   unit_str = g_strdup_printf (spec->str, (double) va_arg(args, Angle));
00650                   break;
00651                 case '+':
00652                   mask = va_arg(args, enum e_allow);
00653                   break;
00654                 default:
00655                   for (i = 0; i < N_UNITS; ++i)
00656                     if (*fmt == Units[i].printf_code)
00657                       unit_str = CoordsToString(value, 1, spec->str, Units[i].allow, suffix);
00658                   if (unit_str == NULL)
00659                     unit_str = CoordsToString(value, 1, spec->str, ALLOW_ALL, suffix);
00660                   break;
00661                 }
00662               break;
00663             }
00664           if (unit_str != NULL)
00665             {
00666               g_string_append (string, unit_str);
00667               g_free (unit_str);
00668             }
00669         }
00670       else
00671         g_string_append_c (string, *fmt);
00672       ++fmt;
00673     }
00674   g_string_free (spec, TRUE);
00675   /* Return just the gchar* part of our string */
00676   return g_string_free (string, FALSE);
00677 }
00678 
00679 
00696 int pcb_snprintf(char *string, size_t size, const char *fmt, ...)
00697 {
00698   gchar *tmp;
00699   gsize length;
00700 
00701   va_list args;
00702   va_start(args, fmt);
00703 
00704   tmp = pcb_vprintf (fmt, args);
00705   length = strlen (tmp);
00706   strncpy (string, tmp, size);
00707   string[size - 1] = '\0';
00708 
00709   g_free (tmp);
00710   va_end(args);
00711 
00712   return length;
00713 }
00714 
00723 int pcb_fprintf(FILE *fh, const char *fmt, ...)
00724 {
00725   int rv;
00726   gchar *tmp;
00727 
00728   va_list args;
00729   va_start(args, fmt);
00730 
00731   if (fh == NULL)
00732     rv = -1;
00733   else
00734     {
00735       tmp = pcb_vprintf (fmt, args);
00736       rv = fprintf (fh, "%s", tmp);
00737       g_free (tmp);
00738     }
00739   
00740   va_end(args);
00741   return rv;
00742 }
00743 
00751 int pcb_printf(const char *fmt, ...)
00752 {
00753   int rv;
00754   gchar *tmp;
00755 
00756   va_list args;
00757   va_start(args, fmt);
00758 
00759   tmp = pcb_vprintf (fmt, args);
00760   rv = printf ("%s", tmp);
00761   g_free (tmp);
00762   
00763   va_end(args);
00764   return rv;
00765 }
00766 
00775 char *pcb_g_strdup_printf(const char *fmt, ...)
00776 {
00777   gchar *tmp;
00778 
00779   va_list args;
00780   va_start(args, fmt);
00781   tmp = pcb_vprintf (fmt, args);
00782   va_end(args);
00783   return tmp;
00784 }
00785 
00786 #ifdef PCB_UNIT_TEST
00787 void
00788 pcb_printf_register_tests ()
00789 {
00790   g_test_add_func ("/pcb-printf/test-unit", pcb_printf_test_unit);
00791   g_test_add_func ("/pcb-printf/test-printf", pcb_printf_test_printf);
00792 }
00793 
00794 void
00795 pcb_printf_test_unit ()
00796 {
00797   Coord c[] = {
00798     unit_to_coord (get_unit_struct ("m"), 1.0),
00799     unit_to_coord (get_unit_struct ("mm"), 1.0),
00800     unit_to_coord (get_unit_struct ("um"), 1.0),
00801     unit_to_coord (get_unit_struct ("mil"), 1.0),
00802     unit_to_coord (get_unit_struct ("mil"), 0.5),
00803     unit_to_coord (get_unit_struct ("nm"), 67)
00804   };
00805 
00806   /* Loop unrolled for ease of pinpointing failure */
00807   g_assert (get_unit_struct ("m") != NULL);
00808 
00809   g_assert_cmpuint (c[0], ==, 1000000000);
00810   g_assert_cmpuint (c[1], ==, 1000000);
00811   g_assert_cmpuint (c[2], ==, 1000);
00812   g_assert_cmpuint (c[3], ==, 25400);
00813   g_assert_cmpuint (c[4], ==, 12700);
00814   g_assert_cmpuint (c[5], ==, 67);
00815 }
00816 
00817 void
00818 pcb_printf_test_printf ()
00819 {
00820   Coord c = unit_to_coord (get_unit_struct ("nm"), 314);
00821   Coord d = unit_to_coord (get_unit_struct ("nm"), 218);
00822   Coord e = unit_to_coord (get_unit_struct ("mm"), 101);
00823   Coord f = unit_to_coord (get_unit_struct ("mil"), 3);
00824 
00825   g_assert_cmpstr (pcb_g_strdup_printf ("%mn", c), ==, "314");
00826   g_assert_cmpstr (pcb_g_strdup_printf ("%$mn", c), ==, "314 nm");
00827   g_assert_cmpstr (pcb_g_strdup_printf ("%mu", c), ==, "0.31");
00828   g_assert_cmpstr (pcb_g_strdup_printf ("%.3mu", c), ==, "0.314");
00829   g_assert_cmpstr (pcb_g_strdup_printf ("%$mu", c), ==, "0.31 um");
00830   g_assert_cmpstr (pcb_g_strdup_printf ("%$ml", c), ==, "0.01 mil");
00831   g_assert_cmpstr (pcb_g_strdup_printf ("%.5$ml", c), ==, "0.01236 mil");
00832 
00833   g_assert_cmpstr (pcb_g_strdup_printf ("%mn", e), ==, "101000000");
00834   g_assert_cmpstr (pcb_g_strdup_printf ("%$mn", e), ==, "101000000 nm");
00835   g_assert_cmpstr (pcb_g_strdup_printf ("%mu", e), ==, "101000.00");
00836   g_assert_cmpstr (pcb_g_strdup_printf ("%$mu", e), ==, "101000.00 um");
00837   g_assert_cmpstr (pcb_g_strdup_printf ("%ms", e), ==, "101.0000");
00838   g_assert_cmpstr (pcb_g_strdup_printf ("%$ms", e), ==, "101.0000 mm");
00839   g_assert_cmpstr (pcb_g_strdup_printf ("%mS", e), ==, "10.10000");
00840   g_assert_cmpstr (pcb_g_strdup_printf ("%$mS", e), ==, "10.10000 cm");
00841   g_assert_cmpstr (pcb_g_strdup_printf ("%$ml", e), ==, "3976.38 mil");
00842   g_assert_cmpstr (pcb_g_strdup_printf ("%.5$ml", e), ==, "3976.37795 mil");
00843 
00844   g_assert_cmpstr (pcb_g_strdup_printf ("%mn", f), ==, "76200");
00845   g_assert_cmpstr (pcb_g_strdup_printf ("%$mn", f), ==, "76200 nm");
00846   g_assert_cmpstr (pcb_g_strdup_printf ("%mm", f), ==, "0.0762");
00847   g_assert_cmpstr (pcb_g_strdup_printf ("%$mm", f), ==, "0.0762 mm");
00848   g_assert_cmpstr (pcb_g_strdup_printf ("%ms", f), ==, "3.00");
00849   g_assert_cmpstr (pcb_g_strdup_printf ("%$ms", f), ==, "3.00 mil");
00850   g_assert_cmpstr (pcb_g_strdup_printf ("%mS", f), ==, "3.00");
00851   g_assert_cmpstr (pcb_g_strdup_printf ("%$mS", f), ==, "3.00 mil");
00852 
00853   g_assert_cmpstr (pcb_g_strdup_printf ("%ms", c), ==, "0.0003");
00854   g_assert_cmpstr (pcb_g_strdup_printf ("%$ms", c), ==, "0.0003 mm");
00855   g_assert_cmpstr (pcb_g_strdup_printf ("%mS", c), ==, "314");
00856   g_assert_cmpstr (pcb_g_strdup_printf ("%$mS", c), ==, "314 nm");
00857 
00858   g_assert_cmpstr (pcb_g_strdup_printf ("%mD", c, d), ==, "(314, 218)");
00859   g_assert_cmpstr (pcb_g_strdup_printf ("%$mD", c, d), ==, "(314, 218) nm");
00860 
00861   g_assert_cmpstr (pcb_g_strdup_printf ("%`f", 7.2456), ==, "7.245600");
00862   g_assert_cmpstr (pcb_g_strdup_printf ("%`.2f", 7.2456), ==, "7.25");
00863 
00864   /* Some crashes noticed by Peter Clifton */
00865   /* specifiers in "wrong" order (should work fine) */
00866   g_assert_cmpstr (pcb_g_strdup_printf ("%$#mS", e), ==, "397638 cmil");
00867   /* invalid specifier (should passthrough to g_strdup_printf and output nothing) */
00868   g_assert_cmpstr (pcb_g_strdup_printf ("%#S", e), ==, "");
00869 }
00870 
00871 #endif