1 /* $Id: demandoc.c,v 1.33 2019/03/03 11:01:15 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 #include "mandoc_parse.h" 33 34 static void pline(int, int *, int *, int); 35 static void pman(const struct roff_node *, int *, int *, int); 36 static void pmandoc(struct mparse *, int, const char *, int); 37 static void pmdoc(const struct roff_node *, int *, int *, int); 38 static void pstring(const char *, int, int *, int); 39 static void usage(void); 40 41 static const char *progname; 42 43 int 44 main(int argc, char *argv[]) 45 { 46 struct mparse *mp; 47 int ch, fd, i, list; 48 extern int optind; 49 50 if (argc < 1) 51 progname = "demandoc"; 52 else if ((progname = strrchr(argv[0], '/')) == NULL) 53 progname = argv[0]; 54 else 55 ++progname; 56 57 mp = NULL; 58 list = 0; 59 60 while (-1 != (ch = getopt(argc, argv, "ikm:pw"))) 61 switch (ch) { 62 case ('i'): 63 /* FALLTHROUGH */ 64 case ('k'): 65 /* FALLTHROUGH */ 66 case ('m'): 67 /* FALLTHROUGH */ 68 case ('p'): 69 break; 70 case ('w'): 71 list = 1; 72 break; 73 default: 74 usage(); 75 return (int)MANDOCLEVEL_BADARG; 76 } 77 78 argc -= optind; 79 argv += optind; 80 81 mchars_alloc(); 82 mp = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 | 83 MPARSE_VALIDATE, MANDOC_OS_OTHER, NULL); 84 assert(mp); 85 86 if (argc < 1) 87 pmandoc(mp, STDIN_FILENO, "<stdin>", list); 88 89 for (i = 0; i < argc; i++) { 90 mparse_reset(mp); 91 if ((fd = mparse_open(mp, argv[i])) == -1) { 92 perror(argv[i]); 93 continue; 94 } 95 pmandoc(mp, fd, argv[i], list); 96 } 97 98 mparse_free(mp); 99 mchars_free(); 100 return (int)MANDOCLEVEL_OK; 101 } 102 103 static void 104 usage(void) 105 { 106 107 fprintf(stderr, "usage: %s [-w] [files...]\n", progname); 108 } 109 110 static void 111 pmandoc(struct mparse *mp, int fd, const char *fn, int list) 112 { 113 struct roff_meta *meta; 114 int line, col; 115 116 mparse_readfd(mp, fd, fn); 117 close(fd); 118 meta = mparse_result(mp); 119 line = 1; 120 col = 0; 121 122 if (meta->macroset == MACROSET_MDOC) 123 pmdoc(meta->first->child, &line, &col, list); 124 else 125 pman(meta->first->child, &line, &col, list); 126 127 if ( ! list) 128 putchar('\n'); 129 } 130 131 /* 132 * Strip the escapes out of a string, emitting the results. 133 */ 134 static void 135 pstring(const char *p, int col, int *colp, int list) 136 { 137 enum mandoc_esc esc; 138 const char *start, *end; 139 int emit; 140 141 /* 142 * Print as many column spaces til we achieve parity with the 143 * input document. 144 */ 145 146 again: 147 if (list && '\0' != *p) { 148 while (isspace((unsigned char)*p)) 149 p++; 150 151 while ('\'' == *p || '(' == *p || '"' == *p) 152 p++; 153 154 emit = isalpha((unsigned char)p[0]) && 155 isalpha((unsigned char)p[1]); 156 157 for (start = p; '\0' != *p; p++) 158 if ('\\' == *p) { 159 p++; 160 esc = mandoc_escape(&p, NULL, NULL); 161 if (ESCAPE_ERROR == esc) 162 return; 163 emit = 0; 164 } else if (isspace((unsigned char)*p)) 165 break; 166 167 end = p - 1; 168 169 while (end > start) 170 if ('.' == *end || ',' == *end || 171 '\'' == *end || '"' == *end || 172 ')' == *end || '!' == *end || 173 '?' == *end || ':' == *end || 174 ';' == *end) 175 end--; 176 else 177 break; 178 179 if (emit && end - start >= 1) { 180 for ( ; start <= end; start++) 181 if (ASCII_HYPH == *start) 182 putchar('-'); 183 else 184 putchar((unsigned char)*start); 185 putchar('\n'); 186 } 187 188 if (isspace((unsigned char)*p)) 189 goto again; 190 191 return; 192 } 193 194 while (*colp < col) { 195 putchar(' '); 196 (*colp)++; 197 } 198 199 /* 200 * Print the input word, skipping any special characters. 201 */ 202 while ('\0' != *p) 203 if ('\\' == *p) { 204 p++; 205 esc = mandoc_escape(&p, NULL, NULL); 206 if (ESCAPE_ERROR == esc) 207 break; 208 } else { 209 putchar((unsigned char )*p++); 210 (*colp)++; 211 } 212 } 213 214 static void 215 pline(int line, int *linep, int *col, int list) 216 { 217 218 if (list) 219 return; 220 221 /* 222 * Print out as many lines as needed to reach parity with the 223 * original input. 224 */ 225 226 while (*linep < line) { 227 putchar('\n'); 228 (*linep)++; 229 } 230 231 *col = 0; 232 } 233 234 static void 235 pmdoc(const struct roff_node *p, int *line, int *col, int list) 236 { 237 238 for ( ; p; p = p->next) { 239 if (NODE_LINE & p->flags) 240 pline(p->line, line, col, list); 241 if (ROFFT_TEXT == p->type) 242 pstring(p->string, p->pos, col, list); 243 if (p->child) 244 pmdoc(p->child, line, col, list); 245 } 246 } 247 248 static void 249 pman(const struct roff_node *p, int *line, int *col, int list) 250 { 251 252 for ( ; p; p = p->next) { 253 if (NODE_LINE & p->flags) 254 pline(p->line, line, col, list); 255 if (ROFFT_TEXT == p->type) 256 pstring(p->string, p->pos, col, list); 257 if (p->child) 258 pman(p->child, line, col, list); 259 } 260 } 261