mandoc.c revision 371584c2eae4cf827fd406ba26c14f021adaaa70
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov/* $Id: mandoc.c,v 1.98 2015/11/12 22:44:27 schwarze Exp $ */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore/*
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * Copyright (c) 2011-2015 Ingo Schwarze <schwarze@openbsd.org>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore *
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * copyright notice and this permission notice appear in all copies.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore *
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include "config.h"
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include <sys/types.h>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include <assert.h>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include <ctype.h>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include <errno.h>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include <limits.h>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include <stdlib.h>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include <stdio.h>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include <string.h>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include <time.h>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include "mandoc.h"
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov#include "mandoc_aux.h"
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include "libmandoc.h"
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic int a2time(time_t *, const char *, const char *);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic char *time2a(time_t);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreenum mandoc_esc
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoremandoc_escape(const char **end, const char **start, int *sz)
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore{
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore const char *local_start;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore int local_sz;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore char term;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov enum mandoc_esc gly;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * When the caller doesn't provide return storage,
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * use local storage.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore if (NULL == start)
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore start = &local_start;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore if (NULL == sz)
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore sz = &local_sz;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore /*
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * Beyond the backslash, at least one input character
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * is part of the escape sequence. With one exception
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * (see below), that character won't be returned.
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore gly = ESCAPE_ERROR;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *start = ++*end;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *sz = 0;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore term = '\0';
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore switch ((*start)[-1]) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * First the glyphs. There are several different forms of
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * these, but each eventually returns a substring of the glyph
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * name.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '(':
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore gly = ESCAPE_SPECIAL;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *sz = 2;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '[':
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore gly = ESCAPE_SPECIAL;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore term = ']';
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'C':
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore if ('\'' != **start)
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return ESCAPE_ERROR;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *start = ++*end;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov gly = ESCAPE_SPECIAL;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore term = '\'';
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore /*
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * Escapes taking no arguments at all.
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'd':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'u':
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov case ',':
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov case '/':
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return ESCAPE_IGNORE;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore /*
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * The \z escape is supposed to output the following
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * character without advancing the cursor position.
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * Since we are mostly dealing with terminal mode,
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * let us just skip the next character.
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'z':
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return ESCAPE_SKIPCHAR;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Handle all triggers matching \X(xy, \Xx, and \X[xxxx], where
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * 'X' is the trigger. These have opaque sub-strings.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'F':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'g':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'k':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'M':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'm':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'n':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'V':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'Y':
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore gly = ESCAPE_IGNORE;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'f':
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (ESCAPE_ERROR == gly)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore gly = ESCAPE_FONT;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore switch (**start) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '(':
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *start = ++*end;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *sz = 2;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '[':
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *start = ++*end;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore term = ']';
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore default:
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *sz = 1;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * These escapes are of the form \X'Y', where 'X' is the trigger
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * and 'Y' is any string. These have opaque sub-strings.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * The \B and \w escapes are handled in roff.c, roff_res().
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'A':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'b':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'D':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'R':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'X':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'Z':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov gly = ESCAPE_IGNORE;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'o':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (**start == '\0')
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return ESCAPE_ERROR;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (gly == ESCAPE_ERROR)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov gly = ESCAPE_OVERSTRIKE;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov term = **start;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *start = ++*end;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * These escapes are of the form \X'N', where 'X' is the trigger
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * and 'N' resolves to a numerical expression.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'h':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'H':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'L':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'l':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'S':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'v':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'x':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (strchr(" %&()*+-./0123456789:<=>", **start)) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if ('\0' != **start)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov ++*end;
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return ESCAPE_ERROR;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov }
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore gly = ESCAPE_IGNORE;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov term = **start;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *start = ++*end;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Special handling for the numbered character escape.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * XXX Do any other escapes need similar handling?
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'N':
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore if ('\0' == **start)
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return ESCAPE_ERROR;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore (*end)++;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore if (isdigit((unsigned char)**start)) {
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *sz = 1;
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return ESCAPE_IGNORE;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore }
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore (*start)++;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore while (isdigit((unsigned char)**end))
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore (*end)++;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *sz = *end - *start;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if ('\0' != **end)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore (*end)++;
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return ESCAPE_NUMBERED;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov /*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Sizes get a special category of their own.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 's':
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore gly = ESCAPE_IGNORE;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* See +/- counts as a sign. */
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore if ('+' == **end || '-' == **end || ASCII_HYPH == **end)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov *start = ++*end;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore switch (**end) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '(':
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *start = ++*end;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *sz = 2;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '[':
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *start = ++*end;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore term = ']';
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '\'':
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *start = ++*end;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore term = '\'';
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '3':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '2':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '1':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov *sz = (*end)[-1] == 's' &&
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov isdigit((unsigned char)(*end)[1]) ? 2 : 1;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore default:
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *sz = 1;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Anything else is assumed to be a glyph.
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * In this case, pass back the character after the backslash.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore default:
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore gly = ESCAPE_SPECIAL;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *start = --*end;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *sz = 1;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore assert(ESCAPE_ERROR != gly);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * Read up to the terminating character,
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * paying attention to nested escapes.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if ('\0' != term) {
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore while (**end != term) {
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore switch (**end) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '\0':
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return ESCAPE_ERROR;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '\\':
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore (*end)++;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore if (ESCAPE_ERROR ==
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore mandoc_escape(end, NULL, NULL))
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return ESCAPE_ERROR;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore break;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore default:
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore (*end)++;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore break;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore }
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore }
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *sz = (*end)++ - *start;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore } else {
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore assert(*sz > 0);
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore if ((size_t)*sz > strlen(*start))
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return ESCAPE_ERROR;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore *end += *sz;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Run post-processors. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore switch (gly) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case ESCAPE_FONT:
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore if (2 == *sz) {
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore if ('C' == **start) {
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore /*
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * Treat constant-width font modes
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * just like regular font modes.
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore */
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore (*start)++;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore (*sz)--;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore } else {
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore if ('B' == (*start)[0] && 'I' == (*start)[1])
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore gly = ESCAPE_FONTBI;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore break;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore }
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore } else if (1 != *sz)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore switch (**start) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '3':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'B':
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore gly = ESCAPE_FONTBOLD;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '2':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'I':
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore gly = ESCAPE_FONTITALIC;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'P':
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore gly = ESCAPE_FONTPREV;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '1':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 'R':
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore gly = ESCAPE_FONTROMAN;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case ESCAPE_SPECIAL:
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore if (1 == *sz && 'c' == **start)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore gly = ESCAPE_NOSPACE;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov /*
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * Unicode escapes are defined in groff as \[u0000]
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * to \[u10FFFF], where the contained value must be
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * a valid Unicode codepoint. Here, however, only
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * check the length and range.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (**start != 'u' || *sz < 5 || *sz > 7)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (*sz == 7 && ((*start)[1] != '1' || (*start)[2] != '0'))
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (*sz == 6 && (*start)[1] == '0')
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov break;
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov if (*sz == 5 && (*start)[1] == 'D' &&
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov strchr("89ABCDEF", (*start)[2]) != NULL)
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if ((int)strspn(*start + 1, "0123456789ABCDEFabcdef")
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov + 1 == *sz)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov gly = ESCAPE_UNICODE;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore default:
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return gly;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore/*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Parse a quoted or unquoted roff-style request or macro argument.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Return a pointer to the parsed argument, which is either the original
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * pointer or advanced by one byte in case the argument is quoted.
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * NUL-terminate the argument in place.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Collapse pairs of quotes inside quoted arguments.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Advance the argument pointer to the next argument,
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * or to the NUL byte terminating the argument line.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorechar *
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoremandoc_getarg(struct mparse *parse, char **cpp, int ln, int *pos)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore char *start, *cp;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore int quoted, pairs, white;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Quoting can only start with a new word. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore start = *cpp;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore quoted = 0;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if ('"' == *start) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore quoted = 1;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore start++;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore pairs = 0;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore white = 0;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore for (cp = start; '\0' != *cp; cp++) {
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore /*
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * Move the following text left
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * after quoted quotes and after "\\" and "\t".
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (pairs)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore cp[-pairs] = cp[0];
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if ('\\' == cp[0]) {
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore /*
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * In copy mode, translate double to single
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * backslashes and backslash-t to literal tabs.
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore */
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore switch (cp[1]) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case 't':
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore cp[0] = '\t';
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore /* FALLTHROUGH */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '\\':
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore pairs++;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore cp++;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case ' ':
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Skip escaped blanks. */
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore if (0 == quoted)
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore cp++;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore break;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore default:
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore break;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore } else if (0 == quoted) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (' ' == cp[0]) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Unescaped blanks end unquoted args. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore white = 1;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore } else if ('"' == cp[0]) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if ('"' == cp[1]) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Quoted quotes collapse. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore pairs++;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore cp++;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore } else {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Unquoted quotes end quoted args. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore quoted = 2;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Quoted argument without a closing quote. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (1 == quoted)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov mandoc_msg(MANDOCERR_ARG_QUOTE, parse, ln, *pos, NULL);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore /* NUL-terminate this argument and move to the next one. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (pairs)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore cp[-pairs] = '\0';
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if ('\0' != *cp) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore *cp++ = '\0';
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore while (' ' == *cp)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore cp++;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore *pos += (int)(cp - start) + (quoted ? 1 : 0);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore *cpp = cp;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if ('\0' == *cp && (white || ' ' == cp[-1]))
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov mandoc_msg(MANDOCERR_SPACE_EOL, parse, ln, *pos, NULL);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return start;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic int
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorea2time(time_t *t, const char *fmt, const char *p)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore struct tm tm;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore char *pp;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore memset(&tm, 0, sizeof(struct tm));
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore pp = NULL;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov#if HAVE_STRPTIME
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore pp = strptime(p, fmt, &tm);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#endif
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (NULL != pp && '\0' == *pp) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore *t = mktime(&tm);
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return 1;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return 0;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic char *
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoretime2a(time_t t)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore struct tm *tm;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore char *buf, *p;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore size_t ssz;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore int isz;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore tm = localtime(&t);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (tm == NULL)
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return NULL;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Reserve space:
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * up to 9 characters for the month (September) + blank
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * up to 2 characters for the day + comma + blank
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * 4 characters for the year and a terminating '\0'
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p = buf = mandoc_malloc(10 + 4 + 4 + 1);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov if ((ssz = strftime(p, 10 + 1, "%B ", tm)) == 0)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore goto fail;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p += (int)ssz;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov /*
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov * The output format is just "%d" here, not "%2d" or "%02d".
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov * That's also the reason why we can't just format the
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov * date as a whole with "%B %e, %Y" or "%B %d, %Y".
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov * Besides, the present approach is less prone to buffer
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov * overflows, in case anybody should ever introduce the bug
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov * of looking at LC_TIME.
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov */
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov if ((isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday)) == -1)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore goto fail;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p += isz;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov if (strftime(p, 4 + 1, "%Y", tm) == 0)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore goto fail;
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return buf;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorefail:
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore free(buf);
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return NULL;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorechar *
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoremandoc_normdate(struct mparse *parse, char *in, int ln, int pos)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore time_t t;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov /* No date specified: use today's date. */
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov if (in == NULL || *in == '\0' || strcmp(in, "$" "Mdocdate$") == 0) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov mandoc_msg(MANDOCERR_DATE_MISSING, parse, ln, pos, NULL);
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return time2a(time(NULL));
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov /* Valid mdoc(7) date format. */
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov if (a2time(&t, "$" "Mdocdate: %b %d %Y $", in) ||
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov a2time(&t, "%b %d, %Y", in))
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return time2a(t);
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov /* Do not warn about the legacy man(7) format. */
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov if ( ! a2time(&t, "%Y-%m-%d", in))
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov mandoc_msg(MANDOCERR_DATE_BAD, parse, ln, pos, in);
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov /* Use any non-mdoc(7) date verbatim. */
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return mandoc_strdup(in);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoreint
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovmandoc_eos(const char *p, size_t sz)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov const char *q;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov int enclosed, found;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (0 == sz)
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return 0;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * End-of-sentence recognition must include situations where
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * some symbols, such as `)', allow prior EOS punctuation to
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * propagate outward.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov enclosed = found = 0;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore for (q = p + (int)sz - 1; q >= p; q--) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore switch (*q) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '\"':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '\'':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case ']':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case ')':
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (0 == found)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore enclosed = 1;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '.':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '!':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '?':
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore found = 1;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore default:
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return found &&
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov (!enclosed || isalnum((unsigned char)*q));
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return found && !enclosed;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore/*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Convert a string to a long that may not be <0.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * If the string is invalid, or is less than 0, return -1.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoreint
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoremandoc_strntoi(const char *p, size_t sz, int base)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore char buf[32];
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore char *ep;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore long v;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (sz > 31)
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return -1;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore memcpy(buf, p, sz);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore buf[(int)sz] = '\0';
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore errno = 0;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore v = strtol(buf, &ep, base);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (buf[0] == '\0' || *ep != '\0')
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return -1;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (v > INT_MAX)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore v = INT_MAX;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (v < INT_MIN)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore v = INT_MIN;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return (int)v;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}