(linenum→info "unix/slp.c:2238")

emacs/22.1/src/doprnt.c

    1: /* Output like sprintf to a buffer of specified size.
    2:    Also takes args differently: pass one pointer to an array of strings
    3:    in addition to the format string which is separate.
    4:    Copyright (C) 1985, 2001, 2002, 2003, 2004, 2005,
    5:                  2006, 2007  Free Software Foundation, Inc.
    6: 
    7: This file is part of GNU Emacs.
    8: 
    9: GNU Emacs is free software; you can redistribute it and/or modify
   10: it under the terms of the GNU General Public License as published by
   11: the Free Software Foundation; either version 2, or (at your option)
   12: any later version.
   13: 
   14: GNU Emacs is distributed in the hope that it will be useful,
   15: but WITHOUT ANY WARRANTY; without even the implied warranty of
   16: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   17: GNU General Public License for more details.
   18: 
   19: You should have received a copy of the GNU General Public License
   20: along with GNU Emacs; see the file COPYING.  If not, write to
   21: the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   22: Boston, MA 02110-1301, USA.  */
   23: 
   24: 
   25: #include <config.h>
   26: #include <stdio.h>
   27: #include <ctype.h>
   28: 
   29: #ifdef STDC_HEADERS
   30: #include <float.h>
   31: #endif
   32: 
   33: #ifdef HAVE_UNISTD_H
   34: #include <unistd.h>
   35: #endif
   36: 
   37: #ifdef HAVE_STDLIB_H
   38: #include <stdlib.h>
   39: #endif
   40: 
   41: #include "lisp.h"
   42: 
   43: #ifndef DBL_MAX_10_EXP
   44: #define DBL_MAX_10_EXP 308 /* IEEE double */
   45: #endif
   46: 
   47: /* Since we use the macro CHAR_HEAD_P, we have to include this, but
   48:    don't have to include others because CHAR_HEAD_P does not contains
   49:    another macro.  */
   50: #include "charset.h"
   51: 
   52: static int doprnt1 ();
   53: 
   54: /* Generate output from a format-spec FORMAT,
   55:    terminated at position FORMAT_END.
   56:    Output goes in BUFFER, which has room for BUFSIZE chars.
   57:    If the output does not fit, truncate it to fit.
   58:    Returns the number of bytes stored into BUFFER.
   59:    ARGS points to the vector of arguments, and NARGS says how many.
   60:    A double counts as two arguments.
   61:    String arguments are passed as C strings.
   62:    Integers are passed as C integers.  */
   63: 
   64: int
   65: doprnt (buffer, bufsize, format, format_end, nargs, args)
   66:      char *buffer;
   67:      register int bufsize;
   68:      char *format;
   69:      char *format_end;
   70:      int nargs;
   71:      char **args;
   72: {
   73:   return doprnt1 (0, buffer, bufsize, format, format_end, nargs, args);
   74: }
   75: 
   76: /* Like doprnt except that strings in ARGS are passed
   77:    as Lisp_Object.  */
   78: 
   79: int
   80: doprnt_lisp (buffer, bufsize, format, format_end, nargs, args)
   81:      char *buffer;
   82:      register int bufsize;
   83:      char *format;
   84:      char *format_end;
   85:      int nargs;
   86:      char **args;
   87: {
   88:   return doprnt1 (1, buffer, bufsize, format, format_end, nargs, args);
   89: }
   90: 
   91: static int
   92: doprnt1 (lispstrings, buffer, bufsize, format, format_end, nargs, args)
   93:      int lispstrings;
   94:      char *buffer;
   95:      register int bufsize;
   96:      char *format;
   97:      char *format_end;
   98:      int nargs;
   99:      char **args;
  100: {
  101:   int cnt = 0;                  /* Number of arg to gobble next */
  102:   register char *fmt = format;  /* Pointer into format string */
  103:   register char *bufptr = buffer; /* Pointer into output buffer.. */
  104: 
  105:   /* Use this for sprintf unless we need something really big.  */
  106:   char tembuf[DBL_MAX_10_EXP + 100];
  107: 
  108:   /* Size of sprintf_buffer.  */
  109:   unsigned size_allocated = sizeof (tembuf);
  110: 
  111:   /* Buffer to use for sprintf.  Either tembuf or same as BIG_BUFFER.  */
  112:   char *sprintf_buffer = tembuf;
  113: 
  114:   /* Buffer we have got with malloc.  */
  115:   char *big_buffer = 0;
  116: 
  117:   register int tem;
  118:   unsigned char *string;
  119:   char fixed_buffer[20];        /* Default buffer for small formatting. */
  120:   char *fmtcpy;
  121:   int minlen;
  122:   unsigned char charbuf[5];     /* Used for %c.  */
  123: 
  124:   if (format_end == 0)
  125:     format_end = format + strlen (format);
  126: 
  127:   if ((format_end - format + 1) < sizeof (fixed_buffer))
  128:     fmtcpy = fixed_buffer;
  129:   else
  130:     fmtcpy = (char *) alloca (format_end - format + 1);
  131: 
  132:   bufsize--;
  133: 
  134:   /* Loop until end of format string or buffer full. */
  135:   while (fmt != format_end && bufsize > 0)
  136:     {
  137:       if (*fmt == '%')  /* Check for a '%' character */
  138:         {
  139:           unsigned size_bound = 0;
  140:           int width;           /* Columns occupied by STRING.  */
  141: 
  142:           fmt++;
  143:           /* Copy this one %-spec into fmtcpy.  */
  144:           string = (unsigned char *) fmtcpy;
  145:           *string++ = '%';
  146:           while (1)
  147:             {
  148:               *string++ = *fmt;
  149:               if ('0' <= *fmt && *fmt <= '9')
  150:                 {
  151:                   /* Get an idea of how much space we might need.
  152:                      This might be a field width or a precision; e.g.
  153:                      %1.1000f and %1000.1f both might need 1000+ bytes.
  154:                      Parse the width or precision, checking for overflow.  */
  155:                   unsigned n = *fmt - '0';
  156:                   while ('0' <= fmt[1] && fmt[1] <= '9')
  157:                     {
  158:                       if (n * 10 / 10 != n
  159:                           || (n = n * 10 + (fmt[1] - '0')) < n)
  160:                         error ("Format width or precision too large");
  161:                       *string++ = *++fmt;
  162:                     }
  163: 
  164:                   if (size_bound < n)
  165:                     size_bound = n;
  166:                 }
  167:               else if (*fmt == '-' || *fmt == ' ' || *fmt == '.' || *fmt == '+')
  168:                 ;
  169:               else
  170:                 break;
  171:               fmt++;
  172:             }
  173:           *string = 0;
  174: 
  175:           /* Make the size bound large enough to handle floating point formats
  176:              with large numbers.  */
  177:           if (size_bound + DBL_MAX_10_EXP + 50 < size_bound)
  178:             error ("Format width or precision too large");
  179:           size_bound += DBL_MAX_10_EXP + 50;
  180: 
  181:           /* Make sure we have that much.  */
  182:           if (size_bound > size_allocated)
  183:             {
  184:               if (big_buffer)
  185:                 big_buffer = (char *) xrealloc (big_buffer, size_bound);
  186:               else
  187:                 big_buffer = (char *) xmalloc (size_bound);
  188:               sprintf_buffer = big_buffer;
  189:               size_allocated = size_bound;
  190:             }
  191:           minlen = 0;
  192:           switch (*fmt++)
  193:             {
  194:             default:
  195:               error ("Invalid format operation %%%c", fmt[-1]);
  196: 
  197: /*          case 'b': */
  198:             case 'd':
  199:             case 'o':
  200:             case 'x':
  201:               if (cnt == nargs)
  202:                 error ("Not enough arguments for format string");
  203:               if (sizeof (int) == sizeof (EMACS_INT))
  204:                 ;
  205:               else if (sizeof (long) == sizeof (EMACS_INT))
  206:                 /* Insert an `l' the right place.  */
  207:                 string[1] = string[0],
  208:                 string[0] = string[-1],
  209:                 string[-1] = 'l',
  210:                 string++;
  211:               else
  212:                 abort ();
  213:               sprintf (sprintf_buffer, fmtcpy, args[cnt++]);
  214:               /* Now copy into final output, truncating as nec.  */
  215:               string = (unsigned char *) sprintf_buffer;
  216:               goto doit;
  217: 
  218:             case 'f':
  219:             case 'e':
  220:             case 'g':
  221:               {
  222:                 union { double d; char *half[2]; } u;
  223:                 if (cnt + 1 == nargs)
  224:                   error ("Not enough arguments for format string");
  225:                 u.half[0] = args[cnt++];
  226:                 u.half[1] = args[cnt++];
  227:                 sprintf (sprintf_buffer, fmtcpy, u.d);
  228:                 /* Now copy into final output, truncating as nec.  */
  229:                 string = (unsigned char *) sprintf_buffer;
  230:                 goto doit;
  231:               }
  232: 
  233:             case 'S':
  234:               string[-1] = 's';
  235:             case 's':
  236:               if (cnt == nargs)
  237:                 error ("Not enough arguments for format string");
  238:               if (fmtcpy[1] != 's')
  239:                 minlen = atoi (&fmtcpy[1]);
  240:               if (lispstrings)
  241:                 {
  242:                   string = ((struct Lisp_String *) args[cnt])->data;
  243:                   tem = STRING_BYTES ((struct Lisp_String *) args[cnt]);
  244:                   cnt++;
  245:                 }
  246:               else
  247:                 {
  248:                   string = (unsigned char *) args[cnt++];
  249:                   tem = strlen (string);
  250:                 }
  251:               width = strwidth (string, tem);
  252:               goto doit1;
  253: 
  254:               /* Copy string into final output, truncating if no room.  */
  255:             doit:
  256:               /* Coming here means STRING contains ASCII only.  */
  257:               width = tem = strlen (string);
  258:             doit1:
  259:               /* We have already calculated:
  260:                  TEM -- length of STRING,
  261:                  WIDTH -- columns occupied by STRING when displayed, and
  262:                  MINLEN -- minimum columns of the output.  */
  263:               if (minlen > 0)
  264:                 {
  265:                   while (minlen > width && bufsize > 0)
  266:                     {
  267:                       *bufptr++ = ' ';
  268:                       bufsize--;
  269:                       minlen--;
  270:                     }
  271:                   minlen = 0;
  272:                 }
  273:               if (tem > bufsize)
  274:                 {
  275:                   /* Truncate the string at character boundary.  */
  276:                   tem = bufsize;
  277:                   while (!CHAR_HEAD_P (string[tem - 1])) tem--;
  278:                   bcopy (string, bufptr, tem);
  279:                   /* We must calculate WIDTH again.  */
  280:                   width = strwidth (bufptr, tem);
  281:                 }
  282:               else
  283:                 bcopy (string, bufptr, tem);
  284:               bufptr += tem;
  285:               bufsize -= tem;
  286:               if (minlen < 0)
  287:                 {
  288:                   while (minlen < - width && bufsize > 0)
  289:                     {
  290:                       *bufptr++ = ' ';
  291:                       bufsize--;
  292:                       minlen++;
  293:                     }
  294:                   minlen = 0;
  295:                 }
  296:               continue;
  297: 
  298:             case 'c':
  299:               if (cnt == nargs)
  300:                 error ("Not enough arguments for format string");
  301:               tem = CHAR_STRING ((int) (EMACS_INT) args[cnt], charbuf);
  302:               string = charbuf;
  303:               cnt++;
  304:               string[tem] = 0;
  305:               width = strwidth (string, tem);
  306:               if (fmtcpy[1] != 'c')
  307:                 minlen = atoi (&fmtcpy[1]);
  308:               goto doit1;
  309: 
  310:             case '%':
  311:               fmt--;    /* Drop thru and this % will be treated as normal */
  312:             }
  313:         }
  314: 
  315:       {
  316:         /* Just some character; Copy it if the whole multi-byte form
  317:            fit in the buffer.  */
  318:         char *save_bufptr = bufptr;
  319: 
  320:         do { *bufptr++ = *fmt++; }
  321:         while (--bufsize > 0 && !CHAR_HEAD_P (*fmt));
  322:         if (!CHAR_HEAD_P (*fmt))
  323:           {
  324:             bufptr = save_bufptr;
  325:             break;
  326:           }
  327:       }
  328:     };
  329: 
  330:   /* If we had to malloc something, free it.  */
  331:   if (big_buffer)
  332:     xfree (big_buffer);
  333: 
  334:   *bufptr = 0;          /* Make sure our string end with a '\0' */
  335:   return bufptr - buffer;
  336: }
  337: 
  338: /* arch-tag: aa0ab528-7c5f-4c73-894c-aa2526a1efb3
  339:    (do not change this comment) */
Syntax (Markdown)