Annotation of mandoc/mandoc.c, Revision 1.121
1.121 ! schwarze 1: /* $Id: mandoc.c,v 1.120 2022/04/13 13:19:34 schwarze Exp $ */
1.1 kristaps 2: /*
1.121 ! schwarze 3: * Copyright (c) 2010, 2011, 2015, 2017, 2018, 2019, 2020, 2021
! 4: * Ingo Schwarze <schwarze@openbsd.org>
! 5: * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
1.1 kristaps 6: *
7: * Permission to use, copy, modify, and distribute this software for any
8: * purpose with or without fee is hereby granted, provided that the above
9: * copyright notice and this permission notice appear in all copies.
10: *
1.36 schwarze 11: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1.1 kristaps 12: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1.36 schwarze 13: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1.1 kristaps 14: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.121 ! schwarze 18: *
! 19: * Utility functions to handle end of sentence punctuation
! 20: * and dates and times, for use by mdoc(7) and man(7) parsers.
! 21: * Utility functions to handle fonts and numbers,
! 22: * for use by mandoc(1) parsers and formatters.
1.1 kristaps 23: */
1.9 kristaps 24: #include "config.h"
1.7 kristaps 25:
1.2 kristaps 26: #include <sys/types.h>
27:
1.1 kristaps 28: #include <assert.h>
29: #include <ctype.h>
1.50 kristaps 30: #include <errno.h>
31: #include <limits.h>
1.1 kristaps 32: #include <stdlib.h>
1.4 kristaps 33: #include <stdio.h>
34: #include <string.h>
1.7 kristaps 35: #include <time.h>
1.1 kristaps 36:
1.101 schwarze 37: #include "mandoc_aux.h"
1.18 kristaps 38: #include "mandoc.h"
1.101 schwarze 39: #include "roff.h"
1.1 kristaps 40: #include "libmandoc.h"
1.114 schwarze 41: #include "roff_int.h"
1.37 schwarze 42:
1.18 kristaps 43: static int a2time(time_t *, const char *, const char *);
1.37 schwarze 44: static char *time2a(time_t);
1.7 kristaps 45:
1.45 kristaps 46:
47: enum mandoc_esc
1.112 schwarze 48: mandoc_font(const char *cp, int sz)
49: {
50: switch (sz) {
51: case 0:
52: return ESCAPE_FONTPREV;
53: case 1:
54: switch (cp[0]) {
55: case 'B':
56: case '3':
57: return ESCAPE_FONTBOLD;
58: case 'I':
59: case '2':
60: return ESCAPE_FONTITALIC;
61: case 'P':
62: return ESCAPE_FONTPREV;
63: case 'R':
64: case '1':
65: return ESCAPE_FONTROMAN;
66: case '4':
67: return ESCAPE_FONTBI;
68: default:
69: return ESCAPE_ERROR;
70: }
71: case 2:
72: switch (cp[0]) {
73: case 'B':
74: switch (cp[1]) {
75: case 'I':
76: return ESCAPE_FONTBI;
77: default:
78: return ESCAPE_ERROR;
79: }
80: case 'C':
81: switch (cp[1]) {
82: case 'B':
1.119 schwarze 83: return ESCAPE_FONTCB;
1.112 schwarze 84: case 'I':
1.119 schwarze 85: return ESCAPE_FONTCI;
1.112 schwarze 86: case 'R':
87: case 'W':
1.119 schwarze 88: return ESCAPE_FONTCR;
1.112 schwarze 89: default:
90: return ESCAPE_ERROR;
91: }
92: default:
93: return ESCAPE_ERROR;
94: }
95: default:
96: return ESCAPE_ERROR;
97: }
1.4 kristaps 98: }
1.7 kristaps 99:
100: static int
101: a2time(time_t *t, const char *fmt, const char *p)
102: {
103: struct tm tm;
104: char *pp;
105:
106: memset(&tm, 0, sizeof(struct tm));
107:
1.56 kristaps 108: pp = NULL;
1.85 schwarze 109: #if HAVE_STRPTIME
1.7 kristaps 110: pp = strptime(p, fmt, &tm);
1.56 kristaps 111: #endif
1.7 kristaps 112: if (NULL != pp && '\0' == *pp) {
113: *t = mktime(&tm);
1.94 schwarze 114: return 1;
1.7 kristaps 115: }
116:
1.94 schwarze 117: return 0;
1.7 kristaps 118: }
119:
1.37 schwarze 120: static char *
121: time2a(time_t t)
122: {
1.56 kristaps 123: struct tm *tm;
1.38 schwarze 124: char *buf, *p;
125: size_t ssz;
1.37 schwarze 126: int isz;
127:
1.116 schwarze 128: buf = NULL;
1.56 kristaps 129: tm = localtime(&t);
1.89 schwarze 130: if (tm == NULL)
1.116 schwarze 131: goto fail;
1.37 schwarze 132:
1.38 schwarze 133: /*
134: * Reserve space:
135: * up to 9 characters for the month (September) + blank
136: * up to 2 characters for the day + comma + blank
137: * 4 characters for the year and a terminating '\0'
138: */
1.98 schwarze 139:
1.38 schwarze 140: p = buf = mandoc_malloc(10 + 4 + 4 + 1);
141:
1.98 schwarze 142: if ((ssz = strftime(p, 10 + 1, "%B ", tm)) == 0)
1.38 schwarze 143: goto fail;
144: p += (int)ssz;
1.37 schwarze 145:
1.98 schwarze 146: /*
147: * The output format is just "%d" here, not "%2d" or "%02d".
148: * That's also the reason why we can't just format the
149: * date as a whole with "%B %e, %Y" or "%B %d, %Y".
150: * Besides, the present approach is less prone to buffer
151: * overflows, in case anybody should ever introduce the bug
152: * of looking at LC_TIME.
153: */
154:
1.116 schwarze 155: isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday);
156: if (isz < 0 || isz > 4)
1.38 schwarze 157: goto fail;
1.37 schwarze 158: p += isz;
159:
1.98 schwarze 160: if (strftime(p, 4 + 1, "%Y", tm) == 0)
1.38 schwarze 161: goto fail;
1.94 schwarze 162: return buf;
1.38 schwarze 163:
164: fail:
165: free(buf);
1.116 schwarze 166: return mandoc_strdup("");
1.37 schwarze 167: }
168:
169: char *
1.117 schwarze 170: mandoc_normdate(struct roff_node *nch, struct roff_node *nbl)
1.7 kristaps 171: {
1.103 schwarze 172: char *cp;
1.7 kristaps 173: time_t t;
1.116 schwarze 174:
1.117 schwarze 175: /* No date specified. */
1.7 kristaps 176:
1.117 schwarze 177: if (nch == NULL) {
178: if (nbl == NULL)
179: mandoc_msg(MANDOCERR_DATE_MISSING, 0, 0, NULL);
180: else
181: mandoc_msg(MANDOCERR_DATE_MISSING, nbl->line,
182: nbl->pos, "%s", roff_name[nbl->tok]);
183: return mandoc_strdup("");
184: }
185: if (*nch->string == '\0') {
186: mandoc_msg(MANDOCERR_DATE_MISSING, nch->line,
187: nch->pos, "%s", roff_name[nbl->tok]);
188: return mandoc_strdup("");
189: }
190: if (strcmp(nch->string, "$" "Mdocdate$") == 0)
1.98 schwarze 191: return time2a(time(NULL));
192:
193: /* Valid mdoc(7) date format. */
194:
1.117 schwarze 195: if (a2time(&t, "$" "Mdocdate: %b %d %Y $", nch->string) ||
196: a2time(&t, "%b %d, %Y", nch->string)) {
1.103 schwarze 197: cp = time2a(t);
198: if (t > time(NULL) + 86400)
1.117 schwarze 199: mandoc_msg(MANDOCERR_DATE_FUTURE, nch->line,
200: nch->pos, "%s %s", roff_name[nbl->tok], cp);
201: else if (*nch->string != '$' &&
202: strcmp(nch->string, cp) != 0)
203: mandoc_msg(MANDOCERR_DATE_NORM, nch->line,
204: nch->pos, "%s %s", roff_name[nbl->tok], cp);
1.103 schwarze 205: return cp;
206: }
1.98 schwarze 207:
1.101 schwarze 208: /* In man(7), do not warn about the legacy format. */
1.98 schwarze 209:
1.117 schwarze 210: if (a2time(&t, "%Y-%m-%d", nch->string) == 0)
211: mandoc_msg(MANDOCERR_DATE_BAD, nch->line, nch->pos,
212: "%s %s", roff_name[nbl->tok], nch->string);
1.103 schwarze 213: else if (t > time(NULL) + 86400)
1.117 schwarze 214: mandoc_msg(MANDOCERR_DATE_FUTURE, nch->line, nch->pos,
215: "%s %s", roff_name[nbl->tok], nch->string);
216: else if (nbl->tok == MDOC_Dd)
217: mandoc_msg(MANDOCERR_DATE_LEGACY, nch->line, nch->pos,
218: "Dd %s", nch->string);
1.98 schwarze 219:
220: /* Use any non-mdoc(7) date verbatim. */
221:
1.117 schwarze 222: return mandoc_strdup(nch->string);
1.7 kristaps 223: }
224:
1.12 kristaps 225: int
1.75 schwarze 226: mandoc_eos(const char *p, size_t sz)
1.12 kristaps 227: {
1.75 schwarze 228: const char *q;
229: int enclosed, found;
1.12 kristaps 230:
1.13 kristaps 231: if (0 == sz)
1.94 schwarze 232: return 0;
1.12 kristaps 233:
1.14 kristaps 234: /*
235: * End-of-sentence recognition must include situations where
236: * some symbols, such as `)', allow prior EOS punctuation to
1.49 kristaps 237: * propagate outward.
1.14 kristaps 238: */
239:
1.75 schwarze 240: enclosed = found = 0;
1.25 kristaps 241: for (q = p + (int)sz - 1; q >= p; q--) {
1.23 schwarze 242: switch (*q) {
1.79 schwarze 243: case '\"':
244: case '\'':
245: case ']':
246: case ')':
1.23 schwarze 247: if (0 == found)
248: enclosed = 1;
1.14 kristaps 249: break;
1.79 schwarze 250: case '.':
251: case '!':
252: case '?':
1.23 schwarze 253: found = 1;
254: break;
1.14 kristaps 255: default:
1.94 schwarze 256: return found &&
257: (!enclosed || isalnum((unsigned char)*q));
1.14 kristaps 258: }
1.12 kristaps 259: }
260:
1.94 schwarze 261: return found && !enclosed;
1.44 kristaps 262: }
1.50 kristaps 263:
264: /*
265: * Convert a string to a long that may not be <0.
266: * If the string is invalid, or is less than 0, return -1.
267: */
268: int
1.54 kristaps 269: mandoc_strntoi(const char *p, size_t sz, int base)
1.50 kristaps 270: {
271: char buf[32];
272: char *ep;
273: long v;
274:
275: if (sz > 31)
1.94 schwarze 276: return -1;
1.50 kristaps 277:
278: memcpy(buf, p, sz);
1.51 kristaps 279: buf[(int)sz] = '\0';
1.50 kristaps 280:
281: errno = 0;
282: v = strtol(buf, &ep, base);
283:
284: if (buf[0] == '\0' || *ep != '\0')
1.94 schwarze 285: return -1;
1.50 kristaps 286:
1.54 kristaps 287: if (v > INT_MAX)
288: v = INT_MAX;
289: if (v < INT_MIN)
290: v = INT_MIN;
1.50 kristaps 291:
1.94 schwarze 292: return (int)v;
1.50 kristaps 293: }
CVSweb