/* * tardy - a tar post-processor * Copyright (C) 1991-1995, 1998-2002 Peter Miller; * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * * MANIFEST: functions to provide consistent treatment of -Help options */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define PAIR(a, b) ((a) * 256 + (b)) static char *cr[] = { "\\*(n) version \\*(v)", ".br", "Copyright (C) \\*(Y) Peter Miller;", "All rights reserved.", "", "The \\*(n) program comes with ABSOLUTELY NO WARRANTY;", "for details use the '\\*(n) -VERSion Warranty' command.", "The \\*(n) program is free software, and you are welcome to", "redistribute it under certain conditions;", "for details use the '\\*(n) -VERSion Redistribution' command.", }; static char *au[] = { ".nf", "Peter Miller EMail: millerp@canb.auug.org.au", "/\\e/\\e* WWW: http://www.canb.auug.org.au/~millerp/", ".fi", }; char *so_o__rules[] = { #include <../man1/o__rules.h> }; char *so_o_help[] = { #include <../man1/o_help.h> }; char *so_z_cr[] = { ".SH COPYRIGHT", ".so cr", ".SH AUTHOR", ".so au", }; char *so_z_exit[] = { #include <../man1/z_exit.h> }; typedef struct so_list_ty so_list_ty; struct so_list_ty { char *name; char **text; int length; }; static so_list_ty so_list[] = { { "o__rules.so", so_o__rules, SIZEOF(so_o__rules) }, { "o_help.so", so_o_help, SIZEOF(so_o_help) }, { "z_cr.so", so_z_cr, SIZEOF(so_z_cr) }, { "z_exit.so", so_z_exit, SIZEOF(so_z_exit) }, { "z_name.so", 0, 0 }, { "../etc/version.so", 0, 0 }, { "cr", cr, SIZEOF(cr), }, { "au", au, SIZEOF(au), }, }; static int ocol; static int icol; static int fill; /* true if currently filling */ static int in; /* current indent */ static int in_base; /* current paragraph indent */ static int ll; /* line length */ static long roff_line; static char *roff_file; static int TP_line; static void emit(int c) { switch (c) { case ' ': icol++; break; case '\t': icol = ((icol / 8) + 1) * 8; break; case '\n': putchar('\n'); icol = 0; ocol = 0; break; default: if (!isprint(c)) break; while (((ocol / 8) + 1) * 8 <= icol && ocol + 1 < icol) { putchar('\t'); ocol = ((ocol / 8) + 1) * 8; } while (ocol < icol) { putchar(' '); ++ocol; } putchar(c); ++icol; ++ocol; break; } if (ferror(stdout)) nfatal("standard output"); } static void emit_word(char *buf, long len) { if (len <= 0) return; /* * if this line is not yet indented, indent it */ if (!ocol && !icol) icol = in; /* * if there is already something on this line * and we are in "fill" mode * and this word would cause it to overflow * then wrap the line */ if (ocol && fill && icol + len >= ll) { emit('\n'); icol = in; } if (ocol) emit(' '); while (len-- > 0) emit(*buf++); } static void br(void) { if (ocol) emit('\n'); } static void sp(void) { br(); emit('\n'); } static void interpret_line_of_words(char *line) { /* * if not filling, * pump the line out literrally. */ if (!fill) { if (!ocol && !icol) icol = in; while (*line) emit(*line++); emit('\n'); return; } /* * in fill mode, a blank line means * finish the paragraph and emit a blank line */ if (!*line) { sp(); return; } /* * break the line into space-separated words * and emit each individually */ while (*line) { char *start; while (isspace(*line)) ++line; if (!*line) break; start = line; while (*line && !isspace(*line)) ++line; emit_word(start, line - start); /* * extra space at end of sentences */ if ( (line[-1] == '.' || line[-1] == '?') && ( !line[0] || ( line[0] == ' ' && (!line[1] || line[1] == ' ') ) ) ) emit(' '); } } static void roff_error(char *s, ...) { va_list ap; va_start(ap, s); char buffer[1000]; vsnprintf(buffer, sizeof(buffer), s, ap); va_end(ap); #if 0 br(); if (roff_file) emit_word(roff_file, strlen(roff_file)); if (roff_line) { char line[20]; snprintf(line, sizeof(line), "%ld", roff_line); emit_word(line, strlen(line)); } interpret_line_of_words(buffer); br(); #else fatal ( "%s: %ld: %s", (roff_file ? roff_file : "(noname)"), roff_line, buffer ); #endif } static void get_name(char **lp, char *name) { char *line; line = *lp; if (*line == '('/*)*/) { ++line; if (*line) { name[0] = *line++; if (*line) { name[1] = *line++; name[2] = 0; } else name[1] = 0; } else name[0] = 0; } else if (*line) { name[0] = *line++; name[1] = 0; } else name[0] = 0; *lp = line; } typedef struct string_reg_ty string_reg_ty; struct string_reg_ty { char *name; char *value; }; static long string_reg_count; static string_reg_ty *string_reg; static char * string_find(char *name) { long j; for (j = 0; j < string_reg_count; ++j) { string_reg_ty *srp; srp = &string_reg[j]; if (!strcmp(name, srp->name)) return srp->value; } return 0; } static char * numreg_find(char *) { return 0; } static void roff_prepro(char *buffer, char *line) { char *bp; char *value; char name[4]; bp = buffer; while (*line) { int c = *line++; if (c != '\\') { *bp++ = c; continue; } c = *line++; if (!c) { roff_error("can't do escaped end-of-line"); break; } switch (c) { default: roff_error("unknown \\%c inline directive", c); break; case '%': /* word break info */ break; case '*': /* inline string */ get_name(&line, name); value = string_find(name); if (value) { while (*value) *bp++ = *value++; } break; case 'n': /* inline number register */ get_name(&line, name); value = numreg_find(name); if (value) { while (*value) *bp++ = *value++; } break; case 'e': case '\\': *bp++ = '\\'; break; case '-': *bp++ = '-'; break; case 'f': /* ignore font directives */ get_name(&line, name); break; case '&': case '|': /* ignore weird space directives */ break; } } *bp = 0; } static void interpret_text(char *line) { char buffer[1000]; roff_prepro(buffer, line); interpret_line_of_words(buffer); if (TP_line) { if (icol >= 15) br(); else icol = 15; TP_line = 0; in = in_base + 8; } } static void roff_sub(char *buffer, int argc, char **argv) { int j; char *bp; long len; bp = buffer; for (j = 0; j < argc; ++j) { len = strlen(argv[j]); if (j) *bp++ = ' '; memcpy(bp, argv[j], len); bp += len; } *bp = 0; } static void interpret_text_args(int argc, char **argv) { char buffer[1000]; roff_sub(buffer, argc, argv); interpret_text(buffer); } static void concat_text_args(int argc, char **argv) { int j; char *bp; size_t len; char buffer[1000]; bp = buffer; for (j = 0; j < argc; ++j) { len = strlen(argv[j]); if ((bp - buffer) + len + 1 >= sizeof(buffer)) break; memcpy(bp, argv[j], len); bp += len; } *bp = 0; interpret_text(buffer); } static void interpret(char **, int); /* forward */ static void so(int argc, char **argv) { so_list_ty *sop; if (argc != 1) { roff_error(".so requires one argument"); return; } for (sop = so_list; sop < ENDOF(so_list); ++sop) { if (!strcmp(sop->name, argv[0])) { interpret(sop->text, sop->length); return; } } roff_error("\".so %s\" not known", argv[0]); } static void lf(int argc, char **argv) { if (roff_file) mem_free(roff_file); if (argc >= 1) roff_line = atol(argv[0]) - 1; else roff_line = 0; if (argc >= 2) roff_file = mem_copy_string(argv[1]); else roff_file = 0; } static void ds_guts(const char *name, const char *value) { long j; string_reg_ty *srp; for (j = 0; j < string_reg_count; ++j) { srp = &string_reg[j]; if (!strcmp(name, srp->name)) { mem_free(srp->value); srp->value = mem_copy_string(value); return; } } if (string_reg_count) { string_reg = (string_reg_ty *) mem_change_size ( string_reg, (string_reg_count + 1) * sizeof(string_reg_ty) ); } else string_reg = (string_reg_ty *)mem_alloc(sizeof(string_reg_ty)); srp = &string_reg[string_reg_count++]; srp->name = mem_copy_string(name); srp->value = mem_copy_string(value); } static void ds(int argc, char **argv) { char buf1[1000]; char buf2[1000]; if (!argc) return; roff_sub(buf1, argc - 1, argv + 1); roff_prepro(buf2, buf1); ds_guts(argv[0], buf2); } static void dot_in(int argc, char **argv) { if (argc < 1) return; switch (argv[0][0]) { case '-': in -= atoi(argv[0] + 1); break; case '+': in += atoi(argv[0] + 1); break; default: in = atoi(argv[0] + 1); break; } if (in < 0) in = 0; } static void interpret(char **, int); /* forward */ static void interpret_control(char *line) { int c1, c2; int argc; char *argv[20]; char temp[1000]; char *cp; /* * find the directive name */ line++; while (isspace(*line)) ++line; if (*line) c1 = *line++; else c1 = ' '; if (*line) c2 = *line++; else c2 = ' '; /* * break the line into space-separated arguments */ argc = 0; cp = temp; while (argc < (int)SIZEOF(argv)) { int quoting; while (isspace(*line)) ++line; if (!*line) break; argv[argc++] = cp; quoting = 0; while (*line) { if (*line == '"') { quoting = !quoting; ++line; continue; } if (!quoting && isspace(*line)) break; *cp++ = *line++; } *cp++ = 0; if (!*line) break; } /* * now do something with it */ switch (PAIR(c1, c2)) { case PAIR('n', 'e'): /* ignore the space needed directive */ break; case PAIR('i', 'n'): dot_in(argc, argv); break; case PAIR('I', ' '): case PAIR('I', 'R'): case PAIR('I', 'B'): case PAIR('R', ' '): case PAIR('R', 'I'): case PAIR('R', 'B'): case PAIR('B', ' '): case PAIR('B', 'I'): case PAIR('B', 'R'): concat_text_args(argc, argv); break; case PAIR('n', 'f'): br(); fill = 0; break; case PAIR('f', 'i'): br(); fill = 1; break; case PAIR('t', 'a'): /* ignore tab directive */ break; case PAIR('b', 'r'): br(); break; case PAIR('s', 'p'): sp(); break; case PAIR('I', 'P'): in = in_base; sp(); emit(' '); emit(' '); break; case PAIR('P', 'P'): in = in_base; sp(); break; case PAIR('T', 'H'): break; case PAIR('T', 'P'): in = in_base; sp(); TP_line = 1; break; case PAIR('S', 'H'): in = 0; sp(); interpret_text_args(argc, argv); br(); in_base = 8; in = 8; break; case PAIR('s', 'o'): so(argc, argv); break; case PAIR('l', 'f'): lf(argc, argv); break; case PAIR('R', 'S'): in_base = 16; in = 16; br(); break; case PAIR('R', 'E'): in_base = 8; in = 8; br(); break; case PAIR('d', 's'): ds(argc, argv); break; case PAIR('r', /*(*/')'): cp = string_find(/*(*/"R)"); if (!cp) cp = ""; if (strcmp(cp, "no") != 0) { static char *macro[] = { ".PP", "See also", ".IR \\*(n) (1)", "for options common to all \\*(n) commands.", }; interpret(macro, SIZEOF(macro)); } break; default: roff_error("formatting directive \".%c%c\" unknown", c1, c2); break; } } static void interpret(char **text, int text_len) { int j; long hold_line; char *hold_file; /* * save position */ trace(("interpret()\n{\n"/*}*/)); hold_line = roff_line; hold_file = roff_file ? mem_copy_string(roff_file) : (char *)0; /* * interpret the text */ for (j = 0; j < text_len; ++j) { char *s; s = text[j]; if (*s == '.' || *s == '\'') interpret_control(s); else interpret_text(s); ++roff_line; if (ferror(stdout)) nfatal("standard output"); } /* * restore position */ if (roff_file) mem_free(roff_file); roff_line = hold_line; roff_file = hold_file; trace((/*{*/"}\n")); } void help(char **text, int text_len, void (*usage)(void)) { /* * collect the rest of thge command line, * if necessary */ trace(("help(text = %08lX, text_len = %d, usage = %08lX)\n{\n"/*}*/, text, text_len, usage)); if (usage) { arglex(); while (arglex_token != arglex_token_eoln) generic_argument(usage); } /* * initialize the state of the interpreter */ ds_guts(/*(*/"n)", progname_get()); ds_guts(/*(*/"v)", version_stamp()); ds_guts(/*(*/"Y)", copyright_years()); ll = 79; in = 0; in_base = 0; fill = 1; ocol = 0; icol = 0; lf(0, 0); TP_line = 0; /* * do what they asked */ interpret(text, text_len); br(); /* * close the paginator */ if (fflush(stdout)) nfatal("standard output"); trace((/*{*/"}\n")); } void generic_argument(void (*usage)(void)) { trace(("generic_argument()\n{\n"/*}*/)); switch (arglex_token) { default: bad_argument(usage); /* NOTREACHED */ case arglex_token_trace: if (arglex() != arglex_token_string) usage(); for (;;) { trace_enable(arglex_value.alv_string); if (arglex() != arglex_token_string) break; } #ifndef DEBUG error ( "Warning: the -TRace option is only effective when the %s program \ is compiled using the DEBUG define in the common/main.h include file.", progname_get() ); #endif break; } trace((/*{*/"}\n")); } void bad_argument(void (*usage)(void)) { trace(("bad_argument()\n{\n"/*}*/)); switch (arglex_token) { case arglex_token_string: error("misplaced file name (\"%s\")", arglex_value.alv_string); break; case arglex_token_number: error("misplaced number (%s)", arglex_value.alv_string); break; case arglex_token_option: error("unknown \"%s\" option", arglex_value.alv_string); break; case arglex_token_eoln: error("command line too short"); break; default: error("misplaced \"%s\" option", arglex_value.alv_string); break; } usage(); trace((/*{*/"}\n")); quit(1); /* NOTREACHED */ }