cordprnt.c raw

   1  /*
   2   * Copyright (c) 1993-1994 by Xerox Corporation.  All rights reserved.
   3   *
   4   * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
   5   * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
   6   *
   7   * Permission is hereby granted to use or copy this program
   8   * for any purpose, provided the above notices are retained on all copies.
   9   * Permission to modify the code and to distribute modified code is granted,
  10   * provided the above notices are retained, and a notice that the code was
  11   * modified is included with the above copyright notice.
  12   */
  13  
  14  /*
  15   * A `sprintf` implementation that understands cords.  This is probably
  16   * not terribly portable.  It assumes an ANSI platform `stdarg.h` file.
  17   * It further assumes that I can make copies of `va_list` variables,
  18   * and read arguments repeatedly by applying `va_arg` to the copies.
  19   * This could be avoided at some performance cost.
  20   * We also assume that unsigned and signed integers of various kinds
  21   * have the same sizes, and can be cast back and forth.
  22   * We assume that `void *` and `char *` have the same size.
  23   * All this cruft is needed because we want to rely on the underlying
  24   * `sprintf` implementation whenever possible.
  25   */
  26  
  27  #ifdef HAVE_CONFIG_H
  28  #  include "config.h"
  29  #endif
  30  
  31  #include "gc/gc.h"
  32  
  33  #include <stdarg.h>
  34  #include <stdlib.h>
  35  #include <string.h>
  36  
  37  #ifndef CORD_BUILD
  38  #  define CORD_BUILD
  39  #endif
  40  #ifndef CORD_DONT_DECLARE_OOM_FN
  41  /* Avoid `CORD_oom_fn` symbol redefinition by MinGW. */
  42  #  define CORD_DONT_DECLARE_OOM_FN
  43  #endif
  44  #include "gc/cord.h"
  45  #include "gc/ec.h"
  46  
  47  /* Maximum length of a single conversion specification. */
  48  #define CONV_SPEC_LEN 50
  49  
  50  /* Maximum length of any conversion with the default width and precision. */
  51  #define CONV_RESULT_LEN 50
  52  
  53  #if defined(CPPCHECK)
  54  #  define MACRO_BLKSTMT_BEGIN {
  55  #  define MACRO_BLKSTMT_END }
  56  #else
  57  #  define MACRO_BLKSTMT_BEGIN do {
  58  #  define MACRO_BLKSTMT_END \
  59      }                       \
  60      while (0)
  61  #endif
  62  
  63  #define OUT_OF_MEMORY                 \
  64    MACRO_BLKSTMT_BEGIN                 \
  65    CORD__call_oom_fn();                \
  66    fprintf(stderr, "Out of memory\n"); \
  67    abort();                            \
  68    MACRO_BLKSTMT_END
  69  
  70  static size_t
  71  ec_len(CORD_ec x)
  72  {
  73    return CORD_len(x[0].ec_cord) + (size_t)(x[0].ec_bufptr - x[0].ec_buf);
  74  }
  75  
  76  /* Possible non-numeric precision values. */
  77  #define NONE -1
  78  #define VARIABLE -2
  79  
  80  /*
  81   * Copy the conversion specification from `CORD_pos` into the
  82   * buffer `buf`.  Return a negative value on error.
  83   * `source` initially points one past the leading "%" symbol;
  84   * it is left-pointing at the conversion type.  Assign width and
  85   * precision fields to `*width` and `*prec`, respectively.  If the
  86   * width or precision is specified as "*", then the corresponding
  87   * variable is assigned.  Set `*left` to 1 if left adjustment flag
  88   * is present.  Set `*long_arg` to 1 if `long` flag ("l" or "L") is
  89   * present, or to -1 if "h" is present, or to 2 if "z" is present.
  90   */
  91  static int
  92  extract_conv_spec(CORD_pos source, char *buf, int *width, int *prec, int *left,
  93                    int *long_arg)
  94  {
  95    int result = 0;
  96    int current_number = 0;
  97    int saw_period = 0;
  98    int saw_number = 0;
  99    int chars_so_far = 0;
 100    char current;
 101  
 102    *width = NONE;
 103    buf[chars_so_far++] = '%';
 104    while (CORD_pos_valid(source)) {
 105      if (chars_so_far >= CONV_SPEC_LEN)
 106        return -1;
 107      current = CORD_pos_fetch(source);
 108      buf[chars_so_far++] = current;
 109      switch (current) {
 110      case '*':
 111        saw_number = 1;
 112        current_number = VARIABLE;
 113        break;
 114      case '0':
 115        if (!saw_number) {
 116          /* Zero-fill flag; ignore it. */
 117          break;
 118        }
 119        current_number *= 10;
 120        break;
 121      case '1':
 122      case '2':
 123      case '3':
 124      case '4':
 125      case '5':
 126      case '6':
 127      case '7':
 128      case '8':
 129      case '9':
 130        saw_number = 1;
 131        current_number *= 10;
 132        current_number += current - '0';
 133        break;
 134      case '.':
 135        saw_period = 1;
 136        if (saw_number) {
 137          *width = current_number;
 138          saw_number = 0;
 139        }
 140        current_number = 0;
 141        break;
 142      case 'l':
 143      case 'L':
 144        *long_arg = 1;
 145        current_number = 0;
 146        break;
 147      case 'z':
 148        *long_arg = 2;
 149        current_number = 0;
 150        break;
 151      case 'h':
 152        *long_arg = -1;
 153        current_number = 0;
 154        break;
 155      case ' ':
 156      case '+':
 157      case '#':
 158        current_number = 0;
 159        break;
 160      case '-':
 161        *left = 1;
 162        current_number = 0;
 163        break;
 164      case 'd':
 165      case 'i':
 166      case 'o':
 167      case 'u':
 168      case 'x':
 169      case 'X':
 170      case 'f':
 171      case 'e':
 172      case 'E':
 173      case 'g':
 174      case 'G':
 175      case 'c':
 176      case 'C':
 177      case 's':
 178      case 'S':
 179      case 'p':
 180      case 'n':
 181      case 'r':
 182        goto done;
 183      default:
 184        return -1;
 185      }
 186      CORD_next(source);
 187    }
 188    return -1;
 189  done:
 190    if (saw_number) {
 191      if (saw_period) {
 192        *prec = current_number;
 193      } else {
 194        *prec = NONE;
 195        *width = current_number;
 196      }
 197    } else {
 198      *prec = NONE;
 199    }
 200    buf[chars_so_far] = '\0';
 201    return result;
 202  }
 203  
 204  #if defined(__DJGPP__) || defined(__STRICT_ANSI__)
 205  /* `vsnprintf` is missing in DJGPP (v2.0.3). */
 206  #  define GC_VSNPRINTF(buf, bufsz, format, args) vsprintf(buf, format, args)
 207  #elif defined(_MSC_VER)
 208  #  if defined(_WIN32_WCE)
 209  /* `_vsnprintf` is deprecated in WinCE. */
 210  #    define GC_VSNPRINTF StringCchVPrintfA
 211  #  else
 212  #    define GC_VSNPRINTF _vsnprintf
 213  #  endif
 214  #else
 215  #  define GC_VSNPRINTF vsnprintf
 216  #endif
 217  
 218  int
 219  CORD_vsprintf(CORD *out, CORD format, va_list args)
 220  {
 221    CORD_ec result;
 222    int count;
 223    char current;
 224    CORD_pos pos;
 225    char conv_spec[CONV_SPEC_LEN + 1];
 226  
 227  #if defined(CPPCHECK)
 228    memset(pos, '\0', sizeof(CORD_pos));
 229  #endif
 230    CORD_ec_init(result);
 231    for (CORD_set_pos(pos, format, 0); CORD_pos_valid(pos); CORD_next(pos)) {
 232      current = CORD_pos_fetch(pos);
 233      if (current == '%') {
 234        CORD_next(pos);
 235        if (!CORD_pos_valid(pos))
 236          return -1;
 237        current = CORD_pos_fetch(pos);
 238        if (current == '%') {
 239          CORD_ec_append(result, current);
 240        } else {
 241          int width, prec;
 242          int left_adj = 0;
 243          int long_arg = 0;
 244          CORD arg;
 245          size_t len;
 246  
 247          if (extract_conv_spec(pos, conv_spec, &width, &prec, &left_adj,
 248                                &long_arg)
 249              < 0) {
 250            return -1;
 251          }
 252          current = CORD_pos_fetch(pos);
 253          switch (current) {
 254          case 'n':
 255            /* Assign the length to next argument. */
 256            if (long_arg == 0) {
 257              unsigned *pos_ptr = va_arg(args, unsigned *);
 258              *pos_ptr = (unsigned)ec_len(result);
 259            } else if (long_arg == 2) {
 260              size_t *pos_ptr = va_arg(args, size_t *);
 261              *pos_ptr = ec_len(result);
 262            } else if (long_arg > 0) {
 263              unsigned long *pos_ptr = va_arg(args, unsigned long *);
 264              *pos_ptr = (unsigned long)ec_len(result);
 265            } else {
 266              unsigned short *pos_ptr = va_arg(args, unsigned short *);
 267              *pos_ptr = (unsigned short)ec_len(result);
 268            }
 269            goto done;
 270          case 'r':
 271            /* Append the cord and padding, if any. */
 272            if (width == VARIABLE)
 273              width = va_arg(args, int);
 274            if (prec == VARIABLE)
 275              prec = va_arg(args, int);
 276            arg = va_arg(args, CORD);
 277            len = CORD_len(arg);
 278            if (prec != NONE && len > (unsigned)prec) {
 279              if (prec < 0)
 280                return -1;
 281              arg = CORD_substr(arg, 0, (unsigned)prec);
 282              len = (unsigned)prec;
 283            }
 284            if (width != NONE && len < (unsigned)width) {
 285              char *blanks = (char *)GC_MALLOC_ATOMIC((unsigned)width - len + 1);
 286  
 287              if (NULL == blanks)
 288                OUT_OF_MEMORY;
 289              memset(blanks, ' ', (unsigned)width - len);
 290              blanks[(unsigned)width - len] = '\0';
 291              if (left_adj) {
 292                arg = CORD_cat(arg, blanks);
 293              } else {
 294                arg = CORD_cat(blanks, arg);
 295              }
 296            }
 297            CORD_ec_append_cord(result, arg);
 298            goto done;
 299          case 'c':
 300            if (width == NONE && prec == NONE) {
 301              char c;
 302  
 303              c = (char)va_arg(args, int);
 304              CORD_ec_append(result, c);
 305              goto done;
 306            }
 307            break;
 308          case 's':
 309            if (width == NONE && prec == NONE) {
 310              const char *str = va_arg(args, char *);
 311              char c;
 312  
 313              while ((c = *str++) != '\0') {
 314                CORD_ec_append(result, c);
 315              }
 316              goto done;
 317            }
 318            break;
 319          default:
 320            break;
 321          }
 322          /* Use standard `sprintf` to perform the conversion. */
 323          {
 324            char *buf;
 325            va_list vsprintf_args;
 326            int max_size = 0;
 327            int res = 0;
 328  
 329  #if defined(CPPCHECK)
 330            va_copy(vsprintf_args, args);
 331  #elif defined(__va_copy)
 332            __va_copy(vsprintf_args, args);
 333  #elif defined(__GNUC__) && !defined(__DJGPP__) \
 334      && !defined(__EMX__) /*< and probably in other cases */
 335            va_copy(vsprintf_args, args);
 336  #else
 337            vsprintf_args = args;
 338  #endif
 339            if (width == VARIABLE)
 340              width = va_arg(args, int);
 341            if (prec == VARIABLE)
 342              prec = va_arg(args, int);
 343            if (width != NONE)
 344              max_size = width;
 345            if (prec != NONE && prec > max_size)
 346              max_size = prec;
 347            max_size += CONV_RESULT_LEN;
 348            if (max_size >= CORD_BUFSZ) {
 349              buf = (char *)GC_MALLOC_ATOMIC((unsigned)max_size + 1);
 350              if (NULL == buf)
 351                OUT_OF_MEMORY;
 352            } else {
 353              if (CORD_BUFSZ - (result[0].ec_bufptr - result[0].ec_buf)
 354                  < max_size) {
 355                CORD_ec_flush_buf(result);
 356              }
 357              buf = result[0].ec_bufptr;
 358            }
 359            switch (current) {
 360            case 'd':
 361            case 'i':
 362            case 'o':
 363            case 'u':
 364            case 'x':
 365            case 'X':
 366            case 'c':
 367              if (long_arg <= 0) {
 368                (void)va_arg(args, int);
 369              } else if (long_arg == 2) {
 370                (void)va_arg(args, size_t);
 371              } else /* `long_arg == 1` */ {
 372                (void)va_arg(args, long);
 373              }
 374              break;
 375            case 's':
 376            case 'p':
 377              (void)va_arg(args, char *);
 378              break;
 379            case 'f':
 380            case 'e':
 381            case 'E':
 382            case 'g':
 383            case 'G':
 384              (void)va_arg(args, double);
 385              break;
 386            default:
 387              res = -1;
 388            }
 389            if (0 == res)
 390              res = GC_VSNPRINTF(buf, (unsigned)max_size + 1, conv_spec,
 391                                 vsprintf_args);
 392  #if defined(CPPCHECK) || defined(__va_copy) \
 393      || (defined(__GNUC__) && !defined(__DJGPP__) && !defined(__EMX__))
 394            va_end(vsprintf_args);
 395  #endif
 396            len = (unsigned)res;
 397            if ((GC_uintptr_t)len == (GC_uintptr_t)buf) {
 398              /* Old-style `vsprintf`. */
 399              len = strlen(buf);
 400            } else if (res < 0) {
 401              return -1;
 402            }
 403            if (buf != result[0].ec_bufptr) {
 404              char c;
 405  
 406              while ((c = *buf++) != '\0') {
 407                CORD_ec_append(result, c);
 408              }
 409            } else {
 410              result[0].ec_bufptr = buf + len;
 411            }
 412          }
 413        done:;
 414        }
 415      } else {
 416        CORD_ec_append(result, current);
 417      }
 418    }
 419    count = (int)ec_len(result);
 420    *out = CORD_balance(CORD_ec_to_cord(result));
 421    return count;
 422  }
 423  
 424  int
 425  CORD_sprintf(CORD *out, CORD format, ...)
 426  {
 427    va_list args;
 428    int result;
 429  
 430    va_start(args, format);
 431    result = CORD_vsprintf(out, format, args);
 432    va_end(args);
 433    return result;
 434  }
 435  
 436  int
 437  CORD_fprintf(FILE *f, CORD format, ...)
 438  {
 439    va_list args;
 440    int result;
 441    CORD out = CORD_EMPTY; /*< initialized to prevent a compiler warning */
 442  
 443    va_start(args, format);
 444    result = CORD_vsprintf(&out, format, args);
 445    va_end(args);
 446    if (result > 0)
 447      CORD_put(out, f);
 448    return result;
 449  }
 450  
 451  int
 452  CORD_vfprintf(FILE *f, CORD format, va_list args)
 453  {
 454    int result;
 455    CORD out = CORD_EMPTY;
 456  
 457    result = CORD_vsprintf(&out, format, args);
 458    if (result > 0)
 459      CORD_put(out, f);
 460    return result;
 461  }
 462  
 463  int
 464  CORD_printf(CORD format, ...)
 465  {
 466    va_list args;
 467    int result;
 468    CORD out = CORD_EMPTY;
 469  
 470    va_start(args, format);
 471    result = CORD_vsprintf(&out, format, args);
 472    va_end(args);
 473    if (result > 0)
 474      CORD_put(out, stdout);
 475    return result;
 476  }
 477  
 478  int
 479  CORD_vprintf(CORD format, va_list args)
 480  {
 481    int result;
 482    CORD out = CORD_EMPTY;
 483  
 484    result = CORD_vsprintf(&out, format, args);
 485    if (result > 0)
 486      CORD_put(out, stdout);
 487    return result;
 488  }
 489