logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git/

printf.c (14691B)


  1. /*-
  2. * SPDX-License-Identifier: BSD-3-Clause
  3. *
  4. * Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
  5. * Copyright 2014 Garrett D'Amore <garrett@damore.org>
  6. * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
  7. * Copyright (c) 1989, 1993
  8. * The Regents of the University of California. All rights reserved.
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions
  12. * are met:
  13. * 1. Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in the
  17. * documentation and/or other materials provided with the distribution.
  18. * 3. Neither the name of the University nor the names of its contributors
  19. * may be used to endorse or promote products derived from this software
  20. * without specific prior written permission.
  21. *
  22. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  23. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  24. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  25. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  26. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  27. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  28. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  29. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  31. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  32. * SUCH DAMAGE.
  33. */
  34. /*
  35. * Important: This file is used both as a standalone program /usr/bin/printf
  36. * and as a builtin for /bin/sh (#define SHELL).
  37. */
  38. #define _POSIX_C_SOURCE 200809L
  39. #include "../lib/err.h"
  40. #include <assert.h>
  41. #include <ctype.h>
  42. #include <errno.h>
  43. #include <inttypes.h>
  44. #include <limits.h>
  45. #include <locale.h>
  46. #include <stdio.h>
  47. #include <stdlib.h>
  48. #include <string.h>
  49. #include <sys/types.h>
  50. #include <unistd.h>
  51. #include <wchar.h>
  52. #define PF(f, func) \
  53. do \
  54. { \
  55. if(havewidth) \
  56. if(haveprec) \
  57. (void)printf(f, fieldwidth, precision, func); \
  58. else \
  59. (void)printf(f, fieldwidth, func); \
  60. else if(haveprec) \
  61. (void)printf(f, precision, func); \
  62. else \
  63. (void)printf(f, func); \
  64. } while(0)
  65. static int asciicode(void);
  66. static char *printf_doformat(char *, int *);
  67. static int escape(char *, int, size_t *);
  68. static int getchr(void);
  69. static int getfloating(long double *, int);
  70. static int getint(int *);
  71. static int getnum(intmax_t *, uintmax_t *, int);
  72. static const char *getstr(void);
  73. static char *mknum(char *, char);
  74. static void usage(void);
  75. static const char digits[] = "0123456789";
  76. static char end_fmt[1];
  77. static int myargc;
  78. static char **myargv;
  79. static char **gargv;
  80. static char **maxargv;
  81. const char *argv0 = "printf";
  82. int
  83. main(int argc, char *argv[])
  84. {
  85. size_t len;
  86. int end, rval;
  87. char *format, *fmt, *start;
  88. (void)setlocale(LC_ALL, "");
  89. for(int c = -1; (c = getopt(argc, argv, "")) != -1;)
  90. {
  91. switch(c)
  92. {
  93. case '?':
  94. default:
  95. usage();
  96. return (1);
  97. }
  98. }
  99. argc -= optind;
  100. argv += optind;
  101. if(argc < 1)
  102. {
  103. usage();
  104. return (1);
  105. }
  106. /*
  107. * Basic algorithm is to scan the format string for conversion
  108. * specifications -- once one is found, find out if the field
  109. * width or precision is a '*'; if it is, gather up value. Note,
  110. * format strings are reused as necessary to use up the provided
  111. * arguments, arguments of zero/null string are provided to use
  112. * up the format string.
  113. */
  114. fmt = format = *argv;
  115. escape(fmt, 1, &len); /* backslash interpretation */
  116. rval = end = 0;
  117. gargv = ++argv;
  118. for(;;)
  119. {
  120. maxargv = gargv;
  121. myargv = gargv;
  122. for(myargc = 0; gargv[myargc]; myargc++)
  123. /* nop */;
  124. start = fmt;
  125. while(fmt < format + len)
  126. {
  127. if(fmt[0] == '%')
  128. {
  129. fwrite(start, 1, fmt - start, stdout);
  130. if(fmt[1] == '%')
  131. {
  132. /* %% prints a % */
  133. putchar('%');
  134. fmt += 2;
  135. }
  136. else
  137. {
  138. fmt = printf_doformat(fmt, &rval);
  139. if(fmt == NULL || fmt == end_fmt)
  140. {
  141. return (fmt == NULL ? 1 : rval);
  142. }
  143. end = 0;
  144. }
  145. start = fmt;
  146. }
  147. else
  148. fmt++;
  149. if(gargv > maxargv) maxargv = gargv;
  150. }
  151. gargv = maxargv;
  152. if(end == 1)
  153. {
  154. utils_warnx("missing format character");
  155. return (1);
  156. }
  157. fwrite(start, 1, fmt - start, stdout);
  158. if(!*gargv)
  159. {
  160. return (rval);
  161. }
  162. /* Restart at the beginning of the format string. */
  163. fmt = format;
  164. end = 1;
  165. }
  166. /* NOTREACHED */
  167. }
  168. static char *
  169. printf_doformat(char *fmt, int *rval)
  170. {
  171. static const char skip1[] = "#'-+ 0";
  172. int fieldwidth, haveprec, havewidth, mod_ldbl, precision;
  173. char convch, nextch;
  174. char start[strlen(fmt) + 1];
  175. char **fargv;
  176. char *dptr;
  177. int l;
  178. dptr = start;
  179. *dptr++ = '%';
  180. *dptr = 0;
  181. fmt++;
  182. /* look for "n$" field index specifier */
  183. l = strspn(fmt, digits);
  184. if((l > 0) && (fmt[l] == '$'))
  185. {
  186. int idx = atoi(fmt);
  187. if(idx <= myargc)
  188. {
  189. gargv = &myargv[idx - 1];
  190. }
  191. else
  192. {
  193. gargv = &myargv[myargc];
  194. }
  195. if(gargv > maxargv) maxargv = gargv;
  196. fmt += l + 1;
  197. /* save format argument */
  198. fargv = gargv;
  199. }
  200. else
  201. {
  202. fargv = NULL;
  203. }
  204. /* skip to field width */
  205. while(*fmt && strchr(skip1, *fmt) != NULL)
  206. {
  207. *dptr++ = *fmt++;
  208. *dptr = 0;
  209. }
  210. if(*fmt == '*')
  211. {
  212. fmt++;
  213. l = strspn(fmt, digits);
  214. if((l > 0) && (fmt[l] == '$'))
  215. {
  216. int idx = atoi(fmt);
  217. if(fargv == NULL)
  218. {
  219. utils_warnx("incomplete use of n$");
  220. return (NULL);
  221. }
  222. if(idx <= myargc)
  223. {
  224. gargv = &myargv[idx - 1];
  225. }
  226. else
  227. {
  228. gargv = &myargv[myargc];
  229. }
  230. fmt += l + 1;
  231. }
  232. else if(fargv != NULL)
  233. {
  234. utils_warnx("incomplete use of n$");
  235. return (NULL);
  236. }
  237. if(getint(&fieldwidth)) return (NULL);
  238. if(gargv > maxargv) maxargv = gargv;
  239. havewidth = 1;
  240. *dptr++ = '*';
  241. *dptr = 0;
  242. }
  243. else
  244. {
  245. havewidth = 0;
  246. /* skip to possible '.', get following precision */
  247. while(isdigit(*fmt))
  248. {
  249. *dptr++ = *fmt++;
  250. *dptr = 0;
  251. }
  252. }
  253. if(*fmt == '.')
  254. {
  255. /* precision present? */
  256. fmt++;
  257. *dptr++ = '.';
  258. if(*fmt == '*')
  259. {
  260. fmt++;
  261. l = strspn(fmt, digits);
  262. if((l > 0) && (fmt[l] == '$'))
  263. {
  264. int idx = atoi(fmt);
  265. if(fargv == NULL)
  266. {
  267. utils_warnx("incomplete use of n$");
  268. return (NULL);
  269. }
  270. if(idx <= myargc)
  271. {
  272. gargv = &myargv[idx - 1];
  273. }
  274. else
  275. {
  276. gargv = &myargv[myargc];
  277. }
  278. fmt += l + 1;
  279. }
  280. else if(fargv != NULL)
  281. {
  282. utils_warnx("incomplete use of n$");
  283. return (NULL);
  284. }
  285. if(getint(&precision)) return (NULL);
  286. if(gargv > maxargv) maxargv = gargv;
  287. haveprec = 1;
  288. *dptr++ = '*';
  289. *dptr = 0;
  290. }
  291. else
  292. {
  293. haveprec = 0;
  294. /* skip to conversion char */
  295. while(isdigit(*fmt))
  296. {
  297. *dptr++ = *fmt++;
  298. *dptr = 0;
  299. }
  300. }
  301. }
  302. else
  303. haveprec = 0;
  304. if(!*fmt)
  305. {
  306. utils_warnx("missing format character");
  307. return (NULL);
  308. }
  309. *dptr++ = *fmt;
  310. *dptr = 0;
  311. /*
  312. * Look for a length modifier. POSIX doesn't have these, so
  313. * we only support them for floating-point conversions, which
  314. * are extensions. This is useful because the L modifier can
  315. * be used to gain extra range and precision, while omitting
  316. * it is more likely to produce consistent results on different
  317. * architectures. This is not so important for integers
  318. * because overflow is the only bad thing that can happen to
  319. * them, but consider the command printf %a 1.1
  320. */
  321. if(*fmt == 'L')
  322. {
  323. mod_ldbl = 1;
  324. fmt++;
  325. if(!strchr("aAeEfFgG", *fmt))
  326. {
  327. utils_warnx("bad modifier L for %%%c", *fmt);
  328. return (NULL);
  329. }
  330. }
  331. else
  332. {
  333. mod_ldbl = 0;
  334. }
  335. /* save the current arg offset, and set to the format arg */
  336. if(fargv != NULL)
  337. {
  338. gargv = fargv;
  339. }
  340. convch = *fmt;
  341. nextch = *++fmt;
  342. *fmt = '\0';
  343. switch(convch)
  344. {
  345. case 'b':
  346. {
  347. size_t len;
  348. char *p;
  349. int getout;
  350. /* Convert "b" to "s" for output. */
  351. start[strlen(start) - 1] = 's';
  352. if((p = strdup(getstr())) == NULL)
  353. {
  354. utils_warnx("%s", strerror(ENOMEM));
  355. return (NULL);
  356. }
  357. getout = escape(p, 0, &len);
  358. PF(start, p);
  359. /* Restore format for next loop. */
  360. free(p);
  361. if(getout) return (end_fmt);
  362. break;
  363. }
  364. case 'c':
  365. {
  366. char p;
  367. p = getchr();
  368. if(p != '\0') PF(start, p);
  369. break;
  370. }
  371. case 's':
  372. {
  373. const char *p;
  374. p = getstr();
  375. PF(start, p);
  376. break;
  377. }
  378. case 'd':
  379. case 'i':
  380. case 'o':
  381. case 'u':
  382. case 'x':
  383. case 'X':
  384. {
  385. char *f;
  386. intmax_t val;
  387. uintmax_t uval;
  388. int signedconv;
  389. signedconv = (convch == 'd' || convch == 'i');
  390. if((f = mknum(start, convch)) == NULL) return (NULL);
  391. if(getnum(&val, &uval, signedconv)) *rval = 1;
  392. if(signedconv)
  393. PF(f, val);
  394. else
  395. PF(f, uval);
  396. break;
  397. }
  398. case 'e':
  399. case 'E':
  400. case 'f':
  401. case 'F':
  402. case 'g':
  403. case 'G':
  404. case 'a':
  405. case 'A':
  406. {
  407. long double p;
  408. if(getfloating(&p, mod_ldbl)) *rval = 1;
  409. if(mod_ldbl)
  410. PF(start, p);
  411. else
  412. PF(start, (double)p);
  413. break;
  414. }
  415. default:
  416. utils_warnx("illegal format character '%c'", convch);
  417. return (NULL);
  418. }
  419. *fmt = nextch;
  420. /* return the gargv to the next element */
  421. return (fmt);
  422. }
  423. static char *
  424. mknum(char *str, char ch)
  425. {
  426. static char *copy;
  427. static size_t copy_size;
  428. char *newcopy;
  429. size_t len, newlen;
  430. len = strlen(str) + 2;
  431. if(len > copy_size)
  432. {
  433. newlen = len + 1023;
  434. assert(newlen != 0);
  435. if((newcopy = realloc(copy, newlen)) == NULL)
  436. {
  437. utils_warnx("%s", strerror(ENOMEM));
  438. return (NULL);
  439. }
  440. copy = newcopy;
  441. copy_size = newlen;
  442. }
  443. memmove(copy, str, len - 3);
  444. copy[len - 3] = 'j';
  445. copy[len - 2] = ch;
  446. copy[len - 1] = '\0';
  447. return (copy);
  448. }
  449. static int
  450. escape(char *fmt, int percent, size_t *len)
  451. {
  452. char *save, *store, c;
  453. int value;
  454. for(save = store = fmt; ((c = *fmt) != 0); ++fmt, ++store)
  455. {
  456. if(c != '\\')
  457. {
  458. *store = c;
  459. continue;
  460. }
  461. switch(*++fmt)
  462. {
  463. case '\0': /* EOS, user error */
  464. *store = '\\';
  465. *++store = '\0';
  466. *len = store - save;
  467. return (0);
  468. case '\\': /* backslash */
  469. case '\'': /* single quote */
  470. *store = *fmt;
  471. break;
  472. case 'a': /* bell/alert */
  473. *store = '\a';
  474. break;
  475. case 'b': /* backspace */
  476. *store = '\b';
  477. break;
  478. case 'c':
  479. if(!percent)
  480. {
  481. *store = '\0';
  482. *len = store - save;
  483. return (1);
  484. }
  485. *store = 'c';
  486. break;
  487. case 'f': /* form-feed */
  488. *store = '\f';
  489. break;
  490. case 'n': /* newline */
  491. *store = '\n';
  492. break;
  493. case 'r': /* carriage-return */
  494. *store = '\r';
  495. break;
  496. case 't': /* horizontal tab */
  497. *store = '\t';
  498. break;
  499. case 'v': /* vertical tab */
  500. *store = '\v';
  501. break;
  502. /* octal constant */
  503. case 'x': /* hex */
  504. c = 2;
  505. fmt++;
  506. for(value = 0; c-- && isxdigit(*fmt); ++fmt)
  507. {
  508. value <<= 4;
  509. if(*fmt <= '9')
  510. value += *fmt - '0';
  511. else if(*fmt <= 'F')
  512. value += *fmt - 'A' + 10;
  513. else
  514. value += *fmt - 'a' + 10;
  515. }
  516. --fmt;
  517. *store = (char)value;
  518. break;
  519. case '0':
  520. case '1':
  521. case '2':
  522. case '3':
  523. case '4':
  524. case '5':
  525. case '6':
  526. case '7':
  527. c = (!percent && *fmt == '0') ? 4 : 3;
  528. for(value = 0; c-- && *fmt >= '0' && *fmt <= '7'; ++fmt)
  529. {
  530. value <<= 3;
  531. value += *fmt - '0';
  532. }
  533. --fmt;
  534. if(percent && value == '%')
  535. {
  536. *store++ = '%';
  537. *store = '%';
  538. }
  539. else
  540. *store = (char)value;
  541. break;
  542. default:
  543. *store = *fmt;
  544. break;
  545. }
  546. }
  547. *store = '\0';
  548. *len = store - save;
  549. return (0);
  550. }
  551. static int
  552. getchr(void)
  553. {
  554. if(!gargv || !*gargv) return ('\0');
  555. return ((int)**gargv++);
  556. }
  557. static const char *
  558. getstr(void)
  559. {
  560. if(!gargv || !*gargv) return ("");
  561. return (*gargv++);
  562. }
  563. static int
  564. getint(int *ip)
  565. {
  566. intmax_t val;
  567. uintmax_t uval;
  568. int rval;
  569. if(getnum(&val, &uval, 1)) return (1);
  570. rval = 0;
  571. if(val < INT_MIN || val > INT_MAX)
  572. {
  573. utils_warnx("%s: %s", *gargv, strerror(ERANGE));
  574. rval = 1;
  575. }
  576. *ip = (int)val;
  577. return (rval);
  578. }
  579. static int
  580. getnum(intmax_t *ip, uintmax_t *uip, int signedconv)
  581. {
  582. char *ep;
  583. int rval;
  584. if(!gargv || !*gargv)
  585. {
  586. *ip = *uip = 0;
  587. return (0);
  588. }
  589. if(**gargv == '"' || **gargv == '\'')
  590. {
  591. if(signedconv)
  592. *ip = asciicode();
  593. else
  594. *uip = asciicode();
  595. return (0);
  596. }
  597. rval = 0;
  598. errno = 0;
  599. if(signedconv)
  600. *ip = strtoimax(*gargv, &ep, 0);
  601. else
  602. *uip = strtoumax(*gargv, &ep, 0);
  603. if(ep == *gargv)
  604. {
  605. utils_warnx("%s: expected numeric value", *gargv);
  606. rval = 1;
  607. }
  608. else if(*ep != '\0')
  609. {
  610. utils_warnx("%s: not completely converted", *gargv);
  611. rval = 1;
  612. }
  613. if(errno == ERANGE)
  614. {
  615. utils_warnx("%s: %s", *gargv, strerror(ERANGE));
  616. rval = 1;
  617. }
  618. ++gargv;
  619. return (rval);
  620. }
  621. static int
  622. getfloating(long double *dp, int mod_ldbl)
  623. {
  624. char *ep;
  625. int rval;
  626. if(!*gargv)
  627. {
  628. *dp = 0.0;
  629. return (0);
  630. }
  631. if(**gargv == '"' || **gargv == '\'')
  632. {
  633. *dp = asciicode();
  634. return (0);
  635. }
  636. rval = 0;
  637. errno = 0;
  638. if(mod_ldbl)
  639. *dp = strtold(*gargv, &ep);
  640. else
  641. *dp = strtod(*gargv, &ep);
  642. if(ep == *gargv)
  643. {
  644. utils_warnx("%s: expected numeric value", *gargv);
  645. rval = 1;
  646. }
  647. else if(*ep != '\0')
  648. {
  649. utils_warnx("%s: not completely converted", *gargv);
  650. rval = 1;
  651. }
  652. if(errno == ERANGE)
  653. {
  654. utils_warnx("%s: %s", *gargv, strerror(ERANGE));
  655. rval = 1;
  656. }
  657. ++gargv;
  658. return (rval);
  659. }
  660. static int
  661. asciicode(void)
  662. {
  663. int ch;
  664. wchar_t wch;
  665. mbstate_t mbs;
  666. ch = (unsigned char)**gargv;
  667. if(ch == '\'' || ch == '"')
  668. {
  669. memset(&mbs, 0, sizeof(mbs));
  670. switch(mbrtowc(&wch, *gargv + 1, MB_LEN_MAX, &mbs))
  671. {
  672. case(size_t)-2:
  673. case(size_t)-1:
  674. wch = (unsigned char)gargv[0][1];
  675. break;
  676. case 0:
  677. wch = 0;
  678. break;
  679. }
  680. ch = wch;
  681. }
  682. ++gargv;
  683. return (ch);
  684. }
  685. static void
  686. usage(void)
  687. {
  688. (void)fprintf(stderr, "usage: printf format [arguments ...]\n");
  689. }