unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Michael Welsh Duggan <mwd@cert.org>
To: Alan Mackenzie <acm@muc.de>
Cc: "11749@debbugs.gnu.org" <11749@debbugs.gnu.org>,
	Kim Storm <storm@cua.dk>
Subject: bug#11749: Acknowledgement (24.1; C-mode indentation gives wrong-type-argument error.)
Date: Mon, 26 Nov 2012 08:25:33 -0500	[thread overview]
Message-ID: <tnty5ho4hte.fsf@waterbuck.yellow.cert.org> (raw)
In-Reply-To: <20121121213340.GB4025@acm.acm> (Alan Mackenzie's message of "Wed, 21 Nov 2012 16:33:40 -0500")

[-- Attachment #1: Type: text/plain, Size: 1957 bytes --]

Alan Mackenzie <acm@muc.de> writes:

> Hi, Michael.
>
> On Wed, Nov 14, 2012 at 11:52:41AM -0500, Michael Welsh Duggan wrote:
>
>> ... Today I started working in C again, and ran into issues like this:
>
>> Error during redisplay: (jit-lock-function 3411) signaled
>> (wrong-type-argument number-or-marker-p nil)
>> c-parse-state inconsistency at 3599: using cache: nil, from scratch:
>> ((3409 . 3599))
>> Old state:
>> (setq c-state-cache '((3409 . 3599)) c-state-cache-good-pos 3599
>> c-state-nonlit-pos-cache '(6307 3307) c-state-nonlit-pos-cache-limit
>> 3592 c-state-semi-nonlit-pos-cache '(3307)
>> c-state-semi-nonlit-pos-cache-limit 3592 c-state-brace-pair-desert
>> (nil . 3396) c-state-point-min 1 c-state-point-min-lit-type nil
>> c-state-point-min-lit-start nil c-state-min-scan-pos 1
>> c-state-old-cpp-beg nil c-state-old-cpp-end nil c-parse-state-point
>> 3600)
>
>> I have finally managed to come up with a recipe for this one.  I am
>> using "110803 rgm@gnu.org-20121105111732-ilq2sbfo09xg9i9z" with your
>> patches.
>
>> emacs -Q rwfileinfo.c
>> M-x c-toggle-parse-state-debug
>> C-x 4 b *Messages* RET
>
> I assumed a C-x o here.  ;-)
>
>> C-s enum RET
>> C-M-f C-p C-e ,
>
>> Error occurs after the comma (should be on line 197).
>
> I find myself on L96.  I can't reproduce this error, neither on a tty or
> in X windows.  I tried repeating the C-M-f, so that the C-p C-e brought
> point to just after "RWINFO_PROPERTY_COUNT" and typing a comma there.
> That still didn't signal an error.

I'm sorry.  I realized that I attached the wrong file.  And the line
number should have been 97, not 197.  Unfortunately, I cannot reliably
trigger the problem using the above recipe using a recent Emacs trunk
check-out.  Even more unfortunately, I have still encountered the
problem (but have been unable to recreate it).  All in all, though, I
think your changes have been beneficial.  I'll try to isolate an example
over the next few days.


[-- Attachment #2: rwsiteinfo.c --]
[-- Type: text/plain, Size: 61958 bytes --]

/*
** Copyright (C) 2011-2012 by Carnegie Mellon University.
**
** @OPENSOURCE_HEADER_START@
**
** Use of the SILK system and related source code is subject to the terms
** of the following licenses:
**
** GNU Public License (GPL) Rights pursuant to Version 2, June 1991
** Government Purpose License Rights (GPLR) pursuant to DFARS 252.227.7013
**
** NO WARRANTY
**
** ANY INFORMATION, MATERIALS, SERVICES, INTELLECTUAL PROPERTY OR OTHER
** PROPERTY OR RIGHTS GRANTED OR PROVIDED BY CARNEGIE MELLON UNIVERSITY
** PURSUANT TO THIS LICENSE (HEREINAFTER THE "DELIVERABLES") ARE ON AN
** "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY
** KIND, EITHER EXPRESS OR IMPLIED AS TO ANY MATTER INCLUDING, BUT NOT
** LIMITED TO, WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE,
** MERCHANTABILITY, INFORMATIONAL CONTENT, NONINFRINGEMENT, OR ERROR-FREE
** OPERATION. CARNEGIE MELLON UNIVERSITY SHALL NOT BE LIABLE FOR INDIRECT,
** SPECIAL OR CONSEQUENTIAL DAMAGES, SUCH AS LOSS OF PROFITS OR INABILITY
** TO USE SAID INTELLECTUAL PROPERTY, UNDER THIS LICENSE, REGARDLESS OF
** WHETHER SUCH PARTY WAS AWARE OF THE POSSIBILITY OF SUCH DAMAGES.
** LICENSEE AGREES THAT IT WILL NOT MAKE ANY WARRANTY ON BEHALF OF
** CARNEGIE MELLON UNIVERSITY, EXPRESS OR IMPLIED, TO ANY PERSON
** CONCERNING THE APPLICATION OF OR THE RESULTS TO BE OBTAINED WITH THE
** DELIVERABLES UNDER THIS LICENSE.
**
** Licensee hereby agrees to defend, indemnify, and hold harmless Carnegie
** Mellon University, its trustees, officers, employees, and agents from
** all claims or demands made against them (and any related losses,
** expenses, or attorney's fees) arising out of, or relating to Licensee's
** and/or its sub licensees' negligent use or willful misuse of or
** negligent conduct or willful misconduct regarding the Software,
** facilities, or other rights or assistance granted by Carnegie Mellon
** University under this License, including, but not limited to, any
** claims of product liability, personal injury, death, damage to
** property, or violation of any laws or regulations.
**
** Carnegie Mellon University Software Engineering Institute authored
** documents are sponsored by the U.S. Department of Defense under
** Contract FA8721-05-C-0003. Carnegie Mellon University retains
** copyrights in all material produced under this contract. The U.S.
** Government retains a non-exclusive, royalty-free license to publish or
** reproduce these documents, or allow others to do so, for U.S.
** Government purposes only pursuant to the copyright license under the
** contract clause at 252.227.7013.
**
** @OPENSOURCE_HEADER_END@
*/

/*
**  Prints information about SiLK site configurations
**
**  Michael Duggan
**  September 2011
*/


#include <silk/silk.h>

RCSIDENT("$Id$");

#include <silk/skstream.h>
#include <silk/skstringmap.h>
#include <silk/sksite.h>
#include <silk/utils.h>


/* LOCAL DEFINES AND TYPEDEFS */

/* where to write --help output */
#define USAGE_FH stdout

/*
 *  Printer function typedef.
 *
 *    rwsiteinfo "prints" the output twice.  In the first pass, it
 *    uses a print function that does not produce output but instead
 *    determines the sizes of what it would print.  In the second
 *    pass, it uses an actual print function (fprintf) to produce the
 *    output.
 */
typedef int (*rws_fprintf_t)(FILE *f, const char *fmt, ...);


/* Sub-iterator types */
typedef enum {
    RWS_NULL,
    RWS_FLOWTYPE,
    RWS_CLASS,
    RWS_SENSOR,
    RWS_CLASS_FROM_SENSOR,
    RWS_SENSOR_FROM_CLASS,
    RWS_FLOWTYPE_FROM_CLASS,
    RWS_DEFAULT_FLOWTYPE_FROM_CLASS
} rws_iter_type_t;

/* Site iterator */
typedef struct rws_iter_st {
    /* The iterators */
    flowtype_iter_t flowtype_iter;
    class_iter_t    class_iter;
    sensor_iter_t   sensor_iter;

    /* The values */
    flowtypeID_t    flowtype_id;
    classID_t       class_id;
    sensorID_t      sensor_id;

    /* Order and type of iterators */
    rws_iter_type_t order[3];
    /* Number of iterators  */
    int             level;
    /* Highest bound iterator */
    int             bound;
    /* Highest started iterator */
    int             started;

    /* Whether RWS_DEFAULT_FLOWTYPE_FROM_CLASS is one of the
     * iterators */
    unsigned        default_type : 1;
} rws_iter_t;


/* LOCAL VARIABLE DEFINITIONS */

/* Masks for flowtypes, classes, and sensors as set by the --classes,
 * --types, --flowtypes, and --sensors switches.  When the bitmap is
 * NULL, all values are printed; when the bitmap is not NULL, only
 * values where the bit is high are printed. */
static sk_bitmap_t *flowtype_mask = NULL;
static sk_bitmap_t *class_mask   = NULL;
static sk_bitmap_t *sensor_mask   = NULL;

/* paging program */
static const char *pager;

/* the output stream, set by --output-path or --pager */
static sk_fileptr_t output;

/* raw filter arguments */
static char *classes_arg   = NULL;
static char *types_arg     = NULL;
static char *flowtypes_arg = NULL;
static char *sensors_arg   = NULL;
static char *fields_arg    = NULL;

/* delimiters */
static char column_separator = '|';
static char list_separator   = ',';

/* how to format output */
static int no_columns         = 0;
static int no_final_delimiter = 0;
static int no_titles          = 0;

/* final delimiter */
static char final_delim[] = {'\0', '\0'};

/* Field types */
typedef enum {
    RWST_CLASS,
    RWST_TYPE,
    RWST_FLOWTYPE,
    RWST_FLOWTYPE_ID,
    RWST_SENSOR,
    RWST_SENSOR_ID,
    RWST_SENSOR_DESC,
    RWST_DEFAULT_CLASS,
    RWST_DEFAULT_TYPE,
    RWST_MARK_DEFAULTS,
    RWST_CLASS_LIST,
    RWST_TYPE_LIST,
    RWST_FLOWTYPE_LIST,
    RWST_FLOWTYPE_ID_LIST,
    RWST_SENSOR_LIST,
    RWST_SENSOR_ID_LIST,
    RWST_DEFAULT_CLASS_LIST,
    RWST_DEFAULT_TYPE_LIST,
    /* Number of field types */
    RWST_MAX_FIELD_COUNT
} rws_field_t;

/* Field names, types, descriptions, and titles.  MUST be in the same
 * order as the rws_field_t enumeration. */
static const sk_stringmap_entry_t field_map_entries[] = {
    {"class",              RWST_CLASS,
     "class name",                             "Class"},
    {"type",               RWST_TYPE,
     "type name",                              "Type"},
    {"flowtype",           RWST_FLOWTYPE,
     "flowtype name",                          "Flowtype"},
    {"id-flowtype",        RWST_FLOWTYPE_ID,
     "flowtype integer identifier",            "Flowtype-ID"},
    {"sensor",             RWST_SENSOR,
     "sensor name",                            "Sensor"},
    {"id-sensor",          RWST_SENSOR_ID,
     "sensor integer identifier",              "Sensor-ID"},
    {"describe-sensor",    RWST_SENSOR_DESC,
     "sensor description",                     "Sensor-Description"},
    {"default-class",      RWST_DEFAULT_CLASS,
     "default class name",                     "Default-Class"},
    {"default-type",       RWST_DEFAULT_TYPE,
     "default type name",                      "Default-Type"},
    {"mark-defaults",      RWST_MARK_DEFAULTS,
     "'+' for default classes, '*' for types", "Defaults"},
    {"class:list",         RWST_CLASS_LIST,
     "list of class names",                    "Class:list"},
    {"type:list",          RWST_TYPE_LIST,
     "list of type names",                     "Type:list"},
    {"flowtype:list",      RWST_FLOWTYPE_LIST,
     "list of flowtype names",                 "Flowtype:list"},
    {"id-flowtype:list",   RWST_FLOWTYPE_ID_LIST,
     "list of flowtype integer identifier",    "Flowtype-ID:list"},
    {"sensor:list",        RWST_SENSOR_LIST,
     "list of sensor names",                   "Sensor:list"},
    {"id-sensor:list",     RWST_SENSOR_ID_LIST,
     "list of sensor integer identifiers",     "Sensor-ID:list"},
    {"default-class:list", RWST_DEFAULT_CLASS_LIST,
     "list of default class names",            "Default-Class:list"},
    {"default-type:list",  RWST_DEFAULT_TYPE_LIST,
     "list of default type names",             "Default-Type:list"},
    SK_STRINGMAP_SENTINEL
};

/* Fields to print, in the order in which to print them */
static rws_field_t fields[RWST_MAX_FIELD_COUNT];

/* Number of fields in list */
static size_t num_fields = 0;

/* Width of the columns, where index is an rws_field_t. */
static int col_width[RWST_MAX_FIELD_COUNT];


/* OPTIONS SETUP */

typedef enum {
    OPT_FIELDS, OPT_CLASSES, OPT_TYPES, OPT_FLOWTYPES,
    OPT_SENSORS, OPT_NO_TITLES, OPT_NO_COLUMNS, OPT_COLUMN_SEPARATOR,
    OPT_NO_FINAL_DELIMITER, OPT_DELIMITED, OPT_LIST_DELIMETER,
    OPT_PAGER, OPT_DATA_ROOTDIR
} appOptionsEnum;

static struct option appOptions[] = {
    {"fields",              REQUIRED_ARG, 0, OPT_FIELDS},
    {"classes",             REQUIRED_ARG, 0, OPT_CLASSES},
    {"types",               REQUIRED_ARG, 0, OPT_TYPES},
    {"flowtypes",           REQUIRED_ARG, 0, OPT_FLOWTYPES},
    {"sensors",             REQUIRED_ARG, 0, OPT_SENSORS},
    {"no-titles",           NO_ARG,       0, OPT_NO_TITLES},
    {"no-columns",          NO_ARG,       0, OPT_NO_COLUMNS},
    {"column-separator",    REQUIRED_ARG, 0, OPT_COLUMN_SEPARATOR},
    {"no-final-delimiter",  NO_ARG,       0, OPT_NO_FINAL_DELIMITER},
    {"delimited",           OPTIONAL_ARG, 0, OPT_DELIMITED},
    {"list-delimiter",      REQUIRED_ARG, 0, OPT_LIST_DELIMETER},
    {"pager",               REQUIRED_ARG, 0, OPT_PAGER},
    {"data-rootdir",        REQUIRED_ARG, 0, OPT_DATA_ROOTDIR},
    {0,0,0,0}               /* sentinel entry */
};

static const char *appHelp[] = {
    ("Print the fields named in this comma-separated list. Choices:"),
    ("Restrict the output using classes named in this comma-\n"
     "\tseparated list. Use '@' to designate the default class.\n"
     "\tDef. Print data for all classes"),
    ("Restrict the output using the types named in this comma-\n"
     "\tseparated list. Use '@' to designate the default type(s) for a class.\n"
     "\tDef. Print data for all types"),
    ("Restrict the output using the class/type pairs named in\n"
     "\tthis comma-separated list. May use 'all' for class and/or type. This\n"
     "\tis an alternate way to specify class/type; switch may not be used\n"
     "\twith --class or --type. Def. Print data for all class/type pairs"),
    ("Restrict the output using the sensors named in this comma-\n"
     "\tseparated list. Sensors may be designated by name, ID (integer),\n"
     "\tand/or ranges of IDs. Def. Print data for all sensors"),
    ("Do not print column headers. Def. Print titles"),
    ("Disable fixed-width columnar output. Def. Columnar"),
    ("Use specified character between columns. Def. '|'"),
    ("Suppress column delimiter at end of line. Def. No"),
    ("Shortcut for --no-columns --no-final-del --column-sep=CHAR"),
    ("Use specified character between items in FIELD:list\n"
     "\tfields. Def. ','"),
    ("Program to invoke to page output. Def. $SILK_PAGER or $PAGER"),
    ("Root of directory tree containing packed data."),
    (char *)NULL
};



/* LOCAL FUNCTION PROTOTYPES */

static int  appOptionsHandler(clientData cData, int opt_index, char *opt_arg);
static sk_stringmap_t *createStringmap(void);
static int rws_parse_fields(void);
static int rws_parse_restrictions(void);
static int rws_print_list_field(
    rws_fprintf_t  printer,
    FILE          *fd,
    rws_iter_t    *iter,
    rws_field_t    field,
    int            width);
static int fprintf_size(FILE *stream, const char *format, ...);


/* FUNCTION DEFINITIONS */

/*
 *  appUsageLong();
 *
 *    Print complete usage information to USAGE_FH.  Pass this
 *    function to skOptionsSetUsageCallback(); skOptionsParse() will
 *    call this funciton and then exit the program when the --help
 *    option is given.
 */
static void appUsageLong(void)
{
#define MIN_TEXT_ON_LINE  15
#define MAX_TEXT_ON_LINE  72
#define USAGE_MSG                                                            \
    ("--fields=<FIELDS> [SWITCHES]\n"                                        \
     "\tPrint selected information about the classes, types, flowtypes\n"    \
     "\tand sensors defined in the SiLK site configuration file.  By\n"      \
     "\tdefault, the selected information is printed for every class,\n"     \
     "\ttype, and sensor defined in the file; to restrict the output,\n"     \
     "\tspecify one or more of --classes, --types, --flowtypes, or\n"        \
     "\t--sensors.\n")

    FILE *fh = USAGE_FH;
    char *cp, *ep, *sp;
    char buf[2 * PATH_MAX];
    char path[PATH_MAX];
    int i;

    fprintf(fh, "%s %s", skAppName(), USAGE_MSG);
    fprintf(fh, "\nSWITCHES:\n");
    skOptionsDefaultUsage(fh);

    for (i = 0; appOptions[i].name; i++ ) {
        fprintf(fh, "--%s %s. ",
                appOptions[i].name, SK_OPTION_HAS_ARG(appOptions[i]));
        /* Print the static help text from the appHelp array */
        fprintf(fh, "%s\n", appHelp[i]);
        switch (appOptions[i].val) {
          case OPT_FIELDS:
            {
                sk_stringmap_t *map = createStringmap();
                if (map == NULL) {
                    skAppPrintErr("Error creating string map");
                    exit(EXIT_FAILURE);
                }
                skStringMapPrintUsage(map, fh, 8);
                skStringMapDestroy(map);
            }
            break;
          case OPT_DATA_ROOTDIR:
            /* put the text into a buffer, and then wrap the text in
             * the buffer at space characters. */
            snprintf(buf, sizeof(buf),
                     ("Currently '%s'. Def. $" SILK_DATA_ROOTDIR_ENVAR
                      " or '%s'"),
                     sksiteGetRootDir(path, sizeof(path)),
                     sksiteGetDefaultRootDir());
            sp = buf;
            while (strlen(sp) > MAX_TEXT_ON_LINE) {
                cp = &sp[MIN_TEXT_ON_LINE];
                while ((ep = strchr(cp+1, ' ')) != NULL) {
                    /* text is now too long */
                    if (ep - sp > MAX_TEXT_ON_LINE) {
                        if (cp == &sp[MIN_TEXT_ON_LINE]) {
                            /* this is the first space character we have
                             * on this line; so use it */
                            cp = ep;
                        }
                        break;
                    }
                    cp = ep;
                }
                if (cp == &sp[MIN_TEXT_ON_LINE]) {
                    /* no space characters anywhere on the line */
                    break;
                }
                assert(' ' == *cp);
                *cp = '\0';
                fprintf(fh, "\t%s\n", sp);
                sp = cp + 1;
            }
            if (*sp) {
                fprintf(fh, "\t%s\n", sp);
            }
            break;
          default:
            break;
        }
    }

    sksiteOptionsUsage(fh);
}


/*
 *  appTeardown()
 *
 *    Teardown all modules, close all files, and tidy up all
 *    application state.
 *
 *    This function is idempotent.
 */
static void appTeardown(void)
{
    static int teardownFlag = 0;

    if (teardownFlag) {
        return;
    }
    teardownFlag = 1;

    /* close the output file or process */
    if (output.of_name) {
        skFileptrClose(&output, &skAppPrintErr);
    }

    skBitmapDestroy(&flowtype_mask);
    skBitmapDestroy(&class_mask);
    skBitmapDestroy(&sensor_mask);

    skAppUnregister();
}


/*
 *  appSetup(argc, argv);
 *
 *    Perform all the setup for this application include setting up
 *    required modules, parsing options, etc.  This function should be
 *    passed the same arguments that were passed into main().
 *
 *    Returns to the caller if all setup succeeds.  If anything fails,
 *    this function will cause the application to exit with a FAILURE
 *    exit status.
 */
static void appSetup(int argc, char **argv)
{
    SILK_FEATURES_DEFINE_STRUCT(features);
    int arg_index;
    int rv;

    /* verify same number of options and help strings */
    assert((sizeof(appHelp)/sizeof(char *)) ==
           (sizeof(appOptions)/sizeof(struct option)));

    /* register the application */
    skAppRegister(argv[0]);
    skAppVerifyFeatures(&features, NULL);
    skOptionsSetUsageCallback(&appUsageLong);

    /* initialize globals */
    memset(&output, 0, sizeof(output));
    output.of_fp = stdout;

    /* register the options */
    if (skOptionsRegister(appOptions, &appOptionsHandler, NULL)
        || sksiteOptionsRegister(SK_SITE_FLAG_CONFIG_FILE))
    {
        skAppPrintErr("Unable to register options");
        exit(EXIT_FAILURE);
    }

    /* register the teardown handler */
    if (atexit(appTeardown) < 0) {
        skAppPrintErr("Unable to register appTeardown() with atexit()");
        appTeardown();
        exit(EXIT_FAILURE);
    }

    /* parse the options */
    arg_index = skOptionsParse(argc, argv);
    if (arg_index < 0) {
        skAppUsage();           /* never returns */
    }

    /* try to load site config file; if it fails, we will not be able
     * to resolve flowtype and sensor from input file names */
    if (sksiteConfigure(1)) {
        exit(EXIT_FAILURE);
    }

    /* parse fields */
    if (rws_parse_fields()) {
        exit(EXIT_FAILURE);
    }

    /* parse restrictions (--classes, --types, etc) */
    if (rws_parse_restrictions()) {
        exit(EXIT_FAILURE);
    }

    /* check for extraneous arguments */
    if (arg_index != argc) {
        skAppPrintErr("Too many arguments or unrecognized switch '%s'",
                      argv[arg_index]);
        skAppUsage();           /* never returns */
    }

    /* Initialize column widths */
    if (no_titles || no_columns) {
        memset(col_width, 0, sizeof(col_width));
    } else {
        size_t i;
        for (i = 0; i < RWST_MAX_FIELD_COUNT; i++) {
            col_width[i] = strlen((char *)field_map_entries[i].userdata);

            /* While looping through, verify that the
             * field_map_entries is in the same order as the
             * enumeration. */
            assert(i == field_map_entries[i].id);
        }
    }

    /* Set the final delimiter, if used */
    if (!no_final_delimiter) {
        final_delim[0] = column_separator;
    }

    rv = skFileptrOpenPager(&output, pager);
    if (rv && rv != SK_FILEPTR_PAGER_IGNORED) {
        skAppPrintErr("Unable to invoke pager");
    }

    return;  /* OK */
}


/*
 *  status = appOptionsHandler(cData, opt_index, opt_arg);
 *
 *    This function is passed to skOptionsRegister(); it will be called
 *    by skOptionsParse() for each user-specified switch that the
 *    application has registered; it should handle the switch as
 *    required---typically by setting global variables---and return 1
 *    if the switch processing failed or 0 if it succeeded.  Returning
 *    a non-zero from from the handler causes skOptionsParse() to return
 *    a negative value.
 *
 *    The clientData in 'cData' is typically ignored; 'opt_index' is
 *    the index number that was specified as the last value for each
 *    struct option in appOptions[]; 'opt_arg' is the user's argument
 *    to the switch for options that have a REQUIRED_ARG or an
 *    OPTIONAL_ARG.
 */
static int appOptionsHandler(
    clientData  UNUSED(cData),
    int         opt_index,
    char       *opt_arg)
{
    switch ((appOptionsEnum)opt_index) {
      case OPT_DATA_ROOTDIR:
        if (!skDirExists(opt_arg)) {
            skAppPrintErr("Root data directory '%s' does not exist", opt_arg);
            return -1;
        }
        if (sksiteSetRootDir(opt_arg)) {
            skAppPrintErr("Unable to set root data directory to %s", opt_arg);
            return -1;
        }
        break;

      case OPT_CLASSES:
        if (classes_arg != NULL) {
            skAppPrintErr("Invalid %s: Switch specified multiple times",
                          appOptions[opt_index].name);
            return -1;
        }
        classes_arg = opt_arg;
        break;

      case OPT_TYPES:
        if (types_arg != NULL) {
            skAppPrintErr("Invalid %s: Switch specified multiple times",
                          appOptions[opt_index].name);
            return -1;
        }
        types_arg = opt_arg;
        break;

      case OPT_FLOWTYPES:
        if (flowtypes_arg != NULL) {
            skAppPrintErr("Invalid %s: Switch specified multiple times",
                          appOptions[opt_index].name);
            return -1;
        }
        flowtypes_arg = opt_arg;
        break;

      case OPT_SENSORS:
        if (sensors_arg != NULL) {
            skAppPrintErr("Invalid %s: Switch specified multiple times",
                          appOptions[opt_index].name);
            return -1;
        }
        sensors_arg = opt_arg;
        break;

      case OPT_FIELDS:
        if (fields_arg != NULL) {
            skAppPrintErr("Invalid %s: Switch specified multiple times",
                          appOptions[opt_index].name);
            return -1;
        }
        fields_arg = opt_arg;
        break;

      case OPT_NO_TITLES:
        no_titles = 1;
        break;

      case OPT_NO_COLUMNS:
        no_columns = 1;
        break;

      case OPT_COLUMN_SEPARATOR:
        column_separator = opt_arg[0];
        break;

      case OPT_NO_FINAL_DELIMITER:
        no_final_delimiter = 1;
        break;

      case OPT_DELIMITED:
        no_columns = 1;
        no_final_delimiter = 1;
        if (opt_arg) {
            column_separator = opt_arg[0];
        }
        break;

      case OPT_LIST_DELIMETER:
        list_separator = opt_arg[0];
        break;

      case OPT_PAGER:
        pager = opt_arg;
        break;
    }

    return 0;  /* OK */
}


/*
 *  stringmap = createStringmap();
 *
 *    Create the string map that is used to parse the --fields
 *    paramater.
 */
static sk_stringmap_t *createStringmap(void)
{
    sk_stringmap_t *field_map;

    /* Create the map */
    if (SKSTRINGMAP_OK != skStringMapCreate(&field_map)) {
        return NULL;
    }

    /* add entries */
    if (skStringMapAddEntries(field_map, -1, field_map_entries)
        != SKSTRINGMAP_OK)
    {
        skStringMapDestroy(field_map);
        return NULL;
    }

    return field_map;
}


/*
 *  status = rws_parse_fields();
 *
 *     Parse the --fields argument.  Return 0 on success, -1 on
 *     failure.
 */
static int rws_parse_fields(void)
{
    sk_stringmap_t *field_map = NULL;
    sk_stringmap_iter_t *iter = NULL;
    sk_stringmap_entry_t *entry;
    char *errmsg;
    int rv = -1;

    if (fields_arg == NULL) {
        skAppPrintErr("The --%s switch is required",
                      appOptions[OPT_FIELDS].name);
        return rv;
    }
    field_map = createStringmap();
    if (NULL == field_map) {
        skAppPrintOutOfMemory(NULL);
        goto END;
    }

    /* parse the field-list */
    if (skStringMapParse(field_map, fields_arg, SKSTRINGMAP_DUPES_ERROR,
                         &iter, &errmsg))
    {
        skAppPrintErr("Invalid %s: %s",
                      appOptions[OPT_FIELDS].name, errmsg);
        goto END;
    }

    /* add the selected fields to the 'fields[]' array */
    while (skStringMapIterNext(iter, &entry, NULL) == SK_ITERATOR_OK) {
        assert(num_fields < RWST_MAX_FIELD_COUNT);
        fields[num_fields] = (rws_field_t)entry->id;
         ++num_fields;
    }

    rv = 0;

  END:
    if (iter != NULL) {
        skStringMapIterDestroy(iter);
    }
    if (field_map != NULL) {
        skStringMapDestroy(field_map);
    }
    return rv;
}


/*
 *  status = rws_parse_sensors(sn_bitmap);
 *
 *      Parse the --sensors argument from the global 'sensors_arg'.
 *      Set a bit in 'sn_bitmap' for each sensor we see.  Return 0 on
 *      success, or -1 if any invalid sensors are found.
 */
static int rws_parse_sensors(
    sk_bitmap_t    *sn_bitmap)
{
    char *user_arg = NULL;
    char *user_arg_freeable = NULL;
    char *sensor_token = NULL;
    uint32_t min_sensor_id;
    uint32_t max_sensor_id;
    uint32_t val_min, val_max;
    sensorID_t sid = SK_INVALID_SENSOR;
    int p_err;
    int rv = 0;

    /* nothing to do when no --sensors switch is given */
    if (NULL == sensors_arg) {
        return 0;
    }

    assert(sn_bitmap);
    assert(skBitmapGetSize(sn_bitamp) > sksiteSensorGetMaxID());

    min_sensor_id = sksiteSensorGetMinID();
    max_sensor_id = sksiteSensorGetMaxID();

    /* create a copy of the input string and maintain a reference to
     * it so we can free it */
    user_arg = strdup(sensors_arg);
    user_arg_freeable = user_arg;
    if (user_arg == NULL) {
        skAppPrintOutOfMemory(NULL);
        rv = -1;
        goto END;
    }

    /* parse the sensors as a comma separated list of tokens */
    while ((sensor_token = strsep(&user_arg, ",")) != NULL) {
        /* check for empty token (e.g., double comma) */
        if ('\0' == *sensor_token) {
            continue;
        }

        /* look up sensor_token as a sensor name */
        sid = sksiteSensorLookup(sensor_token);
        if (SK_INVALID_SENSOR != sid) {
            /* found it. add it to our lists and continue */
            skBitmapSetBit(sn_bitmap, sid);
            continue;
        }

        /* parsing failed. does the token look like a number? */
        if (!isdigit((int)(*sensor_token))) {
            /* not a number: error */
            skAppPrintErr("Invalid %s '%s': Unknown sensor name",
                          appOptions[OPT_SENSORS].name, sensor_token);
            rv = -1;
            continue;
        }

        /* it is a digit: parse the token as a single number or a
         * range */
        p_err = skStringParseRange32(&val_min, &val_max, sensor_token,
                                     min_sensor_id, max_sensor_id,
                                     SKUTILS_RANGE_NO_OPEN);
        if (p_err != 0) {
            /* an error */
            skAppPrintErr("Invalid %s '%s': %s",
                          appOptions[OPT_SENSORS].name, sensor_token,
                          skStringParseStrerror(p_err));
            rv = -1;
            continue;
        }

        /* verify that start of range is valid */
        sid = (sensorID_t)val_min;
        if (!sksiteSensorExists(sid)) {
            skAppPrintErr(("Invalid %s: Value %" PRIu32
                           " is not a valid sensor id"),
                          appOptions[OPT_SENSORS].name, val_min);
            rv = -1;
            continue;
        }

        /* verify that end of range is valid */
        if (val_min != val_max && !sksiteSensorExists((sensorID_t)val_max)) {
            skAppPrintErr(("Invalid %s: Value %" PRIu32
                           " is not a valid sensor id"),
                          appOptions[OPT_SENSORS].name, val_max);
            rv = -1;
            continue;
        }

        /* add all sensor IDs in range */
        skBitmapRangeSet(sn_bitmap, sid, val_max);
    }

    /* did we find any sensors? */
    if ((0 == rv) && (0 == skBitmapGetHighCount(sn_bitmap))) {
        skAppPrintErr("Invalid %s: No valid sensors",
                      appOptions[OPT_SENSORS].name);
        rv = -1;
    }

  END:
    if (user_arg_freeable) {
        free(user_arg_freeable);
    }
    return rv;
}


/*
 *  status = rws_parse_flowtypes(cl_bitmap, ft_bitmap);
 *
 *    Parse the --flowtypes argument from the global 'flowtypes_arg'.
 *    Set a bit on 'cl_bitmap' for each valid class and and a bit on
 *    'ft_bitmap' for each valid flowtype.  Return 0 on success, or -1
 *    on if any class/type value is not a valid pair.
 */
static int rws_parse_flowtypes(
    sk_bitmap_t    *cl_bitmap,
    sk_bitmap_t    *ft_bitmap)
{
    char *user_arg = NULL;
    char *user_arg_freeable = NULL;
    char *class_name;
    char *type_name;
    flowtype_iter_t ft_iter;
    flowtypeID_t ft;
    classID_t class_id;
    int rv = 0;

    /* nothing to do when no --flowtypes switch is given */
    if (NULL == flowtypes_arg) {
        return 0;
    }

    /* create a modifiable version of the user's input. store a
     * pointer to it we can free it */
    user_arg = strdup(flowtypes_arg);
    user_arg_freeable = user_arg;
    if (NULL == user_arg) {
        skAppPrintOutOfMemory(NULL);
        rv = -1;
        goto END;
    }

    /* parse user's string as a comma separated list */
    while ((class_name = strsep(&user_arg, ",")) != NULL) {
        if (class_name[0] == '\0') {
            /* empty token (e.g., double comma) */
            continue;
        }

        /* break token into class and type separated by '/' */
        type_name = strchr(class_name, '/');
        if (type_name == NULL) {
            skAppPrintErr("Invalid %s: Missing '/' in token '%s'",
                          appOptions[OPT_FLOWTYPES].name, class_name);
            rv = -1;
            continue;
        }
        *type_name = '\0';
        ++type_name;

        /* find class and type.  if lookup fails, test for special
         * "all" keyword */
        ft = sksiteFlowtypeLookupByClassType(class_name, type_name);
        if (SK_INVALID_FLOWTYPE != ft) {
            /* Yay!  Class and type are specific */
            skBitmapSetBit(ft_bitmap, ft);
            skBitmapSetBit(cl_bitmap, sksiteFlowtypeGetClassID(ft));

        } else if (0 == strcmp(class_name, "all")) {
            if (0 == strcmp(type_name, "all")) {
                /* Use all classes and all types. */
                sksiteFlowtypeIterator(&ft_iter);
                while (sksiteFlowtypeIteratorNext(&ft_iter, &ft)) {
                    skBitmapSetBit(ft_bitmap, ft);
                    skBitmapSetBit(cl_bitmap, sksiteFlowtypeGetClassID(ft));
                }
            } else {
                /* Loop over all classes and add flowtype if type_name
                 * is valid for that class.  Don't complain unless the
                 * type is not valid for any class. */
                class_iter_t ci;
                int found_type = 0;

                sksiteClassIterator(&ci);
                while (sksiteClassIteratorNext(&ci, &class_id)) {
                    ft = sksiteFlowtypeLookupByClassIDType(class_id,type_name);
                    if (SK_INVALID_FLOWTYPE != ft) {
                        ++found_type;
                        skBitmapSetBit(ft_bitmap, ft);
                        skBitmapSetBit(cl_bitmap, class_id);
                    }
                }
                if (!found_type) {
                    skAppPrintErr(("Invalid %s:"
                                   " Type '%s' not valid for any class"),
                                  appOptions[OPT_FLOWTYPES].name, type_name);
                    rv = -1;
                }
            }

        } else if (0 == strcmp(type_name, "all")) {
            /* Use all types in the specified class */
            class_id = sksiteClassLookup(class_name);
            if (SK_INVALID_CLASS == class_id) {
                skAppPrintErr("Invalid %s: Invalid class '%s'",
                              appOptions[OPT_FLOWTYPES].name, class_name);
                rv = -1;
            } else {
                skBitmapSetBit(cl_bitmap, class_id);
                sksiteClassFlowtypeIterator(class_id, &ft_iter);
                while (sksiteFlowtypeIteratorNext(&ft_iter, &ft)) {
                    skBitmapSetBit(ft_bitmap, ft);
                }
            }

        } else {
            /* Invalid class/type */
            skAppPrintErr("Invalid %s: Unknown class/type pair '%s/%s'",
                          appOptions[OPT_FLOWTYPES].name,
                          class_name, type_name);
            rv = -1;
        }
    }

    if ((rv == 0) && (skBitmapGetHighCount(ft_bitmap) == 0)) {
        skAppPrintErr("Invalid %s: No valid class/type pairs",
                      appOptions[OPT_FLOWTYPES].name);
        rv = -1;
    }

  END:
    if (user_arg_freeable) {
        free(user_arg_freeable);
    }
    return rv;
}


/*
 *  status = rws_parse_classes_and_types();
 *
 *    Parse the --classes and/or --types arguments from the globals
 *    'classes_arg' and 'types_arg'.  Set a bit on 'cl_bitmap' for
 *    each valid class and and a bit on 'ft_bitmap' for each valid
 *    flowtype.  Return 0 on success, or -1 on if any class/type value
 *    is not a valid pair.
 */
static int rws_parse_classes_and_types(
    sk_bitmap_t    *cl_bitmap,
    sk_bitmap_t    *ft_bitmap)
{
    sk_bitmap_iter_t bmap_iter;
    uint32_t bmap_val;
    class_iter_t ci;
    flowtype_iter_t ft_iter;
    char *user_arg = NULL;
    char *user_arg_freeable = NULL;
    char *class_token = NULL;
    char *type_token = NULL;
    classID_t class_id;
    flowtypeID_t ft;
    int found_type;
    int rv = 0;

    if (NULL == classes_arg) {
        /* temporarily enable all classes */
        sksiteClassIterator(&ci);
        while (sksiteClassIteratorNext(&ci, &class_id)) {
            skBitmapSetBit(cl_bitmap, class_id);
        }

    } else {
        /* create a copy of the input string and maintain a reference to
         * it so we can free it */
        user_arg = strdup(classes_arg);
        user_arg_freeable = user_arg;
        if (user_arg == NULL) {
            skAppPrintOutOfMemory(NULL);
            rv = -1;
            goto END;
        }

        /* parse the classes as a comma separated list of tokens */
        while ((class_token = strsep(&user_arg, ",")) != NULL) {
            /* check for empty token (e.g., double comma) */
            if ('\0' == *class_token) {
                continue;
            }

            /* Handle default class token */
            if (strcmp(class_token, "@") == 0) {
                skBitmapSetBit(cl_bitmap, sksiteClassGetDefault());
                continue;
            }

            /* look up class_token as a class name */
            class_id = sksiteClassLookup(class_token);
            if (SK_INVALID_CLASS != class_id) {
                /* found it. add it to our lists and continue */
                skBitmapSetBit(cl_bitmap, class_id);
                continue;
            }

            skAppPrintErr("Invalid %s '%s': Unknown class name",
                          appOptions[OPT_CLASSES].name, class_token);
            rv = -1;
            continue;
        }

        free(user_arg_freeable);
        user_arg_freeable = NULL;

        /* did we find any classes? */
        if ((0 == rv) && (0 == skBitmapGetHighCount(cl_bitmap))) {
            skAppPrintErr("Invalid %s: No valid classes",
                          appOptions[OPT_CLASSES].name);
            rv = -1;
        }

        if (NULL == types_arg) {
            if (rv != 0) {
                goto END;
            }
            /* there is no --types; enable all flowtypes that exist in
             * the specified class(es) */
            skBitmapIteratorBind(cl_bitmap, &bmap_iter);
            while (skBitmapIteratorNext(&bmap_iter, &bmap_val)==SK_ITERATOR_OK)
            {
                class_id = (classID_t)bmap_val;
                sksiteClassFlowtypeIterator(class_id, &ft_iter);
                while (sksiteFlowtypeIteratorNext(&ft_iter, &ft)) {
                    skBitmapSetBit(ft_bitmap, ft);
                }
            }
            goto END;
        }
    }

    /* create a modifiable version of the user's input. store a
     * pointer to it we can free */
    user_arg = strdup(types_arg);
    user_arg_freeable = user_arg;
    if (NULL == user_arg) {
        skAppPrintOutOfMemory(NULL);
        rv = -1;
        goto END;
    }

    /* parse user's string as a comma separated list */
    while ((type_token = strsep(&user_arg, ",")) != NULL) {
        found_type = 0;
        if (type_token[0] == '\0') {
            /* empty token (e.g., double comma) */
            continue;
        }

        /* check for the type in all the classes we found above */
        skBitmapIteratorBind(cl_bitmap, &bmap_iter);
        while (skBitmapIteratorNext(&bmap_iter, &bmap_val)==SK_ITERATOR_OK) {
            class_id = (classID_t)bmap_val;

            /* find class and type */
            ft = sksiteFlowtypeLookupByClassIDType(class_id, type_token);
            if (SK_INVALID_FLOWTYPE != ft) {
                /* Yay!  Class and type are specific */
                skBitmapSetBit(ft_bitmap, ft);
                ++found_type;

            } else if (0 == strcmp(type_token, "all")) {
                /* Use all types in the specified class */
                sksiteClassFlowtypeIterator(class_id, &ft_iter);
                while (sksiteFlowtypeIteratorNext(&ft_iter, &ft)) {
                    ++found_type;
                    skBitmapSetBit(ft_bitmap, ft);
                }

            } else if (0 == strcmp(type_token, "@")) {
                sksiteClassDefaultFlowtypeIterator(class_id, &ft_iter);
                while (sksiteFlowtypeIteratorNext(&ft_iter, &ft)) {
                    ++found_type;
                    skBitmapSetBit(ft_bitmap, ft);
                }
            }
        }

        if (!found_type) {
            skAppPrintErr("Invalid %s: Type '%s' not valid for %s",
                          appOptions[OPT_TYPES].name, type_token,
                          (classes_arg ? "specified classes" : "any class"));
            rv = -1;
        }
    }

    if ((rv == 0) && (skBitmapGetHighCount(ft_bitmap) == 0)) {
        skAppPrintErr("Invalid --%s: No valid types",
                      appOptions[OPT_TYPES].name);
        rv = -1;
    }

    if ((rv == 0) && (NULL == classes_arg)) {
        /* no --classes were specified.  reset the cl_bitmap based on
         * the flowtypes we found */
        skBitmapClearAllBits(cl_bitmap);

        skBitmapIteratorBind(ft_bitmap, &bmap_iter);
        while (skBitmapIteratorNext(&bmap_iter, &bmap_val)==SK_ITERATOR_OK) {
            ft = (flowtypeID_t)bmap_val;
            skBitmapSetBit(cl_bitmap, sksiteFlowtypeGetClassID(ft));
        }
    }

  END:
    if (user_arg_freeable) {
        free(user_arg_freeable);
    }
    return rv;
}


/*
 *  status = rws_parse_restrictions();
 *
 *    Parse the --classes, --types, --flowtypes, and --sensors options
 *    and create/fill bitmaps that restrict the output.
 *
 *    Returns 0 on success, -1 on error.
 */
static int rws_parse_restrictions(void)
{
    sk_bitmap_iter_t bmap_iter;
    uint32_t bmap_val;
    sk_bitmap_t *cl_mask = NULL;
    sk_bitmap_t *ft_mask = NULL;
    sk_bitmap_t *sn_mask = NULL;
    class_iter_t class_iter;
    flowtype_iter_t ft_iter;
    sensor_iter_t sensor_iter;
    classID_t class_id;
    sensorID_t sensor_id;
    flowtypeID_t flowtype_id;
    int sensors_only = 0;
    int rv = 0;

    if (!classes_arg && !types_arg && !flowtypes_arg) {
        if (!sensors_arg) {
            /* nothing to do */
            return 0;
        }
        /* else, only need to process --sensors */
        sensors_only = 1;
    }

    /* create the global bitmaps for all classes, all flowtypes, and
     * all sensors */
    if (skBitmapCreate(&class_mask, 1 + sksiteClassGetMaxID())) {
        skAppPrintOutOfMemory("class bitmap");
        return -1;
    }
    if (skBitmapCreate(&flowtype_mask, 1 + sksiteFlowtypeGetMaxID())) {
        skAppPrintOutOfMemory("flowtype bitmap");
        return -1;
    }
    if (skBitmapCreate(&sensor_mask, 1 + sksiteSensorGetMaxID())) {
        skAppPrintOutOfMemory("sensor bitmap");
        return -1;
    }

    if (!sensors_only && sensors_arg) {
        /* need to create temporary bitmaps */
        if (skBitmapCreate(&cl_mask, 1 + sksiteClassGetMaxID())) {
            skAppPrintOutOfMemory("class bitmap");
            rv = -1;
            goto END;
        }
        if (skBitmapCreate(&ft_mask, 1 + sksiteFlowtypeGetMaxID())) {
            skAppPrintOutOfMemory("flowtype bitmap");
            rv = -1;
            goto END;
        }
        if (skBitmapCreate(&sn_mask, 1 + sksiteSensorGetMaxID())) {
            skAppPrintOutOfMemory("sensor bitmap");
            rv = -1;
            goto END;
        }
    } else {
        /* else we can point the temporaries at the real thing */
        cl_mask = class_mask;
        ft_mask = flowtype_mask;
        sn_mask = sensor_mask;
    }

    if (sensors_arg) {
        rv = rws_parse_sensors(sensor_mask);

        if (sensors_only && rv != 0) {
            goto END;
        }

        /* set class_mask and flowtype_mask based on the sensors we saw */
        skBitmapIteratorBind(sensor_mask, &bmap_iter);
        while (skBitmapIteratorNext(&bmap_iter, &bmap_val)==SK_ITERATOR_OK) {
            sensor_id = (sensorID_t)bmap_val;
            sksiteSensorClassIterator(sensor_id, &class_iter);
            while (sksiteClassIteratorNext(&class_iter, &class_id)) {
                skBitmapSetBit(cl_mask, class_id);
                sksiteClassFlowtypeIterator(class_id, &ft_iter);
                while (sksiteFlowtypeIteratorNext(&ft_iter, &flowtype_id)) {
                    skBitmapSetBit(ft_mask, flowtype_id);
                }
            }
        }

        if (sensors_only) {
            return 0;
        }
    }

    /* handle case when --flowtypes is given */
    if (flowtypes_arg) {
        if (classes_arg || types_arg) {
            skAppPrintErr(("Cannot use --%s when either --%s or --%s is"
                           " specified"),
                          appOptions[OPT_FLOWTYPES].name,
                          appOptions[OPT_CLASSES].name,
                          appOptions[OPT_TYPES].name);
            goto END;
        }
        rv |= rws_parse_flowtypes(class_mask, flowtype_mask);
        if (rv != 0) {
            goto END;
        }
    } else {
        assert(classes_arg || types_arg);
        rv |= rws_parse_classes_and_types(class_mask, flowtype_mask);
        if (rv != 0) {
            goto END;
        }
    }

    /* set sensor_mask based on the classes we saw */
    skBitmapIteratorBind(class_mask, &bmap_iter);
    while (skBitmapIteratorNext(&bmap_iter, &bmap_val)==SK_ITERATOR_OK) {
        class_id = (classID_t)bmap_val;
        sksiteClassSensorIterator(class_id, &sensor_iter);
        while (sksiteSensorIteratorNext(&sensor_iter, &sensor_id)) {
            skBitmapSetBit(sn_mask, sensor_id);
        }
    }

    /* perform the intersection of the masks with the temporaries */
    if (sn_mask && sn_mask != sensor_mask) {
        skBitmapIntersection(sensor_mask, sn_mask);
        skBitmapDestroy(&sn_mask);
    }
    if (cl_mask && cl_mask != class_mask) {
        skBitmapIntersection(class_mask, cl_mask);
        skBitmapDestroy(&cl_mask);
    }
    if (ft_mask && ft_mask != flowtype_mask) {
        skBitmapIntersection(flowtype_mask, ft_mask);
        skBitmapDestroy(&ft_mask);
    }

  END:
    if (sn_mask && sn_mask != sensor_mask) {
        skBitmapDestroy(&sn_mask);
    }
    if (cl_mask && cl_mask != class_mask) {
        skBitmapDestroy(&cl_mask);
    }
    if (ft_mask && ft_mask != flowtype_mask) {
        skBitmapDestroy(&ft_mask);
    }
    return rv;
}


/*
 *  rws_iter_bind(&iter, level);
 *
 *    Bind the sub-iterator of a site iterator at the given level.
 */
static void rws_iter_bind(
    rws_iter_t *iter,
    int         level)
{
    assert(iter);
    assert(iter->level >= level);

    /* Negative level is for a non-iterable iterator.  This is used
     * for options that require no iteration, like
     * --fields=class-default. */
    if (level >= 0) {
        switch (iter->order[level]) {
          case RWS_FLOWTYPE:
            sksiteFlowtypeIterator(&iter->flowtype_iter);
            break;
          case RWS_CLASS:
            sksiteClassIterator(&iter->class_iter);
            break;
          case RWS_SENSOR:
            sksiteSensorIterator(&iter->sensor_iter);
            break;
          case RWS_FLOWTYPE_FROM_CLASS:
            sksiteClassFlowtypeIterator(iter->class_id, &iter->flowtype_iter);
            break;
          case RWS_CLASS_FROM_SENSOR:
            sksiteSensorClassIterator(iter->sensor_id, &iter->class_iter);
            break;
          case RWS_SENSOR_FROM_CLASS:
            sksiteClassSensorIterator(iter->class_id, &iter->sensor_iter);
            break;
          case RWS_DEFAULT_FLOWTYPE_FROM_CLASS:
            sksiteClassDefaultFlowtypeIterator(
                iter->class_id, &iter->flowtype_iter);
            iter->default_type = 1;
            break;
          case RWS_NULL:
            skAbortBadCase(iter->order[level]);
            break;
        }
    }

    /* We are now bound at this level */
    iter->bound = level;

    /* But mark as not having stared iteration yet */
    iter->started = level - 1;
}


/*
 *  status = rws_iter_next(iter, level);
 *
 *    Site iterator iteration, at a particular sub-iterator level.
 *    Returns 1 on success, 0 if the iteration is complete at the
 *    given level.
 */
static int rws_iter_next(
    rws_iter_t *iter,
    int         level)
{
    int rv;

    assert(iter);
    assert(iter->level >= level);

    /* Prevent re-iteration after already having completed
     * iteration */
    if (iter->bound < level) {
        assert(level == 0);
        return 0;
    }

    do {
        /* Iterate at the next level if we've already started
         * iteration on this level */
        if (iter->started >= level && level < iter->level) {
            if (iter->bound == level) {
                rws_iter_bind(iter, level + 1);
            }
            rv = rws_iter_next(iter, level + 1);
            if (rv != 0) {
                /* Leaf iter succeeded */
                return 1;
            }
        }

        /* Iterate at the current level */
        switch (iter->order[level]) {
          case RWS_FLOWTYPE:
          case RWS_FLOWTYPE_FROM_CLASS:
          case RWS_DEFAULT_FLOWTYPE_FROM_CLASS:
            /* Flowtype iteration */
            while ((rv = sksiteFlowtypeIteratorNext(&iter->flowtype_iter,
                                                    &iter->flowtype_id))
                   && flowtype_mask != NULL
                   && !skBitmapGetBit(flowtype_mask, iter->flowtype_id))
                ; /* empty */
            /* Set class from flowtype */
            if (rv) {
                iter->class_id = sksiteFlowtypeGetClassID(iter->flowtype_id);
            }
            break;

          case RWS_CLASS:
          case RWS_CLASS_FROM_SENSOR:
            /* Class iteration */
            while ((rv = sksiteClassIteratorNext(&iter->class_iter,
                                                 &iter->class_id))
                   && class_mask != NULL
                   && !skBitmapGetBit(class_mask, iter->class_id))
                ; /* empty */
            break;

          case RWS_SENSOR:
          case RWS_SENSOR_FROM_CLASS:
            /* Sensor iteration */
            while ((rv = sksiteSensorIteratorNext(&iter->sensor_iter,
                                                  &iter->sensor_id))
                   && sensor_mask != NULL
                   && !skBitmapGetBit(sensor_mask, iter->sensor_id))
                ; /* empty */
            break;

          default:
            skAbortBadCase(iter->order[level]);
        }

        /* Mark that we've started iterating at the current level */
        if (iter->started < level) {
            iter->started = level;
        }

        if (rv && level == iter->level) {
            /* return success at leaf */
            return 1;
        }

        /* Iterate until a leaf iteration succeeds, or we fail to
         * iterate at this level. */
    } while (rv);

    /* Iteration is over.  We are no longer bound at this level.  */
    iter->bound = level - 1;

    return 0;
}


/*
 *  len = rws_print_field(printer, fd, iter, field, width);
 *
 *    Print a 'width'-wide column containing the value for 'field'
 *    using the fprintf-style function 'printer' to the FILE pointer
 *    'fd', where 'iter' is the current context.
 *
 *    Return the number of characters printed.
 */
static int rws_print_field(
    rws_fprintf_t  printer,
    FILE          *fd,
    rws_iter_t    *iter,
    rws_field_t    field,
    int            width)
{
    /* buffer large enough to hold a single sensor name or flowtype
     * name */
    char buf[SK_MAX_STRLEN_SENSOR + SK_MAX_STRLEN_FLOWTYPE];
    const char *s;
    int rv = 0;

    switch (field) {
      case RWST_CLASS:
        if (iter->class_id != SK_INVALID_CLASS) {
            sksiteClassGetName(buf, sizeof(buf), iter->class_id);
            rv = printer(fd, "%*s", width, buf);
        }
        break;

      case RWST_DEFAULT_TYPE:
        if (!iter->default_type) {
            /* When there's no default_type iterator, we can't do a
             * default_type */
            break;
        }
        /* Fall through */
      case RWST_TYPE:
        if (iter->flowtype_id != SK_INVALID_FLOWTYPE) {
            sksiteFlowtypeGetType(buf, sizeof(buf), iter->flowtype_id);
            rv = printer(fd, "%*s", width, buf);
        }
        break;

      case RWST_FLOWTYPE:
        if (iter->flowtype_id != SK_INVALID_FLOWTYPE) {
            sksiteFlowtypeGetName(buf, sizeof(buf), iter->flowtype_id);
            rv = printer(fd, "%*s", width, buf);
        }
        break;

      case RWST_FLOWTYPE_ID:
        if (iter->flowtype_id != SK_INVALID_FLOWTYPE) {
            rv = printer(fd, "%*" PRIu8, width, iter->flowtype_id);
        }
        break;

      case RWST_SENSOR:
        if (iter->sensor_id != SK_INVALID_SENSOR) {
            sksiteSensorGetName(buf, sizeof(buf), iter->sensor_id);
            rv = printer(fd, "%*s", width, buf);
        }
        break;

      case RWST_SENSOR_ID:
        if (iter->sensor_id != SK_INVALID_SENSOR) {
            rv = printer(fd, "%*" PRIu16, width, iter->sensor_id);
        }
        break;

      case RWST_SENSOR_DESC:
        if (iter->sensor_id != SK_INVALID_SENSOR) {
            s = sksiteSensorGetDescription(iter->sensor_id);
            if (s != NULL) {
                rv = printer(fd, "%*s", width, s);
            }
        }
        break;

      case RWST_DEFAULT_CLASS:
      case RWST_DEFAULT_CLASS_LIST:
        {
            classID_t cid = sksiteClassGetDefault();
            if (cid != SK_INVALID_CLASS) {
                sksiteClassGetName(buf, sizeof(buf), cid);
                rv = printer(fd, "%*s", width, buf);
            }
        }
        break;

      case RWST_MARK_DEFAULTS:
        {
            char mark[3] = {' ', ' ', '\0'};
            int i = 0;
            if (iter->class_id != SK_INVALID_CLASS) {
                if (no_columns) {
                    memset(mark, 0, sizeof(mark));
                }
                if (iter->class_id == sksiteClassGetDefault())
                {
                    mark[0] = '+';
                    ++i;
                }
                if (iter->flowtype_id != SK_INVALID_FLOWTYPE) {
                    flowtype_iter_t fi;
                    flowtypeID_t ft;

                    /* See if this type is a default for this class */
                    sksiteClassDefaultFlowtypeIterator(iter->class_id, &fi);
                    while (sksiteFlowtypeIteratorNext(&fi, &ft)) {
                        if (iter->flowtype_id == ft) {
                            mark[no_columns ? i : 1] = '*';
                            ++i;
                            break;
                        }
                    }
                }
            }
            rv = printer(fd, "%*s", width, mark);
        }
        break;

      case RWST_CLASS_LIST:
        rv = rws_print_list_field(printer, fd, iter, RWST_CLASS, width);
        break;
      case RWST_TYPE_LIST:
        rv = rws_print_list_field(printer, fd, iter, RWST_TYPE, width);
        break;
      case RWST_FLOWTYPE_LIST:
        rv = rws_print_list_field(printer, fd, iter, RWST_FLOWTYPE, width);
        break;
      case RWST_FLOWTYPE_ID_LIST:
        rv = rws_print_list_field(printer, fd, iter, RWST_FLOWTYPE_ID, width);
        break;
      case RWST_SENSOR_LIST:
        rv = rws_print_list_field(printer, fd, iter, RWST_SENSOR, width);
        break;
      case RWST_SENSOR_ID_LIST:
        rv = rws_print_list_field(printer, fd, iter, RWST_SENSOR_ID, width);
        break;
      case RWST_DEFAULT_TYPE_LIST:
        rv = rws_print_list_field(printer, fd, iter, RWST_DEFAULT_TYPE, width);
        break;

      case RWST_MAX_FIELD_COUNT:
        skAbortBadCase(field);
    }

    /* Fill in any missing spaces. */
    if (rv < width) {
        printer(fd, "%*s", width - rv, "");
    }

    return rv;
}


/*
 *  len = rws_print_list_field(printer, fd, iter, field, width);
 *
 *    Print a 'width'-wide column containing the value for 'field'
 *    (where 'field' represents a "FIELD:list" field) using the
 *    fprintf-style function 'printer' to the FILE pointer 'fd', where
 *    'iter' is the current context.
 *
 *    Return the number of characters printed.
 */
static int rws_print_list_field(
    rws_fprintf_t  printer,
    FILE          *fd,
    rws_iter_t    *iter,
    rws_field_t    field,
    int            width)
{
    int total = 0;
    int len;
    rws_iter_t subiter;
    int first;

    /* Create a site iterator containing a single sub-iterator of the
     * correct type.  */
    memset(&subiter, 0, sizeof(subiter));
    switch (field) {
      case RWST_CLASS:
        if (iter->sensor_id == SK_INVALID_SENSOR) {
            subiter.order[0] = RWS_CLASS;
        } else {
            subiter.order[0] = RWS_CLASS_FROM_SENSOR;
        }
        subiter.sensor_id = iter->sensor_id;
        break;
      case RWST_TYPE:
      case RWST_FLOWTYPE:
      case RWST_FLOWTYPE_ID:
        if (iter->class_id == SK_INVALID_CLASS) {
            subiter.order[0] = RWS_FLOWTYPE;
        } else {
            subiter.order[0] = RWS_FLOWTYPE_FROM_CLASS;
        }
        subiter.class_id = iter->class_id;
        subiter.flowtype_id = iter->flowtype_id;
        break;
      case RWST_SENSOR:
      case RWST_SENSOR_ID:
        if (iter->class_id == SK_INVALID_CLASS) {
            subiter.order[0] = RWS_SENSOR;
        } else {
            subiter.order[0] = RWS_SENSOR_FROM_CLASS;
        }
        subiter.class_id = iter->class_id;
        break;
      case RWST_DEFAULT_TYPE:
        if (iter->class_id == SK_INVALID_CLASS) {
            return 0;
        }
        subiter.order[0] = RWS_DEFAULT_FLOWTYPE_FROM_CLASS;
        subiter.class_id = iter->class_id;
        break;
      default:
        skAbortBadCase(field);
    }
    rws_iter_bind(&subiter, 0);

    /* Call ourself with the fake-fprintf to determine the number of
     * printed characters in this field, and then print the padding */
    if (width != 0) {
        len = rws_print_list_field(fprintf_size, NULL, iter, field, 0);
        if (len < width) {
            total += printer(fd, "%*s", width - len, "");
        }
    }

    /* Iterate over the fields, printing each */
    first = 1;
    while (rws_iter_next(&subiter, 0)) {
        if (!first) {
            len = printer(fd, "%c", list_separator);
            total += len;
        }
        len = rws_print_field(printer, fd, &subiter, field, 0);
        total += len;
        first = 0;
    }

    return total;
}


/*
 *  rws_print_row(iter);
 *
 *    Print a single row from an iterator.
 */
static void rws_print_row(
    rws_iter_t *iter)
{
    size_t i;

    for (i = 0; i < num_fields; ++i) {
        if (i > 0) {
            fprintf(output.of_fp, "%c", column_separator);
        }
        rws_print_field(fprintf, output.of_fp, iter,
                        fields[i], col_width[fields[i]]);
    }
    fprintf(output.of_fp, "%s\n", final_delim);
}


/*
 *  len = fprintf_size(fs, format, ...);
 *
 *    Used to calculate print sizes.  This fprintf() variation does
 *    not produce outout, it simply returns how many charcters it
 *    WOULD have printed.
 */
static int fprintf_size(
    FILE    UNUSED(*stream),
    const char     *format,
    ...)
{
    char buf;
    int len;
    va_list ap;
    va_start(ap, format);
    len = vsnprintf(&buf, 0, format, ap);
    va_end(ap);
    return len;
}


/*
 *  rws_calcsize_row(iter);
 *
 *    Updates column sizes based on a single row that would be printed
 *    from the given iterator.
 */
static void rws_calcsize_row(
    rws_iter_t *iter)
{
    size_t i;
    int len;

    for (i = 0; i < num_fields; i++) {
        len = rws_print_field(fprintf_size, NULL, iter, fields[i], 0);
        if (len > col_width[fields[i]]) {
            col_width[fields[i]] = len;
        }
    }
}


/*
 *  rws_print_titles();
 *
 *    Print the column headings unless --no-titles was specified.
 */
static void rws_print_titles(void)
{
    size_t i;

    if (no_titles) {
        return;
    }

    for (i = 0; i < num_fields; ++i) {
        if (i > 0) {
            fprintf(output.of_fp, "%c", column_separator);
        }
        fprintf(output.of_fp, "%*s", col_width[fields[i]],
                (char *)field_map_entries[fields[i]].userdata);
    }
    fprintf(output.of_fp, "%s\n", final_delim);
}


/*
 *  status = rws_setup_iter_from_fields(iter);
 *
 *    Set up the iterator for iterating based on the field list.
 *
 *    Return 0 for normal iteration.  Return -1 for no output (other
 *    than titles).  Return 1 for outputting a single non-iterated
 *    entry.
 */
static int rws_setup_iter_from_fields(
    rws_iter_t *iter)
{
    size_t i;
    /* Next iterator level to initialize */
    int level = 0;
    /* Boolean: class iterator set? */
    int class_set = 0;
    /* Boolean: flowtype iterator set? */
    int flowtype_set = 0;
    /* Boolean: sensor iterator set? */
    int sensor_set = 0;
    /* Default-type iterator set at this level (0 == none, since
     * default-type cannot exist at level 0. */
    int default_type = 0;
    /* Boolean: could be a non-iterable singleton */
    int singleton = 0;

    memset(iter, 0, sizeof(*iter));
    iter->level = -1;

    for (i = 0; i < num_fields; i++) {
        switch (fields[i]) {
          case RWST_CLASS:
          case RWST_DEFAULT_TYPE_LIST:
            /* Need classes to generate default types */
            if (class_set || flowtype_set) {
                break;
            }
            iter->order[level] = sensor_set ? RWS_CLASS_FROM_SENSOR : RWS_CLASS;
            iter->level = level;
            ++level;
            class_set = 1;
            break;
          case RWST_TYPE:
          case RWST_FLOWTYPE:
          case RWST_FLOWTYPE_ID:
            if (flowtype_set) {
                break;
            }
            if (default_type) {
                /* Replace default type, as flowtype and default type
                   is nonsensical */
                iter->order[default_type] =
                    class_set ? RWS_FLOWTYPE_FROM_CLASS : RWS_FLOWTYPE;
                default_type = 0;
            } else {
                iter->order[level] =
                    class_set ? RWS_FLOWTYPE_FROM_CLASS : RWS_FLOWTYPE;
                iter->level = level;
                ++level;
            }
            flowtype_set = 1;
            break;
          case RWST_SENSOR:
          case RWST_SENSOR_ID:
          case RWST_SENSOR_DESC:
            if (sensor_set) {
                break;
            }
            iter->order[level] = class_set ? RWS_SENSOR_FROM_CLASS : RWS_SENSOR;
            iter->level = level;
            ++level;
            sensor_set = 1;
            break;
          case RWST_DEFAULT_TYPE:
            assert(!default_type);
            if (flowtype_set) {
                break;
            }
            if (!class_set) {
                /* Default-type needs a class iterator, so add one */
                iter->order[level] =
                    sensor_set ? RWS_CLASS_FROM_SENSOR : RWS_CLASS;
                ++level;
                class_set = 1;
            }
            iter->order[level] = RWS_DEFAULT_FLOWTYPE_FROM_CLASS;
            iter->level = level;
            ++level;
            default_type = level;
            break;
          case RWST_DEFAULT_CLASS:
          case RWST_CLASS_LIST:
          case RWST_TYPE_LIST:
          case RWST_FLOWTYPE_LIST:
          case RWST_FLOWTYPE_ID_LIST:
          case RWST_SENSOR_LIST:
          case RWST_SENSOR_ID_LIST:
          case RWST_DEFAULT_CLASS_LIST:
            /* These fields can generate null-iterators with a single
             * row of output. */
            singleton = 1;
            break;
          case RWST_MARK_DEFAULTS:
            break;
          case RWST_MAX_FIELD_COUNT:
          default:
            skAbortBadCase(fields[i]);
        }
    }

    /* Bind the iterator (at level -1 if this is a null-iterator) */
    iter->flowtype_id = SK_INVALID_FLOWTYPE;
    iter->class_id    = SK_INVALID_CLASS;
    iter->sensor_id   = SK_INVALID_SENSOR;
    rws_iter_bind(iter, level ? 0 : -1);

    if (level != 0) {
        /* Return 0 if this is not a null iterator */
        return 0;
    }

    /* Return 1 if this is a singleton null-iterator.  -1 otherwise */
    return singleton ? 1 : -1;
}


int main(int argc, char **argv)
{
    rws_iter_t iter;
    rws_iter_t calciter;
    int rv;

    appSetup(argc, argv);       /* never returns on error */

    /* Set up site iterator */
    rv = rws_setup_iter_from_fields(&iter);
    if (rv == -1) {
        /* Nothing to do */
        rws_print_titles();
        return EXIT_SUCCESS;
    }

    /* Calculate column sizes */
    if (!no_columns) {
        calciter = iter;
        if (rv == 1) {
            /* Null iterator case */
            rws_calcsize_row(&calciter);
        } else {
            while (rws_iter_next(&calciter, 0)) {
                rws_calcsize_row(&calciter);
            }
        }
    }

    /* Print titles */
    rws_print_titles();

    /* Print rows */
    if (rv == 1) {
        /* Null iterator case */
        rws_print_row(&iter);
    } else {
        while (rws_iter_next(&iter, 0)) {
            rws_print_row(&iter);
        }
    }

    return EXIT_SUCCESS;
}


/*
** Local Variables:
** mode:c
** indent-tabs-mode:nil
** c-basic-offset:4
** End:
*/

[-- Attachment #3: Type: text/plain, Size: 41 bytes --]


-- 
Michael Welsh Duggan
(mwd@cert.org)

  reply	other threads:[~2012-11-26 13:25 UTC|newest]

Thread overview: 55+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-06-19 20:45 bug#11749: 24.1; C-mode indentation gives wrong-type-argument error Kim F. Storm
2012-06-21  7:17 ` Glenn Morris
2012-06-21  9:34   ` Kim Storm
2012-08-28 16:17 ` bug#11749: 24.2; wrong-type-argument Kim F. Storm
     [not found] ` <handler.11749.B.13401389485673.ack@debbugs.gnu.org>
2012-08-28 22:49   ` bug#11749: Acknowledgement (24.1; C-mode indentation gives wrong-type-argument error.) Kim Storm
2012-08-31 11:01     ` Eli Zaretskii
2012-08-31 12:37       ` Kim Storm
2012-09-02 21:16     ` Alan Mackenzie
2012-09-03  9:47       ` Kim Storm
2012-09-03 13:56         ` Stefan Monnier
2012-09-03 14:20           ` Kim Storm
2012-09-03 18:52             ` Stefan Monnier
2012-09-05 20:48         ` Alan Mackenzie
2012-09-07  3:45           ` Michael Welsh Duggan
2012-09-07 14:53             ` Stefan Monnier
2012-09-07 16:16               ` Kim Storm
2012-09-08 21:14             ` Alan Mackenzie
2012-09-10 12:18               ` Michael Welsh Duggan
2012-09-10 12:48                 ` Michael Welsh Duggan
2012-09-21 17:47                   ` Michael Welsh Duggan
2012-10-07 10:59                     ` Alan Mackenzie
2012-10-09 14:05                       ` Michael Welsh Duggan
2012-10-10 20:00                         ` Alan Mackenzie
2012-10-14 17:06                           ` Alan Mackenzie
2012-10-23 16:13                             ` Michael Welsh Duggan
2012-10-25 13:41                             ` Michael Welsh Duggan
2012-10-28 11:36                               ` Alan Mackenzie
2012-11-04  3:43                                 ` Chong Yidong
2012-11-04 20:42                                   ` Alan Mackenzie
2012-11-21 20:58                                   ` Alan Mackenzie
2012-11-22 14:52                                     ` Stefan Monnier
2012-11-04 20:39                                 ` Alan Mackenzie
2012-11-04 21:04                                   ` Kim Storm
2012-11-14 16:52                                   ` Michael Welsh Duggan
2012-11-21 21:33                                     ` Alan Mackenzie
2012-11-26 13:25                                       ` Michael Welsh Duggan [this message]
2012-12-10  3:35                                         ` Michael Welsh Duggan
2013-01-07 12:09                                           ` Alan Mackenzie
2013-01-17 16:27                                             ` Michael Welsh Duggan
2013-01-17 16:28                                               ` Michael Welsh Duggan
2013-01-23 14:16                                               ` Alan Mackenzie
2013-01-23 15:39                                                 ` Michael Welsh Duggan
2013-01-29 11:37                                                   ` Alan Mackenzie
     [not found]                                                   ` <20130129113737.GA4544@acm.acm>
2013-02-01 22:18                                                     ` Michael Welsh Duggan
2013-02-01 23:50                                                       ` Kim Storm
2013-02-02 19:35                                                         ` Alan Mackenzie
2012-09-10 13:10                 ` Michael Welsh Duggan
2012-09-10 13:22                   ` Michael Welsh Duggan
2012-09-10 18:25                     ` Michael Welsh Duggan
2012-09-05 13:11 ` bug#11749: I also have this issue Denis Zalevskiy
2013-01-08 19:10 ` bug#11749: Acknowledgement (24.1; C-mode indentation gives wrong-type-argument error.) Glenn Morris
2013-01-09 22:13   ` Alan Mackenzie
2013-02-02 18:37 ` bug#11749: 24.1; C-mode indentation gives wrong-type-argument error Alan Mackenzie
2013-01-08 13:49   ` bug#13385: 24.1; TAB in C file causes type error Julian Stecklina
     [not found]     ` <handler.13385.D11749.1359830688892.notifdone@debbugs.gnu.org>
2013-02-03 13:00       ` bug#13385: closed (Re: 24.1; C-mode indentation gives wrong-type-argument error.) Julian Stecklina

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=tnty5ho4hte.fsf@waterbuck.yellow.cert.org \
    --to=mwd@cert.org \
    --cc=11749@debbugs.gnu.org \
    --cc=acm@muc.de \
    --cc=storm@cua.dk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).