[BACK]Return to statistics.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / docbook2mdoc

Annotation of docbook2mdoc/statistics.c, Revision 1.4

1.4     ! schwarze    1: /* $Id: statistics.c,v 1.3 2019/04/03 08:39:53 schwarze Exp $ */
1.1       schwarze    2: /*
                      3:  * Copyright (c) 2019 Ingo Schwarze <schwarze@openbsd.org>
                      4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
                      8:  *
                      9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
                     10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
                     12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     16:  */
                     17: #include <assert.h>
                     18: #include <ctype.h>
                     19: #include <err.h>
                     20: #include <fcntl.h>
1.2       schwarze   21: #include <getopt.h>
1.1       schwarze   22: #include <stdio.h>
                     23: #include <stdlib.h>
                     24: #include <string.h>
                     25: #include <unistd.h>
                     26:
                     27: /*
                     28:  * Count parent-child element relations in a corpus of DocBook documents.
                     29:  *
                     30:  * Read absolute or relative input file names from standard input,
                     31:  * one per line.
                     32:  * For each parent-child relation, print the total number of occurrences,
                     33:  * the parent name, and the child name, separated by tab characters
                     34:  * and followed by a newline character.
                     35:  *
                     36:  * Typical usage:
                     37:  * statistics < filenames.txt | sort -n
                     38:  * statistics < filenames.txt | grep '\<listitem\>' | sort -n
1.4     ! schwarze   39:  *
        !            40:  * Relations already fully implemented are excluded by default.
        !            41:  * The option -a shows all relations.
        !            42:  *
        !            43:  * If two arguments (parent and child) are given, a histogram
        !            44:  * of the number of children of the kind in each parent is given
        !            45:  * in addition to the normal output.
        !            46:  *
        !            47:  * Example usage:
        !            48:  * statistics tgroup colspec < filenames.txt | grep colspec
1.1       schwarze   49:  */
                     50:
                     51: struct entry {
                     52:        char    *parent;
                     53:        char    *child;
                     54:        int      count;
                     55: };
                     56:
                     57: static struct entry     *table;
                     58: static size_t            tablesz;
                     59: static size_t            tablei;
                     60:
                     61: static char            **stack;
                     62: static size_t            stacksz;
                     63: static size_t            stacki;
                     64:
1.4     ! schwarze   65: static const int         nchildsz = 8;
        !            66: struct nchild {
        !            67:        char    *parent;
        !            68:        char    *child;
        !            69:        int      freq[nchildsz];
        !            70:        int      count;
        !            71: };
        !            72:
        !            73: static struct nchild     nchild;
        !            74: static char             *fname;
        !            75:
1.1       schwarze   76:
                     77: /*
                     78:  * Count one instance of a parent-child relation.
1.2       schwarze   79:  * Before the special call table_add(NULL, NULL),
                     80:  * mark relations to not be counted;
                     81:  * in that phase, child can be NULL as a wildcard.
1.1       schwarze   82:  */
                     83: static void
                     84: table_add(const char *parent, const char *child)
                     85: {
1.2       schwarze   86:        static int       init_done;
                     87:        size_t           i;
                     88:
                     89:        if (parent == NULL && child == NULL) {
                     90:                init_done = 1;
                     91:                return;
                     92:        }
1.1       schwarze   93:
1.4     ! schwarze   94:        /* Optional parent-child histogram. */
        !            95:
        !            96:        if (init_done && parent != NULL && child != NULL &&
        !            97:            nchild.parent != NULL && nchild.child != NULL &&
        !            98:            strcmp(parent, nchild.parent) == 0 &&
        !            99:            strcmp(child, nchild.child) == 0) {
        !           100:                if (nchild.count < nchildsz) {
        !           101:                        nchild.freq[nchild.count]++;
        !           102:                        if (nchild.count > 0)
        !           103:                                nchild.freq[nchild.count - 1]--;
        !           104:                } else if (nchild.count == nchildsz)
        !           105:                        puts(fname);
        !           106:                nchild.count++;
        !           107:        }
        !           108:
1.1       schwarze  109:        /* If the table entry already exists, increment its count. */
                    110:
                    111:        for (i = 0; i < tablei; i++) {
                    112:                if (strcmp(parent, table[i].parent) == 0 &&
1.2       schwarze  113:                    (child == NULL || table[i].child == NULL ||
                    114:                     strcmp(child, table[i].child) == 0)) {
                    115:                        assert(init_done);
                    116:                        if (table[i].count != -1)
                    117:                                table[i].count++;
1.1       schwarze  118:                        return;
                    119:                }
                    120:        }
                    121:
                    122:        /* If the table is full, make room. */
                    123:
                    124:        if (tablei == tablesz) {
                    125:                tablesz += 64;
                    126:                table = reallocarray(table, tablesz, sizeof(*table));
                    127:                if (table == NULL)
                    128:                        err(1, NULL);
                    129:        }
                    130:
                    131:        /* Add a new entry to the table. */
                    132:
                    133:        if ((table[tablei].parent = strdup(parent)) == NULL)
                    134:                err(1, NULL);
1.2       schwarze  135:        if (child == NULL)
                    136:                table[tablei].child = NULL;
                    137:        else if ((table[tablei].child = strdup(child)) == NULL)
1.1       schwarze  138:                err(1, NULL);
1.2       schwarze  139:        table[tablei++].count = init_done ? 1 : -1;
1.1       schwarze  140: }
                    141:
                    142: /*
                    143:  * Enter an element.
                    144:  */
                    145: static void
                    146: stack_push(const char *name)
                    147: {
1.4     ! schwarze  148:        if (nchild.parent != NULL && strcmp(name, nchild.parent) == 0)
        !           149:                nchild.count = 0;
        !           150:
1.1       schwarze  151:        if (stacki == stacksz) {
                    152:                stacksz += 8;
                    153:                stack = reallocarray(stack, stacksz, sizeof(*stack));
                    154:                if (stack == NULL)
                    155:                        err(1, NULL);
                    156:        }
                    157:        if ((stack[stacki++] = strdup(name)) == NULL)
                    158:                err(1, NULL);
                    159: }
                    160:
                    161: /*
                    162:  * Exit an element.
                    163:  */
                    164: static void
                    165: stack_pop(const char *name)
                    166: {
                    167:        if (stacki > 0 && (name == NULL ||
                    168:            strcmp(name, stack[stacki - 1]) == 0))
                    169:                free(stack[--stacki]);
                    170: }
                    171:
                    172: /*
                    173:  * Simplified version from parse.c.
                    174:  */
                    175: static int
                    176: advance(char *b, size_t rlen, size_t *pend, const char *charset)
                    177: {
                    178:        int              space;
                    179:
                    180:        if (*charset == ' ') {
                    181:                space = 1;
                    182:                charset++;
                    183:        } else
                    184:                space = 0;
                    185:
                    186:        while (*pend < rlen) {
                    187:                if (space && isspace((unsigned char)b[*pend]))
                    188:                        break;
                    189:                if (strchr(charset, b[*pend]) != NULL)
                    190:                        break;
                    191:                ++*pend;
                    192:        }
                    193:        if (*pend == rlen) {
                    194:                b[rlen] = '\0';
                    195:                return 1;
                    196:        } else
                    197:                return 0;
                    198: }
                    199:
                    200: /*
                    201:  * Simplified version from parse.c.
                    202:  */
                    203: static void
                    204: parse_file(int fd, char *fname)
                    205: {
                    206:        char             b[4096];
1.3       schwarze  207:        char            *cp;
1.1       schwarze  208:        ssize_t          rsz;   /* Return value from read(2). */
                    209:        size_t           rlen;  /* Number of bytes in b[]. */
                    210:        size_t           poff;  /* Parse offset in b[]. */
                    211:        size_t           pend;  /* Offset of the end of the current word. */
                    212:        int              in_tag, in_arg, in_quotes, elem_end;
                    213:
                    214:        rlen = 0;
                    215:        in_tag = in_arg = in_quotes = 0;
                    216:        while ((rsz = read(fd, b + rlen, sizeof(b) - rlen - 1)) >= 0) {
                    217:                if ((rlen += rsz) == 0)
                    218:                        break;
                    219:                pend = 0;
                    220:                for (;;) {
                    221:                        if ((poff = pend) == rlen)
                    222:                                break;
                    223:                        if (isspace((unsigned char)b[pend])) {
                    224:                                pend++;
                    225:                                continue;
                    226:                        }
                    227:                        if (in_arg) {
                    228:                                if (in_quotes == 0 && b[pend] == '"') {
                    229:                                        in_quotes = 1;
                    230:                                        pend++;
                    231:                                        continue;
                    232:                                }
                    233:                                if (advance(b, rlen, &pend,
                    234:                                    in_quotes ? "\"" : " >") && rsz > 0)
                    235:                                        break;
                    236:                                in_arg = in_quotes = elem_end = 0;
                    237:                                if (b[pend] == '>') {
                    238:                                        in_tag = 0;
                    239:                                        if (pend > 0 && b[pend - 1] == '/') {
                    240:                                                b[pend - 1] = '\0';
                    241:                                                elem_end = 1;
                    242:                                        }
                    243:                                }
                    244:                                b[pend] = '\0';
                    245:                                if (pend < rlen)
                    246:                                        pend++;
                    247:                                if (elem_end)
                    248:                                        stack_pop(NULL);
                    249:                        } else if (in_tag) {
                    250:                                if (advance(b, rlen, &pend, " =>") && rsz > 0)
                    251:                                        break;
                    252:                                elem_end = 0;
                    253:                                switch (b[pend]) {
                    254:                                case '>':
                    255:                                        in_tag = 0;
                    256:                                        if (pend > 0 && b[pend - 1] == '/') {
                    257:                                                b[pend - 1] = '\0';
                    258:                                                elem_end = 1;
                    259:                                        }
                    260:                                        break;
                    261:                                case '=':
                    262:                                        in_arg = 1;
                    263:                                        break;
                    264:                                default:
                    265:                                        break;
                    266:                                }
                    267:                                b[pend] = '\0';
                    268:                                if (pend < rlen)
                    269:                                        pend++;
                    270:                                if (elem_end)
                    271:                                        stack_pop(NULL);
                    272:                        } else if (b[poff] == '<') {
                    273:                                if (advance(b, rlen, &pend, " >") && rsz > 0)
                    274:                                        break;
1.3       schwarze  275:                                if (pend > poff + 3 &&
                    276:                                    strncmp(b + poff, "<!--", 4) == 0) {
                    277:                                        /* Skip a comment. */
                    278:                                        cp = strstr(b + pend - 2, "-->");
                    279:                                        if (cp == NULL) {
                    280:                                                pend = rlen;
                    281:                                                if (rsz > 0)
                    282:                                                        break;
                    283:                                        } else
                    284:                                                pend = cp + 3 - b;
                    285:                                        continue;
                    286:                                }
1.1       schwarze  287:                                elem_end = 0;
                    288:                                if (b[pend] != '>')
                    289:                                        in_tag = 1;
                    290:                                else if (pend > 0 && b[pend - 1] == '/') {
                    291:                                        b[pend - 1] = '\0';
                    292:                                        elem_end = 1;
                    293:                                }
                    294:                                b[pend] = '\0';
                    295:                                if (pend < rlen)
                    296:                                        pend++;
                    297:                                if (b[++poff] == '/') {
                    298:                                        elem_end = 1;
                    299:                                        poff++;
                    300:                                } else if (b[poff] != '!' && b[poff] != '?') {
                    301:                                        table_add(stacki > 0 ?
1.2       schwarze  302:                                            stack[stacki - 1] : "ROOT",
1.1       schwarze  303:                                            b + poff);
                    304:                                        stack_push(b + poff);
                    305:                                }
                    306:                                if (elem_end)
                    307:                                        stack_pop(b + poff);
                    308:                        } else {
                    309:                                advance(b, rlen, &pend, "<");
                    310:                                if (stacki > 0)
                    311:                                        table_add(stack[stacki - 1], "TEXT");
                    312:                        }
                    313:                }
                    314:                assert(poff > 0);
                    315:                memmove(b, b + poff, rlen - poff);
                    316:                rlen -= poff;
                    317:        }
                    318:        if (rsz < 0)
                    319:                perror(fname);
                    320: }
                    321:
                    322: int
                    323: main(int argc, char *argv[])
                    324: {
                    325:        size_t           fsz, i;
                    326:        ssize_t          rsz;
1.2       schwarze  327:        int              ch, fd, show_all;
                    328:
                    329:        show_all = 0;
                    330:        while ((ch = getopt(argc, argv, "a")) != -1) {
                    331:                switch (ch) {
                    332:                case 'a':
                    333:                        show_all = 1;
                    334:                        break;
                    335:                default:
                    336:                        return 1;
                    337:                }
                    338:        }
1.4     ! schwarze  339:        argc -= optind;
        !           340:        argv += optind;
        !           341:
        !           342:        if (argc > 1) {
        !           343:                nchild.parent = argv[0];
        !           344:                nchild.child = argv[1];
        !           345:        }
1.1       schwarze  346:
1.2       schwarze  347:        /* Exclude relations that are already fully implemented. */
                    348:        if (show_all == 0) {
                    349:                table_add("para", NULL);
                    350:        }
                    351:        table_add(NULL, NULL);
                    352:
                    353:        /* Loop over input files. */
1.1       schwarze  354:        fd = -1;
                    355:        fname = NULL;
                    356:        while ((rsz = getline(&fname, &fsz, stdin)) != -1) {
                    357:                if (fname[rsz - 1] == '\n')
                    358:                        fname[--rsz] = '\0';
                    359:                if ((fd = open(fname, O_RDONLY, 0)) == -1)
                    360:                        err(1, "%s", fname);
                    361:                parse_file(fd, fname);
                    362:                close(fd);
                    363:        }
                    364:
                    365:        /* Cleanup and error handling. */
                    366:        free(fname);
                    367:        if (ferror(stdin))
                    368:                err(1, "standard input");
                    369:        if (fd == -1)
                    370:                errx(1, "No input file names found on standard input");
                    371:
                    372:        /* Dump results. */
                    373:        for (i = 0; i < tablei; i++)
1.2       schwarze  374:                if (table[i].count != -1)
                    375:                        printf("%d\t%s\t%s\n", table[i].count,
                    376:                            table[i].parent, table[i].child);
1.4     ! schwarze  377:
        !           378:        /* Optional parent-child histogram. */
        !           379:        if (nchild.parent != NULL) {
        !           380:                printf("%s %s", nchild.parent, nchild.child);
        !           381:                for (i = 0; i < nchildsz; i++)
        !           382:                        printf(" %d", nchild.freq[i]);
        !           383:                putchar('\n');
        !           384:        }
1.1       schwarze  385:        return 0;
                    386: }

CVSweb