1 /* Id: demandoc.c,v 1.28 2017/01/10 13:47:00 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 "roff.h" 29 #include "man.h" 30 #include "mdoc.h" 31 #include "mandoc.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, MANDOCLEVEL_BADARG, NULL, NULL); 82 assert(mp); 83 84 if (argc < 1) 85 pmandoc(mp, STDIN_FILENO, "<stdin>", list); 86 87 for (i = 0; i < argc; i++) { 88 mparse_reset(mp); 89 if ((fd = mparse_open(mp, argv[i])) == -1) { 90 perror(argv[i]); 91 continue; 92 } 93 pmandoc(mp, fd, argv[i], list); 94 } 95 96 mparse_free(mp); 97 mchars_free(); 98 return (int)MANDOCLEVEL_OK; 99 } 100 101 static void 102 usage(void) 103 { 104 105 fprintf(stderr, "usage: %s [-w] [files...]\n", progname); 106 } 107 108 static void 109 pmandoc(struct mparse *mp, int fd, const char *fn, int list) 110 { 111 struct roff_man *man; 112 int line, col; 113 114 mparse_readfd(mp, fd, fn); 115 close(fd); 116 mparse_result(mp, &man, NULL); 117 line = 1; 118 col = 0; 119 120 if (man == NULL) 121 return; 122 if (man->macroset == MACROSET_MDOC) { 123 mdoc_validate(man); 124 pmdoc(man->first->child, &line, &col, list); 125 } else { 126 man_validate(man); 127 pman(man->first->child, &line, &col, list); 128 } 129 130 if ( ! list) 131 putchar('\n'); 132 } 133 134 /* 135 * Strip the escapes out of a string, emitting the results. 136 */ 137 static void 138 pstring(const char *p, int col, int *colp, int list) 139 { 140 enum mandoc_esc esc; 141 const char *start, *end; 142 int emit; 143 144 /* 145 * Print as many column spaces til we achieve parity with the 146 * input document. 147 */ 148 149 again: 150 if (list && '\0' != *p) { 151 while (isspace((unsigned char)*p)) 152 p++; 153 154 while ('\'' == *p || '(' == *p || '"' == *p) 155 p++; 156 157 emit = isalpha((unsigned char)p[0]) && 158 isalpha((unsigned char)p[1]); 159 160 for (start = p; '\0' != *p; p++) 161 if ('\\' == *p) { 162 p++; 163 esc = mandoc_escape(&p, NULL, NULL); 164 if (ESCAPE_ERROR == esc) 165 return; 166 emit = 0; 167 } else if (isspace((unsigned char)*p)) 168 break; 169 170 end = p - 1; 171 172 while (end > start) 173 if ('.' == *end || ',' == *end || 174 '\'' == *end || '"' == *end || 175 ')' == *end || '!' == *end || 176 '?' == *end || ':' == *end || 177 ';' == *end) 178 end--; 179 else 180 break; 181 182 if (emit && end - start >= 1) { 183 for ( ; start <= end; start++) 184 if (ASCII_HYPH == *start) 185 putchar('-'); 186 else 187 putchar((unsigned char)*start); 188 putchar('\n'); 189 } 190 191 if (isspace((unsigned char)*p)) 192 goto again; 193 194 return; 195 } 196 197 while (*colp < col) { 198 putchar(' '); 199 (*colp)++; 200 } 201 202 /* 203 * Print the input word, skipping any special characters. 204 */ 205 while ('\0' != *p) 206 if ('\\' == *p) { 207 p++; 208 esc = mandoc_escape(&p, NULL, NULL); 209 if (ESCAPE_ERROR == esc) 210 break; 211 } else { 212 putchar((unsigned char )*p++); 213 (*colp)++; 214 } 215 } 216 217 static void 218 pline(int line, int *linep, int *col, int list) 219 { 220 221 if (list) 222 return; 223 224 /* 225 * Print out as many lines as needed to reach parity with the 226 * original input. 227 */ 228 229 while (*linep < line) { 230 putchar('\n'); 231 (*linep)++; 232 } 233 234 *col = 0; 235 } 236 237 static void 238 pmdoc(const struct roff_node *p, int *line, int *col, int list) 239 { 240 241 for ( ; p; p = p->next) { 242 if (NODE_LINE & p->flags) 243 pline(p->line, line, col, list); 244 if (ROFFT_TEXT == p->type) 245 pstring(p->string, p->pos, col, list); 246 if (p->child) 247 pmdoc(p->child, line, col, list); 248 } 249 } 250 251 static void 252 pman(const struct roff_node *p, int *line, int *col, int list) 253 { 254 255 for ( ; p; p = p->next) { 256 if (NODE_LINE & p->flags) 257 pline(p->line, line, col, list); 258 if (ROFFT_TEXT == p->type) 259 pstring(p->string, p->pos, col, list); 260 if (p->child) 261 pman(p->child, line, col, list); 262 } 263 } 264