aprintf.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  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. char tmp;
  174. /* found a conversion specifier */
  175. if (*c == 's') {
  176. /* if this is a string specifier */
  177. int width = -1, prec = -1, use_ovprintf = 1;
  178. char *p_tmp, *s;
  179. va_list ap_copy;
  180. /* save this ap and use ovprintf() for non-wide
  181. * characters */
  182. va_copy(ap_copy, ap);
  183. *p_spec = 0;
  184. p_spec = spec;
  185. if (*p_spec == '-')
  186. /* alignment */
  187. p_spec++;
  188. if (*p_spec == '*') {
  189. /* read width from next argument */
  190. width = va_arg(ap, int);
  191. p_spec++;
  192. } else if (*p_spec >= '0' && *p_spec <= '9') {
  193. /* read width */
  194. p_tmp = p_spec;
  195. while (*p_spec >= '0' && *p_spec <= '9')
  196. p_spec++;
  197. tmp = *p_spec;
  198. *p_spec = 0;
  199. width = atoi(p_tmp);
  200. *p_spec = tmp;
  201. }
  202. if (*p_spec == '.') {
  203. /* precision */
  204. p_spec++;
  205. if (*p_spec == '*') {
  206. /* read precision from next argument */
  207. prec = va_arg(ap, int);
  208. p_spec++;
  209. } else if (*p_spec >= '0' && *p_spec <= '9') {
  210. /* read precision */
  211. p_tmp = p_spec;
  212. while (*p_spec >= '0' && *p_spec <= '9')
  213. p_spec++;
  214. tmp = *p_spec;
  215. *p_spec = 0;
  216. prec = atoi(p_tmp);
  217. *p_spec = tmp;
  218. }
  219. }
  220. if (*p_spec) {
  221. /* illegal string specifier? */
  222. va_end(ap_copy);
  223. *(q + 1) = 0;
  224. G_fatal_error(
  225. _("Failed to parse string specifier: %s"),
  226. p);
  227. }
  228. s = va_arg(ap, char *);
  229. if (width > 0) {
  230. /* if width is specified */
  231. int wcount = count_wide_chars(s);
  232. if (wcount) {
  233. /* if there are wide characters */
  234. if (prec > 0)
  235. width += count_wide_chars_in_cols(s, prec,
  236. &prec);
  237. else if (prec < 0)
  238. width += wcount;
  239. p_spec = spec;
  240. p_spec += sprintf(p_spec, "%%%s%d",
  241. spec[0] == '-' ? "-" : "", width);
  242. if (prec >= 0)
  243. p_spec += sprintf(p_spec, ".%d", prec);
  244. *p_spec++ = 's';
  245. *p_spec = 0;
  246. nbytes += oprintf(opts, spec, s);
  247. use_ovprintf = 0;
  248. }
  249. /* else use ovprintf() as much as possible */
  250. }
  251. /* else use ovprintf() as much as possible */
  252. if (use_ovprintf) {
  253. tmp = *(q + 1);
  254. *(q + 1) = 0;
  255. nbytes += ovprintf(opts, p, ap_copy);
  256. *(q + 1) = tmp;
  257. }
  258. va_end(ap_copy);
  259. } else {
  260. /* else use ovprintf() for non-string specifiers */
  261. tmp = *(q + 1);
  262. *(q + 1) = 0;
  263. nbytes += ovprintf(opts, p, ap);
  264. *(q + 1) = tmp;
  265. }
  266. break;
  267. } else if (p_spec - spec < SPEC_BUF_SIZE - 2)
  268. /* 2 reserved for % and NULL */
  269. *p_spec++ = *q;
  270. else
  271. G_fatal_error(
  272. _("Format specifier exceeds the buffer size (%d)"),
  273. SPEC_BUF_SIZE);
  274. }
  275. asis = (p = q) + 1;
  276. }
  277. p++;
  278. }
  279. /* print the remaining string */
  280. *p = 0;
  281. nbytes += oprintf(opts, asis);
  282. *p = '%';
  283. return nbytes;
  284. }
  285. /*!
  286. * \brief vprintf() version of G_aprintf(). See G_aprintf() for more details.
  287. *
  288. * \param[in] format string format
  289. * \param[in] ap variable argument list for the format string
  290. * \return number of bytes printed or fatal error on error
  291. */
  292. int G_vaprintf(const char *format, va_list ap)
  293. {
  294. return oaprintf(NULL, format, ap);
  295. }
  296. /*!
  297. * \brief vfprintf() version of G_aprintf(). See G_aprintf() for more details.
  298. *
  299. * \param[in] stream file pointer
  300. * \param[in] format string format
  301. * \param[in] ap variable argument list for the format string
  302. * \return number of bytes printed or fatal error on error
  303. */
  304. int G_vfaprintf(FILE *stream, const char *format, va_list ap)
  305. {
  306. struct options opts;
  307. opts.stream = stream;
  308. opts.str = NULL;
  309. opts.size = -1;
  310. return oaprintf(&opts, format, ap);
  311. }
  312. /*!
  313. * \brief vsprintf() version of G_aprintf(). See G_aprintf() for more details.
  314. *
  315. * \param[in] str string buffer
  316. * \param[in] format string format
  317. * \param[in] ap variable argument list for the format string
  318. * \return number of bytes printed or fatal error on error
  319. */
  320. int G_vsaprintf(char *str, const char *format, va_list ap)
  321. {
  322. struct options opts;
  323. opts.stream = NULL;
  324. opts.str = opts._str = str;
  325. opts.size = -1;
  326. return oaprintf(&opts, format, ap);
  327. }
  328. /*!
  329. * \brief vsnprintf() version of G_aprintf(). See G_aprintf() for more details.
  330. *
  331. * \param[in] str string buffer
  332. * \param[in] size string buffer size
  333. * \param[in] format string format
  334. * \param[in] ap variable argument list for the format string
  335. * \return number of bytes that would be printed if size was big enough or
  336. * fatal error on error
  337. */
  338. int G_vsnaprintf(char *str, size_t size, const char *format, va_list ap)
  339. {
  340. struct options opts;
  341. opts.stream = NULL;
  342. opts.str = opts._str = str;
  343. opts.size = opts._size = size;
  344. return oaprintf(&opts, format, ap);
  345. }
  346. /*!
  347. * \brief Adjust the width of string specifiers to the display space instead of
  348. * the number of bytes for wide characters and print them formatted using the
  349. * adjusted display width.
  350. *
  351. * compare
  352. * printf("%10s|\n%10s|\n", "ABCD", "가나");
  353. -----------
  354. ABCD|
  355. 가나|
  356. -----------
  357. * and
  358. * G_aprintf("%10s|\n%10s|\n", "ABCD", "가나");
  359. -----------
  360. ABCD|
  361. 가나|
  362. -----------
  363. *
  364. * \param[in] format string format
  365. * \param[in] ... arguments for the format string
  366. * \return number of bytes printed or fatal error on error
  367. */
  368. int G_aprintf(const char *format, ...)
  369. {
  370. va_list ap;
  371. int nbytes;
  372. va_start(ap, format);
  373. nbytes = G_vaprintf(format, ap);
  374. va_end(ap);
  375. return nbytes;
  376. }
  377. /*!
  378. * \brief fprintf() version of G_aprintf(). See G_aprintf() for more details.
  379. *
  380. * \param[in] stream file pointer
  381. * \param[in] format string format
  382. * \param[in] ... arguments for the format string
  383. * \return number of bytes printed or fatal error on error
  384. */
  385. int G_faprintf(FILE *stream, const char *format, ...)
  386. {
  387. va_list ap;
  388. int nbytes;
  389. va_start(ap, format);
  390. nbytes = G_vfaprintf(stream, format, ap);
  391. va_end(ap);
  392. return nbytes;
  393. }
  394. /*!
  395. * \brief sprintf() version of G_aprintf(). See G_aprintf() for more details.
  396. *
  397. * \param[in] str string buffer
  398. * \param[in] format string format
  399. * \param[in] ... arguments for the format string
  400. * \return number of bytes printed or fatal error on error
  401. */
  402. int G_saprintf(char *str, const char *format, ...)
  403. {
  404. va_list ap;
  405. int nbytes;
  406. va_start(ap, format);
  407. nbytes = G_vsaprintf(str, format, ap);
  408. va_end(ap);
  409. return nbytes;
  410. }
  411. /*!
  412. * \brief snprintf() version of G_aprintf(). See G_aprintf() for more details.
  413. *
  414. * \param[in] str string buffer
  415. * \param[in] size string buffer size
  416. * \param[in] format string format
  417. * \param[in] ... arguments for the format string
  418. * \return number of bytes that would be printed if size was big enough or
  419. * fatal error on error
  420. */
  421. int G_snaprintf(char *str, size_t size, const char *format, ...)
  422. {
  423. va_list ap;
  424. int nbytes;
  425. va_start(ap, format);
  426. nbytes = G_vsnaprintf(str, size, format, ap);
  427. va_end(ap);
  428. return nbytes;
  429. }