| // © 2016 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html |
| /* |
| ******************************************************************************* |
| * |
| * Copyright (C) 1999-2014, International Business Machines |
| * Corporation and others. All Rights Reserved. |
| * |
| ******************************************************************************* |
| * file name: toolutil.c |
| * encoding: UTF-8 |
| * tab size: 8 (not used) |
| * indentation:4 |
| * |
| * created on: 1999nov19 |
| * created by: Markus W. Scherer |
| * |
| * 6/25/08 - Added Cygwin specific code in uprv_mkdir - Brian Rower |
| * |
| * This file contains utility functions for ICU tools like genccode. |
| */ |
| #include "unicode/platform.h" |
| #if U_PLATFORM == U_PF_MINGW |
| // *cough* - for struct stat |
| #ifdef __STRICT_ANSI__ |
| #undef __STRICT_ANSI__ |
| #endif |
| #endif |
| |
| #include <stdio.h> |
| #include <sys/stat.h> |
| #include "unicode/utypes.h" |
| |
| #ifndef U_TOOLUTIL_IMPLEMENTATION |
| #error U_TOOLUTIL_IMPLEMENTATION not set - must be set for all ICU source files in common/ - see https://unicode-org.github.io/icu/userguide/howtouseicu |
| #endif |
| |
| #if U_PLATFORM_USES_ONLY_WIN32_API |
| # define VC_EXTRALEAN |
| # define WIN32_LEAN_AND_MEAN |
| # define NOUSER |
| # define NOSERVICE |
| # define NOIME |
| # define NOMCX |
| # if U_PLATFORM == U_PF_MINGW |
| # define __NO_MINGW_LFS /* gets around missing 'off64_t' */ |
| # endif |
| # include <windows.h> |
| # include <direct.h> |
| #else |
| # include <sys/stat.h> |
| # include <sys/types.h> |
| #endif |
| |
| /* In MinGW environment, io.h needs to be included for _mkdir() */ |
| #if U_PLATFORM == U_PF_MINGW |
| #include <io.h> |
| #endif |
| |
| #include <errno.h> |
| |
| #include <cstddef> |
| |
| #include "unicode/errorcode.h" |
| #include "unicode/putil.h" |
| #include "cmemory.h" |
| #include "cstring.h" |
| #include "toolutil.h" |
| #include "unicode/ucal.h" |
| |
| U_NAMESPACE_BEGIN |
| |
| IcuToolErrorCode::~IcuToolErrorCode() { |
| // Safe because our handleFailure() does not throw exceptions. |
| if(isFailure()) { handleFailure(); } |
| } |
| |
| void IcuToolErrorCode::handleFailure() const { |
| fprintf(stderr, "error at %s: %s\n", location, errorName()); |
| exit(errorCode); |
| } |
| |
| U_NAMESPACE_END |
| |
| static int32_t currentYear = -1; |
| |
| U_CAPI int32_t U_EXPORT2 getCurrentYear() { |
| #if !UCONFIG_NO_FORMATTING |
| UErrorCode status=U_ZERO_ERROR; |
| UCalendar *cal = NULL; |
| |
| if(currentYear == -1) { |
| cal = ucal_open(NULL, -1, NULL, UCAL_TRADITIONAL, &status); |
| ucal_setMillis(cal, ucal_getNow(), &status); |
| currentYear = ucal_get(cal, UCAL_YEAR, &status); |
| ucal_close(cal); |
| } |
| #else |
| /* No formatting- no way to set the current year. */ |
| #endif |
| return currentYear; |
| } |
| |
| |
| U_CAPI const char * U_EXPORT2 |
| getLongPathname(const char *pathname) { |
| #if U_PLATFORM_USES_ONLY_WIN32_API |
| /* anticipate problems with "short" pathnames */ |
| static WIN32_FIND_DATAA info; |
| HANDLE file=FindFirstFileA(pathname, &info); |
| if(file!=INVALID_HANDLE_VALUE) { |
| if(info.cAlternateFileName[0]!=0) { |
| /* this file has a short name, get and use the long one */ |
| const char *basename=findBasename(pathname); |
| if(basename!=pathname) { |
| /* prepend the long filename with the original path */ |
| uprv_memmove(info.cFileName+(basename-pathname), info.cFileName, uprv_strlen(info.cFileName)+1); |
| uprv_memcpy(info.cFileName, pathname, basename-pathname); |
| } |
| pathname=info.cFileName; |
| } |
| FindClose(file); |
| } |
| #endif |
| return pathname; |
| } |
| |
| U_CAPI const char * U_EXPORT2 |
| findDirname(const char *path, char *buffer, int32_t bufLen, UErrorCode* status) { |
| if(U_FAILURE(*status)) return NULL; |
| const char *resultPtr = NULL; |
| int32_t resultLen = 0; |
| |
| const char *basename=uprv_strrchr(path, U_FILE_SEP_CHAR); |
| if (U_FILE_ALT_SEP_CHAR != U_FILE_SEP_CHAR) { |
| const char* basenameAlt = uprv_strrchr(path, U_FILE_ALT_SEP_CHAR); |
| if (basenameAlt && (!basename || basename < basenameAlt)) { |
| basename = basenameAlt; |
| } |
| } |
| if(!basename) { |
| /* no basename - return ''. */ |
| resultPtr = ""; |
| resultLen = 0; |
| } else { |
| resultPtr = path; |
| resultLen = static_cast<int32_t>(basename - path); |
| if(resultLen<1) { |
| resultLen = 1; /* '/' or '/a' -> '/' */ |
| } |
| } |
| |
| if((resultLen+1) <= bufLen) { |
| uprv_strncpy(buffer, resultPtr, resultLen); |
| buffer[resultLen]=0; |
| return buffer; |
| } else { |
| *status = U_BUFFER_OVERFLOW_ERROR; |
| return NULL; |
| } |
| } |
| |
| U_CAPI const char * U_EXPORT2 |
| findBasename(const char *filename) { |
| const char *basename=uprv_strrchr(filename, U_FILE_SEP_CHAR); |
| |
| if (U_FILE_ALT_SEP_CHAR != U_FILE_SEP_CHAR) { |
| //be lenient about pathname separators on Windows, like official implementation of C++17 std::filesystem in MSVC |
| //would be convenient to merge this loop with the one above, but alas, there is no such solution in the standard library |
| const char *alt_basename=uprv_strrchr(filename, U_FILE_ALT_SEP_CHAR); |
| if(alt_basename>basename) { |
| basename=alt_basename; |
| } |
| } |
| |
| if(basename!=NULL) { |
| return basename+1; |
| } else { |
| return filename; |
| } |
| } |
| |
| U_CAPI void U_EXPORT2 |
| uprv_mkdir(const char *pathname, UErrorCode *status) { |
| |
| int retVal = 0; |
| #if U_PLATFORM_USES_ONLY_WIN32_API |
| retVal = _mkdir(pathname); |
| #else |
| retVal = mkdir(pathname, S_IRWXU | (S_IROTH | S_IXOTH) | (S_IROTH | S_IXOTH)); |
| #endif |
| if (retVal && errno != EEXIST) { |
| #if U_PF_MINGW <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN |
| /*if using Cygwin and the mkdir says it failed...check if the directory already exists..*/ |
| /* if it does...don't give the error, if it does not...give the error - Brian Rower - 6/25/08 */ |
| struct stat st; |
| |
| if(stat(pathname,&st) != 0) |
| { |
| *status = U_FILE_ACCESS_ERROR; |
| } |
| #else |
| *status = U_FILE_ACCESS_ERROR; |
| #endif |
| } |
| } |
| |
| #if !UCONFIG_NO_FILE_IO |
| U_CAPI UBool U_EXPORT2 |
| uprv_fileExists(const char *file) { |
| struct stat stat_buf; |
| if (stat(file, &stat_buf) == 0) { |
| return TRUE; |
| } else { |
| return FALSE; |
| } |
| } |
| #endif |
| |
| /*U_CAPI UDate U_EXPORT2 |
| uprv_getModificationDate(const char *pathname, UErrorCode *status) |
| { |
| if(U_FAILURE(*status)) { |
| return; |
| } |
| // TODO: handle case where stat is not available |
| struct stat st; |
| |
| if(stat(pathname,&st) != 0) |
| { |
| *status = U_FILE_ACCESS_ERROR; |
| } else { |
| return st.st_mtime; |
| } |
| } |
| */ |
| |
| /* tool memory helper ------------------------------------------------------- */ |
| |
| struct UToolMemory { |
| char name[64]; |
| int32_t capacity, maxCapacity, size, idx; |
| void *array; |
| alignas(std::max_align_t) char staticArray[1]; |
| }; |
| |
| U_CAPI UToolMemory * U_EXPORT2 |
| utm_open(const char *name, int32_t initialCapacity, int32_t maxCapacity, int32_t size) { |
| UToolMemory *mem; |
| |
| if(maxCapacity<initialCapacity) { |
| maxCapacity=initialCapacity; |
| } |
| |
| mem=(UToolMemory *)uprv_malloc(sizeof(UToolMemory)+initialCapacity*size); |
| if(mem==NULL) { |
| fprintf(stderr, "error: %s - out of memory\n", name); |
| exit(U_MEMORY_ALLOCATION_ERROR); |
| } |
| mem->array=mem->staticArray; |
| |
| uprv_strcpy(mem->name, name); |
| mem->capacity=initialCapacity; |
| mem->maxCapacity=maxCapacity; |
| mem->size=size; |
| mem->idx=0; |
| return mem; |
| } |
| |
| U_CAPI void U_EXPORT2 |
| utm_close(UToolMemory *mem) { |
| if(mem!=NULL) { |
| if(mem->array!=mem->staticArray) { |
| uprv_free(mem->array); |
| } |
| uprv_free(mem); |
| } |
| } |
| |
| |
| U_CAPI void * U_EXPORT2 |
| utm_getStart(UToolMemory *mem) { |
| return (char *)mem->array; |
| } |
| |
| U_CAPI int32_t U_EXPORT2 |
| utm_countItems(UToolMemory *mem) { |
| return mem->idx; |
| } |
| |
| |
| static UBool |
| utm_hasCapacity(UToolMemory *mem, int32_t capacity) { |
| if(mem->capacity<capacity) { |
| int32_t newCapacity; |
| |
| if(mem->maxCapacity<capacity) { |
| fprintf(stderr, "error: %s - trying to use more than maxCapacity=%ld units\n", |
| mem->name, (long)mem->maxCapacity); |
| exit(U_MEMORY_ALLOCATION_ERROR); |
| } |
| |
| /* try to allocate a larger array */ |
| if(capacity>=2*mem->capacity) { |
| newCapacity=capacity; |
| } else if(mem->capacity<=mem->maxCapacity/3) { |
| newCapacity=2*mem->capacity; |
| } else { |
| newCapacity=mem->maxCapacity; |
| } |
| |
| if(mem->array==mem->staticArray) { |
| mem->array=uprv_malloc(newCapacity*mem->size); |
| if(mem->array!=NULL) { |
| uprv_memcpy(mem->array, mem->staticArray, (size_t)mem->idx*mem->size); |
| } |
| } else { |
| mem->array=uprv_realloc(mem->array, newCapacity*mem->size); |
| } |
| |
| if(mem->array==NULL) { |
| fprintf(stderr, "error: %s - out of memory\n", mem->name); |
| exit(U_MEMORY_ALLOCATION_ERROR); |
| } |
| mem->capacity=newCapacity; |
| } |
| |
| return TRUE; |
| } |
| |
| U_CAPI void * U_EXPORT2 |
| utm_alloc(UToolMemory *mem) { |
| char *p=NULL; |
| int32_t oldIndex=mem->idx; |
| int32_t newIndex=oldIndex+1; |
| if(utm_hasCapacity(mem, newIndex)) { |
| p=(char *)mem->array+oldIndex*mem->size; |
| mem->idx=newIndex; |
| uprv_memset(p, 0, mem->size); |
| } |
| return p; |
| } |
| |
| U_CAPI void * U_EXPORT2 |
| utm_allocN(UToolMemory *mem, int32_t n) { |
| char *p=NULL; |
| int32_t oldIndex=mem->idx; |
| int32_t newIndex=oldIndex+n; |
| if(utm_hasCapacity(mem, newIndex)) { |
| p=(char *)mem->array+oldIndex*mem->size; |
| mem->idx=newIndex; |
| uprv_memset(p, 0, n*mem->size); |
| } |
| return p; |
| } |