nis-util
1.0.D108
|
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 :