2

I have a format string say char *format = "hello %d world %d" and an array int array[2] = {10, 20}; with me. This is just for example sake, the number of values to be printed in the array can be arbitrary, a max-sized array with a count of the number of values to be printed. (int array[MAX]; int array_count).

So obviously I wont be able to use the standard prinf(format, ..) for that. So what I am thiking is that I will walk the format string, and extract a sub-string with just one format specifier from that, and use printf(sub-string, array[index]); and advance the sub-string to the next format specifier - something like what printf does internally.

So I am curious to know if there are any libs around which given a print format string will return me the offset of the first formatting specifier in that string, so that it saves me some work ?

Dan
  • 10,531
  • 2
  • 36
  • 55
Gopa
  • 33
  • 4
  • Finding the first format specifier is incredibly easy: `x = strstr(input, "%"); if (x && input[x] != '%') { /*do things*/ }` – ssube Feb 10 '12 at 05:51
  • If its just searching for %, then yes its easy. But I was not sure whether the C format specifier has any wierd format specifiers like saying that a "\%" ignores the % or that a "%%" means something else. Thats why I dint want to explore writing a decoder myself because I would then need to understand the format specifiers in detail. So the question then becomes are all the format specifiers guaranteed to be just one "%" and the preceeding / superceeding characters dont matter ? I guess I need to sit and read the format specifiers in detail :) – Gopa Feb 10 '12 at 05:56
  • Format specifiers tend to vary by platform (I don't remember if its kernel or system library), but that keeps them relatively constant within a platform, and they don't change too much between them. They also tend to use % as the escape character in my experience, so %% is a non-format % (hence my snippet checking for the next char being %). If you need significant amounts of data, it may be better to use a full parser, but the rules are simple enough that finding them and the type is easyish. – ssube Feb 10 '12 at 05:58
  • I don't understand why you need to do this. It your entire format string is known, bite the bullet and directly use `array[0]`, `array[1]`, ... `array[n]` arguments to `printf`. If the size of the array is variable, then you'd need to dynamically build the format string at runtime anyway. Also, since all the data is coming from an array, it's a homogeneous data type, so you already know what format specifiers to use for every item. – jamesdlin Feb 10 '12 at 08:05

2 Answers2

0

I have one. It's a bit large to post here, though. Contact me by email - see my profile.

Here's the header, so you have some idea of what you're getting into:

/*
@(#)File:           $RCSfile: printfmt.h,v $
@(#)Version:        $Revision: 2.1 $
@(#)Last changed:   $Date: 2011/07/18 16:30:54 $
@(#)Purpose:        Parse printf() format string for conversion specification
@(#)Author:         J Leffler
@(#)Copyright:      (C) JLSS 2011
@(#)Product:        :PRODUCT:
*/

/*TABSTOP=4*/

#ifndef JLSS_ID_PRINTFMT_H
#define JLSS_ID_PRINTFMT_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stddef.h>

#ifdef MAIN_PROGRAM
#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
extern const char jlss_id_printfmt_h[];
const char jlss_id_printfmt_h[] = "@(#)$Id: printfmt.h,v 2.1 2011/07/18 16:30:54 jleffler Exp $";
#endif /* lint */
#endif /* MAIN_PROGRAM */

typedef enum PFP_Errno
{
    PFE_NoError,            /* No error */
    PFE_InvalidFormat,      /* Found % with no valid conversion specifier */
    PFE_NumberOverflow,     /* Number too big (max: 16,383) (should not be seen by users) */
    PFE_WidthOverflow,      /* Field width too big (max: 16,383)     */
    PFE_PrecOverflow,       /* Field precision too big (max: 16,383) */
    PFE_InvalidModifier,    /* Invalid modifier characters */
    PFE_MissingNDollar,     /* One n$ was present and others were missing */
    PFE_RepeatedFlag,       /* One of the flags was repeated */
    PFE_DollarOverflow,     /* Value in n$ too big (max: 16,383) */
    PFE_InvalidError,       /* Error number is not recognized */
    PFE_BufferTooSmall,     /* Buffer too small */
    PFE_BogusWidth,         /* Faulty width specification */
    PFE_BogusPrecision,     /* Faulty precision specification */
} PFP_Errno;

enum
{
    FWP_None   = -1,
    FWP_Star   = -2,
};

typedef enum PFP_Status
{
    PFP_Found  = +1,
    PFP_Error  = -1,
    PFP_Finish =  0,
} PFP_Status;

typedef struct PrintFormat
{
    const char *start;          /* Pointer to % symbol */
    const char *end;            /* Pointer to conversion specifier */
    PFP_Errno   error;          /* Conversion error number */
    short       width;          /* Field width (FPW_None for none, FPW_Star for *) */
    short       precision;      /* Field precision (FPW_None for none, FPW_Star for *) */
    short       conv_num;       /* n of %n$ (0 for none) */
    short       width_num;      /* n of *n$ for width (0 for none) */
    short       prec_num;       /* n of *n$ for precision (0 for none) */
    char        flags[6];       /* [+-0# ] */
    char        modifier[3];    /* hh|h|l|ll|j|z|t|L */
    char        convspec;       /* [diouxXfFeEgGAascp] */
} PrintFormat;

/*
** print_format_parse() - isolate and parse next printf() conversion specification
**
**  PrintFormat pf;
**  PFP_Status rc;
**  const char *format = "...%3$+-*2$.*1$llX...";
**  const char *start = format;
**  while ((rc = print_format_parse(start, &pf)) == PFP_Found)
**  {
**      ...use filled in pf to identify format...
**      start = pf.end + 1;
**  }
**  if (rc == PFP_Error)
**      ...report error, possibly using print_format_error(pf.error)...
*/
extern PFP_Status  print_format_parse(const char *src, PrintFormat *pf);
extern const char *print_format_error(PFP_Errno err);
extern PFP_Status  print_format_create(PrintFormat *pf, char *buffer, size_t buflen);

#ifdef __cplusplus
}
#endif

#endif /* JLSS_ID_PRINTFMT_H */
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0

Well, the GNU libc implementation of printf-parse is open source. You'll have to make sure the licensing fits with your project.

Another option would be to use a foreign function interface library to make the function call with a dynamic number of arguments. avcall should allow this and it could probably be done with libffi as well.

Judge Maygarden
  • 26,961
  • 9
  • 82
  • 99
  • avcall sounds interesting, never knew about it before. Meanwhile I decided to just go with a simple strstr as someone else suggested above, after going through the format specifier details, looks like %% is the only "special case" to be handled, so that makes strstr sufficient. – Gopa Feb 10 '12 at 18:16