]>
git.cameronkatri.com Git - mandoc.git/blob - main.c
1 /* $Id: main.c,v 1.8 2009/03/22 19:01:11 kristaps Exp $ */
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
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.
32 extern int getsubopt(char **, char * const *, char **);
34 # define __dead __attribute__((__noreturn__))
46 #define WARN_WALL 0x03 /* All-warnings mask. */
47 #define WARN_WCOMPAT (1 << 0) /* Compatibility warnings. */
48 #define WARN_WSYNTAX (1 << 1) /* Syntax warnings. */
49 #define WARN_WERR (1 << 2) /* Warnings->errors. */
60 typedef int (*out_run
)(void *, const struct mdoc
*);
61 typedef void (*out_free
)(void *);
63 extern char *__progname
;
65 extern void *ascii_alloc(void);
66 extern void *latin1_alloc(void);
67 extern void *utf8_alloc(void);
68 extern int terminal_run(void *, const struct mdoc
*);
69 extern int tree_run(void *, const struct mdoc
*);
70 extern void terminal_free(void *);
72 __dead
static void version(void);
73 __dead
static void usage(void);
74 static int foptions(int *, char *);
75 static int toptions(enum outt
*, char *);
76 static int woptions(int *, char *);
77 static int merr(void *, int, int, const char *);
78 static int mwarn(void *, int, int,
79 enum mdoc_warn
, const char *);
80 static int file(struct buf
*, struct buf
*,
81 const char *, struct mdoc
*);
82 static int fdesc(struct buf
*, struct buf
*,
83 const char *, int, struct mdoc
*);
87 main(int argc
, char *argv
[])
100 outtype
= OUTT_ASCII
;
102 bzero(&curp
, sizeof(struct curparse
));
105 while (-1 != (c
= getopt(argc
, argv
, "f:VW:T:")))
108 if ( ! foptions(&fflags
, optarg
))
112 if ( ! toptions(&outtype
, optarg
))
116 if ( ! woptions(&curp
.wflags
, optarg
))
131 * Allocate the appropriate front-end. Note that utf8, ascii
132 * and latin1 all resolve to the terminal front-end with
133 * different encodings (see terminal.c). Not all frontends have
134 * cleanup or alloc routines.
139 outdata
= latin1_alloc();
140 outrun
= terminal_run
;
141 outfree
= terminal_free
;
144 outdata
= utf8_alloc();
145 outrun
= terminal_run
;
146 outfree
= terminal_free
;
159 outdata
= ascii_alloc();
160 outrun
= terminal_run
;
161 outfree
= terminal_free
;
166 * All callbacks route into here, where we print them onto the
167 * screen. XXX - for now, no path for debugging messages.
172 cb
.mdoc_warn
= mwarn
;
174 bzero(&ln
, sizeof(struct buf
));
175 bzero(&blk
, sizeof(struct buf
));
177 mdoc
= mdoc_alloc(&curp
, fflags
, &cb
);
180 * Loop around available files.
184 curp
.file
= "<stdin>";
185 c
= fdesc(&blk
, &ln
, "stdin", STDIN_FILENO
, mdoc
);
187 if (c
&& NULL
== outrun
)
189 else if (c
&& outrun
&& (*outrun
)(outdata
, mdoc
))
194 c
= file(&blk
, &ln
, *argv
, mdoc
);
197 if (outrun
&& ! (*outrun
)(outdata
, mdoc
))
199 /* Reset the parser for another file. */
215 return(rc
? EXIT_SUCCESS
: EXIT_FAILURE
);
223 (void)printf("%s %s\n", __progname
, VERSION
);
233 (void)fprintf(stderr
, "usage: %s\n", __progname
);
240 file(struct buf
*blk
, struct buf
*ln
,
241 const char *file
, struct mdoc
*mdoc
)
245 if (-1 == (fd
= open(file
, O_RDONLY
, 0))) {
250 c
= fdesc(blk
, ln
, file
, fd
, mdoc
);
260 fdesc(struct buf
*blk
, struct buf
*ln
,
261 const char *f
, int fd
, struct mdoc
*mdoc
)
268 int macro
, xo
, xeoln
;
272 * Two buffers: ln and buf. buf is the input buffer, optimised
273 * for each file's block size. ln is a line buffer. Both
274 * growable, hence passed in by ptr-ptr.
279 if (-1 == fstat(fd
, &st
))
281 else if ((size_t)st
.st_blksize
> sz
)
285 blk
->buf
= realloc(blk
->buf
, sz
);
286 if (NULL
== blk
->buf
)
292 * Fill buf with file blocksize and parse newlines into ln.
295 macro
= xo
= xeoln
= 0;
298 for (lnn
= 1, pos
= 0; ; ) {
299 if (-1 == (ssz
= read(fd
, blk
->buf
, sz
))) {
305 for (i
= 0; i
< (int)ssz
; i
++) {
306 if (pos
>= (int)ln
->sz
) {
307 ln
->sz
+= 256; /* Step-size. */
308 ln
->buf
= realloc(ln
->buf
, ln
->sz
);
313 if ('\n' != blk
->buf
[i
]) {
315 * Ugly of uglies. Here we handle the
316 * dreaded `Xo/Xc' scoping. Cover the
317 * eyes of any nearby children. This
318 * makes `Xo/Xc' enclosures look like
323 * First, note whether we're in a macro
326 if (0 == pos
&& '.' == blk
->buf
[i
])
330 * If we're in an `Xo' context and just
331 * nixed a newline, remove the control
332 * character for new macro lines:
333 * they're going to show up as all part
336 if (xo
&& xeoln
&& '.' == blk
->buf
[i
]) {
343 * If we've parsed `Xo', enter an xo
344 * context. `Xo' must be in a parsable
345 * state. This is the ugly part. IT IS
346 * NOT SMART ENOUGH TO HANDLE ESCAPED
349 if (macro
&& pos
&& 'o' == blk
->buf
[i
]) {
350 if (xo
&& 'X' == ln
->buf
[pos
- 1]) {
351 if (' ' == ln
->buf
[pos
- 2])
353 } else if ('X' == ln
->buf
[pos
- 1]) {
354 if (2 == pos
&& '.' == ln
->buf
[pos
- 2])
356 else if (' ' == ln
->buf
[pos
- 2])
362 * If we're parsed `Xc', leave an xo
363 * context if one's already pending.
364 * `Xc' must be in a parsable state.
365 * THIS IS NOT SMART ENOUGH TO HANDLE
366 * ESCAPED WHITESPACE.
368 if (macro
&& pos
&& 'c' == blk
->buf
[i
])
369 if (xo
&& 'X' == ln
->buf
[pos
- 1])
370 if (' ' == ln
->buf
[pos
- 2])
372 #endif /* STRIP_XO */
374 ln
->buf
[pos
++] = blk
->buf
[i
];
378 /* Check for CPP-escaped newline. */
380 if (pos
> 0 && '\\' == ln
->buf
[pos
- 1]) {
381 for (j
= pos
- 1; j
>= 0; j
--)
382 if ('\\' != ln
->buf
[j
])
385 if ( ! ((pos
- j
) % 2)) {
394 * If we're in an xo context, put a space in
395 * place of the newline and continue parsing.
396 * Mark that we just did a newline.
400 ln
->buf
[pos
++] = ' ';
404 #endif /* STRIP_XO */
407 if ( ! mdoc_parseln(mdoc
, lnn
, ln
->buf
))
414 return(mdoc_endparse(mdoc
));
419 toptions(enum outt
*tflags
, char *arg
)
422 if (0 == strcmp(arg
, "ascii"))
423 *tflags
= OUTT_ASCII
;
424 else if (0 == strcmp(arg
, "latin1"))
425 *tflags
= OUTT_LATIN1
;
426 else if (0 == strcmp(arg
, "utf8"))
428 else if (0 == strcmp(arg
, "lint"))
430 else if (0 == strcmp(arg
, "tree"))
433 warnx("bad argument: -T%s", arg
);
442 * Parse out the options for [-fopt...] setting compiler options. These
443 * can be comma-delimited or called again.
446 foptions(int *fflags
, char *arg
)
451 toks
[0] = "ign-scope";
452 toks
[1] = "ign-escape";
453 toks
[2] = "ign-macro";
457 switch (getsubopt(&arg
, toks
, &v
)) {
459 *fflags
|= MDOC_IGN_SCOPE
;
462 *fflags
|= MDOC_IGN_ESCAPE
;
465 *fflags
|= MDOC_IGN_MACRO
;
468 warnx("bad argument: -f%s", arg
);
477 * Parse out the options for [-Werr...], which sets warning modes.
478 * These can be comma-delimited or called again.
481 woptions(int *wflags
, char *arg
)
493 switch (getsubopt(&arg
, toks
, &v
)) {
495 *wflags
|= WARN_WALL
;
498 *wflags
|= WARN_WCOMPAT
;
501 *wflags
|= WARN_WSYNTAX
;
504 *wflags
|= WARN_WERR
;
507 warnx("bad argument: -W%s", arg
);
517 merr(void *arg
, int line
, int col
, const char *msg
)
519 struct curparse
*curp
;
521 curp
= (struct curparse
*)arg
;
523 warnx("%s:%d: error: %s (column %d)",
524 curp
->file
, line
, msg
, col
);
530 mwarn(void *arg
, int line
, int col
,
531 enum mdoc_warn type
, const char *msg
)
533 struct curparse
*curp
;
536 curp
= (struct curparse
*)arg
;
542 if (curp
->wflags
& WARN_WCOMPAT
)
547 if (curp
->wflags
& WARN_WSYNTAX
)
553 warnx("%s:%d: %s warning: %s (column %d)",
554 curp
->file
, line
, wtype
, msg
, col
);
556 if ( ! (curp
->wflags
& WARN_WERR
))
559 warnx("%s: considering warnings as errors",