2
static char sccsid[] = "@(#)lgrind.c 3.66 (MPi) 5/10/99";
4
"$Id: lgrind.c,v 1.14 2000/12/27 21:42:42 mike Exp $";
8
* Copyright %%\copyright%% 1980 The Regents of the University of California.
11
* Redistribution and use in source and binary forms, with or without
12
* modification, are permitted provided that the following conditions
14
* 1. Redistributions of source code must retain the above copyright
15
* notice, this list of conditions and the following disclaimer.
16
* 2. Redistributions in binary form must reproduce the above copyright
17
* notice, this list of conditions and the following disclaimer in the
18
* documentation and/or other materials provided with the distribution.
19
* 3. All advertising materials mentioning features or use of this software
20
* must display the following acknowledgement:
21
* This product includes software developed by the University of
22
* California, Berkeley and its contributors.
23
* 4. Neither the name of the University nor the names of its contributors
24
* may be used to endorse or promote products derived from this software
25
* without specific prior written permission.
27
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41
/* lgrind --- general purpose "pretty printer" for use with %%\LaTeX%%.
42
* A %%\LaTeX%% version of tgrind, which is a %%\TeX%% version
43
* of vgrind. Used to "grind nice program listings."
45
* Copyright %%\copyright%% 1985 by Van Jacobson, Lawrence Berkeley Laboratory
46
* This program may be freely used and copied but %%{\bf may not be sold}%%
47
* without the author's %%{\bf written permission}%%. This notice must remain
48
* in any copy or derivative.
50
* This program is an adaptation of "vfontedpr" v4.2 (12/11/84) from
51
* the 4.2bsd Unix distribution. Vfontedpr was written by Dave
52
* Presotto (based on an earlier program of the same name written by
55
* I would welcome comments, enhancements, bug fixes, etc. Please
57
* van@@lbl-rtsg.arpa (from arpanet, milnet, csnet, etc.)
58
* ..!ucbvax!lbl-csam!van (from Usenet/UUCP)
61
/*%%\par{\bf Thank you very much, Van. Due to the ``may not be sold'' clause%%*/
62
/*%%this program has become non-free. And since the author of this licence%%*/
63
/*%%cannot be contacted (address too old, apparently newer ones exist, but%%*/
64
/*%%no answers from them), this cannot be changed.}\par%%*/
66
/* Copyright %%\copyright%% 1995-99 by Michael Piefel
70
* 10 Feb 85 Van Written.
71
* 29 Mar 85 Chris Torek (chris@@maryland): Bug fixes for %|~|% and
72
* %|^L|% output. Most cpu-time eaters recoded
73
* to improve efficiency.
74
* 30 Mar 85 Chris & Van Fixed %|\C|% & %|\S|% (comment & string start
75
* indicators to really appear at the start of
76
* comments & strings. Changes for speeded-up
78
* 8 Oct 87 JSL Modified so as to compile on VMS. %|-i|% option
79
* Jan 88 JSL %|-e|% option for embedded code.
80
* Sep 91 George V Reilly Reformated and cleaned up code, including
81
* naughtiness with @NULL@. Added %|@|%,
82
* %|%%|%, %|%$|%, and %%\tt \%|%% features.
83
* Also the %|%<|%, %|%!|%, and %|%#|% features.
84
* Oct 94 Matthias Eckermann (%|ud311aa@@sunmail.lrz-muenchen.de|%)
85
* fixed a little bug, added: Internal_Help,
86
* Sep 95 Michael Piefel Modified for %%\LaTeXe%%
87
* Feb 96 Michael Piefel Restructured into ANSI C
88
* May 99 Michael Piefel Made it Y2K compliant
89
* Oct 99 Michael Piefel Space runs make tabstops
90
* Dec 99 Michael Piefel Version option, space allowed after '-l'
99
/* One of the following two (depending on your system) */
101
/* #include <io.h> */
102
#include "lgrindef.h"
106
# include <sys/types.h>
107
# include <sys/stat.h>
116
#define NOTCODE 0 /* the three states of @incode@ */
120
#define PNAMELEN 80 /* length of a function/procedure name */
121
#define PSMAX 20 /* size of procedure name stacking */
127
# define DEFSFILE "/usr/lib/texmf/tex/latex/lgrind/lgrindef"
132
# define DEFSFILE "TEX$INPUTS:lgrindef.src"
136
# define HELPOPTION "-?"
138
# define HELPOPTION "--help"
141
typedef struct varsubst {
144
struct varsubst *next;
147
varsubst *varsubstlist;
149
/* forward declarations */
151
int getredirection(int argc, char **argv);
153
void Internal_Help();
154
void Internal_Help_Language_List();
155
void readfile(FILE *);
157
boolean putKcp(char *, char *, boolean);
158
void putVcp(char *, char *);
160
boolean (*isproc)(char *);
161
boolean isprocNorm(char *);
162
boolean isprocC(char *);
164
void parsechartab(void);
165
varsubst *parsevartab(char *);
166
void parsepreamble(char *);
167
void setpreambles(void);
168
void printpreamble(char *);
169
void writeDefsfileInExe(char *, char *);
170
int substvarname(char **, int);
173
* The state variables
176
boolean incomm; /* in a comment of the primary type */
177
boolean instr; /* in a string constant */
178
boolean inchr; /* in a character constant */
179
int incode; /* in program text within a comment */
180
int latexcode; /* in program text within %%\LaTeX%% */
181
int latex_tt; /* in %|\texttt|% text within %%\LaTeX%% */
182
boolean use_tt; /* use %|\texttt|% everywhere */
183
boolean do_at; /* pay attention to %|@|%s in %%\LaTeX%% */
184
boolean do_tt; /* pay attention to %|||%s in %%\LaTeX%% */
185
boolean nokeyw = FALSE; /* no keywords being flagged */
186
boolean prccont; /* continue last procedure */
187
boolean code_cmnts = TRUE; /* Treat %|@|%, etc specially in comments */
188
boolean code_latex = TRUE; /* Treat %|@|%, etc specially in %%\LaTeX%% */
189
int lastout; /* (extended) last character to outchar */
190
int comtype; /* type of comment */
191
int psptr; /* the stack index of the current procedure */
192
char pstack[PSMAX][PNAMELEN+1]; /* the procedure name stack */
193
int plstack[PSMAX]; /* the procedure nesting level stack */
194
int blklevel; /* current nesting level */
195
int reclevel; /* current @record@ nesting level */
196
int procedures_pending; /* proc starts w/o block starts */
197
char fname[200]=""; /* File being read */
198
int lineno; /* Line number in that file */
199
int linenocode; /* Line number of the source code worked on */
200
char pname[BUFFERSIZE+1]; /* Current procedure name */
203
* The language specific globals
205
char defsbuf[200]="DeFsBuF"DEFSFILE;
206
char *defsfile=&defsbuf[7]; /* name of language definitions file */
207
char language[PNAMELEN]="mPi"; /* the language indicator */
208
char the_buf[BUFFERSIZE+1]; /* general purpose buffer */
209
char *buf = the_buf + 1; /* @buf-1@ must be valid */
210
char *strings; /* store the keywords */
211
char *defs; /* language definitions from lgrindef */
212
char preamble[BUFFERSIZE/4]; /* first preamble */
213
char postamble[BUFFERSIZE/4]; /* first preamble */
214
char preamble2[BUFFERSIZE/4]; /* file preamble */
215
char config[BUFFERSIZE/4]; /* redefinitions within lgrind-environment */
216
char chartab[BUFFERSIZE/4]; /* buffer for chartab modifications */
217
char *l_keywds[BUFFERSIZE/2]; /* keyword table address */
218
char *l_prcbeg; /* regular expr for procedure begin */
219
char *l_noproc; /* regexp for lines with NO procedure begin */
220
char *l_combeg; /* regexp introducing a comment */
221
char *l_comend; /* regexp ending a comment */
222
char *l_acmbeg; /* regexp introducing a comment */
223
char *l_acmend; /* regexp ending a comment */
224
char *l_blkbeg; /* regexp beginning of a block */
225
char *l_blkend; /* regexp ending a block */
226
char *l_strbeg; /* regexp starting string constant */
227
char *l_strend; /* regexp ending string constant */
228
char *l_chrbeg; /* regexp starting character constant */
229
char *l_chrend; /* regexp ending character constant */
230
char *l_cdebeg; /* regexp starting prog text within comment */
231
char s_cdebeg[BUFFERSIZE/8]; /* actual string corresponding to @l_cdebeg@ */
232
char *l_cdeend; /* regexp ending prog text within comment */
233
char *l_texbeg; /* regexp starting %%\TeX%% text in comment */
234
char *l_texend; /* regexp ending %%\TeX%% text in comment */
235
char *l_txmbeg; /* regexp starting %%\TeX%% math in comment */
236
char *l_txmend; /* regexp ending %%\TeX%% math in comment */
237
char *l_tt_beg; /* regexp starting verbatim text in comment */
238
char *l_tt_end; /* regexp ending typewriter text in comment */
239
char *l_at; /* regexp for %|@|% in %%\LaTeX%% text */
240
char *l_tt; /* regexp for %|||% in %%\LaTeX%% text */
241
char *l_pc; /* regexp for %|%|% in %%\LaTeX%% text */
242
char *l_id; /* string containing valid identifier chars */
243
char *l_record; /* start of lexical block outside functions */
244
char l_escape; /* character used to escape characters */
245
boolean l_toplex; /* procedures only defined at top lex level */
246
boolean l_onecase; /* upper & lower case equivalent */
247
boolean l_cfunctions; /* special for C function search */
248
boolean embed = FALSE; /* -e seen --- do embedded code */
249
boolean code = TRUE; /* Looking at code */
250
int TabWidth = 8; /* Default width of a tab */
253
* global variables also used by expmatch
256
extern boolean _escaped; /* if last character was an escape */
257
extern char *_sstart; /* start of the current string */
259
extern int (*re_strncmp)(const char*, const char*, size_t);
260
/* function to do string compares */
262
int main(int argc, char **argv)
265
boolean hseen = FALSE; /* -h seen */
266
char *hstring=""; /* header string to use */
267
char *programname; /* the zeroeth argument */
268
boolean iseen = FALSE; /* -i seen */
271
defs=(char*)malloc(2*BUFFERSIZE);
272
strings=(char*)malloc(2*BUFFERSIZE);
273
argc = getredirection(argc, argv);
275
/* abuse of programname */
276
programname=getenv("LGRINDEF");
277
if (programname) strcpy(defsfile, programname);
288
struct tm modtime = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
292
if (!strcmp(argv[0], HELPOPTION)) {
297
if (!strcmp(argv[0], "-s")) {
298
Internal_Help_Language_List();
302
if (!strcmp(argv[0], "--version")) {
303
printf("LGrind %s\n", VERSION);
309
if (!strcmp(argv[0], "-h")) {
323
/* take input from the standard place */
324
if (!strcmp(argv[0], "-")) {
329
/* put output to non-standard place */
330
if (!strcmp(argv[0], "-o")) {
331
if (freopen(argv[1], "w", stdout) == NULL) {
340
/* Process embedded text */
341
if (!strcmp(argv[0], "-e")) {
347
/* format for inclusion */
348
if (!strcmp(argv[0], "-i")) {
354
/* indicate no keywords */
355
if (!strcmp(argv[0], "-n")) {
361
/* Don't treat %|@|%, etc. specially in comments */
362
if (!strcmp(argv[0], "-c")) {
368
/* Do treat %|@|%, etc. specially in comments */
369
if (!strcmp(argv[0], "+c")) {
375
/* Don't treat %|@|%, etc. specially in
376
* %%\LaTeX%% text (@embed@ only) */
377
if (!strcmp(argv[0], "-a")) {
383
/* Do treat %|@|%, etc. specially in
384
* %%\LaTeX%% text (@embed@ only) */
385
if (!strcmp(argv[0], "+a")) {
391
/* Use %|\texttt|% for all fonts */
392
if (!strcmp(argv[0], "-t")) {
393
sscanf(argv[1], "%d", &TabWidth);
399
/* specify the language */
400
if (!strncmp(argv[0], "-l", 2)) {
401
if (strlen(argv[0])==2) {
402
strcpy(language, argv[1]);
406
strcpy(language, argv[0]+2);
412
/* specify the language description file for permanent use */
413
if (!strcmp(argv[0], "-d!")) {
414
writeDefsfileInExe(programname, argv[1]);
418
/* specify the language description file */
419
if (!strncmp(argv[0], "-d", 2)) { /* TODO strncmp? */
420
strcpy(defsfile, argv[1]);
426
/* specify the file containing variable substitutions */
427
if (!strcmp(argv[0], "-v")) {
428
varsubstlist=parsevartab(argv[1]);
434
/* open the file for input */
435
if (freopen(argv[0], "r", stdin) == NULL) {
440
strcpy(fname, argv[0]);
448
/* get config data from %|lgrindef|% */
449
if (tgetent(buf, "firstpreamble", defsfile)>0)
450
parsepreamble(preamble);
452
if (tgetent(buf, "postamble", defsfile)>0)
453
parsepreamble(postamble);
455
if (tgetent(buf, "filepreamble", defsfile)>0)
456
parsepreamble(preamble2);
458
if (tgetent(buf, "configuration", defsfile)>0)
459
parsepreamble(config);
461
if (tgetent(buf, "chartab", defsfile)>0)
464
if (iseen && embed) {
465
fprintf(stderr, "-i makes no sense with -e; -e ignored\n");
469
if (!iseen && !embed) {
470
if (files_done == 0) printpreamble(preamble);
471
printpreamble(preamble2);
472
printf("\\begin{lgrind}\n");
473
printpreamble(config);
474
/* In case \BGfont was modified in config */
475
printf("\\BGfont\n");
480
printf("%% This document was generated automagically by lgrind. DO NOT EDIT.\n\n");
483
printf("%% Remember to use the lgrind style\n\n");
485
if (hseen) putstr(hstring);
491
/* initialize the program */
493
incomm = instr = inchr = _escaped = FALSE;
494
incode = latexcode = latex_tt = NOTCODE;
495
do_at = do_tt = code_latex;
498
for (psptr = 0; psptr < PSMAX; psptr++) {
499
pstack[psptr][0] = '\0';
505
strcpy(fname, "stdin");
508
modtime = *localtime(&tm);
512
modtime = *localtime(&stbuf.st_mtime);
518
printf("}{%d}{%d}{%d}{%d:%02d}{%ld}\n", modtime.tm_year+1900, modtime.tm_mon+1,
519
modtime.tm_mday, modtime.tm_hour, modtime.tm_min, stbuf.st_size);
526
if (!iseen && !embed) {
527
printf("\\end{lgrind}\n");
531
fprintf(stderr, "Reached EOF within code in file %s\n", fname);
534
if (!iseen && !embed) {
535
printpreamble(postamble);
542
* @readfile()@ --- read and process a file
544
void readfile(FILE *fp)
547
boolean LGinline=FALSE; /* Doing %( -- %) */
548
char term; /* ']' or ')' */
549
char *atptr; /* start of %|@|% within %%\LaTeX%% text */
550
char *atendptr; /* end of %|@|% within %%\LaTeX%% text */
551
char *pcptr; /* start of %|%|% within %%\LaTeX%% text */
552
char *pcendptr; /* end of %|%|% within %%\LaTeX%% text */
553
char temp[BUFFERSIZE];
557
while (fgets(buf, BUFFERSIZE, fp) != NULL) {
558
cp = &buf[strlen(buf)-1];
559
if (*cp != '\n') {*(++cp) = '\n'; *(++cp) = '\0'; }
566
(buf[1] == '[' || buf[1] == '('
567
|| buf[1] == '#' || buf[1] == '<' || buf[1] == '!'
568
|| buf[1] == '@' || buf[1] == '|')) {
570
for (cp = buf + 2; *cp == ' ' || *cp == '\t'; cp++)
573
/* Change the language */
576
fprintf(stderr, "no language seen after %%# in file\
577
%s at line %d\n", fname, lineno);
579
cp[strlen(cp) - 1] = '\0'; /* nuke the @'\n'@ */
580
while ((*cp==' ') || (*cp=='\t')) cp++;
581
strcpy(language, cp);
582
printf("%% switching to %s\n", language);
588
/* Turn %|@|% or %|||% processing within %%\LaTeX%%
591
if (buf[1] == '@' || buf[1] == '|') {
593
fprintf(stderr, "no setting seen after %%%c in file\
594
%s at line %d\n", buf[1], fname, lineno);
596
int flag = (*cp == '1' || *cp == '+');
607
/* take input from another file or from a shell command */
608
if (buf[1] == '<' || buf[1] == '!') {
610
char source = buf[1];
612
cp[strlen(cp) - 1] = '\0'; /* nuke the @'\n'@ */
615
else /* @source == '!'@ */
619
sprintf(temp, "%%%c on `%s' failed", source, cp);
623
printf("%% start of `%s'\n", temp);
624
/* printf("\\LGinlinefalse\\LGbegin\\lgrinde\n"); */
625
/* the above was not %%all%% incorrect ... */
626
printf("\\LGinlinefalse");
627
printf("\\begin{lgrind}\n");
628
printpreamble(config);
629
/* In case \BGfont was modified in config */
630
printf("\\BGfont\n");
632
linenobak=lineno; strcpy(fnamebak, fname);
633
lineno=0; strcpy(fname, temp);
636
lineno=linenobak; strcpy(fname, fnamebak);
638
printf("\\end{lgrind}\n");
639
printf("%% end of `%s'\n", temp);
649
/* embedded inline or displayed code */
654
printf("\\LGinlinetrue");
655
} else { /* @buf[1] == '['@ */
658
printf("\\LGinlinefalse");
660
if (buf[2] == '\n') {
661
printf("\\LGbegin\\lgrinde\n");
663
buf[strlen(buf)-1] = '\0';
664
printf("%s\\lgrinde\n", &buf[2]);
668
} /* @if (buf[0] == '%'@%%\dots%% (special comment) */
671
while (do_at && (atendptr = expmatch(cp, l_at, &atptr, (char *) NULL)) != NULL
672
&& ( (pcendptr = expmatch(cp, l_pc, &pcptr, (char *) NULL)) == NULL
676
printf("\\LGinlinetrue\\LGbegin\\lgrinde");
678
atendptr = expmatch(cp, l_at, &atptr, (char *) NULL);
679
/* No %|@|%: implicit %|@|% just before NL */
681
atptr = atendptr = cp + strlen(cp) - 1;
682
strncpy(temp, cp, (size_t)(atptr-cp));
683
temp[(int)(atptr-cp)] = '\0';
687
printf("}}\\egroup\\endlgrinde\\LGend{}");
695
* We're in embedded code.
700
fprintf(stderr, "%%* only makes sense in display mode\
701
in file %s at line %d\n", fname, lineno);
703
printf("\\endlgrinde\\LGend\n");
704
printf("\\LGinlinefalse\\LGbegin\\lgrinde[%d]\n", linenocode+1);
707
} else if (buf[1] == term) {
708
if (term == ')') printf("\\egroup");
710
printf("\\endlgrinde\\LGend\n");
712
printf("\\endlgrinde%s", &buf[2]);
715
} else if (buf[1] == '=') { /* Send literal */
716
fputs(&buf[2], stdout);
718
} else if (buf[1] == '[' || buf[1] == '('
719
|| buf[1] == ']' || buf[1] == ')') {
721
"Possible nested embedded code in file %s at line %d\n",
727
* Inline code --- suppress leading whitespace
736
printf("\\NewPage\n");
738
if (*cp == '\n') /* some people like ^Ls on their own line */
744
if (!embed && prccont && (psptr >= 0)) {
745
printf("\\ProcCont{");
746
putstr(pstack[psptr]);
750
printf("com %o str %o chr %o ptr %d\n", incomm, instr, inchr, psptr);
752
} /* @while fgets()@ */
758
* Set all of the language-dependent variables
766
* get the language definition from the defs file
768
if (strcmp(language, "mPi") == 0) /* none yet defined */
770
i = tgetent(defs, "extensions", defsfile);
771
if (i == 0) strcpy(language, "c");
774
cp = strrchr(fname, '.');
777
p=buf; cp = tgetstr(cp+1, &p);
779
if (cp != NULL) strcpy(language, cp);
780
else strcpy(language, "c");
783
i = tgetent(defs, language, defsfile);
785
fprintf(stderr, "no entry for language %s\n", language);
788
fprintf(stderr, "cannot find lgrindef file `%s'\n", defsfile);
793
if (tgetstr("kw", &p) == NULL)
801
while (*cp == ' ' || *cp =='\t')
805
while (*cp != ' ' && *cp != '\t' && *cp)
811
p = buf; l_prcbeg = convexp(tgetstr("pb", &p));
812
p = buf; l_noproc = convexp(tgetstr("np", &p));
813
p = buf; l_combeg = convexp(tgetstr("cb", &p));
814
p = buf; l_comend = convexp(tgetstr("ce", &p));
815
p = buf; l_acmbeg = convexp(tgetstr("ab", &p));
816
p = buf; l_acmend = convexp(tgetstr("ae", &p));
817
p = buf; l_strbeg = convexp(tgetstr("sb", &p));
818
p = buf; l_strend = convexp(tgetstr("se", &p));
819
p = buf; l_blkbeg = convexp(tgetstr("bb", &p));
820
p = buf; l_blkend = convexp(tgetstr("be", &p));
821
p = buf; l_chrbeg = convexp(tgetstr("lb", &p));
822
p = buf; l_chrend = convexp(tgetstr("le", &p));
823
p = s_cdebeg; l_cdebeg = convexp(tgetstr("zb", &p));
824
p = buf; l_cdeend = convexp(tgetstr("ze", &p));
825
p = buf; l_texbeg = convexp(tgetstr("tb", &p));
826
p = buf; l_texend = convexp(tgetstr("te", &p));
827
p = buf; l_txmbeg = convexp(tgetstr("mb", &p));
828
p = buf; l_txmend = convexp(tgetstr("me", &p));
829
p = buf; l_tt_beg = convexp(tgetstr("vb", &p));
830
p = buf; l_tt_end = convexp(tgetstr("ve", &p));
831
p = buf; l_record = convexp(tgetstr("rb", &p));
832
p = buf; cp = tgetstr("id", &p) ;
835
l_id = (char*)malloc(strlen(cp));
840
l_tt = convexp("\\|");
845
l_cfunctions = tgetflag("cf");
846
isproc = l_cfunctions ? isprocC : isprocNorm;
848
l_onecase = tgetflag("oc");
849
re_strncmp = l_onecase ? lc_strncmp : strncmp;
850
l_toplex = tgetflag("tl");
855
* Write out a line of TeX in comment verbatim
857
void putVtc(char *stringstart, char *comacmptr)
859
char *texptr, *texendptr;
860
char texline[BUFFERSIZE/8];
861
texendptr = expmatch(stringstart, l_texend, &texptr, (char *) NULL);
862
if (texendptr == comacmptr)
864
strncpy(texline, stringstart, texptr-stringstart);
865
texline[texptr-stringstart]='\0';
866
printf("%s\n", texline);
872
* Write out a formatted line of program text
874
void putScp(char *os)
876
register char *s = os; /* pointer to unmatched string */
877
register char *s1; /* temp. string */
878
char *comptr; /* start of a comment delimiter */
879
char *comendptr; /* end of a comment delimiter */
880
char *acmptr; /* start of an alt. comment delimiter */
881
char *acmendptr; /* end of an alt. comment delimiter */
882
char *strptr; /* start of a string delimiter */
883
char *strendptr; /* end of a string delimiter */
884
char *chrptr; /* start of a char. const delimiter */
885
char *chrendptr; /* end of a char. const delimiter */
886
char *cdeptr; /* start of prog text delim within a comment */
887
char *cdeendptr; /* end of prog text delim within a comment */
888
char *cdbptr; /* start of prog text delim within a comment */
889
char *cdbendptr; /* end of prog text delim within a comment */
890
char *texptr; /* start of %%\TeX%% text delim within a comment */
891
char *texendptr; /* end of %%\TeX%% text delim within a comment */
892
char *txmptr; /* start of %%\TeX%% math delim within a comment */
893
char *txmendptr; /* end of %%\TeX%% math delim within a comment */
894
char *tt_ptr; /* start of typewriter delim within a comment */
895
char *tt_endptr; /* end of typewriter delim within a comment */
896
char *blksptr; /* start of a lexical block start */
897
char *blksendptr; /* end of a lexical block start */
898
char *recsptr; /* start of a lexical block outside functions start */
899
char *recsendptr; /* end of a lexical block outside functions start */
900
char *blkeptr; /* start of a lexical block end */
901
char *blkeendptr; /* end of a lexical block end */
903
_sstart = os; /* remember the start for @expmatch()@ */
905
if (nokeyw || incomm || instr)
908
/* check for complete comment TeX line */
909
comendptr = expmatch(s, l_combeg, &comptr, (char *) NULL);
910
acmendptr = expmatch(s, l_acmbeg, &acmptr, (char *) NULL);
911
texendptr = expmatch(s, l_texbeg, &texptr, (char *) NULL);
912
if (texptr && comptr == s)
913
if (texptr == comendptr)
915
comendptr = expmatch(comendptr, l_comend, &comptr, (char *) NULL);
916
if (*comendptr=='\n')
918
putVtc(texendptr, comptr);
921
} else if (texptr == acmendptr)
923
acmendptr = expmatch(acmendptr, l_acmend, &acmptr, (char *) NULL);
924
if (*acmendptr=='\n')
926
putVtc(texendptr, acmptr);
931
if ((*isproc)(s) && reclevel == 0) {
932
printf("\\index{"); putstr(pname); printf("}");
934
if (strlen(pname)>30)
936
s1=pname+strlen(pname)-1;
937
while (s1>pname && (isalnum(*s1) || *s1=='_')) s1--;
938
if (s1!=pname) strcpy(pname, s1+1);
940
if (strlen(pname)>32) {
941
strcpy(pname, pname+strlen(pname)-28);
945
if (!embed && psptr < PSMAX-1) {
947
strncpy(pstack[psptr], pname, PNAMELEN);
948
pstack[psptr][PNAMELEN] = '\0';
949
plstack[psptr] = blklevel-1;
957
/* check for string, comment, blockstart, etc */
958
if (!incomm && !instr && !inchr) {
960
blkeendptr = expmatch(s, l_blkend, &blkeptr, (char *) NULL);
961
blksendptr = expmatch(s, l_blkbeg, &blksptr, (char *) NULL);
962
recsendptr = expmatch(s, l_record, &recsptr, (char *) NULL);
963
comendptr = expmatch(s, l_combeg, &comptr, (char *) NULL);
964
acmendptr = expmatch(s, l_acmbeg, &acmptr, (char *) NULL);
965
strendptr = expmatch(s, l_strbeg, &strptr, (char *) NULL);
966
chrendptr = expmatch(s, l_chrbeg, &chrptr, (char *) NULL);
968
/* check for end of program text in comment */
969
if (incode != NOTCODE) {
970
cdeendptr = expmatch(s, l_cdeend, &cdeptr, (char *) NULL);
972
if ((cdeptr == NULL && (comptr != NULL || acmptr != NULL))
974
&& ((comptr != NULL && comptr < cdeptr)
975
|| ((acmptr != NULL && acmptr < cdeptr))))) {
976
fprintf(stderr, "Comments may not be nested within\
977
program text within comments in file %s at\
978
line %d\n", fname, lineno);
979
s = (comptr != NULL) ? comptr : acmptr;
984
/* look to see if there's a \<zb> in the program text */
985
cdbendptr = expmatch(s, l_cdebeg, &cdbptr, (char *) NULL);
987
&& (strptr == NULL || cdbptr < strptr)
988
&& (chrptr == NULL || cdbptr < chrptr)
989
&& (blksptr == NULL || cdbptr < blksptr)
990
&& (recsptr == NULL || cdbptr < recsptr)
991
&& (blkeptr == NULL || cdbptr < blkeptr)) {
992
if (cdbptr > s && cdbptr[-1] == l_escape) {
993
putKcp(s, cdbptr-2, FALSE);
995
putKcp(s_cdebeg, s_cdebeg + strlen(s_cdebeg) - 1, TRUE);
1004
expmatch(s, (comtype == STANDARD) ? l_comend : l_acmend,
1005
&comptr, (char *) NULL);
1007
if ((cdeptr == NULL && comptr != NULL)
1008
|| (cdeptr != NULL && (comptr != NULL && comptr < cdeptr))) {
1009
fprintf(stderr, "Unterminated program text within comment\
1010
in file %s at line %d\n", fname, lineno);
1013
incode = NOTCODE; incomm = TRUE;
1018
if (cdeendptr != NULL) {
1019
if ((strptr == NULL || cdeptr < strptr)
1020
&& (chrptr == NULL || cdeptr < chrptr)
1021
&& (blksptr == NULL || cdeptr < blksptr)
1022
&& (recsptr == NULL || cdeptr < recsptr)
1023
&& (blkeptr == NULL || cdeptr < blkeptr)) {
1024
if (incode == INITCODE && cdeptr == s) {
1026
putKcp(s_cdebeg, s_cdebeg + strlen(s_cdebeg) - 1, TRUE);
1028
putKcp(s, cdeptr-1, FALSE);
1032
incode = NOTCODE; incomm = TRUE;
1035
} else if (strptr == NULL && chrptr == NULL
1036
&& blksptr == NULL && recsptr == NULL
1037
&& blkeptr == NULL) {
1040
putKcp(cdeptr, s-1, FALSE);
1043
} /* else there is a string/char/block on this line */
1046
} /* @if (incode)@ */
1049
/* start of a comment? */
1051
&& (strptr == NULL || comptr < strptr)
1052
&& (acmptr == NULL || comptr < acmptr)
1053
&& (chrptr == NULL || comptr < chrptr)
1054
&& (blksptr == NULL || comptr < blksptr)
1055
&& (recsptr == NULL || comptr < recsptr)
1056
&& (blkeptr == NULL || comptr < blkeptr)) {
1057
putKcp(s, comptr-1, FALSE);
1060
putKcp(comptr, comendptr-1, TRUE);
1066
/* start of an alternate-form comment? */
1068
&& (strptr == NULL || acmptr < strptr)
1069
&& (chrptr == NULL || acmptr < chrptr)
1070
&& (blksptr == NULL || acmptr < blksptr)
1071
&& (recsptr == NULL || acmptr < recsptr)
1072
&& (blkeptr == NULL || acmptr < blkeptr)) {
1073
putKcp(s, acmptr-1, FALSE);
1076
putKcp(acmptr, acmendptr-1, TRUE);
1078
comtype = ALTERNATE;
1082
/* start of a string? */
1084
&& (chrptr == NULL || strptr < chrptr)
1085
&& (blksptr == NULL || strptr < blksptr)
1086
&& (recsptr == NULL || strptr < recsptr)
1087
&& (blkeptr == NULL || strptr < blkeptr)) {
1088
putKcp(s, strptr-1, FALSE);
1091
putKcp(strptr, strendptr-1, FALSE);
1096
/* start of a character string? */
1098
&& (blksptr == NULL || chrptr < blksptr)
1099
&& (recsptr == NULL || chrptr < recsptr)
1100
&& (blkeptr == NULL || chrptr < blkeptr)) {
1101
putKcp(s, chrptr-1, FALSE);
1104
putKcp(chrptr, chrendptr-1, FALSE);
1109
/* end of a lexical block */
1110
if (blkeptr != NULL) {
1111
if ((blksptr == NULL || blkeptr < blksptr) &&
1112
(recsptr == NULL || blkeptr < recsptr)) {
1113
putKcp(s, blkeendptr - 1, FALSE);
1115
if (blklevel) blklevel--;
1116
if (reclevel) reclevel--;
1117
else if (psptr >= 0 && plstack[psptr] >= blklevel) {
1119
/* end of current procedure */
1120
blklevel = plstack[psptr];
1122
/* see if we should print the last proc name */
1132
/* start of a lexical block */
1133
if (blksptr != NULL) {
1134
putKcp(s, blksendptr - 1, FALSE);
1136
if (procedures_pending)
1137
procedures_pending--;
1139
/* when C functions then not on top level */
1140
if (!l_cfunctions || blklevel)
1145
/* start of a lexical block outside functions */
1146
if (recsptr != NULL) {
1147
putKcp(s, recsendptr - 1, FALSE);
1149
if (procedures_pending)
1150
procedures_pending--;
1157
/* check for end of comment */
1158
} else if (incomm) {
1160
cdeendptr = expmatch(s, l_cdebeg, &cdeptr, (char *) NULL);
1161
texendptr = expmatch(s, l_texbeg, &texptr, (char *) NULL);
1162
txmendptr = expmatch(s, l_txmbeg, &txmptr, (char *) NULL);
1163
tt_endptr = expmatch(s, l_tt_beg, &tt_ptr, (char *) NULL);
1167
/* Check for program text within comment */
1169
&& (texptr == NULL || cdeptr < texptr)
1170
&& (tt_ptr == NULL || cdeptr < tt_ptr)
1171
&& (txmptr == NULL || cdeptr < txmptr)) {
1172
putKcp(s, cdeptr-1, TRUE);
1175
incode = INITCODE; incomm = FALSE;
1179
/* Check for %%\TeX%% text within comment */
1181
&& (tt_ptr == NULL || texptr < tt_ptr)
1182
&& (txmptr == NULL || texptr < txmptr)) {
1183
putKcp(s, texptr-1, TRUE);
1186
expmatch(s, l_texend, &texptr, (char *) NULL)) != NULL) {
1187
putchar('{'); putVcp(s, texptr-1); putchar('}');
1190
fprintf(stderr, "LaTeX text within a comment must all be\
1191
on one line (file %s at line %d)\n", fname, lineno);
1197
/* Check for typewriter text within comment */
1199
&& (txmptr == NULL || tt_ptr < txmptr)) {
1200
putKcp(s, tt_ptr-1, TRUE);
1203
expmatch(s, l_tt_end, &tt_ptr, (char *) NULL)) != NULL) {
1204
printf("{\\TTfont ");
1206
putKcp(s, tt_ptr-1, TRUE);
1211
fprintf(stderr, "typewriter text within a comment must all\
1212
be on one line (file %s at line %d)\n\t%s",
1219
/* Check for %%\TeX%% math within comment */
1220
if (txmptr != NULL) {
1221
putKcp(s, txmptr-1, TRUE);
1224
expmatch(s, l_txmend, &txmptr, (char *) NULL)) != NULL) {
1225
putchar('$'); putVcp(s, txmptr-1); putchar('$');
1228
fprintf(stderr, "TeX math within a comment must all be\
1229
on one line (file %s at line %d)\n", fname, lineno);
1234
} /* @if (code_cmnts)@ */
1237
expmatch(s, (comtype == STANDARD) ? l_comend : l_acmend,
1238
(char **) NULL, (char *) NULL)) != NULL) {
1239
putKcp(s, comendptr-1, TRUE);
1246
putKcp(comptr, s-1, TRUE);
1250
/* check for end of string */
1253
expmatch(s, l_strend, (char **) NULL, (char *) NULL)) != NULL) {
1254
putKcp(s, strendptr-1, TRUE);
1261
putKcp(strptr, s-1, TRUE);
1265
/* check for end of character string */
1268
expmatch(s, l_chrend, (char **) NULL, (char *) NULL)) != NULL) {
1269
putKcp(s, chrendptr-1, TRUE);
1276
putKcp(chrptr, s-1, TRUE);
1281
/* print out the line */
1284
putKcp(chrptr, s-1, FALSE);
1291
* Output a %%\LaTeX%% command to tab to column "col" (see the documentation
1292
* for a partial explanation of the bizarre brace arrangement).
1294
#define tabto(col) printf("}\\Tab{%d}{", col);
1298
* @islidchr()@ is @TRUE@ for things that can begin identifiers;
1299
* @isidchr@ is @TRUE@ of identifier constituents.
1301
#define islidchr(c)(((isalpha(c) || (strchr(l_id, c))) && c!=0))
1302
#define isidchr(c)(((isalnum(c) || (strchr(l_id, c))) && c!=0))
1306
* Get the identifier starting at @s@. It is assumed that @s@ may indeed
1307
* start an identifier. Value is %$>0$% for a keyword --- the count of
1308
* characters in the keyword --- and %$<0$% if not a keyword, in which
1309
* case the value returned is the negative of the number of bytes in
1310
* the matched identifier.
1312
* This function checks @nokeyw@ and won't check to see if the
1313
* identifier found is a keyword if it is @TRUE@.
1317
char **ss = l_keywds;
1322
while (++cp, isidchr(*cp))
1326
while ((cp = *ss++) != '\0') {
1327
if (!l_onecase && firstc != *cp)
1329
if ((*re_strncmp)(s, cp, i) == 0 && !isidchr(cp[i]))
1337
* Calculate the width of a string, including tabs
1339
int width(register char *s, register char *os)
1341
register int i = 0, c;
1346
/* i = (i + 8) &~ 7; */
1347
i = ((i + TabWidth) / TabWidth) * TabWidth;
1360
* Write out a portion of the line
1362
boolean putKcp(char *start, char *end, boolean nix)
1363
/* start Start of string to write
1364
end End of string to write
1365
nix Don't look for identifiers, numbers
1366
returns whether we ate only as much as we ought to */
1370
while (start <= end) {
1373
* take care of nice tab stops ...
1374
* ... and of space runs
1376
if (c == '\t' || (c == ' ' && *start == ' ')) {
1377
while (start <= end && ( *start == '\t' || *start == ' ' ))
1379
tabto(width(_sstart, start));
1384
* First split off numbers. We have a rather ad hoc
1385
* definition: A number is a digit followed by any number
1386
* of digits or letters, and periods.
1387
* (Additionally a number can start with %|$|% (e.g. hex numbers).)
1388
* This produces meaningless parses --- %$.2$% is parsed as %|.|%
1389
* followed by the number %|2|% --- but should produce decent
1390
* results for most languages (outside of maybe FORTRAN and DCL).
1393
if (c == '#' || islidchr(c)) {
1399
i = substvarname(&start, -i);
1408
else if (isdigit(c) || c == '$') {
1416
} while (isalnum(c) || (c == '.' && *start != '.'));
1424
return (start-1)==end;
1430
* Write out a portion of the line verbatim
1432
void putVcp(char *start, char *end)
1433
/* start Start of string to write
1434
end End of string to write */
1436
for ( ; start <= end; start++) {
1443
* Output a string, escaping special characters
1445
void putstr(register char *cp)
1451
while ((c = *cp++) != '\0')
1457
* The following table converts ASCII characters to a printed
1458
* representation, taking care of all the %%\LaTeX%% quoting. N.B.: all
1459
* single-character strings are assumed to be equivalent to the
1460
* character for that index (i.e., @printtab['c']@ can't be @"f"@).
1461
* (This is purely for efficiency hacking.)
1464
* Some pairs of characters are handled specially within @outchar()@;
1465
* this table contains no indication when that happens.
1466
* %|`|% is output as %|{`}|% to avoid various ligatures, such as %|!`|%.
1467
* The %|''|% and %|--|% (and %|---|%) ligatures are not a problem
1468
* because those characters are output as macros anyway. Using %|{`}|%
1469
* is sufficient since nothing we put out will ever be given to the
1470
* hyphenation algorithms anyway.
1472
* @ttprinttab@ is the same for the emulated verbatim mode
1475
char *printtab[256] = {
1476
"\0x", "\\^A", "\\^B", "\\^C", "\\^D", "\\^E", "\\^F", "\\^G",
1477
"\\^H", "\t", "}}\n", "\\^K", "\0x", "\\^M", "\\^N", "\\^O",
1478
"\\^P", "\\^Q", "\\^R", "\\^S", "\\^T", "\\^U", "\\^V", "\\^W",
1479
"\\^X", "\\^Y", "\\^Z", "\\^[", "\\^\\!","\\^]", "\\^\\^","\\^\\_",
1480
" ", "!", "\\3", "\\#", "\\$", "\\%", "\\&", "{'}",
1481
"(", ")", "*", "+", ",", "\\-", ".", "/",
1482
"0", "1", "2", "3", "4", "5", "6", "7",
1483
"8", "9", ":", ";", "\\<", "=", "\\>", "?",
1484
"@", "A", "B", "C", "D", "E", "F", "G",
1485
"H", "I", "J", "K", "L", "M", "N", "O",
1486
"P", "Q", "R", "S", "T", "U", "V", "W",
1487
"X", "Y", "Z", "[", "\\2", "]", "\\5", "\\_",
1488
"{`}", "a", "b", "c", "d", "e", "f", "g",
1489
"h", "i", "j", "k", "l", "m", "n", "o",
1490
"p", "q", "r", "s", "t", "u", "v", "w",
1491
"x", "y", "z", "\\{", "\\|", "\\}", "\\~{}", "\\^?",
1492
"\200", "\201", "\202", "\203", "\204", "\205", "\206", "\207",
1493
"\210", "\211", "\212", "\213", "\214", "\215", "\216", "\217",
1494
"\220", "\221", "\222", "\223", "\224", "\225", "\226", "\227",
1495
"\230", "\231", "\232", "\233", "\234", "\235", "\236", "\237",
1496
"\240", "\241", "\242", "\243", "\244", "\245", "\246", "\247",
1497
"\250", "\251", "\252", "\253", "\254", "\255", "\256", "\257",
1498
"\260", "\261", "\262", "\263", "\264", "\265", "\266", "\267",
1499
"\270", "\271", "\272", "\273", "\274", "\275", "\276", "\277",
1500
"\300", "\301", "\302", "\303", "\304", "\305", "\306", "\307",
1501
"\310", "\311", "\312", "\313", "\314", "\315", "\316", "\317",
1502
"\320", "\321", "\322", "\323", "\324", "\325", "\326", "\327",
1503
"\330", "\331", "\332", "\333", "\334", "\335", "\336", "\337",
1504
"\340", "\341", "\342", "\343", "\344", "\345", "\346", "\347",
1505
"\350", "\351", "\352", "\353", "\354", "\355", "\356", "\357",
1506
"\360", "\361", "\362", "\363", "\364", "\365", "\366", "\367",
1507
"\370", "\371", "\372", "\373", "\374", "\375", "\376", "\377",
1510
char *ttprinttab[256] = {
1511
"\0x", "\\^A", "\\^B", "\\^C", "\\^D", "\\^E", "\\^F", "\\^G",
1512
"\\^H", "\t", "}}\n", "\\^K", "\0x", "\\^M", "\\^N", "\\^O",
1513
"\\^P", "\\^Q", "\\^R", "\\^S", "\\^T", "\\^U", "\\^V", "\\^W",
1514
"\\^X", "\\^Y", "\\^Z", "\\^[", "\\^\\!","\\^]", "\\^\\^","\\^\\_",
1515
" ", "!", "{34}", "{35}", "{36}", "{37}", "{38}", "{39}",
1516
"(", ")", "*", "+", ",", "{45}", ".", "/",
1517
"0", "1", "2", "3", "4", "5", "6", "7",
1518
"8", "9", ":", ";", "{60}", "=", "{62}", "?",
1519
"@", "A", "B", "C", "D", "E", "F", "G",
1520
"H", "I", "J", "K", "L", "M", "N", "O",
1521
"P", "Q", "R", "S", "T", "U", "V", "W",
1522
"X", "Y", "Z", "[", "{92}", "]", "{94}", "{95}",
1523
"{96}", "a", "b", "c", "d", "e", "f", "g",
1524
"h", "i", "j", "k", "l", "m", "n", "o",
1525
"p", "q", "r", "s", "t", "u", "v", "w",
1526
"x", "y", "z", "{123}", "{124}", "{125}", "{126}", "\\^?",
1527
"\200", "\201", "\202", "\203", "\204", "\205", "\206", "\207",
1528
"\210", "\211", "\212", "\213", "\214", "\215", "\216", "\217",
1529
"\220", "\221", "\222", "\223", "\224", "\225", "\226", "\227",
1530
"\230", "\231", "\232", "\233", "\234", "\235", "\236", "\237",
1531
"\240", "\241", "\242", "\243", "\244", "\245", "\246", "\247",
1532
"\250", "\251", "\252", "\253", "\254", "\255", "\256", "\257",
1533
"\260", "\261", "\262", "\263", "\264", "\265", "\266", "\267",
1534
"\270", "\271", "\272", "\273", "\274", "\275", "\276", "\277",
1535
"\300", "\301", "\302", "\303", "\304", "\305", "\306", "\307",
1536
"\310", "\311", "\312", "\313", "\314", "\315", "\316", "\317",
1537
"\320", "\321", "\322", "\323", "\324", "\325", "\326", "\327",
1538
"\330", "\331", "\332", "\333", "\334", "\335", "\336", "\337",
1539
"\340", "\341", "\342", "\343", "\344", "\345", "\346", "\347",
1540
"\350", "\351", "\352", "\353", "\354", "\355", "\356", "\357",
1541
"\360", "\361", "\362", "\363", "\364", "\365", "\366", "\367",
1542
"\370", "\371", "\372", "\373", "\374", "\375", "\376", "\377",
1549
* Output one character, fixed up as required. Since there is no call-back
1550
* to tell @outchar()@ to flush what it has stored, it must always produce
1551
* some output --- it can't simply retain state. This makes certain
1552
* translations impossible, but we can live without them for now....
1569
#if 0 /* gvr hates this trick */
1570
if (incomm && c == '-') {
1572
c = EMDASH; /* For future cleverness... */
1590
if (incomm && c == '-') {
1594
printtab[(unsigned char)c][1] ?
1595
printf("%s", printtab[(unsigned char)c]) : putchar(c);
1600
c==32 ? putchar('~') :
1601
ttprinttab[(unsigned char)c][1] ?
1602
printf("\\symbol%s", ttprinttab[(unsigned char)c]) : putchar(c);
1607
* Look for a procedure beginning on this line
1609
boolean isprocNorm(char *s)
1612
if ((!l_toplex || blklevel == 0)
1613
&& expmatch(s, l_prcbeg, (char **) NULL, pname) != NULL
1614
&& expmatch(s, l_noproc, (char **) NULL, (char *) NULL) == NULL)
1616
procedures_pending++;
1623
/* Special version of the above for C functions */
1624
boolean isprocC(char *s)
1626
char cheat[BUFFERSIZE];
1627
char *j=s, *i=cheat;
1628
boolean comm=FALSE, string=FALSE, schar=FALSE;
1631
if (blklevel!=0) return FALSE;
1634
if (*j=='"' && !comm && !schar) string=(string+1)%2;
1635
else if (*j=='\'' && !comm && !string) schar=(schar+1)%2;
1636
else if (*j=='\\' && !comm) j++;
1637
else if (!comm && !string && !schar && *j=='{') brack++;
1638
else if (!comm && !string && !schar && *j=='}') brack--;
1639
else if (!comm && !string && !schar && *j=='#') break;
1640
else if (!comm && !string && !schar && *j=='/' && *(j+1)=='/') break;
1641
else if (!comm && !string && !schar && *j=='/' && *(j+1)=='*') comm=TRUE;
1642
else if ( !string && !schar && *j=='*' && *(j+1)=='/')
1644
else if (!brack && !comm && !string && !schar)
1650
return isprocNorm(cheat);
1654
/* The VMS code below has been absolutely untested for the last few years.
1655
* Don't ask me about it. Please. MPi
1659
* @getredirection()@ is intended to aid in porting C programs
1660
* to VMS (Vax-11 C) which does not support %|>|% and %|<|%
1661
* I/O redirection. With suitable modification, it may
1662
* useful for other portability problems as well.
1664
* Modified, 24-Jan-86 by Jerry Leichter
1665
* When creating a new output file, force the maximum record size to
1666
* 512; otherwise, it ends up as 0 (though the C I/O system won't write
1667
* a record longer than 512 bytes anyway) which will cause problems if
1668
* the file is later opened for @APPEND@ --- if the maximum record size
1669
* is 0, C will use the length of the longest record written to the file
1678
getredirection(argc, argv)
1682
* Process VMS redirection args. Exit if any error is seen.
1683
* If @getredirection()@ processes an argument, it is erased
1684
* from the vector. @getredirection()@ returns a new @argc@ value.
1686
* Warning: do not try to simplify the code for VMS. The code
1687
* presupposes that @getredirection()@ is called before any data is
1688
* read from @stdin@ or written to @stdout@.
1690
* Normal usage is as follows:
1696
* argc = getredirection(argc, argv);
1700
register char *ap; /* Argument pointer */
1701
int i; /* @argv[]@ index */
1702
int j; /* Output index */
1703
int file; /* File_descriptor */
1705
for (j = i = 1; i < argc; i++) { /* Do all arguments */
1706
switch (*(ap = argv[i])) {
1707
case '<': /* %|<file|% */
1708
if (freopen(++ap, "r", stdin) == NULL) {
1709
perror(ap); /* Can't find file */
1710
exit(errno); /* Is a fatal error */
1714
case '>': /* %|>file|% or %|>>file|% */
1715
if (*++ap == '>') { /* %|>>file|% */
1717
* If the file exists, and is writable by us,
1718
* call @freopen()@ to append to the file (using the
1719
* file's current attributes). Otherwise, create
1720
* a new file with "vanilla" attributes as if
1721
* the argument was given as %|>filename|%.
1722
* @access(name, 2)@ is @TRUE@ if we can write on
1723
* the specified file.
1725
if (access(++ap, 2) == 0) {
1726
if (freopen(ap, "a", stdout) != NULL)
1727
break; /* Exit @case@ statement */
1728
perror(ap); /* Error, can't append */
1729
exit(errno); /* After @access@ test */
1730
} /* If file accessable */
1733
* On VMS, we want to create the file using "standard"
1734
* record attributes. @create(...)@ creates the file
1735
* using the caller's default protection mask and
1736
* "variable length, implied carriage return"
1737
* attributes. @dup2()@ associates the file with @stdout@.
1739
if ((file = creat(ap, 0, "rat=cr", "rfm=var", "mrs=512")) == -1
1740
|| dup2(file, fileno(stdout)) == -1) {
1741
perror(ap); /* Can't create file */
1742
exit(errno); /* is a fatal error */
1743
} /* If %|>|% creation */
1744
break; /* Exit @case@ test */
1747
argv[j++] = ap; /* Not a redirector */
1748
break; /* Exit @case@ test */
1750
} /* For all arguments */
1751
argv[j] = NULL; /* Terminate @argv[]@ */
1752
return j; /* Return new @argc@ */
1757
int getredirection(int argc, char **argv)
1768
/* Helper functions */