Cleanup r_string when leaving make_route_string().
[geda-pcb/pcjc2.git] / src / pcb-printf.c
blob4d45750423f53ac0917eac76c0bebfeffd9fe000
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 2011 Andrew Poelstra
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * Contact addresses for paper mail and Email:
22 * Andrew Poelstra, 16966 60A Ave, V3S 8X5 Surrey, BC, Canada
23 * asp11@sfu.ca
27 /*! \file <pcb-printf.c>
28 * \brief Implementation of printf wrapper to output pcb coords and angles
29 * \par Description
30 * For details of all supported specifiers, see the comment at the
31 * top of pcb-printf.h
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
38 #include "global.h"
40 #include "pcb-printf.h"
42 /* Helper macros for tables */
43 #define MM_TO_COORD3(a,b,c) MM_TO_COORD (a), MM_TO_COORD (b), MM_TO_COORD (c)
44 #define MIL_TO_COORD3(a,b,c) MIL_TO_COORD (a), MIL_TO_COORD (b), MIL_TO_COORD (c)
45 #define MM_TO_COORD5(a,b,c,d,e) MM_TO_COORD (a), MM_TO_COORD (b), MM_TO_COORD (c), \
46 MM_TO_COORD (d), MM_TO_COORD (e)
47 #define MIL_TO_COORD5(a,b,c,d,e) MIL_TO_COORD (a), MIL_TO_COORD (b), MIL_TO_COORD (c), \
48 MIL_TO_COORD (d), MIL_TO_COORD (e)
50 /* These should be kept in order of smallest scale_factor
51 * to largest -- the code uses this ordering when finding
52 * the best scale to use for a group of measures. */
54 static Unit Units[] = {
55 { 0, "km", NULL, 'k', 0.000001, METRIC, ALLOW_KM, 5,
56 0.00005, 0.0005, 0.0025, 0.05, 0.25,
57 { "" } },
58 { 0, "m", NULL, 'f', 0.001, METRIC, ALLOW_M, 5,
59 0.0005, 0.005, 0.025, 0.5, 2.5,
60 { "" } },
61 { 0, "cm", NULL, 'e', 0.1, METRIC, ALLOW_CM, 5,
62 0.005, 0.05, 0.25, 5, 25,
63 { "" } },
64 { 0, "mm", NULL, 'm', 1, METRIC, ALLOW_MM, 4,
65 0.005, 0.05, 0.25, 5, 25,
66 { "" } },
67 { 0, "um", NULL, 'u', 1000, METRIC, ALLOW_UM, 2,
68 0.005, 0.05, 0.25, 5, 25,
69 { "" } },
70 { 0, "nm", NULL, 'n', 1000000, METRIC, ALLOW_NM, 0,
71 5, 50, 2500, 5000, 25000,
72 { "" } },
73 /* Hack: Pixels get parsed like nanometers. If the value of the
74 * resulting integer is sufficiently small, the code interprets
75 * it a screen pixels. This affects rat thickness. */
76 { 0, "px", NULL, 'n', 1000000, METRIC, ALLOW_NM, 0,
77 5, 50, 2500, 5000, 25000,
78 { "" } },
81 { 0, "in", NULL, 'i', 0.001, IMPERIAL, ALLOW_IN, 5,
82 0.1, 1.0, 5.0, 25, 100,
83 { "inch" } },
84 { 0, "mil", NULL, 'l', 1, IMPERIAL, ALLOW_MIL, 2,
85 0.1, 1.0, 10, 100, 1000,
86 { "" } },
87 { 0, "dmil", NULL, 't', 10, IMPERIAL, ALLOW_DMIL, 0,
88 1, 10, 100, 1000, 10000,
89 { "" } },
90 { 0, "cmil", NULL, 'c', 100, IMPERIAL, ALLOW_CMIL, 0,
91 1, 10, 100, 1000, 10000,
92 { "pcb" } }
94 #define N_UNITS ((int) (sizeof Units / sizeof Units[0]))
95 /* \brief Initialize non-static data for pcb-printf
96 * \par Function Description
97 * Assigns each unit its index for quick access through the
98 * main units array, and internationalize the units for GUI
99 * display.
101 void initialize_units()
103 int i;
104 for (i = 0; i < N_UNITS; ++i)
106 Units[i].index = i;
107 Units[i].in_suffix = _(Units[i].suffix);
111 /* \brief Get/set a mask of units to use when saving .pcb files
112 * \par Function Description
113 * If passed 0, returns the current mask of units to use in a .pcb
114 * file; if passed anything else, replaces the current mask. This
115 * mask should only contain units which are readable by recent versions
116 * of pcb; currently this means only ALLOW_MIL and ALLOW_MM. (Versions
117 * prior to 20110703 nominally support other units, but in fact the scaling
118 * calculations are incorrect so the wrong values will be read! See commit
119 * 750a1c5 for more details.)
121 * \return the current mask.
123 enum e_allow set_allow_readable(enum e_allow new_mask)
125 static enum e_allow readable_mask = ALLOW_READABLE;
126 if (new_mask != 0)
127 readable_mask = new_mask;
128 return readable_mask;
132 /* TABLE FORMAT | default | min | max
133 * grid | | |
134 * size | | |
135 * line | | |
136 * clear | | |
138 static Increments increments_metric = {
139 "mm",
140 MM_TO_COORD3 (0.1, 0.01, 1.0),
141 MM_TO_COORD3 (0.2, 0.01, 0.5),
142 MM_TO_COORD3 (0.1, 0.005, 0.5),
143 MM_TO_COORD3 (0.05, 0.005, 0.5)
145 static Increments increments_imperial = {
146 "mil",
147 MIL_TO_COORD3 (5, 1, 25),
148 MIL_TO_COORD3 (10, 1, 10),
149 MIL_TO_COORD3 (5, 0.5, 10),
150 MIL_TO_COORD3 (2, 0.5, 10)
153 /* \brief Obtain a unit object from its suffix
154 * \par Function Description
155 * Looks up a given suffix in the main units array. Internationalized
156 * unit suffixes are not supported, though pluralized units are, for
157 * backward-compatibility.
159 * \param [in] const_suffix The suffix to look up
161 * \return A const pointer to the Unit struct, or NULL if none was found
163 const Unit *get_unit_struct (const char *const_suffix)
165 int i;
166 int s_len = 0;
167 /* Turn given suffix into something we can modify... */
168 char *m_suffix = g_strdup (const_suffix);
169 /* ...and store this in a pointer we can move. */
170 char *suffix = m_suffix;
172 /* Determine bounds */
173 while (isspace (*suffix))
174 ++suffix;
175 while (isalnum (suffix[s_len]))
176 ++s_len;
178 /* Also understand plural suffixes: "inches", "mils" */
179 if (s_len > 2)
181 if (suffix[s_len - 2] == 'e' && suffix[s_len - 1] == 's')
182 suffix[s_len - 2] = 0;
183 else if (suffix[s_len - 1] == 's')
184 suffix[s_len - 1] = 0;
187 /* Do lookup */
188 if (*suffix && s_len > 0)
189 for (i = 0; i < N_UNITS; ++i)
190 if (strncmp (suffix, Units[i].suffix, s_len) == 0 ||
191 strncmp (suffix, Units[i].alias[0], s_len) == 0)
193 g_free (m_suffix);
194 return &Units[i];
196 g_free (m_suffix);
197 return NULL;
200 void copy_nonzero_increments (Increments *dst, const Increments *src)
202 if (src->grid >= dst->grid_min && src->grid <= dst->grid_max)
203 dst->grid = src->grid;
204 if (src->line >= dst->line_min && src->line <= dst->line_max)
205 dst->line = src->line;
206 if (src->size >= dst->size_min && src->size <= dst->size_max)
207 dst->size = src->size;
208 if (src->clear >= dst->clear_min && src->clear <= dst->clear_max)
209 dst->clear = src->clear;
212 /* ACCESSORS */
213 /* \brief Returns the master unit list. This may not be modified. */
214 const Unit *get_unit_list (void)
216 return Units;
218 /* \brief Returns the length of the master unit list. */
219 int get_n_units (void)
221 return N_UNITS;
224 /* \brief Obtain the increment values for a given family of units
225 * \par Function Description
227 * \param [in] family One of METRIC or IMPERIAL.
229 * \return A pointer to the appropriate increments structure.
231 Increments *get_increments_struct (enum e_family family)
233 switch (family)
235 case METRIC:
236 return &increments_metric;
237 case IMPERIAL:
238 return &increments_imperial;
240 return NULL;
243 /* \brief Convert a pcb coord to the given unit
245 * \param [in] unit The unit to convert to
246 * \param [in] x The quantity to convert
248 * \return The converted measure
250 double coord_to_unit (const Unit *unit, Coord x)
252 double base;
253 if (unit == NULL)
254 return -1;
255 base = unit->family == METRIC
256 ? COORD_TO_MM (x)
257 : COORD_TO_MIL (x);
258 return unit->scale_factor * base;
261 /* \brief Convert a given unit to pcb coords
263 * \param [in] unit The unit to convert from
264 * \param [in] x The quantity to convert
266 * \return The converted measure
268 Coord unit_to_coord (const Unit *unit, double x)
270 double base;
271 if (unit == NULL)
272 return -1;
273 base = unit->family == METRIC
274 ? MM_TO_COORD (x)
275 : MIL_TO_COORD (x);
276 return DOUBLE_TO_COORD (base / unit->scale_factor);
279 static int min_sig_figs(double d)
281 char buf[50];
282 int rv;
284 if(d == 0) return 0;
286 /* Normalize to x.xxxx... form */
287 if(d < 0) d *= -1;
288 while(d >= 10) d /= 10;
289 while(d < 1) d *= 10;
291 rv = sprintf(buf, "%g", d);
292 return rv;
295 /* \brief Internal coord-to-string converter for pcb-printf
296 * \par Function Description
297 * Converts a (group of) measurement(s) to a comma-deliminated
298 * string, with appropriate units. If more than one coord is
299 * given, the list is enclosed in parens to make the scope of
300 * the unit suffix clear.
302 * \param [in] coord Array of coords to convert
303 * \param [in] n_coords Number of coords in array
304 * \param [in] printf_spec printf sub-specifier to use with %f
305 * \param [in] e_allow Bitmap of units the function may use
306 * \param [in] suffix_type Whether to add a suffix
308 * \return A string containing the formatted coords. Must be freed with g_free.
310 static gchar *CoordsToString(Coord coord[], int n_coords, const char *printf_spec, enum e_allow allow, enum e_suffix suffix_type)
312 GString *buff;
313 gchar *printf_buff;
314 gchar filemode_buff[G_ASCII_DTOSTR_BUF_SIZE];
315 enum e_family family;
316 double *value;
317 const char *suffix;
318 int i, n;
320 value = malloc (n_coords * sizeof *value);
321 buff = g_string_new ("");
323 /* Sanity checks */
324 if (buff == NULL || value == NULL)
325 return NULL;
326 if (allow == 0)
327 allow = ALLOW_ALL;
328 if (printf_spec == NULL)
329 printf_spec = "";
331 /* Check our freedom in choosing units */
332 if ((allow & ALLOW_IMPERIAL) == 0)
333 family = METRIC;
334 else if ((allow & ALLOW_METRIC) == 0)
335 family = IMPERIAL;
336 else
338 int met_votes = 0,
339 imp_votes = 0;
341 for (i = 0; i < n_coords; ++i)
342 if(min_sig_figs(COORD_TO_MIL(coord[i])) < min_sig_figs(COORD_TO_MM(coord[i])))
343 ++imp_votes;
344 else
345 ++met_votes;
347 if (imp_votes > met_votes)
348 family = IMPERIAL;
349 else
350 family = METRIC;
353 /* Set base unit */
354 for (i = 0; i < n_coords; ++i)
356 switch (family)
358 case METRIC: value[i] = COORD_TO_MM (coord[i]); break;
359 case IMPERIAL: value[i] = COORD_TO_MIL (coord[i]); break;
363 /* Determine scale factor -- find smallest unit that brings
364 * the whole group above unity */
365 for (n = 0; n < N_UNITS; ++n)
367 if ((Units[n].allow & allow) != 0 && (Units[n].family == family))
369 int n_above_one = 0;
371 for (i = 0; i < n_coords; ++i)
372 if (fabs(value[i] * Units[n].scale_factor) > 1)
373 ++n_above_one;
374 if (n_above_one == n_coords)
375 break;
378 /* If nothing worked, wind back to the smallest allowable unit */
379 if (n == N_UNITS)
381 do {
382 --n;
383 } while ((Units[n].allow & allow) == 0 || Units[n].family != family);
386 /* Apply scale factor */
387 suffix = Units[n].suffix;
388 for (i = 0; i < n_coords; ++i)
389 value[i] = value[i] * Units[n].scale_factor;
391 /* Create sprintf specifier, using default_prec no precision is given */
392 i = 0;
393 while (printf_spec[i] == '%' || isdigit(printf_spec[i]) ||
394 printf_spec[i] == '-' || printf_spec[i] == '+' ||
395 printf_spec[i] == '#')
396 ++i;
397 if (printf_spec[i] == '.')
398 printf_buff = g_strdup_printf (", %sf", printf_spec);
399 else
400 printf_buff = g_strdup_printf (", %s.%df", printf_spec, Units[n].default_prec);
402 /* Actually sprintf the values in place
403 * (+ 2 skips the ", " for first value) */
404 if (n_coords > 1)
405 g_string_append_c (buff, '(');
406 if (suffix_type == FILE_MODE || suffix_type == FILE_MODE_NO_SUFFIX)
408 g_ascii_formatd (filemode_buff, sizeof filemode_buff, printf_buff + 2, value[0]);
409 g_string_append_printf (buff, "%s", filemode_buff);
411 else
412 g_string_append_printf (buff, printf_buff + 2, value[0]);
413 for (i = 1; i < n_coords; ++i)
415 if (suffix_type == FILE_MODE || suffix_type == FILE_MODE_NO_SUFFIX)
417 g_ascii_formatd (filemode_buff, sizeof filemode_buff, printf_buff, value[i]);
418 g_string_append_printf (buff, "%s", filemode_buff);
420 else
421 g_string_append_printf (buff, printf_buff, value[i]);
423 if (n_coords > 1)
424 g_string_append_c (buff, ')');
425 /* Append suffix */
426 if (value[0] != 0 || n_coords > 1)
428 switch (suffix_type)
430 case NO_SUFFIX:
431 case FILE_MODE_NO_SUFFIX:
432 break;
433 case SUFFIX:
434 g_string_append_printf (buff, " %s", suffix);
435 break;
436 case FILE_MODE:
437 g_string_append_printf (buff, "%s", suffix);
438 break;
442 g_free (printf_buff);
443 free (value);
444 /* Return just the gchar* part of our string */
445 return g_string_free (buff, FALSE);
448 /* \brief Main pcb-printf function
449 * \par Function Description
450 * This is a printf wrapper that accepts new format specifiers to
451 * output pcb coords as various units. See the comment at the top
452 * of pcb-printf.h for full details.
454 * \param [in] fmt Format specifier
455 * \param [in] args Arguments to specifier
457 * \return A formatted string. Must be freed with g_free.
459 gchar *pcb_vprintf(const char *fmt, va_list args)
461 GString *string = g_string_new ("");
462 GString *spec = g_string_new ("");
464 enum e_allow mask = ALLOW_ALL;
466 if (string == NULL || spec == NULL)
467 return NULL;
469 while(*fmt)
471 enum e_suffix suffix = NO_SUFFIX;
473 if(*fmt == '%')
475 gchar *unit_str = NULL;
476 const char *ext_unit = "";
477 Coord value[10];
478 int count, i, done;
480 g_string_assign (spec, "%");
482 done = 0;
483 while ( ! done && fmt++ && *fmt)
485 switch (*fmt)
487 /* Our sub-specifiers */
488 case '#':
489 mask = ALLOW_CMIL; /* This must be pcb's base unit */
490 break;
491 case '$':
492 suffix = (suffix == NO_SUFFIX) ? SUFFIX : FILE_MODE;
493 break;
494 case '`':
495 suffix = (suffix == SUFFIX) ? FILE_MODE : FILE_MODE_NO_SUFFIX;
496 break;
497 /* Printf sub-specifiers */
498 case '*':
499 g_string_append_printf (spec, "%d", va_arg (args, int));
500 break;
501 case '.':
502 case ' ':
503 //case '#': (duplicate)
504 case 'l':
505 case 'L':
506 case 'h':
507 case '+':
508 case '-':
509 case '0':
510 case '1':
511 case '2':
512 case '3':
513 case '4':
514 case '5':
515 case '6':
516 case '7':
517 case '8':
518 case '9':
519 g_string_append_c (spec, *fmt);
520 break;
521 default:
522 done = 1;
526 /* Tack full specifier onto specifier */
527 if (*fmt != 'm')
528 g_string_append_c (spec, *fmt);
529 switch(*fmt)
531 /* Printf specs */
532 case 'o': case 'i': case 'd':
533 case 'u': case 'x': case 'X':
534 if(strchr (spec->str, 'l'))
536 if(strchr (spec->str, 'l') != strrchr (spec->str, 'l'))
537 unit_str = g_strdup_printf (spec->str, va_arg(args, long long));
538 else
539 unit_str = g_strdup_printf (spec->str, va_arg(args, long));
541 else
543 unit_str = g_strdup_printf (spec->str, va_arg(args, int));
545 break;
546 case 'e': case 'E':
547 case 'f': case 'F':
548 case 'g': case 'G':
549 if(suffix == FILE_MODE || suffix == FILE_MODE_NO_SUFFIX)
551 gchar buffer[128];
552 g_ascii_formatd (buffer, 128, spec->str, va_arg(args, double));
553 unit_str = g_strdup_printf ("%s", buffer);
555 else
556 unit_str = g_strdup_printf (spec->str, va_arg(args, double));
557 break;
558 case 'c':
559 if(strchr (spec->str, 'l') && sizeof(int) <= sizeof(wchar_t))
560 unit_str = g_strdup_printf (spec->str, va_arg(args, wchar_t));
561 else
562 unit_str = g_strdup_printf (spec->str, va_arg(args, int));
563 break;
564 case 's':
565 if(strchr (spec->str, 'l'))
566 unit_str = g_strdup_printf (spec->str, va_arg(args, wchar_t *));
567 else
568 unit_str = g_strdup_printf (spec->str, va_arg(args, char *));
569 break;
570 case 'n':
571 /* Depending on gcc settings, this will probably break with
572 * some silly "can't put %n in writeable data space" message */
573 unit_str = g_strdup_printf (spec->str, va_arg(args, int *));
574 break;
575 case 'p':
576 unit_str = g_strdup_printf (spec->str, va_arg(args, void *));
577 break;
578 case '%':
579 g_string_append_c (string, '%');
580 break;
581 /* Our specs */
582 case 'm':
583 ++fmt;
584 if (*fmt == '*')
585 ext_unit = va_arg(args, const char *);
586 if (*fmt != '+' && *fmt != 'a')
587 value[0] = va_arg(args, Coord);
588 count = 1;
589 switch(*fmt)
591 case 's': unit_str = CoordsToString(value, 1, spec->str, ALLOW_MM | ALLOW_MIL, suffix); break;
592 case 'S': unit_str = CoordsToString(value, 1, spec->str, mask & ALLOW_ALL, suffix); break;
593 case 'M': unit_str = CoordsToString(value, 1, spec->str, mask & ALLOW_METRIC, suffix); break;
594 case 'L': unit_str = CoordsToString(value, 1, spec->str, mask & ALLOW_IMPERIAL, suffix); break;
595 case 'r': unit_str = CoordsToString(value, 1, spec->str, set_allow_readable(0), FILE_MODE); break;
596 /* All these fallthroughs are deliberate */
597 case '9': value[count++] = va_arg(args, Coord);
598 case '8': value[count++] = va_arg(args, Coord);
599 case '7': value[count++] = va_arg(args, Coord);
600 case '6': value[count++] = va_arg(args, Coord);
601 case '5': value[count++] = va_arg(args, Coord);
602 case '4': value[count++] = va_arg(args, Coord);
603 case '3': value[count++] = va_arg(args, Coord);
604 case '2':
605 case 'D':
606 value[count++] = va_arg(args, Coord);
607 unit_str = CoordsToString(value, count, spec->str, mask & ALLOW_ALL, suffix);
608 break;
609 case 'd':
610 value[1] = va_arg(args, Coord);
611 unit_str = CoordsToString(value, 2, spec->str, ALLOW_MM | ALLOW_MIL, suffix);
612 break;
613 case '*':
614 for (i = 0; i < N_UNITS; ++i)
615 if (strcmp (ext_unit, Units[i].suffix) == 0)
616 unit_str = CoordsToString(value, 1, spec->str, Units[i].allow, suffix);
617 if (unit_str == NULL)
618 unit_str = CoordsToString(value, 1, spec->str, mask & ALLOW_ALL, suffix);
619 break;
620 case 'a':
621 g_string_append (spec, ".0f");
622 if (suffix == SUFFIX)
623 g_string_append (spec, " deg");
624 unit_str = g_strdup_printf (spec->str, (double) va_arg(args, Angle));
625 break;
626 case '+':
627 mask = va_arg(args, enum e_allow);
628 break;
629 default:
630 for (i = 0; i < N_UNITS; ++i)
631 if (*fmt == Units[i].printf_code)
632 unit_str = CoordsToString(value, 1, spec->str, Units[i].allow, suffix);
633 if (unit_str == NULL)
634 unit_str = CoordsToString(value, 1, spec->str, ALLOW_ALL, suffix);
635 break;
637 break;
639 if (unit_str != NULL)
641 g_string_append (string, unit_str);
642 g_free (unit_str);
645 else
646 g_string_append_c (string, *fmt);
647 ++fmt;
649 g_string_free (spec, TRUE);
650 /* Return just the gchar* part of our string */
651 return g_string_free (string, FALSE);
656 * \brief Wrapper for pcb_vprintf that outputs to a string.
658 * \param [in] string Pointer to string to output into.
660 * \param [in] size Maximum length of this string, including the
661 * terminating null byte.
663 * \param [in] fmt Format specifier.
665 * \return The length of the written string. In case the string was truncated
666 * due to the size limit, it's the length of the string which would
667 * have been written if enough space had been available.
669 * The returned string is guaranteed to be null terminated, even if truncated.
671 int pcb_snprintf(char *string, size_t size, const char *fmt, ...)
673 gchar *tmp;
674 gsize length;
676 va_list args;
677 va_start(args, fmt);
679 tmp = pcb_vprintf (fmt, args);
680 length = strlen (tmp);
681 strncpy (string, tmp, size);
682 string[size - 1] = '\0';
684 g_free (tmp);
685 va_end(args);
687 return length;
690 /* \brief Wrapper for pcb_vprintf that outputs to a file
692 * \param [in] fh File to output to
693 * \param [in] fmt Format specifier
695 * \return The length of the written string
697 int pcb_fprintf(FILE *fh, const char *fmt, ...)
699 int rv;
700 gchar *tmp;
702 va_list args;
703 va_start(args, fmt);
705 if (fh == NULL)
706 rv = -1;
707 else
709 tmp = pcb_vprintf (fmt, args);
710 rv = fprintf (fh, "%s", tmp);
711 g_free (tmp);
714 va_end(args);
715 return rv;
718 /* \brief Wrapper for pcb_vprintf that outputs to stdout
720 * \param [in] fmt Format specifier
722 * \return The length of the written string
724 int pcb_printf(const char *fmt, ...)
726 int rv;
727 gchar *tmp;
729 va_list args;
730 va_start(args, fmt);
732 tmp = pcb_vprintf (fmt, args);
733 rv = printf ("%s", tmp);
734 g_free (tmp);
736 va_end(args);
737 return rv;
740 /* \brief Wrapper for pcb_vprintf that outputs to a newly allocated string
742 * \param [in] fmt Format specifier
744 * \return The newly allocated string. Must be freed with g_free.
746 char *pcb_g_strdup_printf(const char *fmt, ...)
748 gchar *tmp;
750 va_list args;
751 va_start(args, fmt);
752 tmp = pcb_vprintf (fmt, args);
753 va_end(args);
754 return tmp;
757 #ifdef PCB_UNIT_TEST
758 void
759 pcb_printf_register_tests ()
761 g_test_add_func ("/pcb-printf/test-unit", pcb_printf_test_unit);
762 g_test_add_func ("/pcb-printf/test-printf", pcb_printf_test_printf);
765 void
766 pcb_printf_test_unit ()
768 Coord c[] = {
769 unit_to_coord (get_unit_struct ("m"), 1.0),
770 unit_to_coord (get_unit_struct ("mm"), 1.0),
771 unit_to_coord (get_unit_struct ("um"), 1.0),
772 unit_to_coord (get_unit_struct ("mil"), 1.0),
773 unit_to_coord (get_unit_struct ("mil"), 0.5),
774 unit_to_coord (get_unit_struct ("nm"), 67)
777 /* Loop unrolled for ease of pinpointing failure */
778 g_assert (get_unit_struct ("m") != NULL);
780 g_assert_cmpuint (c[0], ==, 1000000000);
781 g_assert_cmpuint (c[1], ==, 1000000);
782 g_assert_cmpuint (c[2], ==, 1000);
783 g_assert_cmpuint (c[3], ==, 25400);
784 g_assert_cmpuint (c[4], ==, 12700);
785 g_assert_cmpuint (c[5], ==, 67);
788 void
789 pcb_printf_test_printf ()
791 Coord c = unit_to_coord (get_unit_struct ("nm"), 314);
792 Coord d = unit_to_coord (get_unit_struct ("nm"), 218);
793 Coord e = unit_to_coord (get_unit_struct ("mm"), 101);
794 Coord f = unit_to_coord (get_unit_struct ("mil"), 3);
796 g_assert_cmpstr (pcb_g_strdup_printf ("%mn", c), ==, "314");
797 g_assert_cmpstr (pcb_g_strdup_printf ("%$mn", c), ==, "314 nm");
798 g_assert_cmpstr (pcb_g_strdup_printf ("%mu", c), ==, "0.31");
799 g_assert_cmpstr (pcb_g_strdup_printf ("%.3mu", c), ==, "0.314");
800 g_assert_cmpstr (pcb_g_strdup_printf ("%$mu", c), ==, "0.31 um");
801 g_assert_cmpstr (pcb_g_strdup_printf ("%$ml", c), ==, "0.01 mil");
802 g_assert_cmpstr (pcb_g_strdup_printf ("%.5$ml", c), ==, "0.01236 mil");
804 g_assert_cmpstr (pcb_g_strdup_printf ("%mn", e), ==, "101000000");
805 g_assert_cmpstr (pcb_g_strdup_printf ("%$mn", e), ==, "101000000 nm");
806 g_assert_cmpstr (pcb_g_strdup_printf ("%mu", e), ==, "101000.00");
807 g_assert_cmpstr (pcb_g_strdup_printf ("%$mu", e), ==, "101000.00 um");
808 g_assert_cmpstr (pcb_g_strdup_printf ("%ms", e), ==, "101.0000");
809 g_assert_cmpstr (pcb_g_strdup_printf ("%$ms", e), ==, "101.0000 mm");
810 g_assert_cmpstr (pcb_g_strdup_printf ("%mS", e), ==, "10.10000");
811 g_assert_cmpstr (pcb_g_strdup_printf ("%$mS", e), ==, "10.10000 cm");
812 g_assert_cmpstr (pcb_g_strdup_printf ("%$ml", e), ==, "3976.38 mil");
813 g_assert_cmpstr (pcb_g_strdup_printf ("%.5$ml", e), ==, "3976.37795 mil");
815 g_assert_cmpstr (pcb_g_strdup_printf ("%mn", f), ==, "76200");
816 g_assert_cmpstr (pcb_g_strdup_printf ("%$mn", f), ==, "76200 nm");
817 g_assert_cmpstr (pcb_g_strdup_printf ("%mm", f), ==, "0.0762");
818 g_assert_cmpstr (pcb_g_strdup_printf ("%$mm", f), ==, "0.0762 mm");
819 g_assert_cmpstr (pcb_g_strdup_printf ("%ms", f), ==, "3.00");
820 g_assert_cmpstr (pcb_g_strdup_printf ("%$ms", f), ==, "3.00 mil");
821 g_assert_cmpstr (pcb_g_strdup_printf ("%mS", f), ==, "3.00");
822 g_assert_cmpstr (pcb_g_strdup_printf ("%$mS", f), ==, "3.00 mil");
824 g_assert_cmpstr (pcb_g_strdup_printf ("%ms", c), ==, "0.0003");
825 g_assert_cmpstr (pcb_g_strdup_printf ("%$ms", c), ==, "0.0003 mm");
826 g_assert_cmpstr (pcb_g_strdup_printf ("%mS", c), ==, "314");
827 g_assert_cmpstr (pcb_g_strdup_printf ("%$mS", c), ==, "314 nm");
829 g_assert_cmpstr (pcb_g_strdup_printf ("%mD", c, d), ==, "(314, 218)");
830 g_assert_cmpstr (pcb_g_strdup_printf ("%$mD", c, d), ==, "(314, 218) nm");
832 g_assert_cmpstr (pcb_g_strdup_printf ("%`f", 7.2456), ==, "7.245600");
833 g_assert_cmpstr (pcb_g_strdup_printf ("%`.2f", 7.2456), ==, "7.25");
835 /* Some crashes noticed by Peter Clifton */
836 /* specifiers in "wrong" order (should work fine) */
837 g_assert_cmpstr (pcb_g_strdup_printf ("%$#mS", e), ==, "397638 cmil");
838 /* invalid specifier (should passthrough to g_strdup_printf and output nothing) */
839 g_assert_cmpstr (pcb_g_strdup_printf ("%#S", e), ==, "");
842 #endif