=================================================================== RCS file: /cvs/mandoc/mandoc.c,v retrieving revision 1.94 retrieving revision 1.101 diff -u -p -r1.94 -r1.101 --- mandoc/mandoc.c 2015/10/06 18:32:19 1.94 +++ mandoc/mandoc.c 2017/06/11 19:37:01 1.101 @@ -1,7 +1,7 @@ -/* $Id: mandoc.c,v 1.94 2015/10/06 18:32:19 schwarze Exp $ */ +/* $Id: mandoc.c,v 1.101 2017/06/11 19:37:01 schwarze Exp $ */ /* * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons - * Copyright (c) 2011-2015 Ingo Schwarze + * Copyright (c) 2011-2015, 2017 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 @@ -28,12 +28,11 @@ #include #include -#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); @@ -93,11 +92,8 @@ mandoc_escape(const char **end, const char **start, in * Escapes taking no arguments at all. */ case 'd': - /* FALLTHROUGH */ case 'u': - /* FALLTHROUGH */ case ',': - /* FALLTHROUGH */ case '/': return ESCAPE_IGNORE; @@ -115,19 +111,12 @@ mandoc_escape(const char **end, const char **start, in * '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 */ @@ -155,15 +144,10 @@ mandoc_escape(const char **end, const char **start, in * 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 +165,28 @@ mandoc_escape(const char **end, const char **start, in * 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 +235,7 @@ mandoc_escape(const char **end, const char **start, in term = '\''; break; case '3': - /* FALLTHROUGH */ case '2': - /* FALLTHROUGH */ case '1': *sz = (*end)[-1] == 's' && isdigit((unsigned char)(*end)[1]) ? 2 : 1; @@ -325,12 +311,10 @@ mandoc_escape(const char **end, const char **start, in switch (**start) { case '3': - /* FALLTHROUGH */ case 'B': gly = ESCAPE_FONTBOLD; break; case '2': - /* FALLTHROUGH */ case 'I': gly = ESCAPE_FONTITALIC; break; @@ -338,7 +322,6 @@ mandoc_escape(const char **end, const char **start, in gly = ESCAPE_FONTPREV; break; case '1': - /* FALLTHROUGH */ case 'R': gly = ESCAPE_FONTROMAN; break; @@ -359,6 +342,9 @@ mandoc_escape(const char **end, const char **start, in 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 +489,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 +519,34 @@ 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; 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; - } - out = t ? time2a(t) : NULL; - return out ? out : mandoc_strdup(in); + + /* Valid mdoc(7) date format. */ + + if (a2time(&t, "$" "Mdocdate: %b %d %Y $", in) || + a2time(&t, "%b %d, %Y", in)) + return time2a(t); + + /* 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 (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 +568,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;