nis-util  1.0.D108
lib/mprintf.cc
Go to the documentation of this file.
00001 //
00002 // nis-util - NIS Administration Utilities
00003 // Copyright (C) 2001, 2008, 2009, 2011, 2012 Peter Miller
00004 //
00005 // This program is free software; you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation; either version 2 of the License, or (at
00008 // your option) any later version.
00009 //
00010 // This program is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License
00016 // along with this program. If not, see <http://www.gnu.org/licenses/>.
00017 //
00018 
00019 #include <lib/ac/errno.h>
00020 #include <lib/ac/stdio.h>
00021 #include <lib/ac/stdlib.h>
00022 #include <lib/ac/string.h>
00023 #include <libexplain/malloc.h>
00024 #include <libexplain/realloc.h>
00025 
00026 #include <lib/mprintf.h>
00027 
00028 //
00029 // size to grow memory by
00030 //
00031 #define QUANTUM 200
00032 
00033 //
00034 // maximum width for numbers
00035 //
00036 #define MAX_WIDTH (QUANTUM - 1)
00037 
00038 //
00039 // the buffer for storing results
00040 //
00041 static size_t   tmplen;
00042 static size_t   length;
00043 static char     *tmp;
00044 
00045 
00052 static int
00053 bigger(void)
00054 {
00055     size_t nbytes = tmplen + QUANTUM;
00056     char *hold = (char *)explain_realloc_or_die(tmp, nbytes);
00057     tmplen = nbytes;
00058     tmp = hold;
00059     return 1;
00060 }
00061 
00062 
00082 static void
00083 build_fake(char *fake, int flag, int width, int precision, int qualifier,
00084     int specifier)
00085 {
00086     char *fp = fake;
00087     *fp++ = '%';
00088     if (flag)
00089         *fp++ = flag;
00090     if (width > 0)
00091     {
00092         sprintf(fp, "%d", width);
00093         fp += strlen(fp);
00094     }
00095     *fp++ = '.';
00096     sprintf(fp, "%d", precision);
00097     fp += strlen(fp);
00098     if (qualifier)
00099         *fp++ = qualifier;
00100     *fp++ = specifier;
00101     *fp = 0;
00102 }
00103 
00104 
00105 const char *
00106 vmprintf(const char *fmt, va_list ap)
00107 {
00108     int         width;
00109     int         width_set;
00110     int         prec;
00111     int         prec_set;
00112     int         c;
00113     const char  *s;
00114     int         qualifier;
00115     int         flag;
00116     char        fake[QUANTUM - 1];
00117 
00118     //
00119     // Build the result string in a temporary buffer.
00120     // Grow the temporary buffer as necessary.
00121     //
00122     // It is important to only make one pass across the variable argument
00123     // list.  Behaviour is undefined for more than one pass.
00124     //
00125     if (!tmplen)
00126     {
00127         tmplen = 500;
00128         tmp = (char *)explain_malloc_or_die(tmplen);
00129     }
00130 
00131     length = 0;
00132     s = fmt;
00133     while (*s)
00134     {
00135         c = (unsigned char)*s++;
00136         if (c != '%')
00137         {
00138             normal:
00139             if (length >= tmplen && !bigger())
00140                 return 0;
00141             tmp[length++] = c;
00142             continue;
00143         }
00144         c = (unsigned char)*s++;
00145 
00146         //
00147         // get optional flag
00148         //
00149         switch (c)
00150         {
00151         case '+':
00152         case '-':
00153         case '#':
00154         case '0':
00155         case ' ':
00156             flag = c;
00157             c = *s++;
00158             break;
00159 
00160         default:
00161             flag = 0;
00162             break;
00163         }
00164 
00165         //
00166         // get optional width
00167         //
00168         width = 0;
00169         width_set = 0;
00170         switch (c)
00171         {
00172         case '*':
00173             width = va_arg(ap, int);
00174             if (width < 0)
00175             {
00176                 flag = '-';
00177                 width = -width;
00178             }
00179             c = *s++;
00180             width_set = 1;
00181             break;
00182 
00183         case '0': case '1': case '2': case '3': case '4':
00184         case '5': case '6': case '7': case '8': case '9':
00185             for (;;)
00186             {
00187                 width = width * 10 + c - '0';
00188                 c = *s++;
00189                 switch (c)
00190                 {
00191                 default:
00192                     break;
00193 
00194                 case '0': case '1': case '2': case '3':
00195                 case '4': case '5': case '6': case '7':
00196                 case '8': case '9':
00197                     continue;
00198                 }
00199                 break;
00200             }
00201             width_set = 1;
00202             break;
00203 
00204         default:
00205             break;
00206         }
00207 
00208         //
00209         // get optional precision
00210         //
00211         prec = 0;
00212         prec_set = 0;
00213         if (c == '.')
00214         {
00215             c = *s++;
00216             switch (c)
00217             {
00218             default:
00219                 prec_set = 1;
00220                 break;
00221 
00222             case '*':
00223                 c = *s++;
00224                 prec = va_arg(ap, int);
00225                 if (prec < 0)
00226                 {
00227                     prec = 0;
00228                     break;
00229                 }
00230                 prec_set = 1;
00231                 break;
00232 
00233             case '0': case '1': case '2': case '3': case '4':
00234             case '5': case '6': case '7': case '8': case '9':
00235                 for (;;)
00236                 {
00237                     prec = prec * 10 + c - '0';
00238                     c = *s++;
00239                     switch (c)
00240                     {
00241                     default:
00242                         break;
00243 
00244                     case '0': case '1': case '2': case '3':
00245                     case '4': case '5': case '6': case '7':
00246                     case '8': case '9':
00247                         continue;
00248                     }
00249                     break;
00250                 }
00251                 prec_set = 1;
00252                 break;
00253             }
00254         }
00255 
00256         //
00257         // get the optional qualifier
00258         //
00259         switch (c)
00260         {
00261         default:
00262             qualifier = 0;
00263             break;
00264 
00265         case 'l':
00266         case 'h':
00267         case 'L':
00268             qualifier = c;
00269             c = *s++;
00270             break;
00271         }
00272 
00273         //
00274         // get conversion specifier
00275         //
00276         switch (c)
00277         {
00278         default:
00279             errno = EINVAL;
00280             return 0;
00281 
00282         case '%':
00283             goto normal;
00284 
00285         case 'c':
00286             {
00287                 int     a;
00288                 char    num[MAX_WIDTH + 1];
00289                 size_t  len;
00290 
00291                 a = (unsigned char)va_arg(ap, int);
00292                 if (!prec_set)
00293                     prec = 1;
00294                 if (width > MAX_WIDTH)
00295                     width = MAX_WIDTH;
00296                 if (prec > MAX_WIDTH)
00297                     prec = MAX_WIDTH;
00298                 build_fake(fake, flag, width, prec, 0, c);
00299                 sprintf(num, fake, a);
00300                 len = strlen(num);
00301                 // assert(len < QUANTUM);
00302                 if (length + len > tmplen && !bigger())
00303                     return 0;
00304                 memcpy(tmp + length, num, len);
00305                 length += len;
00306             }
00307             break;
00308 
00309         case 'd':
00310         case 'i':
00311             {
00312                 long    a;
00313                 char    num[MAX_WIDTH + 1];
00314                 size_t  len;
00315 
00316                 switch (qualifier)
00317                 {
00318                 case 'l':
00319                     a = va_arg(ap, long);
00320                     break;
00321 
00322                 case 'h':
00323                     a = (short)va_arg(ap, int);
00324                     break;
00325 
00326                 default:
00327                     a = va_arg(ap, int);
00328                     break;
00329                 }
00330                 if (!prec_set)
00331                     prec = 1;
00332                 if (width > MAX_WIDTH)
00333                     width = MAX_WIDTH;
00334                 if (prec > MAX_WIDTH)
00335                     prec = MAX_WIDTH;
00336                 build_fake(fake, flag, width, prec, 'l', c);
00337                 sprintf(num, fake, a);
00338                 len = strlen(num);
00339                 // assert(len < QUANTUM);
00340                 if (length + len > tmplen && !bigger())
00341                     return 0;
00342                 memcpy(tmp + length, num, len);
00343                 length += len;
00344             }
00345             break;
00346 
00347         case 'e':
00348         case 'f':
00349         case 'g':
00350         case 'E':
00351         case 'F':
00352         case 'G':
00353             {
00354                 double  a;
00355                 char    num[MAX_WIDTH + 1];
00356                 size_t  len;
00357 
00358                 //
00359                 // Ignore "long double" for now,
00360                 // traditional implementations no grok.
00361                 //
00362                 a = va_arg(ap, double);
00363                 if (!prec_set)
00364                     prec = 6;
00365                 if (width > MAX_WIDTH)
00366                     width = MAX_WIDTH;
00367                 if (prec > MAX_WIDTH)
00368                     prec = MAX_WIDTH;
00369                 build_fake(fake, flag, width, prec, 0, c);
00370                 sprintf(num, fake, a);
00371                 len = strlen(num);
00372                 // assert(len < QUANTUM);
00373                 if (length + len > tmplen && !bigger())
00374                     return 0;
00375                 memcpy(tmp + length, num, len);
00376                 length += len;
00377             }
00378             break;
00379 
00380         case 'n':
00381             switch (qualifier)
00382             {
00383             case 'l':
00384                 {
00385                     long    *a;
00386 
00387                     a = va_arg(ap, long *);
00388                     *a = length;
00389                 }
00390                 break;
00391 
00392             case 'h':
00393                 {
00394                     short   *a;
00395 
00396                     a = va_arg(ap, short *);
00397                     *a = length;
00398                 }
00399                 break;
00400 
00401             default:
00402                 {
00403                     int     *a;
00404 
00405                     a = va_arg(ap, int *);
00406                     *a = length;
00407                 }
00408                 break;
00409             }
00410             break;
00411 
00412         case 'u':
00413         case 'o':
00414         case 'x':
00415         case 'X':
00416             {
00417                 unsigned long   a;
00418                 char        num[MAX_WIDTH + 1];
00419                 size_t      len;
00420 
00421                 switch (qualifier)
00422                 {
00423                 case 'l':
00424                     a = va_arg(ap, unsigned long);
00425                     break;
00426 
00427                 case 'h':
00428                     a = (unsigned short)va_arg(ap, unsigned int);
00429                     break;
00430 
00431                 default:
00432                     a = va_arg(ap, unsigned int);
00433                     break;
00434                 }
00435                 if (!prec_set)
00436                     prec = 1;
00437                 if (prec > MAX_WIDTH)
00438                     prec = MAX_WIDTH;
00439                 if (width > MAX_WIDTH)
00440                     width = MAX_WIDTH;
00441                 build_fake(fake, flag, width, prec, 'l', c);
00442                 sprintf(num, fake, a);
00443                 len = strlen(num);
00444                 // assert(len < QUANTUM);
00445                 if (length + len > tmplen && !bigger())
00446                     return 0;
00447                 memcpy(tmp + length, num, len);
00448                 length += len;
00449             }
00450             break;
00451 
00452         case 's':
00453             {
00454                 size_t len = 0;
00455                 char *a = va_arg(ap, char *);
00456                 if (prec_set)
00457                 {
00458                     char *ep = (char *)memchr(a, 0, prec);
00459                     if (ep)
00460                         len = ep - a;
00461                     else
00462                         len = prec;
00463                 }
00464                 else
00465                     len = strlen(a);
00466                 if (!prec_set || (int)len < prec)
00467                     prec = len;
00468                 if (!width_set || width < prec)
00469                     width = prec;
00470                 len = width;
00471                 while (length + len > tmplen)
00472                 {
00473                     if (!bigger())
00474                         return 0;
00475                 }
00476                 if (flag != '-')
00477                 {
00478                     while (width > prec)
00479                     {
00480                         tmp[length++] = ' ';
00481                         width--;
00482                     }
00483                 }
00484                 memcpy(tmp + length, a, prec);
00485                 length += prec;
00486                 width -= prec;
00487                 if (flag == '-')
00488                 {
00489                     while (width > 0)
00490                     {
00491                         tmp[length++] = ' ';
00492                         width--;
00493                     }
00494                 }
00495             }
00496             break;
00497         }
00498     }
00499 
00500     //
00501     // append a trailing NUL
00502     //
00503     if (length >= tmplen && !bigger())
00504         return 0;
00505     tmp[length] = 0;
00506 
00507     //
00508     // return the temporary string
00509     //
00510     return tmp;
00511 }
00512 
00513 
00514 const char *
00515 mprintf(const char *fmt, ...)
00516 {
00517     va_list ap;
00518     va_start(ap, fmt);
00519     const char *result = vmprintf(fmt, ap);
00520     va_end(ap);
00521     return result;
00522 }
00523 
00524 
00525 const char *
00526 vmprintfe(const char *fmt, va_list ap)
00527 {
00528     return vmprintf(fmt, ap);
00529 }
00530 
00531 
00532 const char *
00533 mprintfe(const char *fmt, ...)
00534 {
00535     va_list ap;
00536     va_start(ap, fmt);
00537     const char *result = vmprintfe(fmt, ap);
00538     va_end(ap);
00539     return result;
00540 }
00541 
00542 
00543 // vim: set ts=8 sw=4 et :