mandoc 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +7 -0
  3. data/COPYING +674 -0
  4. data/README.md +117 -0
  5. data/ext/mandoc/extconf.rb +59 -0
  6. data/ext/mandoc/rb_mandoc.c +548 -0
  7. data/ext/mandoc/rb_mandoc.h +22 -0
  8. data/lib/mandoc/version.rb +19 -0
  9. data/lib/mandoc.rb +26 -0
  10. data/mandoc-1.14.6/LICENSE +55 -0
  11. data/mandoc-1.14.6/arch.c +54 -0
  12. data/mandoc-1.14.6/att.c +49 -0
  13. data/mandoc-1.14.6/catman.c +260 -0
  14. data/mandoc-1.14.6/cgi.c +1279 -0
  15. data/mandoc-1.14.6/chars.c +507 -0
  16. data/mandoc-1.14.6/compat_err.c +103 -0
  17. data/mandoc-1.14.6/compat_fts.c +696 -0
  18. data/mandoc-1.14.6/compat_fts.h +106 -0
  19. data/mandoc-1.14.6/compat_getline.c +59 -0
  20. data/mandoc-1.14.6/compat_getsubopt.c +87 -0
  21. data/mandoc-1.14.6/compat_isblank.c +23 -0
  22. data/mandoc-1.14.6/compat_mkdtemp.c +50 -0
  23. data/mandoc-1.14.6/compat_mkstemps.c +63 -0
  24. data/mandoc-1.14.6/compat_ohash.c +330 -0
  25. data/mandoc-1.14.6/compat_ohash.h +72 -0
  26. data/mandoc-1.14.6/compat_progname.c +31 -0
  27. data/mandoc-1.14.6/compat_reallocarray.c +40 -0
  28. data/mandoc-1.14.6/compat_recallocarray.c +99 -0
  29. data/mandoc-1.14.6/compat_strcasestr.c +64 -0
  30. data/mandoc-1.14.6/compat_stringlist.c +135 -0
  31. data/mandoc-1.14.6/compat_stringlist.h +48 -0
  32. data/mandoc-1.14.6/compat_strlcat.c +57 -0
  33. data/mandoc-1.14.6/compat_strlcpy.c +52 -0
  34. data/mandoc-1.14.6/compat_strndup.c +42 -0
  35. data/mandoc-1.14.6/compat_strsep.c +70 -0
  36. data/mandoc-1.14.6/compat_strtonum.c +67 -0
  37. data/mandoc-1.14.6/compat_vasprintf.c +47 -0
  38. data/mandoc-1.14.6/config.h +52 -0
  39. data/mandoc-1.14.6/dba.c +508 -0
  40. data/mandoc-1.14.6/dba.h +50 -0
  41. data/mandoc-1.14.6/dba_array.c +190 -0
  42. data/mandoc-1.14.6/dba_array.h +47 -0
  43. data/mandoc-1.14.6/dba_read.c +74 -0
  44. data/mandoc-1.14.6/dba_write.c +127 -0
  45. data/mandoc-1.14.6/dba_write.h +30 -0
  46. data/mandoc-1.14.6/dbm.c +480 -0
  47. data/mandoc-1.14.6/dbm.h +68 -0
  48. data/mandoc-1.14.6/dbm_map.c +194 -0
  49. data/mandoc-1.14.6/dbm_map.h +29 -0
  50. data/mandoc-1.14.6/demandoc.c +260 -0
  51. data/mandoc-1.14.6/eqn.c +1132 -0
  52. data/mandoc-1.14.6/eqn.h +72 -0
  53. data/mandoc-1.14.6/eqn_html.c +246 -0
  54. data/mandoc-1.14.6/eqn_parse.h +48 -0
  55. data/mandoc-1.14.6/eqn_term.c +174 -0
  56. data/mandoc-1.14.6/html.c +1102 -0
  57. data/mandoc-1.14.6/html.h +142 -0
  58. data/mandoc-1.14.6/lib.c +35 -0
  59. data/mandoc-1.14.6/libman.h +42 -0
  60. data/mandoc-1.14.6/libmandoc.h +85 -0
  61. data/mandoc-1.14.6/libmdoc.h +87 -0
  62. data/mandoc-1.14.6/main.c +1375 -0
  63. data/mandoc-1.14.6/main.h +53 -0
  64. data/mandoc-1.14.6/man.c +345 -0
  65. data/mandoc-1.14.6/man.h +21 -0
  66. data/mandoc-1.14.6/man_html.c +640 -0
  67. data/mandoc-1.14.6/man_macro.c +470 -0
  68. data/mandoc-1.14.6/man_term.c +1143 -0
  69. data/mandoc-1.14.6/man_validate.c +660 -0
  70. data/mandoc-1.14.6/manconf.h +58 -0
  71. data/mandoc-1.14.6/mandoc.c +669 -0
  72. data/mandoc-1.14.6/mandoc.h +329 -0
  73. data/mandoc-1.14.6/mandoc_aux.c +118 -0
  74. data/mandoc-1.14.6/mandoc_aux.h +27 -0
  75. data/mandoc-1.14.6/mandoc_msg.c +375 -0
  76. data/mandoc-1.14.6/mandoc_ohash.c +65 -0
  77. data/mandoc-1.14.6/mandoc_ohash.h +23 -0
  78. data/mandoc-1.14.6/mandoc_parse.h +44 -0
  79. data/mandoc-1.14.6/mandoc_xr.c +123 -0
  80. data/mandoc-1.14.6/mandoc_xr.h +31 -0
  81. data/mandoc-1.14.6/mandocd.c +282 -0
  82. data/mandoc-1.14.6/mandocdb.c +2448 -0
  83. data/mandoc-1.14.6/manpath.c +363 -0
  84. data/mandoc-1.14.6/mansearch.c +851 -0
  85. data/mandoc-1.14.6/mansearch.h +118 -0
  86. data/mandoc-1.14.6/mdoc.c +433 -0
  87. data/mandoc-1.14.6/mdoc.h +158 -0
  88. data/mandoc-1.14.6/mdoc_argv.c +682 -0
  89. data/mandoc-1.14.6/mdoc_html.c +1762 -0
  90. data/mandoc-1.14.6/mdoc_macro.c +1600 -0
  91. data/mandoc-1.14.6/mdoc_man.c +1850 -0
  92. data/mandoc-1.14.6/mdoc_markdown.c +1610 -0
  93. data/mandoc-1.14.6/mdoc_state.c +256 -0
  94. data/mandoc-1.14.6/mdoc_term.c +1964 -0
  95. data/mandoc-1.14.6/mdoc_validate.c +3062 -0
  96. data/mandoc-1.14.6/msec.c +37 -0
  97. data/mandoc-1.14.6/out.c +544 -0
  98. data/mandoc-1.14.6/out.h +70 -0
  99. data/mandoc-1.14.6/preconv.c +179 -0
  100. data/mandoc-1.14.6/read.c +732 -0
  101. data/mandoc-1.14.6/roff.c +4390 -0
  102. data/mandoc-1.14.6/roff.h +561 -0
  103. data/mandoc-1.14.6/roff_html.c +119 -0
  104. data/mandoc-1.14.6/roff_int.h +94 -0
  105. data/mandoc-1.14.6/roff_term.c +266 -0
  106. data/mandoc-1.14.6/roff_validate.c +151 -0
  107. data/mandoc-1.14.6/soelim.c +182 -0
  108. data/mandoc-1.14.6/st.c +82 -0
  109. data/mandoc-1.14.6/tag.c +327 -0
  110. data/mandoc-1.14.6/tag.h +35 -0
  111. data/mandoc-1.14.6/tbl.c +183 -0
  112. data/mandoc-1.14.6/tbl.h +121 -0
  113. data/mandoc-1.14.6/tbl_data.c +323 -0
  114. data/mandoc-1.14.6/tbl_html.c +293 -0
  115. data/mandoc-1.14.6/tbl_int.h +47 -0
  116. data/mandoc-1.14.6/tbl_layout.c +376 -0
  117. data/mandoc-1.14.6/tbl_opts.c +173 -0
  118. data/mandoc-1.14.6/tbl_parse.h +30 -0
  119. data/mandoc-1.14.6/tbl_term.c +948 -0
  120. data/mandoc-1.14.6/term.c +1113 -0
  121. data/mandoc-1.14.6/term.h +158 -0
  122. data/mandoc-1.14.6/term_ascii.c +424 -0
  123. data/mandoc-1.14.6/term_ps.c +1362 -0
  124. data/mandoc-1.14.6/term_tab.c +130 -0
  125. data/mandoc-1.14.6/term_tag.c +227 -0
  126. data/mandoc-1.14.6/term_tag.h +34 -0
  127. data/mandoc-1.14.6/tree.c +536 -0
  128. metadata +170 -0
@@ -0,0 +1,669 @@
1
+ /* $Id: mandoc.c,v 1.119 2021/08/10 12:55:03 schwarze Exp $ */
2
+ /*
3
+ * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4
+ * Copyright (c) 2011-2015, 2017-2021 Ingo Schwarze <schwarze@openbsd.org>
5
+ *
6
+ * Permission to use, copy, modify, and distribute this software for any
7
+ * purpose with or without fee is hereby granted, provided that the above
8
+ * copyright notice and this permission notice appear in all copies.
9
+ *
10
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
+ */
18
+ #include "config.h"
19
+
20
+ #include <sys/types.h>
21
+
22
+ #include <assert.h>
23
+ #include <ctype.h>
24
+ #include <errno.h>
25
+ #include <limits.h>
26
+ #include <stdlib.h>
27
+ #include <stdio.h>
28
+ #include <string.h>
29
+ #include <time.h>
30
+
31
+ #include "mandoc_aux.h"
32
+ #include "mandoc.h"
33
+ #include "roff.h"
34
+ #include "libmandoc.h"
35
+ #include "roff_int.h"
36
+
37
+ static int a2time(time_t *, const char *, const char *);
38
+ static char *time2a(time_t);
39
+
40
+
41
+ enum mandoc_esc
42
+ mandoc_font(const char *cp, int sz)
43
+ {
44
+ switch (sz) {
45
+ case 0:
46
+ return ESCAPE_FONTPREV;
47
+ case 1:
48
+ switch (cp[0]) {
49
+ case 'B':
50
+ case '3':
51
+ return ESCAPE_FONTBOLD;
52
+ case 'I':
53
+ case '2':
54
+ return ESCAPE_FONTITALIC;
55
+ case 'P':
56
+ return ESCAPE_FONTPREV;
57
+ case 'R':
58
+ case '1':
59
+ return ESCAPE_FONTROMAN;
60
+ case '4':
61
+ return ESCAPE_FONTBI;
62
+ default:
63
+ return ESCAPE_ERROR;
64
+ }
65
+ case 2:
66
+ switch (cp[0]) {
67
+ case 'B':
68
+ switch (cp[1]) {
69
+ case 'I':
70
+ return ESCAPE_FONTBI;
71
+ default:
72
+ return ESCAPE_ERROR;
73
+ }
74
+ case 'C':
75
+ switch (cp[1]) {
76
+ case 'B':
77
+ return ESCAPE_FONTCB;
78
+ case 'I':
79
+ return ESCAPE_FONTCI;
80
+ case 'R':
81
+ case 'W':
82
+ return ESCAPE_FONTCR;
83
+ default:
84
+ return ESCAPE_ERROR;
85
+ }
86
+ default:
87
+ return ESCAPE_ERROR;
88
+ }
89
+ default:
90
+ return ESCAPE_ERROR;
91
+ }
92
+ }
93
+
94
+ enum mandoc_esc
95
+ mandoc_escape(const char **end, const char **start, int *sz)
96
+ {
97
+ const char *local_start;
98
+ int local_sz, c, i;
99
+ char term;
100
+ enum mandoc_esc gly;
101
+
102
+ /*
103
+ * When the caller doesn't provide return storage,
104
+ * use local storage.
105
+ */
106
+
107
+ if (NULL == start)
108
+ start = &local_start;
109
+ if (NULL == sz)
110
+ sz = &local_sz;
111
+
112
+ /*
113
+ * Treat "\E" just like "\";
114
+ * it only makes a difference in copy mode.
115
+ */
116
+
117
+ if (**end == 'E')
118
+ ++*end;
119
+
120
+ /*
121
+ * Beyond the backslash, at least one input character
122
+ * is part of the escape sequence. With one exception
123
+ * (see below), that character won't be returned.
124
+ */
125
+
126
+ gly = ESCAPE_ERROR;
127
+ *start = ++*end;
128
+ *sz = 0;
129
+ term = '\0';
130
+
131
+ switch ((*start)[-1]) {
132
+ /*
133
+ * First the glyphs. There are several different forms of
134
+ * these, but each eventually returns a substring of the glyph
135
+ * name.
136
+ */
137
+ case '(':
138
+ gly = ESCAPE_SPECIAL;
139
+ *sz = 2;
140
+ break;
141
+ case '[':
142
+ if (**start == ' ') {
143
+ ++*end;
144
+ return ESCAPE_ERROR;
145
+ }
146
+ gly = ESCAPE_SPECIAL;
147
+ term = ']';
148
+ break;
149
+ case 'C':
150
+ if ('\'' != **start)
151
+ return ESCAPE_ERROR;
152
+ *start = ++*end;
153
+ gly = ESCAPE_SPECIAL;
154
+ term = '\'';
155
+ break;
156
+
157
+ /*
158
+ * Escapes taking no arguments at all.
159
+ */
160
+ case '!':
161
+ case '?':
162
+ return ESCAPE_UNSUPP;
163
+ case '%':
164
+ case '&':
165
+ case ')':
166
+ case ',':
167
+ case '/':
168
+ case '^':
169
+ case 'a':
170
+ case 'd':
171
+ case 'r':
172
+ case 't':
173
+ case 'u':
174
+ case '{':
175
+ case '|':
176
+ case '}':
177
+ return ESCAPE_IGNORE;
178
+ case 'c':
179
+ return ESCAPE_NOSPACE;
180
+ case 'p':
181
+ return ESCAPE_BREAK;
182
+
183
+ /*
184
+ * The \z escape is supposed to output the following
185
+ * character without advancing the cursor position.
186
+ * Since we are mostly dealing with terminal mode,
187
+ * let us just skip the next character.
188
+ */
189
+ case 'z':
190
+ return ESCAPE_SKIPCHAR;
191
+
192
+ /*
193
+ * Handle all triggers matching \X(xy, \Xx, and \X[xxxx], where
194
+ * 'X' is the trigger. These have opaque sub-strings.
195
+ */
196
+ case 'F':
197
+ case 'f':
198
+ case 'g':
199
+ case 'k':
200
+ case 'M':
201
+ case 'm':
202
+ case 'n':
203
+ case 'O':
204
+ case 'V':
205
+ case 'Y':
206
+ case '*':
207
+ switch ((*start)[-1]) {
208
+ case 'f':
209
+ gly = ESCAPE_FONT;
210
+ break;
211
+ case '*':
212
+ gly = ESCAPE_DEVICE;
213
+ break;
214
+ default:
215
+ gly = ESCAPE_IGNORE;
216
+ break;
217
+ }
218
+ switch (**start) {
219
+ case '(':
220
+ if ((*start)[-1] == 'O')
221
+ gly = ESCAPE_ERROR;
222
+ *start = ++*end;
223
+ *sz = 2;
224
+ break;
225
+ case '[':
226
+ if ((*start)[-1] == 'O')
227
+ gly = (*start)[1] == '5' ?
228
+ ESCAPE_UNSUPP : ESCAPE_ERROR;
229
+ *start = ++*end;
230
+ term = ']';
231
+ break;
232
+ default:
233
+ if ((*start)[-1] == 'O') {
234
+ switch (**start) {
235
+ case '0':
236
+ gly = ESCAPE_UNSUPP;
237
+ break;
238
+ case '1':
239
+ case '2':
240
+ case '3':
241
+ case '4':
242
+ break;
243
+ default:
244
+ gly = ESCAPE_ERROR;
245
+ break;
246
+ }
247
+ }
248
+ *sz = 1;
249
+ break;
250
+ }
251
+ break;
252
+
253
+ /*
254
+ * These escapes are of the form \X'Y', where 'X' is the trigger
255
+ * and 'Y' is any string. These have opaque sub-strings.
256
+ * The \B and \w escapes are handled in roff.c, roff_res().
257
+ */
258
+ case 'A':
259
+ case 'b':
260
+ case 'D':
261
+ case 'R':
262
+ case 'X':
263
+ case 'Z':
264
+ gly = ESCAPE_IGNORE;
265
+ /* FALLTHROUGH */
266
+ case 'o':
267
+ if (**start == '\0')
268
+ return ESCAPE_ERROR;
269
+ if (gly == ESCAPE_ERROR)
270
+ gly = ESCAPE_OVERSTRIKE;
271
+ term = **start;
272
+ *start = ++*end;
273
+ break;
274
+
275
+ /*
276
+ * These escapes are of the form \X'N', where 'X' is the trigger
277
+ * and 'N' resolves to a numerical expression.
278
+ */
279
+ case 'h':
280
+ case 'H':
281
+ case 'L':
282
+ case 'l':
283
+ case 'S':
284
+ case 'v':
285
+ case 'x':
286
+ if (strchr(" %&()*+-./0123456789:<=>", **start)) {
287
+ if ('\0' != **start)
288
+ ++*end;
289
+ return ESCAPE_ERROR;
290
+ }
291
+ switch ((*start)[-1]) {
292
+ case 'h':
293
+ gly = ESCAPE_HORIZ;
294
+ break;
295
+ case 'l':
296
+ gly = ESCAPE_HLINE;
297
+ break;
298
+ default:
299
+ gly = ESCAPE_IGNORE;
300
+ break;
301
+ }
302
+ term = **start;
303
+ *start = ++*end;
304
+ break;
305
+
306
+ /*
307
+ * Special handling for the numbered character escape.
308
+ * XXX Do any other escapes need similar handling?
309
+ */
310
+ case 'N':
311
+ if ('\0' == **start)
312
+ return ESCAPE_ERROR;
313
+ (*end)++;
314
+ if (isdigit((unsigned char)**start)) {
315
+ *sz = 1;
316
+ return ESCAPE_IGNORE;
317
+ }
318
+ (*start)++;
319
+ while (isdigit((unsigned char)**end))
320
+ (*end)++;
321
+ *sz = *end - *start;
322
+ if ('\0' != **end)
323
+ (*end)++;
324
+ return ESCAPE_NUMBERED;
325
+
326
+ /*
327
+ * Sizes get a special category of their own.
328
+ */
329
+ case 's':
330
+ gly = ESCAPE_IGNORE;
331
+
332
+ /* See +/- counts as a sign. */
333
+ if ('+' == **end || '-' == **end || ASCII_HYPH == **end)
334
+ *start = ++*end;
335
+
336
+ switch (**end) {
337
+ case '(':
338
+ *start = ++*end;
339
+ *sz = 2;
340
+ break;
341
+ case '[':
342
+ *start = ++*end;
343
+ term = ']';
344
+ break;
345
+ case '\'':
346
+ *start = ++*end;
347
+ term = '\'';
348
+ break;
349
+ case '3':
350
+ case '2':
351
+ case '1':
352
+ *sz = (*end)[-1] == 's' &&
353
+ isdigit((unsigned char)(*end)[1]) ? 2 : 1;
354
+ break;
355
+ default:
356
+ *sz = 1;
357
+ break;
358
+ }
359
+
360
+ break;
361
+
362
+ /*
363
+ * Several special characters can be encoded as
364
+ * one-byte escape sequences without using \[].
365
+ */
366
+ case ' ':
367
+ case '\'':
368
+ case '-':
369
+ case '.':
370
+ case '0':
371
+ case ':':
372
+ case '_':
373
+ case '`':
374
+ case 'e':
375
+ case '~':
376
+ gly = ESCAPE_SPECIAL;
377
+ /* FALLTHROUGH */
378
+ default:
379
+ if (gly == ESCAPE_ERROR)
380
+ gly = ESCAPE_UNDEF;
381
+ *start = --*end;
382
+ *sz = 1;
383
+ break;
384
+ }
385
+
386
+ /*
387
+ * Read up to the terminating character,
388
+ * paying attention to nested escapes.
389
+ */
390
+
391
+ if ('\0' != term) {
392
+ while (**end != term) {
393
+ switch (**end) {
394
+ case '\0':
395
+ return ESCAPE_ERROR;
396
+ case '\\':
397
+ (*end)++;
398
+ if (ESCAPE_ERROR ==
399
+ mandoc_escape(end, NULL, NULL))
400
+ return ESCAPE_ERROR;
401
+ break;
402
+ default:
403
+ (*end)++;
404
+ break;
405
+ }
406
+ }
407
+ *sz = (*end)++ - *start;
408
+
409
+ /*
410
+ * The file chars.c only provides one common list
411
+ * of character names, but \[-] == \- is the only
412
+ * one of the characters with one-byte names that
413
+ * allows enclosing the name in brackets.
414
+ */
415
+ if (gly == ESCAPE_SPECIAL && *sz == 1 && **start != '-')
416
+ return ESCAPE_ERROR;
417
+ } else {
418
+ assert(*sz > 0);
419
+ if ((size_t)*sz > strlen(*start))
420
+ return ESCAPE_ERROR;
421
+ *end += *sz;
422
+ }
423
+
424
+ /* Run post-processors. */
425
+
426
+ switch (gly) {
427
+ case ESCAPE_FONT:
428
+ gly = mandoc_font(*start, *sz);
429
+ break;
430
+ case ESCAPE_SPECIAL:
431
+ if (**start == 'c') {
432
+ if (*sz < 6 || *sz > 7 ||
433
+ strncmp(*start, "char", 4) != 0 ||
434
+ (int)strspn(*start + 4, "0123456789") + 4 < *sz)
435
+ break;
436
+ c = 0;
437
+ for (i = 4; i < *sz; i++)
438
+ c = 10 * c + ((*start)[i] - '0');
439
+ if (c < 0x21 || (c > 0x7e && c < 0xa0) || c > 0xff)
440
+ break;
441
+ *start += 4;
442
+ *sz -= 4;
443
+ gly = ESCAPE_NUMBERED;
444
+ break;
445
+ }
446
+
447
+ /*
448
+ * Unicode escapes are defined in groff as \[u0000]
449
+ * to \[u10FFFF], where the contained value must be
450
+ * a valid Unicode codepoint. Here, however, only
451
+ * check the length and range.
452
+ */
453
+ if (**start != 'u' || *sz < 5 || *sz > 7)
454
+ break;
455
+ if (*sz == 7 && ((*start)[1] != '1' || (*start)[2] != '0'))
456
+ break;
457
+ if (*sz == 6 && (*start)[1] == '0')
458
+ break;
459
+ if (*sz == 5 && (*start)[1] == 'D' &&
460
+ strchr("89ABCDEF", (*start)[2]) != NULL)
461
+ break;
462
+ if ((int)strspn(*start + 1, "0123456789ABCDEFabcdef")
463
+ + 1 == *sz)
464
+ gly = ESCAPE_UNICODE;
465
+ break;
466
+ case ESCAPE_DEVICE:
467
+ assert(*sz == 2 && (*start)[0] == '.' && (*start)[1] == 'T');
468
+ break;
469
+ default:
470
+ break;
471
+ }
472
+
473
+ return gly;
474
+ }
475
+
476
+ static int
477
+ a2time(time_t *t, const char *fmt, const char *p)
478
+ {
479
+ struct tm tm;
480
+ char *pp;
481
+
482
+ memset(&tm, 0, sizeof(struct tm));
483
+
484
+ pp = NULL;
485
+ #if HAVE_STRPTIME
486
+ pp = strptime(p, fmt, &tm);
487
+ #endif
488
+ if (NULL != pp && '\0' == *pp) {
489
+ *t = mktime(&tm);
490
+ return 1;
491
+ }
492
+
493
+ return 0;
494
+ }
495
+
496
+ static char *
497
+ time2a(time_t t)
498
+ {
499
+ struct tm *tm;
500
+ char *buf, *p;
501
+ size_t ssz;
502
+ int isz;
503
+
504
+ buf = NULL;
505
+ tm = localtime(&t);
506
+ if (tm == NULL)
507
+ goto fail;
508
+
509
+ /*
510
+ * Reserve space:
511
+ * up to 9 characters for the month (September) + blank
512
+ * up to 2 characters for the day + comma + blank
513
+ * 4 characters for the year and a terminating '\0'
514
+ */
515
+
516
+ p = buf = mandoc_malloc(10 + 4 + 4 + 1);
517
+
518
+ if ((ssz = strftime(p, 10 + 1, "%B ", tm)) == 0)
519
+ goto fail;
520
+ p += (int)ssz;
521
+
522
+ /*
523
+ * The output format is just "%d" here, not "%2d" or "%02d".
524
+ * That's also the reason why we can't just format the
525
+ * date as a whole with "%B %e, %Y" or "%B %d, %Y".
526
+ * Besides, the present approach is less prone to buffer
527
+ * overflows, in case anybody should ever introduce the bug
528
+ * of looking at LC_TIME.
529
+ */
530
+
531
+ isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday);
532
+ if (isz < 0 || isz > 4)
533
+ goto fail;
534
+ p += isz;
535
+
536
+ if (strftime(p, 4 + 1, "%Y", tm) == 0)
537
+ goto fail;
538
+ return buf;
539
+
540
+ fail:
541
+ free(buf);
542
+ return mandoc_strdup("");
543
+ }
544
+
545
+ char *
546
+ mandoc_normdate(struct roff_node *nch, struct roff_node *nbl)
547
+ {
548
+ char *cp;
549
+ time_t t;
550
+
551
+ /* No date specified. */
552
+
553
+ if (nch == NULL) {
554
+ if (nbl == NULL)
555
+ mandoc_msg(MANDOCERR_DATE_MISSING, 0, 0, NULL);
556
+ else
557
+ mandoc_msg(MANDOCERR_DATE_MISSING, nbl->line,
558
+ nbl->pos, "%s", roff_name[nbl->tok]);
559
+ return mandoc_strdup("");
560
+ }
561
+ if (*nch->string == '\0') {
562
+ mandoc_msg(MANDOCERR_DATE_MISSING, nch->line,
563
+ nch->pos, "%s", roff_name[nbl->tok]);
564
+ return mandoc_strdup("");
565
+ }
566
+ if (strcmp(nch->string, "$" "Mdocdate$") == 0)
567
+ return time2a(time(NULL));
568
+
569
+ /* Valid mdoc(7) date format. */
570
+
571
+ if (a2time(&t, "$" "Mdocdate: %b %d %Y $", nch->string) ||
572
+ a2time(&t, "%b %d, %Y", nch->string)) {
573
+ cp = time2a(t);
574
+ if (t > time(NULL) + 86400)
575
+ mandoc_msg(MANDOCERR_DATE_FUTURE, nch->line,
576
+ nch->pos, "%s %s", roff_name[nbl->tok], cp);
577
+ else if (*nch->string != '$' &&
578
+ strcmp(nch->string, cp) != 0)
579
+ mandoc_msg(MANDOCERR_DATE_NORM, nch->line,
580
+ nch->pos, "%s %s", roff_name[nbl->tok], cp);
581
+ return cp;
582
+ }
583
+
584
+ /* In man(7), do not warn about the legacy format. */
585
+
586
+ if (a2time(&t, "%Y-%m-%d", nch->string) == 0)
587
+ mandoc_msg(MANDOCERR_DATE_BAD, nch->line, nch->pos,
588
+ "%s %s", roff_name[nbl->tok], nch->string);
589
+ else if (t > time(NULL) + 86400)
590
+ mandoc_msg(MANDOCERR_DATE_FUTURE, nch->line, nch->pos,
591
+ "%s %s", roff_name[nbl->tok], nch->string);
592
+ else if (nbl->tok == MDOC_Dd)
593
+ mandoc_msg(MANDOCERR_DATE_LEGACY, nch->line, nch->pos,
594
+ "Dd %s", nch->string);
595
+
596
+ /* Use any non-mdoc(7) date verbatim. */
597
+
598
+ return mandoc_strdup(nch->string);
599
+ }
600
+
601
+ int
602
+ mandoc_eos(const char *p, size_t sz)
603
+ {
604
+ const char *q;
605
+ int enclosed, found;
606
+
607
+ if (0 == sz)
608
+ return 0;
609
+
610
+ /*
611
+ * End-of-sentence recognition must include situations where
612
+ * some symbols, such as `)', allow prior EOS punctuation to
613
+ * propagate outward.
614
+ */
615
+
616
+ enclosed = found = 0;
617
+ for (q = p + (int)sz - 1; q >= p; q--) {
618
+ switch (*q) {
619
+ case '\"':
620
+ case '\'':
621
+ case ']':
622
+ case ')':
623
+ if (0 == found)
624
+ enclosed = 1;
625
+ break;
626
+ case '.':
627
+ case '!':
628
+ case '?':
629
+ found = 1;
630
+ break;
631
+ default:
632
+ return found &&
633
+ (!enclosed || isalnum((unsigned char)*q));
634
+ }
635
+ }
636
+
637
+ return found && !enclosed;
638
+ }
639
+
640
+ /*
641
+ * Convert a string to a long that may not be <0.
642
+ * If the string is invalid, or is less than 0, return -1.
643
+ */
644
+ int
645
+ mandoc_strntoi(const char *p, size_t sz, int base)
646
+ {
647
+ char buf[32];
648
+ char *ep;
649
+ long v;
650
+
651
+ if (sz > 31)
652
+ return -1;
653
+
654
+ memcpy(buf, p, sz);
655
+ buf[(int)sz] = '\0';
656
+
657
+ errno = 0;
658
+ v = strtol(buf, &ep, base);
659
+
660
+ if (buf[0] == '\0' || *ep != '\0')
661
+ return -1;
662
+
663
+ if (v > INT_MAX)
664
+ v = INT_MAX;
665
+ if (v < INT_MIN)
666
+ v = INT_MIN;
667
+
668
+ return (int)v;
669
+ }