/* Objective Modula-2 Compiler (objm2c) * * @file objm2_filename.c * portable filename function implementation * * Compose and decompose pathnames and test file extensions * * Author: Benjamin Kowarsch * * Copyright (C) 2009 The Objective Modula-2 Project. All rights reserved. * * License: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met * * 1) This file, or any part thereof, may NOT be hosted on websites which * contain advertising, unless specific prior written permission has been * obtained. The licensor will grant such permission upon request at its * sole discretion to websites the licensor does NOT consider abusive in * their use of advertising. Small notices in the footer of a website * naming corporate rights holders, infrastructure providers or sponsors * are not considered advertising in the context of this license. * * 2) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 3) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and other materials provided with the distribution. * * 4) Neither the author's name nor the names of any contributors may be used * to endorse or promote products derived from this software without * specific prior written permission. * * 5) Where this list of conditions or the following disclaimer, in part or * as a whole is overruled or nullified by applicable law, no permission * is granted to use the software. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * TO DO : Move function objm2_working_directory() into a separate module * Add minimal perfect hash for supported file extensions * Add code to check well formedness of directory strings */ #include "objm2_filename.h" #include "ASCII.h" #include "objm2_hash.h" #include "objm2_alloc.h" #include #include // --------------------------------------------------------------------------- // Maximum pathname length // --------------------------------------------------------------------------- // On some operating systems, MAX_PATH may not be defined. #ifdef MAX_PATH // in limits.h #define OBJM2_MAX_PATH_LENGTH MAX_PATH #elif #warning MAX_PATH is undefined, using default value of 511 #define OBJM2_MAX_PATH_LENGTH 511 #endif // --------------------------------------------------------------------------- // Maximum filename length // --------------------------------------------------------------------------- // On some operating systems, MAX_NAME may not be defined. #ifdef MAX_NAME // in limits.h #define OBJM2_MAX_FILENAME_LENGTH MAX_NAME #elif #warning MAX_NAME is undefined, using default value of 127 #define OBJM2_MAX_FILENAME_LENGTH 127 #endif // --------------------------------------------------------------------------- // Check for presence of getcwd() or _getcwd() // --------------------------------------------------------------------------- // On some operating systems, getcwd() may be called _getcwd() instead #if !defined(getcwd) && defined(_getcwd) #define getcwd _getcwd #elif #error function getcwd() is undefined, this should have been in unistd.h #endif // --------------------------------------------------------------------------- // Default filenaming scheme // --------------------------------------------------------------------------- // // If any of ISO9660, POSIX, WINDOWS or WIN, MSDOS or DOS, OPENVMS or VMS are // defined in the compile time environment (usually via CFLAGS) then the // default filenaming is set accordingly. If none of the symbols are defined // then the default filenaming is set to POSIX_FILENAMING. static const objm2_filenaming_t _default_filenaming = #if defined(ISO9660) ISO9660_FILENAMING ; #elif defined(POSIX) POSIX_FILENAMING ; #elif defined(WINDOWS) || defined(WIN) MSDOS_FILENAMING ; #elif defined(MSDOS) || defined(DOS) MSDOS_FILENAMING ; #elif defined(OPENVMS) || defined(VMS) OPENVMS_FILENAMING ; #else #warning OS environment undefined, default filenaming set to POSIX_FILENAMING POSIX_FILENAMING ; #endif // --------------------------------------------------------------------------- // Hash values of filename extensions for input files // --------------------------------------------------------------------------- #define OBJM2_EXT_DEF_UPPER 0x21C12F85 /* DEF */ #define OBJM2_EXT_DEF_CAMEL 0x21E13785 /* Def */ #define OBJM2_EXT_DEF_LOWER 0x31A327A5 /* def */ #define OBJM2_EXT_MOD_UPPER 0x2639BD82 /* MOD */ #define OBJM2_EXT_MOD_CAMEL 0x2659C582 /* Mod */ #define OBJM2_EXT_MOD_LOWER 0x361BB5A2 /* mod */ #define OBJM2_EXT_PROT_UPPER 0x32D146C7 /* PROT */ #define OBJM2_EXT_PROT_CAMEL 0x42B33EE7 /* Prot */ #define OBJM2_EXT_PROT_LOWER 0x138D56C7 /* prot */ // --------------------------------------------------------------------------- // File extension strings // --------------------------------------------------------------------------- static const char *_ext_string[] = { // dummy entry "\0", // source file extensions "def\0", "mod\0", "prot\0", // output file extensions "ast\0", "sym\0", "h\0", "c\0", "mm\0", "llvm\0" }; /* _ext_string */ #define _MAX_EXTSTR_SIZE 5 // --------------------------------------------------------------------------- // Delimiter set type // --------------------------------------------------------------------------- typedef struct /* delimiter_set */ { char for_filename; char for_file_extension; char for_file_version; } delimiter_set; // --------------------------------------------------------------------------- // Delimiter sets // --------------------------------------------------------------------------- static const delimiter_set _delimiter[] = { // dummy entry { CSTRING_TERMINATOR, CSTRING_TERMINATOR, CSTRING_TERMINATOR }, // delimiters for POSIX { '/', '.', CSTRING_TERMINATOR }, // delimiters for ISO9660 { '/', '.', ';' }, // delimiters for MSDOS { '\\', '.', CSTRING_TERMINATOR }, // delimiters for OPENVMS { ']', '.', ';' } }; /* _delimiter */ // --------------------------------------------------------------------------- // Filename descriptor // --------------------------------------------------------------------------- typedef struct /* objm2_filename_s */ { char *directory; char *filename; char *extension; cardinal path_length; objm2_file_type_t file_type; objm2_filenaming_t filenaming; } objm2_filename_s; // --------------------------------------------------------------------------- // Buffer for pathname of working directory // --------------------------------------------------------------------------- static char *_working_directory[OBJM2_MAX_PATH_LENGTH + 1] = EMPTY_STRING; // --------------------------------------------------------------------------- // function: objm2_working_directory() // --------------------------------------------------------------------------- // // Obtains the current working directory pathname of the current process and // returns an immutable pointer to the pathname string. Subsequent calls to // this function will always return the same string as the working directory // is not supposed to change during the process' lifetime. const char *objm2_working_directory() { if (_working_directory[0] == CSTRING_TERMINATOR) return (const char *) getcwd(_working_directory, OBJM2_MAX_PATH_LENGTH + 1); else return _working_directory } // end objm2_working_directory // --------------------------------------------------------------------------- // function: objm2_is_valid_filename_string( filename ) // --------------------------------------------------------------------------- // // Returns true if is a valid Objective Modula-2 filename, returns // false otherwise. Characters allowed in filenames are letters, digits, // underscore and dollar. However, filenames must not start with a digit. The // syntax for filenames is the same as for Objective Modula-2 identifiers: // // filename := ( letter | "_" | "$" ) ( letter | digit | "_" | "$" )* bool objm2_is_valid_filename_string(const char *filename) { int index; if ((IS_NOT_LETTER(filename[0])) && (filename[0] != UNDERSCORE) && (filename[0] != DOLLAR)) return false; index = 1; while (filename[index] != CSTRING_TERMINATOR) { if ((IS_NOT_ALPHANUM(filename[index])) && (filename[0] != UNDERSCORE) && (filename[0] != DOLLAR)) return false; index++; } // end while return true; } // end objm2_is_valid_filename_string // -------------------------------------------------------------------------- // function: objm2_outfile_type_for( target, sourcefile_type ) // -------------------------------------------------------------------------- // // Returns the output file type associated with for // . Returns FILE_TYPE_UNKNOWN if no associated file type exists. objm2_file_type_t objm2_outfile_type_for(objm2_target_t target, objm2_file_type_t sourcefile_type) { switch (target) { // when using Objective-C source code generator case OBJM2_TARGET_OBJC : switch(sourcefile_type) { case FILE_TYPE_MOD : return FILE_TYPE_MM; case FILE_TYPE_DEF : case FILE_TYPE_PROT : return FILE_TYPE_H; default : return FILE_TYPE_UNKNOWN; } // end switch // when using LLVM source code generator case OBJM2_TARGET_LLVM : switch(sourcefile_type) { case FILE_TYPE_MOD : return FILE_TYPE_LLVM; case FILE_TYPE_DEF : case FILE_TYPE_PROT : return FILE_TYPE_SYM; default : return FILE_TYPE_UNKNOWN; } // end switch // no other targets are available default : return FILE_TYPE_UNKNOWN; } // end switch } // end objm2_outfile_type_for // --------------------------------------------------------------------------- // function: objm2_new_filename( dir, name, file_type, filenaming, status ) // --------------------------------------------------------------------------- // // Returns a newly allocated filename descriptor, initialised with the passed // in parameters. The caller is responsible for deallocating the descriptor. // Returns NULL if any of and is invalid. // // The status of the operation is passed back in , unless NULL was // passed in for . objm2_filename_t *objm2_new_filename(const char *directory, const char *filename, objm2_file_type_t file_type, objm2_filenaming_t filenaming, objm2_filename_status_t *status) { objm2_filename_s *new_filename; int size, index = 0, total_length = 0; char *fn_delimiter, *ext_delimiter, *ver_delimiter; // bail out if filename is invalid if ((filename == NULL) || (objm2_is_valid_filename_string(filename) == false)) { ASSIGN_BY_REF(status, OBJM2_FILENAME_STATUS_INVALID_FILENAME); return NULL; } // end if // bail out if file type is invalid if ((file_type == FILE_TYPE_UNKNOWN) || (file_type >= INVALID_FILENAME)) { ASSIGN_BY_REF(status, OBJM2_FILENAME_STATUS_INVALID_FILETYPE); return NULL; } // end if // use default if filenaming value is not specific or invalid if ((filenaming == DEFAULT_FILENAMING) || (filenaming >= NUMBER_OF_FILENAMING_SCHEMES)) filenaming = _default_filenaming; // determine delimiters fn_delimiter = _delimiter[filenaming].for_filename; ver_delimiter = _delimiter[filenaming].for_file_version; ext_delimiter = _delimiter[filenaming].for_file_extension; // allocate memory for filename descriptor new_filename = (objm2_filename_t *) OBJM2_ALLOCATE(sizeof(objm2_filename_t)); // bail out if allocation failed if (new_filename == NULL) { ASSIGN_BY_REF(status, OBJM2_FILENAME_STATUS_ALLOCATION_FAILED); return NULL; } // end if // determine the allocation size of the directory string size = 0; while (directory[size] != CSTRING_TERMINATOR) size++; // allocate memory for directory string new_filename->directory = (char *) OBJM2_ALLOCATE(size); // bail out if allocation failed if (new_filename->directory == NULL) { OBJM2_DEALLOCATE(new_filename); ASSIGN_BY_REF(status, OBJM2_FILENAME_STATUS_ALLOCATION_FAILED); return NULL; } // end if // copy the directory string index = 0; while (index < size) { new_filename->directory[index] = directory[index]; index++; } // end while // accumulated length total_length = index; // terminate the directory string new_filename->directory[index] = CSTRING_TERMINATOR; // determine the allocation size of the filename string size = 0; while (filename[size] != CSTRING_TERMINATOR) size++; // allocate memory for the filename string new_filename->filename = (char *) OBJM2_ALLOCATE(size); // bail out if allocation failed if (new_filename->filename == NULL) { OBJM2_DEALLOCATE(new_filename->directory); OBJM2_DEALLOCATE(new_filename); ASSIGN_BY_REF(status, OBJM2_FILENAME_STATUS_ALLOCATION_FAILED); return NULL; } // end if // copy the filename string index = 0; while (index < size) { new_filename->filename[index] = filename[index]; index++; } // end while // accumulated length total_length = total_length + index; // terminate the filename string new_filename->filename[index] = CSTRING_TERMINATOR; // allocate memory for the file extension string new_filename->extension = (char *) OBJM2_ALLOCATE(_MAX_EXTSTR_SIZE); // bail out if allocation failed if (new_filename->extension == NULL) { OBJM2_DEALLOCATE(new_filename->directory); OBJM2_DEALLOCATE(new_filename->filename); OBJM2_DEALLOCATE(new_filename); ASSIGN_BY_REF(status, OBJM2_FILENAME_STATUS_ALLOCATION_FAILED); return NULL; } // end if // copy the file extension string index = 0; while (_ext_string[file_type][index] != CSTRING_TERMINATOR) { new_filename->extension[index] = _ext_string[file_type][index]; index++; } // end while // accumulated length total_length = total_length + index; // terminate the file extension string new_filename->extension[index] = CSTRING_TERMINATOR; // calculate and set the path name length new_filename->path_length = total_length + 1; // set the file type and filenaming scheme new_filename->file_type = file_type; new_filename->filenaming = filenaming; ASSIGN_BY_REF(status, OBJM2_FILENAME_STATUS_SUCCESS); return (objm2_filename_t) new_filename; } // end objm2_new_filename // --------------------------------------------------------------------------- // function: objm2_new_filename_from_path( path, filenaming, status ) // --------------------------------------------------------------------------- // // Returns a newly allocated filename descriptor, initialised from path using // filenaming scheme . The caller is responsible for deallocating // the descriptor. The function tests the path for the presence of input file // extensions and sets the filename descriptor's file_type field as follows: // // o if the filename consists of nothing but the file extension then the // descriptor's file_type field is set to INVALID_FILENAME. // // o if the file extension is "DEF", "Def" or "def" then the descriptor's // file_type field is set to FILE_TYPE_DEF. // // o if the file extension is "MOD", "Mod" or "mod" then the descriptor's // file_type field is set to FILE_TYPE_MOD. // // o if the file extension is "PROT", "Prot" or "prot" then the descriptor's // file_type field is set to FILE_TYPE_PROT. // // o if the file extension matches none of the above file extensions then the // descriptor's file_type field is set to FILE_TYPE_UNKNOWN. // // For any missing component in an empty string is assigned to the // string field representing the missing component in the descriptor. If all // components are missing in , no descriptor will be created and NULL // will be returned. // // The status of the operation is passed back in , unless NULL was // passed in for . objm2_filename_t objm2_new_filename_from_path(const char *path, objm2_filenaming_t filenaming, objm2_filename_status_t *status) { objm2_filename_s *new_filename; int index, path_index, fn_index, ext_index; int end_of_path, length, total_length; cardinal ext_hash = OBJM2_HASH_INITIAL; char *fn_delimiter, *ext_delimiter, *ver_delimiter; // bail out if there is no path string if ((path == NULL) || (path[0] == CSTRING_TERMINATOR)) { ASSIGN_BY_REF(status, OBJM2_FILENAME_STATUS_INVALID_PATHNAME); return NULL; } // end if // use default if filenaming value is not specific or invalid if ((filenaming == DEFAULT_FILENAMING) || (filenaming >= NUMBER_OF_FILENAMING_SCHEMES)) filenaming = _default_filenaming; // determine delimiters fn_delimiter = _delimiter[filenaming].for_filename; ver_delimiter = _delimiter[filenaming].for_file_version; ext_delimiter = _delimiter[filenaming].for_file_extension; // find end of path index = 0; fn_index = -1; ext_index = -1; while ((path[index] != CSTRING_TERMINATOR) && (path[index] != ver_delimiter)) { if (path[index] == fn_delimiter) fn_index = index; if (path[index] == ext_delimiter) ext_index = index; index++; } // end while index--; end_of_path = index; if (end_of_path >= OBJM2_MAX_PATH_LENGTH) { ASSIGN_BY_REF(status, OBJM2_FILENAME_STATUS_PATHNAME_TOO_LONG); return NULL; } // end if // disregard ext delimiters in directory if (ext_index < fn_index) ext_index = -1; // length of directory string length = fn_index + 1; total_length = length; // allocate memory for directory string new_filename->directory = (char *) OBJM2_ALLOCATE(length + 1); // bail out if allocation failed if (new_filename->directory == NULL) { OBJM2_DEALLOCATE(new_filename); ASSIGN_BY_REF(status, OBJM2_FILENAME_STATUS_ALLOCATION_FAILED); return NULL; } // end if index = 0; if (length > 0) { // copy the directory string while (index < size) { new_filename->directory[index] = path[index]; index++; } // end while } // end if // terminate the directory string new_filename->directory[index] = CSTRING_TERMINATOR; // length of filename string if (ext_index < 0) length = end_of_path - fn_index; else length = ext_index - fn_index - 1; total_length = total_length + length; // allocate memory for filename string new_filename->filename = (char *) OBJM2_ALLOCATE(length + 1); // bail out if allocation failed if (new_filename->filename == NULL) { OBJM2_DEALLOCATE(new_filename->directory); OBJM2_DEALLOCATE(new_filename); ASSIGN_BY_REF(status, OBJM2_FILENAME_STATUS_ALLOCATION_FAILED); return NULL; } // end if index = 0; path_index = fn_index + 1; if (length > 0) { // copy the filename string while (index < size) { new_filename->filename[index] = path[path_index]; index++; path_index++; } // end while } // end if // terminate the filename string new_filename->filename[index] = CSTRING_TERMINATOR; // length of file extension string if (ext_index < 0) length = 0; else length = end_of_path - ext_index - 1; total_length = total_length + length; // allocate memory for file extension string new_filename->extension = (char *) OBJM2_ALLOCATE(length + 1); // bail out if allocation failed if (new_filename->filename == NULL) { OBJM2_DEALLOCATE(new_filename->directory); OBJM2_DEALLOCATE(new_filename->filename); OBJM2_DEALLOCATE(new_filename); ASSIGN_BY_REF(status, OBJM2_FILENAME_STATUS_ALLOCATION_FAILED); return NULL; } // end if index = 0; path_index = ext_index + 1; if (length > 0) { // copy the file extension string while (index < length) { new_filename->extension[index] = path[path_index]; ext_hash = OBJM2_HASH_NEXT_CHAR(ext_hash, path[path_index]); index++; path_index++; } // end while ext_hash = OBJM2_HASH_FINAL(ext_hash); } // end if // terminate the file extension string new_filename->extension[index] = CSTRING_TERMINATOR; // calculate and set the path name length new_filename->path_length = total_length + 1; // determine and set the file type if ((new_filename->filename[0] == CSTRING_TERMINATOR) || (new_filename->extension[0] == CSTRING_TERMINATOR)) new_filename->file_type = INVALID_FILENAME; else if ((ext_hash == OBJM2_EXT_DEF_UPPER) || (ext_hash == OBJM2_EXT_DEF_CAMEL) || (ext_hash == OBJM2_EXT_DEF_LOWER)) new_filename->file_type = FILE_TYPE_DEF; else if ((ext_hash == OBJM2_EXT_MOD_UPPER) || (ext_hash == OBJM2_EXT_MOD_CAMEL) || (ext_hash == OBJM2_EXT_MOD_LOWER)) new_filename->file_type = FILE_TYPE_MOD; else if ((ext_hash == OBJM2_EXT_PROT_UPPER) || (ext_hash == OBJM2_EXT_PROT_CAMEL) || (ext_hash == OBJM2_EXT_PROT_LOWER)) new_filename->file_type = FILE_TYPE_PROT; else new_filename->file_type = FILE_TYPE_UNKNOWN; // set the filenaming scheme new_filename->filenaming = filenaming; ASSIGN_BY_REF(status, OBJM2_FILENAME_STATUS_SUCCESS); return (objm2_filename_t) new_filename; } // end objm2_new_filename_from_path // --------------------------------------------------------------------------- // function: objm2_directory_string( filename ) // --------------------------------------------------------------------------- // // Returns an immutable pointer to the directory string field of filename // descriptor . Returns NULL if the descriptor is NULL. const char *objm2_directory_string(objm2_filename_t filename) { objm2_filename_s *this_filename = (objm2_filename_s *) filename; if (filename == NULL) return NULL; return (const char *) this_filename->directory; } // end objm2_directory_string // --------------------------------------------------------------------------- // function: objm2_filename_string( filename ) // --------------------------------------------------------------------------- // // Returns an immutable pointer to the filename string field of filename // descriptor . Returns NULL if the descriptor is NULL. const char *objm2_filename_string(objm2_filename_t filename) { objm2_filename_s *this_filename = (objm2_filename_s *) filename; if (filename == NULL) return NULL; return (const char *) this_filename->filename; } // end objm2_filename_string // --------------------------------------------------------------------------- // function: objm2_file_ext_string( filename ) // --------------------------------------------------------------------------- // // Returns an immutable pointer to the file_ext string field of filename // descriptor . Returns NULL if the descriptor is NULL. const char *objm2_file_ext_string(objm2_filename_t filename) { objm2_filename_s *this_filename = (objm2_filename_s *) filename; if (filename == NULL) return NULL; return (const char *) this_filename->extension; } // end objm2_file_ext_string // --------------------------------------------------------------------------- // function: objm2_file_type( filename ) // --------------------------------------------------------------------------- // // Returns the value of the file_type field of filename descriptor . // Returns FILE_TYPE_UNKNOWN if the descriptor is NULL. objm2_file_type_t objm2_file_type(objm2_filename_t filename) { objm2_filename_s *this_filename = (objm2_filename_s *) filename; if (filename == NULL) return FILE_TYPE_UNKNOWN; return this_filename->file_type; } // end objm2_file_type // --------------------------------------------------------------------------- // function: objm2_filenaming( filename ) // --------------------------------------------------------------------------- // // Returns the filenaming scheme of filename descriptor . Returns // DEFAULT_FILENAMING if the descriptor is NULL. objm2_filenaming_t objm2_filenaming(objm2_filename_t filename) { objm2_filename_s *this_filename = (objm2_filename_s *) filename; if (filename == NULL) return DEFAULT_FILENAMING; return this_filename->filenaming; } // end objm2_filenaming // --------------------------------------------------------------------------- // function: objm2_path_from_filename( filename, status ) // --------------------------------------------------------------------------- // // Returns a newly allocated C string with a pathname composed from the fields // in filename descriptor . The caller is responsible for // deallocating the returned C string. // // The status of the operation is passed back in , unless NULL was // passed in for . const char objm2_path_from_filename(objm2_filename_t filename, objm2_filename_status_t *status) { objm2_filename_s *this_filename = (objm2_filename_s *) filename; int index, path_index = 0; char *path; // bail out if filename descriptor is NULL if (filename == NULL) { ASSIGN_BY_REF(status, OBJM2_FILENAME_STATUS_INVALID_REFERENCE); return NULL; } // end if // allocate memory for path string path = (char *) OBJM2_ALLOCATE(this_filename->path_length + 1); // bail out if allocation failed if (path = NULL) { ASSIGN_BY_REF(status, OBJM2_FILENAME_STATUS_ALLOCATION_FAILED); return NULL; } // end if // copy the directory string index = 0; while (this_filename->directory[index] != CSTRING_TERMINATOR) { path[path_index] = this_filename->directory[index]; path_index++; index++; } // end while; // append the filename string index = 0; while (this_filename->filename[index] != CSTRING_TERMINATOR) { path[path_index] = this_filename->filename[index]; path_index++; index++; } // end while; // append the file extension delimiter path[path_index] = _delimiter[this_filename->filenaming].for_file_extension; path_index++; // append the file extension string index = 0; while (this_filename->extension[index] != CSTRING_TERMINATOR) { path[path_index] = this_filename->extension[index]; path_index++; index++; } // end while; // terminate the path string path[path_index] = CSTRING_TERMINATOR; ASSIGN_BY_REF(status, OBJM2_FILENAME_STATUS_SUCCESS); return (const char *) path; } // end objm2_path_from_filename // --------------------------------------------------------------------------- // function: objm2_dispose_filename( filename ) // --------------------------------------------------------------------------- // // Deallocates filename descriptor and all of its components. void objm2_dispose_filename(objm2_filename_t filename) { objm2_filename_s *this_filename = (objm2_filename_s *) filename; if (filename == NULL) return; OBJM2_DEALLOCATE(this_filename->directory); OBJM2_DEALLOCATE(this_filename->filename); OBJM2_DEALLOCATE(this_filename->extension); OBJM2_DEALLOCATE(this_filename); } // end objm2_dispose_filename // END OF FILE