1 /* $Id: demandoc.c,v 1.29 2017/06/24 14:38:32 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #include "config.h" 18 19 #include <sys/types.h> 20 21 #include <assert.h> 22 #include <ctype.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <unistd.h> 27 28 #include "mandoc.h" 29 #include "roff.h" 30 #include "man.h" 31 #include "mdoc.h" 32 33 static void pline(int, int *, int *, int); 34 static void pman(const struct roff_node *, int *, int *, int); 35 static void pmandoc(struct mparse *, int, const char *, int); 36 static void pmdoc(const struct roff_node *, int *, int *, int); 37 static void pstring(const char *, int, int *, int); 38 static void usage(void); 39 40 static const char *progname; 41 42 int 43 main(int argc, char *argv[]) 44 { 45 struct mparse *mp; 46 int ch, fd, i, list; 47 extern int optind; 48 49 if (argc < 1) 50 progname = "demandoc"; 51 else if ((progname = strrchr(argv[0], '/')) == NULL) 52 progname = argv[0]; 53 else 54 ++progname; 55 56 mp = NULL; 57 list = 0; 58 59 while (-1 != (ch = getopt(argc, argv, "ikm:pw"))) 60 switch (ch) { 61 case ('i'): 62 /* FALLTHROUGH */ 63 case ('k'): 64 /* FALLTHROUGH */ 65 case ('m'): 66 /* FALLTHROUGH */ 67 case ('p'): 68 break; 69 case ('w'): 70 list = 1; 71 break; 72 default: 73 usage(); 74 return (int)MANDOCLEVEL_BADARG; 75 } 76 77 argc -= optind; 78 argv += optind; 79 80 mchars_alloc(); 81 mp = mparse_alloc(MPARSE_SO, MANDOCERR_MAX, NULL, 82 MANDOC_OS_OTHER, NULL); 83 assert(mp); 84 85 if (argc < 1) 86 pmandoc(mp, STDIN_FILENO, "<stdin>", list); 87 88 for (i = 0; i < argc; i++) { 89 mparse_reset(mp); 90 if ((fd = mparse_open(mp, argv[i])) == -1) { 91 perror(argv[i]); 92 continue; 93 } 94 pmandoc(mp, fd, argv[i], list); 95 } 96 97 mparse_free(mp); 98 mchars_free(); 99 return (int)MANDOCLEVEL_OK; 100 } 101 102 static void 103 usage(void) 104 { 105 106 fprintf(stderr, "usage: %s [-w] [files...]\n", progname); 107 } 108 109 static void 110 pmandoc(struct mparse *mp, int fd, const char *fn, int list) 111 { 112 struct roff_man *man; 113 int line, col; 114 115 mparse_readfd(mp, fd, fn); 116 close(fd); 117 mparse_result(mp, &man, NULL); 118 line = 1; 119 col = 0; 120 121 if (man == NULL) 122 return; 123 if (man->macroset == MACROSET_MDOC) { 124 mdoc_validate(man); 125 pmdoc(man->first->child, &line, &col, list); 126 } else { 127 man_validate(man); 128 pman(man->first->child, &line, &col, list); 129 } 130 131 if ( ! list) 132 putchar('\n'); 133 } 134 135 /* 136 * Strip the escapes out of a string, emitting the results. 137 */ 138 static void 139 pstring(const char *p, int col, int *colp, int list) 140 { 141 enum mandoc_esc esc; 142 const char *start, *end; 143 int emit; 144 145 /* 146 * Print as many column spaces til we achieve parity with the 147 * input document. 148 */ 149 150 again: 151 if (list && '\0' != *p) { 152 while (isspace((unsigned char)*p)) 153 p++; 154 155 while ('\'' == *p || '(' == *p || '"' == *p) 156 p++; 157 158 emit = isalpha((unsigned char)p[0]) && 159 isalpha((unsigned char)p[1]); 160 161 for (start = p; '\0' != *p; p++) 162 if ('\\' == *p) { 163 p++; 164 esc = mandoc_escape(&p, NULL, NULL); 165 if (ESCAPE_ERROR == esc) 166 return; 167 emit = 0; 168 } else if (isspace((unsigned char)*p)) 169 break; 170 171 end = p - 1; 172 173 while (end > start) 174 if ('.' == *end || ',' == *end || 175 '\'' == *end || '"' == *end || 176 ')' == *end || '!' == *end || 177 '?' == *end || ':' == *end || 178 ';' == *end) 179 end--; 180 else 181 break; 182 183 if (emit && end - start >= 1) { 184 for ( ; start <= end; start++) 185 if (ASCII_HYPH == *start) 186 putchar('-'); 187 else 188 putchar((unsigned char)*start); 189 putchar('\n'); 190 } 191 192 if (isspace((unsigned char)*p)) 193 goto again; 194 195 return; 196 } 197 198 while (*colp < col) { 199 putchar(' '); 200 (*colp)++; 201 } 202 203 /* 204 * Print the input word, skipping any special characters. 205 */ 206 while ('\0' != *p) 207 if ('\\' == *p) { 208 p++; 209 esc = mandoc_escape(&p, NULL, NULL); 210 if (ESCAPE_ERROR == esc) 211 break; 212 } else { 213 putchar((unsigned char )*p++); 214 (*colp)++; 215 } 216 } 217 218 static void 219 pline(int line, int *linep, int *col, int list) 220 { 221 222 if (list) 223 return; 224 225 /* 226 * Print out as many lines as needed to reach parity with the 227 * original input. 228 */ 229 230 while (*linep < line) { 231 putchar('\n'); 232 (*linep)++; 233 } 234 235 *col = 0; 236 } 237 238 static void 239 pmdoc(const struct roff_node *p, int *line, int *col, int list) 240 { 241 242 for ( ; p; p = p->next) { 243 if (NODE_LINE & p->flags) 244 pline(p->line, line, col, list); 245 if (ROFFT_TEXT == p->type) 246 pstring(p->string, p->pos, col, list); 247 if (p->child) 248 pmdoc(p->child, line, col, list); 249 } 250 } 251 252 static void 253 pman(const struct roff_node *p, int *line, int *col, int list) 254 { 255 256 for ( ; p; p = p->next) { 257 if (NODE_LINE & p->flags) 258 pline(p->line, line, col, list); 259 if (ROFFT_TEXT == p->type) 260 pstring(p->string, p->pos, col, list); 261 if (p->child) 262 pman(p->child, line, col, list); 263 } 264 } 265