chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / src / snprintf.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to
6  * deal in the Software without restriction, including without limitation the
7  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8  * sell copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies of the Software, its documentation and marketing & publicity
13  * materials, and acknowledgment shall be given in the documentation, materials
14  * and software packages that this Software was used.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 /* sgi's stdio.h has:
24  * 
25  * #if _SGIAPI && _NO_ANSIMODE
26  * extern int      vsnprintf(char *, ssize_t, const char *, char *);
27  * #endif
28  * 
29  * so workaround...
30  */
31 #ifdef __sgi
32 #ifdef _NO_ANSIMODE
33 #undef _NO_ANSIMODE
34 #endif
35 #endif
36
37 #include "E.h"
38 #include <errno.h>
39 #include <ctype.h>
40
41 /* 
42  * Shamelessly snarfed from sane... 
43  * which shamelessly snarfed from LPR 
44  * which probably shamelessly snarfed from....
45  * 
46  * Moved comments to end so I can actually read the code.. cleaned out useless
47  * junk....
48  */
49
50 #define VA_LOCAL_DECL va_list ap
51 #define VA_START(f) va_start(ap, f)
52 #define VA_SHIFT(v,t) ;         /* no-op for ANSI */
53 #define VA_END va_end(ap)
54
55 /*
56  * dopr(): poor man's version of doprintf
57  */
58
59 static void         dopr(char *buffer, const char *format, va_list args);
60 static void         fmtstr(char *value, int ljust, int len, int zpad,
61                            int precision);
62 static void         fmtnum(long value, int base, int dosign, int ljust, int len,
63                            int zpad, int precision);
64 static void         fmtdouble(int fmt, double value, int ljust, int len,
65                               int zpad, int precision);
66 static void         dostr(char *);
67 static char        *output;
68 static void         dopr_outch(int c);
69 static char        *end;
70 int                 visible_control = 1;
71
72 int
73 Evsnprintf(char *str, size_t count, const char *fmt, va_list args)
74 {
75    str[0] = 0;
76    end = str + count - 1;
77    dopr(str, fmt, args);
78    if (count > 0)
79      {
80         end[0] = 0;
81      }
82    return (strlen(str));
83 }
84
85 #ifdef HAVE_STDARG_H
86 int
87 Esnprintf(char *str, size_t count, const char *fmt, ...)
88 #else
89 int
90 Esnprintf(va_alist)
91      va_dcl
92 #endif
93 {
94 #ifndef HAVE_STDARG_H
95    char               *str;
96    size_t              count;
97    char               *fmt;
98
99 #endif
100    VA_LOCAL_DECL;
101
102    VA_START(fmt);
103    VA_SHIFT(str, char *);
104
105    VA_SHIFT(count, size_t);
106    VA_SHIFT(fmt, char *);
107
108    (void)Evsnprintf(str, count, fmt, ap);
109    VA_END;
110    return (strlen(str));
111 }
112
113 static void
114 dopr(char *buffer, const char *format, va_list args)
115 {
116    int                 ch;
117    long                value;
118    int                 longflag = 0;
119    char               *strvalue;
120    int                 ljust;
121    int                 len;
122    int                 zpad;
123    int                 precision;
124    int                 set_precision;
125    double              dval;
126
127    output = buffer;
128    while ((ch = *format++))
129      {
130         switch (ch)
131           {
132           case '%':
133              ljust = len = zpad = 0;
134              precision = -1;
135              set_precision = 0;
136            nextch:
137              ch = *format++;
138              switch (ch)
139                {
140                case 0:
141                   dostr("**end of format**");
142                   return;
143                case '-':
144                   ljust = 1;
145                   goto nextch;
146                case '.':
147                   set_precision = 1;
148                   precision = 0;
149                   goto nextch;
150                case '*':
151                   len = va_arg(args, int);
152
153                   goto nextch;
154                case '0':        /* set zero padding if len not set */
155                   if (len == 0 && set_precision == 0)
156                      zpad = '0';
157                case '1':
158                case '2':
159                case '3':
160                case '4':
161                case '5':
162                case '6':
163                case '7':
164                case '8':
165                case '9':
166                   if (set_precision)
167                     {
168                        precision = precision * 10 + ch - '0';
169                     }
170                   else
171                     {
172                        len = len * 10 + ch - '0';
173                     }
174                   goto nextch;
175                case 'l':
176                   longflag = 1;
177                   goto nextch;
178                case 'u':
179                case 'U':
180                   /*fmtnum(value,base,dosign,ljust,len, zpad, precision) */
181                   if (longflag)
182                     {
183                        value = va_arg(args, long);
184                     }
185                   else
186                     {
187                        value = va_arg(args, int);
188                     }
189                   fmtnum(value, 10, 0, ljust, len, zpad, precision);
190                   break;
191                case 'o':
192                case 'O':
193                   /*fmtnum(value,base,dosign,ljust,len, zpad, precision) */
194                   if (longflag)
195                     {
196                        value = va_arg(args, long);
197                     }
198                   else
199                     {
200                        value = va_arg(args, int);
201                     }
202                   fmtnum(value, 8, 0, ljust, len, zpad, precision);
203                   break;
204                case 'd':
205                case 'i':
206                case 'D':
207                   if (longflag)
208                     {
209                        value = va_arg(args, long);
210                     }
211                   else
212                     {
213                        value = va_arg(args, int);
214                     }
215                   fmtnum(value, 10, 1, ljust, len, zpad, precision);
216                   break;
217                case 'x':
218                   if (longflag)
219                     {
220                        value = va_arg(args, long);
221                     }
222                   else
223                     {
224                        value = va_arg(args, int);
225                     }
226                   fmtnum(value, 16, 0, ljust, len, zpad, precision);
227                   break;
228                case 'X':
229                   if (longflag)
230                     {
231                        value = va_arg(args, long);
232                     }
233                   else
234                     {
235                        value = va_arg(args, int);
236                     }
237                   fmtnum(value, -16, 0, ljust, len, zpad, precision);
238                   break;
239                case 's':
240                   strvalue = va_arg(args, char *);
241
242                   fmtstr(strvalue, ljust, len, zpad, precision);
243                   break;
244                case 'c':
245                   ch = va_arg(args, int);
246
247                   {
248                      char                b[2];
249                      int                 vsb = visible_control;
250
251                      b[0] = ch;
252                      b[1] = 0;
253                      visible_control = 0;
254                      fmtstr(b, ljust, len, zpad, precision);
255                      visible_control = vsb;
256                   }
257                   break;
258                case 'f':
259                case 'g':
260                   dval = va_arg(args, double);
261
262                   fmtdouble(ch, dval, ljust, len, zpad, precision);
263                   break;
264                case '%':
265                   dopr_outch(ch);
266                   continue;
267                default:
268                   dostr("???????");
269                }
270              longflag = 0;
271              break;
272           default:
273              dopr_outch(ch);
274              break;
275           }
276      }
277    *output = 0;
278 }
279
280 /*
281  * Format '%[-]len[.precision]s'
282  * -   = left justify (ljust)
283  * len = minimum length
284  * precision = numbers of chars in string to use
285  */
286 static void
287 fmtstr(char *value, int ljust, int len, int zpad, int precision)
288 {
289    int                 padlen, strlen, i, c;    /* amount to pad */
290
291    zpad = 0;
292    if (value == 0)
293      {
294         value = "<NULL>";
295      }
296    if (precision > 0)
297      {
298         strlen = precision;
299      }
300    else
301      {
302         /* cheap strlen so you do not have library call */
303         for (strlen = 0; (c = value[strlen]); ++strlen)
304           {
305              if (visible_control && iscntrl(c) && !isspace(c))
306                {
307                   ++strlen;
308                }
309           }
310      }
311    padlen = len - strlen;
312    if (padlen < 0)
313       padlen = 0;
314    if (ljust)
315       padlen = -padlen;
316    while (padlen > 0)
317      {
318         dopr_outch(' ');
319         --padlen;
320      }
321    /* output characters */
322    for (i = 0; (c = value[i]); ++i)
323      {
324         if (visible_control && iscntrl(c) && !isspace(c))
325           {
326              dopr_outch('^');
327              c = ('@' | (c & 0x1F));
328           }
329         dopr_outch(c);
330      }
331    while (padlen < 0)
332      {
333         dopr_outch(' ');
334         ++padlen;
335      }
336 }
337
338 static void
339 fmtnum(long value, int base, int dosign, int ljust, int len, int zpad,
340        int precision)
341 {
342    int                 signvalue = 0;
343    unsigned long       uvalue;
344    char                convert[20];
345    int                 place = 0;
346    int                 padlen = 0;      /* amount to pad */
347    int                 caps = 0;
348
349    precision = 0;
350    /* DEBUGP(("value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad %d\n",
351     * value, base, dosign, ljust, len, zpad )); */
352    uvalue = value;
353    if (dosign)
354      {
355         if (value < 0)
356           {
357              signvalue = '-';
358              uvalue = -value;
359           }
360      }
361    if (base < 0)
362      {
363         caps = 1;
364         base = -base;
365      }
366    do
367      {
368         convert[place++] =
369            (caps ? "0123456789ABCDEF" : "0123456789abcdef")[uvalue %
370                                                             (unsigned)base];
371         uvalue = (uvalue / (unsigned)base);
372      }
373    while (uvalue);
374    convert[place] = 0;
375    padlen = len - place;
376    if (padlen < 0)
377       padlen = 0;
378    if (ljust)
379       padlen = -padlen;
380    /* DEBUGP(( "str '%s', place %d, sign %c, padlen %d\n",
381     * convert,place,signvalue,padlen)); */
382    if (zpad && padlen > 0)
383      {
384         if (signvalue)
385           {
386              dopr_outch(signvalue);
387              --padlen;
388              signvalue = 0;
389           }
390         while (padlen > 0)
391           {
392              dopr_outch(zpad);
393              --padlen;
394           }
395      }
396    while (padlen > 0)
397      {
398         dopr_outch(' ');
399         --padlen;
400      }
401    if (signvalue)
402       dopr_outch(signvalue);
403    while (place > 0)
404       dopr_outch(convert[--place]);
405    while (padlen < 0)
406      {
407         dopr_outch(' ');
408         ++padlen;
409      }
410 }
411
412 static void
413 fmtdouble(int fmt, double value, int ljust, int len, int zpad, int precision)
414 {
415    char                convert[128];
416    char                fmtstr[128];
417    int                 l;
418
419    zpad = 0;
420    if (len == 0)
421       len = 10;
422    if (len > (int)sizeof(convert) - 10)
423      {
424         len = (int)sizeof(convert) - 10;
425      }
426    if (precision > (int)sizeof(convert) - 10)
427      {
428         precision = (int)sizeof(convert) - 10;
429      }
430    if (precision > len)
431       precision = len;
432    strcpy(fmtstr, "%");
433    if (ljust)
434       strcat(fmtstr, "-");
435    if (len)
436      {
437         sprintf(fmtstr + strlen(fmtstr), "%d", len);
438      }
439    if (precision > 0)
440      {
441         sprintf(fmtstr + strlen(fmtstr), ".%d", precision);
442      }
443    l = strlen(fmtstr);
444    fmtstr[l] = fmt;
445    fmtstr[l + 1] = 0;
446    sprintf(convert, fmtstr, value);
447    dostr(convert);
448 }
449
450 static void
451 dostr(char *str)
452 {
453    while (*str)
454       dopr_outch(*str++);
455 }
456
457 static void
458 dopr_outch(int c)
459 {
460    if (end == 0 || output < end)
461      {
462         *output++ = c;
463      }
464 }
465
466 /**************************************************************
467  * Original:
468  * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
469  * A bombproof version of doprnt (dopr) included.
470  * Sigh.  This sort of thing is always nasty do deal with.  Note that
471  * the version here does not include floating point...
472  *
473  * plp_snprintf() is used instead of sprintf() as it does limit checks
474  * for string length.  This covers a nasty loophole.
475  *
476  * The other functions are there to prevent NULL pointers from
477  * causing nast effects.
478  **************************************************************/
479
480 /***************************************************************************
481  * LPRng - An Extended Print Spooler System
482  *
483  * Copyright 1988-1997, Patrick Powell, San Diego, CA
484  *     papowell@sdsu.edu
485  * See below for conditions of use.
486  *
487  ***************************************************************************
488  * MODULE: snprintf.c
489  * PURPOSE: LPRng version of printf - absolutely bombproof (hopefully!)
490  **************************************************************************/
491
492 /*
493  * The "Artistic License"
494  * 
495  * Preamble
496  * 
497  * The intent of this document is to state the conditions under which a
498  * Package may be copied, such that the Copyright Holder maintains some
499  * semblance of artistic control over the development of the package,
500  * while giving the users of the package the right to use and distribute
501  * the Package in a more-or-less customary fashion, plus the right to make
502  * reasonable modifications.
503  * 
504  * Definitions:
505  * 
506  * "Package" refers to the collection of files distributed by the
507  * Copyright Holder, and derivatives of that collection of files
508  * created through textual modification.
509  * 
510  * "Standard Version" refers to such a Package if it has not been
511  * modified, or has been modified in accordance with the wishes
512  * of the Copyright Holder as specified below.
513  * 
514  * "Copyright Holder" is whoever is named in the copyright or
515  * copyrights for the package.
516  * 
517  * "You" is you, if you are thinking about copying or distributing
518  * this Package.
519  * 
520  * "Reasonable copying fee" is whatever you can justify on the
521  * basis of media cost, duplication charges, time of people involved,
522  * and so on.  (You will not be required to justify it to the
523  * Copyright Holder, but only to the computing community at large
524  * as a market that must bear the fee.)
525  * 
526  * "Freely Available" means that no fee is charged for the item
527  * itself, though there may be fees involved in handling the item.
528  * It also means that recipients of the item may redistribute it
529  * under the same conditions they received it.
530  * 
531  * 1. You may make and give away verbatim copies of the source form of the
532  * Standard Version of this Package without restriction, provided that you
533  * duplicate all of the original copyright notices and associated disclaimers.
534  * 
535  * 2. You may apply bug fixes, portability fixes and other modifications
536  * derived from the Public Domain or from the Copyright Holder.  A Package
537  * modified in such a way shall still be considered the Standard Version.
538  * 
539  * 3. You may otherwise modify your copy of this Package in any way, provided
540  * that you insert a prominent notice in each changed file stating how and
541  * when you changed that file, and provided that you do at least ONE of the
542  * following:
543  * 
544  * a) place your modifications in the Public Domain or otherwise make them
545  * Freely Available, such as by posting said modifications to Usenet or
546  * an equivalent medium, or placing the modifications on a major archive
547  * site such as uunet.uu.net, or by allowing the Copyright Holder to include
548  * your modifications in the Standard Version of the Package.
549  * 
550  * b) use the modified Package only within your corporation or organization.
551  * 
552  * c) rename any non-standard executables so the names do not conflict
553  * with standard executables, which must also be provided, and provide
554  * a separate manual page for each non-standard executable that clearly
555  * documents how it differs from the Standard Version.
556  * 
557  * d) make other distribution arrangements with the Copyright Holder.
558  * 
559  * 4. You may distribute the programs of this Package in object code or
560  * executable form, provided that you do at least ONE of the following:
561  * 
562  * a) distribute a Standard Version of the executables and library files,
563  * together with instructions (in the manual page or equivalent) on where
564  * to get the Standard Version.
565  * 
566  * b) accompany the distribution with the machine-readable source of
567  * the Package with your modifications.
568  * 
569  * c) give non-standard executables non-standard names, and clearly
570  * document the differences in manual pages (or equivalent), together
571  * with instructions on where to get the Standard Version.
572  * 
573  * d) make other distribution arrangements with the Copyright Holder.
574  * 
575  * 5. You may charge a reasonable copying fee for any distribution of this
576  * Package.  You may charge any fee you choose for support of this
577  * Package.  You may not charge a fee for this Package itself.  However,
578  * you may distribute this Package in aggregate with other (possibly
579  * commercial) programs as part of a larger (possibly commercial) software
580  * distribution provided that you do not advertise this Package as a
581  * product of your own. 
582  * 
583  * 6. The name of the Copyright Holder may not be used to endorse or promote
584  * products derived from this software without specific prior written permission.
585  * 
586  * 7. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
587  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
588  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
589  * 
590  * The End
591  */