summaryrefslogtreecommitdiffstats
path: root/src/preproc/soelim
diff options
context:
space:
mode:
Diffstat (limited to 'src/preproc/soelim')
-rw-r--r--src/preproc/soelim/TODO1
-rw-r--r--src/preproc/soelim/soelim.1.man456
-rw-r--r--src/preproc/soelim/soelim.am31
-rw-r--r--src/preproc/soelim/soelim.cpp315
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: