printf.c

Go to the documentation of this file.
00001 #if ever
00002 static char sccsid[] = "@(#)printf.c    (U of Maryland) FLB 6-Jan-1987";
00003 static char RCSid[] = "@(#)$Header: /opt/proj/minix/cvsroot/src/commands/simple/printf.c,v 1.1.1.1 2005/04/21 14:55:31 beng Exp $";
00004 #endif
00005 
00006 /*
00007  * Printf - Duplicate the C library routine of the same name, but from
00008  *          the shell command level.
00009  *
00010  * Fred Blonder <fred@Mimsy.umd.edu>
00011  *
00012  * To Compile:
00013  %      cc -s -O printf.c -o printf
00014  *
00015  * $Log: printf.c,v $
00016  * Revision 1.1.1.1  2005/04/21 14:55:31  beng
00017  * Initial import of pre-3.0.1
00018  *
00019  * Revision 1.1.1.1  2005/04/20 13:33:30  beng
00020  * Initial import of minix 2.0.4
00021  *
00022  * Revision 1.4  87/01/29  20:52:30  fred
00023  * Re-installed backslash-notation conversion for string & char arguments.
00024  * 
00025  * Revision 1.3  87/01/29  20:44:23  fred
00026  * Converted to portable algorithm.
00027  * Added Roman format for integers.
00028  *      29-Jan-87  FLB
00029  * 
00030  * Revision 1.2  87/01/09  19:10:57  fred
00031  * Fixed bug in argument-count error-checking.
00032  * Changed backslash escapes within strings to correspond to ANSII C
00033  *      draft standard.  (9-Jan-87 FLB)
00034  * 
00035  */
00036 
00037 #include <stdio.h>
00038 #include <stdlib.h>
00039 #include <string.h>
00040 #define EX_OK           0
00041 #define EX_USAGE        1
00042 
00043 int ctrl(char *s);
00044 
00045 #define atoi(a)         strtoul((a), NULL, 0)
00046 
00047 /****************************************************************************/
00048 
00049 int main(int argc, char *argv[])
00050 {
00051 register char *cp, *conv_spec, **argp, **ep;
00052 char *ctor(int x);
00053 
00054 if (argc < 2) {
00055         fprintf(stderr,
00056                 "printf: Usage: printf <format-string> [ arg1 . . . ]\n");
00057         exit(EX_USAGE);
00058         }
00059 
00060 argp = &argv[2];        /* Point at first arg (if any) beyond format string. */
00061 ep = &argv[argc];       /* Point beyond last arg. */
00062 
00063 ctrl(argv[1]);  /* Change backslash notation to control chars in fmt string. */
00064 
00065 /* Scan format string for conversion specifications, and do appropriate
00066    conversion on the corresponding argument. */
00067 for (cp = argv[1]; *cp; cp++) {
00068 register int dynamic_count;
00069 
00070         /* Look for next conversion spec. */
00071         while (*cp && *cp != '%') {
00072                 putchar(*cp++);
00073                 }
00074 
00075         if (!*cp)       /* End of format string */
00076                 break;
00077                 
00078         dynamic_count = 0;      /* Begin counting dynamic field width specs. */
00079         conv_spec = cp++;       /* Remember where this conversion begins. */
00080 
00081         for (;*cp; cp++) {      /* Scan until conversion character. */
00082                 char conv_buf[BUFSIZ];  /* Save conversion string here. */
00083                 register int conv_len;  /* Length of ``conv_buf''. */
00084 
00085                 switch (*cp) {  /* Field-width spec.: Keep scanning. */
00086                         case '.': case '0': case '1': case '2': case '3':
00087                         case '4': case '5': case '6': case '7': case '8':
00088                         case '9':
00089                                 continue;
00090 
00091                         case '*':       /* Dynamic field-width spec */
00092                                 dynamic_count++;
00093                                 continue;
00094 
00095                         case 's':       /* String */
00096                                 if (&argp[dynamic_count] >= ep) {
00097                                         fprintf(stderr,
00098                                         "printf: Not enough args for format.\n"
00099                                                 );
00100                                         exit(EX_USAGE);
00101                                         }
00102 
00103                                 (void) strncpy(conv_buf, conv_spec,
00104                                         conv_len = cp - conv_spec + 1);
00105                                 conv_buf[conv_len] = '\0';
00106 
00107                                 switch (dynamic_count) {
00108                                         case 0:
00109                                                 ctrl(*argp);
00110                                                 printf(conv_buf, *argp++);
00111                                                 break;
00112 
00113                                         case 1:
00114                                                 {
00115                                                 register int a1;
00116 
00117                                                 a1 = atoi(*argp++);
00118                                                 ctrl(*argp);
00119                                                 printf(conv_buf, a1, *argp++);
00120                                                 }
00121                                                 break;
00122 
00123                                         case 2:
00124                                                 {
00125                                                 register int a1, a2;
00126 
00127                                                 a1 = atoi(*argp++);
00128                                                 a2 = atoi(*argp++);
00129                                                 ctrl(*argp);
00130                                                 printf(conv_buf, a1, a2, *argp++);
00131                                                 }
00132                                                 break;
00133 
00134                                         }
00135                                 goto out;
00136 
00137                         case 'c':       /* Char */
00138                                 if (&argp[dynamic_count] >= ep) {
00139                                         fprintf(stderr,
00140                                         "printf: Not enough args for format.\n"
00141                                                 );
00142                                         exit(EX_USAGE);
00143                                         }
00144 
00145                                 (void) strncpy(conv_buf, conv_spec,
00146                                         conv_len = cp - conv_spec + 1);
00147                                 conv_buf[conv_len] = '\0';
00148 
00149                                 switch (dynamic_count) {
00150                                         case 0:
00151                                                 ctrl(*argp);
00152                                                 printf(conv_buf, **argp++);
00153                                                 break;
00154 
00155                                         case 1:
00156                                                 {
00157                                                 register int a1;
00158 
00159                                                 a1 = atoi(*argp++);
00160                                                 ctrl(*argp);
00161                                                 printf(conv_buf, a1, **argp++);
00162                                                 }
00163                                                 break;
00164 
00165                                         case 2:
00166                                                 {
00167                                                 register int a1, a2;
00168 
00169                                                 a1 = atoi(*argp++);
00170                                                 a2 = atoi(*argp++);
00171                                                 ctrl(*argp);
00172                                                 printf(conv_buf, a1, a2, **argp++);
00173                                                 }
00174                                                 break;
00175                                         }
00176                                 goto out;
00177 
00178                         case 'd':       /* Integer */
00179                         case 'o':
00180                         case 'x':
00181                         case 'X':
00182                         case 'u':
00183                                 if (&argp[dynamic_count] >= ep) {
00184                                         fprintf(stderr,
00185                                         "printf: Not enough args for format.\n"
00186                                                 );
00187                                         exit(EX_USAGE);
00188                                         }
00189 
00190                                 (void) strncpy(conv_buf, conv_spec,
00191                                         conv_len = cp - conv_spec + 1);
00192                                 conv_buf[conv_len] = '\0';
00193 
00194                                 switch (dynamic_count) {
00195                                         case 0:
00196                                                 printf(conv_buf, atoi(*argp++));
00197                                                 break;
00198 
00199                                         case 1:
00200                                                 {
00201                                                 register int a1;
00202 
00203                                                 a1 = atoi(*argp++);
00204                                                 printf(conv_buf, a1, atoi(*argp++));
00205                                                 }
00206                                                 break;
00207 
00208                                         case 2:
00209                                                 {
00210                                                 register int a1, a2;
00211 
00212                                                 a1 = atoi(*argp++);
00213                                                 a2 = atoi(*argp++);
00214                                                 printf(conv_buf, a1, a2, atoi(*argp++));
00215                                                 }
00216                                                 break;
00217 
00218                                         }
00219                                 goto out;
00220 
00221                         case 'f':       /* Real */
00222                         case 'e':
00223                         case 'g':
00224                                 if (&argp[dynamic_count] >= ep) {
00225                                         fprintf(stderr,
00226                                         "printf: Not enough args for format.\n"
00227                                                 );
00228                                         exit(EX_USAGE);
00229                                         }
00230 
00231                                 (void) strncpy(conv_buf, conv_spec,
00232                                         conv_len = cp - conv_spec + 1);
00233                                 conv_buf[conv_len] = '\0';
00234 
00235                                 switch (dynamic_count) {
00236                                         case 0:
00237                                                 printf(conv_buf, atof(*argp++));
00238                                                 break;
00239 
00240                                         case 1:
00241                                                 {
00242                                                 register int a1;
00243 
00244                                                 a1 = atoi(*argp++);
00245                                                 printf(conv_buf, a1, atof(*argp++));
00246                                                 }
00247                                                 break;
00248 
00249                                         case 2:
00250                                                 {
00251                                                 register int a1, a2;
00252 
00253                                                 a1 = atoi(*argp++);
00254                                                 a2 = atoi(*argp++);
00255                                                 printf(conv_buf, a1, a2, atof(*argp++));
00256                                                 }
00257                                                 break;
00258 
00259                                         }
00260                                 goto out;
00261 
00262                         case 'r':       /* Roman (Well, why not?) */
00263                                 if (&argp[dynamic_count] >= ep) {
00264                                         fprintf(stderr,
00265                                         "printf: Not enough args for format.\n"
00266                                                 );
00267                                         exit(EX_USAGE);
00268                                         }
00269 
00270                                 (void) strncpy(conv_buf, conv_spec,
00271                                         conv_len = cp - conv_spec + 1);
00272                                 conv_buf[conv_len] = '\0';
00273                                 conv_buf[conv_len - 1] = 's';
00274 
00275                                 switch (dynamic_count) {
00276                                         case 0:
00277                                                 printf(conv_buf,
00278                                                         ctor(atoi(*argp++)));
00279                                                 break;
00280 
00281                                         case 1:
00282                                                 {
00283                                                 register int a1;
00284 
00285                                                 a1 = atoi(*argp++);
00286                                                 printf(conv_buf, a1,
00287                                                         ctor(atoi(*argp++)));
00288                                                 }
00289                                                 break;
00290 
00291                                         case 2:
00292                                                 {
00293                                                 register int a1, a2;
00294 
00295                                                 a1 = atoi(*argp++);
00296                                                 a2 = atoi(*argp++);
00297                                                 printf(conv_buf, a1, a2,
00298                                                         ctor(atoi(*argp++)));
00299                                                 }
00300                                                 break;
00301 
00302                                         }
00303                                 goto out;
00304 
00305                         case '%':       /* Boring */
00306                                 putchar('%');
00307                                 break;
00308 
00309                         default:        /* Probably an error, but let user
00310                                            have his way. */
00311                                 continue;
00312                         }
00313                 }
00314         out: ;
00315         }
00316 
00317 exit(EX_OK);
00318 }
00319 
00320 /****************************************************************************/
00321 
00322 /* Convert backslash notation to control characters, in place. */
00323 
00324 int ctrl(char *s)
00325 {
00326 register char *op;
00327 static int val;
00328 
00329 for (op = s; *s; s++)
00330         if (*s == '\\')
00331                 switch (*++s) {
00332                         case '\0':      /* End-of-string: user goofed */
00333                                 goto out;
00334 
00335                         case '\\':      /* Backslash */
00336                                 *op++ = '\\';
00337                                 break;
00338 
00339                         case 'n':       /* newline */
00340                                 *op++ = '\n';
00341                                 break;
00342 
00343                         case 't':       /* horizontal tab */
00344                                 *op++ = '\t';
00345                                 break;
00346 
00347                         case 'r':       /* carriage-return */
00348                                 *op++ = '\r';
00349                                 break;
00350 
00351                         case 'f':       /* form-feed */
00352                                 *op++ = '\f';
00353                                 break;
00354 
00355                         case 'b':       /* backspace */
00356                                 *op++ = '\b';
00357                                 break;
00358 
00359                         case 'v':       /* vertical tab */
00360                                 *op++ = '\13';
00361                                 break;
00362 
00363                         case 'a':       /* WARNING! DANGER! DANGER! DANGER! */
00364                                 *op++ = '\7';
00365                                 break;
00366 
00367                         case '0': case '1': case '2': case '3':
00368                         case '4': case '5': case '6': case '7':
00369                                 {       /* octal constant */
00370                                 register int digits;
00371 
00372                                 val = 0;
00373                                 (void) sscanf(s, "%3o", &val);
00374                                 *op++ = val;
00375                                 for (digits = 3; s[1] &&
00376                                         strchr("01234567", s[1])
00377                                         && --digits > 0;
00378                                                 s++);
00379                                 }
00380                                 break;
00381 
00382                         case 'x':       /* hex constant */
00383                         case 'X':
00384                                 s++;
00385                                 {
00386                                 register int digits;
00387 
00388                                 val = 0;
00389                                 (void) sscanf(s, "%3x", &val);
00390                                 *op++ = val;
00391                                 for (digits = 3; *s && s[1] &&
00392                                         strchr("0123456789abcdefABCDEF",
00393                                                                         s[1])
00394                                         && --digits > 0;
00395                                                 s++);
00396                                 }
00397                                 break;
00398 
00399                         }
00400         else
00401                 *op++ = *s;
00402 
00403 out:
00404 
00405 *op = '\0';
00406 }
00407 
00408 /****************************************************************************/
00409 
00410 /* Convert integer to Roman Numerals. (Have have you survived without it?) */
00411 
00412 struct roman {
00413         unsigned r_mag;
00414         char r_units, r_fives;
00415         } roman[] = {
00416                 { 1000, 'M', '\0', },
00417                 {  100, 'C', 'D',  },
00418                 {   10, 'X', 'L',  },
00419                 {    1, 'I', 'V',  },
00420                 };
00421 
00422 char *ctor(int x)
00423 {
00424 register struct roman *mp;
00425 static char buf[BUFSIZ];
00426 register char *cp = buf;
00427 
00428 /* I've never actually seen a roman numeral with a minus-sign.
00429    Probably ought to print out some appropriate latin phrase instead. */
00430 if (x < 0) {
00431         *cp++ = '-';
00432         x = -x;
00433         }
00434 
00435 for (mp = roman; x; mp++) {
00436         register unsigned units;
00437 
00438         units = x / mp->r_mag;
00439         x = x % mp->r_mag;
00440 
00441         if (cp > &buf[BUFSIZ-2])
00442                 return "???";
00443 
00444         if (units == 9 && mp > roman) { /* Do inverse notation: Eg: ``IX''. */
00445                 *cp++ = mp->r_units;
00446                 *cp++ = mp[-1].r_units;
00447                 }
00448         else if (units == 4 && mp->r_fives) {
00449                 /* Inverse notation for half-decades: Eg: ``IV'' */
00450                 *cp++ = mp->r_units;
00451                 *cp++ = mp->r_fives;
00452                 }
00453         else {  /* Additive notation */
00454                 if (units >= 5 && mp->r_fives) {
00455                         *cp++ = mp->r_fives;
00456                         units -= 5;
00457                         }
00458                 while (units--) {
00459                         *cp++ = mp->r_units;
00460                         if (cp > &buf[BUFSIZ-5])
00461                                 return "???";
00462                         }
00463                 }
00464         }
00465 
00466 *cp = '\0';
00467 
00468 return buf;
00469 }
00470 
00471 /****************************************************************************/

Generated on Fri Apr 14 22:57:10 2006 for minix by  doxygen 1.4.6