unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#18749: 24.3.94; CC Mode 5.32.5 (C/l); cc-mode state cache failure leading to bad indentation
@ 2014-10-16 21:12 Michael Welsh Duggan
  2014-10-17 18:19 ` Alan Mackenzie
       [not found] ` <mailman.11430.1413622526.1147.bug-gnu-emacs@gnu.org>
  0 siblings, 2 replies; 7+ messages in thread
From: Michael Welsh Duggan @ 2014-10-16 21:12 UTC (permalink / raw)
  To: 18749; +Cc: bug-cc-mode

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

A consistent c-mode indentation error that causes bad indentation, much
cursing, and gnashing of teeth.  This occurs in the latest emacs-24
branch (and trunk).

From emacs -Q, using the attached file:

[-- Attachment #2: rwuniqsetup.c.before.2 --]
[-- Type: text/plain, Size: 79703 bytes --]

/*
** Copyright (C) 2001-2014 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@
*/

/*
**  rwuniqsetup.c
**
**  Application setup for rwuniq.  See rwuniq.c for a description.
*/

#include <silk/silk.h>

RCSIDENT("$Id$");

#include <silk/skcountry.h>
#include <silk/skprefixmap.h>
#include <silk/sksite.h>
#include <silk/skstringmap.h>
#include "rwuniq.h"


/* TYPEDEFS AND DEFINES */

/* file handle for --help usage message */
#define USAGE_FH stdout

/* where to write filenames if --print-file specified */
#define PRINT_FILENAMES_FH  stderr

/* suffix for distinct fields */
#define DISTINCT_SUFFIX  "-Distinct"

/* specific ie identifiers */
#define OCTET_DELTA_COUNT          SK_FIELD_IDENT_CREATE(0, 1)
#define OCTET_TOTAL_COUNT          SK_FIELD_IDENT_CREATE(0, 85)
#define PACKET_DELTA_COUNT         SK_FIELD_IDENT_CREATE(0, 2)
#define PACKET_TOTAL_COUNT         SK_FIELD_IDENT_CREATE(0, 86)
#define SOURCE_IPV4_ADDRESS        SK_FIELD_IDENT_CREATE(0, 8)
#define DESTINATION_IPV4_ADDRESS   SK_FIELD_IDENT_CREATE(0, 12)
#define SOURCE_IPV6_ADDRESS        SK_FIELD_IDENT_CREATE(0, 27)
#define DESTINATION_IPV6_ADDRESS   SK_FIELD_IDENT_CREATE(0, 28)
#define IP_NEXT_HOP_IPV4_ADDRESS   SK_FIELD_IDENT_CREATE(0, 15)
#define IP_NEXT_HOP_IPV6_ADDRESS   SK_FIELD_IDENT_CREATE(0, 62)
#define FLOW_START_MILLISECONDS    SK_FIELD_IDENT_CREATE(0, 152)
#define FLOW_END_MILLISECONDS      SK_FIELD_IDENT_CREATE(0, 153)
#define SILK_FLOW_SENSOR           SK_FIELD_IDENT_CREATE(IPFIX_CERT_PEN, 31)
#define TCP_CONTROL_BITS           SK_FIELD_IDENT_CREATE(0, 6)
#define INITIAL_TCP_FLAGS          SK_FIELD_IDENT_CREATE(IPFIX_CERT_PEN, 14)
#define UNION_TCP_FLAGS            SK_FIELD_IDENT_CREATE(IPFIX_CERT_PEN, 15)
#define REVERSE_TCP_CONTROL_BITS   SK_FIELD_IDENT_CREATE(FB_IE_PEN_REVERSE, 6)
#define REVERSE_INITIAL_TCP_FLAGS                                       \
    SK_FIELD_IDENT_CREATE(IPFIX_CERT_PEN, 14 | FB_IE_VENDOR_BIT_REVERSE)
#define REVERSE_UNION_TCP_FLAGS    \
    SK_FIELD_IDENT_CREATE(IPFIX_CERT_PEN, 15 | FB_IE_VENDOR_BIT_REVERSE)

/* type of field being defined */
typedef enum field_type_en {
    FIELD_TYPE_KEY, FIELD_TYPE_VALUE, FIELD_TYPE_DISTINCT
} field_type_t;


typedef struct field_alias_st {
    const char *alias;
    const char *ie_name;
} field_alias_t;

/* LOCAL VARIABLES */

/* create aliases for exisitng value fields.  the struct contains the
 * name of the alias and an ID to match in the builtin_values[]
 * array */
static const struct builtin_value_aliases_st {
    const char     *ba_name;
    sk_fieldid_t    ba_id;
} builtin_value_aliases[] = {
    {"Flows",   SK_FIELD_RECORDS},
    {NULL,      (sk_fieldid_t)0}
};

static const field_alias_t field_aliases[] = {
    {"sPort",        "sourceTransportPort"},
    {"dPort",        "destinationTransportPort"},
    {"protocol",     "protocolIdentifier"},
    {"flags",        "tcpControlBits"},
    {"sTime",        "flowStartMilliseconds"},
    {"eTime",        "flowEndMilliseconds"},
    {"sensor",       "silkFlowSensor"},
    {"in",           "ingressInterface"},
    {"out",          "egressInterface"},
    {"initialFlags", "initialTCPFlags"},
    {"sessionFlags", "unionTCPFlags"},
    {"attributes",   "silkTCPState"},
    {"application",  "silkAppLabel"},
    {NULL, NULL}};

/* key fields used when parsing the user's --fields switch */
static sk_stringmap_t *key_field_map = NULL;

/* available aggregate value fields */
static sk_stringmap_t *value_field_map = NULL;

/* the text the user entered for the --fields switch */
static const char *fields_arg = NULL;

/* the text the user entered for the --values switch */
static const char *values_arg = NULL;

/* the information model */
static fbInfoModel_t *infomodel = NULL;

/* array of key field idents requested from input stream */
static sk_vector_t *key_fields_vec = NULL;

/* array of distinct value field idents requested from input stream */
static sk_vector_t *distinct_fields_vec = NULL;

/* name of program to run to page output */
static char *pager;

/* where to copy the input to */
static skstream_t *copy_input = NULL;

/* temporary directory */
static const char *temp_directory = NULL;

/* how to print IP addresses */
static uint32_t ip_format = SKIPADDR_CANONICAL;

/* how to print timestamps */
static uint32_t time_flags = SKTIMESTAMP_NOMSEC;

/* the floor of the sTime and/or eTime */
static sktime_t time_bin_size = 0;

/* Vector of pointers to varlen data */
static sk_vector_t *varlen_data = NULL;

/* delimiter between output columns */
static char delimiter = '|';

/* input checker */
static sk_options_ctx_t *optctx = NULL;


/* non-zero if we are shutting down due to a signal; controls whether
 * errors are printed in appTeardown(). */
static int caught_signal = 0;

/* timestamp formats: the first of these will be the default */
static const sk_stringmap_entry_t timestamp_names[] = {
    {"default", 0,                    NULL, "yyyy/mm/ddThh:mm:ss"},
    {"iso",     SKTIMESTAMP_ISO,      NULL, "yyyy-mm-dd hh:mm:ss"},
    {"m/d/y",   SKTIMESTAMP_MMDDYYYY, NULL, "mm/dd/yyyy hh:mm:ss"},
    {"epoch",   SKTIMESTAMP_EPOCH,    NULL,
     "seconds since UNIX epoch; ignores timezone"},
    SK_STRINGMAP_SENTINEL
};
static const sk_stringmap_entry_t timestamp_zones[] = {
    {"utc",     SKTIMESTAMP_UTC,      NULL, "use UTC"},
    {"local",   SKTIMESTAMP_LOCAL,    NULL,
     "use TZ environment variable or local timezone"},
    SK_STRINGMAP_SENTINEL
};
#if 0
static const sk_stringmap_entry_t timestamp_misc[] = {
    {"no-msec", SKTIMESTAMP_NOMSEC,   NULL, "truncate milliseconds"},
    SK_STRINGMAP_SENTINEL
};
#endif

static sktime_t getSTimeFn(const rwRec *rwrec, void *ctx);
static sktime_t getETimeFn(const rwRec *rwrec, void *ctx);
static sktime_t getElapsedFn(const rwRec *rwrec, void *ctx);
static uint64_t getBytesFn(const rwRec *rwrec, void *ctx);
static uint64_t getPacketsFn(const rwRec *rwrec, void *ctx);

static const sk_unique_recfns_t uniq_recfns = {
    NULL,                       /* ctx */
    getSTimeFn,                 /* get_stime */
    getETimeFn,                 /* get_etime */
    getElapsedFn,               /* get_elapsed */
    getBytesFn,                 /* get_bytes */
    getPacketsFn,               /* get_packets */
};


/* OPTIONS */

typedef enum {
    OPT_HELP_FIELDS,
    OPT_FIELDS,
    OPT_VALUES,
    OPT_ALL_COUNTS,
    /* OPT_BYTES...OPT_DIP_DISTINCT must be contiguous and appear in
     * same order as in builtin_values[] */
    OPT_BYTES,
    OPT_PACKETS,
    OPT_FLOWS,
    OPT_STIME,
    OPT_ETIME,
    OPT_SIP_DISTINCT,
    OPT_DIP_DISTINCT,
    OPT_PRESORTED_INPUT,
    OPT_SORT_OUTPUT,
#if 0
    OPT_BIN_TIME,
#endif
    OPT_TIMESTAMP_FORMAT,
    OPT_INTEGER_SENSORS,
    OPT_INTEGER_TCP_FLAGS,
    OPT_NO_TITLES,
    OPT_NO_COLUMNS,
    OPT_COLUMN_SEPARATOR,
    OPT_NO_FINAL_DELIMITER,
    OPT_DELIMITED,
    OPT_PRINT_FILENAMES,
    OPT_COPY_INPUT,
    OPT_OUTPUT_PATH,
    OPT_PAGER
} appOptionsEnum;


static struct option appOptions[] = {
    {"help-fields",         NO_ARG,       0, OPT_HELP_FIELDS},
    {"fields",              REQUIRED_ARG, 0, OPT_FIELDS},
    {"values",              REQUIRED_ARG, 0, OPT_VALUES},
    {"all-counts",          NO_ARG,       0, OPT_ALL_COUNTS},
    {"bytes",               OPTIONAL_ARG, 0, OPT_BYTES},
    {"packets",             OPTIONAL_ARG, 0, OPT_PACKETS},
    {"flows",               OPTIONAL_ARG, 0, OPT_FLOWS},
    {"stime",               NO_ARG,       0, OPT_STIME},
    {"etime",               NO_ARG,       0, OPT_ETIME},
    {"sip-distinct",        OPTIONAL_ARG, 0, OPT_SIP_DISTINCT},
    {"dip-distinct",        OPTIONAL_ARG, 0, OPT_DIP_DISTINCT},
    {"presorted-input",     NO_ARG,       0, OPT_PRESORTED_INPUT},
    {"sort-output",         NO_ARG,       0, OPT_SORT_OUTPUT},
#if 0
    {"bin-time",            OPTIONAL_ARG, 0, OPT_BIN_TIME},
#endif
    {"timestamp-format",    REQUIRED_ARG, 0, OPT_TIMESTAMP_FORMAT},
    {"integer-sensors",     NO_ARG,       0, OPT_INTEGER_SENSORS},
    {"integer-tcp-flags",   NO_ARG,       0, OPT_INTEGER_TCP_FLAGS},
    {"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},
    {"print-filenames",     NO_ARG,       0, OPT_PRINT_FILENAMES},
    {"copy-input",          REQUIRED_ARG, 0, OPT_COPY_INPUT},
    {"output-path",         REQUIRED_ARG, 0, OPT_OUTPUT_PATH},
    {"pager",               REQUIRED_ARG, 0, OPT_PAGER},
    {0,0,0,0}               /* sentinel entry */
};

static const char *appHelp[] = {
    "Describe each possible field and value and exit. Def. no",
    ("Use these fields as the grouping key. Specify fields as a\n"
     "\tcomma-separated list of names, IDs, and/or ID-ranges"),
    ("Compute these values for each group. Def. records\n"
     "\tSpecify values as a comma-separated list of names"),
    ("Enable the next five switches--count everything.  If no\n"
     "\tcount is specified, flows are counted.  Def. No"),
    ("Sum bytes in each bin; optionally choose to print\n"
     "\tbins whose total is in given range; range is MIN or MIN-MAX. Def. No"),
    ("Sum packets in each bin; optionally choose to print\n"
     "\tbins whose total is in given range; range is MIN or MIN-MAX. Def. No"),
    ("Count flow records in each bin; optionally choose to print\n"
     "\tbins whose count is in given range; range is MIN or MIN-MAX. Def. No"),
    "Print earliest time flow was seen in each bin. Def. No",
    "Print latest time flow was seen  in each bin. Def. No",
    ("Count distinct sIPs in each bin; optionally choose to\n"
     "\tprint bins whose count is in range; range is MIN or MIN-MAX. Def. No"),
    ("Count distinct dIPs in each bin; optionally choose to\n"
     "\tprint bins whose count is in range; range is MIN or MIN-MAX. Def. No"),
    ("Assume input has been presorted using\n"
     "\trwsort invoked with the exact same --fields value. Def. No"),
    ("Present the output in sorted order. Def. No"),
#if 0
    ("When using 'sTime' or 'eTime' as a key, adjust time(s) to\n"
     "\tto appear in N-second bins (floor of time is used). Def. No, "),
#endif
    NULL, /* generated dynamically */
    "Print sensor as an integer. Def. Sensor name",
    "Print TCP Flags as an integer. Def. No",
    "Do not print column titles. 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",
    "Print names of input files as they are opened. Def. No",
    "Copy all input SiLK Flows to given pipe or file. Def. No",
    "Send output to given file path. Def. stdout",
    "Program to invoke to page output. Def. $SILK_PAGER or $PAGER",
    (char *)NULL
};



/* LOCAL FUNCTION PROTOTYPES */

static int  appOptionsHandler(clientData cData, int opt_index, char *opt_arg);
static void appHandleSignal(int sig);

static int  timestampFormatParse(const char* opt_arg, uint32_t *out_flags);
static void timestampFormatUsage(FILE *fh);
static void helpFields(FILE *fh);

static int  createStringmaps(void);
static sk_stringmap_t *createFieldStringmap(const fbInfoModel_t *model);
static int  parseKeyFields(const char *field_string);
static int  parseValueFields(const char *field_string);
static int  isFieldDuplicate(
    const sk_fieldlist_t   *flist,
    sk_fieldid_t            fid,
    const void             *fcontext);
static int  prepareFileForRead(skstream_t *rwios);
static void schemaContextDestroy(void *vctx);


/* 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)
{
    FILE *fh = USAGE_FH;
    int i;

#define USAGE_MSG                                                             \
    ("--fields=N [SWITCHES] [FILES]\n"                                        \
     "\tSummarize SiLK Flow records into user-defined keyed bins specified\n" \
     "\twith the --fields switch.  For each keyed bin, print byte, packet,\n" \
     "\tand/or flow counts and/or the time window when key was active.\n"     \
     "\tWhen no files are given on command line, flows are read from STDIN.\n")

    /* Create the string maps for --fields and --values */
    createStringmaps();

    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]));
        switch ((appOptionsEnum)i) {
          case OPT_FIELDS:
            /* Dynamically build the help */
            fprintf(fh, "%s\n", appHelp[i]);
            skStringMapPrintUsage(key_field_map, fh, 4);
            break;
          case OPT_VALUES:
            fprintf(fh, "%s\n", appHelp[i]);
            skStringMapPrintUsage(value_field_map, fh, 4);
            break;
#if 0
          case OPT_BIN_TIME:
            fprintf(fh, "%s%d\n", appHelp[i], DEFAULT_TIME_BIN);
            break;
#endif
          case OPT_TIMESTAMP_FORMAT:
            timestampFormatUsage(fh);
            skOptionsIPFormatUsage(fh);
            break;
          default:
            /* Simple help text from the appHelp array */
            fprintf(fh, "%s\n", appHelp[i]);
            break;
        }
    }

    skOptionsCtxOptionsUsage(optctx, fh);
    skOptionsTempDirUsage(fh);
    sksiteOptionsUsage(fh);
}


/*
 *  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.
 */
void appSetup(int argc, char **argv)
{
    SILK_FEATURES_DEFINE_STRUCT(features);
    int optctx_flags;
    char *path;
    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(&app_flags, 0, sizeof(app_flags));
    memset(&output, 0, sizeof(output));
    output.of_fp = stdout;

    varlen_data = sk_vector_create(sizeof(void *));

    optctx_flags = (SK_OPTIONS_CTX_INPUT_SILK_FLOW | SK_OPTIONS_CTX_ALLOW_STDIN
                    | SK_OPTIONS_CTX_XARGS);

    /* Prepare the information model */
    infomodel = fbInfoModelAlloc();
    skipfixCERTAugmentInfoModel(infomodel);

    /* register the options */
    if (skOptionsCtxCreate(&optctx, optctx_flags)
        || skOptionsCtxOptionsRegister(optctx)
        || skOptionsRegister(appOptions, &appOptionsHandler, NULL)
        || skOptionsTempDirRegister(&temp_directory)
        || skOptionsIPFormatRegister(&ip_format)
        || sksiteOptionsRegister(SK_SITE_FLAG_CONFIG_FILE))
    {
        skAppPrintErr("Unable to register options");
        appExit(EXIT_FAILURE);
    }

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

    /* parse options */
    rv = skOptionsCtxOptionsParse(optctx, argc, argv);
    if (rv < 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, but we
     * should not consider it a complete failure */
    sksiteConfigure(0);

    /* create the ascii format stream and set its properties */
    ipfix_fmt = sk_formatter_create();
    sk_formatter_set_delimeter(ipfix_fmt, delimiter);
    if (app_flags.no_final_delimiter) {
        sk_formatter_set_no_final_delimeter(ipfix_fmt);
    }
    if (app_flags.no_columns) {
        sk_formatter_set_no_columns(ipfix_fmt);
    }

    /* set up the key_field_map and value_field_map */
    if (createStringmaps()) {
        appExit(EXIT_FAILURE);
    }

    /* make sure the user specified at least one key field */
    if (fields_arg == NULL || fields_arg[0] == '\0') {
        skAppPrintErr("The --%s switch is required",
                      appOptions[OPT_FIELDS].name);
        skAppUsage();         /* never returns */
    }

    /* parse the --fields and --values switches */
    if (parseKeyFields(fields_arg)) {
        appExit(EXIT_FAILURE);
    }
    if (parseValueFields(values_arg)) {
        appExit(EXIT_FAILURE);
    }

    sk_formatter_finalize(ipfix_fmt);

    /* make certain stdout is not being used for multiple outputs */
    if (copy_input
        && ((0 == strcmp(skStreamGetPathname(copy_input), "-"))
            || (0 == strcmp(skStreamGetPathname(copy_input), "stdout"))))
    {
        if ((NULL == output.of_name)
            || (0 == strcmp(output.of_name, "-"))
            || (0 == strcmp(output.of_name, "stdout")))
        {
            skAppPrintErr("May not use stdout for multiple output streams");
            exit(EXIT_FAILURE);
        }
    }

    /* create and initialize the uniq object */
    if (app_flags.presorted_input) {
        if (skPresortedUniqueCreate(&ps_uniq)) {
            appExit(EXIT_FAILURE);
        }

        skPresortedUniqueSetTimeBinSize(ps_uniq, time_bin_size);
        skPresortedUniqueSetTempDirectory(ps_uniq, temp_directory);
        skPresortedUniqueSetErrorFunction(ps_uniq, skAppPrintErr);

        if (skPresortedUniqueSetFields(ps_uniq, key_fields, distinct_fields,
                                       value_fields))
        {
            skAppPrintErr("Unable to set fields");
            appExit(EXIT_FAILURE);
        }

        skUniqueSetRecfns(uniq, &uniq_recfns);

        while ((rv = skOptionsCtxNextArgument(optctx, &path)) == 0) {
            skPresortedUniqueAddInputFile(ps_uniq, path);
        }
        if (rv < 0) {
            appExit(EXIT_FAILURE);
        }

        skPresortedUniqueSetPostOpenFn(ps_uniq, prepareFileForRead);

    } else {
        if (skUniqueCreate(&uniq)) {
            appExit(EXIT_FAILURE);
        }
        if (app_flags.sort_output) {
            skUniqueSetSortedOutput(uniq);
        }

        skUniqueSetTimeBinSize(uniq, time_bin_size);
        skUniqueSetTempDirectory(uniq, temp_directory);
        skUniqueSetErrorFunction(uniq, skAppPrintErr);

        if (skUniqueSetFields(uniq, key_fields, distinct_fields, value_fields)
            || skUniquePrepareForInput(uniq))
        {
            skAppPrintErr("Unable to set fields");
            appExit(EXIT_FAILURE);
        }

        skUniqueSetRecfns(uniq, &uniq_recfns);
    }

    /* open the --output-path.  the 'of_name' member is NULL if user
     * didn't get an output-path. */
    if (output.of_name) {
        rv = skFileptrOpen(&output, SK_IO_WRITE);
        if (rv) {
            skAppPrintErr("Unable to open %s '%s': %s",
                          appOptions[OPT_OUTPUT_PATH].name,
                          output.of_name, skFileptrStrerror(rv));
            appExit(EXIT_FAILURE);
        }
    }

    /* open the --copy-input destination */
    if (copy_input) {
        rv = skStreamOpen(copy_input);
        if (rv) {
            skStreamPrintLastErr(copy_input, rv, &skAppPrintErr);
            appExit(EXIT_FAILURE);
        }
    }

    /* set signal handler to clean up temp files on SIGINT, SIGTERM, etc */
    if (skAppSetSignalHandler(&appHandleSignal)) {
        appExit(EXIT_FAILURE);
    }

    return;                       /* OK */
}


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

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

    skUniqueDestroy(&uniq);
    skPresortedUniqueDestroy(&ps_uniq);

    /* destroy field lists */
    skFieldListDestroy(&key_fields);
    skFieldListDestroy(&distinct_fields);
    skFieldListDestroy(&value_fields);

    /* destroy output */
    sk_formatter_destroy(ipfix_fmt);
    ipfix_fmt = NULL;

    /* close output */
    if (output.of_name) {
        skFileptrClose(&output, &skAppPrintErr);
    }
    /* close the --copy-input */
    if (copy_input) {
        rv = skStreamClose(copy_input);
        if (rv && rv != SKSTREAM_ERR_NOT_OPEN) {
            skStreamPrintLastErr(copy_input, rv, &skAppPrintErr);
        }
        skStreamDestroy(&copy_input);
    }

    /* Destroy schemas */
    sk_schema_destroy(uniq_schema);
    uniq_schema = NULL;
    sk_record_destroy(uniq_rec);
    uniq_rec = NULL;
    schemaContextDestroy(uniq_ctx);
    uniq_ctx = NULL;

    /* Destroy varlen data */
    if (varlen_data) {
        size_t i;
        void *data;
        for (i = 0; i < sk_vector_get_count(varlen_data); ++i) {
            sk_vector_get_value(varlen_data, i, &data);
            free(data);
        }
        sk_vector_destroy(varlen_data);
        varlen_data = NULL;
    }

    sk_vector_destroy(key_fields_vec);
    sk_vector_destroy(distinct_fields_vec);

    free(asciibuf);
    asciibuf = NULL;
    asciilen = 0;

    /* destroy string maps for keys and values */
    if (key_field_map) {
        skStringMapDestroy(key_field_map);
        key_field_map = NULL;
    }
    if (value_field_map) {
        skStringMapDestroy(value_field_map);
        value_field_map = NULL;
    }

    skOptionsCtxDestroy(&optctx);

    if (infomodel) {
        fbInfoModelFree(infomodel);
        infomodel = NULL;
    }

    skAppUnregister();
}


/*
 *  status = appOptionsHandler(cData, opt_index, opt_arg);
 *
 *    Called by skOptionsParse(), this handles a user-specified switch
 *    that the application has registered, typically by setting global
 *    variables.  Returns 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)
{
    size_t i;
    int rv;

    switch ((appOptionsEnum)opt_index) {
      case OPT_HELP_FIELDS:
        helpFields(USAGE_FH);
        exit(EXIT_SUCCESS);

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

      case OPT_VALUES:
        if (values_arg) {
            skAppPrintErr("Invalid %s: Switch used multiple times",
                          appOptions[opt_index].name);
            return 1;
        }
        values_arg = opt_arg;
        break;

      case OPT_ALL_COUNTS:
        for (i = 0; i < num_builtin_values; ++i) {
            if (builtin_values[i].bf_all_counts) {
                builtin_values[i].bf_switched_on = 1;
            }
        }
        break;

      case OPT_BYTES:
      case OPT_PACKETS:
      case OPT_FLOWS:
      case OPT_STIME:
      case OPT_ETIME:
      case OPT_SIP_DISTINCT:
      case OPT_DIP_DISTINCT:
        i = opt_index - OPT_BYTES;
        builtin_values[i].bf_switched_on = 1;
        if (opt_arg) {
            rv = skStringParseRange64(&builtin_values[i].bf_min,
                                      &builtin_values[i].bf_max,
                                      opt_arg, 0, 0,
                                      SKUTILS_RANGE_SINGLE_OPEN);
            if (rv) {
                goto PARSE_ERROR;
            }
            /* treat a single value as having no max, not as a range
             * of a single value */
            if ((builtin_values[i].bf_min == builtin_values[i].bf_max)
                && !strchr(opt_arg, '-'))
            {
                builtin_values[i].bf_max = UINT64_MAX;
            }
            app_flags.check_limits = 1;
        }
        break;

#if 0
      case OPT_BIN_TIME:
        if (opt_arg == NULL || opt_arg[0] == '\0') {
            /* no time given; use default */
            time_bin_size = sktimeCreate(DEFAULT_TIME_BIN, 0);
        } else {
            /* parse user's time */
            rv = skStringParseUint32(&val32, opt_arg, 1, 0);
            if (rv) {
                goto PARSE_ERROR;
            }
            time_bin_size = sktimeCreate(val32, 0);
        }
        break;
#endif

      case OPT_PRESORTED_INPUT:
        app_flags.presorted_input = 1;
        break;

      case OPT_SORT_OUTPUT:
        app_flags.sort_output = 1;
        break;

      case OPT_TIMESTAMP_FORMAT:
        if (timestampFormatParse(opt_arg, &time_flags)) {
            return 1;
        }
        break;

      case OPT_INTEGER_SENSORS:
        app_flags.integer_sensors = 1;
        break;

      case OPT_INTEGER_TCP_FLAGS:
        app_flags.integer_tcp_flags = 1;
        break;

      case OPT_NO_TITLES:
        app_flags.no_titles = 1;
        break;

      case OPT_NO_COLUMNS:
        app_flags.no_columns = 1;
        break;

      case OPT_NO_FINAL_DELIMITER:
        app_flags.no_final_delimiter = 1;
        break;

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

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

      case OPT_PRINT_FILENAMES:
        app_flags.print_filenames = 1;
        break;

      case OPT_COPY_INPUT:
        if (copy_input) {
            skAppPrintErr("Invalid %s: Switch used multiple times",
                          appOptions[opt_index].name);
            return 1;
        }
        if ((rv=skStreamCreate(&copy_input, SK_IO_WRITE, SK_CONTENT_SILK_FLOW))
            || (rv = skStreamBind(copy_input, opt_arg)))
        {
            skStreamPrintLastErr(copy_input, rv, &skAppPrintErr);
            return 1;
        }
        break;

      case OPT_OUTPUT_PATH:
        if (output.of_name) {
            skAppPrintErr("Invalid %s: Switch used multiple times",
                          appOptions[opt_index].name);
            return 1;
        }
        output.of_name = opt_arg;
        break;

      case OPT_PAGER:
        pager = opt_arg;
        break;
    }

    return 0;                     /* OK */

  PARSE_ERROR:
    skAppPrintErr("Invalid %s '%s': %s",
                  appOptions[opt_index].name, opt_arg,
                  skStringParseStrerror(rv));
    return 1;
}


/*
 *  appExit(status)
 *
 *  Exit the application with the given status.
 */
void appExit(int status)
{
    appTeardown();
    exit(status);
}


/*
 *  appHandleSignal(signal_value)
 *
 *    Call appExit() to exit the program.  If signal_value is SIGPIPE,
 *    close cleanly; otherwise print a message that we've caught the
 *    signal and exit with EXIT_FAILURE.
 */
static void appHandleSignal(int sig)
{
    caught_signal = 1;

    if (sig == SIGPIPE) {
        /* we get SIGPIPE if something downstream, like rwcut, exits
         * early, so don't bother to print a warning, and exit
         * successfully */
        appExit(EXIT_SUCCESS);
    } else {
        skAppPrintErr("Caught signal..cleaning up and exiting");
        appExit(EXIT_FAILURE);
    }
}


/*
 *  status = timestampFormatParse(format_string, out_flags);
 *
 *    Parse the comma-separated list of timestamp format strings
 *    contained in 'format_string' and set 'out_flags' to the result
 *    of parsing the string.  Return 0 on success, or -1 if parsing of
 *    the values fails or conflicting values are given.
 */
static int timestampFormatParse(
    const char *format,
    uint32_t   *out_flags)
{
    char buf[256];
    char *errmsg;
    sk_stringmap_t *str_map = NULL;
    sk_stringmap_iter_t *iter = NULL;
    sk_stringmap_entry_t *found_entry;
    const sk_stringmap_entry_t *entry;
    int name_seen = 0;
    int zone_seen = 0;
    int rv = -1;

    /* create a stringmap of the available timestamp formats */
    if (SKSTRINGMAP_OK != skStringMapCreate(&str_map)) {
        skAppPrintOutOfMemory(NULL);
        goto END;
    }
    if (skStringMapAddEntries(str_map, -1, timestamp_names) != SKSTRINGMAP_OK){
        skAppPrintOutOfMemory(NULL);
        goto END;
    }
    if (skStringMapAddEntries(str_map, -1, timestamp_zones) != SKSTRINGMAP_OK){
        skAppPrintOutOfMemory(NULL);
        goto END;
    }

    /* attempt to match */
    if (skStringMapParse(str_map, format, SKSTRINGMAP_DUPES_ERROR,
                         &iter, &errmsg))
    {
        skAppPrintErr("Invalid %s: %s",
                      appOptions[OPT_TIMESTAMP_FORMAT].name, errmsg);
        goto END;
    }

    *out_flags = SKTIMESTAMP_NOMSEC;

    while (skStringMapIterNext(iter, &found_entry, NULL) == SK_ITERATOR_OK) {
        *out_flags |= found_entry->id;
        switch (found_entry->id) {
#if 0
          case SKTIMESTAMP_NOMSEC:
            break;
#endif
          case 0:
          case SKTIMESTAMP_EPOCH:
          case SKTIMESTAMP_ISO:
          case SKTIMESTAMP_MMDDYYYY:
            if (name_seen) {
                entry = timestamp_names;
                strncpy(buf, entry->name, sizeof(buf));
                for (++entry; entry->name; ++entry) {
                    strncat(buf, ",", sizeof(buf)-strlen(buf)-1);
                    strncat(buf, entry->name, sizeof(buf)-strlen(buf)-1);
                }
                skAppPrintErr("Invalid %s: May only specify one of %s",
                              appOptions[OPT_TIMESTAMP_FORMAT].name, buf);
                goto END;
            }
            name_seen = 1;
            break;

          case SKTIMESTAMP_UTC:
          case SKTIMESTAMP_LOCAL:
            if (zone_seen) {
                entry = timestamp_zones;
                strncpy(buf, entry->name, sizeof(buf));
                for (++entry; entry->name; ++entry) {
                    strncat(buf, ",", sizeof(buf)-strlen(buf)-1);
                    strncat(buf, entry->name, sizeof(buf)-strlen(buf)-1);
                }
                skAppPrintErr("Invalid %s: May only specify one of %s",
                              appOptions[OPT_TIMESTAMP_FORMAT].name, buf);
                goto END;
            }
            zone_seen = 1;
            break;

          default:
            skAbortBadCase(found_entry->id);
        }
    }

    rv = 0;

  END:
    if (str_map) {
        skStringMapDestroy(str_map);
    }
    if (iter) {
        skStringMapIterDestroy(iter);
    }
    return rv;
}


/*
 *  timestampFormatUsage(fh);
 *
 *    Print the description of the argument to the --timestamp-format
 *    switch to the 'fh' file handle.
 */
static void timestampFormatUsage(FILE *fh)
{
    const sk_stringmap_entry_t *e;
    const char *label;

    fprintf(fh, "Print times in specified format: Def. %s,%s\n",
            timestamp_names[0].name,
            timestamp_zones[(SK_ENABLE_LOCALTIME != 0)].name);

    label = "Format:";
    for (e = timestamp_names; e->name; ++e) {
        fprintf(fh, "\t%-10s%-8s - %s\n",
                label, e->name, (const char*)e->userdata);
        label = "";
    }
    label = "Timezone:";
    for (e = timestamp_zones; e->name; ++e) {
        fprintf(fh, "\t%-10s%-8s - %s\n",
                label, e->name, (const char*)e->userdata);
        label = "";
    }
#if 0
    label = "Misc:";
    for (e = timestamp_misc; e->name; ++e) {
        fprintf(fh, "\t%-10s%-8s - %s\n",
                label, e->name, (const char*)e->userdata);
        label = "";
    }
#endif  /* 0 */
}


/*
 *  helpFields(fh);
 *
 *    Print a description of each field to the 'fh' file pointer
 */
static void helpFields(
    FILE       *fh)
{
    fprintf(fh, "Currently unsupported.\n");
}


/*
 *  value_to_ascii(rwrec, buf, bufsize, field_entry, extra);
 *
 *    Invoked by rwAsciiPrintRecExtra() to get the value for an
 *    aggregate value field.  This function is called for built-in
 *    aggregate values as well as plug-in defined values.
 *
 *    Fill 'buf' with the value for the column represented by the
 *    aggregate value field list entry 'field_entry'.  'rwrec' is
 *    ignored; 'extra' is an array[3] that contains the buffers for
 *    the key, aggregate value, and distinct field-lists.  This
 *    function should write no more than 'bufsize' characters to
 *    'buf'.
 */
static int value_to_ascii(
    const rwRec UNUSED(*rwrec),
    char               *text_buf,
    size_t              text_buf_size,
    void               *v_fl_entry,
    void               *v_outbuf)
{
    sk_fieldentry_t *fl_entry = (sk_fieldentry_t*)v_fl_entry;
    uint64_t val64;
    uint32_t val32;
    size_t len = 0;

    switch (skFieldListEntryGetId(fl_entry)) {
      case SK_FIELD_SUM_BYTES:
      case SK_FIELD_SUM_PACKETS:
        skFieldListExtractFromBuffer(value_fields, ((uint8_t**)v_outbuf)[1],
                                     fl_entry, (uint8_t*)&val64);
        len =  snprintf(text_buf, text_buf_size, ("%" PRIu64), val64);
        break;

      case SK_FIELD_RECORDS:
      case SK_FIELD_SUM_ELAPSED:
        skFieldListExtractFromBuffer(value_fields, ((uint8_t**)v_outbuf)[1],
                                     fl_entry, (uint8_t*)&val32);
        len = snprintf(text_buf, text_buf_size, ("%" PRIu32), val32);
        break;

      case SK_FIELD_MIN_STARTTIME:
      case SK_FIELD_MAX_ENDTIME:
        skFieldListExtractFromBuffer(value_fields, ((uint8_t**)v_outbuf)[1],
                                     fl_entry, (uint8_t*)&val32);
        if (text_buf_size <= SKTIMESTAMP_STRLEN) {
            return SKTIMESTAMP_STRLEN;
        }
        len = strlen(
            sktimestamp_r(text_buf, sktimeCreate(val32, 0), time_flags));
        break;

      case SK_FIELD_CALLER:
      default:
        skAbortBadCase(skFieldListEntryGetId(fl_entry));
    }

    return (len > text_buf_size) ? text_buf_size : len;
}


/*
 *  distinct_to_ascii(rwrec, buf, bufsize, field_entry, extra);
 *
 *    Invoked by rwAsciiPrintRecExtra() to get the value for a
 *    distinct field.  This function is called for built-in distinct
 *    fields as well as those from a plug-in.
 *
 *    Fill 'buf' with the value for the column represented by the
 *    distinct field list entry 'field_entry'.  'rwrec' is ignored;
 *    'extra' is an array[3] that contains the buffers for the key,
 *    aggregate value, and distinct field-lists.  This function should
 *    write no more than 'bufsize' characters to 'buf'.
 */
static int distinct_to_ascii(
    const rwRec UNUSED(*rwrec),
    char               *text_buf,
    size_t              text_buf_size,
    void               *v_fl_entry,
    void               *v_outbuf)
{
    sk_fieldentry_t *fl_entry = (sk_fieldentry_t*)v_fl_entry;
    size_t len;
    union value_un {
        uint8_t   ar[HASHLIB_MAX_VALUE_WIDTH];
        uint64_t  u64;
        uint32_t  u32;
        uint16_t  u16;
        uint8_t   u8;
    } value;

    len = skFieldListEntryGetBinOctets(fl_entry);
    switch (len) {
      case 1:
        skFieldListExtractFromBuffer(distinct_fields, ((uint8_t**)v_outbuf)[2],
                                     fl_entry, &value.u8);
        len = snprintf(text_buf, text_buf_size, ("%" PRIu8), value.u8);
        break;
      case 2:
        skFieldListExtractFromBuffer(distinct_fields, ((uint8_t**)v_outbuf)[2],
                                     fl_entry, (uint8_t*)&value.u16);
        len = snprintf(text_buf, text_buf_size, ("%" PRIu16), value.u16);
        break;
      case 4:
        skFieldListExtractFromBuffer(distinct_fields, ((uint8_t**)v_outbuf)[2],
                                     fl_entry, (uint8_t*)&value.u32);
        len = snprintf(text_buf, text_buf_size, ("%" PRIu32), value.u32);
        break;
      case 8:
        skFieldListExtractFromBuffer(distinct_fields, ((uint8_t**)v_outbuf)[2],
                                     fl_entry, (uint8_t*)&value.u64);
        len = snprintf(text_buf, text_buf_size, ("%" PRIu64), value.u64);
        break;

      case 3:
      case 5:
      case 6:
      case 7:
        value.u64 = 0;
#if SK_BIG_ENDIAN
        skFieldListExtractFromBuffer(distinct_fields, ((uint8_t**)v_outbuf)[2],
                                     fl_entry, &value.ar[8 - len]);
#else
        skFieldListExtractFromBuffer(distinct_fields, ((uint8_t**)v_outbuf)[2],
                                     fl_entry, &value.ar[0]);
#endif  /* #else of #if SK_BIG_ENDIAN */
        len = snprintf(text_buf, text_buf_size, ("%" PRIu64), value.u64);
        break;

      default:
        skFieldListExtractFromBuffer(distinct_fields, ((uint8_t**)v_outbuf)[2],
                                     fl_entry, value.ar);
        len = snprintf(text_buf, text_buf_size, ("%" PRIu64), value.u64);
        break;
    }

    return (len > text_buf_size) ? text_buf_size : len;
}

/** Comparison functions for numbers  **/

#ifdef SK_HAVE_ALIGNED_ACCESS_REQUIRED
#define IMPLEMENT_NUM_COMPARE(_typ_)                    \
    static int                                          \
    field_ ## _typ_ ## _compare(                        \
        const uint8_t *a,                               \
        const uint8_t *b,                               \
        void          *UNUSED(ctx))                     \
    {                                                   \
        _typ_ av;                                       \
        _typ_ bv;                                       \
        memcpy(&av, a, sizeof(_typ_));                  \
        memcpy(&bv, b, sizeof(_typ_));                  \
        return (av < bv) ? -1 : ((av > bv) ? 1 : 0);    \
    }
#else
#define IMPLEMENT_NUM_COMPARE(_typ_)                            \
    static int                                                  \
    field_ ## _typ_ ## _compare(                                \
        const uint8_t *a,                                       \
        const uint8_t *b,                                       \
        void          *UNUSED(ctx))                             \
    {                                                           \
        const _typ_ *av = (_typ_ *)a;                           \
        const _typ_ *bv = (_typ_ *)b;                           \
        return (*av < *bv) ? -1 : ((*av > *bv) ? 1 : 0);        \
    }
#endif

IMPLEMENT_NUM_COMPARE(uint8_t)
IMPLEMENT_NUM_COMPARE(uint16_t)
IMPLEMENT_NUM_COMPARE(uint32_t)
IMPLEMENT_NUM_COMPARE(uint64_t)
IMPLEMENT_NUM_COMPARE(int8_t)
IMPLEMENT_NUM_COMPARE(int16_t)
IMPLEMENT_NUM_COMPARE(int32_t)
IMPLEMENT_NUM_COMPARE(int64_t)
IMPLEMENT_NUM_COMPARE(float)
IMPLEMENT_NUM_COMPARE(double)

/** Comparison functions for string-likes **/

#define IMPLEMENT_MEM_COMPARE(_name_, _cmp_)                            \
    static int                                                          \
    field_ ## _name_ ## _compare(                                       \
        const uint8_t *a,                                               \
        const uint8_t *b,                                               \
        void          *UNUSED(ctx))                                     \
    {                                                                   \
        size_t len;                                                     \
        fbVarfield_t av;                                                \
        fbVarfield_t bv;                                                \
        memcpy(&av, a, sizeof(const fbVarfield_t));                     \
        memcpy(&bv, b, sizeof(const fbVarfield_t));                     \
        len = (av.len < bv.len) ? av.len : bv.len;                      \
        return _cmp_((const char *)av.buf, (const char *)bv.buf, len);  \
    }

IMPLEMENT_MEM_COMPARE(octet, memcmp)
IMPLEMENT_MEM_COMPARE(string, strncmp)

static void
field_rec_to_bin(
    const rwRec *rec,
    uint8_t     *dest,
    void        *ctx);

/* fieldlist entrydata members for each IPFIX type */
static sk_fieldlist_entrydata_t entrydata[FB_IP6_ADDR + 1] = {
    {field_rec_to_bin, field_octet_compare, NULL, NULL, NULL, NULL,
     sizeof(fbVarfield_t)},
    {field_rec_to_bin, field_uint8_t_compare, NULL, NULL, NULL, NULL, 1},
    {field_rec_to_bin, field_uint16_t_compare, NULL, NULL, NULL, NULL, 2},
    {field_rec_to_bin, field_uint32_t_compare, NULL, NULL, NULL, NULL, 4},
    {field_rec_to_bin, field_uint64_t_compare, NULL, NULL, NULL, NULL, 8},
    {field_rec_to_bin, field_int8_t_compare, NULL, NULL, NULL, NULL, 1},
    {field_rec_to_bin, field_int16_t_compare, NULL, NULL, NULL, NULL, 2},
    {field_rec_to_bin, field_int32_t_compare, NULL, NULL, NULL, NULL, 4},
    {field_rec_to_bin, field_int64_t_compare, NULL, NULL, NULL, NULL, 8},
    {field_rec_to_bin, field_float_compare, NULL, NULL, NULL, NULL,
     sizeof(float)},
    {field_rec_to_bin, field_double_compare, NULL, NULL, NULL, NULL,
     sizeof(double)},
    {field_rec_to_bin, NULL, NULL, NULL, NULL, NULL, 1},
    {field_rec_to_bin, NULL, NULL, NULL, NULL, NULL, 6},
    {field_rec_to_bin, field_string_compare, NULL, NULL, NULL, NULL,
     sizeof(fbVarfield_t)},
    {field_rec_to_bin, field_uint64_t_compare, NULL, NULL, NULL, NULL,
     sizeof(sktime_t)},
    {field_rec_to_bin, field_uint64_t_compare, NULL, NULL, NULL, NULL,
     sizeof(sktime_t)},
    {field_rec_to_bin, field_uint64_t_compare, NULL, NULL, NULL, NULL,
     sizeof(sktime_t)},
    {field_rec_to_bin, field_uint64_t_compare, NULL, NULL, NULL, NULL,
     sizeof(sktime_t)},
    {field_rec_to_bin, field_uint32_t_compare, NULL, NULL, NULL, NULL, 4},
    {field_rec_to_bin, NULL, NULL, NULL, NULL, NULL, 16}
};


#define IPADDR_REC_TO_BIN(_name_)                                       \
    static void                                                         \
    _name_ ## _rec_to_bin(                                              \
        const rwRec *rec,                                               \
        uint8_t     *dest,                                              \
        void        *UNUSED(x))                                         \
    {                                                                   \
        skipaddr_t addr;                                                \
        schema_context_t *ctx = (schema_context_t *)sk_schema_get_context( \
            sk_record_get_schema(rec));                                 \
        const sk_field_t *v4 = ctx->_name_ ## v4;                       \
        const sk_field_t *v6 = ctx->_name_ ## v6;                       \
        if (v4 == NULL && v6 == NULL) {                                 \
            memset(dest, 0, 16);                                        \
            return;                                                     \
        }                                                               \
        if (v6 != NULL) {                                               \
            sk_record_get_ipv6_addr(rec, v6, dest);                     \
            if (!SK_IPV6_IS_ZERO(dest) || v4 == NULL) {                 \
                return;                                                 \
            }                                                           \
        }                                                               \
        sk_record_get_ip_address(rec, v4, &addr);                       \
        skipaddrV4toV6(&addr, &addr);                                   \
        skipaddrGetV6(&addr, dest);                                     \
        return;                                                         \
    }

IPADDR_REC_TO_BIN(sip)
IPADDR_REC_TO_BIN(dip)
IPADDR_REC_TO_BIN(nhip)



static const struct builtin_fields_st {
    const char               *name;
    sk_fieldlist_entrydata_t  entrydata;
} builtin_fields[] = {
    {"sIP", {sip_rec_to_bin, NULL, NULL, NULL, NULL, NULL, 16}},
    {"dIP", {dip_rec_to_bin, NULL, NULL, NULL, NULL, NULL, 16}},
    {"nhip", {nhip_rec_to_bin, NULL, NULL, NULL, NULL, NULL, 16}},
    {NULL, {NULL, NULL, NULL, NULL, NULL, NULL, 0}}
};


static void
field_rec_to_bin(
    const rwRec *rec,
    uint8_t     *dest,
    void        *x)
{
    const sk_field_t *f;
    schema_context_t *ctx = (schema_context_t *)sk_schema_get_context(
        sk_record_get_schema(rec));
    uintptr_t idx = (uintptr_t)x;
    uint8_t typ;

    sk_vector_get_value(ctx->key_fields, idx, &f);
    if (f == NULL) {
        sk_vector_get_value(uniq_ctx->key_fields, idx, &f);
        typ = sk_field_get_type(f);
        memset(dest, 0, entrydata[typ].bin_octets);
    }
    typ = sk_field_get_type(f);

    switch (typ) {
      case FB_UINT_8:
      case FB_UINT_16:
      case FB_UINT_32:
      case FB_UINT_64:
        sk_record_get_sized_uint(rec, f, dest, entrydata[typ].bin_octets);
        break;
      case FB_INT_8:
      case FB_INT_16:
      case FB_INT_32:
      case FB_INT_64:
        sk_record_get_sized_int(rec, f, dest, entrydata[typ].bin_octets);
        break;
      case FB_BOOL:
        {
            int b;
            sk_record_get_boolean(rec, f, &b);
            *dest = b;
        }
        break;
      case FB_FLOAT_32:
        {
            float fv;
            sk_record_get_float32(rec, f, &fv);
            memcpy(dest, &fv, sizeof(float));
        }
        break;
      case FB_FLOAT_64:
        {
            double dv;
            sk_record_get_float64(rec, f, &dv);
            memcpy(dest, &dv, sizeof(double));
        }
        break;
      case FB_IP4_ADDR:
        {
            uint32_t ip4;
            sk_record_get_ipv4_addr(rec, f, &ip4);
            memcpy(dest, &ip4, 4);
        }
        break;
      case FB_IP6_ADDR:
        sk_record_get_ipv6_addr(rec, f, dest);
        break;
      case FB_MAC_ADDR:
        sk_record_get_mac_address(rec, f, dest);
        break;
      case FB_DT_SEC:
      case FB_DT_MILSEC:
      case FB_DT_MICROSEC:
      case FB_DT_NANOSEC:
        {
            sktime_t t;
            sk_record_get_datetime(rec, f, &t);
            memcpy(dest, &t, sizeof(sktime_t));
        }
        break;
      case FB_STRING:
      case FB_OCTET_ARRAY:
        {
            uint16_t len;
            fbVarfield_t vf;
            sk_record_get_value_length(rec, f, &len);
            vf.buf = (uint8_t *)sk_alloc_bytes(len, SK_ALLOC_FLAG_NO_CLEAR);
            vf.len = len;
            sk_record_get_octets(rec, f, vf.buf, &len);
            sk_vector_append_value(varlen_data, &vf.buf);
            memcpy(dest, &vf, sizeof(fbVarfield_t));
        }
        break;
      default:
        skAbortBadCase(typ);
    }
}



static sk_stringmap_t *
createFieldStringmap(
    const fbInfoModel_t *model)
{
    char buf[32];
    sk_stringmap_t *map = NULL;
    fbInfoModelIter_t iter;
    const fbInfoElement_t *ie;
    sk_stringmap_id_t id;
    sk_stringmap_status_t err;
    sk_stringmap_entry_t entry;
    const field_alias_t *alias;

    if ((err = skStringMapCreate(&map))) {
        goto ERR;
    }

    /* Add IEs from the infomodel */
    fbInfoModelIterInit(&iter, model);
    for (id = 0; (ie = fbInfoModelIterNext(&iter)) != NULL; ++id) {
        if (ie->type > FB_IP6_ADDR) {
            /* Skip types we don't know how to use (list elements) */
            continue;
        }
        entry.name = ie->ref.name;
        entry.id = id;
        entry.description = ie->description;
        entry.userdata = ie;
        if ((err = skStringMapAddEntries(map, 1, &entry))) {
            goto ERR;
        }
        if (ie->ent) {
            snprintf(buf, sizeof(buf), "ie%" PRIu32 "/%" PRIu16,
                     ie->ent, ie->num);
        } else {
            snprintf(buf, sizeof(buf), "ie%" PRIu16, ie->num);
        }
        entry.name = buf;
        if ((err = skStringMapAddEntries(map, 1, &entry))) {
            goto ERR;
        }
    }

    /* Add simple aliases */
    for (alias = field_aliases; alias->alias; ++alias) {
        sk_stringmap_entry_t *e;
        if ((err = skStringMapGetByName(map, alias->ie_name, &e))) {
            goto ERR;
        }
        entry = *e;
        entry.name = alias->alias;
        if ((err = skStringMapAddEntries(map, 1, &entry))) {
            goto ERR;
        }
    }

    return map;

  ERR:
    skAppPrintErr("Could not create field string map: %s",
                  skStringMapStrerror(err));
    skStringMapDestroy(map);
    return NULL;
}

/*
 *  ok = createStringmaps();
 *
 *    Create the string-maps to assist in parsing the --fields and
 *    --values switches.
 */
static int createStringmaps(void)
{
    sk_stringmap_status_t  sm_err;
    sk_stringmap_entry_t   sm_entry;
    uint32_t               max_id;
    size_t                 i;
    size_t                 j;


    key_field_map = createFieldStringmap(infomodel);
    if (key_field_map == NULL) {
        return -1;
    }

    max_id = 0;

    /* create the string-map for value field identifiers */
    if (skStringMapCreate(&value_field_map)) {
        skAppPrintErr("Unable to create map for values");
        return -1;
    }

    /* add the built-in names */
    for (i = 0; i < num_builtin_values; ++i) {
        memset(&sm_entry, 0, sizeof(sk_stringmap_entry_t));
        sm_entry.name = builtin_values[i].bf_title;
        sm_entry.id = i;
        sm_entry.description = builtin_values[i].bf_description;
        sm_err = skStringMapAddEntries(value_field_map, 1, &sm_entry);
        if (sm_err) {
            skAppPrintErr("Unable to add value field named '%s': %s",
                          sm_entry.name, skStringMapStrerror(sm_err));
            return -1;
        }
        if (sm_entry.id > max_id) {
            max_id = sm_entry.id;
        }
    }

    /* add aliases for built-in fields */
    for (j = 0; builtin_value_aliases[j].ba_name; ++j) {
        for (i = 0; i < num_builtin_values; ++i) {
            if (builtin_value_aliases[j].ba_id == builtin_values[i].bf_id) {
                memset(&sm_entry, 0, sizeof(sk_stringmap_entry_t));
                sm_entry.name = builtin_value_aliases[j].ba_name;
                sm_entry.id = i;
                sm_err = skStringMapAddEntries(value_field_map, 1, &sm_entry);
                if (sm_err) {
                    skAppPrintErr("Unable to add value field named '%s': %s",
                                  sm_entry.name, skStringMapStrerror(sm_err));
                    return -1;
                }
                break;
            }
        }
        if (i == num_builtin_values) {
            skAppPrintErr("No field found with id %d",
                          builtin_value_aliases[j].ba_id);
            return -1;
        }
    }

    return 0;
}

/*
 *  status = parseKeyFields(field_string);
 *
 *    Parse the string that represents the key fields the user wishes
 *    to bin by, create and fill in the global sk_fieldlist_t
 *    'key_fields', and add columns to the sk_formatter_t.  Return 0
 *    on success or non-zero on error.
 */
static int parseKeyFields(
    const char         *field_string)
{
    sk_stringmap_iter_t *sm_iter = NULL;
    sk_stringmap_entry_t *sm_entry = NULL;

    /* return value; assume failure */
    int rv = -1;

    /* error message generated when parsing fields */
    char *errmsg;

    /* parse the --fields argument */
    if (skStringMapParse(key_field_map, field_string, SKSTRINGMAP_DUPES_ERROR,
                         &sm_iter, &errmsg))
    {
        skAppPrintErr("Invalid %s: %s",
                      appOptions[OPT_FIELDS].name, errmsg);
        goto END;
    }

    /* create the field-list */
    if (skFieldListCreate(&key_fields)) {
        skAppPrintErr("Unable to create key field list");
        goto END;
    }

    /* Create the field vectors */
    assert(key_fields_vec == NULL);
    key_fields_vec = sk_vector_create(sizeof(sk_field_ident_t));
    assert(distinct_fields_vec == NULL);
    distinct_fields_vec = sk_vector_create(sizeof(sk_field_ident_t));
    assert(uniq_schema == NULL);
    sk_schema_create(&uniq_schema, infomodel, NULL, 0);
    assert(uniq_ctx == NULL);
    uniq_ctx = sk_alloc(schema_context_t);
    uniq_ctx->key_fields = sk_vector_create(sizeof(const sk_field_t *));

    /* add the key fields to the field-list and to the ascii stream. */
    while (skStringMapIterNext(sm_iter, &sm_entry, NULL) == SK_ITERATOR_OK) {
        const sk_fieldlist_entrydata_t *fl_entry;
        const fbInfoElement_t *ie;
        sk_field_t *f;
        sk_field_ident_t id;
        sk_formatter_field_t *fmt;
        uintptr_t idx;

        /* Get the IE for this entry */
        ie = (const fbInfoElement_t *)sm_entry->userdata;
        assert(ie->type <= FB_IP6_ADDR);

        /* Add to the field list */
        fl_entry = &entrydata[ie->type];
        idx = sk_vector_get_count(key_fields_vec);
        skFieldListAddField(key_fields, fl_entry, (void *)idx);

        /* Add to the key fields vector and uniq schema */
        id = SK_FIELD_IDENT_CREATE(ie->ent, ie->num);
        sk_vector_append_value(key_fields_vec, &id);
        sk_schema_insert_field_by_ident(&f, uniq_schema, id, NULL, NULL);
        sk_vector_append_value(uniq_ctx->key_fields, &f);

        /* Add to the formatter */
        fmt = sk_formatter_add_ie(ipfix_fmt, ie);
        sk_formatter_field_set_title(ipfix_fmt, fmt, sm_entry->name);

        /* Set defaults */
        sk_formatter_field_set_timestamp_format(ipfix_fmt, fmt, time_flags);
        sk_formatter_field_set_ipaddr_format(ipfix_fmt, fmt,
                                             (skipaddr_flags_t)ip_format);

        /* Set integer flags for specific fields */
        if (app_flags.integer_sensors && id == SILK_FLOW_SENSOR) {
            sk_formatter_field_set_number_format(ipfix_fmt, fmt, 10);
        } else if (app_flags.integer_tcp_flags) {
            switch (id) {
              case TCP_CONTROL_BITS:
              case INITIAL_TCP_FLAGS:
              case UNION_TCP_FLAGS:
              case REVERSE_TCP_CONTROL_BITS:
              case REVERSE_INITIAL_TCP_FLAGS:
              case REVERSE_UNION_TCP_FLAGS:
                sk_formatter_field_set_number_format(ipfix_fmt, fmt, 10);
                break;
              default:
                break;
            }
        }
    }

    /* successful */
    rv = 0;

  END:
    if (rv != 0) {
        /* something went wrong.  clean up */
        if (key_fields) {
            skFieldListDestroy(&key_fields);
            key_fields = NULL;
        }
    }
    /* do standard clean-up */
    if (sm_iter != NULL) {
        skStringMapIterDestroy(sm_iter);
    }

    return rv;
}


/*
 *  ok = parseValueFields(field_string);
 *
 *    Parse the string that represents the aggregate value and
 *    distinct fields the user wishes to compute, create and fill in
 *    the global sk_fieldlist_t 'value_fields' and 'distinct_fields',
 *    and add columns to the rwAsciiStream.  Return 0 on success or
 *    non-zero on error.
 *
 *    Returns 0 on success, or non-zero on error.
 */
static int parseValueFields(
    const char         *value_string)
{
    char strbuf[1024];
    sk_stringmap_iter_t *sm_iter = NULL;
    sk_stringmap_entry_t *sm_entry;
    sk_stringmap_status_t sm_err;
    const char *sm_attr;
    size_t width;
    size_t i;

    /* to create a new --values switch */
    char *buf = NULL;
    size_t buf_size;

    /* return value; assume failure */
    int rv = -1;

    /* error message generated when parsing fields */
    char *errmsg;

    builtin_field_t *bf;
    sk_fieldentry_t *fl_entry;
    sk_fieldlist_entrydata_t *fl_data;
    const fbInfoElement_t *ie;
    sk_formatter_field_t *fmtfield;
    sk_field_ident_t id;
    sk_field_t *f;
    uintptr_t idx;

    if (ipv6_policy >= SK_IPV6POLICY_MIX) {
        /* change the field id of the distinct fields */
        for (i = 0, bf = builtin_values; i < num_builtin_values; ++i, ++bf) {
            switch (bf->bf_id) {
              case SK_FIELD_SIPv4:
                bf->bf_id = SK_FIELD_SIPv6;
                break;
              case SK_FIELD_DIPv4:
                bf->bf_id = SK_FIELD_DIPv6;
                break;
              default:
                break;
            }
        }
    }

    if (time_flags & SKTIMESTAMP_EPOCH) {
        /* Reduce width of the textual columns for the MIN_STARTTIME
         * and MAX_ENDTIME fields. */
        for (i = 0, bf = builtin_values; i < num_builtin_values; ++i, ++bf) {
            if ((bf->bf_id == SK_FIELD_MIN_STARTTIME)
                || (bf->bf_id == SK_FIELD_MAX_ENDTIME))
            {
                bf->bf_text_len = 10;
            }
        }
    }

    /*
     *    Handling the old style --bytes,--packets,etc switches and
     *    the new --values switch is a bit of a pain.
     *
     *    First, parse --values if it is provided.  If any --values
     *    fields are also specified as stand-alone switches (e.g.,
     *    --bytes), turn off the stand-alone switch.
     *
     *    If any stand-alone switch is still on, create a new --values
     *    switch that includes the names of the stand-alone switches.
     *    Or, if no --values and no stand-alone switches are given,
     *    fall-back to the default and count flow records.
     */

    /* parse the --values field list if given */
    if (value_string) {
        if (skStringMapParseWithAttributes(value_field_map, value_string,
                                           SKSTRINGMAP_DUPES_KEEP, &sm_iter,
                                           &errmsg))
        {
            skAppPrintErr("Invalid %s: %s",
                          appOptions[OPT_VALUES].name, errmsg);
            goto END;
        }

        /* turn off the --bytes,--packets,etc switches if they also appear
         * in the --values switch */
        while (skStringMapIterNext(sm_iter, &sm_entry, NULL)==SK_ITERATOR_OK) {
            if (sm_entry->id < num_builtin_values) {
                builtin_values[sm_entry->id].bf_switched_on = 0;
            }
        }

        skStringMapIterDestroy(sm_iter);
        sm_iter = NULL;
    }

    /* determine whether there are any active --bytes,--packets,etc
     * switches */
    buf_size = 0;
    for (i = 0, bf = builtin_values; i < num_builtin_values; ++i, ++bf) {
        if (bf->bf_switched_on) {
            buf_size += 2 + strlen(bf->bf_title);
        }
    }

    if (buf_size) {
        /* switches are active; create new --values switch */
        if (NULL == value_string) {
            buf = (char*)malloc(buf_size);
            if (!buf) {
                skAppPrintOutOfMemory(NULL);
                goto END;
            }
            buf[0] = '\0';
        } else {
            buf_size += 1 + strlen(value_string);
            buf = (char*)malloc(buf_size);
            if (!buf) {
                skAppPrintOutOfMemory(NULL);
                goto END;
            }
            strncpy(buf, value_string, buf_size);
        }
        for (i = 0, bf = builtin_values; i < num_builtin_values; ++i, ++bf) {
            if (bf->bf_switched_on) {
                strncat(buf, ",", 2);
                strncat(buf, bf->bf_title, buf_size - 1 - strlen(buf));
            }
        }
        value_string = buf;

    } else if (!value_string) {
        /* no --values switch and no --bytes,--packets,etc switches,
         * so count flow records */
        for (i = 0, bf = builtin_values; i < num_builtin_values; ++i, ++bf) {
            if (SK_FIELD_RECORDS == bf->bf_id) {
                value_string = bf->bf_title;
                break;
            }
        }
    }

    /* parse the --values field list */
    if (skStringMapParseWithAttributes(value_field_map, value_string,
                                       SKSTRINGMAP_DUPES_KEEP, &sm_iter,
                                       &errmsg))
    {
        skAppPrintErr("Invalid %s: %s",
                      appOptions[OPT_VALUES].name, errmsg);
        goto END;
    }

    /* create the field-lists */
    if (skFieldListCreate(&value_fields)) {
        skAppPrintErr("Unable to create value field list");
        goto END;
    }
    if (skFieldListCreate(&distinct_fields)) {
        skAppPrintErr("Unable to create distinct field list");
        goto END;
    }

    /* loop over the selected values */
    while (skStringMapIterNext(sm_iter, &sm_entry, &sm_attr)
           == SK_ITERATOR_OK)
    {
        assert(sm_entry->id < num_builtin_values);
        bf = &builtin_values[sm_entry->id];
        if (0 == bf->bf_is_distinct) {
            /* this is a built-in values field; must have no attribute */
            if (sm_attr[0]) {
                skAppPrintErr("Invalid %s: Unrecognized field '%s:%s'",
                              appOptions[OPT_VALUES].name,
                              bf->bf_title, sm_attr);
                goto END;
            }
            if (isFieldDuplicate(value_fields, bf->bf_id, NULL)) {
                skAppPrintErr("Invalid %s: Duplicate name '%s'",
                              appOptions[OPT_VALUES].name, bf->bf_title);
                goto END;
            }
            fl_entry = skFieldListAddKnownField(value_fields, bf->bf_id, bf);
            if (NULL == fl_entry) {
                skAppPrintErr("Cannot add value field '%s' to field list",
                              sm_entry->name);
                goto END;
            }
            if (!(fmtfield = sk_formatter_add_extra_field(
                      ipfix_fmt, &value_to_ascii,
                      fl_entry, bf->bf_text_len)))
            {
                skAppPrintErr("Cannot add value field '%s' to stream",
                              sm_entry->name);
                goto END;
            }
            sk_formatter_field_set_title(ipfix_fmt, fmtfield, bf->bf_title);
        } else if (SK_FIELD_CALLER != bf->bf_id) {
            /* one of the old sip-distinct,dip-distinct fields; must
             * have no attribute */
            if (sm_attr[0]) {
                skAppPrintErr("Invalid %s: Unrecognized field '%s:%s'",
                              appOptions[OPT_VALUES].name,
                              bf->bf_title, sm_attr);
                goto END;
            }
            /* is this a duplicate field? */
            if (isFieldDuplicate(distinct_fields, bf->bf_id, NULL)) {
                skAppPrintErr("Invalid %s: Duplicate name '%s'",
                              appOptions[OPT_VALUES].name, bf->bf_title);
                goto END;
            }
            fl_entry = skFieldListAddKnownField(distinct_fields,
                                                bf->bf_id,bf);
            if (NULL == fl_entry) {
                skAppPrintErr("Cannot add distinct field '%s' to field list",
                              sm_entry->name);
                goto END;
            }
            /* FIXME: Unfinished */
#if 0
            if (rwAsciiAppendCallbackFieldExtra(ascii_str,
                                                &builtin_distinct_get_title,
                                                &distinct_to_ascii,
                                                fl_entry, bf->bf_text_len))
            {
                skAppPrintErr("Cannot add distinct field '%s' to stream",
                              sm_entry->name);
                goto END;
            }
#endif
        } else {
            /* got a distinct:KEY field */
            if (!sm_attr[0]) {
                skAppPrintErr(("Invalid %s:"
                               " The distinct value requires a field"),
                              appOptions[OPT_VALUES].name);
                goto END;
            }
            /* need to parse KEY as a key field */
            sm_err = skStringMapGetByName(key_field_map, sm_attr, &sm_entry);
            if (sm_err) {
                if (strchr(sm_attr, ',')) {
                    skAppPrintErr(("Invalid %s:"
                                   " May only distinct over a single field"),
                                  appOptions[OPT_VALUES].name);
                } else {
                    skAppPrintErr("Invalid %s: Bad distinct field '%s': %s",
                                  appOptions[OPT_VALUES].name, sm_attr,
                                  skStringMapStrerror(sm_err));
                }
                goto END;
            }
            assert(sm_entry->userdata);
            /* distinct:KEY */
            if (isFieldDuplicate(distinct_fields, SK_FIELD_CALLER,
                                 sm_entry->userdata))
            {
                skAppPrintErr("Invalid %s: Duplicate distinct '%s'",
                              appOptions[OPT_VALUES].name, sm_entry->name);
                goto END;
            }
            ie = (const fbInfoElement_t *)sm_entry->userdata;
            assert(ie->type <= FB_IP6_ADDR);
            fl_data = &entrydata[ie->type];
            idx = sk_vector_get_count(key_fields_vec);
            fl_entry = skFieldListAddField(
                distinct_fields, fl_data, (void *)idx);
            id = SK_FIELD_IDENT_CREATE(ie->ent, ie->num);
            sk_vector_append_value(key_fields_vec, &id);
            sk_schema_insert_field_by_ident(&f, uniq_schema, id, NULL, NULL);
            sk_vector_append_value(uniq_ctx->key_fields, &f);

            switch (ie->len) {
              case 1:
                width = 3;
                break;
              case 2:
                width = 5;
                break;
              case 3:
              case 4:
                width = 10;
                break;
              default:
                width = 20;
                break;
            }
            if (!(fmtfield = sk_formatter_add_extra_field(
                      ipfix_fmt, distinct_to_ascii, fl_entry, width)))
            {
                skAppPrintErr("Cannot add distinct field '%s' from plugin",
                              sm_entry->name);
                goto END;
            }
            snprintf(strbuf, sizeof(buf), "%s%s",
                     ie->ref.name, DISTINCT_SUFFIX);
            strbuf[sizeof(buf) - 1] = '\0';
            sk_formatter_field_set_title(ipfix_fmt, fmtfield, strbuf);
        }
    }

    sk_schema_freeze(uniq_schema);
    sk_record_create(&uniq_rec, uniq_schema);

    rv = 0;

  END:
    /* do standard clean-up */
    if (sm_iter) {
        skStringMapIterDestroy(sm_iter);
    }
    if (buf) {
        free(buf);
    }
    if (rv != 0) {
        /* something went wrong. do additional clean-up */
        if (value_fields) {
            skFieldListDestroy(&value_fields);
            value_fields = NULL;
        }
        if (distinct_fields) {
            skFieldListDestroy(&distinct_fields);
            distinct_fields = NULL;
        }
    }

    return rv;
}


/*
 *  is_duplicate = isFieldDuplicate(flist, fid, fcontext);
 *
 *    Return 1 if the field-id 'fid' appears in the field-list
 *    'flist'.  If 'fid' is SK_FIELD_CALLER, return 1 when a field in
 *    'flist' has the id SK_FIELD_CALLER and its context object points
 *    to 'fcontext'.  Return 0 otherwise.
 *
 *    In this function, IPv4 and IPv6 fields are considered
 *    equivalent; that is, you cannot have both SK_FIELD_SIPv4 and
 *    SK_FIELD_SIPv6, and multiple SK_FIELD_CALLER fields are allowed.
 */
static int  isFieldDuplicate(
    const sk_fieldlist_t   *flist,
    sk_fieldid_t            fid,
    const void             *fcontext)
{
    sk_fieldlist_iterator_t fl_iter;
    sk_fieldentry_t *fl_entry;

    skFieldListIteratorBind(flist, &fl_iter);
    switch (fid) {
      case SK_FIELD_SIPv4:
      case SK_FIELD_SIPv6:
        while ((fl_entry = skFieldListIteratorNext(&fl_iter)) != NULL) {
            switch (skFieldListEntryGetId(fl_entry)) {
              case SK_FIELD_SIPv4:
              case SK_FIELD_SIPv6:
                return 1;
              default:
                break;
            }
        }
        break;

      case SK_FIELD_DIPv4:
      case SK_FIELD_DIPv6:
        while ((fl_entry = skFieldListIteratorNext(&fl_iter)) != NULL) {
            switch (skFieldListEntryGetId(fl_entry)) {
              case SK_FIELD_DIPv4:
              case SK_FIELD_DIPv6:
                return 1;
              default:
                break;
            }
        }
        break;

      case SK_FIELD_NHIPv4:
      case SK_FIELD_NHIPv6:
        while ((fl_entry = skFieldListIteratorNext(&fl_iter)) != NULL) {
            switch (skFieldListEntryGetId(fl_entry)) {
              case SK_FIELD_NHIPv4:
              case SK_FIELD_NHIPv6:
                return 1;
              default:
                break;
            }
        }
        break;

      case SK_FIELD_CALLER:
        while ((fl_entry = skFieldListIteratorNext(&fl_iter)) != NULL) {
            if ((skFieldListEntryGetId(fl_entry) == (uint32_t)fid)
                && (skFieldListEntryGetContext(fl_entry) == fcontext))
            {
                return 1;
            }
        }
        break;

      default:
        while ((fl_entry = skFieldListIteratorNext(&fl_iter)) != NULL) {
            if (skFieldListEntryGetId(fl_entry) == (uint32_t)fid) {
                return 1;
            }
        }
        break;
    }
    return 0;
}

static void
schemaContextDestroy(
    void *vctx)
{
    schema_context_t *ctx = (schema_context_t *)vctx;
    if (ctx) {
        sk_record_destroy(ctx->rec);
        sk_schema_timemap_destroy(ctx->timemap);
        sk_schemamap_destroy(ctx->map);
        sk_vector_destroy(ctx->key_fields);
        free(ctx);
    }
}

static void
schemaCallback(
    sk_schema_t *schema,
    uint16_t     UNUSED(tid),
    void        *UNUSED(cbdata))
{
    schema_context_t *ctx;
    sk_field_ident_t id;
    const sk_field_t *f;
    sk_schema_t *new_schema;
    size_t i;
    int rv;

    /* Make sure there isn't already a context */
    assert(sk_schema_get_context(schema) == NULL);

    /* Allocate a context */
    ctx = sk_alloc(schema_context_t);

    ctx->key_fields = sk_vector_create(sizeof(const sk_field_t *));
    sk_vector_set_capacity(ctx->key_fields,
                           sk_vector_get_count(key_fields_vec));

    rv = sk_schema_copy(&new_schema, schema);
    if (rv) {
        skAppPrintErr("Cannot copy schema: %s", sk_schema_strerror(rv));
        exit(EXIT_FAILURE);
    }

    /* Ensure time fields */
    rv = sk_schema_timemap_create(&ctx->timemap, new_schema);
    if (rv) {
        skAppPrintErr("Cannot create timemap: %s", sk_schema_strerror(rv));
        exit(EXIT_FAILURE);
    }

    /* Populate key fields */
    for (i = 0; i < sk_vector_get_count(key_fields_vec); ++i) {
        sk_vector_get_value(key_fields_vec, i, &id);
        f = sk_schema_get_field_by_ident(new_schema, id, NULL);
        sk_vector_append_value(ctx->key_fields, &f);
    }

    /* Determine time fields */
    ctx->stime = sk_schema_get_field_by_ident(
        new_schema, FLOW_START_MILLISECONDS, NULL);
    assert(ctx->stime);
    ctx->etime = sk_schema_get_field_by_ident(
        new_schema, FLOW_END_MILLISECONDS, NULL);
    assert(ctx->etime);

    /* Determine bytes field */
    ctx->bytes = sk_schema_get_field_by_ident(
        new_schema, OCTET_DELTA_COUNT, NULL);
    if (ctx->bytes == NULL) {
        ctx->bytes = sk_schema_get_field_by_ident(
            new_schema, OCTET_TOTAL_COUNT, NULL);
    }

    /* Determine packets field */
    ctx->packets = sk_schema_get_field_by_ident(
        new_schema, PACKET_DELTA_COUNT, NULL);
    if (ctx->packets == NULL) {
        ctx->packets = sk_schema_get_field_by_ident(
            new_schema, PACKET_TOTAL_COUNT, NULL);
    }

    /* Determine IP address fields */
    ctx->sipv4 = sk_schema_get_field_by_ident(
        new_schema, SOURCE_IPV4_ADDRESS, NULL);
    ctx->dipv4 = sk_schema_get_field_by_ident(
        new_schema, DESTINATION_IPV4_ADDRESS, NULL);
    ctx->nhipv4 = sk_schema_get_field_by_ident(
        new_schema, IP_NEXT_HOP_IPV4_ADDRESS, NULL);
    ctx->sipv6 = sk_schema_get_field_by_ident(
        new_schema, SOURCE_IPV6_ADDRESS, NULL);
    ctx->dipv6 = sk_schema_get_field_by_ident(
        new_schema, DESTINATION_IPV6_ADDRESS, NULL);
    ctx->nhipv6 = sk_schema_get_field_by_ident(
        new_schema, IP_NEXT_HOP_IPV6_ADDRESS, NULL);

    rv = sk_schema_freeze(new_schema);
    if (rv) {
        skAppPrintErr("Cannot freeze schema: %s", sk_schema_strerror(rv));
        exit(EXIT_FAILURE);
    }

    rv = sk_schemamap_create_across_schemas(&ctx->map, new_schema, schema);
    if (rv) {
        skAppPrintErr("Cannot create mapping: %s", sk_schema_strerror(rv));
        exit(EXIT_FAILURE);
    }

    rv = sk_record_create(&ctx->rec, new_schema);
    if (rv) {
        skAppPrintErr("Cannot create record: %s", sk_schema_strerror(rv));
        exit(EXIT_FAILURE);
    }

    sk_schema_destroy(new_schema);

    /* Set the context */
    sk_schema_set_context(schema, ctx, schemaContextDestroy);
    sk_schema_set_context(new_schema, ctx, NULL);
}


static sktime_t
getSTimeFn(
    const rwRec *rwrec,
    void *UNUSED(x))
{
    schema_context_t *ctx;
    sktime_t t;
    ctx = (schema_context_t *)sk_schema_get_context(
        sk_record_get_schema(rwrec));
    if (ctx->stime) {
        sk_record_get_datetime(rwrec, ctx->stime, &t);
        return t;
    }
    return 0;
}

static sktime_t
getETimeFn(
    const rwRec *rwrec,
    void *UNUSED(x))
{
    schema_context_t *ctx;
    sktime_t t;
    ctx = (schema_context_t *)sk_schema_get_context(
        sk_record_get_schema(rwrec));
    if (ctx->etime) {
        sk_record_get_datetime(rwrec, ctx->etime, &t);
        return t;
    }
    return 0;
}

static sktime_t
getElapsedFn(
    const rwRec *rwrec,
    void *UNUSED(x))
{
    schema_context_t *ctx;
    sktime_t s, e;
    ctx = (schema_context_t *)sk_schema_get_context(
        sk_record_get_schema(rwrec));
    if (ctx->stime && ctx->etime) {
        sk_record_get_datetime(rwrec, ctx->stime, &s);
        sk_record_get_datetime(rwrec, ctx->etime, &e);
        return e - s;
    }
    return 0;
}

static uint64_t
getBytesFn(
    const rwRec *rwrec,
    void *UNUSED(x))
{
    schema_context_t *ctx;
    uint64_t val;
    ctx = (schema_context_t *)sk_schema_get_context(
        sk_record_get_schema(rwrec));
    if (ctx->bytes) {
        sk_record_get_unsigned64(rwrec, ctx->bytes, &val);
        return val;
    }
    return 0;
}

static uint64_t
getPacketsFn(
    const rwRec *rwrec,
    void *UNUSED(x))
{
    schema_context_t *ctx;
    uint64_t val;
    ctx = (schema_context_t *)sk_schema_get_context(
        sk_record_get_schema(rwrec));
    if (ctx->packets) {
        sk_record_get_unsigned64(rwrec, ctx->packets, &val);
        return val;
    }
    return 0;
}

static int
prepareFileForRead(
    skstream_t         *rwios)
{
    int rv;
    if (app_flags.print_filenames) {
        fprintf(PRINT_FILENAMES_FH, "%s\n", skStreamGetPathname(rwios));
    }
    if (copy_input) {
        skStreamSetCopyInput(rwios, copy_input);
    }
    skStreamSetIPv6Policy(rwios, ipv6_policy);
    rv = skStreamSetNewSchemaCallback(rwios, schemaCallback, NULL);
    assert(rv == 0);

    return 0;
}


/*
 *  int = appNextInput(&rwios);
 *
 *    Fill 'rwios' with the next input file to read.  Return 0 if
 *    'rwios' was successfully opened, 1 if there are no more input
 *    files, or -1 if an error was encountered.
 */
int appNextInput(
    skstream_t        **rwios)
{
    char *path = NULL;
    int rv;

    rv = skOptionsCtxNextArgument(optctx, &path);
    if (0 == rv) {
        if ((rv = skStreamCreate(rwios, SK_IO_READ, SK_CONTENT_UNKNOWN_FLOW))
            || (rv = skStreamBind(*rwios, path))
            || (rv = skStreamOpen(*rwios)))
        {
            skStreamPrintLastErr(*rwios, rv, &skAppPrintErr);
            skStreamDestroy(rwios);
            return -1;
        }

        (void)prepareFileForRead(*rwios);
    }

    return rv;
}


/*
 *  setOutputHandle();
 *
 *    If using the pager, enable it and bind it to the Ascii stream.
 */
void setOutputHandle(void)
{
    int rv;

    /* only invoke the pager when the user has not specified the
     * output-path, even if output-path is stdout */
    if (NULL == output.of_name) {
        /* invoke the pager */
        rv = skFileptrOpenPager(&output, pager);
        if (rv && rv != SK_FILEPTR_PAGER_IGNORED) {
            skAppPrintErr("Unable to invoke pager");
        }
    }
}


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

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


    M-x c-toggle-parse-state-debug
    C-x C-f rwuniqsetup.c.before.2
    C-s ipaddr_rec_to_bin RET
    C-v

At this point you should see the following in *Messages*:

    c-parse-state inconsistency at 46940: using cache: nil, from scratch: ((43406 . 45002))
    Old state:
    (setq c-state-cache nil  c-state-cache-good-pos 45713  c-state-nonlit-pos-cache '(76939 73939 70939 67939 64939 61939 58939 55939 52939 49939 46939 43114 39291 36291 33291 30291 27178 24178 21178 18178 15145 12046 9013 6002 3002)  c-state-nonlit-pos-cache-limit 76939  c-state-semi-nonlit-pos-cache '(45291 42291 39291 36291 33291 30291 27094 24058 21058 18058 15058 12046 9013 6001 3001)  c-state-semi-nonlit-pos-cache-limit 45291  c-state-brace-pair-desert '(1 . 46940)  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 46940)

At this point, indentation is having problems.  Try hitting TAB on line
1371, for example.  Or line 1374 for a complete failure to indent at
all.


Emacs  : GNU Emacs 24.3.94.1 (x86_64-unknown-linux-gnu, X toolkit)
 of 2014-10-16 on maru2
Package: CC Mode 5.32.5 (C/l)
Buffer Style: gnu
c-emacs-features: (pps-extended-state col-0-paren posix-char-classes gen-string-delim gen-comment-delim syntax-properties 1-bit)

current state:
==============
(setq
 c-basic-offset 4
 c-comment-only-line-offset '(0 . 0)
 c-indent-comment-alist '((anchored-comment column . 0) (end-block space . 1)
			  (cpp-end-block space . 2))
 c-indent-comments-syntactically-p nil
 c-block-comment-prefix ""
 c-comment-prefix-regexp '((pike-mode . "//+!?\\|\\**") (awk-mode . "#+")
			   (other . "//+\\|\\**"))
 c-doc-comment-style '((java-mode . javadoc) (pike-mode . autodoc)
		       (c-mode . gtkdoc))
 c-cleanup-list '(scope-operator)
 c-hanging-braces-alist '((substatement-open before after)
			  (arglist-cont-nonempty))
 c-hanging-colons-alist nil
 c-hanging-semi&comma-criteria '(c-semi&comma-inside-parenlist)
 c-backslash-column 48
 c-backslash-max-column 72
 c-special-indent-hook '(c-gnu-impose-minimum)
 c-label-minimum-indentation 1
 c-offsets-alist '((inexpr-class . +)
		   (inexpr-statement . +)
		   (lambda-intro-cont . +)
		   (inlambda . c-lineup-inexpr-block)
		   (template-args-cont c-lineup-template-args +)
		   (incomposition . +)
		   (inmodule . +)
		   (innamespace . +)
		   (inextern-lang . +)
		   (composition-close . 0)
		   (module-close . 0)
		   (namespace-close . 0)
		   (extern-lang-close . 0)
		   (composition-open . 0)
		   (module-open . 0)
		   (namespace-open . 0)
		   (extern-lang-open . 0)
		   (objc-method-call-cont
		    c-lineup-ObjC-method-call-colons
		    c-lineup-ObjC-method-call
		    +
		    )
		   (objc-method-args-cont . c-lineup-ObjC-method-args)
		   (objc-method-intro . [0])
		   (friend . 0)
		   (cpp-define-intro c-lineup-cpp-define +)
		   (cpp-macro-cont . +)
		   (cpp-macro . [0])
		   (inclass . +)
		   (stream-op . c-lineup-streamop)
		   (arglist-cont-nonempty
		    c-lineup-gcc-asm-reg
		    c-lineup-arglist
		    )
		   (arglist-cont c-lineup-gcc-asm-reg 0)
		   (comment-intro
		    c-lineup-knr-region-comment
		    c-lineup-comment
		    )
		   (catch-clause . 0)
		   (else-clause . 0)
		   (do-while-closure . 0)
		   (access-label . -)
		   (case-label . 0)
		   (substatement . +)
		   (statement-case-intro . +)
		   (statement . 0)
		   (brace-entry-open . 0)
		   (brace-list-entry . 0)
		   (brace-list-intro . +)
		   (brace-list-close . 0)
		   (block-close . 0)
		   (block-open . 0)
		   (inher-cont . c-lineup-multi-inher)
		   (inher-intro . +)
		   (member-init-cont . c-lineup-multi-inher)
		   (member-init-intro . +)
		   (annotation-var-cont . +)
		   (annotation-top-cont . 0)
		   (topmost-intro . 0)
		   (knr-argdecl . 0)
		   (func-decl-cont . +)
		   (inline-close . 0)
		   (class-close . 0)
		   (class-open . 0)
		   (defun-block-intro . +)
		   (defun-close . 0)
		   (defun-open . 0)
		   (c . c-lineup-C-comments)
		   (string . c-lineup-dont-change)
		   (topmost-intro-cont
		    first
		    c-lineup-topmost-intro-cont
		    c-lineup-gnu-DEFUN-intro-cont
		    )
		   (brace-list-open . +)
		   (inline-open . 0)
		   (arglist-close . c-lineup-arglist)
		   (arglist-intro . c-lineup-arglist-intro-after-paren)
		   (statement-cont . +)
		   (statement-case-open . +)
		   (label . 0)
		   (substatement-label . 0)
		   (substatement-open . +)
		   (knr-argdecl-intro . 5)
		   (statement-block-intro . +)
		   )
 c-buffer-is-cc-mode 'c-mode
 c-tab-always-indent t
 c-syntactic-indentation t
 c-syntactic-indentation-in-macros t
 c-ignore-auto-fill '(string cpp code)
 c-auto-align-backslashes t
 c-backspace-function 'backward-delete-char-untabify
 c-delete-function 'delete-char
 c-electric-pound-behavior nil
 c-default-style '((java-mode . "java") (awk-mode . "awk") (other . "gnu"))
 c-enable-xemacs-performance-kludge-p nil
 c-old-style-variable-behavior nil
 defun-prompt-regexp nil
 tab-width 8
 comment-column 32
 parse-sexp-ignore-comments t
 parse-sexp-lookup-properties t
 auto-fill-function nil
 comment-multi-line t
 comment-start-skip "\\(//+\\|/\\*+\\)\\s *"
 fill-prefix nil
 fill-column 70
 paragraph-start "[ 	]*\\(//+\\|\\**\\)[ 	]*$\\|^\f"
 adaptive-fill-mode t
 adaptive-fill-regexp "[ 	]*\\(//+\\|\\**\\)[ 	]*\\([ 	]*\\([-–!|#%;>*·•‣⁃◦]+[ 	]*\\)*\\)"
 )

In GNU Emacs 24.3.94.1 (x86_64-unknown-linux-gnu, X toolkit)
 of 2014-10-16 on maru2
Repository revision: 117589 rgm@gnu.org-20141016014710-zqpn29rkvl69dg14
Windowing system distributor `The X.Org Foundation', version 11.0.11601000
System Description:	Debian GNU/Linux unstable (sid)

Configured using:
 `configure --without-toolkit-scroll-bars --with-x-toolkit=lucid
 'CFLAGS=-Og -ggdb' --with-wide-int'

Important settings:
  value of $LANG: en_US.UTF-8
  locale-coding-system: utf-8-unix

Major mode: Message

Minor modes in effect:
  mml-mode: t
  tooltip-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  tool-bar-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  line-number-mode: t
  auto-fill-function: message-do-auto-fill
  transient-mark-mode: t
  abbrev-mode: t

Recent input:
C-x C-f ~ / t m <tab> <tab> b <tab> <return> C-s _ 
C O M A P <backspace> <backspace> P C-g C-g C-x k <return> 
M-x c - t o <tab> p <tab> <return> C-x C-f M-p <return> 
C-s _ C O M P A R E <return> C-v C-v C-v C-v <help-echo> 
M-x c - r e p <tab> <return> y S t a t e SPC c a c 
h e SPC f a i l u r e SPC l e a d i n g SPC t o SPC 
b a d SPC i n d e n t a t i o n <return> C-n C-n C-n 
C-p C-p C-x 1 C-p C-p C-p C-p C-e M-b M-b <M-backspace> 
C-n C-n C-p C-p C-p C-a C-v C-v C-v C-v <escape> < 
M-x r e p o r t - e m <tab> <return>

Recent messages:
Old state:
(setq c-state-cache '(47851 47758 47502 (47151 . 47403))  c-state-cache-good-pos 47852  c-state-nonlit-pos-cache '(76939 73939 70939 67939 64939 61939 58939 55939 52939 49939 46939 43114 39291 36291 33291 30291 27178 24178 21178 18178 15145 12046 9013 6002 3002)  c-state-nonlit-pos-cache-limit 76939  c-state-semi-nonlit-pos-cache '(45291 42291 39291 36291 33291 30291 27094 24058 21058 18058 15058 12046 9013 6001 3001)  c-state-semi-nonlit-pos-cache-limit 45291  c-state-brace-pair-desert nil  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 47852)
c-parse-state inconsistency at 47350: using cache: ((47287 . 47348) 47151 (47051 . 47131)), from scratch: ((43406 . 45002))
Old state:
(setq c-state-cache '((47287 . 47348) 47151 (47051 . 47131))  c-state-cache-good-pos 47348  c-state-nonlit-pos-cache '(76939 73939 70939 67939 64939 61939 58939 55939 52939 49939 46939 43114 39291 36291 33291 30291 27178 24178 21178 18178 15145 12046 9013 6002 3002)  c-state-nonlit-pos-cache-limit 76939  c-state-semi-nonlit-pos-cache '(45291 42291 39291 36291 33291 30291 27094 24058 21058 18058 15058 12046 9013 6001 3001)  c-state-semi-nonlit-pos-cache-limit 45291  c-state-brace-pair-desert nil  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 47350)
Do you want to submit a report on CC Mode? (y or n) y
Parsing /home/md5i/.mailrc... done
Mark set
Formatting bug report buffer.......
Please enter your report.  Type C-c C-c to send, C-c C-k to abort.

Load-path shadows:
None found.

Features:
(shadow sort mail-extr emacsbug sendmail gnus-util message format-spec
rfc822 mml mml-sec mm-decode mm-bodies mm-encode mail-parse rfc2231
rfc2047 rfc2045 ietf-drums mm-util help-fns mail-prsvr mailabbrev
mail-utils gmm-utils mailheader reporter misearch multi-isearch cc-langs
cl-loaddefs cl-lib cc-mode cc-fonts easymenu cc-guess cc-menus cc-cmds
cc-styles cc-align cc-engine cc-vars cc-defs time-date tooltip electric
uniquify ediff-hook vc-hooks lisp-float-type mwheel x-win x-dnd tool-bar
dnd fontset image regexp-opt fringe tabulated-list newcomment lisp-mode
prog-mode register page menu-bar rfn-eshadow timer select scroll-bar
mouse jit-lock font-lock syntax facemenu font-core frame cham georgian
utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao korean
japanese hebrew greek romanian slovak czech european ethiopic indian
cyrillic chinese case-table epa-hook jka-cmpr-hook help simple abbrev
minibuffer nadvice loaddefs button faces cus-face macroexp files
text-properties overlay sha1 md5 base64 format env code-pages mule
custom widget hashtable-print-readable backquote make-network-process
dbusbind gfilenotify dynamic-setting system-font-setting
font-render-setting x-toolkit x multi-tty emacs)

Memory information:
((conses 16 110171 9329)
 (symbols 48 20183 0)
 (miscs 40 172 211)
 (strings 32 17591 3830)
 (string-bytes 1 597205)
 (vectors 16 12254)
 (vector-slots 8 419544 7579)
 (floats 8 67 354)
 (intervals 56 1339 33)
 (buffers 960 15)
 (heap 1024 19484 929))



-- 
Michael Welsh Duggan
(md5i@md5i.com)

^ permalink raw reply	[flat|nested] 7+ messages in thread

* bug#18749: 24.3.94; CC Mode 5.32.5 (C/l); cc-mode state cache failure leading to bad indentation
  2014-10-16 21:12 bug#18749: 24.3.94; CC Mode 5.32.5 (C/l); cc-mode state cache failure leading to bad indentation Michael Welsh Duggan
@ 2014-10-17 18:19 ` Alan Mackenzie
  2014-10-17 19:35   ` Stefan Monnier
  2014-10-17 20:12   ` Alan Mackenzie
       [not found] ` <mailman.11430.1413622526.1147.bug-gnu-emacs@gnu.org>
  1 sibling, 2 replies; 7+ messages in thread
From: Alan Mackenzie @ 2014-10-17 18:19 UTC (permalink / raw)
  To: Michael Welsh Duggan; +Cc: 18749

Hello, Michael.

It's been a fun day.  ;-)

In article <87h9z367nq.fsf@maru2.md5i.com> you wrote:
> [-- text/plain, encoding 7bit, charset: US-ASCII, 6 lines --]

> A consistent c-mode indentation error that causes bad indentation, much
> cursing, and gnashing of teeth.  This occurs in the latest emacs-24
> branch (and trunk).

> From emacs -Q, using the attached file:

> [-- text/plain, encoding 7bit, charset: US-ASCII, 2434 lines, name: rwuniqsetup.c.before.2 --]

[ .... ]

> [-- text/plain, encoding quoted-printable, charset: utf-8, 291 lines --]


>     M-x c-toggle-parse-state-debug
>     C-x C-f rwuniqsetup.c.before.2
>     C-s ipaddr_rec_to_bin RET
>     C-v

> At this point you should see the following in *Messages*:

[ The following has been altered from the original to show the correct
  first inconsistency which occurred.]
> Mark saved where search started
> c-parse-state inconsistency at 46719: using cache: ((46217 . 46572) 45454 (43406 . 45002)), from scratch: (45454 (43406 . 45002))
> Old state:
> (setq c-state-cache '((46217 . 46572) 45454 (43406 . 45002))  c-state-cache-good-pos 46747  c-state-nonlit-pos-cache '(76939 73939 70939 67939 64939 61939 58939 55939 52939 49939 46939 43114 39291 36291 33291 30291 27178 24178 21178 18178 15145 12046 9013 6002 3002)  c-state-nonlit-pos-cache-limit 76939  c-state-semi-nonlit-pos-cache '(45291 42291 39291 36291 33291 30291 27094 24058 21058 18058 15058 12046 9013 6001 3001)  c-state-semi-nonlit-pos-cache-limit 45291 c-state-brace-pair-desert '(1 . 45935)  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 #<marker at 45006 in rwuniqsetup.c.before.2>  c-state-old-cpp-end #<marker at 46938 in rwuniqsetup.c.before.2>  c-parse-state-point 46793)

> At this point, indentation is having problems.  Try hitting TAB on line
> 1371, for example.  Or line 1374 for a complete failure to indent at
> all.

OK, for once c-parse-state seems to be blameless.

What seems to be happening is that during the display fontification,
operations are done at buffer offset 46793.  This is the spot after the
indentation on L1360.

CC Mode puts a backwards search limit on many operations, and this is
often implemented by temporarily narrowing the buffer (narrow-to-region
(- (point) n) (point-max)), where n is sometimes 1000.

1000 bytes before 46793 is 45793.  This spot is in the middle of the "##"
operator on L1346.  Thus the buffer is getting narrowed such that the
first character, ostensibly at BOL (to the Emacs regexp matcher), is "#".

A `c-beginning-of-macro' operation is thus spuriously finding point
45793, in the middle of this operator.  This spot is then getting
permanently marked as a beginning-of-CPP.  (Technically, this is done
with the 'category property, giving it a value 'c-cpp-delimiter.)

When c-parse-state later tries to scan this part of the code, it trips up
on this spurious beginning-of-CPP, terminating its scanning.  If you look
at the two inconsistent values for c-parse-state above, the "from
scratch" value is actually the false one, where it fails to find the
brace pair (46217 . 46572) which is after 45793.

I think what I'm going to do is insert an extra check into
c-beginning-of-macro, such that if there seems to be a match at
(point-min) of a narrowed buffer (or possibly even on the top line of
one), it will be widened and tested again.

Look forward to a patch soon!

> -- 
> Michael Welsh Duggan
> (md5i@md5i.com)

-- 
Alan Mackenzie (Nuremberg, Germany).





^ permalink raw reply	[flat|nested] 7+ messages in thread

* bug#18749: 24.3.94; CC Mode 5.32.5 (C/l); cc-mode state cache failure leading to bad indentation
  2014-10-17 18:19 ` Alan Mackenzie
@ 2014-10-17 19:35   ` Stefan Monnier
  2014-10-18  8:48     ` Alan Mackenzie
  2014-10-17 20:12   ` Alan Mackenzie
  1 sibling, 1 reply; 7+ messages in thread
From: Stefan Monnier @ 2014-10-17 19:35 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: Michael Welsh Duggan, 18749

> 1000 bytes before 46793 is 45793.  This spot is in the middle of the "##"
> operator on L1346.  Thus the buffer is getting narrowed such that the
> first character, ostensibly at BOL (to the Emacs regexp matcher), is "#".

For these kinds of reasons (along with various related ones), I have
learned to stay away from narrowing.


        Stefan





^ permalink raw reply	[flat|nested] 7+ messages in thread

* bug#18749: 24.3.94; CC Mode 5.32.5 (C/l); cc-mode state cache failure leading to bad indentation
  2014-10-17 18:19 ` Alan Mackenzie
  2014-10-17 19:35   ` Stefan Monnier
@ 2014-10-17 20:12   ` Alan Mackenzie
  1 sibling, 0 replies; 7+ messages in thread
From: Alan Mackenzie @ 2014-10-17 20:12 UTC (permalink / raw)
  To: Michael Welsh Duggan; +Cc: 18749

Hello, Michael.

On Fri, Oct 17, 2014 at 06:19:50PM -0000, Alan Mackenzie wrote:

> > A consistent c-mode indentation error that causes bad indentation, much
> > cursing, and gnashing of teeth.  This occurs in the latest emacs-24
> > branch (and trunk).

[ .... ]

> >     M-x c-toggle-parse-state-debug
> >     C-x C-f rwuniqsetup.c.before.2
> >     C-s ipaddr_rec_to_bin RET
> >     C-v

> > At this point, indentation is having problems.  Try hitting TAB on line
> > 1371, for example.  Or line 1374 for a complete failure to indent at
> > all.

[ .... ]

> Look forward to a patch soon!

OK, Could you try this, please, and get back to me ASAP.  Thanks!



diff -r a785658eaedb cc-engine.el
--- a/cc-engine.el	Thu Oct 02 20:17:43 2014 +0000
+++ b/cc-engine.el	Fri Oct 17 19:58:19 2014 +0000
@@ -254,6 +254,24 @@
     (setq c-macro-cache-start-pos beg
 	  c-macro-cache-syntactic nil))))
 
+(defun c-macro-is-genuine-p ()
+  ;; Check that the ostensible CPP construct at point is a real one.  In
+  ;; particular, if point is on the first line of a narrowed buffer, make sure
+  ;; that the "#" isn't, say, the second character of a "##" operator.  Return
+  ;; t when the macro is real, nil otherwise.
+  (let ((here (point)))
+    (beginning-of-line)
+    (prog1
+	(if (and (eq (point) (point-min))
+		 (/= (point) 1))
+	    (save-restriction
+	      (widen)
+	      (beginning-of-line)
+	      (and (looking-at c-anchored-cpp-prefix)
+		   (eq (match-beginning 1) here)))
+	  t)
+      (goto-char here))))
+
 (defun c-beginning-of-macro (&optional lim)
   "Go to the beginning of a preprocessor directive.
 Leave point at the beginning of the directive and return t if in one,
@@ -284,7 +302,8 @@
 	    (forward-line -1))
 	  (back-to-indentation)
 	  (if (and (<= (point) here)
-		   (looking-at c-opt-cpp-start))
+		   (looking-at c-opt-cpp-start)
+		   (c-macro-is-genuine-p))
 	      (progn
 		(setq c-macro-cache (cons (point) nil)
 		      c-macro-cache-start-pos here)


> > -- 
> > Michael Welsh Duggan
> > (md5i@md5i.com)

-- 
Alan Mackenzie (Nuremberg, Germany).





^ permalink raw reply	[flat|nested] 7+ messages in thread

* bug#18749: 24.3.94; CC Mode 5.32.5 (C/l); cc-mode state cache failure leading to bad indentation
  2014-10-17 19:35   ` Stefan Monnier
@ 2014-10-18  8:48     ` Alan Mackenzie
  2014-10-18 19:11       ` Stefan Monnier
  0 siblings, 1 reply; 7+ messages in thread
From: Alan Mackenzie @ 2014-10-18  8:48 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Michael Welsh Duggan, 18749

Hi, Stefan.

On Fri, Oct 17, 2014 at 03:35:22PM -0400, Stefan Monnier wrote:
> > 1000 bytes before 46793 is 45793.  This spot is in the middle of the "##"
> > operator on L1346.  Thus the buffer is getting narrowed such that the
> > first character, ostensibly at BOL (to the Emacs regexp matcher), is "#".

> For these kinds of reasons (along with various related ones), I have
> learned to stay away from narrowing.

That would be all very well if all movement and scanning primitives
(like `scan-lists' and the things that use it) actually had limit
parameters.  They don't.  What alternative, then, is there to narrowing?

>         Stefan

-- 
Alan Mackenzie (Nuremberg, Germany).





^ permalink raw reply	[flat|nested] 7+ messages in thread

* bug#18749: 24.3.94; CC Mode 5.32.5 (C/l); cc-mode state cache failure leading to bad indentation
  2014-10-18  8:48     ` Alan Mackenzie
@ 2014-10-18 19:11       ` Stefan Monnier
  0 siblings, 0 replies; 7+ messages in thread
From: Stefan Monnier @ 2014-10-18 19:11 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: Michael Welsh Duggan, 18749

> That would be all very well if all movement and scanning primitives
> (like `scan-lists' and the things that use it) actually had limit
> parameters.  They don't.  What alternative, then, is there to narrowing?

In the worst case you can narrow just around the call to scan-lists.
Or you live with the fact that it might move further than you intended.
I've changed a fair bit of code to avoid narrowing, and haven't bumped
into any real show stopper so far.


        Stefan





^ permalink raw reply	[flat|nested] 7+ messages in thread

* bug#18749: 24.3.94; CC Mode 5.32.5 (C/l); cc-mode state cache failure leading to bad indentation
       [not found] ` <mailman.11430.1413622526.1147.bug-gnu-emacs@gnu.org>
@ 2015-12-24 19:41   ` Alan Mackenzie
  0 siblings, 0 replies; 7+ messages in thread
From: Alan Mackenzie @ 2015-12-24 19:41 UTC (permalink / raw)
  To: 18749-done

Closing the bug, which was fixed in revision #118147 on the trunk, and
revision #117673 on the Emacs-24 branch on, respectively, 2014-10-18 and
2014-11-05.

-- 
Alan Mackenzie (Nuremberg, Germany).






^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2015-12-24 19:41 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-10-16 21:12 bug#18749: 24.3.94; CC Mode 5.32.5 (C/l); cc-mode state cache failure leading to bad indentation Michael Welsh Duggan
2014-10-17 18:19 ` Alan Mackenzie
2014-10-17 19:35   ` Stefan Monnier
2014-10-18  8:48     ` Alan Mackenzie
2014-10-18 19:11       ` Stefan Monnier
2014-10-17 20:12   ` Alan Mackenzie
     [not found] ` <mailman.11430.1413622526.1147.bug-gnu-emacs@gnu.org>
2015-12-24 19:41   ` Alan Mackenzie

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).