/* * $Id: cpsencode.c,v 1.1.1.1.4.1 2002/01/17 17:42:52 pwessel Exp $ * * Copyright (c) 1999-2002 by P. Wessel * See COPYING file for copying and redistribution conditions. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Contact info: www.soest.hawaii.edu/wessel *--------------------------------------------------------------------*/ /* * cpsencode - Generating Complete PostScript (cps) files that * contain all the commands and the data required * to recreate the plot. * * Author: Paul Wessel, SOEST, U. of Hawaii * Version: 1.3 * Date: 01-MAR-2000 * * cpsencode should be called at the end of a shell script that * produces a single plot and it will append the script and any * required data files to the PostScript file as comments after * compressing and encoding the files. * * e.g., * cpsencode Figure_3.csh >> Figure_3.ps * * To recover the script and the data files, run * * cpsdecode Figure_3.ps * * Some options to choose what kind of encoding and compression exist, * see the man page. * * WIN32: Note we open all files (except input script) in binary mode * since we dont know if files are binary or ascii. We must therefore * deal explicitly with CR/LF issues. */ #include "cps.h" #include "uu.h" /* cpsencode knows about several program names that it can safely skip without testing to see if they are data files. It also skips various shell/DOS built-in commands and constructs. */ #define N_GMT_PROGS 87 #ifdef WIN32 #define N_DOS_PROGS 95 #define N_PROGS (N_DOS_PROGS+N_GMT_PROGS) char *programs[N_PROGS] = { #include "cpsdos.h" #include "cpsgmt.h" }; #define FILE_POS 9 #else #define N_UNIX_PROGS 64 #define N_PROGS (N_UNIX_PROGS+N_GMT_PROGS) char *programs[N_PROGS] = { #include "cpsunix.h" #include "cpsgmt.h" }; #define FILE_POS 6 #endif int encode_file (char *file, int mode, int compress, int encode, int verbose, int do_it); int new_file (char *file, int *mode, int *executeable, int want_executable); int is_wildcard (char word[]); char **wildcard_expand (char word[], int *n); int get_mode (char *file, int *executable); char **written; int n_written = 0, n_alloc = 50; main (int argc, char **argv) { int i, k = -1, len, n_files, n_bad, n_root = 0, ndir, mode; int single_quote_on, double_quote_on, is_file, skip_inline_data, verbose = FALSE, executable; int more, error = FALSE, compress = TRUE, encode = TRUE, changed_dir = FALSE, want_executable = FALSE; int use_tilde = FALSE, hard_path = FALSE, do_it = TRUE, listing = FALSE; char buffer[BUFSIZ]; char *word, EOF_word[120], previous_word[120], **dir; FILE *fp; for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'e': case 'E': encode = FALSE; break; case 'n': case 'N': do_it = FALSE; listing = TRUE; verbose = 2; break; case 'u': case 'U': compress = FALSE; break; case 'v': case 'V': verbose = TRUE; break; case 'x': case 'X': want_executable = TRUE; break; default: error = TRUE; break; } } else k = i; } if (k < 0) { fprintf (stderr, "cpsencode: No script given!\n"); error++; } if (compress && !encode) { fprintf (stderr, "cpsencode: ERROR. Compress requires encoding\n"); error++; } if (argc < 2 || error) { fprintf (stderr, "cpsencode %s - Embed scripts and data files in a CPS Postscript file\n\n", CPS_VERSION); fprintf (stderr, "usage: cpsencode script [-e] [-n] [-u] [-v] [-x] >> psfile\n\n"); fprintf (stderr, " OPTIONS\n"); fprintf (stderr, " -e will NOT encode files. Requires -u [Default will]\n"); fprintf (stderr, " -n will NOT do anything but show which files to encode\n"); fprintf (stderr, " -u will NOT compress files first [Default will]\n"); fprintf (stderr, " -v will report on progress [Default is silent]\n"); fprintf (stderr, " -x will embed executable files [Default skips executables]\n"); exit (EXIT_FAILURE); } mode = get_mode (argv[k], &executable); if ((fp = fopen (argv[k], "r")) == NULL) { fprintf (stderr, "cpsencode: Could not open %s\n", argv[k]); exit (EXIT_FAILURE); } written = (char **) malloc ((size_t)(n_alloc * sizeof (char *))); if (do_it) { /* Logically terminate PostScript plot with EOF comment */ printf ("%%%%EOF\n"); /* Then append information about what is to come */ printf ("%%%%CPS-I:-------------------------------------------------------------------\n"); printf ("%%%%CPS-I:\tHere lies shell script and possibly data files\n"); printf ("%%%%CPS-I:\tMade by cpsencode from master script file %s\n", argv[k]); printf ("%%%%CPS-I:\tRun cpsdecode"); if (!encode) printf (" -e"); if (!compress) printf (" -u"); printf (" -v on PostScript file to extract script and files\n"); printf ("%%%%CPS-I:\tCPS_MARKER = %%\n"); /* New since v 1.3 - older files have %%CPS| */ printf ("%%%%CPS-I:-------------------------------------------------------------------\n"); } /* Encode the shell script commands in plain ASCII */ if (listing) { fprintf (stderr, "-------------------------------------------------------------------\n"); fprintf (stderr, "Embedded contents for CPS file produced by script: %s\n", argv[k]); fprintf (stderr, "Compressed?: %s\n", (compress) ? "YES" : "NO"); fprintf (stderr, "Encoded?: %s\n", (encode) ? "YES" : "NO"); fprintf (stderr, "CPS Marker: %%\n"); fprintf (stderr, "Filenames of embedded files follow:\n"); fprintf (stderr, "-------------------------------------------------------------------\n"); } if (do_it) { if (verbose) fprintf (stderr, "cpsencode: embed %s [COPY]\n", argv[k]); printf ("%s%s %o\n", CPS_FLAG, argv[k], mode); while (fgets (buffer, BUFSIZ, fp)) printf ("%%%s", buffer); rewind (fp); } else if (listing) fprintf (stderr, "%s\n", argv[k]); (void) new_file (argv[k], &mode, &executable, want_executable); /* Mark script as read */ /* Now scan script for filenames whose contents we must encode */ n_files = 1; n_bad = 0; skip_inline_data = FALSE; while (fgets (buffer, BUFSIZ, fp)) { if (buffer[0] == 0) continue; /* Skip blank lines */ buffer[strlen(buffer)-1] = 0; /* Get rid of the newline character */ #ifdef WIN32 if (!strncmp (buffer, "REM CPS: ", FILE_POS)) { /* Special file to be included as is */ #else if (!strncmp (buffer, "#CPS: ", FILE_POS)) { /* Special file to be included as is */ #endif word = &buffer[FILE_POS]; /* First check for wild cards */ if (is_wildcard (word)) { /* Wild cards are present - use UNIX ls (or DOS dir) to expand them */ if (word[0] == '~') use_tilde = TRUE; dir = wildcard_expand (word, &ndir); for (i = 0; i < ndir; i++) { /* For each expanded file */ if (new_file (dir[i], &mode, &executable, want_executable)) { /* File exists for reading */ n_bad += encode_file (dir[i], mode, compress, encode, verbose, do_it); if (dir[i][0] == DIR_DELIM) n_root++; #ifdef WIN32 if (strlen(dir[i]) > 1 && dir[i][1] == ':') hard_path = TRUE; #endif n_files++; } free ((void *)dir[i]); } free ((void *)dir); } else { /* Perhaps a single file */ if (new_file (word, &mode, &executable, want_executable)) { /* File exists for reading */ n_bad += encode_file (word, mode, compress, encode, verbose, do_it); if (word[0] == DIR_DELIM) n_root++; #ifdef WIN32 if (strlen(word) > 1 && word[1] == ':') hard_path = TRUE; #endif n_files++; } } } #ifdef WIN32 if (!strncmp (buffer, "REM", 3)) continue; /* Skip DOS comments */ if (!strncmp (buffer, "DEL", 3)) continue; /* Skip DOS delete lines */ if (!strncmp (buffer, "del", 3)) continue; /* Skip DOS delete lines */ #else if (buffer[0] == '#') continue; /* Skip comments */ if (!strncmp (buffer, "REM", 3)) continue; /* Skip DOS comments */ if (!strncmp (buffer, "rm ", 3)) continue; /* Skip Unix delete lines */ if (!strncmp (buffer, "\\rm ", 4)) continue; /* Skip Unix delete lines */ #endif /* Replace spaces inside quotes with underscores. This ensures that text labels like -B3f1:"The x label": is seen as one word (and thus skipped because of -) */ single_quote_on = double_quote_on = FALSE; for (i = 0; buffer[i]; i++) { if (buffer[i] == '`') buffer[i] = ' '; /* Get rid of backquotes for commands */ if (buffer[i] == '(' || buffer[i] == ')') buffer[i] = ' '; /* Get rid of parenthesis */ if (buffer[i] == '"') double_quote_on = !double_quote_on; if (buffer[i] == '\'') single_quote_on = !single_quote_on; if ((double_quote_on || single_quote_on) && buffer[i] == ' ') buffer[i] = '_'; } /* Use strtok to read line one word at the time */ word = strtok (buffer, " \t"); more = TRUE; while (word && more) { len = strlen(word); is_file = TRUE; if (word[0] == '[' && len == 1) is_file = FALSE; /* Skip single [ */ if (word[0] == ']') is_file = FALSE; /* Skip word starting with ] */ if (word[0] == '\'') is_file = FALSE; /* Skip word starting with quote */ if (word[0] == '"') is_file = FALSE; /* Skip word starting with quote */ if (word[0] == '+') is_file = FALSE; /* Skip plus */ if (word[0] == '=') is_file = FALSE; /* Skip equals */ if (word[0] == '>') is_file = FALSE; /* Skip redirect operator */ if (word[0] == '|') is_file = FALSE; /* Skip pipe */ if (word[0] == '$') is_file = FALSE; /* Skip shell variable */ if (word[0] == '\\') is_file = FALSE; /* Skip backslash */ if (word[0] == '&') is_file = FALSE; /* Skip ampersands */ if (word[0] == ';') is_file = FALSE; /* Skip semicolons */ if (word[0] == '!') is_file = FALSE; /* Skip exclamations */ if (word[0] == '@') is_file = FALSE; /* Skip at signs */ if (word[0] == '%') is_file = FALSE; /* Skip % signs */ if (word[0] == '^') is_file = FALSE; /* Skip hat signs */ if (word[0] == ':') is_file = FALSE; /* Skip colons */ if (word[0] == '#') is_file = more = FALSE; /* Skip comments */ if (skip_inline_data && !strcmp (word, EOF_word)) { /* End of inline data started with << */ is_file = FALSE; skip_inline_data = FALSE; } else if (!strcmp(word, "<<")) { /* Beginning of inline data started with << EOF_word */ strcpy (previous_word, word); word = strtok (NULL, " \t"); is_file = FALSE; strcpy (EOF_word, word); skip_inline_data = TRUE; } else if (len > 3 && !strncmp(&word[len-3], ".ps", 3) && previous_word[0] == '>') { /* Skip PostScript files */ is_file = FALSE; } else if (len > 4 && !strncmp(&word[len-4], ".eps", 4) && previous_word[0] == '>') { /* Skip EPS files */ is_file = FALSE; } /* Check if user changes directories */ if (!strcmp (word, "cd") || !strcmp (word, "cddir")) changed_dir = TRUE; /* Check if word is one of recognized programs - if so skip it */ for (i = 0; is_file && i < N_PROGS; i++) if (!strcmp (word, programs[i])) is_file = FALSE; /* Special check for the convention -?.cpt or -?.grd which occurs so frequently in GMT */ if (is_file && word[0] == '-') { /* Allow files to be passed as option arguments -? */ if (len > 2) { /* Get rid of the leading option switch and consider this a potential file name */ i = 2; while (word[i]) { word[i-2] = word[i]; i++; } word[i-2] = '\0'; } else is_file = FALSE; } if (!skip_inline_data && is_file) { /* word may a readable file(s), if so must encode it(them) in the PostScript file */ /* First check for wild cards */ if (is_wildcard (word)) { /* Wild cards are present - use UNIX ls (or DOS dir) to expand them */ if (word[0] == '~') use_tilde = TRUE; dir = wildcard_expand (word, &ndir); for (i = 0; i < ndir; i++) { /* For each expanded file */ if (new_file (dir[i], &mode, &executable, want_executable)) { /* File exists for reading */ n_bad += encode_file (dir[i], mode, compress, encode, verbose, do_it); if (dir[i][0] == DIR_DELIM) n_root++; #ifdef WIN32 if (strlen(dir[i]) > 1 && dir[i][1] == ':') hard_path = TRUE; #endif n_files++; } free ((void *)dir[i]); } free ((void *)dir); } else { /* Perhaps a single file */ if (new_file (word, &mode, &executable, want_executable)) { /* File exists for reading */ if (word[0] == DIR_DELIM) hard_path = TRUE; #ifdef WIN32 if (strlen(word) > 1 && word[1] == ':') hard_path = TRUE; #endif n_bad += encode_file (word, mode, compress, encode, verbose, do_it); if (word[0] == DIR_DELIM) n_root++; n_files++; } } } if (!(len == 1 && word[0] == '\\')) strcpy (previous_word, word); /* \ is Unix line continue */ word = strtok (NULL, " \t"); } } fclose (fp); if (listing) { fprintf (stderr, "-------------------------------------------------------------------\n"); fprintf (stderr, "%d files detected (None were embedded)\n", n_files); fprintf (stderr, "-------------------------------------------------------------------\n"); } if (use_tilde) fprintf (stderr, "cpsencode: Filename starting with ~ may not port well\n"); if (hard_path) fprintf (stderr, "cpsencode: Absolute filenames may not port well\n"); if (changed_dir) fprintf (stderr, "cpsencode: Script uses cd, may confuse CPS\n"); if (n_bad) fprintf (stderr, "cpsencode: Unable to encode %d of %d files\n", n_bad, n_files); if (n_root) fprintf (stderr, "cpsencode: Warning: %d files had absolute paths\n", n_root); if (!listing && verbose) fprintf (stderr, "cpsencode: Encoded %d files\n", n_files); for (i = 0; i < n_written; i++) free ((void *)written[i]); exit (EXIT_SUCCESS); } int encode_file (char *file, int mode, int compress, int encode, int verbose, int do_it) { char cmd[BUFSIZ], line[BUFSIZ]; FILE *process; if (verbose == 2) fprintf (stderr, "%s\n", file); else if (verbose) fprintf (stderr, "cpsencode: embed %s [", file); if (compress) { /* Read the file after compressing it */ /* We use COMPRESS to compress the file */ sprintf (cmd, "%s %s\0", COMPRESS, file); if (verbose == 1) fprintf (stderr, "COMPRESS"); if (do_it && (process = popen (cmd, "rb")) == NULL) { fprintf (stderr, "cpsencode: Trouble opening process %s\n", cmd); return (1); } } else { /* Just read directly */ if (do_it && (process = fopen (file, "rb")) == NULL) { fprintf (stderr, "cpsencode: Trouble opening file %s\n", file); return (1); } } if (do_it) printf ("%s%s %o\n", CPS_FLAG, file, mode); /* Filename marker */ if (encode) { /* We use ENCODE to make ASCII */ if ((verbose == 1) && compress) fprintf (stderr, "+"); if (verbose == 1) fprintf (stderr, "ENCODE"); if (do_it) { uu_encoder (process, stdout, NULL, file, "%"); pclose (process); } } else { /* Plain ASCII dump */ if (verbose == 1) fprintf (stderr, "COPY"); if (do_it) { while (fgets (line, BUFSIZ, process)) { #ifdef WIN32 line[strlen(line)-2] = 0; /* Strip off both CR and LF */ printf ("%%%s\n", line); #else printf ("%%%s", line); #endif } fclose (process); } } if (verbose == 1) fprintf (stderr, "]\n"); if (!do_it) return (0); /* Just reporting what whould have been done */ return (0); } int new_file (char *file, int *mode, int *executable, int want_executable) { int i, found; /* Determines if 1) file exist and 2) if it has not yet been written */ if (access (file, R_OK)) return 0; /* Cannot find or read it */ if (!strcmp(file, "..")) return 0; /* Parent directory shorthand */ if (!strcmp(file, ".")) return 0; /* Current directory shorthand */ #ifdef WIN32 if (!strcmp(file, "NUL")) return 0; /* /dev/null */ if (!strcmp(file, "nul")) return 0; /* /dev/null */ #else if (!strcmp(file, "/dev/null")) return 0; /* /dev/null */ #endif /* Ok, now check if we have read this before */ for (i = 0, found = FALSE; !found && i < n_written; i++) found = !strcmp (file, written[i]); if (found) return (0); /* Written already */ /* Must check permissions and ,if necessary, skip executable files */ *mode = get_mode (file, executable); if (*mode < 0) return 0; /* Trouble getting stat - give up */ if (*executable && !want_executable) return (0); /* Skip executables */ /* Here we have a new file to be embedded - add it to list of used files */ if (n_written == n_alloc) { n_alloc += 50; written = (char **) realloc ((void *)written, (size_t)(n_alloc * sizeof (char *))); } written[n_written] = (char *) malloc ((size_t)(strlen(file)+1)); strcpy (written[n_written], file); n_written++; return (1); } int is_wildcard (char word[]) { if (strchr (word, '*') || strchr (word, '?') || strchr (word, '[') || strchr (word, ']') || strchr (word, '{') || strchr (word, '}') || (word[0] == '~')) return (TRUE); return (FALSE); } char **wildcard_expand (char word[], int *n) { char line[BUFSIZ], **flist; FILE *ls; int n_alloc = 25, len; #ifdef WIN32 sprintf (line, "dir /b %s\0", word); #else sprintf (line, "ls %s 2> /dev/null\0", word); #endif if ((ls = popen (line, "r")) == NULL) { fprintf (stderr, "cpsencode: Trouble opening process %s\n", line); exit (EXIT_FAILURE); } flist = (char **) malloc ((size_t)n_alloc * sizeof (char *)); *n = 0; while (fgets (line, BUFSIZ, ls)) { /* For each expanded file */ len = strlen (line); if (line[len-1] == '\n') line[len-1] = '\0'; /* Strip off newline */ flist[*n] = (char *) malloc (strlen(line)+1); strcpy (flist[*n], line); (*n)++; if ((*n) == n_alloc) { n_alloc += 25; flist = (char **) realloc ((void *)flist, (size_t)n_alloc * sizeof (char *)); } } pclose (ls); flist = (char **) realloc ((void *)flist, (size_t)(*n) * sizeof (char *)); return (flist); } int get_mode (char *file, int *executable) { struct STAT sbuf; if (stat (file, &sbuf)) return -1; /* Trouble getting stat - give up */ *executable = ((sbuf.st_mode & IS_EXECUTABLE) > 0); return (sbuf.st_mode & 0777); }