Hi all,
below is a possible replacement for rcs.c's rcs_expand_keywords.
Since my use case doesn't care about time zones, the rcs_set_tz is
missing, but otherwise it should work without further changes.
The memrchr might need to be replaced with an inline loop, but that's
one of the few saner GNU extensions recently...

Most importantly, this actually has a readable version of the $Log$
handling.

Joerg

#if 0
static const struct rcs_kw rcs_expkw[] =  {
        ...
        { "Locker",     RCS_KW_LOCKER   },
};
#endif

static void
buf_puts(BUF *b, const char *str)
{
        buf_append(b, str, strlen(str));
}

static BUF *
rcs_expand_keywords(char *rcsfile, struct rcs_delta *rdp, BUF *bp, int mode)
{
        BUF *newbuf;
        u_char *c, *kw, *fin;
        char buf[256];
        u_char *line, *line2;
        u_int i, j;
        int kwtype;
        int found;

        newbuf = buf_alloc(buf_len(bp));

        /*
         * Keyword formats:
         * $Keyword$
         * $Keyword: value$
         */
        c = buf_get(bp);
        fin = c + buf_len(bp);
        /* Copying to newbuf is deferred until the first keyword. */
        found = 0;

        while (c < fin) {
                kw = memchr(c, '$', fin - c);
                if (kw == NULL)
                        break;
                ++kw;
                if (found) {
                        /* Copy everything up to and including the $ */
                        buf_append(newbuf, c, kw - c);
                }
                c = kw;
                /* c points after the $ now */
                if (c == fin)
                        break;
                if (!isalpha(*c)) /* All valid keywords start with a letter */
                        continue;

                for (i = 0; i < RCS_NKWORDS; ++i) {
                        size_t kwlen;

                        kwlen = strlen(rcs_expkw[i].kw_str);
                        /*
                         * kwlen must be less than clen since clen
                         * includes either a terminating `$' or a `:'.
                         */
                        if (c + kwlen < fin &&
                            memcmp(c , rcs_expkw[i].kw_str, kwlen) == 0 &&
                            (c[kwlen] == '$' || c[kwlen] == ':')) {
                                c += kwlen;
                                break;
                        }
                }
                if (i == RCS_NKWORDS)
                        continue;
                kwtype = rcs_expkw[i].kw_type;

                /*
                 * if the next character is ':' we need to look for
                 * an '$' before the end of the line to be sure it is
                 * in fact a keyword.
                 */
                if (*c == ':') {
                        for (; c < fin; ++c) {
                                if (*c == '$' || *c == '\n')
                                        break;
                        }

                        if (*c != '$') {
                                if (found)
                                        buf_append(newbuf, kw, c - kw);
                                continue;
                        }
                }
                ++c;

                if (!found) {
                        found = 1;
                        /* Copy everything up to and including the $ */
                        buf_append(newbuf, buf_get(bp), kw - buf_get(bp));
                }

                if (mode & RCS_KWEXP_NAME) {
                        buf_puts(newbuf, rcs_expkw[i].kw_str);
                        if (mode & RCS_KWEXP_VAL)
                                buf_puts(newbuf, ": ");
                }

                /* Order matters because of RCS_KW_ID and RCS_KW_HEADER. */
                if (mode & RCS_KWEXP_VAL) {
                        if (kwtype & RCS_KW_RCSFILE) {
                                char *tmpf;
                                if ((kwtype & RCS_KW_FULLPATH) ||
                                    (tmpf = strrchr(rcsfile, '/')) == NULL)
                                        buf_puts(newbuf, rcsfile);
                                else
                                        buf_puts(newbuf, tmpf + 1);
                                buf_putc(newbuf, ' ');
                        }

                        if (kwtype & RCS_KW_REVISION) {
                                rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
                                buf_puts(newbuf, buf);
                                buf_putc(newbuf, ' ');
                        }

                        if (kwtype & RCS_KW_DATE) {
                                strftime(buf, sizeof(buf),
                                    "%Y/%m/%d %H:%M:%S ", &rdp->rd_date);
                                buf_puts(newbuf, buf);
                        }

                        if (kwtype & RCS_KW_AUTHOR) {
                                buf_puts(newbuf, rdp->rd_author);
                                buf_putc(newbuf, ' ');
                        }

                        if (kwtype & RCS_KW_STATE) {
                                buf_puts(newbuf, rdp->rd_state);
                                buf_putc(newbuf, ' ');
                        }

                        /* order does not matter anymore below */
                        if (kwtype & RCS_KW_SOURCE) {
                                buf_puts(newbuf, rcsfile);
                                buf_putc(newbuf, ' ');
                        }

                        if (kwtype & RCS_KW_NAME)
                                buf_putc(newbuf, ' ');

                        if ((kwtype & RCS_KW_LOCKER)) {
                                if (rdp->rd_locker)
                                        buf_puts(newbuf, rdp->rd_locker);
                                buf_putc(newbuf, ' ');
                        }
                }

                /* end the expansion */
                if (mode & RCS_KWEXP_NAME)
                        buf_putc(newbuf, '$');


                if (kwtype & RCS_KW_LOG) {
                        line = memrchr(buf_get(bp), '\n', kw - buf_get(bp) - 1);
                        if (line == NULL)
                                line = buf_get(bp);
                        else
                                ++line;
                        line2 = kw - 1;
                        while (line2 > line && line2[-1] == ' ')
                                --line2;

                        buf_putc(newbuf, '\n');
                        buf_append(newbuf, line, kw - 1 - line);
                        buf_puts(newbuf, "Revision ");
                        rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
                        buf_puts(newbuf, buf);

                        buf_puts(newbuf, "  ");
                        strftime(buf, sizeof(buf), "%Y/%m/%d %H:%M:%S",
                            &rdp->rd_date);
                        buf_puts(newbuf, buf);

                        buf_puts(newbuf, "  ");
                        buf_puts(newbuf, rdp->rd_author);
                        buf_putc(newbuf, '\n');

                        for (i = 0; rdp->rd_log[i]; i += j) {
                                j = strcspn(rdp->rd_log + i, "\n");
                                if (j == 0)
                                        buf_append(newbuf, line, line2 - line);
                                else
                                        buf_append(newbuf, line, kw - 1 - line);
                                if (rdp->rd_log[i + j])
                                        ++j;
                                buf_append(newbuf, rdp->rd_log + i, j);
                        }
                        buf_append(newbuf, line, line2 - line);
                        for (j = 0; c + j < fin; ++j) {
                                if (c[j] != ' ')
                                        break;
                        }
                        if (c[j] == '\n' || c + j == fin)
                                c += j;
                }
        }

        if (found) {
                buf_append(newbuf, c, fin - c);
                buf_free(bp);
                return (newbuf);
        } else {
                buf_free(newbuf);
                return (bp);
        }
}

Reply via email to