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