Print this page
Update to 1.12.3.
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/mandoc/mandoc.c
+++ new/usr/src/cmd/mandoc/mandoc.c
1 -/* $Id: mandoc.c,v 1.62 2011/12/03 16:08:51 schwarze Exp $ */
1 +/* $Id: mandoc.c,v 1.74 2013/12/30 18:30:32 schwarze Exp $ */
2 2 /*
3 3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 - * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
4 + * Copyright (c) 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
5 5 *
6 6 * Permission to use, copy, modify, and distribute this software for any
7 7 * purpose with or without fee is hereby granted, provided that the above
8 8 * copyright notice and this permission notice appear in all copies.
9 9 *
10 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 17 */
18 18 #ifdef HAVE_CONFIG_H
19 19 #include "config.h"
20 20 #endif
21 21
22 22 #include <sys/types.h>
23 23
24 24 #include <assert.h>
25 25 #include <ctype.h>
26 26 #include <errno.h>
27 27 #include <limits.h>
28 28 #include <stdlib.h>
29 29 #include <stdio.h>
↓ open down ↓ |
15 lines elided |
↑ open up ↑ |
30 30 #include <string.h>
31 31 #include <time.h>
32 32
33 33 #include "mandoc.h"
34 34 #include "libmandoc.h"
35 35
36 36 #define DATESIZE 32
37 37
38 38 static int a2time(time_t *, const char *, const char *);
39 39 static char *time2a(time_t);
40 -static int numescape(const char *);
41 40
42 -/*
43 - * Pass over recursive numerical expressions. This context of this
44 - * function is important: it's only called within character-terminating
45 - * escapes (e.g., \s[xxxyyy]), so all we need to do is handle initial
46 - * recursion: we don't care about what's in these blocks.
47 - * This returns the number of characters skipped or -1 if an error
48 - * occurs (the caller should bail).
49 - */
50 -static int
51 -numescape(const char *start)
41 +
42 +enum mandoc_esc
43 +mandoc_escape(const char **end, const char **start, int *sz)
52 44 {
53 - int i;
54 - size_t sz;
55 - const char *cp;
45 + const char *local_start;
46 + int local_sz;
47 + char term;
48 + enum mandoc_esc gly;
56 49
57 - i = 0;
50 + /*
51 + * When the caller doesn't provide return storage,
52 + * use local storage.
53 + */
58 54
59 - /* The expression consists of a subexpression. */
55 + if (NULL == start)
56 + start = &local_start;
57 + if (NULL == sz)
58 + sz = &local_sz;
60 59
61 - if ('\\' == start[i]) {
62 - cp = &start[++i];
63 - /*
64 - * Read past the end of the subexpression.
65 - * Bail immediately on errors.
66 - */
67 - if (ESCAPE_ERROR == mandoc_escape(&cp, NULL, NULL))
68 - return(-1);
69 - return(i + cp - &start[i]);
70 - }
71 -
72 - if ('(' != start[i++])
73 - return(0);
74 -
75 60 /*
76 - * A parenthesised subexpression. Read until the closing
77 - * parenthesis, making sure to handle any nested subexpressions
78 - * that might ruin our parse.
61 + * Beyond the backslash, at least one input character
62 + * is part of the escape sequence. With one exception
63 + * (see below), that character won't be returned.
79 64 */
80 65
81 - while (')' != start[i]) {
82 - sz = strcspn(&start[i], ")\\");
83 - i += (int)sz;
84 -
85 - if ('\0' == start[i])
86 - return(-1);
87 - else if ('\\' != start[i])
88 - continue;
89 -
90 - cp = &start[++i];
91 - if (ESCAPE_ERROR == mandoc_escape(&cp, NULL, NULL))
92 - return(-1);
93 - i += cp - &start[i];
94 - }
95 -
96 - /* Read past the terminating ')'. */
97 - return(++i);
98 -}
99 -
100 -enum mandoc_esc
101 -mandoc_escape(const char **end, const char **start, int *sz)
102 -{
103 - char c, term, numeric;
104 - int i, lim, ssz, rlim;
105 - const char *cp, *rstart;
106 - enum mandoc_esc gly;
107 -
108 - cp = *end;
109 - rstart = cp;
110 - if (start)
111 - *start = rstart;
112 - i = lim = 0;
113 66 gly = ESCAPE_ERROR;
114 - term = numeric = '\0';
67 + *start = ++*end;
68 + *sz = 0;
69 + term = '\0';
115 70
116 - switch ((c = cp[i++])) {
71 + switch ((*start)[-1]) {
117 72 /*
118 73 * First the glyphs. There are several different forms of
119 74 * these, but each eventually returns a substring of the glyph
120 75 * name.
121 76 */
122 77 case ('('):
123 78 gly = ESCAPE_SPECIAL;
124 - lim = 2;
79 + *sz = 2;
125 80 break;
126 81 case ('['):
127 82 gly = ESCAPE_SPECIAL;
128 83 /*
129 84 * Unicode escapes are defined in groff as \[uXXXX] to
130 85 * \[u10FFFF], where the contained value must be a valid
131 86 * Unicode codepoint. Here, however, only check whether
132 87 * it's not a zero-width escape.
133 88 */
134 - if ('u' == cp[i] && ']' != cp[i + 1])
89 + if ('u' == (*start)[0] && ']' != (*start)[1])
135 90 gly = ESCAPE_UNICODE;
136 91 term = ']';
137 92 break;
138 93 case ('C'):
139 - if ('\'' != cp[i])
94 + if ('\'' != **start)
140 95 return(ESCAPE_ERROR);
141 - gly = ESCAPE_SPECIAL;
96 + *start = ++*end;
97 + if ('u' == (*start)[0] && '\'' != (*start)[1])
98 + gly = ESCAPE_UNICODE;
99 + else
100 + gly = ESCAPE_SPECIAL;
142 101 term = '\'';
143 102 break;
144 103
145 104 /*
105 + * Escapes taking no arguments at all.
106 + */
107 + case ('d'):
108 + /* FALLTHROUGH */
109 + case ('u'):
110 + return(ESCAPE_IGNORE);
111 +
112 + /*
113 + * The \z escape is supposed to output the following
114 + * character without advancing the cursor position.
115 + * Since we are mostly dealing with terminal mode,
116 + * let us just skip the next character.
117 + */
118 + case ('z'):
119 + return(ESCAPE_SKIPCHAR);
120 +
121 + /*
146 122 * Handle all triggers matching \X(xy, \Xx, and \X[xxxx], where
147 123 * 'X' is the trigger. These have opaque sub-strings.
148 124 */
149 125 case ('F'):
150 126 /* FALLTHROUGH */
151 127 case ('g'):
152 128 /* FALLTHROUGH */
153 129 case ('k'):
154 130 /* FALLTHROUGH */
155 131 case ('M'):
156 132 /* FALLTHROUGH */
157 133 case ('m'):
158 134 /* FALLTHROUGH */
↓ open down ↓ |
3 lines elided |
↑ open up ↑ |
159 135 case ('n'):
160 136 /* FALLTHROUGH */
161 137 case ('V'):
162 138 /* FALLTHROUGH */
163 139 case ('Y'):
164 140 gly = ESCAPE_IGNORE;
165 141 /* FALLTHROUGH */
166 142 case ('f'):
167 143 if (ESCAPE_ERROR == gly)
168 144 gly = ESCAPE_FONT;
169 -
170 - rstart= &cp[i];
171 - if (start)
172 - *start = rstart;
173 -
174 - switch (cp[i++]) {
145 + switch (**start) {
175 146 case ('('):
176 - lim = 2;
147 + *start = ++*end;
148 + *sz = 2;
177 149 break;
178 150 case ('['):
151 + *start = ++*end;
179 152 term = ']';
180 153 break;
181 154 default:
182 - lim = 1;
183 - i--;
155 + *sz = 1;
184 156 break;
185 157 }
186 158 break;
187 159
188 160 /*
189 161 * These escapes are of the form \X'Y', where 'X' is the trigger
190 162 * and 'Y' is any string. These have opaque sub-strings.
191 163 */
192 164 case ('A'):
193 165 /* FALLTHROUGH */
194 166 case ('b'):
195 167 /* FALLTHROUGH */
168 + case ('B'):
169 + /* FALLTHROUGH */
196 170 case ('D'):
197 171 /* FALLTHROUGH */
198 172 case ('o'):
199 173 /* FALLTHROUGH */
200 174 case ('R'):
201 175 /* FALLTHROUGH */
176 + case ('w'):
177 + /* FALLTHROUGH */
202 178 case ('X'):
203 179 /* FALLTHROUGH */
204 180 case ('Z'):
205 - if ('\'' != cp[i++])
181 + if ('\'' != **start)
206 182 return(ESCAPE_ERROR);
207 183 gly = ESCAPE_IGNORE;
184 + *start = ++*end;
208 185 term = '\'';
209 186 break;
210 187
211 188 /*
212 189 * These escapes are of the form \X'N', where 'X' is the trigger
213 190 * and 'N' resolves to a numerical expression.
214 191 */
215 - case ('B'):
216 - /* FALLTHROUGH */
217 192 case ('h'):
218 193 /* FALLTHROUGH */
219 194 case ('H'):
220 195 /* FALLTHROUGH */
221 196 case ('L'):
222 197 /* FALLTHROUGH */
223 198 case ('l'):
224 - gly = ESCAPE_NUMBERED;
225 199 /* FALLTHROUGH */
226 200 case ('S'):
227 201 /* FALLTHROUGH */
228 202 case ('v'):
229 203 /* FALLTHROUGH */
230 - case ('w'):
231 - /* FALLTHROUGH */
232 204 case ('x'):
233 - if (ESCAPE_ERROR == gly)
234 - gly = ESCAPE_IGNORE;
235 - if ('\'' != cp[i++])
205 + if ('\'' != **start)
236 206 return(ESCAPE_ERROR);
237 - term = numeric = '\'';
207 + gly = ESCAPE_IGNORE;
208 + *start = ++*end;
209 + term = '\'';
238 210 break;
239 211
240 212 /*
241 213 * Special handling for the numbered character escape.
242 214 * XXX Do any other escapes need similar handling?
243 215 */
244 216 case ('N'):
245 - if ('\0' == cp[i])
217 + if ('\0' == **start)
246 218 return(ESCAPE_ERROR);
247 - *end = &cp[++i];
248 - if (isdigit((unsigned char)cp[i-1]))
219 + (*end)++;
220 + if (isdigit((unsigned char)**start)) {
221 + *sz = 1;
249 222 return(ESCAPE_IGNORE);
223 + }
224 + (*start)++;
250 225 while (isdigit((unsigned char)**end))
251 226 (*end)++;
252 - if (start)
253 - *start = &cp[i];
254 - if (sz)
255 - *sz = *end - &cp[i];
227 + *sz = *end - *start;
256 228 if ('\0' != **end)
257 229 (*end)++;
258 230 return(ESCAPE_NUMBERED);
259 231
260 232 /*
261 233 * Sizes get a special category of their own.
262 234 */
263 235 case ('s'):
264 236 gly = ESCAPE_IGNORE;
265 237
266 - rstart = &cp[i];
267 - if (start)
268 - *start = rstart;
269 -
270 238 /* See +/- counts as a sign. */
271 - c = cp[i];
272 - if ('+' == c || '-' == c || ASCII_HYPH == c)
273 - ++i;
239 + if ('+' == **end || '-' == **end || ASCII_HYPH == **end)
240 + (*end)++;
274 241
275 - switch (cp[i++]) {
242 + switch (**end) {
276 243 case ('('):
277 - lim = 2;
244 + *start = ++*end;
245 + *sz = 2;
278 246 break;
279 247 case ('['):
280 - term = numeric = ']';
248 + *start = ++*end;
249 + term = ']';
281 250 break;
282 251 case ('\''):
283 - term = numeric = '\'';
252 + *start = ++*end;
253 + term = '\'';
284 254 break;
285 255 default:
286 - lim = 1;
287 - i--;
256 + *sz = 1;
288 257 break;
289 258 }
290 259
291 - /* See +/- counts as a sign. */
292 - c = cp[i];
293 - if ('+' == c || '-' == c || ASCII_HYPH == c)
294 - ++i;
295 -
296 260 break;
297 261
298 262 /*
299 263 * Anything else is assumed to be a glyph.
264 + * In this case, pass back the character after the backslash.
300 265 */
301 266 default:
302 267 gly = ESCAPE_SPECIAL;
303 - lim = 1;
304 - i--;
268 + *start = --*end;
269 + *sz = 1;
305 270 break;
306 271 }
307 272
308 273 assert(ESCAPE_ERROR != gly);
309 274
310 - rstart = &cp[i];
311 - if (start)
312 - *start = rstart;
313 -
314 275 /*
315 - * If a terminating block has been specified, we need to
316 - * handle the case of recursion, which could have their
317 - * own terminating blocks that mess up our parse. This, by the
318 - * way, means that the "start" and "size" values will be
319 - * effectively meaningless.
276 + * Read up to the terminating character,
277 + * paying attention to nested escapes.
320 278 */
321 279
322 - ssz = 0;
323 - if (numeric && -1 == (ssz = numescape(&cp[i])))
324 - return(ESCAPE_ERROR);
325 -
326 - i += ssz;
327 - rlim = -1;
328 -
329 - /*
330 - * We have a character terminator. Try to read up to that
331 - * character. If we can't (i.e., we hit the nil), then return
332 - * an error; if we can, calculate our length, read past the
333 - * terminating character, and exit.
334 - */
335 -
336 280 if ('\0' != term) {
337 - *end = strchr(&cp[i], term);
338 - if ('\0' == *end)
281 + while (**end != term) {
282 + switch (**end) {
283 + case ('\0'):
284 + return(ESCAPE_ERROR);
285 + case ('\\'):
286 + (*end)++;
287 + if (ESCAPE_ERROR ==
288 + mandoc_escape(end, NULL, NULL))
289 + return(ESCAPE_ERROR);
290 + break;
291 + default:
292 + (*end)++;
293 + break;
294 + }
295 + }
296 + *sz = (*end)++ - *start;
297 + } else {
298 + assert(*sz > 0);
299 + if ((size_t)*sz > strlen(*start))
339 300 return(ESCAPE_ERROR);
340 -
341 - rlim = *end - &cp[i];
342 - if (sz)
343 - *sz = rlim;
344 - (*end)++;
345 - goto out;
301 + *end += *sz;
346 302 }
347 303
348 - assert(lim > 0);
349 -
350 - /*
351 - * We have a numeric limit. If the string is shorter than that,
352 - * stop and return an error. Else adjust our endpoint, length,
353 - * and return the current glyph.
354 - */
355 -
356 - if ((size_t)lim > strlen(&cp[i]))
357 - return(ESCAPE_ERROR);
358 -
359 - rlim = lim;
360 - if (sz)
361 - *sz = rlim;
362 -
363 - *end = &cp[i] + lim;
364 -
365 -out:
366 - assert(rlim >= 0 && rstart);
367 -
368 304 /* Run post-processors. */
369 305
370 306 switch (gly) {
371 307 case (ESCAPE_FONT):
372 - /*
373 - * Pretend that the constant-width font modes are the
374 - * same as the regular font modes.
375 - */
376 - if (2 == rlim && 'C' == *rstart)
377 - rstart++;
378 - else if (1 != rlim)
308 + if (2 == *sz) {
309 + if ('C' == **start) {
310 + /*
311 + * Treat constant-width font modes
312 + * just like regular font modes.
313 + */
314 + (*start)++;
315 + (*sz)--;
316 + } else {
317 + if ('B' == (*start)[0] && 'I' == (*start)[1])
318 + gly = ESCAPE_FONTBI;
319 + break;
320 + }
321 + } else if (1 != *sz)
379 322 break;
380 323
381 - switch (*rstart) {
324 + switch (**start) {
382 325 case ('3'):
383 326 /* FALLTHROUGH */
384 327 case ('B'):
385 328 gly = ESCAPE_FONTBOLD;
386 329 break;
387 330 case ('2'):
388 331 /* FALLTHROUGH */
389 332 case ('I'):
390 333 gly = ESCAPE_FONTITALIC;
391 334 break;
392 335 case ('P'):
↓ open down ↓ |
1 lines elided |
↑ open up ↑ |
393 336 gly = ESCAPE_FONTPREV;
394 337 break;
395 338 case ('1'):
396 339 /* FALLTHROUGH */
397 340 case ('R'):
398 341 gly = ESCAPE_FONTROMAN;
399 342 break;
400 343 }
401 344 break;
402 345 case (ESCAPE_SPECIAL):
403 - if (1 != rlim)
404 - break;
405 - if ('c' == *rstart)
346 + if (1 == *sz && 'c' == **start)
406 347 gly = ESCAPE_NOSPACE;
407 348 break;
408 349 default:
409 350 break;
410 351 }
411 352
412 353 return(gly);
413 354 }
414 355
415 356 void *
416 357 mandoc_calloc(size_t num, size_t size)
417 358 {
418 359 void *ptr;
419 360
420 361 ptr = calloc(num, size);
421 362 if (NULL == ptr) {
422 363 perror(NULL);
423 364 exit((int)MANDOCLEVEL_SYSERR);
424 365 }
425 366
426 367 return(ptr);
427 368 }
428 369
429 370
430 371 void *
431 372 mandoc_malloc(size_t size)
432 373 {
433 374 void *ptr;
434 375
435 376 ptr = malloc(size);
436 377 if (NULL == ptr) {
437 378 perror(NULL);
438 379 exit((int)MANDOCLEVEL_SYSERR);
439 380 }
440 381
441 382 return(ptr);
442 383 }
443 384
444 385
445 386 void *
446 387 mandoc_realloc(void *ptr, size_t size)
447 388 {
448 389
449 390 ptr = realloc(ptr, size);
450 391 if (NULL == ptr) {
451 392 perror(NULL);
452 393 exit((int)MANDOCLEVEL_SYSERR);
453 394 }
454 395
455 396 return(ptr);
456 397 }
457 398
458 399 char *
459 400 mandoc_strndup(const char *ptr, size_t sz)
460 401 {
461 402 char *p;
462 403
463 404 p = mandoc_malloc(sz + 1);
464 405 memcpy(p, ptr, sz);
465 406 p[(int)sz] = '\0';
466 407 return(p);
467 408 }
468 409
469 410 char *
470 411 mandoc_strdup(const char *ptr)
471 412 {
472 413 char *p;
473 414
474 415 p = strdup(ptr);
475 416 if (NULL == p) {
476 417 perror(NULL);
↓ open down ↓ |
61 lines elided |
↑ open up ↑ |
477 418 exit((int)MANDOCLEVEL_SYSERR);
478 419 }
479 420
480 421 return(p);
481 422 }
482 423
483 424 /*
484 425 * Parse a quoted or unquoted roff-style request or macro argument.
485 426 * Return a pointer to the parsed argument, which is either the original
486 427 * pointer or advanced by one byte in case the argument is quoted.
487 - * Null-terminate the argument in place.
428 + * NUL-terminate the argument in place.
488 429 * Collapse pairs of quotes inside quoted arguments.
489 430 * Advance the argument pointer to the next argument,
490 - * or to the null byte terminating the argument line.
431 + * or to the NUL byte terminating the argument line.
491 432 */
492 433 char *
493 434 mandoc_getarg(struct mparse *parse, char **cpp, int ln, int *pos)
494 435 {
495 436 char *start, *cp;
496 437 int quoted, pairs, white;
497 438
498 439 /* Quoting can only start with a new word. */
499 440 start = *cpp;
500 441 quoted = 0;
501 442 if ('"' == *start) {
502 443 quoted = 1;
503 444 start++;
504 445 }
505 446
506 447 pairs = 0;
507 448 white = 0;
508 449 for (cp = start; '\0' != *cp; cp++) {
509 - /* Move left after quoted quotes and escaped backslashes. */
450 +
451 + /*
452 + * Move the following text left
453 + * after quoted quotes and after "\\" and "\t".
454 + */
510 455 if (pairs)
511 456 cp[-pairs] = cp[0];
457 +
512 458 if ('\\' == cp[0]) {
513 - if ('\\' == cp[1]) {
514 - /* Poor man's copy mode. */
459 + /*
460 + * In copy mode, translate double to single
461 + * backslashes and backslash-t to literal tabs.
462 + */
463 + switch (cp[1]) {
464 + case ('t'):
465 + cp[0] = '\t';
466 + /* FALLTHROUGH */
467 + case ('\\'):
515 468 pairs++;
516 469 cp++;
517 - } else if (0 == quoted && ' ' == cp[1])
470 + break;
471 + case (' '):
518 472 /* Skip escaped blanks. */
519 - cp++;
473 + if (0 == quoted)
474 + cp++;
475 + break;
476 + default:
477 + break;
478 + }
520 479 } else if (0 == quoted) {
521 480 if (' ' == cp[0]) {
522 481 /* Unescaped blanks end unquoted args. */
523 482 white = 1;
524 483 break;
525 484 }
526 485 } else if ('"' == cp[0]) {
527 486 if ('"' == cp[1]) {
528 487 /* Quoted quotes collapse. */
529 488 pairs++;
530 489 cp++;
531 490 } else {
532 491 /* Unquoted quotes end quoted args. */
↓ open down ↓ |
3 lines elided |
↑ open up ↑ |
533 492 quoted = 2;
534 493 break;
535 494 }
536 495 }
537 496 }
538 497
539 498 /* Quoted argument without a closing quote. */
540 499 if (1 == quoted)
541 500 mandoc_msg(MANDOCERR_BADQUOTE, parse, ln, *pos, NULL);
542 501
543 - /* Null-terminate this argument and move to the next one. */
502 + /* NUL-terminate this argument and move to the next one. */
544 503 if (pairs)
545 504 cp[-pairs] = '\0';
546 505 if ('\0' != *cp) {
547 506 *cp++ = '\0';
548 507 while (' ' == *cp)
549 508 cp++;
550 509 }
551 510 *pos += (int)(cp - start) + (quoted ? 1 : 0);
552 511 *cpp = cp;
553 512
554 513 if ('\0' == *cp && (white || ' ' == cp[-1]))
555 514 mandoc_msg(MANDOCERR_EOLNSPACE, parse, ln, *pos, NULL);
556 515
557 516 return(start);
558 517 }
559 518
560 519 static int
561 520 a2time(time_t *t, const char *fmt, const char *p)
562 521 {
563 522 struct tm tm;
564 523 char *pp;
565 524
566 525 memset(&tm, 0, sizeof(struct tm));
567 526
568 527 pp = NULL;
569 528 #ifdef HAVE_STRPTIME
570 529 pp = strptime(p, fmt, &tm);
571 530 #endif
572 531 if (NULL != pp && '\0' == *pp) {
573 532 *t = mktime(&tm);
574 533 return(1);
575 534 }
576 535
577 536 return(0);
578 537 }
579 538
580 539 static char *
581 540 time2a(time_t t)
582 541 {
583 542 struct tm *tm;
584 543 char *buf, *p;
585 544 size_t ssz;
586 545 int isz;
587 546
588 547 tm = localtime(&t);
589 548
590 549 /*
591 550 * Reserve space:
592 551 * up to 9 characters for the month (September) + blank
593 552 * up to 2 characters for the day + comma + blank
594 553 * 4 characters for the year and a terminating '\0'
595 554 */
596 555 p = buf = mandoc_malloc(10 + 4 + 4 + 1);
597 556
598 557 if (0 == (ssz = strftime(p, 10 + 1, "%B ", tm)))
599 558 goto fail;
600 559 p += (int)ssz;
601 560
602 561 if (-1 == (isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday)))
603 562 goto fail;
604 563 p += isz;
605 564
606 565 if (0 == strftime(p, 4 + 1, "%Y", tm))
607 566 goto fail;
608 567 return(buf);
609 568
610 569 fail:
611 570 free(buf);
612 571 return(NULL);
613 572 }
614 573
615 574 char *
616 575 mandoc_normdate(struct mparse *parse, char *in, int ln, int pos)
617 576 {
618 577 char *out;
619 578 time_t t;
620 579
621 580 if (NULL == in || '\0' == *in ||
622 581 0 == strcmp(in, "$" "Mdocdate$")) {
623 582 mandoc_msg(MANDOCERR_NODATE, parse, ln, pos, NULL);
624 583 time(&t);
625 584 }
626 585 else if (a2time(&t, "%Y-%m-%d", in))
627 586 t = 0;
628 587 else if (!a2time(&t, "$" "Mdocdate: %b %d %Y $", in) &&
629 588 !a2time(&t, "%b %d, %Y", in)) {
630 589 mandoc_msg(MANDOCERR_BADDATE, parse, ln, pos, NULL);
631 590 t = 0;
632 591 }
633 592 out = t ? time2a(t) : NULL;
634 593 return(out ? out : mandoc_strdup(in));
635 594 }
636 595
637 596 int
638 597 mandoc_eos(const char *p, size_t sz, int enclosed)
639 598 {
640 599 const char *q;
641 600 int found;
642 601
643 602 if (0 == sz)
644 603 return(0);
645 604
646 605 /*
647 606 * End-of-sentence recognition must include situations where
648 607 * some symbols, such as `)', allow prior EOS punctuation to
649 608 * propagate outward.
650 609 */
651 610
652 611 found = 0;
653 612 for (q = p + (int)sz - 1; q >= p; q--) {
654 613 switch (*q) {
655 614 case ('\"'):
656 615 /* FALLTHROUGH */
657 616 case ('\''):
658 617 /* FALLTHROUGH */
659 618 case (']'):
660 619 /* FALLTHROUGH */
661 620 case (')'):
662 621 if (0 == found)
663 622 enclosed = 1;
664 623 break;
665 624 case ('.'):
666 625 /* FALLTHROUGH */
667 626 case ('!'):
668 627 /* FALLTHROUGH */
669 628 case ('?'):
↓ open down ↓ |
116 lines elided |
↑ open up ↑ |
670 629 found = 1;
671 630 break;
672 631 default:
673 632 return(found && (!enclosed || isalnum((unsigned char)*q)));
674 633 }
675 634 }
676 635
677 636 return(found && !enclosed);
678 637 }
679 638
680 -/*
681 - * Find out whether a line is a macro line or not. If it is, adjust the
682 - * current position and return one; if it isn't, return zero and don't
683 - * change the current position.
684 - */
685 -int
686 -mandoc_getcontrol(const char *cp, int *ppos)
687 -{
688 - int pos;
689 -
690 - pos = *ppos;
691 -
692 - if ('\\' == cp[pos] && '.' == cp[pos + 1])
693 - pos += 2;
694 - else if ('.' == cp[pos] || '\'' == cp[pos])
695 - pos++;
696 - else
697 - return(0);
698 -
699 - while (' ' == cp[pos] || '\t' == cp[pos])
700 - pos++;
701 -
702 - *ppos = pos;
703 - return(1);
704 -}
705 -
706 639 /*
707 640 * Convert a string to a long that may not be <0.
708 641 * If the string is invalid, or is less than 0, return -1.
709 642 */
710 643 int
711 644 mandoc_strntoi(const char *p, size_t sz, int base)
712 645 {
713 646 char buf[32];
714 647 char *ep;
715 648 long v;
716 649
717 650 if (sz > 31)
718 651 return(-1);
719 652
720 653 memcpy(buf, p, sz);
721 654 buf[(int)sz] = '\0';
722 655
723 656 errno = 0;
724 657 v = strtol(buf, &ep, base);
725 658
726 659 if (buf[0] == '\0' || *ep != '\0')
727 660 return(-1);
728 661
729 662 if (v > INT_MAX)
730 663 v = INT_MAX;
731 664 if (v < INT_MIN)
732 665 v = INT_MIN;
733 666
734 667 return((int)v);
735 668 }
↓ open down ↓ |
20 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX