GRASS GIS 8 Programmer's Manual  8.2.2dev(2023)-3d2c704037
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 {
35  FILE *stream;
36  char *str, *_str;
37  size_t size, _size;
38 };
39 
40 static int count_wide_chars(const char *);
41 static int count_wide_chars_in_cols(const char *, int, int *);
42 static int ovprintf(struct options *, const char *, va_list);
43 static int oprintf(struct options *, const char *, ...);
44 static 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  */
52 static 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  */
79 static 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  */
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  } 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  */
153 static 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  */
176 static 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)"),
344  SPEC_BUF_SIZE);
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  */
366 int 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  */
379 int 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  */
398 int 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  */
419 int 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  */
452 int 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  */
472 int 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  */
492 int 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  */
514 int 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 }
#define G_malloc(n)
Definition: defs/gis.h:112
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
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_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_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
#define NULL
Definition: ccmath.h:32
#define CONVS
Definition: aprintf.c:28
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
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 SPEC_BUF_SIZE
Definition: aprintf.c:31
int G_saprintf(char *str, const char *format,...)
sprintf() version of G_aprintf(). See G_aprintf() for more details.
Definition: aprintf.c:492
#define _(str)
Definition: glocale.h:10
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_vfaprintf(FILE *stream, const char *format, va_list ap)
vfprintf() version of G_aprintf(). See G_aprintf() for more details.
Definition: aprintf.c:379