1 /*
2  *   $Id: main.c,v 1.3 1992/10/07 22:06:08 craigs Exp $
3  *
4  *   This code was written by Craig Southeren whilst under contract
5  *   to Computer Sciences of Australia, Systems Engineering Division.
6  *   It has been kindly released by CSA into the public domain.
7  *
8  *   Neither CSA or me guarantee that this source code is fit for anything,
9  *   so use it at your peril. I don't even work for CSA any more, so
10  *   don't bother them about it. If you have any suggestions or comments
11  *   (or money, cheques, free trips =8^) !!!!! ) please contact me
12  *   care of geoffw@extro.ucc.oz.au
13  *
14  *   handling for different classification banners for 1st page added by
15  *   Daniel Risacher (magnus@alum.mit.edu)
16  *
17  */
18 
19 #include "machdep.h"
20 #include "defs.h"
21 
22 #include "version.h"
23 #include "print.h"
24 #include "main.h"
25 #include "paper.h"
26 #include "postscri.h"
27 
28 /********************************
29   defines
30  ********************************/
31 
32 /* environment variables */
33 #define		PRINTER		"PRINTER"		/* sets name of printer */
34 #define		NENSCRIPT	"NENSCRIPT"		/* contains options */
35 
36 /********************************
37   exports
38  ********************************/
39 char *progname;
40 
41 /********************************
42   usage
43  ********************************/
44 
usage()45 void usage ()
46 
47 {
48   printf ("usage: %s -?12BGghlNnRrSsVWwZ -f font -F titlefont -b header -i filetitle -L lines -p filename -P printer -S classification -U classification_1st_page -T papertype -t tabstop -# copies file...\n", progname);
49   exit (0);
50 }
51 
52 /*********************************
53 
54 variables set by options
55 
56  *********************************/
57 
58 static int  landscape = False;			/* True if in ladnscape mode, i.e. -r specified */
59 static int  columns = 1;				/* number of columns to print with */
60 static char *outputfilename = NULL;			/* name of output file, if still NULL after parsing args then use lpr */
61 static char *bodyfont = NULL;			/* font to use for text */
62 static char *titlefont = NULL;			/* font to use for titles */
63 static int  wrap = True;				/* True if to wrap long lines */
64 static int  titlesenabled = True;			/* True if page titles enables */
65 static char *title = NULL;				/* set to title if specified */
66 static int  copies = 1;				/* number of copies of each page */
67 static int  gaudy = False;				/* enables gaudy mode */
68 static int  force_lines = 0;				/* force number of lines per page */
69 static char *classification_1st = NULL;			/* security classification for 1st page */
70 static char *classification = NULL;			/* security classification for body of document */
71 static int  line_numbers = False;                    /* true if to display line numbers */
72 static char *printername = DEF_PRINTER;		/* name of printer to use when output goes to lpr */
73 static int  tabstop = 8;			/* tab stops */
74 static int  passthroughflag = False;		/* if True, then check for %! to allow passthrough of raw postscript */
75 static char *filetitle = NULL;                  /* set file title */
76 
77 #ifdef US_VERSION
78 static char *papertype = "US";
79 #else
80 static char *papertype = "A4";
81 #endif
82 
83 
84 /*
85  * forward declarations if in ANSI mode
86  */
87 
88 #ifdef __STDC__
89 char * get_string  (char **);
90 void parse_options (int *, char ***);
91 void parse_env     (char *);
92 
93 
94 #endif
95 
96 
97 /********************************
98   parse_options
99  ********************************/
100 
101 #define	ARGC	(*argc)
102 #define	ARGV	(*argv)
103 
parse_options(argc,argv)104 void parse_options (argc, argv)
105 
106 int *argc;
107 char ***argv;
108 
109 {
110   char c;
111   char *s;
112   int i;
113 
114   /* parse the options */
115 
116 next_argv:
117  while (--ARGC > 0 && (*++ARGV)[0] == '-')
118     while (c = *++ARGV[0])
119       switch (c) {
120 
121 /* -? : print help message */
122         case '?' :
123         case 'h' :
124           usage ();
125           break;
126 
127 /* -1 : single column mode */
128         case '1' :
129           columns = 1;
130           break;
131 
132 /* -2 : double column mode */
133         case '2' :
134           columns = 2;
135           break;
136 
137 /* -#: set number of copies */
138         case '#' :
139           if (*++ARGV[0])
140             copies = atoi (ARGV[0]);
141           else {
142             if (ARGC < 1)
143               fprintf (stderr, "%s: -# option requires argument\n", progname);
144             else {
145               --ARGC;
146               copies = atoi (*++ARGV);
147             }
148           }
149           if (copies < 1) {
150             fprintf (stderr, "%s: illegal number of copies specified - defaulting to one\n", progname);
151             copies = 1;
152           }
153           goto next_argv;
154           break;
155 
156 /* -b: set page title */
157         case 'b' :
158           if (*++ARGV[0])
159             title = ARGV[0];
160           else {
161             if (ARGC < 1)
162               fprintf (stderr, "%s: -b option requires argument\n", progname);
163             else {
164               --ARGC;
165               title = *++ARGV;
166             }
167           }
168           goto next_argv;
169           break;
170 
171 /* -f : set font to use for printing body */
172         case 'f' :
173           if (*++ARGV[0])
174             bodyfont = ARGV[0];
175           else {
176             if (ARGC < 1)
177               fprintf (stderr, "%s: -f option requires argument\n", progname);
178             else {
179               --ARGC;
180               bodyfont = *++ARGV;
181             }
182           }
183           goto next_argv;
184           break;
185 
186 /* -N : display line numbers */
187         case 'N' :
188           line_numbers = True;
189           break;
190 
191 /* -n : disable line numbers */
192         case 'n' :
193           line_numbers = False;
194           break;
195 
196 /* -p: send to file rather than lpr */
197         case 'p' :
198           if (*++ARGV[0])
199             outputfilename = ARGV[0];
200           else {
201             if (ARGC < 1)
202               fprintf (stderr, "%s: -p option requires argument\n", progname);
203             else {
204               --ARGC;
205               outputfilename = *++ARGV;
206             }
207           }
208           goto next_argv;
209           break;
210 
211 /* -r : landscape mode */
212         case 'r' :
213           landscape = True;
214           break;
215 
216 /* -w : wrap mode */
217         case 'w' :
218           wrap = True;
219           break;
220 
221 /* -Z : check for postscript passthrough mode */
222         case 'Z' :
223           passthroughflag = True;
224           break;
225 
226 /* -B : disable page headings and disable gaudy mode */
227         case 'B' :
228           titlesenabled = False;
229           break;
230 
231 /* -F : set font to use for printing titles */
232         case 'F' :
233           if (*++ARGV[0])
234             titlefont = ARGV[0];
235           else {
236             if (ARGC < 1)
237               fprintf (stderr, "%s: -F option requires argument\n", progname);
238             else {
239               --ARGC;
240               titlefont = *++ARGV;
241             }
242           }
243           goto next_argv;
244           break;
245 
246 /* -g : disable gaudy mode */
247         case 'g' :
248           gaudy = False;
249           break;
250 
251 /* -G : enable gaudy mode */
252         case 'G' :
253           gaudy = True;
254           break;
255 
256 /* -i: set file title when inputting from stdin */
257         case 'i' :
258           if (*++ARGV[0])
259             filetitle = ARGV[0];
260           else {
261             if (ARGC < 1)
262               fprintf (stderr, "%s: -i option requires argument\n", progname);
263             else {
264               --ARGC;
265               filetitle = *++ARGV;
266             }
267           }
268           goto next_argv;
269           break;
270 
271 /* -l : don't set number of lines of page, i.e. use maximum */
272         case 'l' :
273           force_lines = 0;
274           break;
275 
276 /* -L : force number of lines per page */
277         case 'L' :
278           if (*++ARGV[0])
279             force_lines = atoi(ARGV[0]);
280           else {
281             if (ARGC < 1)
282               fprintf (stderr, "%s: -x option requires argument\n", progname);
283             else {
284               --ARGC;
285               force_lines = atoi (*++ARGV);
286             }
287           }
288 	  if (force_lines <= 0) {
289             fprintf (stderr, "%s: ignoring illegal arguement to -L option\n", progname);
290 	    force_lines = 0;
291           }
292           goto next_argv;
293           break;
294 
295 /* -P: set printer name - overrides PRINTER environment variables */
296         case 'P' :
297           if (*++ARGV[0])
298             printername = ARGV[0];
299           else {
300             if (ARGC < 1)
301               fprintf (stderr, "%s: -P option requires argument\n", progname);
302             else {
303               --ARGC;
304               printername = *++ARGV;
305             }
306           }
307           goto next_argv;
308           break;
309 
310 /* -R : portrait mode */
311         case 'R' :
312           landscape = False;
313           break;
314 
315 /* -U: set security classification for first page only*/
316         case 'U' :
317           if (*++ARGV[0])
318             classification_1st = ARGV[0];
319           else {
320             if (ARGC < 1)
321               fprintf (stderr, "%s: -U option requires argument\n", progname);
322             else {
323               --ARGC;
324               classification_1st = *++ARGV;
325             }
326           }
327           goto next_argv;
328           break;
329 
330 /* -S: set security classification */
331         case 'S' :
332           if (*++ARGV[0])
333             classification = ARGV[0];
334           else {
335             if (ARGC < 1)
336               fprintf (stderr, "%s: -S option requires argument\n", progname);
337             else {
338               --ARGC;
339               classification = *++ARGV;
340             }
341           }
342           goto next_argv;
343           break;
344 
345 /* -s : disable security classification printing */
346         case 's' :
347           classification_1st = NULL;
348           classification = NULL;
349           break;
350 
351 
352 /* -T: set paper size to either US or A4 */
353         case 'T' :
354           if (*++ARGV[0])
355             papertype = ARGV[0];
356           else {
357             if (ARGC < 1)
358               fprintf (stderr, "%s: -T option requires argument\n", progname);
359             else {
360               --ARGC;
361               papertype = *++ARGV;
362             }
363           }
364           goto next_argv;
365           break;
366 
367 /* -t: set tab stop size */
368         case 't' :
369           if (*++ARGV[0])
370             s = ARGV[0];
371           else {
372             if (ARGC < 1) {
373               fprintf (stderr, "%s: -t option requires argument\n", progname);
374 	      s = 0;		/* We should flag the error in some way */
375             } else {
376               --ARGC;
377               s = *++ARGV;
378             }
379           }
380           if ((i = atoi (s)) <= 0)
381             fprintf (stderr, "%s: illegal tab setting %s ignored\n", progname, s);
382           else
383             tabstop = i;
384           goto next_argv;
385           break;
386 
387 /* -V : print version number */
388 	case 'V' :
389           fputs (version_string,   stderr);
390           fputs ("\n",           stderr);
391           /* fputs (copyright_string, stderr); */
392           exit (0);
393           break;
394 
395 /* -W : turn off wrap mode */
396         case 'W' :
397           wrap = False;
398           break;
399 
400 /* single char option */
401 /*        case 'x' :
402           allow_checkouts = True;
403           break;
404 */
405 
406 /* option with string */
407 /*        case 'x' :
408           if (*++ARGV[0])
409             optionstring = ARGV[0];
410           else {
411             if (ARGC < 1)
412               fprintf (stderr, "%s: -x option requires argument\n", progname);
413             else {
414               --ARGC;
415               optionstring = *++ARGV;
416             }
417           }
418           goto next_argv;
419           break;
420 */
421         default :
422           fprintf (stderr, "%s: unknown option %c\n", progname, c);
423 	  usage();
424           break;
425       }
426 }
427 
428 /********************************
429   get_string
430     return the next string from the argument
431  ********************************/
432 
get_string(str)433 char * get_string (str)
434 
435 char **str;
436 
437 #define	STR	(*str)
438 
439 {
440   char *ret = NULL;
441   int  in_dquote, in_squote, in_bquote;
442   int  quote_count;
443 
444   /* skip leading whitespace */
445   while (*STR == ' ' ||
446          *STR == '\t')
447     STR++;
448 
449   /* return if end of string */
450   if (*STR == '\0')
451     return NULL;
452 
453   /* collect characters until end of string or whitespace */
454   in_dquote = -1;
455   in_squote = -1;
456   in_bquote = -1;
457   quote_count = 0;
458   ret = STR;
459   while (*STR != '\0' &&
460          (quote_count > 0 || (*STR != ' ' && *STR != '\t'))
461         ) {
462     switch (*STR) {
463       case '"':
464         in_dquote = -in_dquote;
465         quote_count += in_dquote;
466         break;
467 
468       case '\'':
469         in_squote = -in_squote;
470         quote_count += in_squote;
471         break;
472 
473       case '`':
474         in_bquote = -in_bquote;
475         quote_count += in_bquote;
476         break;
477 
478       case '\\':
479         if (STR[1] != '\0')
480           STR++;
481         break;
482     }
483     STR++;
484   }
485   if (*STR != '\0') {
486     *STR = '\0';
487     STR++;
488   }
489 
490   if (quote_count > 0)
491     fprintf (stderr, "unmatched quote in %s environment variable\n", NENSCRIPT);
492 
493   return ret;
494 }
495 
496 
497 /********************************
498   parse_env
499     process the options specified in the supplied string
500  ********************************/
501 
parse_env(env)502 void parse_env (env)
503 
504 char *env;
505 
506 {
507   int argc;
508   char *s;
509   char **argv;
510 
511   if (env == NULL)
512     return;
513 
514   /* insert a "fake" argv[0] which corresponds to the real
515      argv[0] passed to main. The allows us to use same routine
516      for handling the real arguments */
517   argv = (char **)malloc (sizeof (char *));
518   argc = 1;
519   argv[0] = progname;
520 
521   /* split the single environment string into separate strings so we can pass
522      them to parse_options */
523   while ((s = get_string (&env)) != NULL) {
524     argv = (char **)realloc ((void *)argv, (argc+1) * sizeof (char *));
525     argv[argc] = s;
526     argc++;
527   }
528 
529   /* parse the options */
530   parse_options (&argc, &argv);
531 }
532 
533 /********************************
534   passthrough
535  ********************************/
536 
passthrough(input,output)537 static void passthrough (input, output)
538 
539 FILE * input;
540 FILE * output;
541 
542 {
543   char buff [512];
544   int  len;
545 
546   while ((len = fread (buff, 1, 512, input)) > 0)
547     fwrite (buff, 1, len, output);
548 }
549 
550 
551 /********************************
552   main
553  ********************************/
554 
main(argc,argv)555 int main (argc, argv)
556 
557 int argc;
558 char *argv[];
559 
560 {
561   char *env;
562   char cmd[1024];
563   struct PaperMetrics * papermetrics;
564   int i;
565   int ch1;
566   int ch2;
567 
568 
569   FILE *outputstream;				/* opened output stream - either file or lpr */
570   FILE *inputstream;				/* opened input stream, 0 if max */
571 
572   /* extract the program name */
573   if ((progname = strrchr (argv[0], '/')) == NULL)
574     progname = argv[0];
575   else
576     progname++;
577 
578   /* get the printer environment variable */
579   if ((env = getenv (PRINTER)) != NULL)
580     printername = env;
581 
582   /* handle the NENSCRIPT environment variable */
583   if ((env = getenv (NENSCRIPT)) != NULL)
584     parse_env (STRDUP(env));
585 
586   /* parse arguments */
587   parse_options (&argc, &argv);
588 
589   /* open either the output file or a pipe to lpr */
590   if (outputfilename != NULL) {
591     if (strcmp (outputfilename, "-") == 0)
592       outputstream = stdout;
593     else if ((outputstream = fopen (outputfilename, "w")) == NULL) {
594       perror (outputfilename);
595       exit (1);
596     }
597   } else {
598 #ifdef MSDOS
599     if ((outputstream = fopen (printername, "w")) == NULL) {
600       perror (printername);
601       exit (1);
602     }
603 #else
604     snprintf (cmd, sizeof(cmd), "%s %s", LPR, printername);
605     if ((outputstream = popen (cmd, "w")) == NULL) {
606       perror (LPR);
607       exit (1);
608     }
609 #endif
610   }
611 
612   /* can't be in passthrough mode with more than one argument */
613   if (argc > 1 && passthroughflag) {
614     fprintf (stderr, "%s: ignoring -Z flag as there is more than one argument", progname);
615     passthroughflag = False;
616   }
617 
618   /* if in passthrough mode, open the input stream and check for %! */
619   if (passthroughflag) {
620     if (argc < 1)
621       inputstream = stdin;
622     else if ((inputstream = fopen (*argv, "r")) == NULL) {
623       perror (*argv);
624       exit (1);
625     }
626     ch1 = fgetc (inputstream);
627     ch2 = fgetc (inputstream);
628     ungetc (ch2, inputstream);
629     ungetc (ch1, inputstream);
630     if (ch1 == '%' && ch2 == '!') {
631       passthrough (inputstream, outputstream);
632       exit (0);
633     }
634   } else
635     inputstream = NULL;
636 
637   /* check the paper type and get ptr to record */
638   papermetrics = NULL;
639   for (i = 0; PaperTypes[i].Name != NULL; i++)
640     if (STRICMP ((papermetrics = &PaperTypes[i])->Name, papertype) == 0)
641       break;
642 
643   /* if the papermetric was not set, give error */
644   if (PaperTypes[i].Name == NULL) {
645     fprintf (stderr, "%s: paper type %s not known\n", progname, papertype);
646     exit (1);
647   }
648 
649   /* set up the fonts */
650   if (bodyfont == NULL)
651     bodyfont = (columns == 2 && landscape) ? papermetrics->LandscapeFont : papermetrics->PortraitFont;
652 
653   if (titlefont == NULL)
654     titlefont = papermetrics->TitleFont;
655 
656   /* display the header */
657   StartJob (outputstream,
658             *argv,
659             landscape,
660             columns,
661             bodyfont,
662             titlefont,
663             wrap,
664             titlesenabled,
665             title,
666             copies,
667             gaudy,
668             force_lines,
669             classification_1st,
670             classification,
671             papermetrics,
672 	    tabstop);
673 
674   /* if no arguments, then accept stdin */
675   if (argc < 1)
676     print_file (stdin, outputstream, (filetitle ? filetitle : "stdin"), line_numbers);
677   else for (;argc > 0;argc--) {
678     if (inputstream == NULL && (inputstream = fopen (*argv, "r")) == NULL)
679       perror (*argv);
680     else {
681       print_file (inputstream, outputstream, (filetitle ? filetitle : *argv), line_numbers);
682       fclose(inputstream);
683       inputstream = NULL;
684     }
685     argv++;
686   }
687 
688   /* output trailer */
689   EndJob (outputstream);
690 
691   /* finished */
692   return (0);
693 }
694