/*
 *	$Id: cpsdecode.c,v 1.2.4.2 2002/02/27 17:41:10 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
 *--------------------------------------------------------------------*/
/*
 *  cpsdecode - Decoding Complete PostScript (cps) files that
 *		contain all the commands and the data required
 *		to recreate a plot.
 *
 * Author:	Paul Wessel, SOEST, U. of Hawaii
 * Version:	1.3
 * Date:	01-MAR-2000
 *
 * cpsdecode will extract a plot script and any required data
 * files (including other scripts) from the PostScript file comments.
 *
 * e.g.,
 *	cpsdecode Figure_3.ps
 *
 * 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"

main (int argc, char **argv)
{
	int i, k, n_files, cps_indent;
	unsigned int mode;
	int verbose = FALSE, first, error = FALSE, expand = TRUE, decode = TRUE, listing = FALSE;
	char buffer[BUFSIZ], cmd[BUFSIZ], file[BUFSIZ], *s, *tag, cps_comment[32];
	FILE *fp = stdin, *process;

	for (i = 1; i < argc; i++) {

		if (argv[i][0] == '-') {
			switch (argv[i][1]) {
				case '\0':
					fp = stdin;	/* For folks who are used to give - for stdin */
					break;
				case 'n':
				case 'N':
					listing = TRUE;
					break;
				case 'e':
				case 'E':
					/* Backwards compatibility, option no longer needed */
					fprintf (stderr, "cpsdecode: -e no longer required\n");
					break;
				case 'u':
				case 'U':
					/* Backwards compatibility, option no longer needed */
					fprintf (stderr, "cpsdecode: -u no longer required\n");
					break;
				case 'v':
				case 'V':
					verbose = TRUE;
					break;
				default:
					error = TRUE;
					break;
			}
		}
		else {
			k = i;
			if ((fp = fopen (argv[k], "r")) == NULL) {
				fprintf (stderr, "cpsdecode: Could not open file %s\n", argv[k]);
				exit (EXIT_FAILURE);
			}
		}
	}

	if (argc < 2 || error) {
		fprintf (stderr, "cpsdecode - Extract embedded files from CPS Postscript file\n\n");
		fprintf (stderr, "usage: cpsdecode [PostScriptfile] [-n] [-v]\n\n");
		fprintf (stderr, "	 Give name of PostScript-file or read from stdin\n\n");
		fprintf (stderr, "	 OPTIONS\n");
		fprintf (stderr, "	 -n will NOT extract files, just report on what it would do\n");
		fprintf (stderr, "	 -v will report on progress [Default is silent]\n");
		exit (EXIT_FAILURE);
	}

	if (verbose && fp == stdin)
		fprintf (stderr, "cpsdecode: Reading standard input\n");
	else if (verbose)
		fprintf (stderr, "cpsdecode: Reading %s\n", argv[k]);

	fgets (buffer, BUFSIZ, fp);
	if (strncmp (buffer, "%!PS", 4)) {
		fprintf (stderr, "cpsdecode: %s is not a PostScript file\n", argv[1]);
		exit (EXIT_FAILURE);
	}

	tag = cmd;	/* Just two names for the same space */
	
	n_files = 0;
	first = TRUE;

	/* Skip to first occurrence of %%CPS-I: */

	while ((s = fgets (buffer, BUFSIZ, fp)) && strncmp (buffer, "%%CPS-I:", 8));
	for (i = 0; s && i < 3; i++) s = fgets (buffer, BUFSIZ, fp);	/* Go to the 4th such line */
	if (!s || strncmp (buffer, "%%CPS-I:\tRun cpsdecode", 22)) {	/* Trouble, could not find CPS-I flag? */
		fprintf (stderr, "cpsdecode: %s was not processed by cpsencode\n", argv[1]);
		exit (EXIT_FAILURE);
        } 
	else {	/* OK, get command options */
		expand = !(int)strstr (buffer, " -u");
		decode = !(int)strstr (buffer, " -e");
	}
	s = fgets (buffer, BUFSIZ, fp);
	if (!s || strncmp (buffer, "%%CPS-I:\tCPS_MARKER = ", 22)) {	/* Probably older CPS file, use old default */
		fprintf (stderr, "cpsdecode: WARNING: %s processed by cpsencode v 1.2 or older\n", argv[1]);
		strcpy (cps_comment, "%%CPS|");
        } 
	else {	/* OK, get command options */
		strcpy (cps_comment, "%");
	}
	cps_indent = strlen (cps_comment);
	if (expand && !decode) {
		fprintf (stderr, "cpsdecode: ERROR.  Cannot decompress without first decoding (check PS file)\n");
		exit (EXIT_FAILURE);
	}
	
	if (listing) {	/* Just list what is inside the file - no extraction occurs */
		fprintf (stderr, "-------------------------------------------------------------------\n");
		fprintf (stderr, "Embedded contents of CPS file: %s\n", (fp == stdin) ? "<stdin>" : argv[k]);
		fprintf (stderr, "Compressed?: %s\n", (expand) ? "YES" : "NO");
		fprintf (stderr, "Encoded?:    %s\n", (decode) ? "YES" : "NO");
		fprintf (stderr, "CPS Marker:  %s\n", cps_comment);
		fprintf (stderr, "Filenames of embedded files follow:\n");
		fprintf (stderr, "-------------------------------------------------------------------\n");
		while ((s = fgets (buffer, BUFSIZ, fp))) {
			if (!strncmp (buffer, CPS_FLAG, CPS_FLAG_WIDTH)) {
				buffer[strlen (buffer)-1] = '\0';	/* Chop off newline */
				strcpy (file, &buffer[CPS_FLAG_WIDTH]);	/* Name of this file */
				fprintf (stderr, "%s\n", file);
				n_files++;
			}
		}
		if (fp != stdin) fclose (fp);
		fprintf (stderr, "-------------------------------------------------------------------\n");
		fprintf (stderr, "Total of %d embedded files\n", n_files);
		fprintf (stderr, "-------------------------------------------------------------------\n");
		exit (EXIT_SUCCESS);
	}
		
	/* Skip to first occurrence of CPS_FLAG */

	while ((s = fgets (buffer, BUFSIZ, fp)) && strncmp (buffer, CPS_FLAG, CPS_FLAG_WIDTH));

	while (s) {	/* There is more to decode */

		buffer[strlen (buffer)-1] = '\0';	/* Chop off newline */

		sscanf (&buffer[CPS_FLAG_WIDTH], "%s %o", file, &mode);	/* Name of this file and its file mode */

		if (verbose) {
			fprintf (stderr, "cpsdecode: Extract %s ", file);
			if (first) fprintf (stderr, "(master script) ");
			fprintf (stderr, "[");
		}
		
		if (expand && !first) {	/* Data must be unscrambled so we must write to a bzip2 process */

			if (verbose) fprintf (stderr, "EXPAND");

			sprintf (cmd, "%s > %s", EXPAND, file);
			if ((process = popen (cmd, "wb")) == NULL) {
				fprintf (stderr, "cpsdecode: Trouble opening process %s\n", cmd);
				exit (EXIT_FAILURE);
			}
		}
		else {	/* Here, files are not compressed, just [possibly] encoded, so we write to a file */
			
			if ((process = fopen (file, "wb")) == NULL) {
				fprintf (stderr, "cpsdecode: Trouble creating file %s\n", file);
				exit (EXIT_FAILURE);
			}
		}

		if (decode && !first) {	/* Must undo uuencoding first, which feeds to process or file */
			if (verbose) {
				if (expand) fprintf (stderr, "+");
				fprintf (stderr, "DECODE]\n", file);
			}
			uu_decoder (fp, process, NULL, tag, cps_comment);
			s = fgets (buffer, BUFSIZ, fp);
		}
		else {	/* Read and feed to file */
			if (verbose) fprintf (stderr, "COPY]\n", file);
			while ((s = fgets (buffer, BUFSIZ, fp)) && strncmp (buffer, CPS_FLAG, CPS_FLAG_WIDTH)) {
#ifdef WIN32
				buffer[strlen(buffer)-1] = 0;	/* Strip off LF */
				fprintf (process, "%s\r\n", &buffer[cps_indent]);
#else
				fprintf (process, "%s", &buffer[cps_indent]);
#endif
			}
		}
		
		(expand) ? pclose (process) : fclose (process);

#ifdef WIN32
		_chmod (file, mode);		/* Set correct file permission - ignore failure */
#else
		chmod (file, (mode_t)mode);	/* Set correct file permission - ignore failure */
#endif

		first = FALSE;
		n_files++;
	}

	if (fp != stdin) fclose (fp);

	if (n_files == 0)
		fprintf (stderr, "cpsdecode: No files extracted. %s is probably not a CPS file\n", argv[k]);
	else if (verbose)
		fprintf (stderr, "cpsdecode: Extracted %d files from %s\n", n_files, argv[k]);

	exit (EXIT_SUCCESS);
}
