]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.49 2008/12/07 16:41:04 kristaps Exp $ */
3 * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/param.h>
20 #include <sys/types.h>
31 #include "libmdocml.h"
35 /* FIXME: First letters of quoted-text interpreted in rofffindtok. */
36 /* FIXME: `No' not implemented. */
37 /* TODO: warn if Pp occurs before/after Sh etc. (see mdoc.samples). */
38 /* TODO: warn about "X section only" macros. */
39 /* TODO: warn about empty lists. */
40 /* TODO: (warn) some sections need specific elements. */
41 /* TODO: (warn) NAME section has particular order. */
42 /* TODO: unify empty-content tags a la <br />. */
43 /* TODO: macros with a set number of arguments? */
44 /* TODO: validate Dt macro arguments. */
45 /* FIXME: Bl -diag supposed to ignore callable children. */
46 /* FIXME: Nm has newline when used in NAME section. */
49 int tok
; /* Token id. */
50 struct roffnode
*parent
; /* Parent (or NULL). */
54 struct roffnode
*last
; /* Last parsed node. */
55 char *cur
; /* Line start. */
56 struct tm tm
; /* `Dd' results. */
57 char name
[64]; /* `Nm' results. */
58 char os
[64]; /* `Os' results. */
59 char title
[64]; /* `Dt' results. */
60 char section
[64]; /* `Dt' results. */
61 char volume
[64]; /* `Dt' results. */
63 #define ROFF_PRELUDE (1 << 1) /* In roff prelude. */
64 #define ROFF_PRELUDE_Os (1 << 2) /* `Os' is parsed. */
65 #define ROFF_PRELUDE_Dt (1 << 3) /* `Dt' is parsed. */
66 #define ROFF_PRELUDE_Dd (1 << 4) /* `Dd' is parsed. */
67 #define ROFF_BODY (1 << 5) /* In roff body. */
68 struct roffcb cb
; /* Callbacks. */
69 void *arg
; /* Callbacks' arg. */
72 static struct roffnode
*roffnode_new(int, struct rofftree
*);
73 static void roffnode_free(struct rofftree
*);
74 static void roff_warn(const struct rofftree
*,
75 const char *, char *, ...);
76 static void roff_err(const struct rofftree
*,
77 const char *, char *, ...);
78 static int roffpurgepunct(struct rofftree
*, char **);
79 static int roffscan(int, const int *);
80 static int rofffindtok(const char *);
81 static int rofffindarg(const char *);
82 static int rofffindcallable(const char *);
83 static int roffargs(const struct rofftree
*,
84 int, char *, char **);
85 static int roffargok(int, int);
86 static int roffnextopt(const struct rofftree
*,
87 int, char ***, char **);
88 static int roffparseopts(struct rofftree
*, int,
89 char ***, int *, char **);
90 static int roffcall(struct rofftree
*, int, char **);
91 static int roffparse(struct rofftree
*, char *);
92 static int textparse(struct rofftree
*, char *);
93 static int roffdata(struct rofftree
*, int, char *);
94 static int roffspecial(struct rofftree
*, int,
95 const char *, const int *,
96 const char **, size_t, char **);
97 static int roffsetname(struct rofftree
*, char **);
100 extern size_t strlcat(char *, const char *, size_t);
101 extern size_t strlcpy(char *, const char *, size_t);
102 extern int vsnprintf(char *, size_t,
103 const char *, va_list);
104 extern char *strptime(const char *, const char *,
109 roff_free(struct rofftree
*tree
, int flush
)
121 if (ROFF_PRELUDE
& tree
->state
) {
122 roff_err(tree
, NULL
, "prelude never finished");
126 for (n
= tree
->last
; n
; n
= n
->parent
) {
127 if (0 != tokens
[n
->tok
].ctx
)
129 roff_err(tree
, NULL
, "closing explicit scope `%s'",
136 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
140 if ( ! (*tree
->cb
.rofftail
)(tree
->arg
))
152 return(error
? 0 : 1);
157 roff_alloc(const struct roffcb
*cb
, void *args
)
159 struct rofftree
*tree
;
164 if (NULL
== (tree
= calloc(1, sizeof(struct rofftree
))))
167 tree
->state
= ROFF_PRELUDE
;
170 (void)memcpy(&tree
->cb
, cb
, sizeof(struct roffcb
));
177 roff_engine(struct rofftree
*tree
, char *buf
)
184 roff_err(tree
, buf
, "blank line");
186 } else if ('.' != *buf
)
187 return(textparse(tree
, buf
));
189 return(roffparse(tree
, buf
));
194 textparse(struct rofftree
*tree
, char *buf
)
198 /* TODO: literal parsing. */
200 if ( ! (ROFF_BODY
& tree
->state
)) {
201 roff_err(tree
, buf
, "data not in body");
207 while (*buf
&& isspace(*buf
))
215 while (*buf
&& ! isspace(*buf
))
220 if ( ! roffdata(tree
, 1, bufp
))
225 if ( ! roffdata(tree
, 1, bufp
))
235 roffargs(const struct rofftree
*tree
,
236 int tok
, char *buf
, char **argv
)
241 assert(tok
>= 0 && tok
< ROFF_MAX
);
247 * This is an ugly little loop. It parses a line into
248 * space-delimited tokens. If a quote mark is encountered, a
249 * token is alloted the entire quoted text. If whitespace is
250 * escaped, it's included in the prior alloted token.
254 for (i
= 0; *buf
&& i
< ROFF_MAXLINEARG
; i
++) {
257 while (*buf
&& '\"' != *buf
)
260 roff_err(tree
, argv
[i
], "unclosed "
269 if ( ! isspace(*buf
)) {
273 if (*(buf
- 1) == '\\') {
283 while (*buf
&& isspace(*buf
))
288 if (ROFF_MAXLINEARG
== i
&& *buf
) {
289 roff_err(tree
, p
, "too many arguments for `%s'", toknames
300 roffscan(int tok
, const int *tokv
)
306 for ( ; ROFF_MAX
!= *tokv
; tokv
++)
315 roffparse(struct rofftree
*tree
, char *buf
)
319 char *argv
[ROFF_MAXLINEARG
];
322 if (0 != *buf
&& 0 != *(buf
+ 1) && 0 != *(buf
+ 2))
323 if (0 == strncmp(buf
, ".\\\"", 3))
326 if (ROFF_MAX
== (tok
= rofffindtok(buf
+ 1))) {
327 roff_err(tree
, buf
+ 1, "bogus line macro");
329 } else if (NULL
== tokens
[tok
].cb
) {
330 roff_err(tree
, buf
+ 1, "unsupported macro `%s'",
335 assert(ROFF___
!= tok
);
336 if ( ! roffargs(tree
, tok
, buf
, argv
))
339 argvp
= (char **)argv
;
342 * Prelude macros break some assumptions, so branch now.
345 if (ROFF_PRELUDE
& tree
->state
) {
346 assert(NULL
== tree
->last
);
347 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
350 assert(ROFF_BODY
& tree
->state
);
353 * First check that our possible parents and parent's possible
354 * children are satisfied.
357 if (tree
->last
&& ! roffscan
358 (tree
->last
->tok
, tokens
[tok
].parents
)) {
359 roff_err(tree
, *argvp
, "`%s' has invalid parent `%s'",
361 toknames
[tree
->last
->tok
]);
365 if (tree
->last
&& ! roffscan
366 (tok
, tokens
[tree
->last
->tok
].children
)) {
367 roff_err(tree
, *argvp
, "`%s' is invalid child of `%s'",
369 toknames
[tree
->last
->tok
]);
374 * Branch if we're not a layout token.
377 if (ROFF_LAYOUT
!= tokens
[tok
].type
)
378 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
379 if (0 == tokens
[tok
].ctx
)
380 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
383 * First consider implicit-end tags, like as follows:
386 * In this, we want to close the scope of the NAME section. If
387 * there's an intermediary implicit-end tag, such as
391 * then it must be closed as well.
394 if (tok
== tokens
[tok
].ctx
) {
396 * First search up to the point where we must close.
397 * If one doesn't exist, then we can open a new scope.
400 for (n
= tree
->last
; n
; n
= n
->parent
) {
401 assert(0 == tokens
[n
->tok
].ctx
||
402 n
->tok
== tokens
[n
->tok
].ctx
);
405 if (ROFF_SHALLOW
& tokens
[tok
].flags
) {
409 if (tokens
[n
->tok
].ctx
== n
->tok
)
411 roff_err(tree
, *argv
, "`%s' breaks `%s' scope",
412 toknames
[tok
], toknames
[n
->tok
]);
417 * Create a new scope, as no previous one exists to
422 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
425 * Close out all intermediary scoped blocks, then hang
426 * the current scope from our predecessor's parent.
431 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
435 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
439 * Now consider explicit-end tags, where we want to close back
440 * to a specific tag. Example:
444 * In this, the `El' tag closes out the scope of `Bl'.
447 assert(tok
!= tokens
[tok
].ctx
&& 0 != tokens
[tok
].ctx
);
450 for (n
= tree
->last
; n
; n
= n
->parent
)
451 if (n
->tok
!= tokens
[tok
].ctx
) {
452 if (n
->tok
== tokens
[n
->tok
].ctx
)
454 roff_err(tree
, *argv
, "`%s' breaks `%s' scope",
455 toknames
[tok
], toknames
[n
->tok
]);
462 roff_err(tree
, *argv
, "`%s' has no starting tag `%s'",
464 toknames
[tokens
[tok
].ctx
]);
471 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
473 } while (t
!= tokens
[tok
].ctx
);
480 rofffindarg(const char *name
)
484 /* FIXME: use a table, this is slow but ok for now. */
487 for (i
= 0; i
< ROFF_ARGMAX
; i
++)
489 if (0 == strcmp(name
, tokargnames
[i
]))
497 rofffindtok(const char *buf
)
502 for (i
= 0; *buf
&& ! isspace(*buf
) && i
< 3; i
++, buf
++)
510 /* FIXME: use a table, this is slow but ok for now. */
513 for (i
= 0; i
< ROFF_MAX
; i
++)
515 if (0 == strcmp(toknames
[i
], token
))
523 roffispunct(const char *p
)
565 rofffindcallable(const char *name
)
569 if (ROFF_MAX
== (c
= rofffindtok(name
)))
571 assert(c
>= 0 && c
< ROFF_MAX
);
572 return(ROFF_CALLABLE
& tokens
[c
].flags
? c
: ROFF_MAX
);
576 static struct roffnode
*
577 roffnode_new(int tokid
, struct rofftree
*tree
)
581 if (NULL
== (p
= malloc(sizeof(struct roffnode
))))
585 p
->parent
= tree
->last
;
593 roffargok(int tokid
, int argid
)
597 if (NULL
== (c
= tokens
[tokid
].args
))
600 for ( ; ROFF_ARGMAX
!= *c
; c
++)
609 roffnode_free(struct rofftree
*tree
)
616 tree
->last
= tree
->last
->parent
;
622 roffspecial(struct rofftree
*tree
, int tok
, const char *start
,
623 const int *argc
, const char **argv
,
624 size_t sz
, char **ordp
)
631 if (0 == strcmp(*ordp
, "v6"))
633 else if (0 == strcmp(*ordp
, "v7"))
635 else if (0 == strcmp(*ordp
, "32v"))
637 else if (0 == strcmp(*ordp
, "V.1"))
639 else if (0 == strcmp(*ordp
, "V.4"))
641 roff_err(tree
, start
, "invalid `At' arg");
647 roff_err(tree
, start
, "`%s' expects at least "
648 "one arg", toknames
[tok
]);
653 if (0 == tree
->name
[0]) {
654 roff_err(tree
, start
, "`Nm' not set");
657 ordp
[0] = tree
->name
;
659 } else if ( ! roffsetname(tree
, ordp
))
670 roff_err(tree
, start
, "`%s' expects one arg",
676 roff_err(tree
, start
, "`Sm' expects one arg");
680 if (0 != strcmp(ordp
[0], "on") &&
681 0 != strcmp(ordp
[0], "off")) {
682 roff_err(tree
, start
, "`Sm' has invalid argument");
693 roff_err(tree
, start
, "`%s' expects no args",
702 return((*tree
->cb
.roffspecial
)(tree
->arg
, tok
,
703 tree
->cur
, argc
, argv
, ordp
));
708 roffcall(struct rofftree
*tree
, int tok
, char **argv
)
711 if (NULL
== tokens
[tok
].cb
) {
712 roff_err(tree
, *argv
, "unsupported macro `%s'",
716 if ( ! (*tokens
[tok
].cb
)(tok
, tree
, argv
, ROFF_ENTER
))
723 roffnextopt(const struct rofftree
*tree
, int tok
,
724 char ***in
, char **val
)
733 if (NULL
== (arg
= *argv
))
738 if (ROFF_ARGMAX
== (v
= rofffindarg(arg
+ 1))) {
739 roff_warn(tree
, arg
, "argument-like parameter `%s' to "
740 "`%s'", arg
, toknames
[tok
]);
744 if ( ! roffargok(tok
, v
)) {
745 roff_warn(tree
, arg
, "invalid argument parameter `%s' to "
746 "`%s'", tokargnames
[v
], toknames
[tok
]);
750 if ( ! (ROFF_VALUE
& tokenargs
[v
]))
756 roff_err(tree
, arg
, "empty value of `%s' for `%s'",
757 tokargnames
[v
], toknames
[tok
]);
766 roffpurgepunct(struct rofftree
*tree
, char **argv
)
774 if ( ! roffispunct(argv
[--i
]))
776 while (i
>= 0 && roffispunct(argv
[i
]))
782 if ( ! roffdata(tree
, 0, argv
[i
++]))
789 roffparseopts(struct rofftree
*tree
, int tok
,
790 char ***args
, int *argc
, char **argv
)
797 while (-1 != (c
= roffnextopt(tree
, tok
, args
, &v
))) {
798 if (ROFF_ARGMAX
== c
)
807 argc
[i
] = ROFF_ARGMAX
;
814 roffdata(struct rofftree
*tree
, int space
, char *buf
)
819 return((*tree
->cb
.roffdata
)(tree
->arg
,
820 space
!= 0, tree
->cur
, buf
));
826 roff_Dd(ROFFCALL_ARGS
)
831 if (ROFF_BODY
& tree
->state
) {
832 assert( ! (ROFF_PRELUDE
& tree
->state
));
833 assert(ROFF_PRELUDE_Dd
& tree
->state
);
834 return(roff_text(tok
, tree
, argv
, type
));
837 assert(ROFF_PRELUDE
& tree
->state
);
838 assert( ! (ROFF_BODY
& tree
->state
));
840 if (ROFF_PRELUDE_Dd
& tree
->state
) {
841 roff_err(tree
, *argv
, "repeated `Dd' in prelude");
843 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
844 roff_err(tree
, *argv
, "out-of-order `Dd' in prelude");
848 assert(NULL
== tree
->last
);
852 if (0 == strcmp(*argv
, "$Mdocdate: December 7 2008 $")) {
854 if (NULL
== localtime_r(&t
, &tree
->tm
))
855 err(1, "localtime_r");
856 tree
->state
|= ROFF_PRELUDE_Dd
;
860 /* Build this from Mdocdate or raw date. */
865 if (0 != strcmp(*argv
, "$Mdocdate:")) {
867 if (strlcat(buf
, *argv
++, sizeof(buf
))
870 roff_err(tree
, p
, "bad `Dd' date");
873 if (strptime(buf
, "%b%d,%Y", &tree
->tm
)) {
874 tree
->state
|= ROFF_PRELUDE_Dd
;
877 roff_err(tree
, *argv
, "bad `Dd' date");
882 while (*argv
&& **argv
!= '$') {
883 if (strlcat(buf
, *argv
++, sizeof(buf
))
885 roff_err(tree
, p
, "bad `Dd' Mdocdate");
888 if (strlcat(buf
, " ", sizeof(buf
))
890 roff_err(tree
, p
, "bad `Dd' Mdocdate");
895 roff_err(tree
, p
, "bad `Dd' Mdocdate");
899 if (NULL
== strptime(buf
, "%b %d %Y", &tree
->tm
)) {
900 roff_err(tree
, *argv
, "bad `Dd' Mdocdate");
904 tree
->state
|= ROFF_PRELUDE_Dd
;
911 roff_Dt(ROFFCALL_ARGS
)
914 if (ROFF_BODY
& tree
->state
) {
915 assert( ! (ROFF_PRELUDE
& tree
->state
));
916 assert(ROFF_PRELUDE_Dt
& tree
->state
);
917 return(roff_text(tok
, tree
, argv
, type
));
920 assert(ROFF_PRELUDE
& tree
->state
);
921 assert( ! (ROFF_BODY
& tree
->state
));
923 if ( ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
924 roff_err(tree
, *argv
, "out-of-order `Dt' in prelude");
926 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
927 roff_err(tree
, *argv
, "repeated `Dt' in prelude");
933 roff_err(tree
, *argv
, "`Dt' needs document title");
935 } else if (strlcpy(tree
->title
, *argv
, sizeof(tree
->title
))
936 >= sizeof(tree
->title
)) {
937 roff_err(tree
, *argv
, "`Dt' document title too long");
943 roff_err(tree
, *argv
, "`Dt' needs section");
945 } else if (strlcpy(tree
->section
, *argv
, sizeof(tree
->section
))
946 >= sizeof(tree
->section
)) {
947 roff_err(tree
, *argv
, "`Dt' section too long");
954 } else if (strlcpy(tree
->volume
, *argv
, sizeof(tree
->volume
))
955 >= sizeof(tree
->volume
)) {
956 roff_err(tree
, *argv
, "`Dt' volume too long");
960 assert(NULL
== tree
->last
);
961 tree
->state
|= ROFF_PRELUDE_Dt
;
968 roffsetname(struct rofftree
*tree
, char **ordp
)
973 /* FIXME: not all sections can set this. */
975 if (NULL
!= *(ordp
+ 1)) {
976 roff_err(tree
, *ordp
, "too many `Nm' args");
980 if (strlcpy(tree
->name
, *ordp
, sizeof(tree
->name
))
981 >= sizeof(tree
->name
)) {
982 roff_err(tree
, *ordp
, "`Nm' arg too long");
992 roff_Ns(ROFFCALL_ARGS
)
997 first
= (*argv
++ == tree
->cur
);
1000 if ( ! roffspecial(tree
, tok
, *argv
, NULL
, NULL
, 0, morep
))
1004 if (ROFF_MAX
!= (c
= rofffindcallable(*argv
))) {
1005 if ( ! roffcall(tree
, c
, argv
))
1010 if ( ! roffispunct(*argv
)) {
1011 if ( ! roffdata(tree
, 1, *argv
++))
1016 for (j
= 0; argv
[j
]; j
++)
1017 if ( ! roffispunct(argv
[j
]))
1021 if ( ! roffdata(tree
, 0, *argv
++))
1032 return(roffpurgepunct(tree
, argv
));
1038 roff_Os(ROFFCALL_ARGS
)
1042 if (ROFF_BODY
& tree
->state
) {
1043 assert( ! (ROFF_PRELUDE
& tree
->state
));
1044 assert(ROFF_PRELUDE_Os
& tree
->state
);
1045 return(roff_text(tok
, tree
, argv
, type
));
1048 assert(ROFF_PRELUDE
& tree
->state
);
1049 if ( ! (ROFF_PRELUDE_Dt
& tree
->state
) ||
1050 ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
1051 roff_err(tree
, *argv
, "out-of-order `Os' in prelude");
1060 if (strlcat(tree
->os
, *argv
++, sizeof(tree
->os
))
1063 roff_err(tree
, p
, "`Os' value too long");
1067 if (0 == tree
->os
[0])
1068 if (strlcpy(tree
->os
, "LOCAL", sizeof(tree
->os
))
1069 >= sizeof(tree
->os
)) {
1070 roff_err(tree
, p
, "`Os' value too long");
1074 tree
->state
|= ROFF_PRELUDE_Os
;
1075 tree
->state
&= ~ROFF_PRELUDE
;
1076 tree
->state
|= ROFF_BODY
;
1078 assert(NULL
== tree
->last
);
1080 return((*tree
->cb
.roffhead
)(tree
->arg
, &tree
->tm
,
1081 tree
->os
, tree
->title
, tree
->section
,
1088 roff_layout(ROFFCALL_ARGS
)
1090 int i
, c
, argcp
[ROFF_MAXLINEARG
];
1091 char *argvp
[ROFF_MAXLINEARG
];
1093 if (ROFF_PRELUDE
& tree
->state
) {
1094 roff_err(tree
, *argv
, "bad `%s' in prelude",
1097 } else if (ROFF_EXIT
== type
) {
1098 roffnode_free(tree
);
1099 if ( ! (*tree
->cb
.roffblkbodyout
)(tree
->arg
, tok
))
1101 return((*tree
->cb
.roffblkout
)(tree
->arg
, tok
));
1104 assert( ! (ROFF_CALLABLE
& tokens
[tok
].flags
));
1108 if ( ! roffparseopts(tree
, tok
, &argv
, argcp
, argvp
))
1110 if (NULL
== roffnode_new(tok
, tree
))
1114 * Layouts have two parts: the layout body and header. The
1115 * layout header is the trailing text of the line macro, while
1116 * the layout body is everything following until termination.
1119 if ( ! (*tree
->cb
.roffblkin
)(tree
->arg
, tok
, argcp
, argvp
))
1122 return((*tree
->cb
.roffblkbodyin
)
1123 (tree
->arg
, tok
, argcp
, argvp
));
1125 if ( ! (*tree
->cb
.roffblkheadin
)(tree
->arg
, tok
, argcp
, argvp
))
1129 * If there are no parsable parts, then write remaining tokens
1130 * into the layout header and exit.
1133 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1136 if ( ! roffdata(tree
, i
++, *argv
++))
1139 if ( ! (*tree
->cb
.roffblkheadout
)(tree
->arg
, tok
))
1141 return((*tree
->cb
.roffblkbodyin
)
1142 (tree
->arg
, tok
, argcp
, argvp
));
1146 * Parsable elements may be in the header (or be the header, for
1147 * that matter). Follow the regular parsing rules for these.
1152 if (ROFF_MAX
== (c
= rofffindcallable(*argv
))) {
1154 if ( ! roffdata(tree
, i
++, *argv
++))
1158 if ( ! roffcall(tree
, c
, argv
))
1164 * If there's trailing punctuation in the header, then write it
1165 * out now. Here we mimic the behaviour of a line-dominant text
1169 if (NULL
== *argv
) {
1170 if ( ! (*tree
->cb
.roffblkheadout
)(tree
->arg
, tok
))
1172 return((*tree
->cb
.roffblkbodyin
)
1173 (tree
->arg
, tok
, argcp
, argvp
));
1177 * Expensive. Scan to the end of line then work backwards until
1178 * a token isn't punctuation.
1181 if ( ! roffpurgepunct(tree
, argv
))
1184 if ( ! (*tree
->cb
.roffblkheadout
)(tree
->arg
, tok
))
1186 return((*tree
->cb
.roffblkbodyin
)
1187 (tree
->arg
, tok
, argcp
, argvp
));
1193 roff_ordered(ROFFCALL_ARGS
)
1195 int i
, first
, c
, argcp
[ROFF_MAXLINEARG
];
1196 char *ordp
[ROFF_MAXLINEARG
], *p
,
1197 *argvp
[ROFF_MAXLINEARG
];
1199 if (ROFF_PRELUDE
& tree
->state
) {
1200 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
1205 first
= (*argv
== tree
->cur
);
1208 if ( ! roffparseopts(tree
, tok
, &argv
, argcp
, argvp
))
1211 if (NULL
== *argv
) {
1213 return(roffspecial(tree
, tok
, p
, argcp
,
1214 (const char **)argvp
, 0, ordp
));
1218 while (*argv
&& i
< ROFF_MAXLINEARG
) {
1219 c
= ROFF_PARSED
& tokens
[tok
].flags
?
1220 rofffindcallable(*argv
) : ROFF_MAX
;
1222 if (ROFF_MAX
== c
&& ! roffispunct(*argv
)) {
1223 ordp
[i
++] = *argv
++;
1231 if ( ! roffspecial(tree
, tok
, p
, argcp
,
1232 (const char **)argvp
,
1236 return(roffcall(tree
, c
, argv
));
1239 assert(i
!= ROFF_MAXLINEARG
);
1242 if ( ! roffspecial(tree
, tok
, p
, argcp
,
1243 (const char**)argvp
,
1247 /* FIXME: error if there's stuff after the punctuation. */
1249 if ( ! first
|| NULL
== *argv
)
1252 return(roffpurgepunct(tree
, argv
));
1258 roff_text(ROFFCALL_ARGS
)
1260 int i
, j
, first
, c
, argcp
[ROFF_MAXLINEARG
];
1261 char *argvp
[ROFF_MAXLINEARG
];
1263 if (ROFF_PRELUDE
& tree
->state
) {
1264 roff_err(tree
, *argv
, "`%s' disallowed in prelude",
1269 first
= (*argv
== tree
->cur
);
1272 if ( ! roffparseopts(tree
, tok
, &argv
, argcp
, argvp
))
1274 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
, argcp
, argvp
))
1277 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1279 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1282 if ( ! roffdata(tree
, i
++, *argv
++))
1285 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1289 * Deal with punctuation. Ugly. Work ahead until we encounter
1290 * terminating punctuation. If we encounter it and all
1291 * subsequent tokens are punctuation, then stop processing (the
1292 * line-dominant macro will print these tokens after closure).
1293 * If the punctuation is followed by non-punctuation, then close
1294 * and re-open our scope, then continue.
1299 if (ROFF_MAX
!= (c
= rofffindcallable(*argv
))) {
1300 if ( ! (ROFF_LSCOPE
& tokens
[tok
].flags
))
1301 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1304 if ( ! roffcall(tree
, c
, argv
))
1307 if (ROFF_LSCOPE
& tokens
[tok
].flags
)
1308 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1314 if ( ! roffispunct(*argv
)) {
1315 if ( ! roffdata(tree
, i
++, *argv
++))
1321 for (j
= 0; argv
[j
]; j
++)
1322 if ( ! roffispunct(argv
[j
]))
1326 if (ROFF_LSCOPE
& tokens
[tok
].flags
) {
1327 if ( ! roffdata(tree
, 0, *argv
++))
1331 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1333 if ( ! roffdata(tree
, 0, *argv
++))
1335 if ( ! (*tree
->cb
.roffin
)(tree
->arg
, tok
,
1343 if ( ! (*tree
->cb
.roffout
)(tree
->arg
, tok
))
1349 return((*tree
->cb
.roffout
)(tree
->arg
, tok
));
1353 return(roffpurgepunct(tree
, argv
));
1359 roff_noop(ROFFCALL_ARGS
)
1368 roff_depr(ROFFCALL_ARGS
)
1371 roff_err(tree
, *argv
, "`%s' is deprecated", toknames
[tok
]);
1377 roff_warn(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1383 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1386 (*tree
->cb
.roffmsg
)(tree
->arg
,
1387 ROFF_WARN
, tree
->cur
, pos
, buf
);
1392 roff_err(const struct rofftree
*tree
, const char *pos
, char *fmt
, ...)
1398 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1401 (*tree
->cb
.roffmsg
)(tree
->arg
,
1402 ROFF_ERROR
, tree
->cur
, pos
, buf
);