home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / unix / volume17 / printf < prev    next >
Encoding:
Text File  |  1989-02-09  |  12.4 KB  |  575 lines

  1. Path: wugate!wucs1!uunet!bbn.com!rsalz
  2. From: rsalz@uunet.uu.net (Rich Salz)
  3. Newsgroups: comp.sources.unix
  4. Subject: v17i091:  A printf program
  5. Message-ID: <1482@fig.bbn.com>
  6. Date: 8 Feb 89 22:29:23 GMT
  7. Lines: 565
  8. Approved: rsalz@uunet.UU.NET
  9.  
  10. Submitted-by: Chris Torek <chris@mimsy.umd.edu>
  11. Posting-number: Volume 17, Issue 91
  12. Archive-name: printf
  13.  
  14. Printf duplicates (as far as possible) the standard C library routine of
  15. the same name, at the shell command level.  It is similar to echo, except
  16. that it formats its arguments according to conversion specifications given
  17. in the format-string before writing them to the standard output.  For a
  18. thorough explanation of format specifications, see printf(3s).
  19.  
  20. It implements most of the ANSI specification, as well as Roman Numerals.
  21.     -Chris
  22.  
  23. : Run this shell script with "sh" not "csh"
  24. PATH=/bin:/usr/bin:/usr/ucb:/etc:$PATH
  25. export PATH
  26. all=false
  27. if [ x$1 = x-a ]; then
  28.     all=true
  29. fi
  30. echo Extracting printf.1
  31. sed 's/^X//' <<'//go.sysin dd *' >printf.1
  32. X.\"    @(#)printf.1    8-Jan-1987
  33. X.\"
  34. X.TH PRINTF 1 "8-Jan-1987"
  35. X.UC 4
  36. X.SH NAME
  37. Xprintf \- formatted output at shell command level
  38. X.SH SYNOPSIS
  39. X.B printf 
  40. X.I format-string
  41. X[
  42. X.I arg1
  43. X] [
  44. X.I arg2
  45. X] ...
  46. X.SH DESCRIPTION
  47. X.I Printf
  48. Xduplicates (as far as possible) the standard C library routine of the
  49. Xsame name, at the shell command level.  It is similar to
  50. X.IR echo ,
  51. Xexcept that it formats its arguments according to conversion specifications
  52. Xgiven in the
  53. X.I format-string
  54. Xbefore writing them to the standard output.
  55. XFor a thorough explanation of format specifications, see
  56. X.IR printf (3s).
  57. X.PP
  58. XFor the sake of perversity,
  59. X.I printf
  60. Ximplements one format conversion
  61. X.B not
  62. Xsupported by the standard printf subroutine: the
  63. X.I %r
  64. Xand
  65. X.IR %R
  66. Xconversions, which print integers as Roman numerals.  The
  67. Xfirst format produces lowercase, and the second uppercase.
  68. X.PP
  69. XAs a convenience, within the
  70. X.I format-string
  71. Xand any string or character arguments,
  72. X.I printf
  73. Xconverts ``backslash notation'' \- as defined in the
  74. Xdraft proposed ANSI C Standard X3J11 \- into the
  75. Xappropriate control characters.
  76. XThe Standard provides for hexadecimal escapes as ``\ex1a2F3c4...'', in
  77. Xwhich the only way to terminate the escape is with a non-hexadecimal
  78. Xcharacter.  This is not always suitable outside the C language, so
  79. X.I printf
  80. Xprovides one additional escape,
  81. X.BR \e& ,
  82. Xwhich expands to nothing, but in so doing serves to terminate a
  83. Xhexadecimal escape.
  84. X.SH EXAMPLES
  85. X.nf
  86. X.na
  87. X.ta 0.6i
  88. X.sp 2
  89. X% printf 'Today is %s the %d of %s.\en' Monday 1 April
  90. XToday is Monday the 1 of April.
  91. X.sp 3
  92. X% printf 'Interesting Numbers\en\en\etPie: %*.*f\en\etFoo: %g\en' \e
  93. X    6 4 3.14159265 42
  94. XInteresting Numbers
  95. X
  96. X    Pie: 3.1416
  97. X    Foo: 42
  98. X.sp 3
  99. X% printf '%s %d, %R\en' July 4 1776
  100. XJuly 4, MDCCLXXVI
  101. X.sp 3
  102. X% printf 'in ASCII this prints dd: \ex64\e&d.\en' 
  103. Xin ASCII this prints dd: dd.
  104. X.sp 2
  105. X.fi
  106. X.ad
  107. X.SH AUTHORS
  108. XFred Blonder <fred@mimsy.umd.edu>
  109. X.sp
  110. XChris Torek <chris@mimsy.umd.edu>
  111. X.SH "SEE ALSO"
  112. Xecho(1), printf(3s)
  113. X.SH BUGS
  114. XThe Roman conversions are not strictly correct.
  115. XZero produces no text;
  116. Xvery large values give the complaint ``abortive Roman numeral''.
  117. XNegative Roman numerals are printed with a leading minus sign.
  118. XIt is unclear what the Romans did in such cases,
  119. Xalthough zero could perhaps be written as ``nihil''.
  120. XValues in the millions were sometimes written
  121. Xusing an M with an overbar,
  122. Xbut there is no bar-M character in ASCII.
  123. X.sp
  124. XThe ``%n'' conversion is unimplementable.
  125. XThe number of characters written is not returned.
  126. XLong double formats are not supported.
  127. //go.sysin dd *
  128. if [ `wc -c < printf.1` != 2521 ]; then
  129.     made=false
  130.     echo error transmitting printf.1 --
  131.     echo length should be 2521, not `wc -c < printf.1`
  132. else
  133.     made=true
  134. fi
  135. if $made; then
  136.     chmod 444 printf.1
  137.     echo -n '    '; ls -ld printf.1
  138. fi
  139. echo Extracting printf.c
  140. sed 's/^X//' <<'//go.sysin dd *' >printf.c
  141. X#ifndef lint
  142. Xstatic char rcsid[]= "$Header: printf.c,v 2.3 88/08/19 03:41:12 chris Exp $";
  143. X#endif
  144. X
  145. X/*
  146. X * printf - duplicate the C library routine of the same name, but from
  147. X * the shell command level.
  148. X *
  149. X * This version by Chris Torek, based on an earlier version by Fred Blonder.
  150. X */
  151. X
  152. X#include <stdio.h>
  153. X#include <ctype.h>
  154. X#include <sysexits.h>
  155. X
  156. Xchar    *progname;
  157. X
  158. Xchar    *ctor(), **doit();
  159. Xdouble    atof();
  160. Xint    atoi();
  161. Xlong    atol();
  162. X
  163. Xmain(argc, argv)
  164. X    int argc;
  165. X    char **argv;
  166. X{
  167. X    register char *cp, *convp, **ap, **ep;
  168. X    register int ch, ndyn, flags;
  169. X    char cbuf[BUFSIZ];    /* separates each conversion */
  170. X    static char hasmod[] = "has integer length modifier";
  171. X
  172. X    /* flags */
  173. X#define    LONG    1
  174. X#define    SHORT    2
  175. X
  176. X    ap = argv;
  177. X    ep = &ap[argc];
  178. X    progname = *ap++;
  179. X    if (argc < 2) {
  180. X        (void) fprintf(stderr,
  181. X            "%s: Usage: %s <format-string> [ arg1 . . . ]\n",
  182. X            progname, progname);
  183. X        exit(EX_USAGE);
  184. X    }
  185. X
  186. X    ctrl(cp = *ap++);    /* backslash interpretation of fmt string */
  187. X
  188. X    /*
  189. X     * Scan format string for conversion specifications.
  190. X     * (The labels would be loops, but then everything falls
  191. X     * off the right.)
  192. X     */
  193. Xscan:
  194. X    while ((ch = *cp++) != '%') {
  195. X        if (ch == 0)
  196. X            exit(EX_OK);
  197. X        (void) putchar(ch);
  198. X    }
  199. X
  200. X    ndyn = 0;
  201. X    flags = 0;
  202. X    convp = cbuf;
  203. X    *convp++ = ch;
  204. X
  205. X    /* scan for conversion character */
  206. Xcvt:
  207. X    switch (ch = *cp++) {
  208. X
  209. X    case '\0':    /* unterminated conversion */
  210. X        exit(EX_OK);
  211. X
  212. X    /* string or character format */
  213. X    case 'c': case 's':
  214. X        if (flags)
  215. X            illfmt(cbuf, convp, ch, hasmod);
  216. X        ap = doit(cbuf, convp, ap, ep, ndyn, ch, ch);
  217. X        goto scan;
  218. X
  219. X    /* integer formats */
  220. X    case 'd': case 'i': case 'o': case 'u': case 'x': case 'X':
  221. X        if ((flags & (LONG|SHORT)) == (LONG|SHORT))
  222. X            illfmt(cbuf, convp, ch, "is both long and short");
  223. X        ap = doit(cbuf, convp, ap, ep, ndyn, ch,
  224. X            flags & LONG ? 'l' : flags & SHORT ? 'h' : 'i');
  225. X        goto scan;
  226. X
  227. X    /* floating point formats */
  228. X    case 'e': case 'E': case 'f': case 'g': case 'G':
  229. X        if (flags)
  230. X            illfmt(cbuf, convp, ch, hasmod);
  231. X        ap = doit(cbuf, convp, ap, ep, ndyn, ch, 'f');
  232. X        goto scan;
  233. X
  234. X    /* Roman (well, why not?) */
  235. X    case 'r': case 'R':
  236. X        if (flags)
  237. X            illfmt(cbuf, convp, ch, hasmod);
  238. X        ap = doit(cbuf, convp, ap, ep, ndyn, 's', ch);
  239. X        goto scan;
  240. X
  241. X    case '%':    /* boring */
  242. X        (void) putchar('%');
  243. X        goto scan;
  244. X
  245. X    /* short integers */
  246. X    case 'h':
  247. X        flags |= SHORT;
  248. X        break;
  249. X
  250. X    /* long integers */
  251. X    case 'l':
  252. X        flags |= LONG;
  253. X        break;
  254. X
  255. X    /* field-width or precision specifier, or flag: keep scanning */
  256. X    case '.': case '#': case '-': case '+': case ' ':
  257. X    case '0': case '1': case '2': case '3': case '4':
  258. X    case '5': case '6': case '7': case '8': case '9':
  259. X        break;
  260. X
  261. X    /* dynamic field width or precision: count it */
  262. X    case '*':
  263. X        ndyn++;
  264. X        break;
  265. X
  266. X    default:    /* something we cannot handle */
  267. X        if (isascii(ch) && isprint(ch))
  268. X            cbuf[0] = ch, cbuf[1] = 0;
  269. X        else
  270. X            (void) sprintf(cbuf, "\\%03o", (unsigned char)ch);
  271. X        (void) fprintf(stderr,
  272. X            "%s: illegal conversion character `%s'\n",
  273. X            progname, cbuf);
  274. X        exit(EX_USAGE);
  275. X        /* NOTREACHED */
  276. X    }
  277. X
  278. X    /* 2 leaves room for ultimate conversion char and for \0 */
  279. X    if (convp >= &cbuf[sizeof(cbuf) - 2]) {
  280. X        (void) fprintf(stderr, "%s: conversion string too long\n",
  281. X            progname);
  282. X        exit(EX_USAGE);
  283. X    }
  284. X    *convp++ = ch;
  285. X    goto cvt;
  286. X}
  287. X
  288. Xillfmt(cbuf, convp, ch, why)
  289. X    char *cbuf, *convp;
  290. X    int ch;
  291. X    char *why;
  292. X{
  293. X
  294. X    *convp++ = ch;
  295. X    *convp = 0;
  296. X    (void) fprintf(stderr, "%s: format `%s' illegal: %s\n",
  297. X        progname, cbuf, why);
  298. X    exit(EX_USAGE);
  299. X}
  300. X
  301. X/*
  302. X * Emit a conversion.  cch holds the printf format character for
  303. X * this conversion; cty holds a simplified version (all integer
  304. X * conversions, e.g., are represented as 'i').
  305. X */
  306. Xchar **
  307. Xdoit(cbuf, convp, ap, ep, ndyn, cch, cty)
  308. X    char *cbuf, *convp;
  309. X    register char **ap;
  310. X    char **ep;
  311. X    register int ndyn;
  312. X    int cch, cty;
  313. X{
  314. X    register char *s;
  315. X    union {        /* four basic conversion types */
  316. X        int i;
  317. X        long l;
  318. X        double d;
  319. X        char *str;
  320. X    } arg;
  321. X    int a1, a2;    /* dynamic width and/or precision */
  322. X
  323. X    /* finish off the conversion string */
  324. X    s = convp;
  325. X    *s++ = cch;
  326. X    *s = 0;
  327. X    s = cbuf;
  328. X
  329. X    /* verify number of arguments */
  330. X    if (&ap[ndyn] >= ep) {
  331. X        (void) fprintf(stderr,
  332. X            "%s: not enough args for format `%s'\n",
  333. X            progname, s);
  334. X        exit(EX_USAGE);
  335. X    }
  336. X
  337. X    /* pick up dynamic specifiers */
  338. X    if (ndyn) {
  339. X        a1 = atoi(*ap++);
  340. X        if (ndyn > 1)
  341. X            a2 = atoi(*ap++);
  342. X        if (ndyn > 2) {
  343. X            (void) fprintf(stderr,
  344. X                "%s: too many `*'s in `%s'\n",
  345. X                progname, s);
  346. X            exit(EX_USAGE);
  347. X        }
  348. X    }
  349. X
  350. X#define    PRINTF(what) \
  351. X    if (ndyn == 0) \
  352. X        (void) printf(s, what); \
  353. X    else if (ndyn == 1) \
  354. X        (void) printf(s, a1, what); \
  355. X    else \
  356. X        (void) printf(s, a1, a2, what);
  357. X
  358. X    /* emit the appropriate conversion */
  359. X    switch (cty) {
  360. X
  361. X    /* string */
  362. X    case 's':
  363. X        ctrl(arg.str = *ap++);
  364. X        goto string;
  365. X
  366. X    /* roman (much like string) */
  367. X    case 'r': case 'R':
  368. X        arg.str = ctor(atoi(*ap++), cty == 'R');
  369. Xstring:
  370. X        PRINTF(arg.str);
  371. X        break;
  372. X
  373. X    /* floating point */
  374. X    case 'f':
  375. X        arg.d = atof(*ap++);
  376. X        PRINTF(arg.d);
  377. X        break;
  378. X
  379. X    /* character */
  380. X    case 'c':
  381. X        ctrl(*ap);
  382. X        arg.i = *(*ap++);
  383. X        goto integer;
  384. X
  385. X    /* short integer */
  386. X    case 'h':
  387. X        arg.i = (short) atoi(*ap++);
  388. X        goto integer;
  389. X
  390. X    /* integer */
  391. X    case 'i':
  392. X        arg.i = atoi(*ap++);
  393. Xinteger:
  394. X        PRINTF(arg.i);
  395. X        break;
  396. X
  397. X    /* long integer */
  398. X    case 'l':
  399. X        arg.l = atol(*ap++);
  400. X        PRINTF(arg.l);
  401. X        break;
  402. X    }
  403. X    return (ap);
  404. X}
  405. X
  406. X/*
  407. X * Return the index of the character c in the string s; character 0
  408. X * is NOT considered part of the string (unlike index() or strchr()).
  409. X * If c is not found (or is 0), return -1.
  410. X *
  411. X * This is used for hex and octal digit conversions in ctrl().
  412. X */
  413. Xint
  414. Xdigit(s, c)
  415. X    char *s;
  416. X    register int c;
  417. X{
  418. X    register char *p;
  419. X
  420. X    for (p = s; *p; p++)
  421. X        if (*p == c)
  422. X            return (p - s);
  423. X    return (-1);
  424. X}
  425. X
  426. X/*
  427. X * Convert backslash notation to control characters, in place.
  428. X */
  429. Xctrl(s)
  430. X    register char *s;
  431. X{
  432. X    register char *op = s;
  433. X    register int v, c;
  434. X    static char oct[] = "01234567";
  435. X    static char hex[] = "0123456789abcdefABCDEF";
  436. X
  437. X    while ((c = *s++) != 0) {
  438. X        if (c != '\\') {
  439. X            *op++ = c;
  440. X            continue;
  441. X        }
  442. X        switch (*s++) {
  443. X        case '\0':    /* end-of-string: user goofed */
  444. X            s--;
  445. X            break;
  446. X
  447. X        case '\\':    /* backslash */
  448. X            *op++ = '\\';
  449. X            break;
  450. X
  451. X        case 'n':    /* newline */
  452. X            *op++ = '\n';
  453. X            break;
  454. X
  455. X        case 't':    /* horizontal tab */
  456. X            *op++ = '\t';
  457. X            break;
  458. X
  459. X        case 'r':    /* carriage-return */
  460. X            *op++ = '\r';
  461. X            break;
  462. X
  463. X        case 'f':    /* form-feed */
  464. X            *op++ = '\f';
  465. X            break;
  466. X
  467. X        case 'b':    /* backspace */
  468. X            *op++ = '\b';
  469. X            break;
  470. X
  471. X        case 'v':    /* vertical tab */
  472. X            *op++ = '\13';
  473. X            break;
  474. X
  475. X        case 'a':    /* WARNING! DANGER! DANGER! DANGER! */
  476. X            *op++ = '\7';
  477. X            break;
  478. X
  479. X        case '0': case '1': case '2': case '3':
  480. X        case '4': case '5': case '6': case '7':
  481. X            /* octal constant, 3 digits maximum */
  482. X            v = digit(oct, s[-1]);
  483. X            if ((c = digit(oct, *s)) >= 0) {
  484. X                v = (v << 3) + c;
  485. X                if ((c = digit(oct, *++s)) >= 0) {
  486. X                    v = (v << 3) + c;
  487. X                    s++;
  488. X                }
  489. X            }
  490. X            *op++ = v;
  491. X            break;
  492. X
  493. X        case 'x':    /* hex constant */
  494. X            v = 0;
  495. X            while ((c = digit(hex, *s)) >= 0) {
  496. X                if (c >= 16)
  497. X                    c -= 6;
  498. X                v = (v << 4) + c;
  499. X                s++;
  500. X            }
  501. X            *op++ = v;
  502. X            break;
  503. X
  504. X        /*
  505. X         * The name of this object is taken from troff:
  506. X         * \z might be better, but this has a precedent.
  507. X         * It exists solely so that we can end a hex constant
  508. X         * which must be followed by a legal hex character.
  509. X         */
  510. X        case '&':    /* special zero-width `character' */
  511. X            break;
  512. X
  513. X        default:
  514. X            *op++ = s[-1];
  515. X        }
  516. X    }
  517. X    *op = '\0';
  518. X}
  519. X
  520. X/*
  521. X * Convert integer to Roman Numerals. (How have you survived without it?)
  522. X */
  523. Xchar *
  524. Xctor(x, caps)
  525. X    int x, caps;
  526. X{
  527. X    static char buf[BUFSIZ];
  528. X    register char *outp = buf;
  529. X    register unsigned n = x;
  530. X    register int u, v;
  531. X    register char *p, *q;
  532. X
  533. X    if ((int)n < 0) {
  534. X        *outp++ = '-';
  535. X        n = -n;
  536. X    }
  537. X    p = caps ? "M\2D\5C\2L\5X\2V\5I" : "m\2d\5c\2l\5x\2v\5i";
  538. X    v = 1000;
  539. X    if (n >= v * BUFSIZ / 2)    /* conservative */
  540. X        return ("[abortive Roman numeral]");
  541. X    for (;;) {
  542. X        while (n >= v)
  543. X            *outp++ = *p, n -= v;
  544. X        if (n == 0)
  545. X            break;
  546. X        q = p + 1;
  547. X        u = v / *q;
  548. X        if (*q == 2)        /* magic */
  549. X            u /= *(q += 2);
  550. X        if (n + u >= v) {
  551. X            *outp++ = *++q;
  552. X            n += u;
  553. X        } else {
  554. X            p++;
  555. X            v /= *p++;
  556. X        }
  557. X    }
  558. X    *outp = 0;
  559. X    return (buf);
  560. X}
  561. //go.sysin dd *
  562. if [ `wc -c < printf.c` != 8021 ]; then
  563.     made=false
  564.     echo error transmitting printf.c --
  565.     echo length should be 8021, not `wc -c < printf.c`
  566. else
  567.     made=true
  568. fi
  569. if $made; then
  570.     chmod 444 printf.c
  571.     echo -n '    '; ls -ld printf.c
  572. fi
  573. -- 
  574. Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
  575.