]> git.cameronkatri.com Git - mandoc.git/blobdiff - mandoc.c
Place mandoc.css into the public domain.
[mandoc.git] / mandoc.c
index 5a7ffc7b1f545fdae7b055588271aff2e93835b7..5476fb410e2d2f25d706f1be70b7ffb0865e9775 100644 (file)
--- a/mandoc.c
+++ b/mandoc.c
@@ -1,7 +1,7 @@
-/*     $Id: mandoc.c,v 1.94 2015/10/06 18:32:19 schwarze Exp $ */
+/*     $Id: mandoc.c,v 1.108 2018/10/25 01:32:40 schwarze Exp $ */
 /*
  * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2011-2015 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011-2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
 #include <string.h>
 #include <time.h>
 
-#include "mandoc.h"
 #include "mandoc_aux.h"
+#include "mandoc.h"
+#include "roff.h"
 #include "libmandoc.h"
 
-#define DATESIZE 32
-
 static int      a2time(time_t *, const char *, const char *);
 static char    *time2a(time_t);
 
@@ -42,7 +41,7 @@ enum mandoc_esc
 mandoc_escape(const char **end, const char **start, int *sz)
 {
        const char      *local_start;
-       int              local_sz;
+       int              local_sz, c, i;
        char             term;
        enum mandoc_esc  gly;
 
@@ -93,13 +92,12 @@ mandoc_escape(const char **end, const char **start, int *sz)
         * Escapes taking no arguments at all.
         */
        case 'd':
-               /* FALLTHROUGH */
        case 'u':
-               /* FALLTHROUGH */
        case ',':
-               /* FALLTHROUGH */
        case '/':
                return ESCAPE_IGNORE;
+       case 'p':
+               return ESCAPE_BREAK;
 
        /*
         * The \z escape is supposed to output the following
@@ -115,19 +113,12 @@ mandoc_escape(const char **end, const char **start, int *sz)
         * 'X' is the trigger.  These have opaque sub-strings.
         */
        case 'F':
-               /* FALLTHROUGH */
        case 'g':
-               /* FALLTHROUGH */
        case 'k':
-               /* FALLTHROUGH */
        case 'M':
-               /* FALLTHROUGH */
        case 'm':
-               /* FALLTHROUGH */
        case 'n':
-               /* FALLTHROUGH */
        case 'V':
-               /* FALLTHROUGH */
        case 'Y':
                gly = ESCAPE_IGNORE;
                /* FALLTHROUGH */
@@ -148,6 +139,13 @@ mandoc_escape(const char **end, const char **start, int *sz)
                        break;
                }
                break;
+       case '*':
+               if (strncmp(*start, "(.T", 3) != 0)
+                       abort();
+               gly = ESCAPE_DEVICE;
+               *start = ++*end;
+               *sz = 2;
+               break;
 
        /*
         * These escapes are of the form \X'Y', where 'X' is the trigger
@@ -155,15 +153,10 @@ mandoc_escape(const char **end, const char **start, int *sz)
         * The \B and \w escapes are handled in roff.c, roff_res().
         */
        case 'A':
-               /* FALLTHROUGH */
        case 'b':
-               /* FALLTHROUGH */
        case 'D':
-               /* FALLTHROUGH */
        case 'R':
-               /* FALLTHROUGH */
        case 'X':
-               /* FALLTHROUGH */
        case 'Z':
                gly = ESCAPE_IGNORE;
                /* FALLTHROUGH */
@@ -181,24 +174,28 @@ mandoc_escape(const char **end, const char **start, int *sz)
         * and 'N' resolves to a numerical expression.
         */
        case 'h':
-               /* FALLTHROUGH */
        case 'H':
-               /* FALLTHROUGH */
        case 'L':
-               /* FALLTHROUGH */
        case 'l':
-               /* FALLTHROUGH */
        case 'S':
-               /* FALLTHROUGH */
        case 'v':
-               /* FALLTHROUGH */
        case 'x':
                if (strchr(" %&()*+-./0123456789:<=>", **start)) {
                        if ('\0' != **start)
                                ++*end;
                        return ESCAPE_ERROR;
                }
-               gly = ESCAPE_IGNORE;
+               switch ((*start)[-1]) {
+               case 'h':
+                       gly = ESCAPE_HORIZ;
+                       break;
+               case 'l':
+                       gly = ESCAPE_HLINE;
+                       break;
+               default:
+                       gly = ESCAPE_IGNORE;
+                       break;
+               }
                term = **start;
                *start = ++*end;
                break;
@@ -247,9 +244,7 @@ mandoc_escape(const char **end, const char **start, int *sz)
                        term = '\'';
                        break;
                case '3':
-                       /* FALLTHROUGH */
                case '2':
-                       /* FALLTHROUGH */
                case '1':
                        *sz = (*end)[-1] == 's' &&
                            isdigit((unsigned char)(*end)[1]) ? 2 : 1;
@@ -307,30 +302,36 @@ mandoc_escape(const char **end, const char **start, int *sz)
 
        switch (gly) {
        case ESCAPE_FONT:
-               if (2 == *sz) {
-                       if ('C' == **start) {
+               if (*sz == 2) {
+                       if (**start == 'C') {
+                               if ((*start)[1] == 'W' ||
+                                   (*start)[1] == 'R') {
+                                       gly = ESCAPE_FONTCW;
+                                       break;
+                               }
                                /*
-                                * Treat constant-width font modes
+                                * Treat other constant-width font modes
                                 * just like regular font modes.
                                 */
                                (*start)++;
                                (*sz)--;
                        } else {
-                               if ('B' == (*start)[0] && 'I' == (*start)[1])
+                               if ((*start)[0] == 'B' && (*start)[1] == 'I')
                                        gly = ESCAPE_FONTBI;
                                break;
                        }
-               } else if (1 != *sz)
+               } else if (*sz != 1) {
+                       if (*sz == 0)
+                               gly = ESCAPE_FONTPREV;
                        break;
+               }
 
                switch (**start) {
                case '3':
-                       /* FALLTHROUGH */
                case 'B':
                        gly = ESCAPE_FONTBOLD;
                        break;
                case '2':
-                       /* FALLTHROUGH */
                case 'I':
                        gly = ESCAPE_FONTITALIC;
                        break;
@@ -338,15 +339,32 @@ mandoc_escape(const char **end, const char **start, int *sz)
                        gly = ESCAPE_FONTPREV;
                        break;
                case '1':
-                       /* FALLTHROUGH */
                case 'R':
                        gly = ESCAPE_FONTROMAN;
                        break;
                }
                break;
        case ESCAPE_SPECIAL:
-               if (1 == *sz && 'c' == **start)
-                       gly = ESCAPE_NOSPACE;
+               if (**start == 'c') {
+                       if (*sz == 1) {
+                               gly = ESCAPE_NOSPACE;
+                               break;
+                       }
+                       if (*sz < 6 || *sz > 7 ||
+                           strncmp(*start, "char", 4) != 0 ||
+                           (int)strspn(*start + 4, "0123456789") + 4 < *sz)
+                               break;
+                       c = 0;
+                       for (i = 4; i < *sz; i++)
+                               c = 10 * c + ((*start)[i] - '0');
+                       if (c < 0x21 || (c > 0x7e && c < 0xa0) || c > 0xff)
+                               break;
+                       *start += 4;
+                       *sz -= 4;
+                       gly = ESCAPE_NUMBERED;
+                       break;
+               }
+
                /*
                 * Unicode escapes are defined in groff as \[u0000]
                 * to \[u10FFFF], where the contained value must be
@@ -359,6 +377,9 @@ mandoc_escape(const char **end, const char **start, int *sz)
                        break;
                if (*sz == 6 && (*start)[1] == '0')
                        break;
+               if (*sz == 5 && (*start)[1] == 'D' &&
+                   strchr("89ABCDEF", (*start)[2]) != NULL)
+                       break;
                if ((int)strspn(*start + 1, "0123456789ABCDEFabcdef")
                    + 1 == *sz)
                        gly = ESCAPE_UNICODE;
@@ -503,17 +524,27 @@ time2a(time_t t)
         * up to 2 characters for the day + comma + blank
         * 4 characters for the year and a terminating '\0'
         */
+
        p = buf = mandoc_malloc(10 + 4 + 4 + 1);
 
-       if (0 == (ssz = strftime(p, 10 + 1, "%B ", tm)))
+       if ((ssz = strftime(p, 10 + 1, "%B ", tm)) == 0)
                goto fail;
        p += (int)ssz;
 
-       if (-1 == (isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday)))
+       /*
+        * The output format is just "%d" here, not "%2d" or "%02d".
+        * That's also the reason why we can't just format the
+        * date as a whole with "%B %e, %Y" or "%B %d, %Y".
+        * Besides, the present approach is less prone to buffer
+        * overflows, in case anybody should ever introduce the bug
+        * of looking at LC_TIME.
+        */
+
+       if ((isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday)) == -1)
                goto fail;
        p += isz;
 
-       if (0 == strftime(p, 4 + 1, "%Y", tm))
+       if (strftime(p, 4 + 1, "%Y", tm) == 0)
                goto fail;
        return buf;
 
@@ -523,25 +554,45 @@ fail:
 }
 
 char *
-mandoc_normdate(struct mparse *parse, char *in, int ln, int pos)
+mandoc_normdate(struct roff_man *man, char *in, int ln, int pos)
 {
-       char            *out;
+       char            *cp;
        time_t           t;
 
-       if (NULL == in || '\0' == *in ||
-           0 == strcmp(in, "$" "Mdocdate$")) {
-               mandoc_msg(MANDOCERR_DATE_MISSING, parse, ln, pos, NULL);
-               time(&t);
+       /* No date specified: use today's date. */
+
+       if (in == NULL || *in == '\0' || strcmp(in, "$" "Mdocdate$") == 0) {
+               mandoc_msg(MANDOCERR_DATE_MISSING, man->parse, ln, pos, NULL);
+               return time2a(time(NULL));
        }
-       else if (a2time(&t, "%Y-%m-%d", in))
-               t = 0;
-       else if (!a2time(&t, "$" "Mdocdate: %b %d %Y $", in) &&
-           !a2time(&t, "%b %d, %Y", in)) {
-               mandoc_msg(MANDOCERR_DATE_BAD, parse, ln, pos, in);
-               t = 0;
+
+       /* Valid mdoc(7) date format. */
+
+       if (a2time(&t, "$" "Mdocdate: %b %d %Y $", in) ||
+           a2time(&t, "%b %d, %Y", in)) {
+               cp = time2a(t);
+               if (t > time(NULL) + 86400)
+                       mandoc_msg(MANDOCERR_DATE_FUTURE, man->parse,
+                           ln, pos, cp);
+               else if (*in != '$' && strcmp(in, cp) != 0)
+                       mandoc_msg(MANDOCERR_DATE_NORM, man->parse,
+                           ln, pos, cp);
+               return cp;
        }
-       out = t ? time2a(t) : NULL;
-       return out ? out : mandoc_strdup(in);
+
+       /* In man(7), do not warn about the legacy format. */
+
+       if (a2time(&t, "%Y-%m-%d", in) == 0)
+               mandoc_msg(MANDOCERR_DATE_BAD, man->parse, ln, pos, in);
+       else if (t > time(NULL) + 86400)
+               mandoc_msg(MANDOCERR_DATE_FUTURE, man->parse, ln, pos, in);
+       else if (man->macroset == MACROSET_MDOC)
+               mandoc_vmsg(MANDOCERR_DATE_LEGACY, man->parse,
+                   ln, pos, "Dd %s", in);
+
+       /* Use any non-mdoc(7) date verbatim. */
+
+       return mandoc_strdup(in);
 }
 
 int
@@ -563,19 +614,14 @@ mandoc_eos(const char *p, size_t sz)
        for (q = p + (int)sz - 1; q >= p; q--) {
                switch (*q) {
                case '\"':
-                       /* FALLTHROUGH */
                case '\'':
-                       /* FALLTHROUGH */
                case ']':
-                       /* FALLTHROUGH */
                case ')':
                        if (0 == found)
                                enclosed = 1;
                        break;
                case '.':
-                       /* FALLTHROUGH */
                case '!':
-                       /* FALLTHROUGH */
                case '?':
                        found = 1;
                        break;