logo

utils-std

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

printf.c (14827B)


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