/**************************************************************************************************
	$Header: /pub/cvsroot/yencode/src/file.c,v 1.24 2002/03/15 14:48:52 bboy Exp $

	Copyright (C) 2002  Don Moore <bboy@bboy.net>

	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; either version 2 of the License, or
	(at Your option) any later version.

	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.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
**************************************************************************************************/

#include "y.h"

extern char		*opt_output_dir;
extern int		opt_keep_paths;								/* Strip paths from filenames? */

/* Static list of extensions for yencfile_cmp().  Must be seeded. */
static char **sort_first = (char **)NULL;
static int	num_sort_first = 0;


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Comparison function for sorting files.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int
ydecfile_cmp(const void *p1, const void *p2)
{
	const YDECFILE *y1 = *(YDECFILE * const *)p1;
	const YDECFILE *y2 = *(YDECFILE * const *)p2;
	register int cmp;

	if ((cmp = strcmp(y1->header->name, y2->header->name)))
		return (cmp);
	if (y1->header && y1->header->part && y2->header && y2->header->part)
		return (*y1->header->part - *y2->header->part);
	else
		return (cmp);
}
/*--- ydecfile_cmp() ----------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Allocate the YHEADER structure.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static YHEADER *
yheader_create(void)
{
	YHEADER *new = (YHEADER *)xmalloc(sizeof(YHEADER));
	memset(new, 0, sizeof(YHEADER));
	return (new);
}
/*--- yheader_create() --------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Allocate the YPART structure.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static YPART *
ypart_create(void)
{
	YPART *new = (YPART *)xmalloc(sizeof(YPART));
	memset(new, 0, sizeof(YPART));
	return (new);
}
/*--- ypart_create() ----------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Allocate the YFOOTER structure.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static YFOOTER *
yfooter_create(void)
{
	YFOOTER *new = (YFOOTER *)xmalloc(sizeof(YFOOTER));
	memset(new, 0, sizeof(YFOOTER));
	return (new);
}
/*--- yfooter_create() --------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	YDECFILE_ADD_YHEADER
	Creates the YHEADER data (if necessary) and parses the input line to fill the header.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
ydecfile_add_yheader(YDECFILE *y, char *line, unsigned long lineno, int strict)
{
	char	*cp;

	if (y->header)
	{
		Notice("%s:%lu: %s", y->input_filename, lineno, _("duplicate `=ybegin' line ignored"));
		return;
	}
	y->header = yheader_create();

	if ((cp = strstr(line, " part=")))
	{
		y->header->part = (int *)xmalloc(sizeof(int));
		*y->header->part = (int)strtoul(cp+6, (char **)NULL, 10);
	}
	if ((cp = strstr(line, " total=")))
	{
		y->header->total = (int *)xmalloc(sizeof(int));
		*y->header->total = (int)strtoul(cp+7, (char **)NULL, 10);
	}
	if ((cp = strstr(line, " line=")))
	{
		y->header->line = (unsigned long *)xmalloc(sizeof(unsigned long));
		*y->header->line = (unsigned long)strtoul(cp+6, (char **)NULL, 10);
	}
	if ((cp = strstr(line, " size=")))
	{
		y->header->size = (size_t *)xmalloc(sizeof(size_t));
		*y->header->size = (size_t)strtoul(cp+6, (char **)NULL, 10);
	}
	if ((cp = strstr(line, " name=")))
	{
		char namebuf[PATH_MAX], *end;

		strncpy(namebuf, cp+6, sizeof(namebuf)-1);
		strtrim(namebuf);

		/* v3 says we should handle filenames in quotation marks */
		end = namebuf + strlen(namebuf) - 1;
		if ((*namebuf == '"' && *end == '"') || (*namebuf == '\'' && *end == '\''))
		{
			*end = '\0';
			y->header->name = xstrdup(strtrim(namebuf+1));
		}
		else
			y->header->name = xstrdup(namebuf);
	}

	/* As of v2, decoders should be able to understand if the "begin" or "end"
		occur in the =ybegin line */
	if ((cp = strstr(line, " begin=")))
	{
		if (strict)
			Info("%s:%lu: %s", y->input_filename, lineno, _("begin offset wrongly specified in the `=ybegin' header"));
		y->header->begin = (unsigned long *)xmalloc(sizeof(unsigned long));
		*y->header->begin = (unsigned long)strtoul(cp+7, (char **)NULL, 10);
	}
	if ((cp = strstr(line, " end=")))
	{
		if (strict)
			Info("%s:%lu: %s", y->input_filename, lineno, _("end offset wrongly specified in the `=ybegin' header"));
		y->header->end = (unsigned long *)xmalloc(sizeof(unsigned long));
		*y->header->end = (unsigned long)strtoul(cp+5, (char **)NULL, 10);
	}
	
	if (!y->header->name)
	{
		free(y->header);
		y->header = NULL;
		return;
	}
}
/*--- ydecfile_add_yheader() --------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	YDECFILE_ADD_YPART
	Creates the YPART data (if necessary) and parses the input line to fill the part information.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
ydecfile_add_ypart(YDECFILE *y, char *line, unsigned long lineno, int strict)
{
	char *cp;

	if (y->part)
	{
		Notice("%s:%lu: %s", y->input_filename, lineno, _("duplicate `=ypart' line ignored"));
		return;
	}
	y->part = ypart_create();

	if ((cp = strstr(line, " begin=")))
	{
		y->part->begin = (unsigned long *)xmalloc(sizeof(unsigned long));
		*y->part->begin = (unsigned long)strtoul(cp+7, (char **)NULL, 10);
	}
	if ((cp = strstr(line, " end=")))
	{
		y->part->end = (unsigned long *)xmalloc(sizeof(unsigned long));
		*y->part->end = (unsigned long)strtoul(cp+5, (char **)NULL, 10);
	}
}
/*--- ydecfile_add_ypart() ----------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	YDECFILE_ADD_YFOOTER
	Creates the YFOOTER data (if necessary) and parses the input line to fill the footer info.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
ydecfile_add_yfooter(YDECFILE *y, char *line, unsigned long lineno, int strict)
{
	char *cp;

	if (y->footer)
	{
		Notice("%s:%lu: %s", y->input_filename, lineno, _("duplicate `=yend' line ignored"));
		return;
	}
	y->footer = yfooter_create();

	if ((cp = strstr(line, " size=")))
	{
		y->footer->size = (size_t *)xmalloc(sizeof(size_t));
		*y->footer->size = (size_t)strtoul(cp+6, (char **)NULL, 10);
	}
	if ((cp = strstr(line, " part=")))
	{
		y->footer->part = (int *)xmalloc(sizeof(int));
		*y->footer->part = (int)strtoul(cp+6, (char **)NULL, 10);
	}
	if ((cp = strstr(line, " crc32=")))
	{
		y->footer->crc32 = (crc32_t *)xmalloc(sizeof(crc32_t));
		*y->footer->crc32 = (crc32_t)strtoul(cp+7, (char **)NULL, 16);
	}
	if ((cp = strstr(line, " pcrc32=")))
	{
		y->footer->pcrc32 = (crc32_t *)xmalloc(sizeof(crc32_t));
		*y->footer->pcrc32 = (crc32_t)strtoul(cp+8, (char **)NULL, 16);
	}
}
/*--- ydecfile_add_yfooter() --------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	YDECFILE_READ_KEYWORDS
	Loads the ybegin and ypart information from the specified YDECFILE and returns.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
ydecfile_read_keywords(YDECFILE *y, int strict)
{
	FILE				*fp;
	unsigned char	buf[BUFSIZ];
	register int	got_begin = 0;
	register unsigned long	lineno = 0;

	if (!(fp = fopen(y->input_filename, "rb")))
		ErrERR("%s", y->input_filename);

	/* Find markers, calculating data length and CRC along the way */
	while (fgets(buf, sizeof(buf), fp))
	{
		lineno++;

		if (!YKEYWORD(buf))
			continue;

		if (YKEYWORD_BEGIN(buf))
		{
			ydecfile_add_yheader(y, buf, lineno, strict);
			y->data_start = ftell(fp);
			y->line_offset = lineno;
			got_begin = 1;
		}
		else if (YKEYWORD_PART(buf))
		{
			ydecfile_add_ypart(y, buf, lineno, strict);
			y->data_start = ftell(fp);
			y->line_offset = lineno;
		}
		else if (YKEYWORD_END(buf))
		{
			ydecfile_add_yfooter(y, buf, lineno, strict);
			fclose(fp);
			return;
		}
		else
		{
			/* In my test file, there was binary garbage "=y" after a newline, so don't warn.. it
				could output garbage and trash the terminal */
#ifdef notdef
			unsigned char *c;

			/* Try to isolate just the unknown keyword */
			for (c = buf; *c; c++)
				if (isspace(*c) || iscntrl(*c))
					*c = '\0';

			Info("%s:%lu: `%s': %s", y->input_filename, lineno, buf, _("unknown keyword ignored"));
#endif
		}
	}
	fclose(fp);
}
/*--- ydecfile_read_keywords() ------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	YDECFILE_VERIFY_KEYWORD_DATA
	Verifies the markers in the specified YDECFILE.  Returns a pointer to the YDECFILE or NULL if an
	error occurred (and processing should not be done on this file).
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static YDECFILE *
ydecfile_verify_keyword_data(YDECFILE *y, int strict)
{
	/*
	**  First, fix any data suspected of being broken but that we can extrapolate from elsewhere
	*/
	/* The file is not valid if no header or footer were found */
	if (!y->header || !y->footer)
		return (NULL);

	/* Copy part begin/end data from header if it was specified there (erroneously) */
	if (y->header->begin && (!y->part || !y->part->begin))
	{
		if (!y->part)
			y->part = ypart_create();
		y->part->begin = (unsigned long *)xmalloc(sizeof(unsigned long));
		*y->part->begin = *y->header->begin;
	}
	if (y->header->end && (!y->part || !y->part->end))
	{
		if (!y->part)
			y->part = ypart_create();
		y->part->end = (unsigned long *)xmalloc(sizeof(unsigned long));
		*y->part->end = *y->header->end;
	}

	/* If no `end' was specified in =ypart, set it based on the size in the footer */
	if (y->part && !y->part->end && y->footer->size)
	{
		y->part->end = (unsigned long *)xmalloc(sizeof(unsigned long));
		*y->part->end = *y->part->begin + *y->footer->size - 1;
	}


	/*
	**  Warn and/or die if vital data is missing
	*/
	if (!y->header->line)
	{
		Notice("%s: %s (%s)", y->input_filename, _("line length not specified"), _("file will not be processed"));
		return (NULL);
	}
	if (!y->header->name)
	{
		Notice("%s: %s (%s)", y->input_filename, _("output filename not specified"), _("file will not be processed"));
		return (NULL);
	}
	if (!y->header->size)
	{
		Notice("%s: %s (%s)", y->input_filename, _("file size not specified"), _("file will not be processed"));
		return (NULL);
	}

	return (y);
}
/*--- ydecfile_verify_keyword_data() ------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Adds the specified file to the list pointed to by ylist_head.  Errors are fatal.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
YDECFILE *
ydecfile_create(const char *filename, int strict)
{
	YDECFILE *y;													// The new file
	struct stat	st;												// File stat info

	if (!filename)
		return (NULL);
	if (stat(filename, &st))									// Treat file as invalid
	{
		WarnERR("%s", filename);
		return (NULL);
	}
	if (!S_ISREG(st.st_mode))
		return (NULL);

	/* Allocate structure and set default values */
	y = (YDECFILE *)xmalloc(sizeof(YDECFILE));
	memset(y, 0, sizeof(YDECFILE));
	y->input_filename = xstrdup(filename);
	y->input_st = (struct stat *)xmalloc(sizeof(struct stat));
	memcpy(y->input_st, &st, sizeof(struct stat));

	ydecfile_read_keywords(y, strict);

	return (ydecfile_verify_keyword_data(y, strict));
}
/*--- ydecfile_create() -------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Allocate the YENCPART structure.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
YENCPART *
yencpart_create(void)
{
	YENCPART *new = (YENCPART *)xmalloc(sizeof(YENCPART));
	memset(new, 0, sizeof(YENCPART));
	return (new);
}
/*--- yencpart_create() -------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	YENCFILE_SEED_SORT_FIRST_EXTENSIONS
	Seeds the list of extensions which should be sorted first by yencfile_cmp().
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void
yencfile_seed_sort_first_extensions(const char *seedstr)
{
	char	*ss, *c;

	ss = (seedstr) ? xstrdup(seedstr) : xstrdup(DEFAULT_SORT_FIRST_EXTENSIONS);

	while ((c = strsep(&ss, ",")))
	{
		strtrim(c);
		if (c[0] == '.')
			c++;
		sort_first = (char **)xrealloc(sort_first, (num_sort_first + 1) * sizeof(char *));
		sort_first[num_sort_first++] = c;
	}
}
/*--- yencfile_seed_sort_first_extensions() -----------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	YENCFILE_CMP
   Comparison function for sorting files.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int
yencfile_cmp(const void *p1, const void *p2)
{
	const YENCFILE *y1 = *(YENCFILE * const *)p1;
	const YENCFILE *y2 = *(YENCFILE * const *)p2;

	/* See if each file has an extension that should be sorted first */
	if (num_sort_first)
	{
		char	*y1ext, *y2ext;
		int	y1first, y2first;
		register int ct;

		y1ext = strrchr(y1->input_filename, '.');
		y2ext = strrchr(y2->input_filename, '.');
		for (ct = y1first = y2first = 0; ct < num_sort_first; ct++)
		{
			if (y1ext && !strcasecmp(y1ext+1, sort_first[ct]))
				y1first = ct+1;
			if (y2ext && !strcasecmp(y2ext+1, sort_first[ct]))
				y2first = ct+1;
		}
		if (y1first && !y2first)
			return (-1);
		if (y2first && !y1first)
			return (1);
		if (y1first && y2first)
			return (y1first - y2first);
	}
	return (strcmp(y1->input_filename, y2->input_filename));
}
/*--- yencfile_cmp() ----------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	YENCFILE_CREATE
	Adds a file to the input file list.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
YENCFILE *
yencfile_create(const char *input_filename, const char *output_dir,
					 ysupportfile_t special_file_type, size_t multipart_size)
{
	YENCFILE	*y;
	struct stat st;
	char *c, base_name[PATH_MAX], prefix[PATH_MAX];

	y = (YENCFILE *)xmalloc(sizeof(YENCFILE));
	memset(y, 0, sizeof(YENCFILE));

	y->input_filename = xstrdup(input_filename);
	y->file_type = special_file_type;

	if (YSUPPORT_IS_SPECIAL(y->file_type))
		return (y);

   /* Check file, get filesize, create filename prefix */
	if (stat(y->input_filename, &st))
		ErrERR("%s", y->input_filename);
	if (!S_ISREG(st.st_mode))
	{
		Debug("%s: %s", y->input_filename, _("not a regular file (skipped)"));
		free(y);
		return (NULL);
	}
	y->filesize = st.st_size;

	/* Generate output prefix */
	if ((c = strrchr(y->input_filename, '/')))			/* Copy basename of input file into `name' */
		strncpy(base_name, c+1, sizeof(base_name)-1);
	else
		strncpy(base_name, y->input_filename, sizeof(base_name)-1);
	if (output_dir)                                /* Generate prefix for output files */
		snprintf(prefix, sizeof(prefix), "%s/%s", output_dir, base_name);
	else
		strncpy(prefix, base_name, sizeof(prefix)-1);	
	y->output_prefix = xstrdup(prefix);

	/* Determine total number of parts if multipart */
	if (multipart_size && (y->filesize > multipart_size))
	{
		y->totalparts = y->filesize / multipart_size;
		if (y->filesize % multipart_size)
			y->totalparts++;
	}
	else
		y->totalparts = 1;

	return (y);
}
/*--- yencfile_create() -------------------------------------------------------------------------*/

/* vi:set ts=3: */


syntax highlighted by Code2HTML, v. 0.9.1