+
+static int
+post_dt(POST_ARGS)
+{
+ struct mdoc_node *nn, *n;
+ const char *cp;
+ char *p;
+
+ n = mdoc->last;
+
+ if (mdoc->meta.title)
+ free(mdoc->meta.title);
+ if (mdoc->meta.vol)
+ free(mdoc->meta.vol);
+ if (mdoc->meta.arch)
+ free(mdoc->meta.arch);
+
+ mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
+
+ /* First check that all characters are uppercase. */
+
+ if (NULL != (nn = n->child))
+ for (p = nn->string; *p; p++) {
+ if (toupper((unsigned char)*p) == *p)
+ continue;
+ mandoc_msg(MANDOCERR_TITLE_CASE,
+ mdoc->parse, nn->line,
+ nn->pos + (p - nn->string),
+ nn->string);
+ break;
+ }
+
+ /* Handles: `.Dt'
+ * title = unknown, volume = local, msec = 0, arch = NULL
+ */
+
+ if (NULL == (nn = n->child)) {
+ /* XXX: make these macro values. */
+ /* FIXME: warn about missing values. */
+ mdoc->meta.title = mandoc_strdup("UNKNOWN");
+ mdoc->meta.vol = mandoc_strdup("LOCAL");
+ mdoc->meta.msec = mandoc_strdup("1");
+ return(1);
+ }
+
+ /* Handles: `.Dt TITLE'
+ * title = TITLE, volume = local, msec = 0, arch = NULL
+ */
+
+ mdoc->meta.title = mandoc_strdup(
+ '\0' == nn->string[0] ? "UNKNOWN" : nn->string);
+
+ if (NULL == (nn = nn->next)) {
+ /* FIXME: warn about missing msec. */
+ /* XXX: make this a macro value. */
+ mdoc->meta.vol = mandoc_strdup("LOCAL");
+ mdoc->meta.msec = mandoc_strdup("1");
+ return(1);
+ }
+
+ /* Handles: `.Dt TITLE SEC'
+ * title = TITLE,
+ * volume = SEC is msec ? format(msec) : SEC,
+ * msec = SEC is msec ? atoi(msec) : 0,
+ * arch = NULL
+ */
+
+ cp = mandoc_a2msec(nn->string);
+ if (cp) {
+ mdoc->meta.vol = mandoc_strdup(cp);
+ mdoc->meta.msec = mandoc_strdup(nn->string);
+ } else {
+ mandoc_msg(MANDOCERR_MSEC_BAD, mdoc->parse,
+ nn->line, nn->pos, nn->string);
+ mdoc->meta.vol = mandoc_strdup(nn->string);
+ mdoc->meta.msec = mandoc_strdup(nn->string);
+ }
+
+ if (NULL == (nn = nn->next))
+ return(1);
+
+ /* Handles: `.Dt TITLE SEC VOL'
+ * title = TITLE,
+ * volume = VOL is vol ? format(VOL) :
+ * VOL is arch ? format(arch) :
+ * VOL
+ */
+
+ cp = mdoc_a2vol(nn->string);
+ if (cp) {
+ free(mdoc->meta.vol);
+ mdoc->meta.vol = mandoc_strdup(cp);
+ } else {
+ cp = mdoc_a2arch(nn->string);
+ if (NULL == cp) {
+ mandoc_msg(MANDOCERR_ARCH_BAD, mdoc->parse,
+ nn->line, nn->pos, nn->string);
+ free(mdoc->meta.vol);
+ mdoc->meta.vol = mandoc_strdup(nn->string);
+ } else
+ mdoc->meta.arch = mandoc_strdup(cp);
+ }
+
+ /* Ignore any subsequent parameters... */
+ /* FIXME: warn about subsequent parameters. */
+
+ return(1);
+}
+
+static int
+post_prol(POST_ARGS)
+{
+ /*
+ * Remove prologue macros from the document after they're
+ * processed. The final document uses mdoc_meta for these
+ * values and discards the originals.
+ */
+
+ mdoc_node_delete(mdoc, mdoc->last);
+ if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
+ mdoc->flags |= MDOC_PBODY;
+
+ return(1);
+}
+
+static int
+post_bx(POST_ARGS)
+{
+ struct mdoc_node *n;
+
+ /*
+ * Make `Bx's second argument always start with an uppercase
+ * letter. Groff checks if it's an "accepted" term, but we just
+ * uppercase blindly.
+ */
+
+ n = mdoc->last->child;
+ if (n && NULL != (n = n->next))
+ *n->string = (char)toupper((unsigned char)*n->string);
+
+ return(1);
+}
+
+static int
+post_os(POST_ARGS)
+{
+#ifndef OSNAME
+ struct utsname utsname;
+ static char *defbuf;
+#endif
+ struct mdoc_node *n;
+
+ n = mdoc->last;
+
+ /*
+ * Set the operating system by way of the `Os' macro.
+ * The order of precedence is:
+ * 1. the argument of the `Os' macro, unless empty
+ * 2. the -Ios=foo command line argument, if provided
+ * 3. -DOSNAME="\"foo\"", if provided during compilation
+ * 4. "sysname release" from uname(3)
+ */
+
+ free(mdoc->meta.os);
+ mdoc->meta.os = NULL;
+ mdoc_deroff(&mdoc->meta.os, n);
+ if (mdoc->meta.os)
+ return(1);
+
+ if (mdoc->defos) {
+ mdoc->meta.os = mandoc_strdup(mdoc->defos);
+ return(1);
+ }
+
+#ifdef OSNAME
+ mdoc->meta.os = mandoc_strdup(OSNAME);
+#else /*!OSNAME */
+ if (NULL == defbuf) {
+ if (-1 == uname(&utsname)) {
+ mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
+ defbuf = mandoc_strdup("UNKNOWN");
+ } else
+ mandoc_asprintf(&defbuf, "%s %s",
+ utsname.sysname, utsname.release);
+ }
+ mdoc->meta.os = mandoc_strdup(defbuf);
+#endif /*!OSNAME*/
+ return(1);
+}
+
+static int
+post_std(POST_ARGS)
+{
+ struct mdoc_node *nn, *n;
+
+ n = mdoc->last;
+
+ /*
+ * Macros accepting `-std' as an argument have the name of the
+ * current document (`Nm') filled in as the argument if it's not
+ * provided.
+ */
+
+ if (n->child)
+ return(1);
+
+ if (NULL == mdoc->meta.name)
+ return(1);
+
+ nn = n;
+ mdoc->next = MDOC_NEXT_CHILD;
+
+ if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
+ return(0);
+
+ mdoc->last = nn;
+ return(1);
+}
+
+static enum mdoc_sec
+a2sec(const char *p)
+{
+ int i;
+
+ for (i = 0; i < (int)SEC__MAX; i++)
+ if (secnames[i] && 0 == strcmp(p, secnames[i]))
+ return((enum mdoc_sec)i);
+
+ return(SEC_CUSTOM);
+}
+
+static size_t
+macro2len(enum mdoct macro)
+{
+
+ switch (macro) {
+ case MDOC_Ad:
+ return(12);
+ case MDOC_Ao:
+ return(12);
+ case MDOC_An:
+ return(12);
+ case MDOC_Aq:
+ return(12);
+ case MDOC_Ar:
+ return(12);
+ case MDOC_Bo:
+ return(12);
+ case MDOC_Bq:
+ return(12);
+ case MDOC_Cd:
+ return(12);
+ case MDOC_Cm:
+ return(10);
+ case MDOC_Do:
+ return(10);
+ case MDOC_Dq:
+ return(12);
+ case MDOC_Dv:
+ return(12);
+ case MDOC_Eo:
+ return(12);
+ case MDOC_Em:
+ return(10);
+ case MDOC_Er:
+ return(17);
+ case MDOC_Ev:
+ return(15);
+ case MDOC_Fa:
+ return(12);
+ case MDOC_Fl:
+ return(10);
+ case MDOC_Fo:
+ return(16);
+ case MDOC_Fn:
+ return(16);
+ case MDOC_Ic:
+ return(10);
+ case MDOC_Li:
+ return(16);
+ case MDOC_Ms:
+ return(6);
+ case MDOC_Nm:
+ return(10);
+ case MDOC_No:
+ return(12);
+ case MDOC_Oo:
+ return(10);
+ case MDOC_Op:
+ return(14);
+ case MDOC_Pa:
+ return(32);
+ case MDOC_Pf:
+ return(12);
+ case MDOC_Po:
+ return(12);
+ case MDOC_Pq:
+ return(12);
+ case MDOC_Ql:
+ return(16);
+ case MDOC_Qo:
+ return(12);
+ case MDOC_So:
+ return(12);
+ case MDOC_Sq:
+ return(12);
+ case MDOC_Sy:
+ return(6);
+ case MDOC_Sx:
+ return(16);
+ case MDOC_Tn:
+ return(10);
+ case MDOC_Va:
+ return(12);
+ case MDOC_Vt:
+ return(12);
+ case MDOC_Xr:
+ return(10);
+ default:
+ break;
+ };
+ return(0);
+}