Example #1
0
/**
 * 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)) {