]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/mdocml/mandoc.c
Upgrade to OpenSSH 6.8p1.
[FreeBSD/FreeBSD.git] / contrib / mdocml / mandoc.c
1 /*      $Id: mandoc.c,v 1.98 2015/11/12 22:44:27 schwarze Exp $ */
2 /*
3  * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2011-2015 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.h"
32 #include "mandoc_aux.h"
33 #include "libmandoc.h"
34
35 static  int      a2time(time_t *, const char *, const char *);
36 static  char    *time2a(time_t);
37
38
39 enum mandoc_esc
40 mandoc_escape(const char **end, const char **start, int *sz)
41 {
42         const char      *local_start;
43         int              local_sz;
44         char             term;
45         enum mandoc_esc  gly;
46
47         /*
48          * When the caller doesn't provide return storage,
49          * use local storage.
50          */
51
52         if (NULL == start)
53                 start = &local_start;
54         if (NULL == sz)
55                 sz = &local_sz;
56
57         /*
58          * Beyond the backslash, at least one input character
59          * is part of the escape sequence.  With one exception
60          * (see below), that character won't be returned.
61          */
62
63         gly = ESCAPE_ERROR;
64         *start = ++*end;
65         *sz = 0;
66         term = '\0';
67
68         switch ((*start)[-1]) {
69         /*
70          * First the glyphs.  There are several different forms of
71          * these, but each eventually returns a substring of the glyph
72          * name.
73          */
74         case '(':
75                 gly = ESCAPE_SPECIAL;
76                 *sz = 2;
77                 break;
78         case '[':
79                 gly = ESCAPE_SPECIAL;
80                 term = ']';
81                 break;
82         case 'C':
83                 if ('\'' != **start)
84                         return ESCAPE_ERROR;
85                 *start = ++*end;
86                 gly = ESCAPE_SPECIAL;
87                 term = '\'';
88                 break;
89
90         /*
91          * Escapes taking no arguments at all.
92          */
93         case 'd':
94         case 'u':
95         case ',':
96         case '/':
97                 return ESCAPE_IGNORE;
98
99         /*
100          * The \z escape is supposed to output the following
101          * character without advancing the cursor position.
102          * Since we are mostly dealing with terminal mode,
103          * let us just skip the next character.
104          */
105         case 'z':
106                 return ESCAPE_SKIPCHAR;
107
108         /*
109          * Handle all triggers matching \X(xy, \Xx, and \X[xxxx], where
110          * 'X' is the trigger.  These have opaque sub-strings.
111          */
112         case 'F':
113         case 'g':
114         case 'k':
115         case 'M':
116         case 'm':
117         case 'n':
118         case 'V':
119         case 'Y':
120                 gly = ESCAPE_IGNORE;
121                 /* FALLTHROUGH */
122         case 'f':
123                 if (ESCAPE_ERROR == gly)
124                         gly = ESCAPE_FONT;
125                 switch (**start) {
126                 case '(':
127                         *start = ++*end;
128                         *sz = 2;
129                         break;
130                 case '[':
131                         *start = ++*end;
132                         term = ']';
133                         break;
134                 default:
135                         *sz = 1;
136                         break;
137                 }
138                 break;
139
140         /*
141          * These escapes are of the form \X'Y', where 'X' is the trigger
142          * and 'Y' is any string.  These have opaque sub-strings.
143          * The \B and \w escapes are handled in roff.c, roff_res().
144          */
145         case 'A':
146         case 'b':
147         case 'D':
148         case 'R':
149         case 'X':
150         case 'Z':
151                 gly = ESCAPE_IGNORE;
152                 /* FALLTHROUGH */
153         case 'o':
154                 if (**start == '\0')
155                         return ESCAPE_ERROR;
156                 if (gly == ESCAPE_ERROR)
157                         gly = ESCAPE_OVERSTRIKE;
158                 term = **start;
159                 *start = ++*end;
160                 break;
161
162         /*
163          * These escapes are of the form \X'N', where 'X' is the trigger
164          * and 'N' resolves to a numerical expression.
165          */
166         case 'h':
167         case 'H':
168         case 'L':
169         case 'l':
170         case 'S':
171         case 'v':
172         case 'x':
173                 if (strchr(" %&()*+-./0123456789:<=>", **start)) {
174                         if ('\0' != **start)
175                                 ++*end;
176                         return ESCAPE_ERROR;
177                 }
178                 gly = ESCAPE_IGNORE;
179                 term = **start;
180                 *start = ++*end;
181                 break;
182
183         /*
184          * Special handling for the numbered character escape.
185          * XXX Do any other escapes need similar handling?
186          */
187         case 'N':
188                 if ('\0' == **start)
189                         return ESCAPE_ERROR;
190                 (*end)++;
191                 if (isdigit((unsigned char)**start)) {
192                         *sz = 1;
193                         return ESCAPE_IGNORE;
194                 }
195                 (*start)++;
196                 while (isdigit((unsigned char)**end))
197                         (*end)++;
198                 *sz = *end - *start;
199                 if ('\0' != **end)
200                         (*end)++;
201                 return ESCAPE_NUMBERED;
202
203         /*
204          * Sizes get a special category of their own.
205          */
206         case 's':
207                 gly = ESCAPE_IGNORE;
208
209                 /* See +/- counts as a sign. */
210                 if ('+' == **end || '-' == **end || ASCII_HYPH == **end)
211                         *start = ++*end;
212
213                 switch (**end) {
214                 case '(':
215                         *start = ++*end;
216                         *sz = 2;
217                         break;
218                 case '[':
219                         *start = ++*end;
220                         term = ']';
221                         break;
222                 case '\'':
223                         *start = ++*end;
224                         term = '\'';
225                         break;
226                 case '3':
227                 case '2':
228                 case '1':
229                         *sz = (*end)[-1] == 's' &&
230                             isdigit((unsigned char)(*end)[1]) ? 2 : 1;
231                         break;
232                 default:
233                         *sz = 1;
234                         break;
235                 }
236
237                 break;
238
239         /*
240          * Anything else is assumed to be a glyph.
241          * In this case, pass back the character after the backslash.
242          */
243         default:
244                 gly = ESCAPE_SPECIAL;
245                 *start = --*end;
246                 *sz = 1;
247                 break;
248         }
249
250         assert(ESCAPE_ERROR != gly);
251
252         /*
253          * Read up to the terminating character,
254          * paying attention to nested escapes.
255          */
256
257         if ('\0' != term) {
258                 while (**end != term) {
259                         switch (**end) {
260                         case '\0':
261                                 return ESCAPE_ERROR;
262                         case '\\':
263                                 (*end)++;
264                                 if (ESCAPE_ERROR ==
265                                     mandoc_escape(end, NULL, NULL))
266                                         return ESCAPE_ERROR;
267                                 break;
268                         default:
269                                 (*end)++;
270                                 break;
271                         }
272                 }
273                 *sz = (*end)++ - *start;
274         } else {
275                 assert(*sz > 0);
276                 if ((size_t)*sz > strlen(*start))
277                         return ESCAPE_ERROR;
278                 *end += *sz;
279         }
280
281         /* Run post-processors. */
282
283         switch (gly) {
284         case ESCAPE_FONT:
285                 if (2 == *sz) {
286                         if ('C' == **start) {
287                                 /*
288                                  * Treat constant-width font modes
289                                  * just like regular font modes.
290                                  */
291                                 (*start)++;
292                                 (*sz)--;
293                         } else {
294                                 if ('B' == (*start)[0] && 'I' == (*start)[1])
295                                         gly = ESCAPE_FONTBI;
296                                 break;
297                         }
298                 } else if (1 != *sz)
299                         break;
300
301                 switch (**start) {
302                 case '3':
303                 case 'B':
304                         gly = ESCAPE_FONTBOLD;
305                         break;
306                 case '2':
307                 case 'I':
308                         gly = ESCAPE_FONTITALIC;
309                         break;
310                 case 'P':
311                         gly = ESCAPE_FONTPREV;
312                         break;
313                 case '1':
314                 case 'R':
315                         gly = ESCAPE_FONTROMAN;
316                         break;
317                 }
318                 break;
319         case ESCAPE_SPECIAL:
320                 if (1 == *sz && 'c' == **start)
321                         gly = ESCAPE_NOSPACE;
322                 /*
323                  * Unicode escapes are defined in groff as \[u0000]
324                  * to \[u10FFFF], where the contained value must be
325                  * a valid Unicode codepoint.  Here, however, only
326                  * check the length and range.
327                  */
328                 if (**start != 'u' || *sz < 5 || *sz > 7)
329                         break;
330                 if (*sz == 7 && ((*start)[1] != '1' || (*start)[2] != '0'))
331                         break;
332                 if (*sz == 6 && (*start)[1] == '0')
333                         break;
334                 if (*sz == 5 && (*start)[1] == 'D' &&
335                     strchr("89ABCDEF", (*start)[2]) != NULL)
336                         break;
337                 if ((int)strspn(*start + 1, "0123456789ABCDEFabcdef")
338                     + 1 == *sz)
339                         gly = ESCAPE_UNICODE;
340                 break;
341         default:
342                 break;
343         }
344
345         return gly;
346 }
347
348 /*
349  * Parse a quoted or unquoted roff-style request or macro argument.
350  * Return a pointer to the parsed argument, which is either the original
351  * pointer or advanced by one byte in case the argument is quoted.
352  * NUL-terminate the argument in place.
353  * Collapse pairs of quotes inside quoted arguments.
354  * Advance the argument pointer to the next argument,
355  * or to the NUL byte terminating the argument line.
356  */
357 char *
358 mandoc_getarg(struct mparse *parse, char **cpp, int ln, int *pos)
359 {
360         char     *start, *cp;
361         int       quoted, pairs, white;
362
363         /* Quoting can only start with a new word. */
364         start = *cpp;
365         quoted = 0;
366         if ('"' == *start) {
367                 quoted = 1;
368                 start++;
369         }
370
371         pairs = 0;
372         white = 0;
373         for (cp = start; '\0' != *cp; cp++) {
374
375                 /*
376                  * Move the following text left
377                  * after quoted quotes and after "\\" and "\t".
378                  */
379                 if (pairs)
380                         cp[-pairs] = cp[0];
381
382                 if ('\\' == cp[0]) {
383                         /*
384                          * In copy mode, translate double to single
385                          * backslashes and backslash-t to literal tabs.
386                          */
387                         switch (cp[1]) {
388                         case 't':
389                                 cp[0] = '\t';
390                                 /* FALLTHROUGH */
391                         case '\\':
392                                 pairs++;
393                                 cp++;
394                                 break;
395                         case ' ':
396                                 /* Skip escaped blanks. */
397                                 if (0 == quoted)
398                                         cp++;
399                                 break;
400                         default:
401                                 break;
402                         }
403                 } else if (0 == quoted) {
404                         if (' ' == cp[0]) {
405                                 /* Unescaped blanks end unquoted args. */
406                                 white = 1;
407                                 break;
408                         }
409                 } else if ('"' == cp[0]) {
410                         if ('"' == cp[1]) {
411                                 /* Quoted quotes collapse. */
412                                 pairs++;
413                                 cp++;
414                         } else {
415                                 /* Unquoted quotes end quoted args. */
416                                 quoted = 2;
417                                 break;
418                         }
419                 }
420         }
421
422         /* Quoted argument without a closing quote. */
423         if (1 == quoted)
424                 mandoc_msg(MANDOCERR_ARG_QUOTE, parse, ln, *pos, NULL);
425
426         /* NUL-terminate this argument and move to the next one. */
427         if (pairs)
428                 cp[-pairs] = '\0';
429         if ('\0' != *cp) {
430                 *cp++ = '\0';
431                 while (' ' == *cp)
432                         cp++;
433         }
434         *pos += (int)(cp - start) + (quoted ? 1 : 0);
435         *cpp = cp;
436
437         if ('\0' == *cp && (white || ' ' == cp[-1]))
438                 mandoc_msg(MANDOCERR_SPACE_EOL, parse, ln, *pos, NULL);
439
440         return start;
441 }
442
443 static int
444 a2time(time_t *t, const char *fmt, const char *p)
445 {
446         struct tm        tm;
447         char            *pp;
448
449         memset(&tm, 0, sizeof(struct tm));
450
451         pp = NULL;
452 #if HAVE_STRPTIME
453         pp = strptime(p, fmt, &tm);
454 #endif
455         if (NULL != pp && '\0' == *pp) {
456                 *t = mktime(&tm);
457                 return 1;
458         }
459
460         return 0;
461 }
462
463 static char *
464 time2a(time_t t)
465 {
466         struct tm       *tm;
467         char            *buf, *p;
468         size_t           ssz;
469         int              isz;
470
471         tm = localtime(&t);
472         if (tm == NULL)
473                 return NULL;
474
475         /*
476          * Reserve space:
477          * up to 9 characters for the month (September) + blank
478          * up to 2 characters for the day + comma + blank
479          * 4 characters for the year and a terminating '\0'
480          */
481
482         p = buf = mandoc_malloc(10 + 4 + 4 + 1);
483
484         if ((ssz = strftime(p, 10 + 1, "%B ", tm)) == 0)
485                 goto fail;
486         p += (int)ssz;
487
488         /*
489          * The output format is just "%d" here, not "%2d" or "%02d".
490          * That's also the reason why we can't just format the
491          * date as a whole with "%B %e, %Y" or "%B %d, %Y".
492          * Besides, the present approach is less prone to buffer
493          * overflows, in case anybody should ever introduce the bug
494          * of looking at LC_TIME.
495          */
496
497         if ((isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday)) == -1)
498                 goto fail;
499         p += isz;
500
501         if (strftime(p, 4 + 1, "%Y", tm) == 0)
502                 goto fail;
503         return buf;
504
505 fail:
506         free(buf);
507         return NULL;
508 }
509
510 char *
511 mandoc_normdate(struct mparse *parse, char *in, int ln, int pos)
512 {
513         time_t           t;
514
515         /* No date specified: use today's date. */
516
517         if (in == NULL || *in == '\0' || strcmp(in, "$" "Mdocdate$") == 0) {
518                 mandoc_msg(MANDOCERR_DATE_MISSING, parse, ln, pos, NULL);
519                 return time2a(time(NULL));
520         }
521
522         /* Valid mdoc(7) date format. */
523
524         if (a2time(&t, "$" "Mdocdate: %b %d %Y $", in) ||
525             a2time(&t, "%b %d, %Y", in))
526                 return time2a(t);
527
528         /* Do not warn about the legacy man(7) format. */
529
530         if ( ! a2time(&t, "%Y-%m-%d", in))
531                 mandoc_msg(MANDOCERR_DATE_BAD, parse, ln, pos, in);
532
533         /* Use any non-mdoc(7) date verbatim. */
534
535         return mandoc_strdup(in);
536 }
537
538 int
539 mandoc_eos(const char *p, size_t sz)
540 {
541         const char      *q;
542         int              enclosed, found;
543
544         if (0 == sz)
545                 return 0;
546
547         /*
548          * End-of-sentence recognition must include situations where
549          * some symbols, such as `)', allow prior EOS punctuation to
550          * propagate outward.
551          */
552
553         enclosed = found = 0;
554         for (q = p + (int)sz - 1; q >= p; q--) {
555                 switch (*q) {
556                 case '\"':
557                 case '\'':
558                 case ']':
559                 case ')':
560                         if (0 == found)
561                                 enclosed = 1;
562                         break;
563                 case '.':
564                 case '!':
565                 case '?':
566                         found = 1;
567                         break;
568                 default:
569                         return found &&
570                             (!enclosed || isalnum((unsigned char)*q));
571                 }
572         }
573
574         return found && !enclosed;
575 }
576
577 /*
578  * Convert a string to a long that may not be <0.
579  * If the string is invalid, or is less than 0, return -1.
580  */
581 int
582 mandoc_strntoi(const char *p, size_t sz, int base)
583 {
584         char             buf[32];
585         char            *ep;
586         long             v;
587
588         if (sz > 31)
589                 return -1;
590
591         memcpy(buf, p, sz);
592         buf[(int)sz] = '\0';
593
594         errno = 0;
595         v = strtol(buf, &ep, base);
596
597         if (buf[0] == '\0' || *ep != '\0')
598                 return -1;
599
600         if (v > INT_MAX)
601                 v = INT_MAX;
602         if (v < INT_MIN)
603                 v = INT_MIN;
604
605         return (int)v;
606 }