Back to index

plt-scheme  4.2.1
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 = 0;
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 = (char)va_arg(args, int);
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;
00259                   int max_size = 0;
00260                   int res;
00261 #                 ifdef __va_copy
00262                       __va_copy(vsprintf_args, args);
00263 #                 else
00264 #                   if defined(__GNUC__) && !defined(__DJGPP__) /* and probably in other cases */
00265                         va_copy(vsprintf_args, args);
00266 #                   else
00267                      vsprintf_args = args;
00268 #                   endif
00269 #                 endif
00270                   if (width == VARIABLE) width = va_arg(args, int);
00271                   if (prec == VARIABLE) prec = va_arg(args, int);
00272                   if (width != NONE) max_size = width;
00273                   if (prec != NONE && prec > max_size) max_size = prec;
00274                   max_size += CONV_RESULT_LEN;
00275                   if (max_size >= CORD_BUFSZ) {
00276                       buf = GC_MALLOC_ATOMIC(max_size + 1);
00277                   } else {
00278                       if (CORD_BUFSZ - (result[0].ec_bufptr-result[0].ec_buf)
00279                           < max_size) {
00280                           CORD_ec_flush_buf(result);
00281                       }
00282                       buf = result[0].ec_bufptr;
00283                   }
00284                   switch(current) {
00285                       case 'd':
00286                       case 'i':
00287                       case 'o':
00288                       case 'u':
00289                       case 'x':
00290                       case 'X':
00291                       case 'c':
00292                           if (long_arg <= 0) {
00293                             (void) va_arg(args, int);
00294                           } else if (long_arg > 0) {
00295                             (void) va_arg(args, long);
00296                           }
00297                           break;
00298                       case 's':
00299                       case 'p':
00300                           (void) va_arg(args, char *);
00301                           break;
00302                       case 'f':
00303                       case 'e':
00304                       case 'E':
00305                       case 'g':
00306                       case 'G':
00307                           (void) va_arg(args, double);
00308                           break;
00309                       default:
00310                           return(-1);
00311                   }
00312                   res = vsprintf(buf, conv_spec, vsprintf_args);
00313                   len = (size_t)res;
00314                   if ((char *)(GC_word)res == buf) {
00315                      /* old style vsprintf */
00316                      len = strlen(buf);
00317                   } else if (res < 0) {
00318                       return(-1);
00319                   }
00320                   if (buf != result[0].ec_bufptr) {
00321                       register char c;
00322 
00323                      while ((c = *buf++)) {
00324                          CORD_ec_append(result, c);
00325                       }
00326                   } else {
00327                       result[0].ec_bufptr = buf + len;
00328                   }
00329               }
00330               done:;
00331             }
00332         } else {
00333             CORD_ec_append(result, current);
00334         }
00335     }
00336     count = ec_len(result);
00337     *out = CORD_balance(CORD_ec_to_cord(result));
00338     return(count);
00339 }
00340 
00341 int CORD_sprintf(CORD * out, CORD format, ...)
00342 {
00343     va_list args;
00344     int result;
00345     
00346     va_start(args, format);
00347     result = CORD_vsprintf(out, format, args);
00348     va_end(args);
00349     return(result);
00350 }
00351 
00352 int CORD_fprintf(FILE * f, CORD format, ...)
00353 {
00354     va_list args;
00355     int result;
00356     CORD out;
00357     
00358     va_start(args, format);
00359     result = CORD_vsprintf(&out, format, args);
00360     va_end(args);
00361     if (result > 0) CORD_put(out, f);
00362     return(result);
00363 }
00364 
00365 int CORD_vfprintf(FILE * f, CORD format, va_list args)
00366 {
00367     int result;
00368     CORD out;
00369     
00370     result = CORD_vsprintf(&out, format, args);
00371     if (result > 0) CORD_put(out, f);
00372     return(result);
00373 }
00374 
00375 int CORD_printf(CORD format, ...)
00376 {
00377     va_list args;
00378     int result;
00379     CORD out;
00380     
00381     va_start(args, format);
00382     result = CORD_vsprintf(&out, format, args);
00383     va_end(args);
00384     if (result > 0) CORD_put(out, stdout);
00385     return(result);
00386 }
00387 
00388 int CORD_vprintf(CORD format, va_list args)
00389 {
00390     int result;
00391     CORD out;
00392     
00393     result = CORD_vsprintf(&out, format, args);
00394     if (result > 0) CORD_put(out, stdout);
00395     return(result);
00396 }