/* * maharani - a czarina-like program that generates interpress files * * Written for Xerox Corporation by William LeFebvre * * Copyright (c) 1984, 1985 Xerox Corporation * * HISTORY * 13-Jan-86 lee at Xerox, WRC * Changed a call to strcpyn to strncpy. * * 8-apr-85 ed flint conditional compilation for vax11-c (vms) * 26-mar-85 ed @ Xerox, WRC * add fclose after do_file to prevent running out of open file descriptors */ #ifdef vax11c # include stdio # include ssdef # include ctype # include descrip #else # include # include # include # include # include # include #endif # include "iptokens.h" # include "literal.h" # include "operator.h" # define QIP "qip" # define Break_size 1024 # define Default_universal_prefix "Xerox/XC1-1-1/" # define Line_size 128 /* All page boundaries are computed in the 1/10 point co-ordinate system */ /* * Orig_y is an offset from the top of the page. It must be converted * to a measurement from the bottom of the page (a calculation that is * rotation-dependent). */ # define INCH 720 # define Half_INCH 360 # define Sixth_INCH 120 /* one line at 6 lpi */ # define Page_width (8 * INCH + Half_INCH) # define Page_length (11 * INCH) # define Orig_x (9 * INCH / 10) # define Orig_y (Sixth_INCH * 5) # define Header_to_orig_x 0 # define Header_to_orig_y (2 * Sixth_INCH) /* Frame variable defines */ # define F_transform 0 # define F_headfont 1 # define F_bodyfont 2 # define F_italicfont 3 # define No 0 # define Yes 1 extern int errno; /* enum, perhaps? */ typedef char boolean; /* routines that return something other than int */ char *strecpy(); char *allocate(); char *next_arg(); char *sbrk(); char *getenv(); char *itoa(); char *rindex(); /* option flags */ boolean lflg = No; /* line printer mode */ boolean rflg = No; /* rotation - landscape mode */ boolean tflg = No; /* omit title */ /* valued options */ int columns = 1; char *bodyfont_name = "Vintage-Printwheel/10"; char *headfont_name = "Modern-Bold/12"; char *italicfont_name = "Modern-Bold-Italic/12"; char *banner = NULL; char *copies = "1"; char *header = "%f %t Page %p, line %l"; char *name = NULL; char *output = NULL; char *pages = NULL; #ifdef vax11c char *template = "IPPXXXXXX"; #endif /* * page characteristics: these variables define the extremes for the * current page or column. 'column_separation' is the distance between * the left sides of each column on the page. */ int left_margin = Orig_x; int right_margin; int top_margin; int bottom_margin; int column_separation; /* sundries */ boolean send_to_printer = Yes; char real_header[256]; /* header built here */ char *myname; /* name invoked with */ char *filename; int *page_select = NULL; /* array of page selections */ int *curr_page_select; int page_low; int page_high; int ipress_file; /* interpress file descriptor */ int null_file; /* fd for /dev/null */ int line_number; int page_number; int pages_printed = 0; /* total pages for this interpress file */ int special_font = 0; /* fonts that require special handling */ int line_spacing; int tab_amount = 8; #ifndef vax11c struct passwd *pwd; /* passwd entry for this user */ struct stat file_stat; /* stat of current file */ #endif # define Font_Terminal 1 /* current arguments */ int argc; char **argv; /* font structure definition */ struct font { char *ft_universal_name; char *ft_leaf_name; int ft_size; }; /* fonts used */ struct font headfont; struct font bodyfont; struct font italicfont; main(_argc, _argv) int _argc; char **_argv; { char *ptr; /* temporary pointers used for loops and such */ char *src; char *dest; int length; int i; FILE *file; /* file currently processing */ #ifdef vax11c int error; int retlen; char command[256]; $DESCRIPTOR(mahadesc,"MAHAENV"); $DESCRIPTOR(cmddesc,command); #endif /* get our name */ if (_argc < 1) { exit(1); } #ifdef vax11c myname= _argv[0]; #else if ((myname = rindex(_argv[0], '/')) == NULL) { myname = _argv[0]; } else { myname++; } #endif /* get the options specified in the environment (defaults) */ #ifdef vax11c if ( (error= lib$get_symbol(&mahadesc,&cmddesc,&retlen)) == SS$_NORMAL ) { if ( retlen != 0 ) { command[retlen & 0xff]= '\0'; /* null terminate string */ src= command; /* break the string up into null terminated arguments */ /* half the length is a good upper bound on number of arguments */ argv = (char **)allocate(strlen(src) / 2); for (argc = 1, ptr = src; *ptr != '\0'; argc++) { while (*ptr == ' ') ptr++; argv[argc] = ptr; while (*ptr != ' ' && *ptr != '\0') { if (*ptr == '"') { while (*++ptr != '"' && *ptr != '\0'); } else if (*ptr == '\'') { while (*++ptr != '\'' && *ptr != '\0'); } ptr++; } *ptr++ = '\0'; } /* terminate the argument list */ argv[argc] = NULL; /* process the options found in the environment */ get_options(); } } #else if ((src = getenv("MAHA")) != NULL) { /* break the string up into null terminated arguments */ /* half the length is a good upper bound on number of arguments */ argv = (char **)allocate(strlen(src) / 2); for (argc = 1, ptr = src; *ptr != '\0'; argc++) { while (*ptr == ' ') ptr++; argv[argc] = ptr; while (*ptr != ' ' && *ptr != '\0') { if (*ptr == '"') { while (*++ptr != '"' && *ptr != '\0'); } else if (*ptr == '\'') { while (*++ptr != '\'' && *ptr != '\0'); } ptr++; } *ptr++ = '\0'; } /* terminate the argument list */ argv[argc] = NULL; /* process the options found in the environment */ get_options(); } #endif /* use the real arguments */ argc = _argc; argv = _argv; /* process (real) arguments */ get_options(); /* establish and verify the requested fonts */ establish_font(headfont_name, &headfont); establish_font(bodyfont_name, &bodyfont); establish_font(italicfont_name, &italicfont); #ifndef vax11c /* get passwd entry for future reference */ pwd = getpwuid(geteuid()); #endif /* setup output file */ if (output == NULL) { /* build a temporary file name */ #ifdef vax11c output= mktemp(template); #else output = allocate(1 + 5 + 1 + 2 + 1); (void) sprintf(output, "/tmp/@%d.ip", getpid()); #endif } #ifdef vax11c if ((ipress_file = creat(output, 0, "rfm=udf")) == -1) #else if ((ipress_file = creat(output, 0666)) == -1) #endif { system_error(output); exit(1); } ip_select(ipress_file); #ifndef vax11c /* open the null device for throwing away output */ null_file = open("/dev/null", 1); /* set null strings to default values */ if (name == NULL) { /* banner name defaults to full name from gecos field */ name = pwd->pw_gecos; /* perform expansion and stripping */ if ((ptr = index(name, ',')) != NULL) { *ptr = '\0'; /* this affects pwd->pw_gecos, too! */ } if (index(name, '&') != NULL) { name = allocate(strlen(name) + strlen(pwd->pw_name) + 1); for (src = pwd->pw_gecos, dest = name; *src != '\0'; src++, dest++) { if (*src == '&') { for (ptr = pwd->pw_name; *ptr != '\0'; ptr++) { *dest++ = *ptr; } } else { *dest = *src; } } } } #endif if (banner == NULL) { /* banner defaults to file name(s) */ if (argc == 0) { banner = "out of the blue"; } else { for (length = 0, i = 0; i < argc; i++) { length += strlen(argv[i]) + 2; } banner = allocate(length + 1); for (ptr = banner, i = 0; i < argc; i++) { ptr = strecpy(ptr, argv[i]); ptr = strecpy(ptr, ", "); } ptr -= 2; *ptr = '\0'; } } /* unravel the page specifiation */ /* we will never need more than strlen(pages) ints to hold the info */ if (pages != NULL) { page_select = (int *)allocate(strlen(pages) * sizeof(int)); unravel_pages(pages, page_select); } /* write the preamble for the interpress file */ Op(beginBlock); Op(beginBody); /* preamble start */ /* setup font definitions in frame */ SetupFont(headfont.ft_universal_name, headfont.ft_size * 10., F_headfont); SetupFont(bodyfont.ft_universal_name, bodyfont.ft_size * 10., F_bodyfont); SetupFont(italicfont.ft_universal_name, headfont.ft_size * 10., /* use headfont's size */ F_italicfont); /* remember special fonts */ if (strcmp(bodyfont.ft_leaf_name, "Terminal") == 0) { special_font = Font_Terminal; } /* save scaling transform that uses 1/10 point co-ordinate system */ top_margin = (rflg ? Page_width : Page_length) - Orig_y; bottom_margin = 2 * Sixth_INCH; right_margin = (rflg ? Page_length : Page_width) - Orig_x; column_separation = (right_margin - Orig_x) / columns; line_spacing = (bodyfont.ft_size + 2) * 10; if (rflg) { /* we need a rotation transform, too */ Rotate(90.); Translate((double)Page_width, (double)0); } AppendRational(353L,10000000L); Op(scale); if (rflg) { Op(concat); Op(concat); } AppendInteger((long) F_transform); Op(fset); Op(endBody); /* end preamble */ if (argc == 0) { /* no filenames -- do standard input */ filename = NULL; do_file(stdin); } for (; argc > 0; argc--, argv++) { filename = argv[0]; if (strcmp(filename, "-") == 0) { /* this is really standard input */ filename = NULL; do_file(stdin); } else { /* open the file */ if ((file = fopen(filename, "r")) == NULL) { system_error(filename); } else { do_file(file); (void) fclose(file); } } } /* wrap up the output */ ip_select(ipress_file); Op(endBlock); ip_close(); /* send to the printer */ if (send_to_printer) { if (pages_printed == 0) { /* don't print anything but remove temporary */ #ifdef vax11c delete(output); #else (void) unlink(output); #endif } else { #ifdef vax11c char buff[256]; int wait= 0; $DESCRIPTOR(buffdesc,buff); (void) strcpy(buff,"xpress/noformat "); (void) strcat(buff,output); buffdesc.dsc$w_length= strlen(buff); if ( (error= lib$spawn(&buffdesc,0,0,&wait)) != SS$_NORMAL ) { fprintf(stderr,"\nFile %s contains interpress master\n",output); exit(error); } delete(output); #else char *buff; /* exec a "qip" to queue the file */ buff = allocate(strlen(name) + 1 + 1); (void) strcpy(buff, "F"); (void) strcat(buff, name); execl(QIP, "qip", "-c", copies, "-nc", "-nk", "-t", banner, "-x", buff, output, 0); perror(QIP); fprintf(stderr, "File %s contains interpress master.\n", output); #endif } } } get_options() { while (--argc > 0) { argv++; if (!process_arg()) { break; } } } /* * unravel_pages(str, spec) - unravel the page range specification in "str" * into integer pairs in "spec". The first two * ints in "spec" bound the first range of pages, * the next two bound the second range, and so on. * The array is terminated with the pair 0, 0. */ unravel_pages(str, spec) char *str; int *spec; { int last_num = 0; int this_num = 0; register char ch; boolean is_range = No; boolean bad_spec = No; boolean done = No; # define Start_new_num (last_num = this_num, this_num = 0) while (!done) { if ((ch = *str++) == '\0') { /* set "done" flag and pretend it's the end of a number */ done = Yes; ch = ','; } if (ch >= '0' && ch <= '9') { this_num *= 10; this_num += ch - '0'; } else if (ch == '-') { if (this_num < last_num && *str != '\0') { bad_spec = Yes; } *spec++ = this_num; Start_new_num; is_range = Yes; } else if (ch == ',') { if (this_num < last_num) { bad_spec = Yes; } *spec++ = this_num; if (is_range) { is_range = No; } else { *spec++ = this_num; } Start_new_num; } else { fprintf(stderr, "%s: bad character in page specification\n", myname); exit(1); } } if (*--spec == 0) { *spec = 1 << 15; /* infinity */ } if (bad_spec) { fprintf(stderr, "%s: pages should be given in non-descending order.\n", myname); } } process_arg() { register char ch; register int temp; register char *p1; register char *p2; if (argv[0][0] == '-') { if ((ch = argv[0][1]) > '0' && ch <= '9') { /* this is a column count specifier */ columns = ch - '0'; } else switch(ch) { case '\0': /* not an option */ return(No); case 'b': banner = next_arg(); break; case 'c': temp = atoi(copies = next_arg()); if (temp < 1) { fprintf(stderr, "%s: bogus number of copies; you only get one!\n", myname); copies = "1"; } break; case 'f': bodyfont_name = next_arg(); break; case 'F': headfont_name = next_arg(); break; case 'H': /* replace header */ header = next_arg(); break; case 'h': /* append to header */ p1 = next_arg(); p2 = allocate(strlen(header) + strlen(p1) + 1); (void) strcpy(p2, header); (void) strcat(p2, " "); (void) strcat(p2, p1); header = p2; break; case 'l': tflg = lflg = Yes; break; case 'n': name = next_arg(); break; case 'o': output = next_arg(); send_to_printer = No; break; case 'r': rflg = Yes; break; case 'R': rflg = No; break; case 's': pages = next_arg(); break; case 't': tflg = Yes; break; default: fprintf(stderr, "%s: unknown option '%c'\n", myname, ch); } return(Yes); } else { return(No); } } char *next_arg() { if (argv[0][2] == '\0') { if (--argc > 0) { return((++argv)[0]); } else { argv++; return(NULL); } } else { return(&(argv[0][2])); } } /* * establish_font(name, font) - break apart the parts of the string "name" * and fill in the structure pointed to by * "font". Also, verify that the font requested * actually exists. This routine also * understands universal font names. */ establish_font(name, font) char *name; struct font *font; { register char *unamep; register char *ptr; char *slashp; register int size; if (name[0] != '/') { /* not a universal name -- put the default on the front */ font->ft_universal_name = unamep = allocate(strlen(Default_universal_prefix) + strlen(name) + 1); (void) strcpy(unamep, Default_universal_prefix); (void) strcat(unamep, name); } else { /* already is a universal name -- just allocate space for it */ font->ft_universal_name = unamep = allocate(strlen(name)); /* copy in the whole name, without the leading slash */ (void) strcpy(unamep, name + 1); } /* strip size off the end, if it is there */ if ((slashp = ptr = rindex(unamep, '/')) != NULL) { register char ch; size = 0; while ((ch = *++ptr) != '\0') { if (ch < '0' || ch > '9') { /* last element is not a number -- no point size */ size = 0; break; } /* shift this digit in */ size *= 10; size += (ch - '0'); } /* if no point size, use default */ if (size == 0) { font->ft_size = 10; } else { font->ft_size = size; *slashp = '\0'; } } /* set pointer to last element */ if ((ptr = rindex(unamep, '/')) != NULL) { font->ft_leaf_name = ptr + 1; } else { font->ft_leaf_name = font->ft_universal_name; } } do_file(file) FILE *file; { char *src; char *dest; char input_line[Line_size]; char line_buffer[Line_size]; char ch; int current_line; int lines_on_page; int length; int column; #ifndef vax11c /* fstat it to get information displayed in the header */ if (fstat(fileno(file), &file_stat) == -1) { system_error("fstat botched"); return; } #endif /* reset essentials */ page_number = 0; line_number = 1; lines_on_page = 0; curr_page_select = page_select; if (pages != NULL) { page_low = page_select[0]; page_high = page_select[1]; } current_line = top_margin; /* * Strangeness: page_number is incremented by page_start and * line_number is incremented in the "while(fgets..." loop. */ /* start the first page */ page_start(); /* * More strangeness: we had to set line_number to 1 to trick * page_start into reporting the right line count in the header. Now * we reset it to 0 before entering the read/print loop. */ line_number = 0; while (fgets(input_line, Line_size, file) != NULL) { /* new line */ line_number++; /* remember the length */ length = strlen(input_line); /* nuke any trailing newline */ if (input_line[length - 1] == '\n') { input_line[--length] = '\0'; } if (lflg ? lines_on_page >= 66 : current_line < bottom_margin) { /* start a new page */ page_end(No); page_start(); lines_on_page = 0; /* remember, y goes backwards */ current_line = top_margin; } /* make sure that the line actually contains something */ if (input_line[0] != '\0') { /* set x and y for the beginning of the line */ Setxy((double)left_margin, (double)current_line); /* copy from input_line to line_buffer making any necessary changes along the way */ column = 0; src = input_line; dest = line_buffer; while ((ch = *src) != '\0') { switch(ch) { case '\f': /* new page after this line */ current_line = bottom_margin; lines_on_page = 66; break; case '\t': /* tab expansion */ do { *dest++ = ' '; column++; } while (column % tab_amount != 0); break; case '$': *dest++ = '\244'; column++; break; case '-': if (special_font == Font_Terminal) { /* heavy hackery here */ *dest = '\0'; ShowString(line_buffer); Setyrel(-20.); ShowString("\305"); Setyrel(20.); dest = line_buffer; column++; break; } /* else fall thru ... */ default: *dest++ = ch; column++; } src++; } *dest = '\0'; if (line_buffer[0] != '\0') { ShowString(line_buffer); } } /* advance the line counters */ current_line -= line_spacing; lines_on_page++; } /* wrap up the file */ page_end(Yes); } /* * page handling: a distinction is made between virtual pages and actual * pages. A virtual page is one series of lines from the file that appears * vertically on the printed page. The actual page is the page as the * printer prints it (a printed page, if you will). There may be several * virtual pages on one actual page. The page_start and page_end routines * that follow start and terminate virtual pages. The mapping between * virtual and actual pages is a function of the options specified by the * user. If the user requests two column output then there will be two * virtual pages for every actual page. These pages will sit side-by-side on * the actual page. The mapping is accomplished by changing the variables * left_margin and right_margin. "page_start" also handles printing of the * page header, since there is only one of these on every actual page. */ static int current_column; page_start() { boolean in_set; #ifdef vax11c long bintim; #endif /* reset the column count if starting a new file */ if (line_number == 1) { current_column = 0; } /* either move the left margin or put out a new page */ if (current_column != 0) { left_margin += column_separation; } else { /* increment page count and reset margin */ page_number++; left_margin = Orig_x; /* is it in the page specification set? */ if (page_select == NULL) { /* every page is in the set if there is no specification */ in_set = Yes; } else { if (page_low <= page_number && page_number <= page_high) { in_set = Yes; ip_select(ipress_file); if (page_number == page_high) { /* at the top of the current range -- time to move up */ curr_page_select += 2; page_low = curr_page_select[0]; page_high = curr_page_select[1]; } } else { /* not in set -- redirect output to null device */ in_set = No; ip_select(null_file); } } if (in_set) { register char *src; register char *dst; register char ch; /* increment total page count */ pages_printed++; /* output stuff for new ip page */ Op(beginBody); /* set the transformation */ Fget(F_transform); Op(concatt); /* build the header if we need to print it */ if (!tflg) { /* move characters from header to real_header */ /* and expand format items along the way. */ src = header; dst = real_header; while ((ch = *src) != '\0') { if (ch == '%') { switch(ch = *++src) { case 'f': /* file name */ dst = strecpy(dst, filename == NULL ? "Standard input" : filename); break; case 't': /* mtime */ #ifdef vax11c time(&bintim); strncpy(dst,ctime(&bintim), 24); dst += 24; #else /* * ctime returns a 26 character string that * has a newline and null at the end. * 26 - 2 == 24. */ if (file_stat.st_mtime != 0) { (void) strncpy(dst,ctime(&file_stat.st_mtime),24); dst += 24; } #endif break; case 'p': /* page number */ dst = itoa(dst, page_number); break; case 'l': /* line number */ dst = itoa(dst, line_number); break; case '\0': /* end of the string */ src--; /* maintain loop invariant */ break; default: /* copy the character */ *dst++ = ch; /* break; */ } } else { *dst++ = ch; } src++; } /* terminate the real header */ *dst = '\0'; /* display the header */ Setxy((double)(left_margin - Header_to_orig_x), (double)(top_margin + Header_to_orig_y)); Setfont(F_headfont); ShowString(real_header); } } } /* select the body font */ Setfont(F_bodyfont); } page_end(eof) int eof; { if ((current_column = ++current_column % columns) == 0 || eof) { Op(endBody); } } char *strecpy(dest, src) register char *src; register char *dest; { while (*dest++ = *src++) ; return(--dest); } char *itoa(buff, val) char *buff; int val; { char tbuff[12]; /* will build number here -- max of 10 digits */ register char *ptr = tbuff + 11; *ptr-- = '\0'; while (val != 0) { *ptr-- = (val % 10) + '0'; val /= 10; } return(strecpy(buff, ++ptr)); } /* * allocate(space) - allocate "space" bytes with sbrk. This routine uses a * fairly naive algorithm. It sbrk-s space in Break_size * chunks and allocates space from a chunk until a request * for more space than is left in the chunk is made. Then, * it allocates a new chunk. The unused space at the end * of the old chunk remains unused. This does NOT depend * on sbrk returning contiguous chunks of memory during the * life of the program. */ char *allocate(space) int space; { static char *hi_water = NULL; static char *max_alloc = NULL; register char *ptr; /* this works with max_alloc = hi_water = NULL, although we probably shouldn't depend on that! */ if (max_alloc + space > hi_water) { hi_water = sbrk(Break_size); if ((int)hi_water == -1) { system_error("out of space"); exit(1); } max_alloc = hi_water + Break_size - 1; } ptr = hi_water; hi_water += space; return(ptr); } system_error(message) char *message; { int saved_errno; /* value of errno not preserved by fprintf */ saved_errno = errno; fprintf(stderr, "%s: ", myname); errno = saved_errno; perror(message); } #ifdef vax11c char *rindex(string, c) char *string, c; { register char *pos; pos = 0; do { if (*string == c) pos = string; } while (*string++); return(pos); } #endif