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