/** * b u _ v l s _ v p r i n t f * * Format a string into a vls. This version should work on * practically any machine, but it serves to highlight the the * grossness of the varargs package requiring the size of a parameter * to be known at compile time. * * %s continues to be a regular 'C' string, null terminated. * %S is a pointer to a (struct bu_vls *) string. * * This routine appends to the given vls similar to how vprintf * appends to stdout (see bu_vls_vsprintf for overwriting the vls). */ void bu_vls_vprintf(struct bu_vls *vls, const char *fmt, va_list ap) { register const char *sp; /* start pointer */ register const char *ep; /* end pointer */ register int len; #define LONGINT 0x001 #define FIELDLEN 0x002 #define SHORTINT 0x003 int flags; int fieldlen=-1; char fbuf[64] = {0}; /* % format buffer */ char buf[BUFSIZ] = {0}; if (!vls || !fmt || fmt[0] == '\0') { /* nothing to print to or from */ return; } BU_CK_VLS(vls); bu_vls_extend(vls, _VLS_ALLOC_STEP); sp = fmt; while ( *sp ) { /* Initial state: just printing chars */ fmt = sp; while (*sp != '%' && *sp) sp++; if (sp != fmt) bu_vls_strncat(vls, fmt, (size_t)(sp-fmt)); if (*sp == '\0') break; /* Saw a percent sign, find end of fmt specifier */ flags = 0; ep = sp; while ( *ep ) { ++ep; if (*ep == ' ' || *ep == '#' || *ep == '-' || *ep == '+' || *ep == '.' || isdigit(*ep)) continue; else if (*ep == 'l' || *ep == 'U' || *ep == 'O') flags |= LONGINT; else if (*ep == '*') { fieldlen = va_arg(ap, int); flags |= FIELDLEN; } else if (*ep == 'h') { flags |= SHORTINT; } else /* Anything else must be the end of the fmt specifier */ break; }
/* The bu_vls_vprintf function aims to adhere to the following specifications: 1. First, follow the POSIX man page at "http://www.unix.com/man-page/POSIX/3/printf/" regarding the definition of a format specifier. 2. Then modify [1] to accommodate a compatible subset of parts applicable to a wide range of standard C libraries including GNU/Linux, Windows, FreeBSD, and others as differences are brought to our attention. 3. The subset [2] shall be the "valid" flags, length modifiers, and conversion specifiers ("parts") accepted by this function. Those are defined in the following local function: format_part_status 4. Parts known to be defined outside subset [3] shall generate a message stating such invalidity and giving a suitable alternative if possible (such parts will be called "obsolete"); otherwise, the part shall be said to be "unsupported." 5. Parts seen by this function but not defined above shall be deemed "unknown" and result in a suitable message. 6. Library users of this function receiving "unknown" messages while attempting to use valid parts according to their O/S and compiler need to contact the BRL-CAD developers to resolve the issue. Resolution should normally result in assigning the "unknown" part to one of the categories described in [4]. */ void bu_vls_vprintf(struct bu_vls *vls, const char *fmt, va_list ap) { const char *sp; /* start pointer */ const char *ep; /* end pointer */ int len; /* flag variables are reset for each fmt specifier */ vflags_t f; char buf[BUFSIZ] = {0}; int c; struct bu_vls fbuf = BU_VLS_INIT_ZERO; /* % format buffer */ char *fbufp = NULL; if (UNLIKELY(!vls || !fmt || fmt[0] == '\0')) { /* nothing to print to or from */ return; } BU_CK_VLS(vls); bu_vls_extend(vls, (unsigned int)_VLS_ALLOC_STEP); sp = fmt; while (*sp) { /* Initial state: just printing chars */ fmt = sp; while (*sp != '%' && *sp) sp++; if (sp != fmt) bu_vls_strncat(vls, fmt, (size_t)(sp - fmt)); if (*sp == '\0') break; /* Saw a percent sign, now need to find end of fmt specifier */ /* All flags get reset for this fmt specifier */ reset_vflags(&f); ep = sp; while ((c = *(++ep))) { if (c == ' ' || c == '#' || c == '+' || c == '\'' ) { /* skip */ } else if (c == '.') { f.have_dot = 1; } else if (isdigit(c)) { /* skip */ } else if (c == '-') { /* the first occurrence before a dot is the left-justify flag, but the occurrence AFTER a dot is taken to be zero precision */ if (f.have_dot) { f.precision = 0; f.have_digit = 0; } else if (f.have_digit) { /* FIXME: ERROR condition?: invalid format string (e.g., '%7.8-f') */ /* seems as if the fprintf man page is indefinite here, looks like the '-' is passed through and appears in output */ ; } else { f.left_justify = 1; } } else if (c == '*') { /* the first occurrence is the field width, but the second occurrence is the precision specifier */ if (!f.have_dot) { f.fieldlen = va_arg(ap, int); f.flags |= FIELDLEN; } else { f.precision = va_arg(ap, int); f.flags |= PRECISION; } /* all length modifiers below here */ } else if (format_part_status(c) == (VP_VALID | VP_LENGTH_MOD)) {