logo

utils-std

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

printf.c (15007B)


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