GRASS GIS 8 Programmer's Manual 8.2.1(2023)-exported
aprintf.c
Go to the documentation of this file.
1/*!
2 * \file lib/gis/aprintf.c
3 *
4 * \brief GIS Library - Print functions for aligning wide characters.
5 *
6 * Extracted from the aligned printf C library (libaprintf under GPL v3+) by
7 * Huidae Cho.
8 *
9 * (C) 2020 by the GRASS Development Team
10 *
11 * This program is free software under the GNU General Public License
12 * (>=v2). Read the file COPYING that comes with GRASS for details.
13 *
14 * \author Huidae Cho
15 *
16 * \date 2020
17 */
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <stdarg.h>
23
24#include <grass/gis.h>
25#include <grass/glocale.h>
26
27/* printf(3) man page */
28#define CONVS "diouxXeEfFgGaAcsCSpnm%"
29
30/* % + flags + width + precision + length + conversion + NULL */
31#define SPEC_BUF_SIZE 16
32
33struct options
34{
35 FILE *stream;
36 char *str, *_str;
37 size_t size, _size;
38};
39
40static int count_wide_chars(const char *);
41static int count_wide_chars_in_cols(const char *, int, int *);
42static int ovprintf(struct options *, const char *, va_list);
43static int oprintf(struct options *, const char *, ...);
44static int oaprintf(struct options *, const char *, va_list);
45
46/*!
47 * \brief Count the number of wide characters in a string.
48 *
49 * \param[in] str input string
50 * \return number of wide characters in str
51 */
52static int count_wide_chars(const char *str)
53{
54 int nwchars = 0, lead = 0;
55
56 while (*str)
57 /* if the first two bits are 10 (0x80 = 1000 0000), this byte is
58 * following a previous multi-byte character */
59 if ((*str++ & 0xc0) != 0x80)
60 lead = 1;
61 else if (lead) {
62 /* only count the second byte of a multi-byte character */
63 lead = 0;
64 nwchars++;
65 }
66
67 return nwchars;
68}
69
70/*!
71 * \brief Count the numbers of wide characters and bytes in a string in a
72 * number of columns.
73 *
74 * \param[in] str input string
75 * \param[in] ncols number of columns
76 * \param[out] nbytes number of bytes (NULL for not counting)
77 * \return number of wide characters in str
78 */
79static int count_wide_chars_in_cols(const char *str, int ncols, int *nbytes)
80{
81 const char *p = str - 1;
82 int lead = 0, nwchars = 0;
83
84 /* count the numbers of wide characters and bytes in one loop */
85 while (ncols >= 0 && *++p)
86 if ((*p & 0xc0) != 0x80) {
87 /* a single-byte character or the leading byte of a multi-byte
88 * character; don't count it */
89 lead = 1;
90 ncols--;
91 } else if (lead) {
92 /* only count the second byte of a multi-byte character; don't
93 * consume more than two columns (leading and second bytes) */
94 lead = 0;
95 ncols--;
96 nwchars++;
97 }
98
99 /* if the current byte after ncols is still part of a multi-byte character,
100 * trash it because it's not a full wide character */
101 if ((*p & 0xc0) == 0x80)
102 nwchars--;
103
104 /* see how many bytes we have advanced */
105 *nbytes = p - str;
106
107 return nwchars;
108}
109
110/*!
111 * \brief Branch into vprintf(), vfprintf(), or vsprintf() depending on passed
112 * options.
113 *
114 * \param[in] opts options for branching
115 * \param[in] format string format
116 * \param[in] ap variable argument list for the format string
117 * \return number of bytes printed or fatal error on error
118 */
119static int ovprintf(struct options *opts, const char *format, va_list ap)
120{
121 int nbytes;
122
123 if (opts == NULL || (opts->stream == NULL && opts->_str == NULL))
124 nbytes = vprintf(format, ap);
125 else if (opts->stream)
126 nbytes = vfprintf(opts->stream, format, ap);
127 else {
128 if ((long int)opts->size >= 0) {
129 /* snprintf(str, 0, ...) does not alter str */
130 nbytes = vsnprintf(opts->_str, opts->_size, format, ap);
131 opts->_size -= nbytes;
132 } else
133 /* snprintf(str, negative, ...) is equivalent to snprintf(str, ...)
134 * because size_t is unsigned */
135 nbytes = vsprintf(opts->_str, format, ap);
136 opts->_str += nbytes;
137 }
138
139 if (nbytes < 0)
140 G_fatal_error(_("Failed to print %s"), format);
141
142 return nbytes;
143}
144
145/*!
146 * \brief Invoke ovprintf() for branching into different *printf() functions.
147 *
148 * \param[in] opts options for branching
149 * \param[in] format string format
150 * \param[in] ... arguments for the format string
151 * \return number of bytes printed or fatal error on error
152 */
153static int oprintf(struct options *opts, const char *format, ...)
154{
155 va_list ap;
156 int nbytes;
157
158 va_start(ap, format);
159 nbytes = ovprintf(opts, format, ap);
160 va_end(ap);
161
162 return nbytes;
163}
164
165/*!
166 * \brief Core function for aligning wide characters with Latin characters
167 * using %s specifiers. G_aprintf(), G_faprintf(), and G_saprintf() wrap around
168 * this function to implement printf(), fprintf(), and sprintf() counterparts,
169 * respectively.
170 *
171 * \param[in] opts options for branching
172 * \param[in] format string format
173 * \param[in] ap variable argument list for the format string
174 * \return number of bytes printed or fatal error on error
175 */
176static int oaprintf(struct options *opts, const char *format, va_list ap)
177{
178 char *fmt, *asis, *p, spec[SPEC_BUF_SIZE];
179 int nbytes = 0;
180
181 /* make a copy so we can temporarily change the format string */
182 p = asis = fmt = (char *)G_malloc(strlen(format) + 1);
183 strcpy(fmt, format);
184
185 while (*p) {
186 if (*p == '%') {
187 char *q = p, *p_spec = spec;
188
189 /* print the string before this specifier */
190 *p = 0;
191 nbytes += oprintf(opts, asis);
192 *p = '%';
193
194 /* skip % */
195 while (*++q) {
196 char *c = CONVS - 1;
197
198 while (*++c && *q != *c);
199 if (*c) {
200 va_list ap_copy;
201 char tmp;
202
203 /* copy ap for ovprintf() */
204 va_copy(ap_copy, ap);
205
206 /* found a conversion specifier */
207 if (*c == 's') {
208 /* if this is a string specifier */
209 int width = -1, prec = -1, use_ovprintf = 1;
210 char *p_tmp, *s;
211
212 *p_spec = 0;
213 p_spec = spec;
214 if (*p_spec == '-')
215 /* alignment */
216 p_spec++;
217 if (*p_spec == '*') {
218 /* read width from next argument */
219 width = va_arg(ap, int);
220 p_spec++;
221 } else if (*p_spec >= '0' && *p_spec <= '9') {
222 /* read width */
223 p_tmp = p_spec;
224 while (*p_spec >= '0' && *p_spec <= '9')
225 p_spec++;
226 tmp = *p_spec;
227 *p_spec = 0;
228 width = atoi(p_tmp);
229 *p_spec = tmp;
230 }
231 if (*p_spec == '.') {
232 /* precision */
233 p_spec++;
234 if (*p_spec == '*') {
235 /* read precision from next argument */
236 prec = va_arg(ap, int);
237 p_spec++;
238 } else if (*p_spec >= '0' && *p_spec <= '9') {
239 /* read precision */
240 p_tmp = p_spec;
241 while (*p_spec >= '0' && *p_spec <= '9')
242 p_spec++;
243 tmp = *p_spec;
244 *p_spec = 0;
245 prec = atoi(p_tmp);
246 *p_spec = tmp;
247 }
248 }
249 if (*p_spec) {
250 /* illegal string specifier? */
251 va_end(ap_copy);
252 *(q + 1) = 0;
254 _("Failed to parse string specifier: %s"),
255 p);
256 }
257
258 s = va_arg(ap, char *);
259 if (width > 0) {
260 /* if width is specified */
261 int wcount = count_wide_chars(s);
262
263 if (wcount) {
264 /* if there are wide characters */
265 if (prec > 0)
266 width += count_wide_chars_in_cols(s, prec,
267 &prec);
268 else if (prec < 0)
269 width += wcount;
270 p_spec = spec;
271 p_spec += sprintf(p_spec, "%%%s%d",
272 spec[0] == '-' ? "-" : "", width);
273 if (prec >= 0)
274 p_spec += sprintf(p_spec, ".%d", prec);
275 *p_spec++ = 's';
276 *p_spec = 0;
277 nbytes += oprintf(opts, spec, s);
278 use_ovprintf = 0;
279 }
280 /* else use ovprintf() as much as possible */
281 }
282 /* else use ovprintf() as much as possible */
283 if (use_ovprintf) {
284 tmp = *(q + 1);
285 *(q + 1) = 0;
286 nbytes += ovprintf(opts, p, ap_copy);
287 *(q + 1) = tmp;
288 }
289 } else {
290 /* else use ovprintf() for non-string specifiers */
291 tmp = *(q + 1);
292 *(q + 1) = 0;
293 nbytes += ovprintf(opts, p, ap_copy);
294 *(q + 1) = tmp;
295
296 /* once ap is passed to another function that calls
297 * va_arg() on it, its value becomes undefined
298 * (printf(3) man page) or indeterminate
299 * (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
300 * section 7.15 paragraph 3) after the callee function
301 * returns; simply passing ap to ovprintf() works on
302 * Linux, but it doesn't on MinGW on Windows; pass its
303 * copy and skip an argument manually; argument types
304 * from printf(3) man page */
305 switch (*c) {
306 case 'd':
307 case 'i':
308 case 'o':
309 case 'u':
310 case 'x':
311 case 'X':
312 case 'c':
313 case 'C':
314 case 'S':
315 va_arg(ap, int);
316 break;
317 case 'e':
318 case 'E':
319 case 'f':
320 case 'F':
321 case 'g':
322 case 'G':
323 case 'a':
324 case 'A':
325 va_arg(ap, double);
326 break;
327 case 'p':
328 va_arg(ap, void *);
329 break;
330 case 'n':
331 va_arg(ap, int *);
332 break;
333 /* otherwise, no argument is required for m% */
334 }
335 }
336 va_end(ap_copy);
337 break;
338 } else if (p_spec - spec < SPEC_BUF_SIZE - 2)
339 /* 2 reserved for % and NULL */
340 *p_spec++ = *q;
341 else
343 _("Format specifier exceeds the buffer size (%d)"),
345 }
346 asis = (p = q) + 1;
347 }
348 p++;
349 }
350
351 /* print the remaining string */
352 *p = 0;
353 nbytes += oprintf(opts, asis);
354 *p = '%';
355
356 return nbytes;
357}
358
359/*!
360 * \brief vprintf() version of G_aprintf(). See G_aprintf() for more details.
361 *
362 * \param[in] format string format
363 * \param[in] ap variable argument list for the format string
364 * \return number of bytes printed or fatal error on error
365 */
366int G_vaprintf(const char *format, va_list ap)
367{
368 return oaprintf(NULL, format, ap);
369}
370
371/*!
372 * \brief vfprintf() version of G_aprintf(). See G_aprintf() for more details.
373 *
374 * \param[in] stream file pointer
375 * \param[in] format string format
376 * \param[in] ap variable argument list for the format string
377 * \return number of bytes printed or fatal error on error
378 */
379int G_vfaprintf(FILE *stream, const char *format, va_list ap)
380{
381 struct options opts;
382
383 opts.stream = stream;
384 opts.str = NULL;
385 opts.size = -1;
386
387 return oaprintf(&opts, format, ap);
388}
389
390/*!
391 * \brief vsprintf() version of G_aprintf(). See G_aprintf() for more details.
392 *
393 * \param[in] str string buffer
394 * \param[in] format string format
395 * \param[in] ap variable argument list for the format string
396 * \return number of bytes printed or fatal error on error
397 */
398int G_vsaprintf(char *str, const char *format, va_list ap)
399{
400 struct options opts;
401
402 opts.stream = NULL;
403 opts.str = opts._str = str;
404 opts.size = -1;
405
406 return oaprintf(&opts, format, ap);
407}
408
409/*!
410 * \brief vsnprintf() version of G_aprintf(). See G_aprintf() for more details.
411 *
412 * \param[in] str string buffer
413 * \param[in] size string buffer size
414 * \param[in] format string format
415 * \param[in] ap variable argument list for the format string
416 * \return number of bytes that would be printed if size was big enough or
417 * fatal error on error
418 */
419int G_vsnaprintf(char *str, size_t size, const char *format, va_list ap)
420{
421 struct options opts;
422
423 opts.stream = NULL;
424 opts.str = opts._str = str;
425 opts.size = opts._size = size;
426
427 return oaprintf(&opts, format, ap);
428}
429
430/*!
431 * \brief Adjust the width of string specifiers to the display space instead of
432 * the number of bytes for wide characters and print them formatted using the
433 * adjusted display width.
434 *
435 * compare
436 * printf("%10s|\n%10s|\n", "ABCD", "가나");
437-----------
438 ABCD|
439 가나|
440-----------
441 * and
442 * G_aprintf("%10s|\n%10s|\n", "ABCD", "가나");
443-----------
444 ABCD|
445 가나|
446-----------
447 *
448 * \param[in] format string format
449 * \param[in] ... arguments for the format string
450 * \return number of bytes printed or fatal error on error
451 */
452int G_aprintf(const char *format, ...)
453{
454 va_list ap;
455 int nbytes;
456
457 va_start(ap, format);
458 nbytes = G_vaprintf(format, ap);
459 va_end(ap);
460
461 return nbytes;
462}
463
464/*!
465 * \brief fprintf() version of G_aprintf(). See G_aprintf() for more details.
466 *
467 * \param[in] stream file pointer
468 * \param[in] format string format
469 * \param[in] ... arguments for the format string
470 * \return number of bytes printed or fatal error on error
471 */
472int G_faprintf(FILE *stream, const char *format, ...)
473{
474 va_list ap;
475 int nbytes;
476
477 va_start(ap, format);
478 nbytes = G_vfaprintf(stream, format, ap);
479 va_end(ap);
480
481 return nbytes;
482}
483
484/*!
485 * \brief sprintf() version of G_aprintf(). See G_aprintf() for more details.
486 *
487 * \param[in] str string buffer
488 * \param[in] format string format
489 * \param[in] ... arguments for the format string
490 * \return number of bytes printed or fatal error on error
491 */
492int G_saprintf(char *str, const char *format, ...)
493{
494 va_list ap;
495 int nbytes;
496
497 va_start(ap, format);
498 nbytes = G_vsaprintf(str, format, ap);
499 va_end(ap);
500
501 return nbytes;
502}
503
504/*!
505 * \brief snprintf() version of G_aprintf(). See G_aprintf() for more details.
506 *
507 * \param[in] str string buffer
508 * \param[in] size string buffer size
509 * \param[in] format string format
510 * \param[in] ... arguments for the format string
511 * \return number of bytes that would be printed if size was big enough or
512 * fatal error on error
513 */
514int G_snaprintf(char *str, size_t size, const char *format, ...)
515{
516 va_list ap;
517 int nbytes;
518
519 va_start(ap, format);
520 nbytes = G_vsnaprintf(str, size, format, ap);
521 va_end(ap);
522
523 return nbytes;
524}
int G_snaprintf(char *str, size_t size, const char *format,...)
snprintf() version of G_aprintf(). See G_aprintf() for more details.
Definition: aprintf.c:514
int G_aprintf(const char *format,...)
Adjust the width of string specifiers to the display space instead of the number of bytes for wide ch...
Definition: aprintf.c:452
#define CONVS
Definition: aprintf.c:28
int G_saprintf(char *str, const char *format,...)
sprintf() version of G_aprintf(). See G_aprintf() for more details.
Definition: aprintf.c:492
int G_vfaprintf(FILE *stream, const char *format, va_list ap)
vfprintf() version of G_aprintf(). See G_aprintf() for more details.
Definition: aprintf.c:379
#define SPEC_BUF_SIZE
Definition: aprintf.c:31
int G_vsnaprintf(char *str, size_t size, const char *format, va_list ap)
vsnprintf() version of G_aprintf(). See G_aprintf() for more details.
Definition: aprintf.c:419
int G_vsaprintf(char *str, const char *format, va_list ap)
vsprintf() version of G_aprintf(). See G_aprintf() for more details.
Definition: aprintf.c:398
int G_faprintf(FILE *stream, const char *format,...)
fprintf() version of G_aprintf(). See G_aprintf() for more details.
Definition: aprintf.c:472
int G_vaprintf(const char *format, va_list ap)
vprintf() version of G_aprintf(). See G_aprintf() for more details.
Definition: aprintf.c:366
#define NULL
Definition: ccmath.h:32
void G_fatal_error(const char *msg,...)
Print a fatal error message to stderr.
Definition: gis/error.c:160