NetBSD/gnu/usr.bin/groff/pic/main.cc
1993-07-15 16:37:48 +00:00

612 lines
12 KiB
C++

// -*- C++ -*-
/* Copyright (C) 1989, 1990, 1991, 1992 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 2, 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 groff; see the file COPYING. If not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "pic.h"
extern int yyparse();
output *out;
int flyback_flag;
int zero_length_line_flag = 0;
// Non-zero means we're using a groff driver.
int driver_extension_flag = 1;
int compatible_flag = 0;
int command_char = '.'; // the character that introduces lines
// that should be passed through tranparently
static int lf_flag = 1; // non-zero if we should attempt to understand
// lines beginning with `.lf'
void do_file(const char *filename);
class top_input : public input {
FILE *fp;
int bol;
int eof;
int push_back[3];
int start_lineno;
public:
top_input(FILE *);
int get();
int peek();
int get_location(const char **, int *);
};
top_input::top_input(FILE *p) : fp(p), bol(1), eof(0)
{
push_back[0] = push_back[1] = push_back[2] = EOF;
start_lineno = current_lineno;
}
int top_input::get()
{
if (eof)
return EOF;
if (push_back[2] != EOF) {
int c = push_back[2];
push_back[2] = EOF;
return c;
}
else if (push_back[1] != EOF) {
int c = push_back[1];
push_back[1] = EOF;
return c;
}
else if (push_back[0] != EOF) {
int c = push_back[0];
push_back[0] = EOF;
return c;
}
int c = getc(fp);
while (illegal_input_char(c)) {
error("illegal input character code %1", int(c));
c = getc(fp);
bol = 0;
}
if (bol && c == '.') {
c = getc(fp);
if (c == 'P') {
c = getc(fp);
if (c == 'F' || c == 'E') {
int d = getc(fp);
if (d != EOF)
ungetc(d, fp);
if (d == EOF || d == ' ' || d == '\n' || compatible_flag) {
eof = 1;
flyback_flag = c == 'F';
return EOF;
}
push_back[0] = c;
push_back[1] = 'P';
return '.';
}
if (c == 'S') {
c = getc(fp);
if (c != EOF)
ungetc(c, fp);
if (c == EOF || c == ' ' || c == '\n' || compatible_flag) {
error("nested .PS");
eof = 1;
return EOF;
}
push_back[0] = 'S';
push_back[1] = 'P';
return '.';
}
if (c != EOF)
ungetc(c, fp);
push_back[0] = 'P';
return '.';
}
else {
if (c != EOF)
ungetc(c, fp);
return '.';
}
}
if (c == '\n') {
bol = 1;
current_lineno++;
return '\n';
}
bol = 0;
if (c == EOF) {
eof = 1;
error("end of file before .PE or .PF");
error_with_file_and_line(current_filename, start_lineno - 1,
".PS was here");
}
return c;
}
int top_input::peek()
{
if (eof)
return EOF;
if (push_back[2] != EOF)
return push_back[2];
if (push_back[1] != EOF)
return push_back[1];
if (push_back[0] != EOF)
return push_back[0];
int c = getc(fp);
while (illegal_input_char(c)) {
error("illegal input character code %1", int(c));
c = getc(fp);
bol = 0;
}
if (bol && c == '.') {
c = getc(fp);
if (c == 'P') {
c = getc(fp);
if (c == 'F' || c == 'E') {
int d = getc(fp);
if (d != EOF)
ungetc(d, fp);
if (d == EOF || d == ' ' || d == '\n' || compatible_flag) {
eof = 1;
flyback_flag = c == 'F';
return EOF;
}
push_back[0] = c;
push_back[1] = 'P';
push_back[2] = '.';
return '.';
}
if (c == 'S') {
c = getc(fp);
if (c != EOF)
ungetc(c, fp);
if (c == EOF || c == ' ' || c == '\n' || compatible_flag) {
error("nested .PS");
eof = 1;
return EOF;
}
push_back[0] = 'S';
push_back[1] = 'P';
push_back[2] = '.';
return '.';
}
if (c != EOF)
ungetc(c, fp);
push_back[0] = 'P';
push_back[1] = '.';
return '.';
}
else {
if (c != EOF)
ungetc(c, fp);
push_back[0] = '.';
return '.';
}
}
if (c != EOF)
ungetc(c, fp);
if (c == '\n')
return '\n';
return c;
}
int top_input::get_location(const char **filenamep, int *linenop)
{
*filenamep = current_filename;
*linenop = current_lineno;
return 1;
}
void do_picture(FILE *fp)
{
flyback_flag = 0;
int c;
while ((c = getc(fp)) == ' ')
;
if (c == '<') {
string filename;
while ((c = getc(fp)) == ' ')
;
while (c != EOF && c != ' ' && c != '\n') {
filename += char(c);
c = getc(fp);
}
if (c == ' ') {
do {
c = getc(fp);
} while (c != EOF && c != '\n');
}
if (c == '\n')
current_lineno++;
if (filename.length() == 0)
error("missing filename after `<'");
else {
filename += '\0';
const char *old_filename = current_filename;
int old_lineno = current_lineno;
// filenames must be permanent
do_file(strsave(filename.contents()));
current_filename = old_filename;
current_lineno = old_lineno;
}
out->set_location(current_filename, current_lineno);
}
else {
out->set_location(current_filename, current_lineno);
string start_line;
while (c != EOF) {
if (c == '\n') {
current_lineno++;
break;
}
start_line += c;
c = getc(fp);
}
if (c == EOF)
return;
start_line += '\0';
double wid, ht;
switch (sscanf(&start_line[0], "%lf %lf", &wid, &ht)) {
case 1:
ht = 0.0;
break;
case 2:
break;
default:
ht = wid = 0.0;
break;
}
out->set_desired_width_height(wid, ht);
out->set_args(start_line.contents());
lex_init(new top_input(fp));
if (yyparse())
lex_error("giving up on this picture");
parse_cleanup();
lex_cleanup();
// skip the rest of the .PF/.PE line
while ((c = getc(fp)) != EOF && c != '\n')
;
if (c == '\n')
current_lineno++;
out->set_location(current_filename, current_lineno);
}
}
void do_file(const char *filename)
{
FILE *fp;
if (strcmp(filename, "-") == 0)
fp = stdin;
else {
errno = 0;
fp = fopen(filename, "r");
if (fp == 0)
fatal("can't open `%1': %2", filename, strerror(errno));
}
out->set_location(filename, 1);
current_filename = filename;
current_lineno = 1;
enum { START, MIDDLE, HAD_DOT, HAD_P, HAD_PS, 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 == 'P')
state = HAD_P;
else if (lf_flag && c == 'l')
state = HAD_l;
else {
putchar('.');
putchar(c);
if (c == '\n') {
current_lineno++;
state = START;
}
else
state = MIDDLE;
}
break;
case HAD_P:
if (c == 'S')
state = HAD_PS;
else {
putchar('.');
putchar('P');
putchar(c);
if (c == '\n') {
current_lineno++;
state = START;
}
else
state = MIDDLE;
}
break;
case HAD_PS:
if (c == ' ' || c == '\n' || compatible_flag) {
ungetc(c, fp);
do_picture(fp);
state = START;
}
else {
fputs(".PS", 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;
while (c != EOF) {
line += c;
if (c == '\n') {
current_lineno++;
break;
}
c = getc(fp);
}
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 START:
break;
case MIDDLE:
putchar('\n');
break;
case HAD_DOT:
fputs(".\n", stdout);
break;
case HAD_P:
fputs(".P\n", stdout);
break;
case HAD_PS:
fputs(".PS\n", stdout);
break;
case HAD_l:
fputs(".l\n", stdout);
break;
case HAD_lf:
fputs(".lf\n", stdout);
break;
}
if (fp != stdin)
fclose(fp);
}
#ifdef FIG_SUPPORT
void do_whole_file(const char *filename)
{
// Do not set current_filename.
FILE *fp;
if (strcmp(filename, "-") == 0)
fp = stdin;
else {
errno = 0;
fp = fopen(filename, "r");
if (fp == 0)
fatal("can't open `%1': %2", filename, strerror(errno));
}
lex_init(new file_input(fp, filename));
yyparse();
parse_cleanup();
lex_cleanup();
}
#endif
void usage()
{
fprintf(stderr, "usage: %s [ -nvC ] [ filename ... ]\n", program_name);
#ifdef TEX_SUPPORT
fprintf(stderr, " %s -t [ -cvzC ] [ filename ... ]\n", program_name);
#endif
#ifdef FIG_SUPPORT
fprintf(stderr, " %s -f [ -v ] [ filename ]\n", program_name);
#endif
exit(1);
}
#ifdef __MSDOS__
static char *fix_program_name(char *arg, char *dflt)
{
if (!arg)
return dflt;
char *prog = strchr(arg, '\0');
for (;;) {
if (prog == arg)
break;
--prog;
if (strchr("\\/:", *prog)) {
prog++;
break;
}
}
char *ext = strchr(prog, '.');
if (ext)
*ext = '\0';
for (char *p = prog; *p; p++)
if ('A' <= *p && *p <= 'Z')
*p = 'a' + (*p - 'A');
return prog;
}
#endif /* __MSDOS__ */
int main(int argc, char **argv)
{
#ifdef __MSDOS__
argv[0] = fix_program_name(argv[0], "pic");
#endif /* __MSDOS__ */
program_name = argv[0];
static char stderr_buf[BUFSIZ];
setbuf(stderr, stderr_buf);
int opt;
#ifdef TEX_SUPPORT
int tex_flag = 0;
int tpic_flag = 0;
#endif
#ifdef FIG_SUPPORT
int whole_file_flag = 0;
int fig_flag = 0;
#endif
while ((opt = getopt(argc, argv, "T:CDtcvnxzpf")) != EOF)
switch (opt) {
case 'C':
compatible_flag = 1;
break;
case 'D':
case 'T':
break;
case 'f':
#ifdef FIG_SUPPORT
whole_file_flag++;
fig_flag++;
#else
fatal("fig support not included");
#endif
break;
case 'n':
driver_extension_flag = 0;
break;
case 'p':
case 'x':
warning("-%1 option is obsolete", char(opt));
break;
case 't':
#ifdef TEX_SUPPORT
tex_flag++;
#else
fatal("TeX support not included");
#endif
break;
case 'c':
#ifdef TEX_SUPPORT
tpic_flag++;
#else
fatal("TeX support not included");
#endif
break;
case 'v':
{
extern const char *version_string;
fprintf(stderr, "GNU pic version %s\n", version_string);
fflush(stderr);
break;
}
case 'z':
// zero length lines will be printed as dots
zero_length_line_flag++;
break;
case '?':
usage();
break;
default:
assert(0);
}
parse_init();
#ifdef TEX_SUPPORT
if (tpic_flag) {
out = make_tpic_output();
lf_flag = 0;
}
else if (tex_flag) {
out = make_tex_output();
command_char = '\\';
lf_flag = 0;
}
else
#endif
#ifdef FIG_SUPPORT
if (fig_flag)
out = make_fig_output();
else
#endif
out = make_troff_output();
#ifdef FIG_SUPPORT
if (whole_file_flag) {
if (optind >= argc)
do_whole_file("-");
else if (argc - optind > 1)
usage();
else
do_whole_file(argv[optind]);
}
else {
#endif
if (optind >= argc)
do_file("-");
else
for (int i = optind; i < argc; i++)
do_file(argv[i]);
#ifdef FIG_SUPPORT
}
#endif
delete out;
if (ferror(stdout) || fflush(stdout) < 0)
fatal("output error");
exit(0);
}