=================================================================== RCS file: /cvs/pod2mdoc/pod2mdoc.c,v retrieving revision 1.36 retrieving revision 1.46 diff -u -p -r1.36 -r1.46 --- pod2mdoc/pod2mdoc.c 2014/10/24 00:28:34 1.36 +++ pod2mdoc/pod2mdoc.c 2015/02/14 15:34:39 1.46 @@ -1,6 +1,7 @@ -/* $Id: pod2mdoc.c,v 1.36 2014/10/24 00:28:34 schwarze Exp $ */ +/* $Id: pod2mdoc.c,v 1.46 2015/02/14 15:34:39 schwarze Exp $ */ /* * Copyright (c) 2014 Kristaps Dzonsons + * Copyright (c) 2014, 2015 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -26,6 +27,8 @@ #include #include +#include "dict.h" + /* * In what section can we find Perl module manuals? * Sometimes (Mac OS X) it's 3pm, sometimes (OpenBSD, etc.) 3p. @@ -132,7 +135,7 @@ static const char fmts[FMT__MAX] = { 'Z' /* FMT_NULL */ }; -static int last; +static unsigned char last; static void @@ -157,7 +160,6 @@ outbuf_addchar(struct state *st) if ('\\' == last) st->outbuf[st->outbuflen++] = 'e'; st->outbuf[st->outbuflen] = '\0'; - st->wantws = 0; } static void @@ -171,7 +173,6 @@ outbuf_addstr(struct state *st, const char *str) memcpy(st->outbuf + st->outbuflen, str, slen+1); st->outbuflen += slen; last = str[slen - 1]; - st->wantws = 0; } static void @@ -181,6 +182,9 @@ outbuf_flush(struct state *st) if (0 == st->outbuflen) return; + if (OUST_TXT == st->oust && st->wantws) + putchar(' '); + fputs(st->outbuf, stdout); *st->outbuf = '\0'; st->outbuflen = 0; @@ -474,8 +478,10 @@ static int formatcode(struct state *st, const char *buf, size_t *start, size_t end, int nomacro, int pos) { - enum fmt fmt; size_t i, j, dsz; + enum fmt fmt; + int wantws; + unsigned char uc; assert(*start + 1 < end); assert('<' == buf[*start + 1]); @@ -559,7 +565,8 @@ formatcode(struct state *st, const char *buf, size_t * * allows to break at this point as well. */ - st->wantws |= ' ' == buf[*start]; + wantws = ' ' == buf[*start] || + (OUST_MAC == st->oust ? st->wantws : ! st->outbuflen); /* * If we are on a text line and there is no @@ -569,22 +576,23 @@ formatcode(struct state *st, const char *buf, size_t * * lest we clobber out output state. */ - if (OUST_MAC != st->oust && !st->wantws) { + if (OUST_MAC != st->oust && ! wantws) { if (OUST_NL != st->oust) putchar('\n'); printf(".Pf "); + st->wantws = 0; } outbuf_flush(st); /* Whitespace is easier to suppress on macro lines. */ - if (OUST_MAC == st->oust && !st->wantws) + if (OUST_MAC == st->oust && ! wantws) printf(" Ns "); /* Unless we are on a macro line, start one. */ - if (OUST_MAC != st->oust && st->wantws) { + if (OUST_MAC != st->oust && wantws) { if (OUST_NL != st->oust) putchar('\n'); putchar('.'); @@ -609,13 +617,28 @@ formatcode(struct state *st, const char *buf, size_t * else printf("Ar "); break; - } - if (0 == strncmp(buf + *start, "NULL", 4) && - ('=' == buf[*start + 4] || - '>' == buf[*start + 4])) + } + i = 0; + uc = buf[*start]; + while (isalnum(uc) || '_' == uc || ' ' == uc) + uc = buf[*start + ++i]; + if ('=' != uc && '>' != uc) + i = 0; + if (4 == i && ! strncmp(buf + *start, "NULL", 4)) { printf("Dv "); - else + break; + } + switch (i ? dict_get(buf + *start, i) : MDOC_MAX) { + case MDOC_Fa: + printf("Fa "); + break; + case MDOC_Vt: + printf("Vt "); + break; + default: printf("Sy "); + break; + } break; case (FMT_CODE): printf("Qo Li "); @@ -668,7 +691,8 @@ formatcode(struct state *st, const char *buf, size_t * } if (*start + 1 < end && '<' == buf[*start + 1] && 'A' <= buf[*start] && 'Z' >= buf[*start]) { - formatcode(st, buf, start, end, nomacro, 1); + if ( ! formatcode(st, buf, start, end, nomacro, 1)) + st->wantws = 1; continue; } @@ -710,14 +734,11 @@ formatcode(struct state *st, const char *buf, size_t * putchar('e'); } - if (FMT__MAX == fmt) - return(0); - if ( ! nomacro && FMT_CODE == fmt) printf(" Qc "); st->wantws = ' ' == last; - return(1); + return(FMT__MAX != fmt); } /* @@ -1003,6 +1024,32 @@ command(struct state *st, const char *buf, size_t star } /* + * Put the type provided as an argument into the dictionary. + */ +static void +register_type(const char *ptype) +{ + const char *pname, *pend; + + pname = ptype; + while (isalnum((unsigned char)*pname) || '_' == *pname) + pname++; + if ((pname - ptype == 6 && ! strncmp(ptype, "struct", 6)) || + (pname - ptype == 4 && ! strncmp(ptype, "enum", 4))) { + while (' ' == *pname) + pname++; + pend = pname; + while (isalnum((unsigned char)*pend) || '_' == *pend) + pend++; + if (pend > pname) + dict_put(pname, pend - pname, MDOC_Vt); + } else + pend = pname; + if (pend > ptype) + dict_put(ptype, pend - ptype, MDOC_Vt); +} + +/* * Just pump out the line in a verbatim block. * From the perspective of external callers, * always stays in OUST_NL/wantws mode. @@ -1011,7 +1058,7 @@ static void verbatim(struct state *st, char *buf, size_t start, size_t end) { size_t i, ift, ifo, ifa, ifc, inl; - char *cp; + char *cp, *cp2; int nopen; if ( ! st->parsing || st->paused || start == end) @@ -1046,11 +1093,21 @@ again: start++; if (start < end && '\n' == buf[start]) start++; - if (start < end) - goto again; - return; + goto again; } + /* Other preprocessor directives. */ + if ('#' == buf[i]) { + fputs(".Fd ", stdout); + start = i; + while(start < end && '\n' != buf[start]) + putchar(buf[start++]); + putchar('\n'); + if (start < end && '\n' == buf[start]) + start++; + goto again; + } + /* Parse function declaration. */ ifo = ifa = ifc = 0; inl = end; @@ -1063,6 +1120,8 @@ again: break; } switch (buf[i]) { + case '\t': + /* FALLTHROUGH */ case ' ': if ( ! ifa) ifo = i; @@ -1099,6 +1158,7 @@ again: if (buf[i] == '\n') buf[i] = ' '; buf[ifo++] = '\0'; + register_type(buf + ift); printf(".Ft %s", buf + ift); if (buf[ifo] == '*') { fputs(" *", stdout); @@ -1107,15 +1167,25 @@ again: putchar('\n'); buf[ifa++] = '\0'; printf(".Fo %s\n", buf + ifo); + dict_put(buf + ifo, 0, MDOC_Fo); buf[ifc++] = '\0'; for (;;) { cp = strchr(buf + ifa, ','); - if (cp != NULL) + if (cp != NULL) { + cp2 = cp; *cp++ = '\0'; + } else + cp2 = strchr(buf + ifa, '\0'); + while (isalnum((unsigned char)cp2[-1]) || + '_' == cp2[-1]) + cp2--; + if ('\0' != *cp2) + dict_put(cp2, 0, MDOC_Fa); + register_type(buf + ifa); printf(".Fa \"%s\"\n", buf + ifa); if (cp == NULL) break; - while (*cp == ' ') + while (*cp == ' ' || *cp == '\t') cp++; ifa = cp - buf; } @@ -1253,14 +1323,16 @@ donamenm(struct state *st, const char *buf, size_t *st * * Uses formatcode() to go to OUST_MAC mode * and outbuf_flush() to go to OUST_TXT mode. - * Main text mode wantws handling is in this function. + * In text mode, wantws requests white space before the text + * currently contained in the outbuf, not before upcoming text. * Must make sure to go back to OUST_NL/wantws mode before returning. */ static void ordinary(struct state *st, const char *buf, size_t start, size_t end) { - size_t i, j, opstack; - int seq; + size_t i, j, opstack, wend; + enum mdoc_type mtype; + int eos, noeos, seq; if ( ! st->parsing || st->paused) return; @@ -1326,19 +1398,89 @@ ordinary(struct state *st, const char *buf, size_t sta &start, end, &opstack)) continue; + /* Merely buffer non-whitespace. */ + + last = buf[start++]; + if ( ! isspace(last)) + outbuf_addchar(st); + if (start < end && + ! isspace((unsigned char)buf[start])) + continue; + /* + * Found the end of a word. + * Rewind trailing delimiters. + */ + + eos = noeos = 0; + for (wend = st->outbuflen; wend; wend--) + if ('.' == st->outbuf[wend - 1] || + '!' == st->outbuf[wend - 1] || + '?' == st->outbuf[wend - 1]) + eos = 1; + else if ('|' == st->outbuf[wend - 1] || + ',' == st->outbuf[wend - 1] || + ';' == st->outbuf[wend - 1] || + ':' == st->outbuf[wend - 1]) + noeos = 1; + else if ('\'' != st->outbuf[wend - 1] && + '"' != st->outbuf[wend - 1] && + ')' != st->outbuf[wend - 1] && + ']' != st->outbuf[wend - 1]) + break; + eos &= ! noeos; + + /* + * Detect function names. + */ + + mtype = MDOC_Fa; + if (wend && ')' == st->outbuf[wend] && + '(' == st->outbuf[wend - 1]) { + mtype = dict_get(st->outbuf, --wend); + if (MDOC_Fo == mtype || MDOC_MAX == mtype) { + st->outbuflen = wend; + st->outbuf[wend] = '\0'; + mdoc_newln(st); + if (MDOC_Fo == mtype) + fputs(".Fn ", stdout); + else + fputs(".Xr ", stdout); + st->oust = OUST_MAC; + } + } + + /* * On whitespace, flush the output buffer * and allow breaking to a macro line. - * Otherwise, buffer text and clear wantws. */ - last = buf[start++]; - if (' ' == last) { - outbuf_flush(st); - putchar(' '); - st->wantws = 1; - } else - outbuf_addchar(st); + outbuf_flush(st); + + /* + * End macro lines, and + * end text lines at the end of sentences. + */ + + if (OUST_MAC == st->oust || (eos && wend > 1 && + islower((unsigned char)st->outbuf[wend - 1]))) { + if (MDOC_MAX == mtype) + fputs(" 3", stdout); + if (MDOC_Fa != mtype) + for (wend += 2; + '\0' != st->outbuf[wend]; + wend++) + printf(" %c", + st->outbuf[wend]); + mdoc_newln(st); + } + + /* Advance to the next word. */ + + while ('\n' != buf[start] && + isspace((unsigned char)buf[start])) + start++; + st->wantws = 1; } if (start < end - 1 && '<' == buf[start + 1] && @@ -1443,7 +1585,7 @@ dofile(const struct args *args, const char *fname, { char datebuf[64]; struct state st; - const char *fbase, *fext, *section, *date; + const char *fbase, *fext, *section, *date, *format; char *title, *cp; size_t sup, end, i, cur = 0; @@ -1488,8 +1630,12 @@ dofile(const struct args *args, const char *fname, /* Date. Or the given "tm" if not supplied. */ - if (NULL == (date = args->date)) { - strftime(datebuf, sizeof(datebuf), "%B %d, %Y", tm); + date = args->date; + format = (NULL == date) ? "%B %d, %Y" : + strcmp(date, "Mdocdate") ? NULL : "$Mdocdate: February 14 2015 $"; + + if (NULL != format) { + strftime(datebuf, sizeof(datebuf), format, tm); date = datebuf; } @@ -1504,6 +1650,7 @@ dofile(const struct args *args, const char *fname, free(title); + dict_init(); memset(&st, 0, sizeof(struct state)); st.oust = OUST_NL; st.wantws = 1; @@ -1531,6 +1678,7 @@ dofile(const struct args *args, const char *fname, dopar(&st, buf, cur, end); cur = sup; } + dict_destroy(); } /*