/* Copyright (c) 2014 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Alex Diener alex@ludobloom.com */ #ifndef __IOUtilities_H__ #define __IOUtilities_H__ #include "utilities/Atom.h" #include #include #include #include struct memreadContext { const void * data; size_t length; size_t position; }; struct memreadContext memreadContextInit(const void * data, size_t length); bool memread(struct memreadContext * context, size_t nbytes, void * outData); struct memwriteContext { void * data; size_t length; size_t allocatedSize; size_t position; bool realloc; }; // If reallocIfNeeded is true, the memwriteContext struct takes ownership of the data pointer. You // should no longer directly access the data pointer you passed to the function; instead, access the // data field of the returned memwriteContext struct. However, if reallocIfNeeded is set to false, // you retain ownership of the pointer, and you can continue accessing it as normal. struct memwriteContext memwriteContextInit(void * data, size_t length, size_t allocatedSize, bool reallocIfNeeded); bool memwrite(struct memwriteContext * context, size_t nbytes, const void * inData); void * readFileSimple(const char * filePath, size_t * outFileLength); bool writeFileSimple(const char * filePath, const void * contents, size_t length); #ifdef WIN32 #define writeFileAtomic(filePath, contents, length) writeFileSimple(filePath, contents, length) #else bool writeFileAtomic(const char * filePath, const void * contents, size_t length); #endif void * readStdinSimple(size_t * outLength); // Use this callback to read a resource file, with behavior defined by the implementation of the current shell. // Shells can set this to a different value to change its behavior. Defaults to readFileSimple. extern void * (* readResourceFile)(const char * fileIdentifier, size_t * outFileLength); // fileNameTemplate is a mkstemp template. Return value is added to AutoFreePool, so it doesn't need to be freed directly by the caller. const char * temporaryFilePath(const char * fileNameTemplate, int * outFD); // Guarantees null termination on all platforms (stdlib *snprintf/strncpy doesn't terminate on Windows if it truncates) int snprintf_safe(char * restrict str, size_t size, const char * restrict format, ...) __attribute__((format(printf, 3, 4))); int vsnprintf_safe(char * restrict str, size_t size, const char * restrict format, va_list ap); char * strncpy_safe(char * restrict dst, const char * restrict src, size_t n); char * strncat_safe(char * restrict s1, const char * restrict s2, size_t n); // Measures the length of str, subtracts it from size, and appends formatted string to the end if it fits int snprintf_append(char * restrict str, size_t size, const char * restrict format, ...) __attribute__((format(printf, 3, 4))); int vsnprintf_append(char * restrict str, size_t size, const char * restrict format, va_list ap); // Returns NULL for NULL arguments, calls strdup otherwise char * strdup_nullSafe(const char * s); // If one argument is NULL, that argument is less than the other one. If both are NULL, 0 is returned. // Calls strcmp otherwise. int strcmp_nullSafe(const char * s1, const char * s2); #ifdef STEM_PLATFORM_windows char * strndup(const char * s, size_t n); #endif // strdup_length allocates a new null-terminated string and copies the first n characters of s into it. // s does not need to be null terminated. Note that this differs from strndup in that n is an exact // length, rather than a maximum. char * strdup_length(const char * s, size_t n); // Allocates and returns a pointer with the contents of data copied into it. If size is 0, returns NULL. void * memdup(const void * data, size_t size); // Returns a value indicating whether s1 is less than (-1), equal to (0), or greater than (1) s2. // Works similarly to strcmp, but contiguous runs of integer characters are compared as a unit, such that a string like // "abc10def" will be considered greater than a string like "abc2def". int lexstrcmp(const char * s1, const char * s2); // Returns the trailing portion of path after the last . character, if any; otherwise returns an empty string. // The returned string is a pointer into path, so it does not need to be freed, but it will be invalidated if path is freed. const char * getFileExtension(const char * path); // Returns the file or directory name at the end of path. If the last character is a path separator (/), it will be included. // The returned string is a pointer into path, so it does not need to be freed, but it will be invalidated if path is freed. const char * getLastPathComponent(const char * path); // Returns a new string constructed by removing the file or directory name at the end of path, resulting in the path to the // directory containing that file. The direct opposite of getLastPathComponent(), except in the case of a path specifying // the root directory (/), in which case the string is returned unaltered. Includes the trailing path separator character. // The returned string is allocated and added to AutoFreePool, so it should not be freed directly by the caller. // On Windows, backslashes are treated the same as slashes. Root directory can start with a drive letter, slash, or backslash. const char * getDirectory(const char * path); // Returns a new string constructed by removing the extension at the end of path. Not the direct opposite of // getFileExtension(), since both omit the (.) separator. // The returned string is allocated and added to AutoFreePool, so it should not be freed directly by the caller. const char * getBaseName(const char * path); // Returns a new string constructed by replacing the file name at the end of path with siblingName. // The returned string is allocated and added to AutoFreePool, so it should not be freed directly by the caller. const char * getSiblingFilePath(const char * path, const char * siblingName); // Returns a new string representing a path relative to basePath that points to targetPath. For example, if basePath is // "/base/subdir/subdir2/" and targetPath is "/base/subdir/file", the returned string will be "../file". If basePath is // absolute, targetPath should also be absolute, and vice versa; mixing absolute and relative paths will give incorrect // results. If basePath ends with something other than a slash, its last component is assumed to be a file and ignored; // for example, getPathRelativeTo("a/b", "a/c") returns "c", while getPathRelativeTo("a/b/", "a/c") returns "../c". const char * getPathRelativeTo(const char * basePath, const char * targetPath); // Returns a new string representing the absolute path to the file specified by relativePath, relative to basePath. // If relativePath starts with a slash, basePath is ignored; otherwise, basePath and relativePath are concatenated. // Any current or parent directory references (. or ..) contained within are squashed. As with getPathRelativeTo(), if // basePath ends with something other than a slash, its last component is assumed to be a file and ignored. const char * getAbsolutePath(const char * basePath, const char * relativePath); // Returns a string representing a path to a file that doesn't collide with any existing files in the specified // directory. The file name "[baseName].[extension]" is tried first, and if it already exists, an increasing // number will be appended, forming "[baseName]_1.[extension]". Numbers up to the specified limit will be checked, // and if a unique name still cannot be found, the function returns NULL. The returned string is allocated and // added to AutoFreePool, so it should not be freed directly by the caller. const char * uniqueFileInDirectory(const char * directory, const char * baseName, const char * extension, unsigned int limit); // Writes each byte in blob as a two-character hex string in outString, up to maxLength, returning outString char * printHexString(const void * blob, size_t length, char * outString, size_t maxLength); #define DECIMAL_STRING_MAX 43 #define DECIMAL_PRECISION_MAX 20 // Writes a decimal representation of value to outString of no more than maxLength, and returns the number of characters // written. Always includes a null terminator. The formatted number will never include an exponent, and will truncate // trailing fractional zero characters beyond decimalPrecisionMin, up to and including the decimal point if // decimalPrecisionMin is 0. If decimalPrecisionMin is greater than the number of decimal digits required for // representation, trailing zeroes are appended. // Fractional precision of a greater number of digits than decimalPrecisionMax will be truncated (not rounded). // Regardless of the provided value, no more fractional digits than DECIMAL_PRECISION_MAX can be written. If // decimalPrecisionMin is greater than decimalPrecisionMax, decimalPrecisionMax takes precedence and decimalPrecisionMin // is clamped to it. // Inputs of positive/negative infinity or NaN will be written as "inf", "-inf", and "nan" respectively. // Results are locale-independent and always use '.' as the decimal separator. // Extremely large numbers (beyond UINT64_MAX) may be truncated. Decimal precision is also somewhat limited; no more // than 20 decimal places can be represented, and accuracy will be poor when close to that limit. Maximum total number // of bytes written to outString (a negative number with the largest possible whole part and the largest possible // decimal part, plus null terminator) is DECIMAL_STRING_MAX. unsigned int formatDecimalString(double value, char * outString, unsigned int maxLength, unsigned int decimalPrecisionMin, unsigned int decimalPrecisionMax); // Reads characters from string, interpreting them as a number and writing the interpreted result to outNumber. // Expects numbers formatted with '.' as a decimal point separator, as written by formatDecimalString. Does not parse // exponents or hexadecimal values. Inputs of "inf", "-inf", and "nan" will be interpreted as positive infinity, // negative infinity, and a generic NaN value respectively. If outCharactersRead is nonzero, the number of characters // interpreted (up to length) is written to it on return. Any non-numeric characters (other than '.' and '-') will // terminate parsing. If a number was successfully parsed, outNumber is written, outCharactersRead is written if // non-NULL, and true is returned. If parsing fails (first character is not a numeral or '-', or there are too many // numerals to parse without overflow), outNumber is not written, 0 is written to outCharactersRead if non-NULL, and // false is returned. // No more than length characters will be read from string. If length is UINT_MAX, reading will continue until // no more numbers can be interpreted and/or a null terminator is encountered. bool parseDecimalString(double * outNumber, const char * string, unsigned int length, unsigned int * outCharactersRead); // Converts a human-readable string to a simplified representation, removing punctuation, downcasing all letters, and // replacing runs of spaces with a single underscore. Truncates leading and trailing spaces. Truncates string if longer // than lengthMax. The returned string should be freed with free(). char * createIdentifierFromDisplayString(const char * displayString, unsigned int lengthMax); // Creates a new unique Atom from newIdentifier, adding a suffix with a number if necessary to achieve uniqueness, with // the overall string being no longer than lengthMax. getIdentifierAtIndexCallback is called to check uniqueness, with // every index from 0 to identifierCount - 1, and it should return all other strings in the pool where the return value // needs to be unique. Tries up to 9999 number suffixes, and calls abort() if no unique string can be found. Atom stringToUniqueIdentifier(const char * newIdentifier, unsigned int lengthMax, unsigned int identifierCount, Atom (* getIdentifierAtIndexCallback)(unsigned int index, void * context), void * context); // Allocates and returns a unique string, using a mechanism similar to stringToUniqueIdentifier. For uniqueness // suffixing, " copy" is first added, then " copy 2" if nonunique, then increasing numbers after that until a unique // string is found. If the string already has a " copy" or " copy %u" suffix, a new one will not be appended, and // the number will be adjusted upward. Tries up to " copy 9999", and calls abort() if no unique string can be found. char * newDisplayStringWithCopySuffix(const char * string, unsigned int lengthMax, unsigned int compareStringCount, const char * (* getCompareStringAtIndexCallback)(unsigned int index, void * context), void * context); // Allocates and returns a unique string, using a mechanism similar to newDisplayStringWithCopySuffix. The suffix // is simply " %u". Tries up to 9999 number suffixes, and calls abort() if no unique string can be found. char * newDisplayStringWithNumberSuffix(const char * string, unsigned int lengthMax, unsigned int compareStringCount, const char * (* getCompareStringAtIndexCallback)(unsigned int index, void * context), void * context); // Lists every file in the specified directory, calling callback once per file with name of each item in the directory. // If callback returns false, enumeration stops immediately and this function returns. The fileName pointer provided to // callback does not have a lifetime beyond that function call, and should be copied if its value needs to be preserved // after the callback returns. // If includeInvisible is false, file names prefixed with "." are omitted from enumeration. void enumerateFilesInDirectory(const char * directoryPath, bool includeInvisible, bool (* callback)(const char * directoryPath, const char * fileName, void * context), void * callbackContext); static inline uint16_t swapEndian16(uint16_t value) { return (value & 0xFF) << 8 | (value & 0xFF00) >> 8; } static inline uint32_t swapEndian32(uint32_t value) { return (value & 0xFF) << 24 | (value & 0xFF00) << 8 | (value & 0xFF0000) >> 8 | (value & 0xFF000000) >> 24; } static inline uint64_t swapEndian64(uint64_t value) { return (value & 0xFF) << 56 | (value & 0xFF00) << 40 | (value & 0xFF0000) << 24 | (value & 0xFF000000u) << 8 | (value & 0xFF00000000ull) >> 8 | (value & 0xFF0000000000ull) >> 24 | (value & 0xFF000000000000ull) >> 40 | (value & 0xFF00000000000000ull) >> 56; } static inline float swapEndianFloat32(float value) { union {float f; uint32_t u32;} valueUnion = {.f = value}; valueUnion.u32 = swapEndian32(valueUnion.u32); return valueUnion.f; } static inline float swapEndianFloat64(double value) { union {double d; uint64_t u64;} valueUnion = {.d = value}; valueUnion.u64 = swapEndian32(valueUnion.u64); return valueUnion.d; } #endif