diff options
Diffstat (limited to 'src/preproc/soelim')
-rw-r--r-- | src/preproc/soelim/TODO | 1 | ||||
-rw-r--r-- | src/preproc/soelim/soelim.1.man | 456 | ||||
-rw-r--r-- | src/preproc/soelim/soelim.am | 31 | ||||
-rw-r--r-- | src/preproc/soelim/soelim.cpp | 315 |
4 files changed, 803 insertions, 0 deletions
diff --git a/src/preproc/soelim/TODO b/src/preproc/soelim/TODO new file mode 100644 index 0000000..f2a3924 --- /dev/null +++ b/src/preproc/soelim/TODO @@ -0,0 +1 @@ +Understand .pso. diff --git a/src/preproc/soelim/soelim.1.man b/src/preproc/soelim/soelim.1.man new file mode 100644 index 0000000..4a1c042 --- /dev/null +++ b/src/preproc/soelim/soelim.1.man @@ -0,0 +1,456 @@ +'\" p +.TH @g@soelim @MAN1EXT@ "@MDATE@" "groff @VERSION@" +.SH Name +@g@soelim \- recursively interpolate source requests in +.I roff +or other text files +. +. +.\" ==================================================================== +.\" Legal Terms +.\" ==================================================================== +.\" +.\" Copyright (C) 1989-2020 Free Software Foundation, Inc. +.\" +.\" Permission is granted to make and distribute verbatim copies of this +.\" manual provided the copyright notice and this permission notice are +.\" preserved on all copies. +.\" +.\" Permission is granted to copy and distribute modified versions of +.\" this manual under the conditions for verbatim copying, provided that +.\" the entire resulting derived work is distributed under the terms of +.\" a permission notice identical to this one. +.\" +.\" Permission is granted to copy and distribute translations of this +.\" manual into another language, under the above conditions for +.\" modified versions, except that this permission notice may be +.\" included in translations approved by the Free Software Foundation +.\" instead of in the original English. +. +. +.\" Save and disable compatibility mode (for, e.g., Solaris 10/11). +.do nr *groff_soelim_1_man_C \n[.cp] +.cp 0 +. +.\" Define fallback for groff 1.23's MR macro if the system lacks it. +.nr do-fallback 0 +.if !\n(.f .nr do-fallback 1 \" mandoc +.if \n(.g .if !d MR .nr do-fallback 1 \" older groff +.if !\n(.g .nr do-fallback 1 \" non-groff *roff +.if \n[do-fallback] \{\ +. de MR +. ie \\n(.$=1 \ +. I \%\\$1 +. el \ +. IR \%\\$1 (\\$2)\\$3 +. . +.\} +.rr do-fallback +.\" Man pages are seldom preprocessed with pic(1). +.mso pic.tmac +. +. +.\" ==================================================================== +.\" Definitions +.\" ==================================================================== +. +.ie t .ds tx T\h'-.1667m'\v'.224m'E\v'-.224m'\h'-.125m'X +.el .ds tx TeX +. +. +.\" ==================================================================== +.SH Synopsis +.\" ==================================================================== +. +.SY @g@soelim +.RB [ \-Crt ] +.RB [ \-I +.IR dir ] +.RI [ input-file\~ .\|.\|.] +.YS +. +. +.SY @g@soelim +.B \-\-help +.YS +. +. +.SY @g@soelim +.B \-v +. +.SY @g@soelim +.B \-\-version +.YS +. +. +.\" ==================================================================== +.SH Description +.\" ==================================================================== +. +GNU +.I soelim \" GNU +is a preprocessor for the +.MR groff @MAN7EXT@ +document formatting system. +. +.I @g@soelim +works as a filter to eliminate source requests in +.MR roff @MAN7EXT@ +input files; +that is, +it replaces lines of the form +.RB \[lq] .so +.IR included-file \[rq] +within each text +.I input-file +with the contents of +.IR included-file , +recursively. +. +By default, +it writes +.B lf +requests as well to record the name and line number of each +.I input-file +and +.IR included-file , +so that any diagnostics produced by later processing can be accurately +traced to the original input. +. +Options allow this information to be suppressed +.RB ( \-r ) +or supplied in \*[tx] comments instead +.RB ( \-t ). +. +In the absence of +.I input-file +arguments, +.I @g@soelim +reads the standard input stream. +. +Output is written to the standard output stream. +. +. +.PP +If the name of a +.I macro-file +contains a backslash, +use +.B \[rs]\[rs] +or +.B \[rs]e +to embed it. +. +To embed a space, +write +.RB \[lq] \[rs]\~ \[rq] +(backslash followed by a space). +. +Any other escape sequence in +.IR macro-file , +including +.RB \[lq] \[rs][rs] \[rq], +prevents +.I @g@soelim +from replacing the source request. +. +. +.PP +The dot must be at the beginning of a line and must be followed by +.RB \[lq] so \[rq] +without intervening spaces or tabs for +.I @g@soelim +to handle it. +. +This convention allows source requests to be \[lq]protected\[rq] from +processing by +.IR @g@soelim , +for instance as part of macro definitions or +.RB \[lq] if \[rq] +requests. +. +. +.PP +There must also be at least one space between +.RB \[lq] so \[rq] +and its +.I macro-file +argument. +. +The +.B \-C +option overrides this requirement. +. +. +.PP +The foregoing is the limit of +.IR @g@soelim 's +understanding of the +.I roff +language; +it does not, +for example, +replace the input line +. +.RS +.EX +\&.if 1 .so otherfile +.EE +.RE +. +with the contents of +.IR otherfile . +. +With its +.B \-r +option, +therefore, +.I @g@soelim +can be used to process text files in general, +to flatten a tree of input documents. +. +. +.PP +.I soelim \" generic +was designed to handle situations where the target of a +.I roff \" generic +source request requires a preprocessor such as +.MR @g@eqn @MAN1EXT@ , +.MR @g@pic @MAN1EXT@ , +.MR @g@refer @MAN1EXT@ , +or +.MR @g@tbl @MAN1EXT@ . +. +The usual processing sequence of +.MR groff @MAN1EXT@ +is as follows. +. +.\" Does this groff installation use a command prefix? In installed +.\" pages, this comparison will not look like it needs to be dynamically +.\" decided. +.\" +.\" This is done so that the box sizes (in the pic(1) diagram) and arrow +.\" alignments (in the text alternative) can remain fixed. +.if !'@g@'\%' \{\ +In the diagrams below, +the traditional names for +.I soelim +and +.I troff +are used; +on this system, +the GNU versions are installed as +.I @g@soelim +and +.IR @g@troff . +.\} +. +. +.PP +.ie t \{\ +.PS +.ps 10 +.vs 12 +box invisible width 0.5 height 0.4 "input" "file"; +move to last box .bottom; +down; +arrow 0.3; +box invisible width 0.8 height 0.2 "preprocessor"; +move to last box .right +right; +arrow 0.3; +A: box invisible width 0.35 height 0.2 "troff"; +move to last box .top; +up; +move 0.3; +box invisible width 0.6 height 0.4 "sourced" "file"; +line <- up 0.3 from A.top; +move to A.right; +right; +arrow 0.3; +box invisible width 0.85 height 0.2 "postprocessor"; +move to last box .bottom; +down; +arrow 0.3; +box invisible width 0.5 height 0.4 "output" "file" +.ps +.vs +.PE +.\} +.el \{\ +.EX + input sourced + file file + \[bv] \[bv] + \[da] \[da] + preprocessor \[an]\[->] troff \[an]\[->] postprocessor + \[bv] + \[da] + output + file +.EE +.\} +.PP +That is, +files sourced with +.RB \[lq] so \[rq] +are normally read +.I only +by the formatter, +.IR @g@troff . +. +.I @g@soelim +is +.I not +required for +.I @g@troff +to source files. +. +. +.PP +If a file to be sourced should also be preprocessed, +it must already be read +.I before +the input file passes through the preprocessor. +. +.IR @g@soelim , +normally invoked via +.IR groff 's +.B \-s +option, +handles this. +. +. +.PP +.ie t \{\ +.PS +.ps 10 +.vs 12 +box invisible width 0.5 height 0.4 "input" "file"; +move to last box .bottom; +down; +arrow 0.3; +A: box invisible width 0.5 height 0.2 "soelim"; +line <- 0.3; +box invisible width 0.5 height 0.4 "sourced" "file"; +move to A.right; +right; +arrow 0.3; +box invisible width 0.8 height 0.2 "preprocessor"; +arrow 0.3; +box invisible width 0.35 height 0.2 "troff"; +arrow 0.3 +box invisible width 0.85 height 0.2 "postprocessor"; +move to last box .bottom; +down; +arrow 0.3; +box invisible width 0.5 height 0.4 "output" "file" +.ps +.vs +.PE +.\} +.el \{\ +.EX + input + file + \[bv] + \[da] + soelim \[an]\[->] preprocessor \[an]\[->] troff \[an]\[->] \ +postprocessor + \[ua] \[bv] + \[bv] \[da] + sourced output + file file +.EE +.\} +. +. +.\" ==================================================================== +.SH Options +.\" ==================================================================== +. +.B \-\-help +displays a usage message, +while +.B \-v +and +.B \-\-version +show version information; +all exit afterward. +. +. +.TP +.B \-C +Recognize an input line starting with +.B .so +even if a character other than a space or newline follows. +. +.TP +.BI \-I\~ dir +Search the directory +.I dir +path for +.I input- +and +.I included-files. +. +.B \-I +may be specified more than once; +each +.I dir +is searched in the given order. +. +To search the current working directory before others, +add +.RB \[lq] "\-I .\&" \[rq] +at the desired place; +it is otherwise searched last. +. +. +.TP +.B \-r +Write files \[lq]raw\[rq]; +do not add +.B lf +requests. +. +. +.TP +.B \-t +Emit \*[tx] comment lines starting with +.RB \[lq] % \[rq] +indicating the current file and line number, +rather than +.B lf +requests for the same purpose. +. +. +.PP +If both +.B \-r +and +.B \-t +are given, +the last one specified controls. +. +. +.\" ==================================================================== +.SH "See also" +.\" ==================================================================== +. +.MR groff @MAN1EXT@ +. +. +.\" Clean up. +.rm tx +. +.\" Restore compatibility mode (for, e.g., Solaris 10/11). +.cp \n[*groff_soelim_1_man_C] +.do rr *groff_soelim_1_man_C +. +. +.\" Local Variables: +.\" fill-column: 72 +.\" mode: nroff +.\" End: +.\" vim: set filetype=groff textwidth=72: diff --git a/src/preproc/soelim/soelim.am b/src/preproc/soelim/soelim.am new file mode 100644 index 0000000..1aa7941 --- /dev/null +++ b/src/preproc/soelim/soelim.am @@ -0,0 +1,31 @@ +# Copyright (C) 2014-2020 Free Software Foundation, Inc. +# +# This file is part of groff. +# +# groff 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 3 of the License, or +# (at your option) any later version. +# +# groff 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, see <http://www.gnu.org/licenses/>. + +prefixexecbin_PROGRAMS += soelim +soelim_LDADD = libgroff.a $(LIBM) lib/libgnu.a +soelim_SOURCES = src/preproc/soelim/soelim.cpp +PREFIXMAN1 += src/preproc/soelim/soelim.1 +EXTRA_DIST += \ + src/preproc/soelim/TODO \ + src/preproc/soelim/soelim.1.man + + +# Local Variables: +# fill-column: 72 +# mode: makefile-automake +# End: +# vim: set autoindent filetype=automake textwidth=72: diff --git a/src/preproc/soelim/soelim.cpp b/src/preproc/soelim/soelim.cpp new file mode 100644 index 0000000..bafc5cd --- /dev/null +++ b/src/preproc/soelim/soelim.cpp @@ -0,0 +1,315 @@ +/* Copyright (C) 1989-2020 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff 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 3 of the License, or +(at your option) any later version. + +groff 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, see <http://www.gnu.org/licenses/>. */ + +#include "lib.h" + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> + +#include "errarg.h" +#include "error.h" +#include "stringclass.h" +#include "nonposix.h" +#include "searchpath.h" +#include "lf.h" + +// The include search path initially contains only the current directory. +static search_path include_search_path(0, 0, 0, 1); + +int compatible_flag = 0; +int raw_flag = 0; +int tex_flag = 0; + +extern "C" const char *Version_string; + +int do_file(const char *); + + +void usage(FILE *stream) +{ + fprintf(stream, "usage: %s [-Crt] [-I dir] [file ...]\n" + "usage: %s {-v | --version}\n" + "usage: %s --help\n", + program_name, program_name, program_name); +} + +int main(int argc, char **argv) +{ + program_name = argv[0]; + int opt; + static const struct option long_options[] = { + { "help", no_argument, 0, CHAR_MAX + 1 }, + { "version", no_argument, 0, 'v' }, + { NULL, 0, 0, 0 } + }; + while ((opt = getopt_long(argc, argv, "CI:rtv", long_options, NULL)) != EOF) + switch (opt) { + case 'v': + printf("GNU soelim (groff) version %s\n", Version_string); + exit(0); + break; + case 'C': + compatible_flag = 1; + break; + case 'I': + include_search_path.command_line_dir(optarg); + break; + case 'r': + raw_flag = 1; + break; + case 't': + tex_flag = 1; + break; + case CHAR_MAX + 1: // --help + usage(stdout); + exit(0); + break; + case '?': + usage(stderr); + exit(1); + break; + default: + assert(0); + } + int nbad = 0; + if (optind >= argc) + nbad += !do_file("-"); + else + for (int i = optind; i < argc; i++) + nbad += !do_file(argv[i]); + if (ferror(stdout) || fflush(stdout) < 0) + fatal("output error"); + return nbad != 0; +} + +void set_location() +{ + if (!raw_flag) { + if (!tex_flag) + printf(".lf %d %s\n", current_lineno, current_filename); + else + printf("%% file %s, line %d\n", current_filename, current_lineno); + } +} + +void do_so(const char *line) +{ + const char *p = line; + while (*p == ' ') + p++; + string filename; + int success = 1; + for (const char *q = p; + success && *q != '\0' && *q != '\n' && *q != ' '; + q++) + if (*q == '\\') { + switch (*++q) { + case 'e': + case '\\': + filename += '\\'; + break; + case ' ': + filename += ' '; + break; + default: + success = 0; + break; + } + } + else + filename += char(*q); + if (success && filename.length() > 0) { + filename += '\0'; + const char *fn = current_filename; + int ln = current_lineno; + current_lineno--; + if (do_file(filename.contents())) { + current_filename = fn; + current_lineno = ln; + set_location(); + return; + } + current_lineno++; + } + fputs(".so", stdout); + fputs(line, stdout); +} + +int do_file(const char *filename) +{ + char *file_name_in_path = 0; + FILE *fp = include_search_path.open_file_cautious(filename, + &file_name_in_path); + int err = errno; + string whole_filename(file_name_in_path ? file_name_in_path : filename); + whole_filename += '\0'; + free(file_name_in_path); + if (fp == 0) { + error("can't open '%1': %2", whole_filename.contents(), strerror(err)); + return 0; + } + normalize_for_lf(whole_filename); + current_filename = whole_filename.contents(); + current_lineno = 1; + set_location(); + enum { START, MIDDLE, HAD_DOT, HAD_s, HAD_so, HAD_l, HAD_lf } state = START; + for (;;) { + int c = getc(fp); + if (c == EOF) + break; + switch (state) { + case START: + if (c == '.') + state = HAD_DOT; + else { + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case MIDDLE: + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + break; + case HAD_DOT: + if (c == 's') + state = HAD_s; + else if (c == 'l') + state = HAD_l; + else { + putchar('.'); + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case HAD_s: + if (c == 'o') + state = HAD_so; + else { + putchar('.'); + putchar('s'); + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case HAD_so: + if (c == ' ' || c == '\n' || compatible_flag) { + string line; + for (; c != EOF && c != '\n'; c = getc(fp)) + line += c; + current_lineno++; + line += '\n'; + line += '\0'; + do_so(line.contents()); + state = START; + } + else { + fputs(".so", stdout); + putchar(c); + state = MIDDLE; + } + break; + case HAD_l: + if (c == 'f') + state = HAD_lf; + else { + putchar('.'); + putchar('l'); + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case HAD_lf: + if (c == ' ' || c == '\n' || compatible_flag) { + string line; + for (; c != EOF && c != '\n'; c = getc(fp)) + line += c; + current_lineno++; + line += '\n'; + line += '\0'; + interpret_lf_args(line.contents()); + printf(".lf%s", line.contents()); + state = START; + } + else { + fputs(".lf", stdout); + putchar(c); + state = MIDDLE; + } + break; + default: + assert(0); + } + } + switch (state) { + case HAD_DOT: + fputs(".\n", stdout); + break; + case HAD_l: + fputs(".l\n", stdout); + break; + case HAD_s: + fputs(".s\n", stdout); + break; + case HAD_lf: + fputs(".lf\n", stdout); + break; + case HAD_so: + fputs(".so\n", stdout); + break; + case MIDDLE: + putc('\n', stdout); + break; + case START: + break; + } + if (fp != stdin) + fclose(fp); + current_filename = 0; + return 1; +} + +// Local Variables: +// fill-column: 72 +// mode: C++ +// End: +// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: |