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