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