Back to index

lightning-sunbird  0.9+nobinonly
cordprnt.c
Go to the documentation of this file.
00001 /* 
00002  * Copyright (c) 1993-1994 by Xerox Corporation.  All rights reserved.
00003  *
00004  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
00005  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
00006  *
00007  * Permission is hereby granted to use or copy this program
00008  * for any purpose,  provided the above notices are retained on all copies.
00009  * Permission to modify the code and to distribute modified code is granted,
00010  * provided the above notices are retained, and a notice that the code was
00011  * modified is included with the above copyright notice.
00012  */
00013 /* An sprintf implementation that understands cords.  This is probably       */
00014 /* not terribly portable.  It assumes an ANSI stdarg.h.  It further   */
00015 /* assumes that I can make copies of va_list variables, and read      */
00016 /* arguments repeatedly by applyting va_arg to the copies.  This      */
00017 /* could be avoided at some performance cost.                         */
00018 /* We also assume that unsigned and signed integers of various kinds  */
00019 /* have the same sizes, and can be cast back and forth.               */
00020 /* We assume that void * and char * have the same size.               */
00021 /* All this cruft is needed because we want to rely on the underlying */
00022 /* sprintf implementation whenever possible.                          */
00023 /* Boehm, September 21, 1995 6:00 pm PDT */
00024 
00025 #include "cord.h"
00026 #include "ec.h"
00027 #include <stdio.h>
00028 #include <stdarg.h>
00029 #include <string.h>
00030 #include "gc.h"
00031 
00032 #define CONV_SPEC_LEN 50    /* Maximum length of a single      */
00033                             /* conversion specification.       */
00034 #define CONV_RESULT_LEN 50  /* Maximum length of any    */
00035                             /* conversion with default  */
00036                             /* width and prec.          */
00037 
00038 
00039 static int ec_len(CORD_ec x)
00040 {
00041     return(CORD_len(x[0].ec_cord) + (x[0].ec_bufptr - x[0].ec_buf));
00042 }
00043 
00044 /* Possible nonumeric precision values.   */
00045 # define NONE -1
00046 # define VARIABLE -2
00047 /* Copy the conversion specification from CORD_pos into the buffer buf       */
00048 /* Return negative on error.                                          */
00049 /* Source initially points one past the leading %.                    */
00050 /* It is left pointing at the conversion type.                        */
00051 /* Assign field width and precision to *width and *prec.              */
00052 /* If width or prec is *, VARIABLE is assigned.                       */
00053 /* Set *left to 1 if left adjustment flag is present.                 */
00054 /* Set *long_arg to 1 if long flag ('l' or 'L') is present, or to     */
00055 /* -1 if 'h' is present.                                       */
00056 static int extract_conv_spec(CORD_pos source, char *buf,
00057                           int * width, int *prec, int *left, int * long_arg)
00058 {
00059     register int result = 0;
00060     register int current_number = 0;
00061     register int saw_period = 0;
00062     register int saw_number;
00063     register int chars_so_far = 0;
00064     register char current;
00065     
00066     *width = NONE;
00067     buf[chars_so_far++] = '%';
00068     while(CORD_pos_valid(source)) {
00069         if (chars_so_far >= CONV_SPEC_LEN) return(-1);
00070         current = CORD_pos_fetch(source);
00071         buf[chars_so_far++] = current;
00072         switch(current) {
00073          case '*':
00074            saw_number = 1;
00075            current_number = VARIABLE;
00076            break;
00077           case '0':
00078             if (!saw_number) {
00079                 /* Zero fill flag; ignore */
00080                 break;
00081             } /* otherwise fall through: */
00082           case '1':
00083          case '2':
00084          case '3':
00085          case '4':
00086          case '5':
00087           case '6':
00088          case '7':
00089          case '8':
00090          case '9':
00091            saw_number = 1;
00092            current_number *= 10;
00093            current_number += current - '0';
00094            break;
00095          case '.':
00096            saw_period = 1;
00097            if(saw_number) {
00098                *width = current_number;
00099                saw_number = 0;
00100            }
00101            current_number = 0;
00102            break;
00103          case 'l':
00104          case 'L':
00105            *long_arg = 1;
00106            current_number = 0;
00107            break;
00108          case 'h':
00109            *long_arg = -1;
00110            current_number = 0;
00111            break;
00112          case ' ':
00113          case '+':
00114          case '#':
00115            current_number = 0;
00116            break;
00117          case '-':
00118            *left = 1;
00119            current_number = 0;
00120            break;
00121          case 'd':
00122          case 'i':
00123          case 'o':
00124          case 'u':
00125          case 'x':
00126          case 'X':
00127          case 'f':
00128          case 'e':
00129          case 'E':
00130          case 'g':
00131          case 'G':
00132          case 'c':
00133          case 'C':
00134          case 's':
00135          case 'S':
00136          case 'p':
00137          case 'n':
00138          case 'r':
00139            goto done;          
00140           default:
00141             return(-1);
00142         }
00143         CORD_next(source);
00144     }
00145     return(-1);
00146   done:
00147     if (saw_number) {
00148        if (saw_period) {
00149            *prec = current_number;
00150        } else {
00151            *prec = NONE;
00152            *width = current_number;
00153        }
00154     } else {
00155        *prec = NONE;
00156     }
00157     buf[chars_so_far] = '\0';
00158     return(result);
00159 }
00160 
00161 int CORD_vsprintf(CORD * out, CORD format, va_list args)
00162 {
00163     CORD_ec result;
00164     register int count;
00165     register char current;
00166     CORD_pos pos;
00167     char conv_spec[CONV_SPEC_LEN + 1];
00168     
00169     CORD_ec_init(result);
00170     for (CORD_set_pos(pos, format, 0); CORD_pos_valid(pos); CORD_next(pos)) {
00171               current = CORD_pos_fetch(pos);
00172               if (current == '%') {
00173             CORD_next(pos);
00174             if (!CORD_pos_valid(pos)) return(-1);
00175             current = CORD_pos_fetch(pos);
00176             if (current == '%') {
00177                      CORD_ec_append(result, current);
00178             } else {
00179               int width, prec;
00180               int left_adj = 0;
00181               int long_arg = 0;
00182               CORD arg;
00183               size_t len;
00184                
00185                      if (extract_conv_spec(pos, conv_spec,
00186                                          &width, &prec,
00187                                          &left_adj, &long_arg) < 0) {
00188                          return(-1);
00189                      }
00190                      current = CORD_pos_fetch(pos);
00191               switch(current) {
00192                   case 'n':
00193                      /* Assign length to next arg */
00194                      if (long_arg == 0) {
00195                          int * pos_ptr;
00196                          pos_ptr = va_arg(args, int *);
00197                          *pos_ptr = ec_len(result);
00198                      } else if (long_arg > 0) {
00199                          long * pos_ptr;
00200                          pos_ptr = va_arg(args, long *);
00201                          *pos_ptr = ec_len(result);
00202                      } else {
00203                          short * pos_ptr;
00204                          pos_ptr = va_arg(args, short *);
00205                          *pos_ptr = ec_len(result);
00206                      }
00207                      goto done;
00208                   case 'r':
00209                      /* Append cord and any padding     */
00210                      if (width == VARIABLE) width = va_arg(args, int);
00211                      if (prec == VARIABLE) prec = va_arg(args, int);
00212                      arg = va_arg(args, CORD);
00213                      len = CORD_len(arg);
00214                      if (prec != NONE && len > prec) {
00215                        if (prec < 0) return(-1);
00216                        arg = CORD_substr(arg, 0, prec);
00217                        len = prec;
00218                      }
00219                      if (width != NONE && len < width) {
00220                        char * blanks = GC_MALLOC_ATOMIC(width-len+1);
00221 
00222                        memset(blanks, ' ', width-len);
00223                        blanks[width-len] = '\0';
00224                        if (left_adj) {
00225                          arg = CORD_cat(arg, blanks);
00226                        } else {
00227                          arg = CORD_cat(blanks, arg);
00228                        }
00229                      }
00230                      CORD_ec_append_cord(result, arg);
00231                      goto done;
00232                   case 'c':
00233                      if (width == NONE && prec == NONE) {
00234                          register char c;
00235 
00236                          c = va_arg(args, char);
00237                          CORD_ec_append(result, c);
00238                          goto done;
00239                      }
00240                      break;
00241                   case 's':
00242                       if (width == NONE && prec == NONE) {
00243                          char * str = va_arg(args, char *);
00244                          register char c;
00245 
00246                          while (c = *str++) {
00247                              CORD_ec_append(result, c);
00248                          }
00249                          goto done;
00250                      }
00251                      break;
00252                   default:
00253                       break;
00254               }
00255               /* Use standard sprintf to perform conversion */
00256               {
00257                   register char * buf;
00258                   va_list vsprintf_args = args;
00259                      /* The above does not appear to be sanctioned    */
00260                      /* by the ANSI C standard.                */
00261                   int max_size = 0;
00262                   int res;
00263                      
00264                   if (width == VARIABLE) width = va_arg(args, int);
00265                   if (prec == VARIABLE) prec = va_arg(args, int);
00266                   if (width != NONE) max_size = width;
00267                   if (prec != NONE && prec > max_size) max_size = prec;
00268                   max_size += CONV_RESULT_LEN;
00269                   if (max_size >= CORD_BUFSZ) {
00270                       buf = GC_MALLOC_ATOMIC(max_size + 1);
00271                   } else {
00272                       if (CORD_BUFSZ - (result[0].ec_bufptr-result[0].ec_buf)
00273                           < max_size) {
00274                           CORD_ec_flush_buf(result);
00275                       }
00276                       buf = result[0].ec_bufptr;
00277                   }
00278                   switch(current) {
00279                       case 'd':
00280                       case 'i':
00281                       case 'o':
00282                       case 'u':
00283                       case 'x':
00284                       case 'X':
00285                       case 'c':
00286                           if (long_arg <= 0) {
00287                             (void) va_arg(args, int);
00288                           } else if (long_arg > 0) {
00289                             (void) va_arg(args, long);
00290                           }
00291                           break;
00292                       case 's':
00293                       case 'p':
00294                           (void) va_arg(args, char *);
00295                           break;
00296                       case 'f':
00297                       case 'e':
00298                       case 'E':
00299                       case 'g':
00300                       case 'G':
00301                           (void) va_arg(args, double);
00302                           break;
00303                       default:
00304                           return(-1);
00305                   }
00306                   res = vsprintf(buf, conv_spec, vsprintf_args);
00307                   len = (size_t)res;
00308                   if ((char *)(GC_word)res == buf) {
00309                      /* old style vsprintf */
00310                      len = strlen(buf);
00311                   } else if (res < 0) {
00312                       return(-1);
00313                   }
00314                   if (buf != result[0].ec_bufptr) {
00315                       register char c;
00316 
00317                      while (c = *buf++) {
00318                          CORD_ec_append(result, c);
00319                       }
00320                   } else {
00321                       result[0].ec_bufptr = buf + len;
00322                   }
00323               }
00324               done:;
00325             }
00326         } else {
00327             CORD_ec_append(result, current);
00328         }
00329     }
00330     count = ec_len(result);
00331     *out = CORD_balance(CORD_ec_to_cord(result));
00332     return(count);
00333 }
00334 
00335 int CORD_sprintf(CORD * out, CORD format, ...)
00336 {
00337     va_list args;
00338     int result;
00339     
00340     va_start(args, format);
00341     result = CORD_vsprintf(out, format, args);
00342     va_end(args);
00343     return(result);
00344 }
00345 
00346 int CORD_fprintf(FILE * f, CORD format, ...)
00347 {
00348     va_list args;
00349     int result;
00350     CORD out;
00351     
00352     va_start(args, format);
00353     result = CORD_vsprintf(&out, format, args);
00354     va_end(args);
00355     if (result > 0) CORD_put(out, f);
00356     return(result);
00357 }
00358 
00359 int CORD_vfprintf(FILE * f, CORD format, va_list args)
00360 {
00361     int result;
00362     CORD out;
00363     
00364     result = CORD_vsprintf(&out, format, args);
00365     if (result > 0) CORD_put(out, f);
00366     return(result);
00367 }
00368 
00369 int CORD_printf(CORD format, ...)
00370 {
00371     va_list args;
00372     int result;
00373     CORD out;
00374     
00375     va_start(args, format);
00376     result = CORD_vsprintf(&out, format, args);
00377     va_end(args);
00378     if (result > 0) CORD_put(out, stdout);
00379     return(result);
00380 }
00381 
00382 int CORD_vprintf(CORD format, va_list args)
00383 {
00384     int result;
00385     CORD out;
00386     
00387     result = CORD_vsprintf(&out, format, args);
00388     if (result > 0) CORD_put(out, stdout);
00389     return(result);
00390 }