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