1 /* 2 * 3 * picpack - picture packing pre-processor 4 * 5 * A trivial troff pre-processor that copies files to stdout, expanding picture 6 * requests into an in-line format that's passed transparently through troff and 7 * handled by dpost. The program is an attempt to address requirements, expressed 8 * by several organizations, of being able to store a document as a single file 9 * (usually troff input) that can then be sent through dpost and ultimately to 10 * a PostScript printer. 11 * 12 * The program looks for strings listed in the keys[] array at the start of each 13 * line. When a picture request (as listed in keys[]) is found the second string 14 * on the line is taken to be a picture file pathname that's added (in transparent 15 * mode) to the output file. In addition each in-line picture file is preceeded by 16 * device control command (again passed through in transparent mode) that looks 17 * like, 18 * 19 * x X InlinePicture filename bytes 20 * 21 * where bytes is the size of the picture file (which begins on the next line) 22 * and filename is the pathname of the picture file. dpost uses both arguments to 23 * manage in-line pictures (in a big temp file). To handle pictures in diversions 24 * picpack reads each input file twice. The first pass looks for picture inclusion 25 * requests and copies each picture file transparently to the output file, while 26 * second pass just copies the input file to the output file. Things could still 27 * break, but the two pass method should handle most jobs. 28 * 29 * The recognized in-line picture requests are saved in keys[] and by default only 30 * expand .BP and .PI macro calls. The -k option changes the recognized strings, 31 * and may be needed if you've built your own picture inclusion macros on top of 32 * .BP or .PI or decided to list each picture file at the start of your input file 33 * using a dummy macro. For example you could require every in-line picture be 34 * named by a dummy macro (say .iP), then the command line, 35 * 36 * picpack -k.iP file > file.pack 37 * 38 * hits on lines that begin with .iP (rather than .BP or .PI), and the only files 39 * pulled in would be ones named as the second argument to the new .iP macro. The 40 * -k option accepts a space or comma separated list of up to 10 different key 41 * strings. picpack imposes no contraints on key strings, other than not allowing 42 * spaces or commas. A key string can begin with \" and in that case it would be 43 * troff comment. 44 * 45 * Although the program will help some users, there are obvious disadvantages. 46 * Perhaps the most important is that troff output files (with in-line pictures 47 * included) don't fit the device independent language accepted by important post 48 * processors like proof, and that means you won't be able to reliably preview a 49 * packed file on your 5620 or whatever. Another potential problem is that picture 50 * files can be large. Packing everything together in a single file at an early 51 * stage has a better chance of exceeding your system's ulimit. 52 * 53 */ 54 55 #include <stdio.h> 56 #include <sys/types.h> 57 #include <sys/stat.h> 58 #include <string.h> 59 60 #include "gen.h" /* general purpose definitions */ 61 #include "ext.h" /* external variable definitions */ 62 #include "path.h" /* just for TEMPDIR definition */ 63 64 char *keys[11] = {".BP", ".PI", NULL}; 65 int quiet = FALSE; 66 67 FILE *fp_in = stdin; /* input */ 68 FILE *fp_out = stdout; /* and output files */ 69 70 /*****************************************************************************/ 71 72 main(agc, agv) 73 74 int agc; 75 char *agv[]; 76 77 { 78 79 /* 80 * 81 * A picture packing pre-processor that copies input files to stdout, expanding 82 * picture requests (as listed in keys[]) to an in-line format that can be passed 83 * through troff (using transparent mode) and handled later by dpost. 84 * 85 */ 86 87 argc = agc; /* global so everyone can use them */ 88 argv = agv; 89 90 prog_name = argv[0]; /* just for error messages */ 91 92 options(); /* command line options */ 93 arguments(); /* translate all the input files */ 94 done(); /* clean things up */ 95 96 exit(x_stat); /* everything probably went OK */ 97 98 } /* End of main */ 99 100 /*****************************************************************************/ 101 102 options() 103 104 { 105 106 int ch; /* name returned by getopt() */ 107 108 extern char *optarg; /* option argument set by getopt() */ 109 extern int optind; 110 111 /* 112 * 113 * Handles the command line options. 114 * 115 */ 116 117 while ( (ch = getopt(argc, argv, "k:qDI")) != EOF ) { 118 switch ( ch ) { 119 case 'k': /* new expansion key strings */ 120 newkeys(optarg); 121 break; 122 123 case 'q': /* disables "missing picture" messages */ 124 quiet = TRUE; 125 break; 126 127 case 'D': /* debug flag */ 128 debug = ON; 129 break; 130 131 case 'I': /* ignore FATAL errors */ 132 ignore = ON; 133 break; 134 135 case '?': /* don't know the option */ 136 error(FATAL, ""); 137 break; 138 139 default: 140 error(FATAL, "missing case for option %c", ch); 141 break; 142 } /* End switch */ 143 } /* End while */ 144 145 argc -= optind; /* get ready for non-options args */ 146 argv += optind; 147 148 } /* End of options */ 149 150 /*****************************************************************************/ 151 152 newkeys(list) 153 154 char *list; /* comma or space separated key strings */ 155 156 { 157 158 char *p; /* next key string from *list */ 159 int i; /* goes in keys[i] */ 160 int n; /* last key string slot in keys[] */ 161 162 /* 163 * 164 * Separates *list into space or comma separated strings and adds each to the 165 * keys[] array. The strings in keys[] are used to locate the picture inclusion 166 * requests that are translated to the in-line format. The keys array must end 167 * with a NULL pointer and by default only expands .BP and .PI macro calls. 168 * 169 */ 170 171 n = (sizeof(keys) / sizeof(char *)) - 1; 172 173 for ( i = 0, p = strtok(list, " ,"); p != NULL; i++, p = strtok(NULL, " ,") ) 174 if ( i >= n ) 175 error(FATAL, "too many key strings"); 176 else keys[i] = p; 177 178 keys[i] = NULL; 179 180 } /* End of newkeys */ 181 182 /*****************************************************************************/ 183 184 arguments() 185 186 { 187 188 FILE *copystdin(); 189 190 /* 191 * 192 * Makes sure all the non-option command line arguments are processed. If we get 193 * here and there aren't any arguments left, or if '-' is one of the input files 194 * we process stdin, after copying it to a temporary file. 195 * 196 */ 197 198 if ( argc < 1 ) { 199 fp_in = copystdin(); 200 picpack(); 201 } else 202 while ( argc > 0 ) { 203 if ( strcmp(*argv, "-") == 0 ) 204 fp_in = copystdin(); 205 else if ( (fp_in = fopen(*argv, "r")) == NULL ) 206 error(FATAL, "can't open %s", *argv); 207 picpack(); 208 fclose(fp_in); 209 argc--; 210 argv++; 211 } /* End while */ 212 213 } /* End of arguments */ 214 215 /*****************************************************************************/ 216 217 FILE *copystdin() 218 219 { 220 221 char *tfile; /* temporary file name */ 222 int fd_out; /* and its file descriptor */ 223 FILE *fp; /* return value - the new input file */ 224 225 /* 226 * 227 * Copies stdin to a temp file, unlinks the file, and returns the file pointer 228 * for the new temporary file to the caller. Needed because we read each input 229 * file twice in an attempt to handle pictures in diversions. 230 * 231 */ 232 233 if ( (tfile = tempnam(TEMPDIR, "post")) == NULL ) 234 error(FATAL, "can't generate temp file name"); 235 236 if ( (fd_out = creat(tfile, 0660)) == -1 ) 237 error(FATAL, "can't create %s", tfile); 238 239 copyfile(fileno(stdin), fd_out); 240 close(fd_out); 241 242 if ( (fp = fopen(tfile, "r")) == NULL ) 243 error(FATAL, "can't open %s", tfile); 244 245 unlink(tfile); 246 return(fp); 247 248 } /* End of copystdin */ 249 250 /*****************************************************************************/ 251 252 copyfile(fd_in, fd_out) 253 254 int fd_in; /* input */ 255 int fd_out; /* and output files */ 256 257 { 258 259 char buf[512]; /* internal buffer for reads and writes */ 260 int count; /* number of bytes put in buf[] */ 261 262 /* 263 * 264 * Copies file fd_in to fd_out. Handles the second pass for each input file and 265 * also used to copy stdin to a temporary file. 266 * 267 */ 268 269 while ( (count = read(fd_in, buf, sizeof(buf))) > 0 ) 270 if ( write(fd_out, buf, count) != count ) 271 error(FATAL, "write error"); 272 273 } /* End of copyfile */ 274 275 /*****************************************************************************/ 276 277 done() 278 279 { 280 281 /* 282 * 283 * Finished with all the input files - unlink the temporary file that was used 284 * to record the in-line picture file pathnames. 285 * 286 */ 287 288 if ( temp_file != NULL ) 289 unlink(temp_file); 290 291 } /* End of done */ 292 293 /*****************************************************************************/ 294 295 picpack() 296 297 { 298 299 char line[512]; /* next input line */ 300 char name[100]; /* picture file names - from BP or PI */ 301 int i; /* for looking through keys[] */ 302 303 /* 304 * 305 * Handles the two passes over the next input file. First pass compares the start 306 * of each line in *fp_in with the key strings saved in the keys[] array. If a 307 * match is found inline() is called to copy the picture file (ie. the file named 308 * as the second string in line[]) to stdout, provided the file hasn't previously 309 * been copied. The second pass goes back to the start of fp_in and copies it all 310 * to the output file. 311 * 312 */ 313 314 while ( fgets(line, sizeof(line), fp_in) != NULL ) { 315 for ( i = 0; keys[i] != NULL; i++ ) 316 if ( strncmp(line, keys[i], strlen(keys[i])) == 0 ) { 317 if ( sscanf(line, "%*s %s", name) == 1 ) { 318 strtok(name, "("); 319 if ( gotpicfile(name) == FALSE ) 320 inline(name); 321 } /* End if */ 322 } /* End if */ 323 } /* End while */ 324 325 fflush(fp_out); /* second pass - copy fp_in to fp_out */ 326 fseek(fp_in, 0L, 0); 327 copyfile(fileno(fp_in), fileno(fp_out)); 328 329 } /* End of picpack */ 330 331 /*****************************************************************************/ 332 333 inline(name) 334 335 char *name; /* name of the in-line picture file */ 336 337 { 338 339 long size; /* size in bytes - from fstat */ 340 FILE *fp; /* for reading *name */ 341 int ch; /* next character from picture file */ 342 int lastch = '\n'; /* so we know when to put out \! */ 343 344 struct stat sbuf; /* for the picture file size */ 345 346 /* 347 * 348 * Copies the picture file *name to the output file in an in-line format that can 349 * be passed through troff and recovered later by dpost. Transparent mode is used 350 * so each line starts with \! and all \ characters must be escaped. The in-line 351 * picture sequence begins with an "x X InlinePicture" device control command that 352 * names the picture file and gives its size (in bytes). 353 * 354 */ 355 356 if ( (fp = fopen(name, "r")) != NULL ) { 357 fstat(fileno(fp), &sbuf); 358 if ( (size = sbuf.st_size) > 0 ) { 359 fprintf(fp_out, "\\!x X InlinePicture %s %ld\n", name, size); 360 while ( (ch = getc(fp)) != EOF ) { 361 if ( lastch == '\n' ) 362 fprintf(fp_out, "\\!"); 363 if ( ch == '\\' ) 364 putc('\\', fp_out); 365 putc(lastch = ch, fp_out); 366 } /* End while */ 367 if ( lastch != '\n' ) 368 putc('\n', fp_out); 369 } /* End if */ 370 fclose(fp); 371 addpicfile(name); 372 } else if ( quiet == FALSE ) 373 error(NON_FATAL, "can't read picture file %s", name); 374 375 } /* End of inline */ 376 377 /*****************************************************************************/ 378 379 gotpicfile(name) 380 381 char *name; 382 383 { 384 385 char buf[100]; 386 FILE *fp_pic; 387 388 /* 389 * 390 * Checks the list of previously added picture files in *temp_file and returns 391 * FALSE if it's a new file and TRUE otherwise. Probably should open the temp 392 * file once for update and leave it open, rather than opening and closing it 393 * every time. 394 * 395 */ 396 397 if ( temp_file != NULL ) 398 if ( (fp_pic = fopen(temp_file, "r")) != NULL ) { 399 while ( fscanf(fp_pic, "%s", buf) != EOF ) 400 if ( strcmp(buf, name) == 0 ) { 401 fclose(fp_pic); 402 return(TRUE); 403 } /* End if */ 404 fclose(fp_pic); 405 } /* End if */ 406 407 return(FALSE); 408 409 } /* End of gotpicfile */ 410 411 /*****************************************************************************/ 412 413 addpicfile(name) 414 415 char *name; 416 417 { 418 419 FILE *fp_pic; 420 421 /* 422 * 423 * Adds string *name to the list of in-line picture files that's maintained in 424 * *temp_file. Should undoubtedly open the file once for update and use fseek() 425 * to move around in the file! 426 * 427 */ 428 429 if ( temp_file == NULL ) 430 if ( (temp_file = tempnam(TEMPDIR, "picpac")) == NULL ) 431 return; 432 433 if ( (fp_pic = fopen(temp_file, "a")) != NULL ) { 434 fprintf(fp_pic, "%s\n", name); 435 fclose(fp_pic); 436 } /* End if */ 437 438 } /* End of addpicfile */ 439 440 /*****************************************************************************/ 441 442