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
27 /*! \file <pcb-printf.c>
28 * \brief Implementation of printf wrapper to output pcb coords and angles
30 * For details of all supported specifiers, see the comment at the
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,
58 { 0, "m", NULL
, 'f', 0.001, METRIC
, ALLOW_M
, 5,
59 0.0005, 0.005, 0.025, 0.5, 2.5,
61 { 0, "cm", NULL
, 'e', 0.1, METRIC
, ALLOW_CM
, 5,
62 0.005, 0.05, 0.25, 5, 25,
64 { 0, "mm", NULL
, 'm', 1, METRIC
, ALLOW_MM
, 4,
65 0.005, 0.05, 0.25, 5, 25,
67 { 0, "um", NULL
, 'u', 1000, METRIC
, ALLOW_UM
, 2,
68 0.005, 0.05, 0.25, 5, 25,
70 { 0, "nm", NULL
, 'n', 1000000, METRIC
, ALLOW_NM
, 0,
71 5, 50, 2500, 5000, 25000,
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,
81 { 0, "in", NULL
, 'i', 0.001, IMPERIAL
, ALLOW_IN
, 5,
82 0.1, 1.0, 5.0, 25, 100,
84 { 0, "mil", NULL
, 'l', 1, IMPERIAL
, ALLOW_MIL
, 2,
85 0.1, 1.0, 10, 100, 1000,
87 { 0, "dmil", NULL
, 't', 10, IMPERIAL
, ALLOW_DMIL
, 0,
88 1, 10, 100, 1000, 10000,
90 { 0, "cmil", NULL
, 'c', 100, IMPERIAL
, ALLOW_CMIL
, 0,
91 1, 10, 100, 1000, 10000,
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
101 void initialize_units()
104 for (i
= 0; i
< N_UNITS
; ++i
)
107 Units
[i
].in_suffix
= _(Units
[i
].suffix
);
111 /* TABLE FORMAT | default | min | max
117 static Increments increments_metric
= {
119 MM_TO_COORD3 (0.1, 0.01, 1.0),
120 MM_TO_COORD3 (0.2, 0.01, 0.5),
121 MM_TO_COORD3 (0.1, 0.005, 0.5),
122 MM_TO_COORD3 (0.05, 0.005, 0.5)
124 static Increments increments_imperial
= {
126 MIL_TO_COORD3 (5, 1, 25),
127 MIL_TO_COORD3 (10, 1, 10),
128 MIL_TO_COORD3 (5, 0.5, 10),
129 MIL_TO_COORD3 (2, 0.5, 10)
132 /* \brief Obtain a unit object from its suffix
133 * \par Function Description
134 * Looks up a given suffix in the main units array. Internationalized
135 * unit suffixes are not supported, though pluralized units are, for
136 * backward-compatibility.
138 * \param [in] const_suffix The suffix to look up
140 * \return A const pointer to the Unit struct, or NULL if none was found
142 const Unit
*get_unit_struct (const char *const_suffix
)
146 /* Turn given suffix into something we can modify... */
147 char *m_suffix
= g_strdup (const_suffix
);
148 /* ...and store this in a pointer we can move. */
149 char *suffix
= m_suffix
;
151 /* Determine bounds */
152 while (isspace (*suffix
))
154 while (isalnum (suffix
[s_len
]))
157 /* Also understand plural suffixes: "inches", "mils" */
160 if (suffix
[s_len
- 2] == 'e' && suffix
[s_len
- 1] == 's')
161 suffix
[s_len
- 2] = 0;
162 else if (suffix
[s_len
- 1] == 's')
163 suffix
[s_len
- 1] = 0;
167 if (*suffix
&& s_len
> 0)
168 for (i
= 0; i
< N_UNITS
; ++i
)
169 if (strncmp (suffix
, Units
[i
].suffix
, s_len
) == 0 ||
170 strncmp (suffix
, Units
[i
].alias
[0], s_len
) == 0)
179 void copy_nonzero_increments (Increments
*dst
, const Increments
*src
)
181 if (src
->grid
>= dst
->grid_min
&& src
->grid
<= dst
->grid_max
)
182 dst
->grid
= src
->grid
;
183 if (src
->line
>= dst
->line_min
&& src
->line
<= dst
->line_max
)
184 dst
->line
= src
->line
;
185 if (src
->size
>= dst
->size_min
&& src
->size
<= dst
->size_max
)
186 dst
->size
= src
->size
;
187 if (src
->clear
>= dst
->clear_min
&& src
->clear
<= dst
->clear_max
)
188 dst
->clear
= src
->clear
;
192 /* \brief Returns the master unit list. This may not be modified. */
193 const Unit
*get_unit_list (void)
197 /* \brief Returns the length of the master unit list. */
198 int get_n_units (void)
203 /* \brief Obtain the increment values for a given family of units
204 * \par Function Description
206 * \param [in] family One of METRIC or IMPERIAL.
208 * \return A pointer to the appropriate increments structure.
210 Increments
*get_increments_struct (enum e_family family
)
215 return &increments_metric
;
217 return &increments_imperial
;
222 /* \brief Convert a pcb coord to the given unit
224 * \param [in] unit The unit to convert to
225 * \param [in] x The quantity to convert
227 * \return The converted measure
229 double coord_to_unit (const Unit
*unit
, Coord x
)
234 base
= unit
->family
== METRIC
237 return x
* unit
->scale_factor
* base
;
240 /* \brief Convert a given unit to pcb coords
242 * \param [in] unit The unit to convert from
243 * \param [in] x The quantity to convert
245 * \return The converted measure
247 Coord
unit_to_coord (const Unit
*unit
, double x
)
249 return x
/ coord_to_unit (unit
, 1);
252 static int min_sig_figs(double d
)
259 /* Normalize to x.xxxx... form */
261 while(d
>= 10) d
/= 10;
262 while(d
< 1) d
*= 10;
264 rv
= sprintf(buf
, "%g", d
);
268 /* \brief Internal coord-to-string converter for pcb-printf
269 * \par Function Description
270 * Converts a (group of) measurement(s) to a comma-deliminated
271 * string, with appropriate units. If more than one coord is
272 * given, the list is enclosed in parens to make the scope of
273 * the unit suffix clear.
275 * \param [in] coord Array of coords to convert
276 * \param [in] n_coords Number of coords in array
277 * \param [in] printf_spec printf sub-specifier to use with %f
278 * \param [in] e_allow Bitmap of units the function may use
279 * \param [in] suffix_type Whether to add a suffix
281 * \return A string containing the formatted coords. Must be freed with g_free.
283 static gchar
*CoordsToString(Coord coord
[], int n_coords
, const char *printf_spec
, enum e_allow allow
, enum e_suffix suffix_type
)
287 gchar filemode_buff
[G_ASCII_DTOSTR_BUF_SIZE
];
288 enum e_family family
;
293 value
= malloc (n_coords
* sizeof *value
);
294 buff
= g_string_new ("");
297 if (buff
== NULL
|| value
== NULL
)
301 if (printf_spec
== NULL
)
304 /* Check our freedom in choosing units */
305 if ((allow
& ALLOW_IMPERIAL
) == 0)
307 else if ((allow
& ALLOW_METRIC
) == 0)
314 for (i
= 0; i
< n_coords
; ++i
)
315 if(min_sig_figs(COORD_TO_MIL(coord
[i
])) < min_sig_figs(COORD_TO_MM(coord
[i
])))
320 if (imp_votes
> met_votes
)
327 for (i
= 0; i
< n_coords
; ++i
)
331 case METRIC
: value
[i
] = COORD_TO_MM (coord
[i
]); break;
332 case IMPERIAL
: value
[i
] = COORD_TO_MIL (coord
[i
]); break;
336 /* Determine scale factor -- find smallest unit that brings
337 * the whole group above unity */
338 for (n
= 0; n
< N_UNITS
; ++n
)
340 if ((Units
[n
].allow
& allow
) != 0 && (Units
[n
].family
== family
))
344 for (i
= 0; i
< n_coords
; ++i
)
345 if (fabs(value
[i
] * Units
[n
].scale_factor
) > 1)
347 if (n_above_one
== n_coords
)
351 /* If nothing worked, wind back to the smallest allowable unit */
356 } while ((Units
[n
].allow
& allow
) == 0 || Units
[n
].family
!= family
);
359 /* Apply scale factor */
360 suffix
= Units
[n
].suffix
;
361 for (i
= 0; i
< n_coords
; ++i
)
362 value
[i
] = value
[i
] * Units
[n
].scale_factor
;
364 /* Create sprintf specifier, using default_prec no precision is given */
366 while (printf_spec
[i
] == '%' || isdigit(printf_spec
[i
]) ||
367 printf_spec
[i
] == '-' || printf_spec
[i
] == '+' ||
368 printf_spec
[i
] == '#')
370 if (printf_spec
[i
] == '.')
371 printf_buff
= g_strdup_printf (", %sf", printf_spec
);
373 printf_buff
= g_strdup_printf (", %s.%df", printf_spec
, Units
[n
].default_prec
);
375 /* Actually sprintf the values in place
376 * (+ 2 skips the ", " for first value) */
378 g_string_append_c (buff
, '(');
379 if (suffix_type
== FILE_MODE
|| suffix_type
== FILE_MODE_NO_SUFFIX
)
381 g_ascii_formatd (filemode_buff
, sizeof filemode_buff
, printf_buff
+ 2, value
[0]);
382 g_string_append_printf (buff
, "%s", filemode_buff
);
385 g_string_append_printf (buff
, printf_buff
+ 2, value
[0]);
386 for (i
= 1; i
< n_coords
; ++i
)
388 if (suffix_type
== FILE_MODE
|| suffix_type
== FILE_MODE_NO_SUFFIX
)
390 g_ascii_formatd (filemode_buff
, sizeof filemode_buff
, printf_buff
, value
[i
]);
391 g_string_append_printf (buff
, "%s", filemode_buff
);
394 g_string_append_printf (buff
, printf_buff
, value
[i
]);
397 g_string_append_c (buff
, ')');
399 if (value
[0] != 0 || n_coords
> 1)
404 case FILE_MODE_NO_SUFFIX
:
407 g_string_append_printf (buff
, " %s", suffix
);
410 g_string_append_printf (buff
, "%s", suffix
);
415 g_free (printf_buff
);
417 /* Return just the gchar* part of our string */
418 return g_string_free (buff
, FALSE
);
421 /* \brief Main pcb-printf function
422 * \par Function Description
423 * This is a printf wrapper that accepts new format specifiers to
424 * output pcb coords as various units. See the comment at the top
425 * of pcb-printf.h for full details.
427 * \param [in] fmt Format specifier
428 * \param [in] args Arguments to specifier
430 * \return A formatted string. Must be freed with g_free.
432 gchar
*pcb_vprintf(const char *fmt
, va_list args
)
434 GString
*string
= g_string_new ("");
435 GString
*spec
= g_string_new ("");
437 enum e_allow mask
= ALLOW_ALL
;
439 if (string
== NULL
|| spec
== NULL
)
444 enum e_suffix suffix
= NO_SUFFIX
;
448 gchar
*unit_str
= NULL
;
449 const char *ext_unit
= "";
453 g_string_assign (spec
, "");
455 /* Get printf sub-specifiers */
456 g_string_append_c (spec
, *fmt
++);
457 while(isdigit(*fmt
) || *fmt
== '.' || *fmt
== ' ' || *fmt
== '*'
458 || *fmt
== '#' || *fmt
== 'l' || *fmt
== 'L'
459 || *fmt
== 'h' || *fmt
== '+' || *fmt
== '-')
463 g_string_append_printf (spec
, "%d", va_arg (args
, int));
467 g_string_append_c (spec
, *fmt
++);
469 /* Get our sub-specifiers */
472 mask
= ALLOW_CMIL
; /* This must be pcb's base unit */
477 suffix
= (suffix
== NO_SUFFIX
) ? SUFFIX
: FILE_MODE
;
482 suffix
= (suffix
== SUFFIX
) ? FILE_MODE
: FILE_MODE_NO_SUFFIX
;
485 /* Tack full specifier onto specifier */
487 g_string_append_c (spec
, *fmt
);
491 case 'o': case 'i': case 'd':
492 case 'u': case 'x': case 'X':
493 if(spec
->str
[1] == 'l')
495 if(spec
->str
[2] == 'l')
496 unit_str
= g_strdup_printf (spec
->str
, va_arg(args
, long long));
498 unit_str
= g_strdup_printf (spec
->str
, va_arg(args
, long));
502 unit_str
= g_strdup_printf (spec
->str
, va_arg(args
, int));
505 case 'e': case 'E': case 'f':
507 if (strchr (spec
->str
, '*'))
509 int prec
= va_arg(args
, int);
510 unit_str
= g_strdup_printf (spec
->str
, va_arg(args
, double), prec
);
513 unit_str
= g_strdup_printf (spec
->str
, va_arg(args
, double));
516 if(spec
->str
[1] == 'l' && sizeof(int) <= sizeof(wchar_t))
517 unit_str
= g_strdup_printf (spec
->str
, va_arg(args
, wchar_t));
519 unit_str
= g_strdup_printf (spec
->str
, va_arg(args
, int));
522 if(spec
->str
[0] == 'l')
523 unit_str
= g_strdup_printf (spec
->str
, va_arg(args
, wchar_t *));
525 unit_str
= g_strdup_printf (spec
->str
, va_arg(args
, char *));
528 /* Depending on gcc settings, this will probably break with
529 * some silly "can't put %n in writeable data space" message */
530 unit_str
= g_strdup_printf (spec
->str
, va_arg(args
, int *));
533 unit_str
= g_strdup_printf (spec
->str
, va_arg(args
, void *));
536 g_string_append_c (string
, '%');
542 ext_unit
= va_arg(args
, const char *);
543 if (*fmt
!= '+' && *fmt
!= 'a')
544 value
[0] = va_arg(args
, Coord
);
548 case 's': unit_str
= CoordsToString(value
, 1, spec
->str
, ALLOW_MM
| ALLOW_MIL
, suffix
); break;
549 case 'S': unit_str
= CoordsToString(value
, 1, spec
->str
, mask
& ALLOW_ALL
, suffix
); break;
550 case 'M': unit_str
= CoordsToString(value
, 1, spec
->str
, mask
& ALLOW_METRIC
, suffix
); break;
551 case 'L': unit_str
= CoordsToString(value
, 1, spec
->str
, mask
& ALLOW_IMPERIAL
, suffix
); break;
552 case 'r': unit_str
= CoordsToString(value
, 1, spec
->str
, ALLOW_READABLE
, FILE_MODE
); break;
553 /* All these fallthroughs are deliberate */
554 case '9': value
[count
++] = va_arg(args
, Coord
);
555 case '8': value
[count
++] = va_arg(args
, Coord
);
556 case '7': value
[count
++] = va_arg(args
, Coord
);
557 case '6': value
[count
++] = va_arg(args
, Coord
);
558 case '5': value
[count
++] = va_arg(args
, Coord
);
559 case '4': value
[count
++] = va_arg(args
, Coord
);
560 case '3': value
[count
++] = va_arg(args
, Coord
);
563 value
[count
++] = va_arg(args
, Coord
);
564 unit_str
= CoordsToString(value
, count
, spec
->str
, mask
& ALLOW_ALL
, suffix
);
567 value
[1] = va_arg(args
, Coord
);
568 unit_str
= CoordsToString(value
, 2, spec
->str
, ALLOW_MM
| ALLOW_MIL
, suffix
);
571 for (i
= 0; i
< N_UNITS
; ++i
)
572 if (strcmp (ext_unit
, Units
[i
].suffix
) == 0)
573 unit_str
= CoordsToString(value
, 1, spec
->str
, Units
[i
].allow
, suffix
);
574 if (unit_str
== NULL
)
575 unit_str
= CoordsToString(value
, 1, spec
->str
, mask
& ALLOW_ALL
, suffix
);
578 g_string_append (spec
, ".0f");
579 if (suffix
== SUFFIX
)
580 g_string_append (spec
, " deg");
581 unit_str
= g_strdup_printf (spec
->str
, (double) va_arg(args
, Angle
));
584 mask
= va_arg(args
, enum e_allow
);
587 for (i
= 0; i
< N_UNITS
; ++i
)
588 if (*fmt
== Units
[i
].printf_code
)
589 unit_str
= CoordsToString(value
, 1, spec
->str
, Units
[i
].allow
, suffix
);
590 if (unit_str
== NULL
)
591 unit_str
= CoordsToString(value
, 1, spec
->str
, ALLOW_ALL
, suffix
);
596 if (unit_str
!= NULL
)
598 g_string_append (string
, unit_str
);
603 g_string_append_c (string
, *fmt
);
606 g_string_free (spec
, TRUE
);
607 /* Return just the gchar* part of our string */
608 return g_string_free (string
, FALSE
);
612 /* \brief Wrapper for pcb_vprintf that outputs to a string
614 * \param [in] string Pointer to string to output into
615 * \param [in] fmt Format specifier
617 * \return The length of the written string
619 int pcb_sprintf(char *string
, const char *fmt
, ...)
626 tmp
= pcb_vprintf (fmt
, args
);
627 strcpy (string
, tmp
);
631 return strlen (string
);
634 /* \brief Wrapper for pcb_vprintf that outputs to a file
636 * \param [in] fh File to output to
637 * \param [in] fmt Format specifier
639 * \return The length of the written string
641 int pcb_fprintf(FILE *fh
, const char *fmt
, ...)
653 tmp
= pcb_vprintf (fmt
, args
);
654 rv
= fprintf (fh
, "%s", tmp
);
662 /* \brief Wrapper for pcb_vprintf that outputs to stdout
664 * \param [in] fmt Format specifier
666 * \return The length of the written string
668 int pcb_printf(const char *fmt
, ...)
676 tmp
= pcb_vprintf (fmt
, args
);
677 rv
= printf ("%s", tmp
);
684 /* \brief Wrapper for pcb_vprintf that outputs to a newly allocated string
686 * \param [in] fmt Format specifier
688 * \return The newly allocated string. Must be freed with g_free.
690 char *pcb_g_strdup_printf(const char *fmt
, ...)
696 tmp
= pcb_vprintf (fmt
, args
);