BRL-CAD
vls_vprintf.c
Go to the documentation of this file.
1 /* V L S _ V P R I N T F . C
2  * BRL-CAD
3  *
4  * Copyright (c) 2004-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 
21 #include "common.h"
22 
23 #include <stdlib.h>
24 #include <ctype.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <assert.h>
28 #include <math.h>
29 
30 #ifdef HAVE_STDINT_H
31 # include <stdint.h>
32 #endif
33 
34 #include "bio.h"
35 
36 #include "bu/log.h"
37 #include "bu/vls.h"
38 
39 #include "./vls_internals.h"
40 
41 /* private constants */
42 
43 /* bit flags for fmt specifier attributes */
44 /* short (char) length modifiers */
45 static const int SHORTINT = 0x0001;
46 static const int SHHRTINT = 0x0002;
47 /* integer length modifiers */
48 static const int LONG_INT = 0x0004;
49 static const int LLONGINT = 0x0008;
50 /* double length modifiers */
51 static const int LONGDBLE = 0x0010;
52 /* other integer length modifiers */
53 static const int INTMAX_T = 0x0020;
54 static const int PTRDIFFT = 0x0040;
55 static const int SIZETINT = 0x0080;
56 /* misc */
57 static const int FIELDLEN = 0x0100;
58 static const int PRECISION = 0x0200;
59 /* groups */
60 #define MISCINTMODS (INTMAX_T | PTRDIFFT | SIZETINT)
61 #define SHORTINTMODS (SHORTINT | SHHRTINT)
62 #define LONGINTMODS (LONG_INT | LLONGINT)
63 #define ALL_INTMODS (SHORTINTMODS | LONGINTMODS | MISCINTMODS)
64 #define ALL_DOUBLEMODS (LONGDBLE)
65 #define ALL_LENGTHMODS (ALL_INTMODS | ALL_DOUBLEMODS)
66 
67 /* private functions */
68 
69 
70 /* defs */
71 static void
72 reset_vflags(vflags_t *f)
73 {
74  f->fieldlen = -1;
75  f->flags = 0;
76  f->have_digit = 0;
77  f->have_dot = 0;
78  f->left_justify = 0;
79  f->precision = 0;
80 }
81 
82 /* Note that multiple instances of a character will invalidate the
83  * second or any later instance, i.e., first found wins.
84  */
85 int
86 format_part_status(const char c)
87 {
88  int status = VP_VALID; /* assume valid */
89 
90  /* return a bit flag with: type, validity */
91  switch (c) {
92  /* VALID ===================================== */
93  case 'd':
94  case 'i':
95  case 'o':
96  case 'u':
97  case 'x':
98  case 'X':
99  case 'e':
100  case 'E':
101  case 'f':
102  case 'F':
103  case 'g':
104  case 'G':
105  case 'a':
106  case 'A':
107  case 'c':
108  case 's':
109  case 'p':
110  case 'n':
111  case '%':
112  case 'V': /* bu_vls extension */
113  status |= VP_CONVERSION_SPEC;
114  break;
115 
116  case 'h': /* can be doubled: 'hh' */
117  case 'l': /* can be doubled: 'll' */
118  case 'L':
119  case 'j':
120  case 'z':
121  case 't':
122  status |= VP_LENGTH_MOD;
123  break;
124 
125  case '#':
126  case '0':
127  case '-':
128  case ' ':
129  case '+':
130  case '\'': /* SUSv2 */
131  status |= VP_FLAG;
132  break;
133 
134  case '*':
135  case ';':
136  status |= VP_MISC;
137  break;
138 
139  /* OBSOLETE ===================================== */
140  /* obsolete or not recommended (considered obsolete for bu_vls): */
141  case 'm': /* glibc extension for printing strerror(errno) (not same as %m$ or %*mS) */
142  case 'C': /* Synonym for lc. (Not in C99, but in SUSv2. Don't use.) */
143  case 'D': /* Synonym for ld. (libc4--don't use) */
144  case 'O': /* Synonym for lo. (libc5--don't use) */
145  case 'S': /* Synonym for ls. (Not in C99, but in SUSv2. Don't use.) */
146  case 'U': /* Synonym for lu. (libc5--don't use) */
147  status = VP_OBSOLETE;
148  status |= VP_CONVERSION_SPEC;
149  break;
150 
151  case 'Z': /* alias for 'z' (libc5--don't use) */
152  status = VP_OBSOLETE;
153  status |= VP_LENGTH_MOD;
154  break;
155 
156  case 'I': /* alias for 'z' (libc5--don't use) */
157  status = VP_OBSOLETE;
158  status |= VP_FLAG;
159  break;
160 
161  default:
162  /* all others are unknown--be sure to redefine the status value to VP_UNKNOWN */
163  status = VP_UNKNOWN;
164  break;
165  }
166 
167  return status;
168 }
169 
170 /* function returns 1 if input char is found, 0 otherwise */
171 int
172 handle_format_part(const int vp_part, vflags_t *f, const char c, const int print)
173 {
174  int status = 1; /* assume found */
175 
176  switch (vp_part) {
177  case VP_CONVERSION_SPEC:
178  switch (c) {
179  default:
180  if (print)
181  fprintf(stderr, "Unhandled conversion specifier '%c'.\n", c);
182  status = 0;
183  break;
184  }
185  break;
186  case VP_LENGTH_MOD:
187  switch (c) {
188  case 'j':
189  f->flags |= INTMAX_T;
190  break;
191  case 't':
192  f->flags |= PTRDIFFT;
193  break;
194  case 'z':
195  f->flags |= SIZETINT;
196  break;
197  case 'l':
198  /* 'l' can be doubled */
199  /* clear all length modifiers AFTER we check for the
200  first 'l' */
201  if (f->flags & LONG_INT) {
202  f->flags ^= ALL_LENGTHMODS;
203  f->flags |= LLONGINT;
204  } else {
205  f->flags ^= ALL_LENGTHMODS;
206  f->flags |= LONG_INT;
207  }
208  break;
209  case 'h':
210  /* 'h' can be doubled */
211  /* clear all length modifiers AFTER we check for the
212  first 'h' */
213  if (f->flags & SHORTINT) {
214  f->flags ^= ALL_LENGTHMODS;
215  f->flags |= SHHRTINT;
216  } else {
217  f->flags ^= ALL_LENGTHMODS;
218  f->flags |= SHORTINT;
219  }
220  break;
221  case 'L':
222  /* a length modifier for doubles */
223  /* clear all length modifiers first */
224  f->flags ^= ALL_LENGTHMODS;
225  /* set the new flag */
226  f->flags |= LONGDBLE;
227  break;
228  default:
229  if (print)
230  fprintf(stderr, "Unhandled length modifier '%c'.\n", c);
231  status = 0;
232  break;
233  }
234  break;
235  case VP_FLAG:
236  switch (c) {
237  default:
238  if (print)
239  fprintf(stderr, "Unhandled flag '%c'.\n", c);
240  status = 0;
241  break;
242  }
243  break;
244  case VP_MISC:
245  switch (c) {
246  default:
247  if (print)
248  fprintf(stderr, "Unhandled miscellaneous format character '%c'.\n", c);
249  status = 0;
250  break;
251  }
252  break;
253  default:
254  if (print)
255  fprintf(stderr, "Unhandled vprintf format part number '%d'.\n", vp_part);
256  status = 0;
257  break;
258  }
259 
260  if (print && !status) {
261  fprintf(stderr, "Report error to BRL-CAD developers.\n");
262  }
263 
264  return status;
265 }
266 
267 /* function returns 1 if input char is found, 0 otherwise */
268 int
269 handle_obsolete_format_char(const char c, const int print)
270 {
271  int status = 1; /* assume found */
272  switch (c) {
273  /* conversion specifiers */
274  case 'm': /* glibc extension for printing strerror(errno) (not same as %m$ or %*mS) */
275  if (print) {
276  fprintf(stderr, "Format specifier '%c' is not allowed.\n", c);
277  fprintf(stderr, " Use normal formatting to print 'strerror' and 'errno').\n");
278  }
279  break;
280  case 'C': /* (Not in C99, but in SUSv2.) Synonym for lc. Don't use. */
281  if (print) {
282  fprintf(stderr, "Format specifier '%c' is obsolete.\n", c);
283  fprintf(stderr, " Use 'lc' instead.\n");
284  }
285  break;
286  case 'D': /* libc4--don't use */
287  if (print) {
288  fprintf(stderr, "Format specifier '%c' is obsolete.\n", c);
289  fprintf(stderr, " Use 'ld' instead.\n");
290  }
291  break;
292  case 'O': /* libc5--don't use */
293  if (print) {
294  fprintf(stderr, "Format specifier '%c' is obsolete.\n", c);
295  fprintf(stderr, " Use 'lo' instead.\n");
296  }
297  break;
298  case 'S': /* (Not in C99, but in SUSv2.) Synonym for lc. Don't use. */
299  if (print) {
300  fprintf(stderr, "Format specifier '%c' is obsolete.\n", c);
301  fprintf(stderr, " Use 'ls' instead.\n");
302  }
303  break;
304  case 'U': /* libc5--don't use */
305  if (print) {
306  fprintf(stderr, "Format specifier '%c' is obsolete.\n", c);
307  fprintf(stderr, " Use 'lu' instead.\n");
308  }
309  break;
310 
311  /* length modifiers */
312  case 'q': /* "quad". 4.4BSD and Linux libc5 only. Don't use. */
313  if (print) {
314  fprintf(stderr, "Format length modifier '%c' is obsolete.\n", c);
315  fprintf(stderr, " Use 'll' instead.\n");
316  }
317  break;
318  case 'Z': /* alias for 'z' Linux libc5 only. Don't use. */
319  if (print) {
320  fprintf(stderr, "Format length modifier '%c' is obsolete.\n", c);
321  fprintf(stderr, " Use 'z' instead.\n");
322  }
323  break;
324 
325  /* flags */
326  case 'I': /* specifies use of locale's alternative output digits */
327  if (print) {
328  fprintf(stderr, "Format flag '%c' is not yet supported.\n", c);
329  }
330  break;
331 
332  default:
333  if (print) {
334  fprintf(stderr, "ERROR: Unhandled format character '%c'.\n", c);
335  fprintf(stderr, "Report error to BRL-CAD developers.\n");
336  }
337  status = 0;
338  break;
339  }
340 
341  return status;
342 }
343 
344 
345 /*
346 The bu_vls_vprintf function aims to adhere to the following
347 specifications:
348 
349  1. First, follow the POSIX man page at
350  "http://www.unix.com/man-page/POSIX/3/printf/" regarding the
351  definition of a format specifier.
352 
353  2. Then modify [1] to accommodate a compatible subset of parts
354  applicable to a wide range of standard C libraries including
355  GNU/Linux, Windows, FreeBSD, and others as differences are
356  brought to our attention.
357 
358  3. The subset [2] shall be the "valid" flags, length modifiers, and
359  conversion specifiers ("parts") accepted by this function.
360  Those are defined in the following local function:
361 
362  format_part_status
363 
364  4. Parts known to be defined outside subset [3] shall generate a
365  message stating such invalidity and giving a suitable
366  alternative if possible (such parts will be called "obsolete");
367  otherwise, the part shall be said to be "unsupported."
368 
369  5. Parts seen by this function but not defined above shall be
370  deemed "unknown" and result in a suitable message.
371 
372  6. Library users of this function receiving "unknown" messages
373  while attempting to use valid parts according to their O/S and
374  compiler need to contact the BRL-CAD developers to resolve the
375  issue. Resolution should normally result in assigning the
376  "unknown" part to one of the categories described in [4].
377 
378 */
379 void
380 bu_vls_vprintf(struct bu_vls *vls, const char *fmt, va_list ap)
381 {
382  const char *sp; /* start pointer */
383  const char *ep; /* end pointer */
384  int len;
385 
386  /* flag variables are reset for each fmt specifier */
387  vflags_t f;
388 
389  char buf[BUFSIZ] = {0};
390  int c;
391 
392  struct bu_vls fbuf = BU_VLS_INIT_ZERO; /* % format buffer */
393  char *fbufp = NULL;
394 
395  if (UNLIKELY(!vls || !fmt || fmt[0] == '\0')) {
396  /* nothing to print to or from */
397  return;
398  }
399 
400  BU_CK_VLS(vls);
401 
402  bu_vls_extend(vls, (unsigned int)_VLS_ALLOC_STEP);
403 
404  sp = fmt;
405  while (*sp) {
406  /* Initial state: just printing chars */
407  fmt = sp;
408  while (*sp != '%' && *sp)
409  sp++;
410 
411  if (sp != fmt)
412  bu_vls_strncat(vls, fmt, (size_t)(sp - fmt));
413 
414  if (*sp == '\0')
415  break;
416 
417  /* Saw a percent sign, now need to find end of fmt specifier */
418  /* All flags get reset for this fmt specifier */
419  reset_vflags(&f);
420 
421  ep = sp;
422  while ((c = *(++ep))) {
423 
424  if (c == ' '
425  || c == '#'
426  || c == '+'
427  || c == '\''
428  )
429  {
430  /* skip */
431  } else if (c == '.') {
432  f.have_dot = 1;
433  } else if (isdigit(c)) {
434  /* skip */
435  } else if (c == '-') {
436  /* the first occurrence before a dot is the
437  left-justify flag, but the occurrence AFTER a dot is
438  taken to be zero precision */
439  if (f.have_dot) {
440  f.precision = 0;
441  f.have_digit = 0;
442  } else if (f.have_digit) {
443  /* FIXME: ERROR condition?: invalid format string
444  (e.g., '%7.8-f') */
445  /* seems as if the fprintf man page is indefinite here,
446  looks like the '-' is passed through and
447  appears in output */
448  ;
449  } else {
450  f.left_justify = 1;
451  }
452  } else if (c == '*') {
453  /* the first occurrence is the field width, but the
454  second occurrence is the precision specifier */
455  if (!f.have_dot) {
456  f.fieldlen = va_arg(ap, int);
457  f.flags |= FIELDLEN;
458  } else {
459  f.precision = va_arg(ap, int);
460  f.flags |= PRECISION;
461  }
462  /* all length modifiers below here */
463  } else if (format_part_status(c) == (VP_VALID | VP_LENGTH_MOD)) {
464  handle_format_part(VP_LENGTH_MOD, &f, c, VP_PRINT);
465  } else {
466  /* Anything else must be the end of the fmt specifier
467  (i.e., the conversion specifier)*/
468  break;
469  }
470  }
471 
472  /* libc left-justifies if there's a '-' char, even if the
473  * value is already negative, so no need to check current value
474  * of left_justify.
475  */
476  if (f.fieldlen < 0) {
477  f.fieldlen = -f.fieldlen;
478  f.left_justify = 1;
479  }
480 
481  /* Copy off this entire format string specifier */
482  len = ep - sp + 1;
483 
484  /* intentionally avoid bu_strlcpy here since the source field
485  * may be legitimately truncated.
486  */
487  bu_vls_strncpy(&fbuf, sp, (size_t)len);
488  fbufp = bu_vls_addr(&fbuf);
489 
490 #ifndef HAVE_C99_FORMAT_SPECIFIERS
491  /* if the format string uses the %z or %t width specifier, we need to
492  * replace it with something more palatable to this busted compiler.
493  */
494 
495  if ((f.flags & SIZETINT) || (f.flags & PTRDIFFT)) {
496  char *fp = fbufp;
497  while (*fp) {
498  if (*fp == '%') {
499  /* found the next format specifier */
500  while (*fp) {
501  fp++;
502  /* possible characters that can precede the field
503  * length character (before the type).
504  */
505  if (isdigit(*fp)
506  || *fp == '$'
507  || *fp == '#'
508  || *fp == '+'
509  || *fp == '.'
510  || *fp == '-'
511  || *fp == ' '
512  || *fp == '*') {
513  continue;
514  }
515  if (*fp == 'z' || *fp == 't') {
516  /* assume MSVC replacing instances of %z or %t with
517  * %I (capital i) until we encounter anything
518  * different.
519  */
520  *fp = 'I';
521  }
522 
523  break;
524  }
525  if (*fp == '\0') {
526  break;
527  }
528  }
529  fp++;
530  }
531  }
532 #endif
533 
534  /* use type specifier to grab parameter appropriately from arg
535  list, and print it correctly */
536  switch (c) {
537  case 's':
538  {
539  /* variables used to determine final effects of
540  field length and precision (different for
541  strings versus numbers) */
542  int minfldwid = -1;
543  int maxstrlen = -1;
544 
545  char *str = va_arg(ap, char *);
546  const char *fp = fbufp;
547 
548  f.left_justify = 0;
549  f.have_dot = 0;
550  while (*fp) {
551  if (isdigit((unsigned char)*fp)) {
552 
553  if (!f.have_dot) {
554  if (*fp == '0') {
555  bu_sscanf(fp, "%d", &f.fieldlen);
556  } else {
557  f.fieldlen = atoi(fp);
558  }
559  f.flags |= FIELDLEN;
560  } else {
561  if (*fp == '0') {
562  bu_sscanf(fp, "%d", &f.precision);
563  } else {
564  f.precision = atoi(fp);
565  }
566  f.flags |= PRECISION;
567  }
568 
569  while (isdigit((unsigned char)*(fp+1)))
570  fp++;
571 
572  if (*fp == '\0') {
573  break;
574  }
575  } else if (*fp == '.') {
576  f.have_dot = 1;
577  } else if (*fp == '-') {
578  f.left_justify = 1;
579  }
580  fp++;
581  }
582 
583  /* for strings only */
584  /* field length is a minimum size and precision is
585  * max length of string to be printed.
586  */
587  if (f.flags & FIELDLEN) {
588  minfldwid = f.fieldlen;
589  }
590  if (f.flags & PRECISION) {
591  maxstrlen = f.precision;
592  }
593 
594  if (str) {
595  int stringlen = (int)strlen(str);
596  struct bu_vls tmpstr = BU_VLS_INIT_ZERO;
597 
598  /* use a copy of the string */
599  bu_vls_strcpy(&tmpstr, str);
600 
601  /* handle a non-empty string */
602  /* strings may be truncated */
603  if (maxstrlen >= 0) {
604  if (maxstrlen < stringlen) {
605  /* have to truncate */
606  bu_vls_trunc(&tmpstr, maxstrlen);
607  stringlen = maxstrlen;
608  } else {
609  maxstrlen = stringlen;
610  }
611  }
612  minfldwid = minfldwid < maxstrlen ? maxstrlen : minfldwid;
613 
614  if (stringlen < minfldwid) {
615  /* padding spaces needed */
616  /* start a temp string to deal with padding */
617  struct bu_vls padded = BU_VLS_INIT_ZERO;
618  int i;
619 
620  if (f.left_justify) {
621  /* string goes before padding spaces */
622  bu_vls_vlscat(&padded, &tmpstr);
623  }
624  /* now put in padding spaces in all cases */
625  for (i = 0; i < minfldwid - stringlen; ++i) {
626  bu_vls_putc(&padded, ' ');
627  }
628  if (!f.left_justify) {
629  /* string follows the padding spaces */
630  bu_vls_vlscat(&padded, &tmpstr);
631  }
632  /* now we can send the padded string to the tmp string */
633  /* have to truncate it to zero length first */
634  bu_vls_trunc(&tmpstr, 0);
635  bu_vls_vlscat(&tmpstr, &padded);
636 
637  bu_vls_free(&padded);
638  }
639  /* now take string as is */
640  bu_vls_vlscat(vls, &tmpstr);
641 
642  bu_vls_free(&tmpstr);
643  } else {
644  /* handle an empty string */
645  /* FIXME: should we trunc to precision if > fieldlen? */
646  if (f.flags & FIELDLEN) {
647  bu_vls_strncat(vls, "(null)", (size_t)f.fieldlen);
648  } else {
649  bu_vls_strcat(vls, "(null)");
650  }
651  }
652  }
653  break;
654  case 'V':
655  {
656  struct bu_vls *vp;
657 
658  vp = va_arg(ap, struct bu_vls *);
659  if (vp) {
660  BU_CK_VLS(vp);
661  if (f.flags & FIELDLEN) {
662  int stringlen = bu_vls_strlen(vp);
663 
664  if (stringlen >= f.fieldlen)
665  bu_vls_strncat(vls, bu_vls_addr(vp), (size_t)f.fieldlen);
666  else {
667  struct bu_vls padded = BU_VLS_INIT_ZERO;
668  int i;
669 
670  if (f.left_justify)
671  bu_vls_vlscat(&padded, vp);
672  for (i = 0; i < f.fieldlen - stringlen; ++i)
673  bu_vls_putc(&padded, ' ');
674  if (!f.left_justify)
675  bu_vls_vlscat(&padded, vp);
676  bu_vls_vlscat(vls, &padded);
677  }
678  } else {
679  bu_vls_vlscat(vls, vp);
680  }
681  } else {
682  if (f.flags & FIELDLEN)
683  bu_vls_strncat(vls, "(null)", (size_t)f.fieldlen);
684  else
685  bu_vls_strcat(vls, "(null)");
686  }
687  }
688  break;
689  case 'e':
690  case 'E':
691  case 'f':
692  case 'g':
693  case 'G':
694  case 'F':
695  /* All floating point ==> "double" */
696  {
697  double d = va_arg(ap, double);
698  if (f.flags & FIELDLEN)
699  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, d);
700  else
701  snprintf(buf, BUFSIZ, fbufp, d);
702  }
703  bu_vls_strcat(vls, buf);
704  break;
705  case 'o':
706  case 'u':
707  case 'x':
708  case 'X':
709  if (f.flags & LONG_INT) {
710  /* Unsigned long int */
711  unsigned long l = va_arg(ap, unsigned long);
712  if (f.flags & FIELDLEN)
713  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, l);
714  else
715  snprintf(buf, BUFSIZ, fbufp, l);
716  } else if (f.flags & LLONGINT) {
717  /* Unsigned long long int */
718  unsigned long long ll = va_arg(ap, unsigned long long);
719  if (f.flags & FIELDLEN)
720  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, ll);
721  else
722  snprintf(buf, BUFSIZ, fbufp, ll);
723  } else if (f.flags & SHORTINT || f.flags & SHHRTINT) {
724  /* unsigned short int */
725  unsigned short int sh = (unsigned short int)va_arg(ap, int);
726  if (f.flags & FIELDLEN)
727  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, sh);
728  else
729  snprintf(buf, BUFSIZ, fbufp, sh);
730  } else if (f.flags & INTMAX_T) {
731  intmax_t im = va_arg(ap, intmax_t);
732  if (f.flags & FIELDLEN)
733  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, im);
734  else
735  snprintf(buf, BUFSIZ, fbufp, im);
736  } else if (f.flags & PTRDIFFT) {
737  ptrdiff_t pd = va_arg(ap, ptrdiff_t);
738  if (f.flags & FIELDLEN)
739  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, pd);
740  else
741  snprintf(buf, BUFSIZ, fbufp, pd);
742  } else if (f.flags & SIZETINT) {
743  size_t st = va_arg(ap, size_t);
744  if (f.flags & FIELDLEN)
745  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, st);
746  else
747  snprintf(buf, BUFSIZ, fbufp, st);
748  } else {
749  /* Regular unsigned int */
750  unsigned int j = (unsigned int)va_arg(ap, unsigned int);
751  if (f.flags & FIELDLEN)
752  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, j);
753  else
754  snprintf(buf, BUFSIZ, fbufp, j);
755  }
756  bu_vls_strcat(vls, buf);
757  break;
758  case 'd':
759  case 'i':
760  if (f.flags & LONG_INT) {
761  /* Long int */
762  long l = va_arg(ap, long);
763  if (f.flags & FIELDLEN)
764  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, l);
765  else
766  snprintf(buf, BUFSIZ, fbufp, l);
767  } else if (f.flags & LLONGINT) {
768  /* Long long int */
769  long long ll = va_arg(ap, long long);
770  if (f.flags & FIELDLEN)
771  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, ll);
772  else
773  snprintf(buf, BUFSIZ, fbufp, ll);
774  } else if (f.flags & SHORTINT || f.flags & SHHRTINT) {
775  /* short int */
776  short int sh = (short int)va_arg(ap, int);
777  if (f.flags & FIELDLEN)
778  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, sh);
779  else
780  snprintf(buf, BUFSIZ, fbufp, sh);
781  } else if (f.flags & INTMAX_T) {
782  intmax_t im = va_arg(ap, intmax_t);
783  if (f.flags & FIELDLEN)
784  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, im);
785  else
786  snprintf(buf, BUFSIZ, fbufp, im);
787  } else if (f.flags & PTRDIFFT) {
788  ptrdiff_t pd = va_arg(ap, ptrdiff_t);
789  if (f.flags & FIELDLEN)
790  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, pd);
791  else
792  snprintf(buf, BUFSIZ, fbufp, pd);
793  } else if (f.flags & SIZETINT) {
794  size_t st = va_arg(ap, size_t);
795  if (f.flags & FIELDLEN)
796  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, st);
797  else
798  snprintf(buf, BUFSIZ, fbufp, st);
799  } else {
800  /* Regular int */
801  int j = va_arg(ap, int);
802  if (f.flags & FIELDLEN)
803  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, j);
804  else
805  snprintf(buf, BUFSIZ, fbufp, j);
806  }
807  bu_vls_strcat(vls, buf);
808  break;
809  case 'n':
810  case 'p':
811  /* all pointer == "void *" */
812  {
813  void *vp = (void *)va_arg(ap, void *);
814  if (f.flags & FIELDLEN)
815  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, vp);
816  else
817  snprintf(buf, BUFSIZ, fbufp, vp);
818  }
819  bu_vls_strcat(vls, buf);
820  break;
821  case '%':
822  bu_vls_putc(vls, '%');
823  break;
824  case 'c':
825  {
826  char ch = (char)va_arg(ap, int);
827  if (f.flags & FIELDLEN)
828  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, ch);
829  else
830  snprintf(buf, BUFSIZ, fbufp, ch);
831  }
832  bu_vls_strcat(vls, buf);
833  break;
834  default:
835  if (format_part_status(c) & VP_UNKNOWN) {
836  fprintf(stderr, "ERROR: Unknown format character '%c'.\n", c);
837  } else if (format_part_status(c) & VP_OBSOLETE) {
838  handle_obsolete_format_char(c, VP_PRINT);
839  } else {
840  fprintf(stderr, "Unknown format character '%c'.\n", c);
841  fprintf(stderr, " Status flags: %x.\n", format_part_status(c));
842  bu_bomb("ERROR: Shouldn't get here.\n");
843  }
844  /* try to get some kind of output, assume it's an int */
845  {
846  int d = va_arg(ap, int);
847  if (f.flags & FIELDLEN)
848  snprintf(buf, BUFSIZ, fbufp, f.fieldlen, d);
849  else
850  snprintf(buf, BUFSIZ, fbufp, d);
851  }
852  bu_vls_strcat(vls, buf);
853  break;
854  }
855  sp = ep + 1;
856  }
857 
858  va_end(ap);
859 
860  bu_vls_free(&fbuf);
861 }
862 
863 /*
864  * Local Variables:
865  * mode: C
866  * tab-width: 8
867  * indent-tabs-mode: t
868  * c-file-style: "stroustrup"
869  * End:
870  * ex: shiftwidth=4 tabstop=8
871  */
#define PTRDIFFT
Definition: sscanf.c:64
void bu_vls_strncat(struct bu_vls *vp, const char *s, size_t n)
Definition: vls.c:390
int handle_format_part(const int vp_part, vflags_t *f, const char c, const int print)
Definition: vls_vprintf.c:172
void bu_vls_strcat(struct bu_vls *vp, const char *s)
Definition: vls.c:368
void bu_vls_trunc(struct bu_vls *vp, int len)
Definition: vls.c:198
#define st
#define VP_VALID
Definition: vls_internals.h:35
void bu_vls_strncpy(struct bu_vls *vp, const char *s, size_t n)
Definition: vls.c:339
Header file for the BRL-CAD common definitions.
void bu_vls_free(struct bu_vls *vp)
Definition: vls.c:248
int format_part_status(const char c)
Definition: vls_vprintf.c:86
#define BU_CK_VLS(_vp)
Definition: vls.h:69
#define VP_LENGTH_MOD
Definition: vls_internals.h:32
int bu_sscanf(const char *src, const char *fmt,...) _BU_ATTR_SCANF23
Definition: sscanf.c:676
#define VP_CONVERSION_SPEC
Definition: vls_internals.h:33
size_t bu_vls_strlen(const struct bu_vls *vp)
Definition: vls.c:189
#define VP_UNKNOWN
Definition: vls_internals.h:30
char * bu_vls_addr(const struct bu_vls *vp)
Definition: vls.c:111
void bu_vls_vprintf(struct bu_vls *vls, const char *fmt, va_list ap)
Definition: vls_vprintf.c:380
int handle_obsolete_format_char(const char c, const int print)
Definition: vls_vprintf.c:269
#define VP_FLAG
Definition: vls_internals.h:31
void pd(register FILE *plotfp, double x, double y, char c)
Definition: plot3.c:127
#define VP_OBSOLETE
Definition: vls_internals.h:36
void bu_vls_extend(struct bu_vls *vp, size_t extra)
Definition: vls.c:136
#define VP_MISC
Definition: vls_internals.h:34
#define ALL_LENGTHMODS
Definition: vls_vprintf.c:65
void bu_vls_strcpy(struct bu_vls *vp, const char *s)
Definition: vls.c:310
#define BU_VLS_INIT_ZERO
Definition: vls.h:84
void bu_vls_vlscat(struct bu_vls *dest, const struct bu_vls *src)
Definition: vls.c:415
Definition: vls.h:56
void bu_bomb(const char *str) _BU_ATTR_NORETURN
Definition: bomb.c:91
void bu_vls_putc(struct bu_vls *vp, int c)
Definition: vls.c:666
#define UNLIKELY(expression)
Definition: common.h:282