=================================================================== RCS file: /cvs/pod2mdoc/pod2mdoc.c,v retrieving revision 1.13 retrieving revision 1.21 diff -u -p -r1.13 -r1.21 --- pod2mdoc/pod2mdoc.c 2014/04/01 13:05:50 1.13 +++ pod2mdoc/pod2mdoc.c 2014/04/03 11:55:01 1.21 @@ -1,4 +1,4 @@ -/* $Id: pod2mdoc.c,v 1.13 2014/04/01 13:05:50 kristaps Exp $ */ +/* $Id: pod2mdoc.c,v 1.21 2014/04/03 11:55:01 kristaps Exp $ */ /* * Copyright (c) 2014 Kristaps Dzonsons * @@ -27,7 +27,9 @@ #include /* - * In what section can we find Perl manuals? + * In what section can we find Perl module manuals? + * Sometimes (Mac OS X) it's 3pm, sometimes (OpenBSD, etc.) 3p. + * XXX IF YOU CHANGE THIS, CHANGE POD2MDOC.1 AS WELL. */ #define PERL_SECTION "3p" @@ -170,76 +172,133 @@ formatescape(const char *buf, size_t *start, size_t en /* * Run some heuristics to intuit a link format. - * I recognise L as a Perl manpage, printing it in section 3p; - * or a general UNIX foo(5) manpage. - * If I recognise one, I set "start" to be the end of the sequence so + * I set "start" to be the end of the sequence (last right-carrot) so * that the caller can safely just continue processing. - * Otherwise, I don't touch "start". + * If this is just an empty tag, I'll return 0. */ static int trylink(const char *buf, size_t *start, size_t end, size_t dsz) { - size_t sv, nstart, nend, i, j; - int hasdouble; + size_t linkstart, realend, linkend, + i, j, textsz, stack; + const char *text; /* * Scan to the start of the terminus. * This function is more or less replicated in the formatcode() * for null or index formatting codes. */ - hasdouble = 0; - for (sv = nstart = *start; nstart < end; nstart++) { - /* Do we have a double-colon? */ - if (':' == buf[nstart] && - nstart > sv && - ':' == buf[nstart - 1]) - hasdouble = 1; - if ('>' != buf[nstart]) + stack = 0; + for (linkstart = realend = *start; realend < end; realend++) { + if ('>' != buf[realend]) continue; else if (dsz == 1) break; - assert(nstart > 0); - if (' ' != buf[nstart - 1]) + assert(realend > 0); + if (' ' != buf[realend - 1]) continue; - i = nstart; - for (j = 0; i < end && j < dsz; j++) + for (i = realend, j = 0; i < end && j < dsz; j++) if ('>' != buf[i++]) break; if (dsz == j) break; } - - /* We don't care about stubs. */ - if (nstart == end || nstart == *start) + + /* Ignore stubs. */ + if (realend == end || realend == *start) return(0); - /* Set nend to the end of content. */ - nend = nstart; - if (dsz > 1) - nend--; + /* Set linkend to the end of content. */ + linkend = dsz > 1 ? realend - 1 : realend; - /* - * Provide for some common invocations of the link primitive. - * First, allow us to link to other Perl manuals. - */ - if (hasdouble) + /* Re-scan to see if we have a title or section. */ + text = &buf[*start]; + for (textsz = *start; textsz < linkend; textsz++) + if ('|' == buf[textsz] || '/' == buf[textsz]) + break; + + if (textsz < linkend && '|' == buf[textsz]) { + /* With title: set start, then end at section. */ + linkstart = textsz + 1; + textsz = textsz - *start; + for (i = linkstart; i < linkend; i++) + if ('/' == buf[i]) + break; + if (i < linkend) + linkend = i; + } else if (textsz < linkend && '/' == buf[textsz]) { + /* With section: set end at section. */ + linkend = textsz; + textsz = 0; + } else + /* No title, no section. */ + textsz = 0; + + *start = realend; + j = linkend - linkstart; + + /* Do we have only subsection material? */ + if (0 == j && '/' == buf[linkend]) { + linkstart = linkend + 1; + linkend = dsz > 1 ? realend - 1 : realend; + if (0 == (j = linkend - linkstart)) + return(0); + printf("Sx %.*s", (int)j, &buf[linkstart]); + return(1); + } else if (0 == j) + return(0); + + /* See if we qualify as being a link or not. */ + if ((j > 4 && 0 == memcmp("http:", &buf[linkstart], j)) || + (j > 5 && 0 == memcmp("https:", &buf[linkstart], j)) || + (j > 3 && 0 == memcmp("ftp:", &buf[linkstart], j)) || + (j > 4 && 0 == memcmp("sftp:", &buf[linkstart], j)) || + (j > 3 && 0 == memcmp("smb:", &buf[linkstart], j)) || + (j > 3 && 0 == memcmp("afs:", &buf[linkstart], j))) { + /* Gross. */ + printf("Lk %.*s", (int)((dsz > 1 ? realend - 1 : + realend) - linkstart), &buf[linkstart]); + return(1); + } + + /* See if we qualify as a mailto. */ + if (j > 6 && 0 == memcmp("mailto:", &buf[linkstart], j)) { + printf("Mt %.*s", (int)j, &buf[linkstart]); + return(1); + } + + /* See if we're a foo(5), foo(5x), or foo(5xx) manpage. */ + if ((j > 3 && ')' == buf[linkend - 1]) && + ('(' == buf[linkend - 3])) { + printf("Xr %.*s %c", (int)(j - 3), + &buf[linkstart], buf[linkend - 2]); + return(1); + } else if ((j > 4 && ')' == buf[linkend - 1]) && + ('(' == buf[linkend - 4])) { + printf("Xr %.*s %.*s", (int)(j - 4), + &buf[linkstart], 2, &buf[linkend - 3]); + return(1); + } else if ((j > 5 && ')' == buf[linkend - 1]) && + ('(' == buf[linkend - 5])) { + printf("Xr %.*s %.*s", (int)(j - 5), + &buf[linkstart], 3, &buf[linkend - 4]); + return(1); + } + + /* Last try: do we have a double-colon? */ + for (i = linkstart + 1; i < linkend; i++) + if (':' == buf[i] && ':' == buf[i - 1]) + break; + + if (i < linkend) printf("Xr %.*s " PERL_SECTION, - (int)(nend - sv), &buf[sv]); - else if (nend - sv > 3 && isalnum(buf[sv]) && - ')' == buf[nend - 1] && - isdigit((int)buf[nend - 2]) && - '(' == buf[nend - 3]) - printf("Xr %.*s %c", - (int)(nend - 3 - sv), - &buf[sv], buf[nend - 2]); + (int)j, &buf[linkstart]); else - return(0); + printf("Xr %.*s 1", (int)j, &buf[linkstart]); - *start = nstart; return(1); } - /* * Doclifting: if we're a bold "-xx" and we're in the SYNOPSIS section, * then it's likely that we're a flag. @@ -252,10 +311,23 @@ dosynopsisfl(const char *buf, size_t *start, size_t en { size_t i; again: + assert(*start + 1 < end); + assert('-' == buf[*start]); + + if ( ! isalnum((int)buf[*start + 1]) && + '?' != buf[*start + 1] && + '-' != buf[*start + 1]) { + (*start)--; + fputs("Ar ", stdout); + return; + } + (*start)++; for (i = *start; i < end; i++) if (isalnum((int)buf[i])) continue; + else if ('?' == buf[i]) + continue; else if ('-' == buf[i]) continue; else if ('_' == buf[i]) @@ -302,13 +374,16 @@ again: * been printed to the current line. * If "nomacro", then we don't print any macros, just contained data * (e.g., following "Sh" or "Nm"). + * "pos" is only significant in SYNOPSIS, and should be 0 when invoked + * as the first format code on a line (for decoration as an "Nm"), + * non-zero otherwise. * Return whether we've printed a macro or not--in other words, whether * this should trigger a subsequent newline (this should be ignored when * reentrant). */ static int -formatcode(struct state *st, const char *buf, - size_t *start, size_t end, int reentrant, int nomacro) +formatcode(struct state *st, const char *buf, size_t *start, + size_t end, int reentrant, int nomacro, int pos) { enum fmt fmt; size_t i, j, dsz; @@ -414,17 +489,22 @@ formatcode(struct state *st, const char *buf, printf("Em "); break; case (FMT_BOLD): - if (SECT_SYNOPSIS == st->sect && - 1 == dsz && - '-' == buf[*start]) - dosynopsisfl(buf, start, end); - else - printf("Sy "); + if (SECT_SYNOPSIS == st->sect) { + if (1 == dsz && '-' == buf[*start]) + dosynopsisfl(buf, start, end); + else if (0 == pos) + printf("Nm "); + else + printf("Ar "); + break; + } + printf("Sy "); break; case (FMT_CODE): printf("Qo Li "); break; case (FMT_LINK): + /* Try to link; use "No" if it's empty. */ if ( ! trylink(buf, start, end, dsz)) printf("No "); break; @@ -467,7 +547,7 @@ formatcode(struct state *st, const char *buf, } } if (*start + 1 < end && '<' == buf[*start + 1]) { - formatcode(st, buf, start, end, 1, nomacro); + formatcode(st, buf, start, end, 1, nomacro, 1); continue; } @@ -529,7 +609,7 @@ formatcodeln(struct state *st, const char *buf, last = ' '; while (*start < end) { if (*start + 1 < end && '<' == buf[*start + 1]) { - formatcode(st, buf, start, end, 1, nomacro); + formatcode(st, buf, start, end, 1, nomacro, 1); continue; } /* @@ -847,6 +927,40 @@ dosynopsisop(const char *buf, int *last, } /* + * Format multiple "Nm" manpage names in the NAME section. + */ +static void +donamenm(struct state *st, const char *buf, size_t *start, size_t end) +{ + size_t word; + + while (*start < end && ' ' == buf[*start]) + (*start)++; + + if (end == *start) { + puts(".Nm unknown"); + return; + } + + while (*start < end) { + fputs(".Nm ", stdout); + for (word = *start; word < end; word++) + if (',' == buf[word]) + break; + formatcodeln(st, buf, start, word, 1); + if (*start == end) { + putchar('\n'); + continue; + } + assert(',' == buf[*start]); + puts(" ,"); + (*start)++; + while (*start < end && ' ' == buf[*start]) + (*start)++; + } +} + +/* * Ordinary paragraph. * Well, this is really the hardest--POD seems to assume that, for * example, a leading space implies a newline, and so on. @@ -858,6 +972,7 @@ static void ordinary(struct state *st, const char *buf, size_t start, size_t end) { size_t i, j, opstack; + int seq; if ( ! st->parsing || st->paused) return; @@ -869,8 +984,8 @@ ordinary(struct state *st, const char *buf, size_t sta * To wit, print out a "Nm" and "Nd" in that format. */ if (SECT_NAME == st->sect) { - for (i = end - 1; i > start; i--) - if ('-' == buf[i]) + for (i = end - 2; i > start; i--) + if ('-' == buf[i] && ' ' == buf[i + 1]) break; if ('-' == buf[i]) { j = i; @@ -878,11 +993,11 @@ ordinary(struct state *st, const char *buf, size_t sta for ( ; i > start; i--) if ('-' != buf[i]) break; - printf(".Nm "); - formatcodeln(st, buf, &start, i + 1, 1); - putchar('\n'); + donamenm(st, buf, &start, i + 1); start = j + 1; - printf(".Nd "); + while (start < end && ' ' == buf[start]) + start++; + fputs(".Nd ", stdout); formatcodeln(st, buf, &start, end, 1); putchar('\n'); return; @@ -896,7 +1011,7 @@ ordinary(struct state *st, const char *buf, size_t sta last = '\n'; opstack = 0; - while (start < end) { + for (seq = 0; start < end; seq++) { /* * Loop til we get either to a newline or escape. * Escape initial control characters. @@ -935,8 +1050,19 @@ ordinary(struct state *st, const char *buf, size_t sta * following that, a newline. * Consume all whitespace so we don't * accidentally start an implicit literal line. + * If the macro ends with a flush comma or + * period, let mdoc(7) handle it for us. */ - if (formatcode(st, buf, &start, end, 0, 0)) { + if (formatcode(st, buf, &start, end, 0, 0, seq)) { + if ((start == end - 1 || + (start < end - 1 && + (' ' == buf[start + 1] || + '\n' == buf[start + 1]))) && + ('.' == buf[start] || + ',' == buf[start])) { + putchar(' '); + putchar(buf[start++]); + } putchar(last = '\n'); while (start < end && ' ' == buf[start]) start++;