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

Annotation of docbook2mdoc/statistics.c, Revision 1.2

1.2     ! schwarze    1: /* $Id: statistics.c,v 1.1 2019/03/29 15:55:28 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
                     39:  */
                     40:
                     41: struct entry {
                     42:        char    *parent;
                     43:        char    *child;
                     44:        int      count;
                     45: };
                     46:
                     47: static struct entry     *table;
                     48: static size_t            tablesz;
                     49: static size_t            tablei;
                     50:
                     51: static char            **stack;
                     52: static size_t            stacksz;
                     53: static size_t            stacki;
                     54:
                     55:
                     56: /*
                     57:  * Count one instance of a parent-child relation.
1.2     ! schwarze   58:  * Before the special call table_add(NULL, NULL),
        !            59:  * mark relations to not be counted;
        !            60:  * in that phase, child can be NULL as a wildcard.
1.1       schwarze   61:  */
                     62: static void
                     63: table_add(const char *parent, const char *child)
                     64: {
1.2     ! schwarze   65:        static int       init_done;
        !            66:        size_t           i;
        !            67:
        !            68:        if (parent == NULL && child == NULL) {
        !            69:                init_done = 1;
        !            70:                return;
        !            71:        }
1.1       schwarze   72:
                     73:        /* If the table entry already exists, increment its count. */
                     74:
                     75:        for (i = 0; i < tablei; i++) {
                     76:                if (strcmp(parent, table[i].parent) == 0 &&
1.2     ! schwarze   77:                    (child == NULL || table[i].child == NULL ||
        !            78:                     strcmp(child, table[i].child) == 0)) {
        !            79:                        assert(init_done);
        !            80:                        if (table[i].count != -1)
        !            81:                                table[i].count++;
1.1       schwarze   82:                        return;
                     83:                }
                     84:        }
                     85:
                     86:        /* If the table is full, make room. */
                     87:
                     88:        if (tablei == tablesz) {
                     89:                tablesz += 64;
                     90:                table = reallocarray(table, tablesz, sizeof(*table));
                     91:                if (table == NULL)
                     92:                        err(1, NULL);
                     93:        }
                     94:
                     95:        /* Add a new entry to the table. */
                     96:
                     97:        if ((table[tablei].parent = strdup(parent)) == NULL)
                     98:                err(1, NULL);
1.2     ! schwarze   99:        if (child == NULL)
        !           100:                table[tablei].child = NULL;
        !           101:        else if ((table[tablei].child = strdup(child)) == NULL)
1.1       schwarze  102:                err(1, NULL);
1.2     ! schwarze  103:        table[tablei++].count = init_done ? 1 : -1;
1.1       schwarze  104: }
                    105:
                    106: /*
                    107:  * Enter an element.
                    108:  */
                    109: static void
                    110: stack_push(const char *name)
                    111: {
                    112:        if (stacki == stacksz) {
                    113:                stacksz += 8;
                    114:                stack = reallocarray(stack, stacksz, sizeof(*stack));
                    115:                if (stack == NULL)
                    116:                        err(1, NULL);
                    117:        }
                    118:        if ((stack[stacki++] = strdup(name)) == NULL)
                    119:                err(1, NULL);
                    120: }
                    121:
                    122: /*
                    123:  * Exit an element.
                    124:  */
                    125: static void
                    126: stack_pop(const char *name)
                    127: {
                    128:        if (stacki > 0 && (name == NULL ||
                    129:            strcmp(name, stack[stacki - 1]) == 0))
                    130:                free(stack[--stacki]);
                    131: }
                    132:
                    133: /*
                    134:  * Simplified version from parse.c.
                    135:  */
                    136: static int
                    137: advance(char *b, size_t rlen, size_t *pend, const char *charset)
                    138: {
                    139:        int              space;
                    140:
                    141:        if (*charset == ' ') {
                    142:                space = 1;
                    143:                charset++;
                    144:        } else
                    145:                space = 0;
                    146:
                    147:        while (*pend < rlen) {
                    148:                if (space && isspace((unsigned char)b[*pend]))
                    149:                        break;
                    150:                if (strchr(charset, b[*pend]) != NULL)
                    151:                        break;
                    152:                ++*pend;
                    153:        }
                    154:        if (*pend == rlen) {
                    155:                b[rlen] = '\0';
                    156:                return 1;
                    157:        } else
                    158:                return 0;
                    159: }
                    160:
                    161: /*
                    162:  * Simplified version from parse.c.
                    163:  */
                    164: static void
                    165: parse_file(int fd, char *fname)
                    166: {
                    167:        char             b[4096];
                    168:        ssize_t          rsz;   /* Return value from read(2). */
                    169:        size_t           rlen;  /* Number of bytes in b[]. */
                    170:        size_t           poff;  /* Parse offset in b[]. */
                    171:        size_t           pend;  /* Offset of the end of the current word. */
                    172:        int              in_tag, in_arg, in_quotes, elem_end;
                    173:
                    174:        rlen = 0;
                    175:        in_tag = in_arg = in_quotes = 0;
                    176:        while ((rsz = read(fd, b + rlen, sizeof(b) - rlen - 1)) >= 0) {
                    177:                if ((rlen += rsz) == 0)
                    178:                        break;
                    179:                pend = 0;
                    180:                for (;;) {
                    181:                        if ((poff = pend) == rlen)
                    182:                                break;
                    183:                        if (isspace((unsigned char)b[pend])) {
                    184:                                pend++;
                    185:                                continue;
                    186:                        }
                    187:                        if (in_arg) {
                    188:                                if (in_quotes == 0 && b[pend] == '"') {
                    189:                                        in_quotes = 1;
                    190:                                        pend++;
                    191:                                        continue;
                    192:                                }
                    193:                                if (advance(b, rlen, &pend,
                    194:                                    in_quotes ? "\"" : " >") && rsz > 0)
                    195:                                        break;
                    196:                                in_arg = in_quotes = elem_end = 0;
                    197:                                if (b[pend] == '>') {
                    198:                                        in_tag = 0;
                    199:                                        if (pend > 0 && b[pend - 1] == '/') {
                    200:                                                b[pend - 1] = '\0';
                    201:                                                elem_end = 1;
                    202:                                        }
                    203:                                }
                    204:                                b[pend] = '\0';
                    205:                                if (pend < rlen)
                    206:                                        pend++;
                    207:                                if (elem_end)
                    208:                                        stack_pop(NULL);
                    209:                        } else if (in_tag) {
                    210:                                if (advance(b, rlen, &pend, " =>") && rsz > 0)
                    211:                                        break;
                    212:                                elem_end = 0;
                    213:                                switch (b[pend]) {
                    214:                                case '>':
                    215:                                        in_tag = 0;
                    216:                                        if (pend > 0 && b[pend - 1] == '/') {
                    217:                                                b[pend - 1] = '\0';
                    218:                                                elem_end = 1;
                    219:                                        }
                    220:                                        break;
                    221:                                case '=':
                    222:                                        in_arg = 1;
                    223:                                        break;
                    224:                                default:
                    225:                                        break;
                    226:                                }
                    227:                                b[pend] = '\0';
                    228:                                if (pend < rlen)
                    229:                                        pend++;
                    230:                                if (elem_end)
                    231:                                        stack_pop(NULL);
                    232:                        } else if (b[poff] == '<') {
                    233:                                if (advance(b, rlen, &pend, " >") && rsz > 0)
                    234:                                        break;
                    235:                                elem_end = 0;
                    236:                                if (b[pend] != '>')
                    237:                                        in_tag = 1;
                    238:                                else if (pend > 0 && b[pend - 1] == '/') {
                    239:                                        b[pend - 1] = '\0';
                    240:                                        elem_end = 1;
                    241:                                }
                    242:                                b[pend] = '\0';
                    243:                                if (pend < rlen)
                    244:                                        pend++;
                    245:                                if (b[++poff] == '/') {
                    246:                                        elem_end = 1;
                    247:                                        poff++;
                    248:                                } else if (b[poff] != '!' && b[poff] != '?') {
                    249:                                        table_add(stacki > 0 ?
1.2     ! schwarze  250:                                            stack[stacki - 1] : "ROOT",
1.1       schwarze  251:                                            b + poff);
                    252:                                        stack_push(b + poff);
                    253:                                }
                    254:                                if (elem_end)
                    255:                                        stack_pop(b + poff);
                    256:                        } else {
                    257:                                advance(b, rlen, &pend, "<");
                    258:                                if (stacki > 0)
                    259:                                        table_add(stack[stacki - 1], "TEXT");
                    260:                        }
                    261:                }
                    262:                assert(poff > 0);
                    263:                memmove(b, b + poff, rlen - poff);
                    264:                rlen -= poff;
                    265:        }
                    266:        if (rsz < 0)
                    267:                perror(fname);
                    268: }
                    269:
                    270: int
                    271: main(int argc, char *argv[])
                    272: {
                    273:        char            *fname;
                    274:        size_t           fsz, i;
                    275:        ssize_t          rsz;
1.2     ! schwarze  276:        int              ch, fd, show_all;
        !           277:
        !           278:        show_all = 0;
        !           279:        while ((ch = getopt(argc, argv, "a")) != -1) {
        !           280:                switch (ch) {
        !           281:                case 'a':
        !           282:                        show_all = 1;
        !           283:                        break;
        !           284:                default:
        !           285:                        return 1;
        !           286:                }
        !           287:        }
1.1       schwarze  288:
1.2     ! schwarze  289:        /* Exclude relations that are already fully implemented. */
        !           290:        if (show_all == 0) {
        !           291:                table_add("para", NULL);
        !           292:        }
        !           293:        table_add(NULL, NULL);
        !           294:
        !           295:        /* Loop over input files. */
1.1       schwarze  296:        fd = -1;
                    297:        fname = NULL;
                    298:        while ((rsz = getline(&fname, &fsz, stdin)) != -1) {
                    299:                if (fname[rsz - 1] == '\n')
                    300:                        fname[--rsz] = '\0';
                    301:                if ((fd = open(fname, O_RDONLY, 0)) == -1)
                    302:                        err(1, "%s", fname);
                    303:                parse_file(fd, fname);
                    304:                close(fd);
                    305:        }
                    306:
                    307:        /* Cleanup and error handling. */
                    308:        free(fname);
                    309:        if (ferror(stdin))
                    310:                err(1, "standard input");
                    311:        if (fd == -1)
                    312:                errx(1, "No input file names found on standard input");
                    313:
                    314:        /* Dump results. */
                    315:        for (i = 0; i < tablei; i++)
1.2     ! schwarze  316:                if (table[i].count != -1)
        !           317:                        printf("%d\t%s\t%s\n", table[i].count,
        !           318:                            table[i].parent, table[i].child);
1.1       schwarze  319:        return 0;
                    320: }

CVSweb