summaryrefslogtreecommitdiff
path: root/src/newhelp
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
committerIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
commit47e6e7c84f008a53061e661f31ae96629bc694ef (patch)
tree648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/newhelp
downloadpcp-debian.tar.gz
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/newhelp')
-rw-r--r--src/newhelp/GNUmakefile40
-rw-r--r--src/newhelp/chkhelp.c348
-rw-r--r--src/newhelp/newhelp.c473
3 files changed, 861 insertions, 0 deletions
diff --git a/src/newhelp/GNUmakefile b/src/newhelp/GNUmakefile
new file mode 100644
index 0000000..658f5cc
--- /dev/null
+++ b/src/newhelp/GNUmakefile
@@ -0,0 +1,40 @@
+#
+# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+
+TOPDIR = ../..
+include $(TOPDIR)/src/include/builddefs
+
+TARGETS = newhelp$(EXECSUFFIX) chkhelp$(EXECSUFFIX)
+CFILES = newhelp.c chkhelp.c
+
+LLDFLAGS = $(WARN_OFF)
+LLDLIBS = $(PCP_PMDALIB)
+LDIRT = $(TARGETS)
+
+default: $(TARGETS)
+
+install: default
+ $(INSTALL) -m 755 $(TARGETS) $(PCP_BINADM_DIR)
+
+include $(BUILDRULES)
+
+newhelp$(EXECSUFFIX): newhelp.o
+ $(CCF) -o $@ $(LDFLAGS) newhelp.o $(LDLIBS)
+
+chkhelp$(EXECSUFFIX): chkhelp.o
+ $(CCF) -o $@ $(LDFLAGS) chkhelp.o $(LDLIBS)
+
+default_pcp: default
+
+install_pcp: install
diff --git a/src/newhelp/chkhelp.c b/src/newhelp/chkhelp.c
new file mode 100644
index 0000000..3e4f4ad
--- /dev/null
+++ b/src/newhelp/chkhelp.c
@@ -0,0 +1,348 @@
+/*
+ *
+ * Copyright (c) 1995-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * check help files build by newhelp
+ *
+ * Usage:
+ * chkhelp helpfile metricname ...
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+
+#define VERSION 2
+static int version = VERSION;
+
+static int handle;
+
+/*
+ * Note: these two are from libpcp_pmda/src/help.c
+ */
+typedef struct {
+ pmID pmid;
+ __uint32_t off_oneline;
+ __uint32_t off_text;
+} help_idx_t;
+
+typedef struct {
+ int dir_fd;
+ int pag_fd;
+ int numidx;
+ help_idx_t *index;
+ char *text;
+ int textlen;
+} help_t;
+
+static int
+next(int *ident, int *type)
+{
+ static help_t *hp = NULL;
+ static int nextidx;
+ pmID pmid;
+ __pmID_int *pi = (__pmID_int *)&pmid;
+ extern void *__pmdaHelpTab(void);
+
+ if (hp == NULL) {
+ hp = (help_t *)__pmdaHelpTab();
+ /*
+ * Note, skip header and version info at index[0]
+ */
+ nextidx = 1;
+ }
+
+ if (nextidx > hp->numidx)
+ return 0;
+
+ pmid = hp->index[nextidx].pmid;
+ nextidx++;
+
+ if (pi->flag == 0) {
+ /* real PMID */
+ *ident = (int)pmid;
+ *type = 1;
+ }
+ else {
+ /* special hack, this is encoding a domain id, not a PMID */
+ pi->flag = 0;
+ *ident = (int)pmid;
+ *type = 2;
+ }
+
+ return 1;
+}
+
+
+/*
+ * with -e come here for every metric in the PMNS ...
+ */
+void
+dometric(const char *name)
+{
+ int sts;
+ pmID pmid;
+ char *tp;
+
+ sts = pmLookupName(1, (char **)&name, &pmid);
+ if (sts < 0) {
+ fprintf(stderr, "pmLookupName: failed for \"%s\": %s\n", name, pmErrStr(sts));
+ return;
+ }
+ if (sts == 0) {
+ fprintf(stderr, "pmLookupName: failed for \"%s\"\n", name);
+ return;
+ }
+
+ tp = pmdaGetHelp(handle, pmid, PM_TEXT_ONELINE);
+ if (tp != NULL)
+ return;
+ tp = pmdaGetHelp(handle, pmid, PM_TEXT_HELP);
+ if (tp != NULL)
+ return;
+
+ /* no help text, report metric */
+ printf("%s\n", name);
+}
+
+int
+main(int argc, char **argv)
+{
+ int sts;
+ int c;
+ int help = 0;
+ int oneline = 0;
+ char *pmnsfile = PM_NS_DEFAULT;
+ int errflag = 0;
+ int aflag = 0;
+ int eflag = 0;
+ int allpmid = 0;
+ int allindom = 0;
+ char *filename;
+ char *tp;
+ char *name;
+ int id;
+ int next_type;
+ char *endnum;
+
+ __pmSetProgname(argv[0]);
+
+ while ((c = getopt(argc, argv, "D:eHin:Opv:?")) != EOF) {
+ switch (c) {
+
+ case 'D': /* debug flag */
+ sts = __pmParseDebug(optarg);
+ if (sts < 0) {
+ fprintf(stderr, "%s: unrecognized debug flag specification (%s)\n",
+ pmProgname, optarg);
+ errflag++;
+ }
+ else
+ pmDebug |= sts;
+ break;
+
+ case 'e': /* help text exists? */
+ eflag = 1;
+ break;
+
+ case 'H': /* help text */
+ help = 1;
+ break;
+
+ case 'i':
+ aflag++;
+ allindom = 1;
+ break;
+
+ case 'n': /* alternative namespace file */
+ pmnsfile = optarg;
+ break;
+
+ case 'O': /* oneline text */
+ oneline = 1;
+ break;
+
+ case 'p':
+ aflag++;
+ allpmid = 1;
+ break;
+
+ case 'v': /* version 2 only these days */
+ version = (int)strtol(optarg, &endnum, 10);
+ if (*endnum != '\0') {
+ fprintf(stderr, "%s: -v requires numeric argument\n", pmProgname);
+ errflag++;
+ }
+ if (version != 2) {
+ fprintf(stderr
+ ,"%s: deprecated option - only version 2 is supported\n"
+ , pmProgname);
+ errflag++;
+ }
+ break;
+
+ case '?':
+ default:
+ errflag++;
+ break;
+ }
+ }
+
+ if (optind == argc) {
+ fprintf(stderr, "%s: missing helpfile argument\n\n", pmProgname);
+ errflag = 1;
+ }
+
+ if (aflag && optind < argc-1) {
+ fprintf(stderr, "%s: metricname arguments cannot be used with -i or -p\n\n",
+ pmProgname);
+ errflag = 1;
+ }
+
+ if (aflag == 0 && optind == argc-1 && oneline+help != 0) {
+ fprintf(stderr, "%s: -O or -H require metricname arguments or -i or -p\n\n",
+ pmProgname);
+ errflag = 1;
+ }
+
+ if (eflag && (allpmid || allindom)) {
+ fprintf(stderr, "%s: -e cannot be used with -i or -p\n\n",
+ pmProgname);
+ errflag = 1;
+ }
+
+ if (errflag || optind >= argc) {
+ fprintf(stderr,
+"Usage: %s helpfile\n"
+" %s [options] helpfile [metricname ...]\n"
+"\n"
+"Options:\n"
+" -e exists check, only report metrics with no help text\n"
+" -H display verbose help text\n"
+" -i process all the instance domains\n"
+" -n pmnsfile use an alternative PMNS\n"
+" -O display the one line help summary\n"
+" -p process all the metrics (PMIDs)\n"
+" -v version deprecated (only version 2 format supported)\n"
+"\n"
+"No options implies silently check internal integrity of the helpfile.\n",
+ pmProgname, pmProgname);
+ exit(1);
+ }
+
+ filename = argv[optind++];
+ if ((handle = pmdaOpenHelp(filename)) < 0) {
+ fprintf(stderr, "pmdaOpenHelp: failed to open \"%s\": ", filename);
+ if (handle == -EINVAL)
+ fprintf(stderr, "Bad format, not version %d PCP help text\n", version);
+ else
+ fprintf(stderr, "%s\n", pmErrStr(handle));
+ exit(1);
+ }
+
+ if ((sts = pmLoadNameSpace(pmnsfile)) < 0) {
+ fprintf(stderr, "pmLoadNameSpace: %s\n", pmErrStr(sts));
+ exit(1);
+ }
+
+ if (help + oneline == 0 && (optind < argc || aflag))
+ /* if metric names, -p or -i => -O is default */
+ oneline = 1;
+
+ if (optind == argc && aflag == 0)
+ /* no metric names, process all entries */
+ aflag = 1;
+
+ if (eflag) {
+ if (optind == argc)
+ sts = pmTraversePMNS("", dometric);
+ if (sts < 0)
+ fprintf(stderr, "Error: pmTraversePMNS(\"\", ...): %s\n", pmErrStr(sts));
+ else {
+ for ( ; optind < argc; optind++) {
+ sts = pmTraversePMNS(argv[optind], dometric);
+ if (sts < 0)
+ fprintf(stderr, "Error: pmTraversePMNS(\"%s\", ...): %s\n", argv[optind], pmErrStr(sts));
+ }
+ }
+ exit(0);
+ }
+
+ while (optind < argc || aflag) {
+ if (aflag) {
+ if (next(&id, &next_type) == 0)
+ break;
+#ifdef PCP_DEBUG
+ if ((pmDebug & DBG_TRACE_APPL0) && allindom+allpmid == 0)
+ fprintf(stderr, "next_type=%d id=0x%x\n", next_type, id);
+#endif
+ if (next_type == 2) {
+ if (!allindom)
+ continue;
+ printf("\nInDom %s:", pmInDomStr((pmInDom)id));
+ }
+ else {
+ char *p;
+ if (!allpmid)
+ continue;
+
+ printf("\nPMID %s", pmIDStr((pmID)id));
+ sts = pmNameID(id, &p);
+ if (sts == 0) {
+ printf(" %s", p);
+ free(p);
+ }
+ putchar(':');
+ }
+ }
+ else {
+ next_type = 1;
+ name = argv[optind++];
+ if ((sts = pmLookupName(1, &name, (pmID *)&id)) < 0) {
+ printf("\n%s: %s\n", name, pmErrStr(sts));
+ continue;
+ }
+ if (id == PM_ID_NULL) {
+ printf("\n%s: unknown metric\n", name);
+ continue;
+ }
+ printf("\nPMID %s %s:", pmIDStr((pmID)id), name);
+ }
+
+ if (oneline) {
+ if (next_type == 1)
+ tp = pmdaGetHelp(handle, (pmID)id, PM_TEXT_ONELINE);
+ else
+ tp = pmdaGetInDomHelp(handle, (pmInDom)id, PM_TEXT_ONELINE);
+ if (tp != NULL)
+ printf(" %s", tp);
+ putchar('\n');
+ }
+
+ if (help) {
+ if (next_type == 1)
+ tp = pmdaGetHelp(handle, (pmID)id, PM_TEXT_HELP);
+ else
+ tp = pmdaGetInDomHelp(handle, (pmInDom)id, PM_TEXT_HELP);
+ if (tp != NULL && *tp)
+ printf("%s\n", tp);
+ }
+
+ }
+
+ return 0;
+}
diff --git a/src/newhelp/newhelp.c b/src/newhelp/newhelp.c
new file mode 100644
index 0000000..97f0eaa
--- /dev/null
+++ b/src/newhelp/newhelp.c
@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) 2014 Red Hat.
+ * Copyright (c) 1995-2001,2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/*
+ * newhelp file
+ *
+ * in the model of newaliases, build ndbm data files for a PMDA's help file
+ * -- given the bloat of the ndbm files, version 2 uses a much simpler
+ * file access method, but preserves the file name conventions from
+ * version 1 that is based on ndbm.
+ */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include "pmapi.h"
+#include "impl.h"
+
+#define DEFAULT_HELP_VERSION 2
+
+/* maximum bytes per line and bytes per entry */
+#define MAXLINE 128
+#define MAXENTRY 1024
+
+static int verbose;
+static int ln;
+static char *filename;
+static int status;
+static int version = DEFAULT_HELP_VERSION;
+static FILE *f;
+
+typedef struct {
+ pmID pmid;
+ __uint32_t off_oneline;
+ __uint32_t off_text;
+} help_idx_t;
+
+static help_idx_t *hindex;
+static int numindex;
+static int thisindex = -1;
+
+static void
+newentry(char *buf)
+{
+ int n;
+ char *p;
+ char *end_name;
+ char end_c;
+ char *name;
+ pmID pmid;
+ char *start;
+ int warn = 0;
+ int i;
+
+ /* skip leading white space ... */
+ for (p = buf; isspace((int)*p); p++)
+ ;
+ /* skip over metric name or indom spec ... */
+ name = p;
+ for (p = buf; *p != '\n' && !isspace((int)*p); p++)
+ ;
+ end_c = *p;
+ *p = '\0'; /* terminate metric name */
+ end_name = p;
+
+ if ((n = pmLookupName(1, &name, &pmid)) < 0) {
+ /* apparently not a metric name */
+ int domain;
+ int cluster;
+ int item;
+ int serial;
+ pmID *pmidp;
+ if (sscanf(buf, "%d.%d.%d", &domain, &cluster, &item) == 3) {
+ /* a numeric pmid */
+ __pmID_int ii;
+ ii.domain = domain;
+ ii.cluster = cluster;
+ ii.item = item;
+ ii.flag = 0;
+ pmidp = (pmID *)&ii;
+ pmid = *pmidp;
+ }
+ else if (sscanf(buf, "%d.%d", &domain, &serial) == 2) {
+ /* an entry for an instance domain */
+ __pmInDom_int ii;
+ ii.domain = domain;
+ ii.serial = serial;
+ /* set a bit here to disambiguate pmInDom from pmID */
+ ii.flag = 1;
+ pmidp = (pmID *)&ii;
+ pmid = *pmidp;
+ }
+ else {
+ fprintf(stderr, "%s: [%s:%d] %s: %s, entry abandoned\n",
+ pmProgname, filename, ln, buf, pmErrStr(n));
+ status = 2;
+ return;
+ }
+ }
+ else {
+ if (pmid == PM_ID_NULL) {
+ fprintf(stderr, "%s: [%s:%d] %s: unknown metric, entry abandoned\n",
+ pmProgname, filename, ln, name);
+ status = 2;
+ return;
+ }
+ }
+
+ for (i = 0; i < thisindex; i++) {
+ if (hindex[thisindex].pmid == pmid) {
+ __pmInDom_int *kp = (__pmInDom_int *)&pmid;
+ fprintf(stderr, "%s: [%s:%d] duplicate key (",
+ pmProgname, filename, ln);
+ if (kp->flag == 0)
+ fprintf(stderr, "%s", pmIDStr(pmid));
+ else {
+ kp->flag = 0;
+ fprintf(stderr, "%s", pmInDomStr((pmInDom)pmid));
+ }
+ fprintf(stderr, ") entry abandoned\n");
+ status = 2;
+ return;
+ }
+ }
+
+ if (++thisindex >= numindex) {
+ if (numindex == 0)
+ numindex = 128;
+ else
+ numindex *= 2;
+ if ((hindex = (help_idx_t *)realloc(hindex, numindex * sizeof(hindex[0]))) == NULL) {
+ __pmNoMem("newentry", numindex * sizeof(hindex[0]), PM_FATAL_ERR);
+ }
+ }
+
+ fprintf(f, "\n@ %s ", name);
+
+ hindex[thisindex].pmid = pmid;
+ hindex[thisindex].off_oneline = ftell(f);
+
+ /* skip white space ... to start of oneline */
+ *p = end_c;
+ if (*p != '\n')
+ p++;
+ for ( ; *p != '\n' && isspace((int)*p); p++)
+ ;
+ start = p;
+
+ /* skip to end of line ... */
+ for ( ; *p != '\n'; p++)
+ ;
+ *p = '\0';
+ p++;
+
+ if (p - start == 1 && verbose) {
+ fprintf(stderr, "%s: [%s:%d] %s: warning, null oneline\n",
+ pmProgname, filename, ln, name);
+ warn = 1;
+ if (!status) status = 1;
+ }
+
+ if (fwrite(start, sizeof(*start), p - start, f) != p - start || ferror(f)) {
+ fprintf(stderr, "%s: [%s:%d] %s: write oneline failed, entry abandoned\n",
+ pmProgname, filename, ln, name);
+ thisindex--;
+ status = 2;
+ return;
+ }
+
+ hindex[thisindex].off_text = ftell(f);
+
+ /* trim all but last newline ... */
+ i = (int)strlen(p) - 1;
+ while (i >= 0 && p[i] == '\n')
+ i--;
+ if (i < 0)
+ i = 0;
+ else {
+ /* really have text ... p[i] is last non-newline char */
+ i++;
+ if (version == 1)
+ p[i++] = '\n';
+ }
+ p[i] = '\0';
+
+ if (i == 0 && verbose) {
+ fprintf(stderr, "%s: [%s:%d] %s: warning, null help\n",
+ pmProgname, filename, ln, name);
+ warn = 1;
+ if (!status) status = 1;
+ }
+
+ if (fwrite(p, sizeof(*p), i+1, f) != i+1 || ferror(f)) {
+ fprintf(stderr,
+ "%s: [%s:%d] %s: write help failed, entry abandoned\n",
+ pmProgname, filename, ln, name);
+ thisindex--;
+ status = 2;
+ return;
+ }
+
+ if (verbose && warn == 0) {
+ *end_name = '\0';
+ fprintf(stderr, "%s\n", name);
+ *end_name = end_c;
+ }
+}
+
+static int
+idcomp(const void *a, const void *b)
+{
+ /*
+ * comparing 32-bit keys here ... want PMIDs to go first the
+ * InDoms, sort by low order bits ... serial from InDom is easier
+ * than cluster and item from PMID, so use InDom format
+ */
+ __pmInDom_int *iiap, *iibp;
+
+ iiap = (__pmInDom_int *)(&((help_idx_t *)a)->pmid);
+ iibp = (__pmInDom_int *)(&((help_idx_t *)b)->pmid);
+
+ if (iiap->flag == iibp->flag)
+ /* both of the same type, use serial to order */
+ return iiap->serial - iibp->serial;
+ else if (iiap->flag == 0)
+ /* a is the PMID, b is an InDom */
+ return -1;
+ else
+ /* b is the PMID, a is an InDom */
+ return 1;
+}
+
+static pmLongOptions longopts[] = {
+ PMAPI_OPTIONS_HEADER("General options"),
+ PMOPT_DEBUG,
+ PMOPT_NAMESPACE,
+ PMOPT_HELP,
+ PMAPI_OPTIONS_HEADER("Output options"),
+ { "output", 1, 'o', "FILE", "base name for output files" },
+ { "verbose", 0, 'V', 0, "verbose/diagnostic output" },
+ { "version", 0, 'v', 0, "deprecated (only version 2 format supported)" },
+ PMAPI_OPTIONS_END
+};
+
+static pmOptions opts = {
+ .short_options = "D:n:o:Vv:?",
+ .long_options = longopts,
+ .short_usage = "[options] [file ...]",
+};
+
+int
+main(int argc, char **argv)
+{
+ int n;
+ int c;
+ int i;
+ int sts;
+ char *pmnsfile = PM_NS_DEFAULT;
+ char *fname = NULL;
+ char pathname[MAXPATHLEN];
+ FILE *inf;
+ char buf[MAXENTRY+MAXLINE];
+ char *endnum;
+ char *bp;
+ char *p;
+ int skip;
+ help_idx_t hdr;
+
+ while ((c = pmgetopt_r(argc, argv, &opts)) != EOF) {
+ switch (c) {
+
+ case 'D': /* debug flag */
+ if ((sts = __pmParseDebug(opts.optarg)) < 0) {
+ pmprintf("%s: unrecognized debug flag specification (%s)\n",
+ pmProgname, opts.optarg);
+ opts.errors++;
+ }
+ else
+ pmDebug |= sts;
+ break;
+
+ case 'n': /* alternative namespace file */
+ pmnsfile = opts.optarg;
+ break;
+
+ case 'o': /* alternative output file name */
+ fname = opts.optarg;
+ break;
+
+ case 'V': /* more chit-chat */
+ verbose++;
+ break;
+
+ case 'v': /* version 2 only these days */
+ version = (int)strtol(opts.optarg, &endnum, 10);
+ if (*endnum != '\0') {
+ pmprintf("%s: -v requires numeric argument\n", pmProgname);
+ opts.errors++;
+ }
+ if (version != 2) {
+ pmprintf("%s: deprecated option - only version 2 is supported\n",
+ pmProgname);
+ opts.errors++;
+ }
+ break;
+
+ case '?':
+ default:
+ opts.errors++;
+ break;
+ }
+ }
+
+ if (opts.errors) {
+ pmUsageMessage(&opts);
+ exit(2);
+ }
+
+ if ((n = pmLoadNameSpace(pmnsfile)) < 0) {
+ fprintf(stderr, "%s: pmLoadNameSpace: %s\n", pmProgname, pmErrStr(n));
+ exit(2);
+ }
+
+ do {
+ if (opts.optind < argc) {
+ filename = argv[opts.optind];
+ if ((inf = fopen(filename, "r")) == NULL) {
+ perror(filename);
+ exit(2);
+ }
+ if (fname == NULL)
+ fname = filename;
+ }
+ else {
+ if (fname == NULL) {
+ fprintf(stderr,
+ "%s: need either a -o option or a filename "
+ "argument to name the output file\n", pmProgname);
+ exit(2);
+ }
+ filename = "<stdin>";
+ inf = stdin;
+ }
+
+ if (version == 2 && f == NULL) {
+ sprintf(pathname, "%s.pag", fname);
+ if ((f = fopen(pathname, "w")) == NULL) {
+ fprintf(stderr, "%s: fopen(\"%s\", ...) failed: %s\n",
+ pmProgname, pathname, osstrerror());
+ exit(2);
+ }
+ /* header: 2 => pag cf 1 => dir */
+ fprintf(f, "PcPh2%c\n", '0' + version);
+ }
+
+ bp = buf;
+ skip = 1;
+ for ( ; ; ) {
+ if (fgets(bp, MAXLINE, inf) == NULL) {
+ skip = -1;
+ *bp = '@';
+ }
+ ln++;
+ if (bp[0] == '#')
+ continue;
+ if (bp[0] == '@') {
+ /* start of a new entry */
+ if (bp > buf) {
+ /* really have a prior entry */
+ p = bp - 1;
+ while (p > buf && *p == '\n')
+ p--;
+ *++p = '\n';
+ *++p = '\0';
+ newentry(buf);
+ }
+ if (skip == -1)
+ break;
+ skip = 0;
+ bp++; /* skip '@' */
+ while (*bp && isspace((int)*bp))
+ bp++;
+ if (bp[0] == '\0') {
+ if (verbose)
+ fprintf(stderr, "%s: [%s:%d] null entry?\n",
+ pmProgname, filename, ln);
+ skip = 1;
+ bp = buf;
+ if (!status) status = 1;
+ }
+ else {
+ for (p = bp; *p; p++)
+ ;
+ memmove(buf, bp, p - bp + 1);
+ for (bp = buf; *bp; bp++)
+ ;
+ }
+ }
+ if (skip)
+ continue;
+ for (p = bp; *p; p++)
+ ;
+ if (bp > buf && p[-1] != '\n') {
+ *p++ = '\n';
+ *p = '\0';
+ fprintf(stderr, "%s: [%s:%d] long line split after ...\n%s",
+ pmProgname, filename, ln, buf);
+ ln--;
+ if (!status) status = 1;
+ }
+ bp = p;
+ if (bp > &buf[MAXENTRY]) {
+ bp = &buf[MAXENTRY];
+ bp[-1] = '\0';
+ bp[-2] = '\n';
+ fprintf(stderr, "%s: [%s:%d] entry truncated after ... %s",
+ pmProgname, filename, ln, &bp[-64]);
+ skip = 1;
+ if (!status) status = 1;
+ }
+ }
+
+ fclose(inf);
+ opts.optind++;
+ } while (opts.optind < argc);
+
+ if (f != NULL) {
+ fclose(f);
+
+ /* do the directory index ... */
+ sprintf(pathname, "%s.dir", fname);
+ if ((f = fopen(pathname, "w")) == NULL) {
+ fprintf(stderr, "%s: fopen(\"%s\", ...) failed: %s\n",
+ pmProgname, pathname, osstrerror());
+ exit(2);
+ }
+
+ /* index header */
+ hdr.pmid = 0x50635068; /* "PcPh" */
+ /* "1" => dir, next char is version */
+ hdr.off_oneline = 0x31000000 | (('0' + version) << 16);
+ hdr.off_text = thisindex + 1; /* # entries */
+ if (fwrite(&hdr, sizeof(hdr), 1, f) != 1 || ferror(f)) {
+ fprintf(stderr, "%s: fwrite index failed: %s\n",
+ pmProgname, osstrerror());
+ exit(2);
+ }
+
+ /* sort and write index */
+ qsort((void *)hindex, thisindex+1, sizeof(hindex[0]), idcomp);
+ for (i = 0; i <= thisindex; i++) {
+ if (fwrite(&hindex[i], sizeof(hindex[0]), 1, f) != 1
+ || ferror(f)) {
+ fprintf(stderr, "%s: fwrite index failed: %s\n",
+ pmProgname, osstrerror());
+ exit(2);
+ }
+ }
+ }
+
+ exit(status);
+}