/* * ============================================================================ * FILE_UTIL: File-related Utility Functions * * Author: J. Zbiciak * Last Revision: 8/29/98 * ============================================================================ * * Exported functionality implemented in this file: * * F_ESCAPE_STR -- escapes non-filename-safe characters within a string. * F_UNESCAPE_STR -- unescapes non-filename-safe characters within a string. * F_HDR_TO_STR -- writes header info to string for manifest file. * F_STR_TO_HDR -- decodes a manifest entry from a string. * F_FILE_EXISTS -- Determines if a given file exists. * F_DIR_EXISTS -- Determines if a given file exists and is a directory. * * F_CREATE_DIR -- Creates a directory, removing any file that might * exist, but only if the overwrite flag is set. * * F_WR_MANIFEST -- Writes a manifest in a directory, when given an * archive pointer * * F_ARCH_TO_DIR -- Writes a hierarchial resource archive structure * to a directory of files. * * F_DIR_TO_ARCH -- Reads a directory tree, and based on the manifest * files therein, builds a resource archive structure. * * Internal helper functionality: * * * Internal helper macros: * * * ============================================================================ */ #include "config.h" #include /* for sprintf() */ #include /* for NULL */ #include /* for size_t */ #include /* for memcpy(), strncpy() */ #include /* for isXXXX() */ #include #include #include #include #include "res_file.h" /* for resource-file related stuff. */ #include "macros.h" /* for HEXINT */ /* * ============================================================================ * ============================================================================ * NON-EXPORTED FUNCTION PROTOTYPES AND FILE-LEVEL MACROS * ============================================================================ * ============================================================================ */ /* * ============================================================================ * ============================================================================ * EXPORTED FUNCTION DEFINITIONS * ============================================================================ * ============================================================================ */ /* * ============================================================================ * F_ESCAPE_STR -- escapes non-filename-safe characters within a string. * * This routine accepts a pointer to the string to be escaped, and a pointer * to the buffer where the escaped string is to be written. * * In addition to control characters and characters above ASCII 126, the * following characters are escaped out of filenames using the HTML-esqe * "%xx" style escape sequence. * * % * ? / \ < > & - + ~ ` ' " | { } [ ] ( ) @ # $ ! [SPACE] * * Additionally, periods are escaped out when they appear as the first * character of a filename. * * To avoid buffer overflow, the output string buffer must be able to hold * an output string that is three times larger than the input string, since * the most a given string will expand is 3:1. * * Return values: * 0 -- string successfully escaped. * -1 -- invalid arguments passed to f_escape_string(). * ============================================================================ */ int f_escape_string ( const char *s1, /* String to escape */ char *s2 /* Escaped string */ ) { int do_escape, first = 1; static const char hex[] = "0123456789ABCDEF"; /* --------------------------------------------------------------------- */ /* Initial sanity check. Do not proceed if it fails. */ /* --------------------------------------------------------------------- */ if (!s1 || !s2) return -1; /* --------------------------------------------------------------------- */ /* For each character in the input string, determine if we need to */ /* escape the character. */ /* --------------------------------------------------------------------- */ while (*s1) { /* ----------------------------------------------------------------- */ /* The 'do_escape' flag marks whether or not we've decided to */ /* escape this character. Note: This test is ASCII specific. */ /* */ /* The rules are these: */ /* -- If it's alpha-numeric, don't escape it. */ /* -- Otherwise, if its outside the range 34..126, escape it, OR */ /* -- If it's in the "escape characters list", OR */ /* -- It's the first character and it's a period. */ /* ----------------------------------------------------------------- */ do_escape = !isalnum(*s1) && ( *s1 < 34 || *s1 > 126 || strchr("%*?/\\<>&-+~`'\"|{}[]()@#$",*s1) != NULL || (first && *s1=='.') ); /* ----------------------------------------------------------------- */ /* If the 'do_escape' flag is set, append an escape sequence of */ /* the form '%XX'. XX is the ASCII value of the character in hex. */ /* ----------------------------------------------------------------- */ if (do_escape) { *s2++ = '%'; *s2++ = hex[(*s1>>4) & 0xF]; *s2++ = hex[(*s1 ) & 0xF]; } else *s2++ = *s1; s1++; first = 0; } *s2 = 0; /* --------------------------------------------------------------------- */ /* Escape operation complete. Report SUCCESS! */ /* --------------------------------------------------------------------- */ return 0; } /* * ============================================================================ * F_UNESCAPE_STR -- unescapes non-filename-safe characters within a string. * * This routine accepts a pointer to an escaped string, and returns the * non-escaped version of the string. Aside from being ASCII-specific, * this code is not nearly as complex as f_escape_str(), because all escape * sequences are un-escaped. * * Notes: * -- Escape sequences must be of the form %XX where XX is the uppercase * hex code for the ASCII value of the character being escaped. * * -- Invalid and incomplete escape sequences will cause the unescape * process to abort with an error. * * Return values: * 0 -- string successfully unescaped. * -1 -- invalid arguments, or invalid escape sequence encountered. * ============================================================================ */ int f_unescape_string ( const char *s1, /* String to unescape */ char *s2 /* Unescaped string */ ) { int hc1, hc2, c; /* --------------------------------------------------------------------- */ /* Initial sanity check. Do not proceed if it fails. */ /* --------------------------------------------------------------------- */ if (!s1 || !s2) return -1; /* --------------------------------------------------------------------- */ /* Scan s1 looking for escape sequences, copying to s2 along the way. */ /* --------------------------------------------------------------------- */ while ( (c = *s1++) != 0 ) { /* ----------------------------------------------------------------- */ /* If this is the start of an escape sequence, grab the sequence */ /* and convert the character. */ /* ----------------------------------------------------------------- */ if (c == '%') { /* ------------------------------------------------------------- */ /* Grab the next two characters, aborting if either is bogus. */ /* ------------------------------------------------------------- */ if ( !(hc1 = s1[0]) || !(hc2 = s1[1]) ) return -1; if ( (hc1 = HEXINT(hc1)) < 0 || (hc2 = HEXINT(hc2)) < 0 ) return -1; s1 += 2; c = (hc1 << 4) + hc2; } /* ----------------------------------------------------------------- */ /* Write the (possibly converted) character to the output string. */ /* ----------------------------------------------------------------- */ *s2++ = c; } /* --------------------------------------------------------------------- */ /* Terminate the output string, and report SUCCESS! */ /* --------------------------------------------------------------------- */ *s2 = 0; return 0; } /* * ============================================================================ * F_HDR_TO_STR -- writes header info to string for manifest file. * * This routine accepts a res_rec_t, and writes a human-readable record * intended to be stored in a "manifest file." Only information which cannot * be gleaned from the file itself is stored in this record. For example, * Resource Name and Type are stored, whereas File Length is not. * ============================================================================ */ int f_hdr_to_str ( res_rec_t *rec, /* Record we wish to encode. */ char *str /* Resulting string. */ ) { /* --------------------------------------------------------------------- */ /* XXX: THIS IS NOT YET IMPLEMENTED BUT NEEDS TO BE!!! */ /* --------------------------------------------------------------------- */ res_report_err(rec, RES_e_not_impl); (void)str; return -1; } /* * ============================================================================ * F_STR_TO_HDR -- decodes a manifest entry from a string. * * This routine accepts a string and decodes its contents into a res_rec_t. * This is intended for decoding a manifest file when rebuilding a resource * archive from a directory tree full of files. Manifest files provide the * metadata that is present in a resource archive, but which is lost in the * filesystem. * ============================================================================ */ int f_str_to_hdr ( char *str, /* String we wish to decode. */ res_rec_t *rec /* Record we wish to set. */ ) { /* --------------------------------------------------------------------- */ /* XXX: THIS IS NOT YET IMPLEMENTED BUT NEEDS TO BE!!! */ /* --------------------------------------------------------------------- */ res_report_err(rec, RES_e_not_impl); (void)str; return -1; } /* * ============================================================================ * F_FILE_EXISTS -- Determines if a given file exists. * ============================================================================ */ int f_file_exists ( const char *pathname ) { /* --------------------------------------------------------------------- */ /* NOTE: access() may not be portable, but works for now. */ /* --------------------------------------------------------------------- */ return access(pathname, R_OK|F_OK) != -1; } /* * ============================================================================ * F_DIR_EXISTS -- Determines if a given file exists and is a directory. * ============================================================================ */ int f_dir_exists ( const char *pathname ) { char * tmp_str; int len; int exists; len = strlen(pathname); tmp_str = malloc(len+3); if (!tmp_str) return -1; strcpy(tmp_str, pathname); /* --------------------------------------------------------------------- */ /* Append "/." to pathname. If "pathname/." exists, then the pathname */ /* is a directory. */ /* --------------------------------------------------------------------- */ tmp_str[len ] = '/'; tmp_str[len+1] = '.'; tmp_str[len+2] = 0; /* --------------------------------------------------------------------- */ /* NOTE: access() may not be portable, but works for now. */ /* --------------------------------------------------------------------- */ exists = access(tmp_str, R_OK | W_OK | X_OK); free(tmp_str); return exists != -1; } /* * ============================================================================ * F_CREATE_DIR -- Creates a directory, removing any file that might * exist, but only if the overwrite flag is set. * ============================================================================ */ int f_create_dir ( const char *pathname, int overwrite ) { /* --------------------------------------------------------------------- */ /* If the directory already exists, stop now, and return SUCCESS! */ /* --------------------------------------------------------------------- */ if (f_dir_exists(pathname)) return 0; /* --------------------------------------------------------------------- */ /* Otherwise, if a file exists with this name, try to remove it if */ /* we are allowed to, otherwise abort. */ /* --------------------------------------------------------------------- */ if (f_file_exists(pathname)) { if (overwrite) { unlink(pathname); if (f_file_exists(pathname)) return -1; } else return -1; } /* --------------------------------------------------------------------- */ /* If we make it this far, try making the directory, and let the user */ /* know how we did. */ /* --------------------------------------------------------------------- */ return mkdir(pathname, 0755); } /* * ============================================================================ * Copyright (c) 1998, Joseph Zbiciak. * ============================================================================ */