home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / OS2 / EMXFIX04.ZIP / OUTPUT.C < prev    next >
C/C++ Source or Header  |  1994-01-24  |  23KB  |  873 lines

  1. /* output.c (emx+gcc) -- Copyright (c) 1990-1994 by Eberhard Mattes */
  2.  
  3. #include <sys/emx.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <stdarg.h>
  7. #include <string.h>
  8. #include <setjmp.h>
  9. #include <float.h>
  10. #include <limits.h>
  11.  
  12. #define DEFAULT_PREC    6
  13.  
  14. #define BEGIN do {
  15. #define END   } while (0)
  16.  
  17. #define PUTC(V,C) BEGIN \
  18.                     if (putc (C, (V)->stream) == EOF) \
  19.                       longjmp ((V)->jerror, 1); \
  20.                     ++(V)->count; \
  21.                   END
  22.  
  23. #define STRLEN(S) (((S) == NULL) ? 0 : strlen (S))
  24.  
  25. /* This structure holds the local variables of _output() which are
  26.    passed to the various functions called by _output(). */
  27.  
  28. typedef struct
  29. {
  30.   FILE *stream;                 /* Where output should go */
  31.   char minus;                   /* Non-zero if `-' flag present */
  32.   char plus;                    /* Non-zero if `+' flag present */
  33.   char blank;                   /* Non-zero if ` ' flag present */
  34.   char hash;                    /* Non-zero if `#' flag present */
  35.   char pad;                     /* Pad character (' ' or '0')  */
  36.   int width;                    /* Field width (or 0 if none specified) */
  37.   int prec;                     /* Precision (or -1 if none specified) */
  38.   int lim;                      /* Number of significant digits */
  39.   int count;                    /* Number of characters printed */
  40.   jmp_buf jerror;               /* Jump there on output error */
  41. } olocal;
  42.  
  43.  
  44. /* Print the first N characters of the string S. */
  45.  
  46. static void out_str (olocal *v, const char *s, int n)
  47. {
  48.   if (n >= 16)
  49.     {
  50.       if (fwrite (s, 1, n, v->stream) != n)
  51.         longjmp (v->jerror, 1);
  52.       v->count += n;
  53.     }
  54.   else
  55.     while (n > 0)
  56.       {
  57.         PUTC (v, *s);
  58.         ++s; --n;
  59.       }
  60. }
  61.  
  62.  
  63. /* Print the character C N times. */
  64.  
  65. static void out_pad (olocal *v, char c, int n)
  66. {
  67.   char buf[256];
  68.  
  69.   if (n > sizeof (buf))
  70.     {
  71.       int i;
  72.  
  73.       /* Very big padding -- do 256 characters at a time. */
  74.       memset (buf, c, sizeof (buf));
  75.       while (n > 0)
  76.         {
  77.           i = sizeof (buf);
  78.           if (i > n)
  79.             i = n;
  80.           out_str (v, buf, i);
  81.           n -= i;
  82.         }
  83.     }
  84.   else if (n >= 16)
  85.     {
  86.       memset (buf, c, n);
  87.       out_str (v, buf, n);
  88.     }
  89.   else
  90.     while (n > 0)
  91.       {
  92.         PUTC (v, c);
  93.         --n;
  94.       }
  95. }
  96.  
  97.  
  98. /* Perform formatting for the "%c" and "%s" formats.  The meaning of
  99.    the '0' flag is not defined by ANSI for "%c" and "%s"; we always
  100.    use blanks for padding. */
  101.  
  102. static void cvt_str (olocal *v, const char *str)
  103. {
  104.   int str_len;
  105.  
  106.   if (str == NULL)
  107.     {
  108.       /* Print "(null)" for the NULL pointer if the precision is big
  109.          enough or not specified. */
  110.  
  111.       if (v->prec < 0 || v->prec >= 6)
  112.         str = "(null)";
  113.       else
  114.         str = "";
  115.     }
  116.   str_len = strlen (str);
  117.  
  118.   /* Limit the number of characters printed by the precision. */
  119.  
  120.   if (v->prec >= 0 && v->prec < str_len)
  121.     str_len = v->prec;
  122.  
  123.   /* Print the string, using blanks for padding. */
  124.  
  125.   if (str_len < v->width && !v->minus)
  126.     out_pad (v, ' ', v->width - str_len);
  127.   out_str (v, str, str_len);
  128.   if (str_len < v->width && v->minus)
  129.     out_pad (v, ' ', v->width - str_len);
  130. }
  131.  
  132.  
  133. /* Print and pad a number (which has already been converted into
  134.    strings).  PFX is "0" for non-zero octal numbers, "0x" or "0X" for
  135.    non-zero hexadecimal numbers, "0." for floating point numbers in
  136.    [0.0,1.0) unless using exponential format, the significant digits
  137.    for large floating point numbers in the "%f" format, or NULL.
  138.    Insert LPAD0 zeros between PFX and STR.  STR is the number.  Insert
  139.    RPAD0 zeros between STR and XPS.  XPS is the exponent (unless it is
  140.    NULL).  IS_SIGNED is non-zero when printing a signed number. IS_NEG
  141.    is non-zero for negative numbers (the strings don't contain a
  142.    sign). */
  143.  
  144. static void cvt_number (olocal *v, const char *pfx, const char *str,
  145.                         const char *xps, int lpad0, int rpad0,
  146.                         int is_signed, int is_neg)
  147. {
  148.   int sign, pfx_len, str_len, xps_len, min_len;
  149.  
  150.   if (!is_signed)               /* No sign for %u, %o and %x */
  151.     sign = EOF;
  152.   else if (is_neg)
  153.     sign = '-';
  154.   else if (v->plus)             /* '+' overrides ' ' */
  155.     sign = '+';
  156.   else if (v->blank)
  157.     sign = ' ';
  158.   else
  159.     sign = EOF;
  160.  
  161.   pfx_len = STRLEN (pfx);
  162.   str_len = strlen (str);
  163.   xps_len = STRLEN (xps);
  164.  
  165.   if (lpad0 < 0)
  166.     lpad0 = 0;
  167.   if (rpad0 < 0)
  168.     rpad0 = 0;
  169.  
  170.   /* Compute the minimum length required for printing the number. */
  171.  
  172.   min_len = lpad0 + pfx_len + str_len + rpad0 + xps_len;
  173.   if (sign != EOF)
  174.     ++min_len;
  175.  
  176.   /* If padding with zeros is requested, increase LPAD0 to pad the
  177.      number on the left with zeros.  Note that the `-' flag
  178.      (left-justify) turns off padding with zeros. */
  179.  
  180.   if (v->pad == '0' && min_len < v->width)
  181.     {
  182.       lpad0 += v->width - min_len;
  183.       min_len = v->width;
  184.     }
  185.  
  186.   /* Pad on the left with blanks. */
  187.  
  188.   if (min_len < v->width && !v->minus)
  189.     out_pad (v, ' ', v->width - min_len);
  190.  
  191.   /* Print the number. */
  192.  
  193.   if (sign != EOF)
  194.     PUTC (v, sign);
  195.   out_str (v, pfx, pfx_len);
  196.   out_pad (v, '0', lpad0);
  197.   out_str (v, str, str_len);
  198.   out_pad (v, '0', rpad0);
  199.   out_str (v, xps, xps_len);
  200.  
  201.   /* Pad on the right with blanks. */
  202.  
  203.   if (min_len < v->width && v->minus)
  204.     out_pad (v, ' ', v->width - min_len);
  205. }
  206.  
  207.  
  208. /* Print and pad an integer (which has already been converted into the
  209.    string STR).  PFX is "0" for non-zero octal numbers, "0x" or "0X"
  210.    for hexadecimal numbers, or NULL.  ZERO is non-zero if the number
  211.    is zero.  IS_NEG is non-zero if the number is negative (the string
  212.    STR doesn't contain a sign). */
  213.  
  214. static void cvt_integer (olocal *v, const char *pfx, const char *str,
  215.                          int zero, int is_signed, int is_neg)
  216. {
  217.   int lpad0;
  218.  
  219.   if (zero && v->prec == 0)     /* ANSI */
  220.     return;
  221.  
  222.   if (v->prec >= 0)             /* Ignore `0' if `-' is given for an integer */
  223.     v->pad = ' ';
  224.  
  225.   lpad0 = v->prec - strlen (str);
  226.   cvt_number (v, pfx, str, NULL, lpad0, 0, is_signed, is_neg);
  227. }
  228.  
  229.  
  230. static void cvt_hex (olocal *v, char *str, char x, int zero)
  231. {
  232.   if (x == 'X')
  233.     strupr (str);
  234.   cvt_integer (v, ((!zero && v->hash) ? "0x" : NULL),
  235.                str, zero, FALSE, FALSE);
  236. }
  237.  
  238.  
  239. static void cvt_hex_32 (olocal *v, unsigned n, char x)
  240. {
  241.   char buf[9];
  242.  
  243.   _ltoa (n, buf, 16);
  244.   cvt_hex (v, buf, x, n == 0);
  245. }
  246.  
  247.  
  248. static void cvt_hex_64 (olocal *v, unsigned long long n, char x)
  249. {
  250.   char buf[17];
  251.  
  252.   _ulltoa (n, buf, 16);
  253.   cvt_hex (v, buf, x, n == 0);
  254. }
  255.  
  256.  
  257. static void cvt_oct (olocal *v, char *str, int zero)
  258. {
  259.   cvt_integer (v, ((!zero && v->hash) ? "0" : NULL),
  260.                str, zero, FALSE, FALSE);
  261. }
  262.  
  263.  
  264. static void cvt_oct_32 (olocal *v, unsigned n)
  265. {
  266.   char buf[12];
  267.  
  268.   _ltoa (n, buf, 8);
  269.   cvt_oct (v, buf, n == 0);
  270. }
  271.  
  272.  
  273. static void cvt_oct_64 (olocal *v, unsigned long long n)
  274. {
  275.   char buf[23];
  276.  
  277.   _ulltoa (n, buf, 8);
  278.   cvt_oct (v, buf, n == 0);
  279. }
  280.  
  281.  
  282. static void cvt_dec_32 (olocal *v, unsigned n, int is_signed, int is_neg)
  283. {
  284.   char buf[11];
  285.  
  286.   _ultoa (n, buf, 10);
  287.   cvt_integer (v, NULL, buf, n == 0, is_signed, is_neg);
  288. }
  289.  
  290.  
  291. static void cvt_dec_64 (olocal *v, unsigned long long n, int is_signed,
  292.                         int is_neg)
  293. {
  294.   char buf[21];
  295.  
  296.   _ulltoa (n, buf, 10);
  297.   cvt_integer (v, NULL, buf, n == 0, is_signed, is_neg);
  298. }
  299.  
  300.  
  301. /* Print a floating point number (which has been turned into the digit
  302.    string DIGITS and the exponent XP) for the "%f" format.  If IS_NEG
  303.    is non-zero, the number is negative. */
  304.  
  305. static void cvt_fixed_digits (olocal *v, char *digits, int xp, int is_neg,
  306.                               int is_auto)
  307. {
  308.   int lpad0, rpad0, len, frac;
  309.  
  310.   /* We have to handle 9 cases (the examples are for DIG=4):
  311.  
  312.          | Fmt | Number    || digits | xp | pfx  |lpad0|rpad0| Output
  313.      ----+-----+-----------++--------+----+------+-----+-----+---------
  314.      (1) | .3f | 0.0001234 || "1234" | -4 | "0." | 0   | 3   | 0.000
  315.      (2) | .7f | 0.001234  || "1234" | -3 | "0." | 2   | 1   | 0.0012340
  316.      (3) | .3f | 0.001234  || "1234" | -3 | "0." | 2   | 0   | 0.001
  317.      (4) | .3f | 0.1234    || "1234" | -1 | "0." | 0   | 0   | 0.123
  318.      (5) | .3f | 1.234     || "1234" |  0 | N/A  | N/A | 0   | 1.234
  319.      (6) | .1f | 12.34     || "1234" |  1 | N/A  | N/A | 0   | 12.3
  320.      (7) | .3f | 123.4     || "1234" |  2 | N/A  | N/A | 2   | 123.400
  321.      (8) | .3f | 1234.0    || "1234" |  3 | N/A  | N/A | 3   | 1234.000
  322.      (9) | .3f | 123456.0  || "1235" |  5 |"1235"| 2   | 3   | 123500.000
  323.  
  324.    */
  325.  
  326.  
  327.   if (xp < 0)
  328.     {
  329.       /* Cases (1) through (4).  We print "0." followed by
  330.  
  331.            min (-xp - 1, v->prec)
  332.  
  333.          zeros and the string of digit. */
  334.  
  335.       lpad0 = -xp - 1;          /* This is for cases (2) through (3) */
  336.       if (v->prec < lpad0)
  337.         lpad0 = v->prec;        /* This is for case (1) */
  338.  
  339.       /* Compute the number of zeros to append. */
  340.  
  341.       rpad0 = v->prec - (lpad0 + DIG); /* This is for case (2) */
  342.       if (rpad0 < 0)
  343.         rpad0 = 0;              /* This is for cases (1), (3) and (4) */
  344.  
  345.       /* Truncate the string of digits according to the precision for
  346.          cases (1), (3) and (4). */
  347.  
  348.       len = v->prec - lpad0;
  349.       if (len >= 0 && len < DIG)
  350.         digits[len] = 0;
  351.  
  352.       if (is_auto)
  353.         {
  354.           rpad0 = 0;
  355.           _cvt_remove_zeros (digits, 0);
  356.           if (digits[0] == 0)
  357.             lpad0 = 0;
  358.         }
  359.  
  360.       cvt_number (v, ((v->hash || digits[0] != 0 || lpad0 != 0) ? "0." : "0"),
  361.                   digits, NULL, lpad0, rpad0, TRUE, is_neg);
  362.     }
  363.   else if (xp < DIG)
  364.     {
  365.       /* Cases (5) through (8). */
  366.  
  367.       /* Compute the number of zeros to append and truncate the string
  368.          of digits. */
  369.  
  370.       frac = DIG - xp - 1;      /* Number of decimals (frac >= 0) */
  371.       rpad0 = v->prec - frac;   /* For cases (5), (7) and (8) */
  372.       if (rpad0 < 0)
  373.         {
  374.           /* Case (6) */
  375.           digits[DIG + rpad0] = 0;
  376.           rpad0 = 0;
  377.         }
  378.  
  379.       if (is_auto)
  380.         {
  381.           _cvt_remove_zeros (digits, xp + 1);
  382.           rpad0 = 0;
  383.         }
  384.  
  385.       /* Insert the decimal point. */
  386.       if (v->hash || digits[xp + 1] != 0 || rpad0 != 0)
  387.         {
  388.           memmove (digits + xp + 2, digits + xp + 1, DIG - xp);
  389.           digits[xp + 1] = '.';
  390.         }
  391.       cvt_number (v, NULL, digits, NULL, 0, rpad0, TRUE, is_neg);
  392.     }
  393.   else
  394.     {
  395.       /* Case (9). */
  396.  
  397.       lpad0 = xp - DIG + 1;
  398.       rpad0 = (is_auto ? 0 : v->prec);
  399.  
  400.       cvt_number (v, digits, ((v->hash || rpad0 != 0) ? "." : ""),
  401.                   NULL, lpad0, rpad0, TRUE, is_neg);
  402.     }
  403. }
  404.  
  405.  
  406. /* Print a floating point number (which has been turned into the digit
  407.    string DIGITS and the exponent XP) for the "%e" and "%g" formats.
  408.    If IS_NEG is non-zero, the number is negative.  XP_CHAR is 'e' or
  409.    'E'.  If IS_AUTO is non-zero, we should omit trailing zeros for
  410.    the "%g" format. */
  411.  
  412. static void cvt_exp_digits (olocal *v, char *digits, int xp, int is_neg,
  413.                             char xp_char, int is_auto)
  414. {
  415.   int i, rpad0;
  416.   char xps_buf[10];
  417.  
  418.   xps_buf[0] = xp_char;
  419.   xps_buf[1] = '+';
  420.   if (xp < 0)
  421.     {
  422.       xps_buf[1] = '-';
  423.       xp = -xp;
  424.     }
  425.   i = 2;
  426.   if (xp >= 1000)
  427.     xps_buf[i++] = (char)((xp / 1000) % 10) + '0';
  428.   if (xp >= 100)
  429.     xps_buf[i++] = (char)((xp / 100) % 10) + '0';
  430.   xps_buf[i++] = (char)((xp / 10) % 10) + '0';
  431.   xps_buf[i++] = (char)(xp % 10) + '0';
  432.   xps_buf[i] = 0;
  433.  
  434.   /* Insert decimal point. */
  435.  
  436.   if (v->prec == 0 && !v->hash)
  437.     {
  438.       digits[1] = 0;
  439.       rpad0 = 0;
  440.     }
  441.   else
  442.     {
  443.       memmove (digits + 2, digits + 1, DIG);
  444.       digits[1] = '.';
  445.       if (v->prec >= DIG)
  446.         rpad0 = 1 + v->prec - DIG;
  447.       else
  448.         {
  449.           rpad0 = 0;
  450.           digits[2 + v->prec] = 0;
  451.         }
  452.       if (is_auto)
  453.         {
  454.           _cvt_remove_zeros (digits, 2);
  455.           if (digits[2] == 0)
  456.             digits[1] = 0;
  457.           rpad0 = 0;
  458.         }
  459.     }
  460.   cvt_number (v, NULL, digits, xps_buf, 0, rpad0, TRUE, is_neg);
  461. }
  462.  
  463.  
  464. /* Perform formatting for the "%f" format. */
  465.  
  466. static void cvt_fixed (olocal *v, DOUBLE x, int is_neg)
  467. {
  468.  
  469.   if (x == 0.0)
  470.     cvt_number (v, NULL, ((v->hash || v->prec > 0) ? "0." : "0"),
  471.                 NULL, 0, v->prec, TRUE, is_neg);
  472.   else
  473.     {
  474.       char digits[DIG+2];
  475.       int xp;
  476.  
  477.       xp = _cvt_float_decimal (x, digits);
  478.       _cvt_round (digits, &xp, xp + 1 + v->prec, v->lim);
  479.       cvt_fixed_digits (v, digits, xp, is_neg, FALSE);
  480.     }
  481. }
  482.  
  483.  
  484. /* Perform formatting for the "%e" format.  XP_CHAR is 'e' or 'E'. */
  485.  
  486. static void cvt_exp (olocal *v, DOUBLE x, int is_neg, char xp_char)
  487. {
  488.   if (x == 0.0)
  489.     {
  490.       static char xps_buf[] = "e+00";
  491.  
  492.       xps_buf[0] = xp_char;
  493.       cvt_number (v, NULL, ((v->hash || v->prec > 0) ? "0." : "0"),
  494.                   xps_buf, 0, v->prec, TRUE, FALSE);
  495.     }
  496.   else
  497.     {
  498.       char digits[DIG+2];
  499.       int xp;
  500.  
  501.       xp = _cvt_float_decimal (x, digits);
  502.       _cvt_round (digits, &xp, v->prec + 1, v->lim);
  503.       cvt_exp_digits (v, digits, xp, is_neg, xp_char, FALSE);
  504.     }
  505. }
  506.  
  507.  
  508. /* Perform formatting for the "%g" format.  XP_CHAR is 'e' or 'E'. */
  509.  
  510. static void cvt_auto (olocal *v, DOUBLE x, int is_neg, char xp_char)
  511. {
  512.   /* A precision of zero is treated as a precision of 1.  Note that
  513.      the precision defines the number of significant digits, not the
  514.      number of decimals! */
  515.  
  516.   if (v->prec == 0)
  517.     v->prec = 1;
  518.  
  519.   /* 0.0 is treated specially as _cvt_float_decimal etc. cannot handle
  520.      that case. */
  521.  
  522.   if (x == 0.0)
  523.     cvt_number (v, NULL, (v->hash ? "0." : "0"), NULL,
  524.                 0, (v->hash ? v->prec : 0), TRUE, FALSE);
  525.   else
  526.     {
  527.       char digits[DIG+2];
  528.       int xp;
  529.  
  530.       /* Convert the number to decimal and round it according to the
  531.          precision (number of significant digits). */
  532.  
  533.       xp = _cvt_float_decimal (x, digits);
  534.       _cvt_round (digits, &xp, v->prec, v->lim);
  535.  
  536.       /* If the exponent (of the "%e" format) is less than -4 or
  537.          greater than or equal to the precision, use the "%e"
  538.          format.  Otherwise, use the "%f" format. */
  539.  
  540.       if (xp < -4 || xp >= v->prec)
  541.         {
  542.           /* Adjust the precision to indicate the number of
  543.              decimals. */
  544.  
  545.           --v->prec;
  546.  
  547.           /* Treat "%#g" like "%e" (except for the precision).  "%g"
  548.              (without `#') removes trailing zeros. */
  549.  
  550.           cvt_exp_digits (v, digits, xp, is_neg, xp_char, !v->hash);
  551.         }
  552.       else
  553.         {
  554.           /* Compute the number of decimals from the exponent and the
  555.              precision.  We must not remove trailing zeros from the
  556.              integer part of the number! */
  557.  
  558.           v->prec -= xp + 1;
  559.           if (v->prec < 0)
  560.             v->prec = 0;
  561.  
  562.           cvt_fixed_digits (v, digits, xp, is_neg, !v->hash);
  563.         }
  564.     }
  565. }
  566.  
  567.  
  568. /* Print the floating point number X.  LIM is the number of
  569.    significant digits of X (depending on the type of X), FMT is the
  570.    format character from the format string. */
  571.  
  572. static void cvt_float (olocal *v, DOUBLE x, int lim, char fmt)
  573. {
  574.   const char *s;
  575.   int is_neg;
  576.  
  577.   s = _cvt_nan (FXAM (x));
  578.   if (s != NULL)
  579.     {
  580.       v->prec = -1;             /* TODO: Is this correct? */
  581.       cvt_str (v, s);           /* TODO: Is this correct? */
  582.       return;
  583.     }
  584.  
  585.   v->lim = lim;
  586.   if (v->prec < 0)
  587.     v->prec = DEFAULT_PREC;
  588.  
  589.   is_neg = (x < 0.0);
  590.   if (is_neg)
  591.     x = -x;
  592.  
  593.   switch (fmt)
  594.     {
  595.     case 'f':
  596.       cvt_fixed (v, x, is_neg);
  597.       break;
  598.  
  599.     case 'g':
  600.       cvt_auto (v, x, is_neg, 'e');
  601.       break;
  602.  
  603.     case 'G':
  604.       cvt_auto (v, x, is_neg, 'E');
  605.       break;
  606.  
  607.     case 'e':
  608.       cvt_exp (v, x, is_neg, 'e');
  609.       break;
  610.  
  611.     case 'E':
  612.       cvt_exp (v, x, is_neg, 'E');
  613.       break;
  614.  
  615.     default:
  616.       abort ();
  617.     }
  618. }
  619.   
  620.  
  621. /* This is the working horse for printf() and friends. */
  622.  
  623. int _output (FILE *stream, const char *format, char *arg_ptr)
  624. {
  625.   olocal v;
  626.   char size, cont;
  627.  
  628.   /* Initialize local variables. */
  629.  
  630.   v.stream = stream;
  631.   v.count = 0;
  632.  
  633.   /* Return -1 on output error. */
  634.  
  635.   if (setjmp (v.jerror) != 0)
  636.     return (-1);
  637.  
  638.   /* Interpret the string. */
  639.  
  640.   while (*format != 0)
  641.     if (*format != '%')
  642.       {
  643.         PUTC (&v, *format);
  644.         ++format;
  645.       }
  646.     else if (format[1] == '%')
  647.       {
  648.         PUTC (&v, '%');
  649.         format += 2;
  650.       }
  651.     else
  652.       {
  653.         v.minus = v.plus = v.blank = v.hash = FALSE;
  654.         v.width = 0; v.prec = -1; size = 0; v.pad = ' ';
  655.         cont = TRUE;
  656.         do
  657.           {
  658.             ++format;
  659.             switch (*format)
  660.               {
  661.               case '-':
  662.                 v.minus = TRUE;
  663.                 break;
  664.               case '+':
  665.                 v.plus = TRUE;
  666.                 break;
  667.               case '0':
  668.                 v.pad = '0';
  669.                 break;
  670.               case ' ':
  671.                 v.blank = TRUE;
  672.                 break;
  673.               case '#':
  674.                 v.hash = TRUE;
  675.                 break;
  676.               default: 
  677.                 cont = FALSE;
  678.                 break;
  679.               }
  680.           } while (cont);
  681.  
  682.         /* `-' overrides `0' */
  683.  
  684.         if (v.minus)
  685.           v.pad = ' ';
  686.  
  687.         /* Field width */
  688.  
  689.         if (*format == '*')
  690.           {
  691.             ++format;
  692.             v.width = va_arg (arg_ptr, int);
  693.             if (v.width < 0)
  694.               {
  695.                 v.width = -v.width;
  696.                 v.minus = TRUE;
  697.               }
  698.           }
  699.         else
  700.           while (*format >= '0' && *format <= '9')
  701.             {
  702.               v.width = v.width * 10 + (*format - '0');
  703.               ++format;
  704.             }
  705.  
  706.         /* Precision */
  707.  
  708.         if (*format == '.')
  709.           {
  710.             ++format;
  711.             if (*format == '*')
  712.               {
  713.                 ++format;
  714.                 v.prec = va_arg (arg_ptr, int);
  715.                 if (v.prec < 0)
  716.                   v.prec = -1;  /* We don't need this */
  717.               }
  718.             else
  719.               {
  720.                 v.prec = 0;
  721.                 while (*format >= '0' && *format <= '9')
  722.                   {
  723.                     v.prec = v.prec * 10 + (*format - '0');
  724.                     ++format;
  725.                   }
  726.               }
  727.           }
  728.  
  729.         /* Size */
  730.  
  731.         if (*format == 'h' || *format == 'l' || *format == 'L')
  732.           {
  733.             size = *format++;
  734.             if (size == 'l' && *format == 'l')
  735.               {
  736.                 size = 'L'; ++format;
  737.               }
  738.           }
  739.  
  740.         /* Format */
  741.  
  742.         switch (*format)
  743.           {
  744.           case 0:
  745.             return (v.count);
  746.  
  747.           case 'n':
  748.             if (size == 'L')
  749.               {
  750.                 long long *ptr = va_arg (arg_ptr, long long *);
  751.                 *ptr = v.count;
  752.               }
  753.             else if (size == 'h')
  754.               {
  755.                 short *ptr = va_arg (arg_ptr, short *);
  756.                 *ptr = v.count;
  757.               }
  758.             else
  759.               {
  760.                 int *ptr = va_arg (arg_ptr, int *);
  761.                 *ptr = v.count;
  762.               }
  763.             break;
  764.  
  765.           case 'c':
  766.             {
  767.               char c;
  768.  
  769.               c = (char)va_arg (arg_ptr, int);
  770.               v.prec = 1;
  771.               cvt_str (&v, &c);
  772.             }
  773.             break;
  774.  
  775.           case 's':
  776.             cvt_str (&v, va_arg (arg_ptr, const char *));
  777.             break;
  778.  
  779.           case 'd':
  780.           case 'i':
  781.             if (size == 'L')
  782.               {
  783.                 long long n = va_arg (arg_ptr, long long);
  784.                 if (n < 0)
  785.                   cvt_dec_64 (&v, -n, TRUE, TRUE);
  786.                 else
  787.                   cvt_dec_64 (&v, n, TRUE, FALSE);
  788.               }
  789.             else
  790.               {
  791.                 int n = va_arg (arg_ptr, int);
  792.                 if (size == 'h')
  793.                   n = (short)n;
  794.                 if (n < 0)
  795.                   cvt_dec_32 (&v, -n, TRUE, TRUE);
  796.                 else
  797.                   cvt_dec_32 (&v, n, TRUE, FALSE);
  798.               }
  799.             break;
  800.  
  801.           case 'u':
  802.             if (size == 'L')
  803.               cvt_dec_64 (&v, va_arg (arg_ptr, unsigned long long),
  804.                           FALSE, FALSE);
  805.             else
  806.               {
  807.                 unsigned n = va_arg (arg_ptr, unsigned);
  808.                 if (size == 'h')
  809.                   n = (unsigned short)n;
  810.                 cvt_dec_32 (&v, n, FALSE, FALSE);
  811.               }
  812.             break;
  813.  
  814.           case 'p':
  815.             v.hash = TRUE;
  816.             cvt_hex_32 (&v, va_arg (arg_ptr, unsigned), 'x');
  817.             break;
  818.  
  819.           case 'x':
  820.           case 'X':
  821.             if (size == 'L')
  822.               cvt_hex_64 (&v, va_arg (arg_ptr, unsigned long long), *format);
  823.             else
  824.               {
  825.                 unsigned n = va_arg (arg_ptr, unsigned);
  826.                 if (size == 'h')
  827.                   n = (unsigned short)n;
  828.                 cvt_hex_32 (&v, n, *format);
  829.               }
  830.             break;
  831.  
  832.           case 'o':
  833.             if (size == 'L')
  834.               cvt_oct_64 (&v, va_arg (arg_ptr, unsigned long long));
  835.             else
  836.               {
  837.                 unsigned n = va_arg (arg_ptr, unsigned);
  838.                 if (size == 'h')
  839.                   n = (unsigned short)n;
  840.                 cvt_oct_32 (&v, n);
  841.               }
  842.             break;
  843.  
  844.           case 'g':
  845.           case 'G':
  846.           case 'e':
  847.           case 'E':
  848.           case 'f':
  849.             if (size == 'L')
  850.               {
  851.                 DOUBLE x = va_arg (arg_ptr, long double);
  852.                 cvt_float (&v, x, DIG, *format);
  853.               }
  854.             else
  855.               {
  856.                 double x = va_arg (arg_ptr, double);
  857.                 cvt_float (&v, x, DBL_DIG, *format);
  858.               }
  859.             break;
  860.  
  861.           default:
  862.             /* TODO: print the last letter only or all the characters? */
  863.             out_str (&v, format, 1);
  864.             break;
  865.           }
  866.         ++format;
  867.       }
  868.  
  869.   /* Return the number of characters printed. */
  870.  
  871.   return (v.count);
  872. }
  873.