aprintf.c 13 KB

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