GRASS GIS 8 Programmer's Manual  8.5.0dev(2024)-d6dec75dd4
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 
33 struct options {
34  FILE *stream;
35  char *str, *_str;
36  size_t size, _size;
37 };
38 
39 static int count_wide_chars(const char *);
40 static int count_wide_chars_in_cols(const char *, int, int *);
41 static int ovprintf(struct options *, const char *, va_list);
42 static int oprintf(struct options *, const char *, ...);
43 static 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  */
51 static 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  */
78 static 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  */
119 static 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  */
154 static 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  */
177 static 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 aq;
203  char tmp;
204 
205  /* copy ap for ovprintf() */
206  va_copy(aq, 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(aq);
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, aq);
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, aq);
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(aq);
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)"),
357  SPEC_BUF_SIZE);
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  */
379 int 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  */
392 int 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  */
411 int 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  */
432 int 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  */
465 int 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  */
485 int 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  */
505 int 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  */
527 int 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 void void void G_fatal_error(const char *,...) __attribute__((format(printf
#define G_malloc(n)
Definition: defs/gis.h:94
#define _(str)
Definition: glocale.h:10
#define strcpy
Definition: parson.c:62
Definition: segment.h:14