1 /**************************************************************************************************
2 	$Header: /pub/cvsroot/yencode/src/file.c,v 1.24 2002/03/15 14:48:52 bboy Exp $
3 
4 	Copyright (C) 2002  Don Moore <bboy@bboy.net>
5 
6 	This program is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 2 of the License, or
9 	(at Your option) any later version.
10 
11 	This program is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 	GNU General Public License for more details.
15 
16 	You should have received a copy of the GNU General Public License
17 	along with this program; if not, write to the Free Software
18 	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 **************************************************************************************************/
20 
21 #include "y.h"
22 
23 extern char		*opt_output_dir;
24 extern int		opt_keep_paths;								/* Strip paths from filenames? */
25 
26 /* Static list of extensions for yencfile_cmp().  Must be seeded. */
27 static char **sort_first = (char **)NULL;
28 static int	num_sort_first = 0;
29 
30 
31 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
32 	Comparison function for sorting files.
33 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
34 int
ydecfile_cmp(const void * p1,const void * p2)35 ydecfile_cmp(const void *p1, const void *p2)
36 {
37 	const YDECFILE *y1 = *(YDECFILE * const *)p1;
38 	const YDECFILE *y2 = *(YDECFILE * const *)p2;
39 	register int cmp;
40 
41 	if ((cmp = strcmp(y1->header->name, y2->header->name)))
42 		return (cmp);
43 	if (y1->header && y1->header->part && y2->header && y2->header->part)
44 		return (*y1->header->part - *y2->header->part);
45 	else
46 		return (cmp);
47 }
48 /*--- ydecfile_cmp() ----------------------------------------------------------------------------*/
49 
50 
51 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
52 	Allocate the YHEADER structure.
53 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
54 static YHEADER *
yheader_create(void)55 yheader_create(void)
56 {
57 	YHEADER *new = (YHEADER *)xmalloc(sizeof(YHEADER));
58 	memset(new, 0, sizeof(YHEADER));
59 	return (new);
60 }
61 /*--- yheader_create() --------------------------------------------------------------------------*/
62 
63 
64 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
65 	Allocate the YPART structure.
66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
67 static YPART *
ypart_create(void)68 ypart_create(void)
69 {
70 	YPART *new = (YPART *)xmalloc(sizeof(YPART));
71 	memset(new, 0, sizeof(YPART));
72 	return (new);
73 }
74 /*--- ypart_create() ----------------------------------------------------------------------------*/
75 
76 
77 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
78 	Allocate the YFOOTER structure.
79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
80 static YFOOTER *
yfooter_create(void)81 yfooter_create(void)
82 {
83 	YFOOTER *new = (YFOOTER *)xmalloc(sizeof(YFOOTER));
84 	memset(new, 0, sizeof(YFOOTER));
85 	return (new);
86 }
87 /*--- yfooter_create() --------------------------------------------------------------------------*/
88 
89 
90 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
91 	YDECFILE_ADD_YHEADER
92 	Creates the YHEADER data (if necessary) and parses the input line to fill the header.
93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
94 static void
ydecfile_add_yheader(YDECFILE * y,char * line,unsigned long lineno,int strict)95 ydecfile_add_yheader(YDECFILE *y, char *line, unsigned long lineno, int strict)
96 {
97 	char	*cp;
98 
99 	if (y->header)
100 	{
101 		Notice("%s:%lu: %s", y->input_filename, lineno, _("duplicate `=ybegin' line ignored"));
102 		return;
103 	}
104 	y->header = yheader_create();
105 
106 	if ((cp = strstr(line, " part=")))
107 	{
108 		y->header->part = (int *)xmalloc(sizeof(int));
109 		*y->header->part = (int)strtoul(cp+6, (char **)NULL, 10);
110 	}
111 	if ((cp = strstr(line, " total=")))
112 	{
113 		y->header->total = (int *)xmalloc(sizeof(int));
114 		*y->header->total = (int)strtoul(cp+7, (char **)NULL, 10);
115 	}
116 	if ((cp = strstr(line, " line=")))
117 	{
118 		y->header->line = (unsigned long *)xmalloc(sizeof(unsigned long));
119 		*y->header->line = (unsigned long)strtoul(cp+6, (char **)NULL, 10);
120 	}
121 	if ((cp = strstr(line, " size=")))
122 	{
123 		y->header->size = (size_t *)xmalloc(sizeof(size_t));
124 		*y->header->size = (size_t)strtoul(cp+6, (char **)NULL, 10);
125 	}
126 	if ((cp = strstr(line, " name=")))
127 	{
128 		char namebuf[PATH_MAX], *end;
129 
130 		strncpy(namebuf, cp+6, sizeof(namebuf)-1);
131 		strtrim(namebuf);
132 
133 		/* v3 says we should handle filenames in quotation marks */
134 		end = namebuf + strlen(namebuf) - 1;
135 		if ((*namebuf == '"' && *end == '"') || (*namebuf == '\'' && *end == '\''))
136 		{
137 			*end = '\0';
138 			y->header->name = xstrdup(strtrim(namebuf+1));
139 		}
140 		else
141 			y->header->name = xstrdup(namebuf);
142 	}
143 
144 	/* As of v2, decoders should be able to understand if the "begin" or "end"
145 		occur in the =ybegin line */
146 	if ((cp = strstr(line, " begin=")))
147 	{
148 		if (strict)
149 			Info("%s:%lu: %s", y->input_filename, lineno, _("begin offset wrongly specified in the `=ybegin' header"));
150 		y->header->begin = (unsigned long *)xmalloc(sizeof(unsigned long));
151 		*y->header->begin = (unsigned long)strtoul(cp+7, (char **)NULL, 10);
152 	}
153 	if ((cp = strstr(line, " end=")))
154 	{
155 		if (strict)
156 			Info("%s:%lu: %s", y->input_filename, lineno, _("end offset wrongly specified in the `=ybegin' header"));
157 		y->header->end = (unsigned long *)xmalloc(sizeof(unsigned long));
158 		*y->header->end = (unsigned long)strtoul(cp+5, (char **)NULL, 10);
159 	}
160 
161 	if (!y->header->name)
162 	{
163 		free(y->header);
164 		y->header = NULL;
165 		return;
166 	}
167 }
168 /*--- ydecfile_add_yheader() --------------------------------------------------------------------*/
169 
170 
171 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
172 	YDECFILE_ADD_YPART
173 	Creates the YPART data (if necessary) and parses the input line to fill the part information.
174 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
175 static void
ydecfile_add_ypart(YDECFILE * y,char * line,unsigned long lineno,int strict)176 ydecfile_add_ypart(YDECFILE *y, char *line, unsigned long lineno, int strict)
177 {
178 	char *cp;
179 
180 	if (y->part)
181 	{
182 		Notice("%s:%lu: %s", y->input_filename, lineno, _("duplicate `=ypart' line ignored"));
183 		return;
184 	}
185 	y->part = ypart_create();
186 
187 	if ((cp = strstr(line, " begin=")))
188 	{
189 		y->part->begin = (unsigned long *)xmalloc(sizeof(unsigned long));
190 		*y->part->begin = (unsigned long)strtoul(cp+7, (char **)NULL, 10);
191 	}
192 	if ((cp = strstr(line, " end=")))
193 	{
194 		y->part->end = (unsigned long *)xmalloc(sizeof(unsigned long));
195 		*y->part->end = (unsigned long)strtoul(cp+5, (char **)NULL, 10);
196 	}
197 }
198 /*--- ydecfile_add_ypart() ----------------------------------------------------------------------*/
199 
200 
201 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
202 	YDECFILE_ADD_YFOOTER
203 	Creates the YFOOTER data (if necessary) and parses the input line to fill the footer info.
204 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
205 static void
ydecfile_add_yfooter(YDECFILE * y,char * line,unsigned long lineno,int strict)206 ydecfile_add_yfooter(YDECFILE *y, char *line, unsigned long lineno, int strict)
207 {
208 	char *cp;
209 
210 	if (y->footer)
211 	{
212 		Notice("%s:%lu: %s", y->input_filename, lineno, _("duplicate `=yend' line ignored"));
213 		return;
214 	}
215 	y->footer = yfooter_create();
216 
217 	if ((cp = strstr(line, " size=")))
218 	{
219 		y->footer->size = (size_t *)xmalloc(sizeof(size_t));
220 		*y->footer->size = (size_t)strtoul(cp+6, (char **)NULL, 10);
221 	}
222 	if ((cp = strstr(line, " part=")))
223 	{
224 		y->footer->part = (int *)xmalloc(sizeof(int));
225 		*y->footer->part = (int)strtoul(cp+6, (char **)NULL, 10);
226 	}
227 	if ((cp = strstr(line, " crc32=")))
228 	{
229 		y->footer->crc32 = (crc32_t *)xmalloc(sizeof(crc32_t));
230 		*y->footer->crc32 = (crc32_t)strtoul(cp+7, (char **)NULL, 16);
231 	}
232 	if ((cp = strstr(line, " pcrc32=")))
233 	{
234 		y->footer->pcrc32 = (crc32_t *)xmalloc(sizeof(crc32_t));
235 		*y->footer->pcrc32 = (crc32_t)strtoul(cp+8, (char **)NULL, 16);
236 	}
237 }
238 /*--- ydecfile_add_yfooter() --------------------------------------------------------------------*/
239 
240 
241 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
242 	YDECFILE_READ_KEYWORDS
243 	Loads the ybegin and ypart information from the specified YDECFILE and returns.
244 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
245 static void
ydecfile_read_keywords(YDECFILE * y,int strict)246 ydecfile_read_keywords(YDECFILE *y, int strict)
247 {
248 	FILE				*fp;
249 	unsigned char	buf[BUFSIZ];
250 	register int	got_begin = 0;
251 	register unsigned long	lineno = 0;
252 
253 	if (!(fp = fopen(y->input_filename, "rb")))
254 		ErrERR("%s", y->input_filename);
255 
256 	/* Find markers, calculating data length and CRC along the way */
257 	while (fgets(buf, sizeof(buf), fp))
258 	{
259 		lineno++;
260 
261 		if (!YKEYWORD(buf))
262 			continue;
263 
264 		if (YKEYWORD_BEGIN(buf))
265 		{
266 			ydecfile_add_yheader(y, buf, lineno, strict);
267 			y->data_start = ftell(fp);
268 			y->line_offset = lineno;
269 			got_begin = 1;
270 		}
271 		else if (YKEYWORD_PART(buf))
272 		{
273 			ydecfile_add_ypart(y, buf, lineno, strict);
274 			y->data_start = ftell(fp);
275 			y->line_offset = lineno;
276 		}
277 		else if (YKEYWORD_END(buf))
278 		{
279 			ydecfile_add_yfooter(y, buf, lineno, strict);
280 			fclose(fp);
281 			return;
282 		}
283 		else
284 		{
285 			/* In my test file, there was binary garbage "=y" after a newline, so don't warn.. it
286 				could output garbage and trash the terminal */
287 #ifdef notdef
288 			unsigned char *c;
289 
290 			/* Try to isolate just the unknown keyword */
291 			for (c = buf; *c; c++)
292 				if (isspace(*c) || iscntrl(*c))
293 					*c = '\0';
294 
295 			Info("%s:%lu: `%s': %s", y->input_filename, lineno, buf, _("unknown keyword ignored"));
296 #endif
297 		}
298 	}
299 	fclose(fp);
300 }
301 /*--- ydecfile_read_keywords() ------------------------------------------------------------------*/
302 
303 
304 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
305 	YDECFILE_VERIFY_KEYWORD_DATA
306 	Verifies the markers in the specified YDECFILE.  Returns a pointer to the YDECFILE or NULL if an
307 	error occurred (and processing should not be done on this file).
308 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
309 static YDECFILE *
ydecfile_verify_keyword_data(YDECFILE * y,int strict)310 ydecfile_verify_keyword_data(YDECFILE *y, int strict)
311 {
312 	/*
313 	**  First, fix any data suspected of being broken but that we can extrapolate from elsewhere
314 	*/
315 	/* The file is not valid if no header or footer were found */
316 	if (!y->header || !y->footer)
317 		return (NULL);
318 
319 	/* Copy part begin/end data from header if it was specified there (erroneously) */
320 	if (y->header->begin && (!y->part || !y->part->begin))
321 	{
322 		if (!y->part)
323 			y->part = ypart_create();
324 		y->part->begin = (unsigned long *)xmalloc(sizeof(unsigned long));
325 		*y->part->begin = *y->header->begin;
326 	}
327 	if (y->header->end && (!y->part || !y->part->end))
328 	{
329 		if (!y->part)
330 			y->part = ypart_create();
331 		y->part->end = (unsigned long *)xmalloc(sizeof(unsigned long));
332 		*y->part->end = *y->header->end;
333 	}
334 
335 	/* If no `end' was specified in =ypart, set it based on the size in the footer */
336 	if (y->part && !y->part->end && y->footer->size)
337 	{
338 		y->part->end = (unsigned long *)xmalloc(sizeof(unsigned long));
339 		*y->part->end = *y->part->begin + *y->footer->size - 1;
340 	}
341 
342 
343 	/*
344 	**  Warn and/or die if vital data is missing
345 	*/
346 	if (!y->header->line)
347 	{
348 		Notice("%s: %s (%s)", y->input_filename, _("line length not specified"), _("file will not be processed"));
349 		return (NULL);
350 	}
351 	if (!y->header->name)
352 	{
353 		Notice("%s: %s (%s)", y->input_filename, _("output filename not specified"), _("file will not be processed"));
354 		return (NULL);
355 	}
356 	if (!y->header->size)
357 	{
358 		Notice("%s: %s (%s)", y->input_filename, _("file size not specified"), _("file will not be processed"));
359 		return (NULL);
360 	}
361 
362 	return (y);
363 }
364 /*--- ydecfile_verify_keyword_data() ------------------------------------------------------------*/
365 
366 
367 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
368 	Adds the specified file to the list pointed to by ylist_head.  Errors are fatal.
369 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
370 YDECFILE *
ydecfile_create(const char * filename,int strict)371 ydecfile_create(const char *filename, int strict)
372 {
373 	YDECFILE *y;													// The new file
374 	struct stat	st;												// File stat info
375 
376 	if (!filename)
377 		return (NULL);
378 	if (stat(filename, &st))									// Treat file as invalid
379 	{
380 		WarnERR("%s", filename);
381 		return (NULL);
382 	}
383 	if (!S_ISREG(st.st_mode))
384 		return (NULL);
385 
386 	/* Allocate structure and set default values */
387 	y = (YDECFILE *)xmalloc(sizeof(YDECFILE));
388 	memset(y, 0, sizeof(YDECFILE));
389 	y->input_filename = xstrdup(filename);
390 	y->input_st = (struct stat *)xmalloc(sizeof(struct stat));
391 	memcpy(y->input_st, &st, sizeof(struct stat));
392 
393 	ydecfile_read_keywords(y, strict);
394 
395 	return (ydecfile_verify_keyword_data(y, strict));
396 }
397 /*--- ydecfile_create() -------------------------------------------------------------------------*/
398 
399 
400 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
401 	Allocate the YENCPART structure.
402 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
403 YENCPART *
yencpart_create(void)404 yencpart_create(void)
405 {
406 	YENCPART *new = (YENCPART *)xmalloc(sizeof(YENCPART));
407 	memset(new, 0, sizeof(YENCPART));
408 	return (new);
409 }
410 /*--- yencpart_create() -------------------------------------------------------------------------*/
411 
412 
413 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
414 	YENCFILE_SEED_SORT_FIRST_EXTENSIONS
415 	Seeds the list of extensions which should be sorted first by yencfile_cmp().
416 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
417 void
yencfile_seed_sort_first_extensions(const char * seedstr)418 yencfile_seed_sort_first_extensions(const char *seedstr)
419 {
420 	char	*ss, *c;
421 
422 	ss = (seedstr) ? xstrdup(seedstr) : xstrdup(DEFAULT_SORT_FIRST_EXTENSIONS);
423 
424 	while ((c = strsep(&ss, ",")))
425 	{
426 		strtrim(c);
427 		if (c[0] == '.')
428 			c++;
429 		sort_first = (char **)xrealloc(sort_first, (num_sort_first + 1) * sizeof(char *));
430 		sort_first[num_sort_first++] = c;
431 	}
432 }
433 /*--- yencfile_seed_sort_first_extensions() -----------------------------------------------------*/
434 
435 
436 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
437 	YENCFILE_CMP
438    Comparison function for sorting files.
439 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
440 int
yencfile_cmp(const void * p1,const void * p2)441 yencfile_cmp(const void *p1, const void *p2)
442 {
443 	const YENCFILE *y1 = *(YENCFILE * const *)p1;
444 	const YENCFILE *y2 = *(YENCFILE * const *)p2;
445 
446 	/* See if each file has an extension that should be sorted first */
447 	if (num_sort_first)
448 	{
449 		char	*y1ext, *y2ext;
450 		int	y1first, y2first;
451 		register int ct;
452 
453 		y1ext = strrchr(y1->input_filename, '.');
454 		y2ext = strrchr(y2->input_filename, '.');
455 		for (ct = y1first = y2first = 0; ct < num_sort_first; ct++)
456 		{
457 			if (y1ext && !strcasecmp(y1ext+1, sort_first[ct]))
458 				y1first = ct+1;
459 			if (y2ext && !strcasecmp(y2ext+1, sort_first[ct]))
460 				y2first = ct+1;
461 		}
462 		if (y1first && !y2first)
463 			return (-1);
464 		if (y2first && !y1first)
465 			return (1);
466 		if (y1first && y2first)
467 			return (y1first - y2first);
468 	}
469 	return (strcmp(y1->input_filename, y2->input_filename));
470 }
471 /*--- yencfile_cmp() ----------------------------------------------------------------------------*/
472 
473 
474 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
475 	YENCFILE_CREATE
476 	Adds a file to the input file list.
477 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
478 YENCFILE *
yencfile_create(const char * input_filename,const char * output_dir,ysupportfile_t special_file_type,size_t multipart_size)479 yencfile_create(const char *input_filename, const char *output_dir,
480 					 ysupportfile_t special_file_type, size_t multipart_size)
481 {
482 	YENCFILE	*y;
483 	struct stat st;
484 	char *c, base_name[PATH_MAX], prefix[PATH_MAX];
485 
486 	y = (YENCFILE *)xmalloc(sizeof(YENCFILE));
487 	memset(y, 0, sizeof(YENCFILE));
488 
489 	y->input_filename = xstrdup(input_filename);
490 	y->file_type = special_file_type;
491 
492 	if (YSUPPORT_IS_SPECIAL(y->file_type))
493 		return (y);
494 
495    /* Check file, get filesize, create filename prefix */
496 	if (stat(y->input_filename, &st))
497 		ErrERR("%s", y->input_filename);
498 	if (!S_ISREG(st.st_mode))
499 	{
500 		Debug("%s: %s", y->input_filename, _("not a regular file (skipped)"));
501 		free(y);
502 		return (NULL);
503 	}
504 	y->filesize = st.st_size;
505 
506 	/* Generate output prefix */
507 	if ((c = strrchr(y->input_filename, '/')))			/* Copy basename of input file into `name' */
508 		strncpy(base_name, c+1, sizeof(base_name)-1);
509 	else
510 		strncpy(base_name, y->input_filename, sizeof(base_name)-1);
511 	if (output_dir)                                /* Generate prefix for output files */
512 		snprintf(prefix, sizeof(prefix), "%s/%s", output_dir, base_name);
513 	else
514 		strncpy(prefix, base_name, sizeof(prefix)-1);
515 	y->output_prefix = xstrdup(prefix);
516 
517 	/* Determine total number of parts if multipart */
518 	if (multipart_size && (y->filesize > multipart_size))
519 	{
520 		y->totalparts = y->filesize / multipart_size;
521 		if (y->filesize % multipart_size)
522 			y->totalparts++;
523 	}
524 	else
525 		y->totalparts = 1;
526 
527 	return (y);
528 }
529 /*--- yencfile_create() -------------------------------------------------------------------------*/
530 
531 /* vi:set ts=3: */
532