/************************************************************************************************** $Header: /pub/cvsroot/yencode/src/file.c,v 1.24 2002/03/15 14:48:52 bboy Exp $ Copyright (C) 2002 Don Moore 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: */