2 * cawf - a C version of Henry Spencer's awf(1), the Amazingly
3 * Workable (text) Formatter
5 * V. Abell, Purdue University Computing Center
9 * Copyright (c) 1991 Purdue University Research Foundation,
10 * West Lafayette, Indiana 47907. All rights reserved.
12 * Written by Victor A. Abell <abe@mace.cc.purdue.edu>, Purdue
13 * University Computing Center. Not derived from licensed software;
14 * derived from awf(1) by Henry Spencer of the University of Toronto.
16 * Permission is granted to anyone to use this software for any
17 * purpose on any computer system, and to alter it and redistribute
18 * it freely, subject to the following restrictions:
20 * 1. The author is not responsible for any consequences of use of
21 * this software, even if they arise from flaws in it.
23 * 2. The origin of this software must not be misrepresented, either
24 * by explicit claim or by omission. Credits must appear in the
27 * 3. Altered versions must be plainly marked as such, and must not
28 * be misrepresented as being the original software. Credits must
29 * appear in the documentation.
31 * 4. This notice may not be removed or altered.
34 static char Version
[] = "4.0";
44 #include <sys\types.h>
49 int main(int argc
, char *argv
[]) {
50 char *ep
; /* environment pointer */
51 int fff
= 0; /* final form feed status */
52 char **files
; /* file names */
53 int help
= 0; /* help status */
54 int i
; /* temporary index */
55 size_t l
; /* length */
56 char *lib
= CAWFLIB
; /* library path */
57 int libl
; /* library path length */
58 int mac
= 0; /* macro specification status */
59 int nf
= 0; /* number of files */
60 char *np
; /* name pointer */
61 int pc
; /* prolog count */
62 struct stat sbuf
; /* stat buffer */
66 if ((Pname
= strrchr(argv
[0], '\\')) != NULL
)
68 else if ((Pname
= strrchr(argv
[0], '/')) != NULL
)
73 * Set error file stream pointer.
79 if ((np
= getenv("CAWFLIB")) != NULL
)
83 * Get device file name.
85 for (ep
= getenv("TERM");; ep
= NULL
) {
86 if (ep
== NULL
|| *ep
== '\0')
88 l
= libl
+ 1 + strlen(ep
) + strlen(".dev") + 1;
89 if ((np
= malloc(l
)) == NULL
)
91 " no string space for device file: ", ep
);
92 (void) sprintf(np
, "%s/%s.dev", lib
, ep
);
93 if (stat(np
, &sbuf
) == 0)
95 if (strcmp(ep
, "dumb") == 0)
96 Error(FATAL
, NOLINE
, " no dumb.dev file in ", lib
);
99 if ((files
= malloc((argc
+ 2) * sizeof(files
[0]))) == NULL
)
100 Error(FATAL
, NOLINE
, " no space for file list",
104 * Get common text file name.
106 l
= libl
+ 1 + strlen("common") + 1;
107 if ((np
= malloc(l
)) == NULL
)
108 Error(FATAL
, NOLINE
, " no string space for common file name",
110 (void) sprintf(np
, "%s/common", lib
);
115 while ((i
= getopt(argc
, argv
, "c:d:ef:hm:")) != EOF
) {
118 * -c<device_configuration_file_path>>
124 * -d<output_device_name> -- define output device name
126 * The default output device name is NORMAL -- i.e., a device that
127 * does bold face with backspace and overprinting and italic face with
128 * underscore. NORMAL is usually a terminal device.
130 * There is a built-in device, named ANSI, that does bold face with
131 * the ANSI shadow mode and italic face with the ANSI underscore mode.
132 * ANSI is normally a terminal device that supports the ANSI shadow
133 * and underscore modes.
135 * There is a built-in output device, named NONE, that does nothing
136 * at all for the bold or italic faces. This is usually a terminal
139 * All other device names must match a stanza in the device
140 * configuration file.
146 * -e -- eject: issue final form feed
152 * -f<output_device_font_name> -- define font name for the output
153 * device (from device configuration
160 * -h -- display help (usage)
166 * -m<macro_file_name>
168 * Special support is provided for -man, -me and -ms.
173 "multiple macro file declaration",
177 l
= libl
+ 2 + strlen(optarg
) + strlen(".mac") + 1;
178 if ((np
= malloc(l
)) == NULL
)
179 Error(FATAL
, NOLINE
, " no string space for ",
181 (void) sprintf(np
, "%s/m%s.mac", lib
, optarg
);
183 if (strcmp(optarg
, "an") == 0)
185 else if (strcmp(optarg
, "s") == 0
186 || strcmp(optarg
, "e") == 0)
191 * Option not recognized by getopt().
200 (void) fprintf(stderr
,
201 "%s %s usage: [-c<c>] [-d<d>] [-e] [-f<f>] [-h] [-m<m>] file...\n",
203 (void) fprintf(stderr
,
204 "\t-c<c> <c> is the device configuration file path\n");
205 (void) fprintf(stderr
,
206 "\t-d<d> <d> is the output device name\n");
207 (void) fprintf(stderr
,
208 "\t (default = NORMAL, using \\b for bold and italic)\n");
209 (void) fprintf(stderr
,
210 "\t (built-ins = ANSI, NONE and NORMAL)\n");
211 (void) fprintf(stderr
,
212 "\t-e issue eject after last page\n");
213 (void) fprintf(stderr
,
214 "\t-f<f> <f> is the output device font name\n");
215 (void) fprintf(stderr
,
216 "\t-h display help (this output)\n");
217 (void) fprintf(stderr
,
218 "\t-m<m> m<m> is the macro file name\n");
219 (void) fprintf(stderr
,
220 "\tfile ... source file names\n");
226 * No macroes - enable Bold, Italic, Roman and Courier fonts.
228 for (i
= 0; Fcode
[i
].nm
; i
++) {
229 switch (Fcode
[i
].nm
) {
234 Fcode
[i
].status
= '1';
239 * Add user-supplied file names.
242 if (optind
>= argc
) {
243 files
[nf
++] = NULL
; /* STDIN */
245 while (optind
< argc
)
246 files
[nf
++] = argv
[optind
++];
249 * Make sure all input files are accessible.
251 for (i
= 0; i
< nf
; i
++) {
252 if (files
[i
] != NULL
) {
253 if (stat(files
[i
], &sbuf
) != 0)
254 Error(WARN
, NOLINE
, " can't find ", files
[i
]);
260 * Miscellaneous initialization.
264 if (Pat
[i
].re
== NULL
)
266 if ((Pat
[i
].pat
= regcomp(Pat
[i
].re
)) == NULL
)
267 Error(WARN
, NOLINE
, Pat
[i
].re
, " regcomp failure");
269 if ((i
= Findscale((int)'n', 0.0, 0)) < 0)
270 Error(WARN
, NOLINE
, " can't find Scale['n']", NULL
);
271 Scalen
= Scale
[i
].val
;
272 if ((i
= Findscale((int)'u', 0.0, 0)) < 0)
273 Error(WARN
, NOLINE
, " can't find Scale['u']", NULL
);
274 Scaleu
= Scale
[i
].val
;
275 if ((i
= Findscale((int)'v', 0.0, 0)) < 0)
276 Error(WARN
, NOLINE
, " can't find Scale['v']", NULL
);
277 Scalev
= Scale
[i
].val
;
278 (void) Findstr((unsigned char *)"CH", (unsigned char *)"= % -", 1);
279 Cont
= Newstr((unsigned char *)" ");
281 if ((Trtbl
= (unsigned char *)malloc(256)) == NULL
)
282 Error(WARN
, NOLINE
, " can't allocate translate table space",
286 for (i
= 1; i
< 256; i
++)
287 Trtbl
[i
] = (unsigned char) i
;
292 * Here begins pass1 of awf - reading input lines and expanding macros.
299 for (i
= 0; i
< Fstr
.il
; i
++) {
300 Charput((int)Fstr
.i
[i
]);
303 Macro((unsigned char *)".^x");
304 Macro((unsigned char *)".^b");
305 Macro((unsigned char *)".^# 1 <prolog>");
309 for (i
= 0; i
< nf
; i
++) {
311 if (files
[i
] == NULL
) {
316 if ((Ifs
= fopen(files
[i
], "r")) == NULL
)
318 if ((Ifs
= fopen(files
[i
], "rt")) == NULL
)
320 Error(FATAL
, NOLINE
, " can't open ", files
[i
]);
324 (void) sprintf((char *)Line
, ".^# 1 %s", np
);
330 while (fgets((char *)Line
, MAXLINE
, Ifs
) != NULL
) {
332 if ((np
= strrchr((char *)Line
, '\n')) != NULL
)
335 Line
[MAXLINE
-1] = '\0';
339 Macro((unsigned char *)".^e");
344 Inname
= Inn_stk
[Fsp
-1];
346 Ifs
= Ifs_stk
[Fsp
-1];
358 * Macro(inp) - process a possible macro statement
359 * pass non-macros and macros alike to pass 2
362 void Macro(unsigned char *inp
) { /* possible macro statement pointer */
363 unsigned char c
[2]; /* characters */
364 int endm
; /* end of macro status */
365 FILE *fs
; /* temporary file stream */
366 int i
, j
, k
; /* temporary indexes */
367 int mx
; /* Macrotab[] index */
368 int req
; /* request character status */
369 unsigned char *s1
, *s2
; /* temporary string pointers */
375 req
= (*inp
== '.' || *inp
== '\'') ? 1 : 0;
377 * Check for file name designator.
379 if (req
&& inp
[1] == '^' && inp
[2] == '#') {
381 Inname
= Field(3, inp
, 1);
387 * Check for source command - "^[.']so".
389 if (req
&& inp
[1] == 's' && inp
[2] == 'o') {
390 if ((s1
= Field(2, inp
, 1)) == NULL
) {
391 Error(WARN
, LINE
, " no file specified", NULL
);
394 if ((fs
= fopen((char *)s1
, "r")) == NULL
) {
395 Error(WARN
, LINE
, " can't open", NULL
);
398 if (Fsp
>= MAXFSTK
) {
400 Error(WARN
, LINE
, " nesting too deep", NULL
);
405 Inn_stk
[Fsp
] = Inname
;
415 if (req
&& inp
[1] == 'i' && inp
[2] == 'g') {
416 while (fgets((char *)inp
, MAXLINE
, Ifs
) != NULL
) {
418 if (inp
[0] == '.' && inp
[1] == '.') break;
423 * Check for start of macro definition.
425 if (req
&& inp
[1] == 'd' && inp
[2] == 'e') {
426 if (inp
[3] != ' ' || inp
[4] == '\0') {
427 Error(WARN
, LINE
, " illegal macro definition", NULL
);
432 Curmx
= Findmacro(c
, 1);
436 * Check for macro text. Remove double backslashes.
438 if (req
&& (inp
[1] == '\0' || (inp
[2] == '\0' && inp
[0] == inp
[1])))
442 if (Curmx
>= 0 && !endm
) {
444 Error(FATAL
, LINE
, " out of macro text space", NULL
);
445 if ((s1
= (unsigned char *)strchr((char *)inp
, '\\')) == NULL
)
446 Macrotxt
[Mtx
] = Newstr(inp
);
448 for (s1
= Pass1ln
, s2
= inp
;; s1
++) {
449 if ((*s1
= *s2
++) == '\0')
451 if (*s1
== '\\' && *s2
== '\\')
454 Macrotxt
[Mtx
] = Newstr(Pass1ln
);
456 if (Macrotab
[Curmx
].bx
== -1)
457 Macrotab
[Curmx
].bx
= Mtx
;
459 Macrotab
[Curmx
].ct
++;
463 * Check for end of macro.
465 if (Curmx
>= 0 && endm
) {
467 (void) sprintf((char *)Pass1ln
, ".^# %d %s", NR
, Inname
);
472 * Check for conditionals and macro expansions.
475 && (((mx
= Findmacro(inp
+1, 0)) != -1) || regexec(Pat
[0].pat
, inp
))) {
480 * None of the above: forward the line.