logo

utils-std

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

printf.c (14682B)


  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. int ch;
  89. (void)setlocale(LC_ALL, "");
  90. while((ch = getopt(argc, argv, "")) != -1)
  91. switch(ch)
  92. {
  93. case '?':
  94. default:
  95. usage();
  96. return (1);
  97. }
  98. argc -= optind;
  99. argv += optind;
  100. if(argc < 1)
  101. {
  102. usage();
  103. return (1);
  104. }
  105. /*
  106. * Basic algorithm is to scan the format string for conversion
  107. * specifications -- once one is found, find out if the field
  108. * width or precision is a '*'; if it is, gather up value. Note,
  109. * format strings are reused as necessary to use up the provided
  110. * arguments, arguments of zero/null string are provided to use
  111. * up the format string.
  112. */
  113. fmt = format = *argv;
  114. escape(fmt, 1, &len); /* backslash interpretation */
  115. rval = end = 0;
  116. gargv = ++argv;
  117. for(;;)
  118. {
  119. maxargv = gargv;
  120. myargv = gargv;
  121. for(myargc = 0; gargv[myargc]; myargc++)
  122. /* nop */;
  123. start = fmt;
  124. while(fmt < format + len)
  125. {
  126. if(fmt[0] == '%')
  127. {
  128. fwrite(start, 1, fmt - start, stdout);
  129. if(fmt[1] == '%')
  130. {
  131. /* %% prints a % */
  132. putchar('%');
  133. fmt += 2;
  134. }
  135. else
  136. {
  137. fmt = printf_doformat(fmt, &rval);
  138. if(fmt == NULL || fmt == end_fmt)
  139. {
  140. return (fmt == NULL ? 1 : rval);
  141. }
  142. end = 0;
  143. }
  144. start = fmt;
  145. }
  146. else
  147. fmt++;
  148. if(gargv > maxargv) maxargv = gargv;
  149. }
  150. gargv = maxargv;
  151. if(end == 1)
  152. {
  153. utils_warnx("missing format character");
  154. return (1);
  155. }
  156. fwrite(start, 1, fmt - start, stdout);
  157. if(!*gargv)
  158. {
  159. return (rval);
  160. }
  161. /* Restart at the beginning of the format string. */
  162. fmt = format;
  163. end = 1;
  164. }
  165. /* NOTREACHED */
  166. }
  167. static char *
  168. printf_doformat(char *fmt, int *rval)
  169. {
  170. static const char skip1[] = "#'-+ 0";
  171. int fieldwidth, haveprec, havewidth, mod_ldbl, precision;
  172. char convch, nextch;
  173. char start[strlen(fmt) + 1];
  174. char **fargv;
  175. char *dptr;
  176. int l;
  177. dptr = start;
  178. *dptr++ = '%';
  179. *dptr = 0;
  180. fmt++;
  181. /* look for "n$" field index specifier */
  182. l = strspn(fmt, digits);
  183. if((l > 0) && (fmt[l] == '$'))
  184. {
  185. int idx = atoi(fmt);
  186. if(idx <= myargc)
  187. {
  188. gargv = &myargv[idx - 1];
  189. }
  190. else
  191. {
  192. gargv = &myargv[myargc];
  193. }
  194. if(gargv > maxargv) maxargv = gargv;
  195. fmt += l + 1;
  196. /* save format argument */
  197. fargv = gargv;
  198. }
  199. else
  200. {
  201. fargv = NULL;
  202. }
  203. /* skip to field width */
  204. while(*fmt && strchr(skip1, *fmt) != NULL)
  205. {
  206. *dptr++ = *fmt++;
  207. *dptr = 0;
  208. }
  209. if(*fmt == '*')
  210. {
  211. fmt++;
  212. l = strspn(fmt, digits);
  213. if((l > 0) && (fmt[l] == '$'))
  214. {
  215. int idx = atoi(fmt);
  216. if(fargv == NULL)
  217. {
  218. utils_warnx("incomplete use of n$");
  219. return (NULL);
  220. }
  221. if(idx <= myargc)
  222. {
  223. gargv = &myargv[idx - 1];
  224. }
  225. else
  226. {
  227. gargv = &myargv[myargc];
  228. }
  229. fmt += l + 1;
  230. }
  231. else if(fargv != NULL)
  232. {
  233. utils_warnx("incomplete use of n$");
  234. return (NULL);
  235. }
  236. if(getint(&fieldwidth)) return (NULL);
  237. if(gargv > maxargv) maxargv = gargv;
  238. havewidth = 1;
  239. *dptr++ = '*';
  240. *dptr = 0;
  241. }
  242. else
  243. {
  244. havewidth = 0;
  245. /* skip to possible '.', get following precision */
  246. while(isdigit(*fmt))
  247. {
  248. *dptr++ = *fmt++;
  249. *dptr = 0;
  250. }
  251. }
  252. if(*fmt == '.')
  253. {
  254. /* precision present? */
  255. fmt++;
  256. *dptr++ = '.';
  257. if(*fmt == '*')
  258. {
  259. fmt++;
  260. l = strspn(fmt, digits);
  261. if((l > 0) && (fmt[l] == '$'))
  262. {
  263. int idx = atoi(fmt);
  264. if(fargv == NULL)
  265. {
  266. utils_warnx("incomplete use of n$");
  267. return (NULL);
  268. }
  269. if(idx <= myargc)
  270. {
  271. gargv = &myargv[idx - 1];
  272. }
  273. else
  274. {
  275. gargv = &myargv[myargc];
  276. }
  277. fmt += l + 1;
  278. }
  279. else if(fargv != NULL)
  280. {
  281. utils_warnx("incomplete use of n$");
  282. return (NULL);
  283. }
  284. if(getint(&precision)) return (NULL);
  285. if(gargv > maxargv) maxargv = gargv;
  286. haveprec = 1;
  287. *dptr++ = '*';
  288. *dptr = 0;
  289. }
  290. else
  291. {
  292. haveprec = 0;
  293. /* skip to conversion char */
  294. while(isdigit(*fmt))
  295. {
  296. *dptr++ = *fmt++;
  297. *dptr = 0;
  298. }
  299. }
  300. }
  301. else
  302. haveprec = 0;
  303. if(!*fmt)
  304. {
  305. utils_warnx("missing format character");
  306. return (NULL);
  307. }
  308. *dptr++ = *fmt;
  309. *dptr = 0;
  310. /*
  311. * Look for a length modifier. POSIX doesn't have these, so
  312. * we only support them for floating-point conversions, which
  313. * are extensions. This is useful because the L modifier can
  314. * be used to gain extra range and precision, while omitting
  315. * it is more likely to produce consistent results on different
  316. * architectures. This is not so important for integers
  317. * because overflow is the only bad thing that can happen to
  318. * them, but consider the command printf %a 1.1
  319. */
  320. if(*fmt == 'L')
  321. {
  322. mod_ldbl = 1;
  323. fmt++;
  324. if(!strchr("aAeEfFgG", *fmt))
  325. {
  326. utils_warnx("bad modifier L for %%%c", *fmt);
  327. return (NULL);
  328. }
  329. }
  330. else
  331. {
  332. mod_ldbl = 0;
  333. }
  334. /* save the current arg offset, and set to the format arg */
  335. if(fargv != NULL)
  336. {
  337. gargv = fargv;
  338. }
  339. convch = *fmt;
  340. nextch = *++fmt;
  341. *fmt = '\0';
  342. switch(convch)
  343. {
  344. case 'b':
  345. {
  346. size_t len;
  347. char *p;
  348. int getout;
  349. /* Convert "b" to "s" for output. */
  350. start[strlen(start) - 1] = 's';
  351. if((p = strdup(getstr())) == NULL)
  352. {
  353. utils_warnx("%s", strerror(ENOMEM));
  354. return (NULL);
  355. }
  356. getout = escape(p, 0, &len);
  357. PF(start, p);
  358. /* Restore format for next loop. */
  359. free(p);
  360. if(getout) return (end_fmt);
  361. break;
  362. }
  363. case 'c':
  364. {
  365. char p;
  366. p = getchr();
  367. if(p != '\0') PF(start, p);
  368. break;
  369. }
  370. case 's':
  371. {
  372. const char *p;
  373. p = getstr();
  374. PF(start, p);
  375. break;
  376. }
  377. case 'd':
  378. case 'i':
  379. case 'o':
  380. case 'u':
  381. case 'x':
  382. case 'X':
  383. {
  384. char *f;
  385. intmax_t val;
  386. uintmax_t uval;
  387. int signedconv;
  388. signedconv = (convch == 'd' || convch == 'i');
  389. if((f = mknum(start, convch)) == NULL) return (NULL);
  390. if(getnum(&val, &uval, signedconv)) *rval = 1;
  391. if(signedconv)
  392. PF(f, val);
  393. else
  394. PF(f, uval);
  395. break;
  396. }
  397. case 'e':
  398. case 'E':
  399. case 'f':
  400. case 'F':
  401. case 'g':
  402. case 'G':
  403. case 'a':
  404. case 'A':
  405. {
  406. long double p;
  407. if(getfloating(&p, mod_ldbl)) *rval = 1;
  408. if(mod_ldbl)
  409. PF(start, p);
  410. else
  411. PF(start, (double)p);
  412. break;
  413. }
  414. default:
  415. utils_warnx("illegal format character %c", convch);
  416. return (NULL);
  417. }
  418. *fmt = nextch;
  419. /* return the gargv to the next element */
  420. return (fmt);
  421. }
  422. static char *
  423. mknum(char *str, char ch)
  424. {
  425. static char *copy;
  426. static size_t copy_size;
  427. char *newcopy;
  428. size_t len, newlen;
  429. len = strlen(str) + 2;
  430. if(len > copy_size)
  431. {
  432. newlen = len + 1023;
  433. assert(newlen != 0);
  434. if((newcopy = realloc(copy, newlen)) == NULL)
  435. {
  436. utils_warnx("%s", strerror(ENOMEM));
  437. return (NULL);
  438. }
  439. copy = newcopy;
  440. copy_size = newlen;
  441. }
  442. memmove(copy, str, len - 3);
  443. copy[len - 3] = 'j';
  444. copy[len - 2] = ch;
  445. copy[len - 1] = '\0';
  446. return (copy);
  447. }
  448. static int
  449. escape(char *fmt, int percent, size_t *len)
  450. {
  451. char *save, *store, c;
  452. int value;
  453. for(save = store = fmt; ((c = *fmt) != 0); ++fmt, ++store)
  454. {
  455. if(c != '\\')
  456. {
  457. *store = c;
  458. continue;
  459. }
  460. switch(*++fmt)
  461. {
  462. case '\0': /* EOS, user error */
  463. *store = '\\';
  464. *++store = '\0';
  465. *len = store - save;
  466. return (0);
  467. case '\\': /* backslash */
  468. case '\'': /* single quote */
  469. *store = *fmt;
  470. break;
  471. case 'a': /* bell/alert */
  472. *store = '\a';
  473. break;
  474. case 'b': /* backspace */
  475. *store = '\b';
  476. break;
  477. case 'c':
  478. if(!percent)
  479. {
  480. *store = '\0';
  481. *len = store - save;
  482. return (1);
  483. }
  484. *store = 'c';
  485. break;
  486. case 'f': /* form-feed */
  487. *store = '\f';
  488. break;
  489. case 'n': /* newline */
  490. *store = '\n';
  491. break;
  492. case 'r': /* carriage-return */
  493. *store = '\r';
  494. break;
  495. case 't': /* horizontal tab */
  496. *store = '\t';
  497. break;
  498. case 'v': /* vertical tab */
  499. *store = '\v';
  500. break;
  501. /* octal constant */
  502. case 'x': /* hex */
  503. c = 2;
  504. fmt++;
  505. for(value = 0; c-- && isxdigit(*fmt); ++fmt)
  506. {
  507. value <<= 4;
  508. if(*fmt <= '9')
  509. value += *fmt - '0';
  510. else if(*fmt <= 'F')
  511. value += *fmt - 'A' + 10;
  512. else
  513. value += *fmt - 'a' + 10;
  514. }
  515. --fmt;
  516. *store = (char)value;
  517. break;
  518. case '0':
  519. case '1':
  520. case '2':
  521. case '3':
  522. case '4':
  523. case '5':
  524. case '6':
  525. case '7':
  526. c = (!percent && *fmt == '0') ? 4 : 3;
  527. for(value = 0; c-- && *fmt >= '0' && *fmt <= '7'; ++fmt)
  528. {
  529. value <<= 3;
  530. value += *fmt - '0';
  531. }
  532. --fmt;
  533. if(percent && value == '%')
  534. {
  535. *store++ = '%';
  536. *store = '%';
  537. }
  538. else
  539. *store = (char)value;
  540. break;
  541. default:
  542. *store = *fmt;
  543. break;
  544. }
  545. }
  546. *store = '\0';
  547. *len = store - save;
  548. return (0);
  549. }
  550. static int
  551. getchr(void)
  552. {
  553. if(!gargv || !*gargv) return ('\0');
  554. return ((int)**gargv++);
  555. }
  556. static const char *
  557. getstr(void)
  558. {
  559. if(!gargv || !*gargv) return ("");
  560. return (*gargv++);
  561. }
  562. static int
  563. getint(int *ip)
  564. {
  565. intmax_t val;
  566. uintmax_t uval;
  567. int rval;
  568. if(getnum(&val, &uval, 1)) return (1);
  569. rval = 0;
  570. if(val < INT_MIN || val > INT_MAX)
  571. {
  572. utils_warnx("%s: %s", *gargv, strerror(ERANGE));
  573. rval = 1;
  574. }
  575. *ip = (int)val;
  576. return (rval);
  577. }
  578. static int
  579. getnum(intmax_t *ip, uintmax_t *uip, int signedconv)
  580. {
  581. char *ep;
  582. int rval;
  583. if(!gargv || !*gargv)
  584. {
  585. *ip = *uip = 0;
  586. return (0);
  587. }
  588. if(**gargv == '"' || **gargv == '\'')
  589. {
  590. if(signedconv)
  591. *ip = asciicode();
  592. else
  593. *uip = asciicode();
  594. return (0);
  595. }
  596. rval = 0;
  597. errno = 0;
  598. if(signedconv)
  599. *ip = strtoimax(*gargv, &ep, 0);
  600. else
  601. *uip = strtoumax(*gargv, &ep, 0);
  602. if(ep == *gargv)
  603. {
  604. utils_warnx("%s: expected numeric value", *gargv);
  605. rval = 1;
  606. }
  607. else if(*ep != '\0')
  608. {
  609. utils_warnx("%s: not completely converted", *gargv);
  610. rval = 1;
  611. }
  612. if(errno == ERANGE)
  613. {
  614. utils_warnx("%s: %s", *gargv, strerror(ERANGE));
  615. rval = 1;
  616. }
  617. ++gargv;
  618. return (rval);
  619. }
  620. static int
  621. getfloating(long double *dp, int mod_ldbl)
  622. {
  623. char *ep;
  624. int rval;
  625. if(!*gargv)
  626. {
  627. *dp = 0.0;
  628. return (0);
  629. }
  630. if(**gargv == '"' || **gargv == '\'')
  631. {
  632. *dp = asciicode();
  633. return (0);
  634. }
  635. rval = 0;
  636. errno = 0;
  637. if(mod_ldbl)
  638. *dp = strtold(*gargv, &ep);
  639. else
  640. *dp = strtod(*gargv, &ep);
  641. if(ep == *gargv)
  642. {
  643. utils_warnx("%s: expected numeric value", *gargv);
  644. rval = 1;
  645. }
  646. else if(*ep != '\0')
  647. {
  648. utils_warnx("%s: not completely converted", *gargv);
  649. rval = 1;
  650. }
  651. if(errno == ERANGE)
  652. {
  653. utils_warnx("%s: %s", *gargv, strerror(ERANGE));
  654. rval = 1;
  655. }
  656. ++gargv;
  657. return (rval);
  658. }
  659. static int
  660. asciicode(void)
  661. {
  662. int ch;
  663. wchar_t wch;
  664. mbstate_t mbs;
  665. ch = (unsigned char)**gargv;
  666. if(ch == '\'' || ch == '"')
  667. {
  668. memset(&mbs, 0, sizeof(mbs));
  669. switch(mbrtowc(&wch, *gargv + 1, MB_LEN_MAX, &mbs))
  670. {
  671. case(size_t)-2:
  672. case(size_t)-1:
  673. wch = (unsigned char)gargv[0][1];
  674. break;
  675. case 0:
  676. wch = 0;
  677. break;
  678. }
  679. ch = wch;
  680. }
  681. ++gargv;
  682. return (ch);
  683. }
  684. static void
  685. usage(void)
  686. {
  687. (void)fprintf(stderr, "usage: printf format [arguments ...]\n");
  688. }