| /* $Id$ | |
| * | |
| * Licensed to the Apache Software Foundation (ASF) under one or more | |
| * contributor license agreements. See the NOTICE file distributed with | |
| * this work for additional information regarding copyright ownership. | |
| * The ASF licenses this file to you under the Apache License, Version | |
| * 2.0 (the "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| /* | |
| * etchutl.c -- utility methods | |
| */ | |
| /* todo include a dummy windows.h for linux build */ | |
| #include <windows.h> | |
| #include "etchutl.h" | |
| #include "etch_global.h" | |
| #include "etch_encoding.h" | |
| #include <sys/stat.h> | |
| #include <stdio.h> | |
| #include <errno.h> | |
| /** | |
| * get_map_keys() | |
| * return a collection of the specified map's keys. | |
| * caller must invoke the destructor on the returned list. the returned list | |
| * is marked as readonly content, in order that arraylist_destroy() will not | |
| * attempt to free memory for the list content, which remains owned by the map. | |
| */ | |
| etch_arraylist* get_map_keys(etch_hashtable* map) | |
| { | |
| etch_iterator iterator; | |
| etch_arraylist* list = NULL; | |
| const int typecount = map->vtab->count(map->realtable,0,0); | |
| list = new_arraylist(typecount, 0); | |
| list->content_type = ETCHARRAYLIST_CONTENT_OBJECT; | |
| list->is_readonly = TRUE; /* content is refs to objects owned by map */ | |
| set_iterator(&iterator, map, &map->iterable); | |
| while(iterator.vtab->has_next(&iterator)) | |
| { | |
| arraylist_add(list, iterator.current_key); | |
| iterator.vtab->next(&iterator); | |
| } | |
| return list; | |
| } | |
| /** | |
| * get_map_values() | |
| * return a collection of the specified map's values. | |
| * caller must invoke the destructor on the returned list. the returned list | |
| * is marked as readonly content, in order that arraylist_destroy() will not | |
| * attempt to free memory for the list content, which remains owned by the map. | |
| */ | |
| etch_arraylist* get_map_values(etch_hashtable* map) | |
| { | |
| etch_iterator iterator; | |
| etch_arraylist* list = NULL; | |
| const int typecount = map->vtab->count(map->realtable,0,0); | |
| list = new_arraylist(typecount, 0); | |
| list->content_type = map->content_type; | |
| list->is_readonly = TRUE; /* content is refs to objects owned by map */ | |
| set_iterator(&iterator, map, &map->iterable); | |
| while(iterator.vtab->has_next(&iterator)) | |
| { | |
| arraylist_add(list, iterator.current_value); | |
| iterator.vtab->next(&iterator); | |
| } | |
| return list; | |
| } | |
| /** | |
| * hexchar_to_int() | |
| */ | |
| int hexchar_to_int (const unsigned char hexchar) | |
| { | |
| switch(hexchar) | |
| { case '0': return 0; | |
| case '1': return 1; | |
| case '2': return 2; | |
| case '3': return 3; | |
| case '4': return 4; | |
| case '5': return 5; | |
| case '6': return 6; | |
| case '7': return 7; | |
| case '8': return 8; | |
| case '9': return 9; | |
| case 'a': case 'A': return 10; | |
| case 'b': case 'B': return 11; | |
| case 'c': case 'C': return 12; | |
| case 'd': case 'D': return 13; | |
| case 'e': case 'E': return 14; | |
| case 'f': case 'F': return 15; | |
| } | |
| return -1; | |
| } | |
| /** | |
| * hexwchar_to_int() | |
| */ | |
| int hexwchar_to_int (const wchar_t hexwchar) | |
| { | |
| switch(hexwchar) | |
| { case L'0': return 0; | |
| case L'1': return 1; | |
| case L'2': return 2; | |
| case L'3': return 3; | |
| case L'4': return 4; | |
| case L'5': return 5; | |
| case L'6': return 6; | |
| case L'7': return 7; | |
| case L'8': return 8; | |
| case L'9': return 9; | |
| case L'a': case L'A': return 10; | |
| case L'b': case L'B': return 11; | |
| case L'c': case L'C': return 12; | |
| case L'd': case L'D': return 13; | |
| case L'e': case L'E': return 14; | |
| case L'f': case L'F': return 15; | |
| } | |
| return -1; | |
| } | |
| /** | |
| * is_etcharray() | |
| */ | |
| #if(0) /* this method is not needed yet so is left out of the compile */ | |
| int is_etcharray(objmask* obj, etch_arrayinfo* info) | |
| { | |
| union_alltypes u; | |
| if (info) memset(info, 0, sizeof(etch_arrayinfo)); | |
| if (NULL == obj) return FALSE; | |
| switch(obj->obj_type) | |
| { | |
| case ETCHTYPEB_ARRAYLIST: | |
| case ETCHTYPEB_ARRAYVALUE: | |
| case ETCHTYPEB_NATIVEARRAY: | |
| break; | |
| default: return FALSE; | |
| } | |
| if (NULL == info) return TRUE; | |
| switch(obj->obj_type) | |
| { | |
| case ETCHTYPEB_ARRAYLIST: | |
| u.varraylist = (etch_arraylist*) obj; | |
| info->array_type = ETCH_ARRAYTYPE_LIST; | |
| info->is_content_object | |
| = u.varraylist->content_type = ETCHARRAYLIST_CONTENT_OBJECT ; | |
| info->numdims = 1; | |
| info->numitems = u.varraylist->count; | |
| break; | |
| case ETCHTYPEB_ARRAYVALUE: | |
| u.varrayval = (etch_arrayvalue*) obj; | |
| info->array_type = ETCH_ARRAYTYPE_ARRAYVAL; | |
| info->is_content_object = TRUE; | |
| info->numdims = u.varrayval->dim; | |
| info->numitems = u.varrayval->length; | |
| break; | |
| case ETCHTYPEB_NATIVEARRAY: | |
| u.vnatarray = (etch_nativearray*) obj; | |
| info->array_type = ETCH_ARRAYTYPE_NATIVE; | |
| info->is_content_object = u.vnatarray->content_class_id == CLASSID_NONE; | |
| info->numdims = u.vnatarray->numdims; | |
| info->numitems = (int) u.vnatarray->dimension[info->numdims]; | |
| break; | |
| } | |
| return TRUE; | |
| } | |
| #endif /* this method is not needed yet so is left out of the compile */ | |
| /** | |
| * etch_existsfile() | |
| * determine if specified path exists on the file system. | |
| * @return boolean valued indicator. | |
| */ | |
| int etch_existsfile (char* path) | |
| { | |
| struct stat fs; | |
| return 0 == stat(path, &fs); | |
| } | |
| /** | |
| * etchlog_make_logfilepatha() | |
| * build and return a path to specified log file. | |
| * @return an etch_malloc'ed path string, caller owns it. | |
| */ | |
| char* etchlog_make_logfilepatha (char* dirpath, char* filename) | |
| { | |
| const size_t pathlen = strlen(dirpath), namelen = strlen(filename); | |
| const size_t pathbuflen = pathlen + namelen + sizeof('/') + sizeof('\0'); | |
| char* outpath = etch_malloc (pathbuflen, 0), *p = outpath; | |
| memcpy (p, dirpath, pathlen); p += pathlen; | |
| *p++ = '/'; | |
| memcpy (p, filename, namelen); p += namelen; | |
| *p = '\0'; | |
| return outpath; /* caller must etch_free */ | |
| } | |
| /** | |
| * etchlog_make_logfilepathw() | |
| * build and return a path to specified log file. | |
| * @return an etch_malloc'ed path string, caller owns it. | |
| */ | |
| char* etchlog_make_logfilepathw (char* dirpath, wchar_t* filename) | |
| { | |
| char* aname = NULL, *outpath = NULL; | |
| etch_unicode_to_utf8 (&aname, filename); | |
| outpath = etchlog_make_logfilepatha (dirpath, aname); | |
| etch_free (aname); | |
| return outpath; /* caller must etch_free */ | |
| } | |
| /** | |
| * etchlog_is_logfilew() | |
| * determine if specified path identifies a log file. | |
| */ | |
| int is_etch_logfilew (const wchar_t* filepath) | |
| { | |
| int is_logfile = 0, extndx = 0; | |
| const wchar_t* ETCHLOG_WIDEEXT = L".log"; | |
| const size_t extlen = wcslen(ETCHLOG_WIDEEXT); | |
| const int wstrlen = (int) wcslen (filepath); | |
| if (wstrlen < 1 || wstrlen > MAXPATHSIZE) return FALSE; | |
| extndx = (int) wstrlen - (int) extlen; /* point at .log extension */ | |
| is_logfile = (0 == wcscmp (&filepath[extndx], ETCHLOG_WIDEEXT)); | |
| return is_logfile; | |
| } | |
| /** | |
| * etchlog_is_logfilea() | |
| * determine if specified path identifies a log file. | |
| */ | |
| int is_etch_logfilea (const char* filepath) | |
| { | |
| int is_logfile = 0, extndx = 0; | |
| const char* ETCHLOG_LOGEXT = ".log"; | |
| const size_t extlen = strlen(ETCHLOG_LOGEXT); | |
| const int astrlen = (int) strlen (filepath); | |
| if (astrlen < 1 || astrlen > MAXPATHSIZE) return FALSE; | |
| extndx = (int) astrlen - (int) extlen; /* point at .log extension */ | |
| is_logfile = (0 == strcmp (&filepath[extndx], ETCHLOG_LOGEXT)); | |
| return is_logfile; | |
| } | |
| /** | |
| * etchlog_countfiles() | |
| * count log files in specified directory. | |
| * @param dirpath narrow directory string, may be relative, can't be root or net. | |
| * @return count of files in directory having .log file extension. | |
| * @remarks do not invoke this until after etch runtime is initialized, since | |
| * encoding functions use etch_malloc(), which requires the global cache. | |
| * @remarks this uses the linux/unix idiom for enumerating files in a directory. | |
| * etch emulations of those routines for windows are provided following. | |
| */ | |
| int etchlog_countfiles(char* dirpath) | |
| { | |
| int logfilecount = 0; | |
| DIR* dirstream = NULL; | |
| dirent* direntry = NULL; | |
| assert(is_runtime_initialized); | |
| dirstream = opendir(dirpath); | |
| while(1) | |
| { if (NULL == (direntry = readdir (dirstream))) break; | |
| if (is_etch_logfilew (direntry->d_name)) | |
| logfilecount++; | |
| } | |
| closedir(dirstream); /* free DIR memory */ | |
| return logfilecount; | |
| } | |
| /** | |
| * etchlog_purgefiles() | |
| * purge up tp specified number of log files from specified directory. | |
| * @param dirpath narrow directory string, can be relative, can't be root or net. | |
| * @param count max number of files to purge. | |
| * @param option not currently used. | |
| * @return count of files so purged. | |
| * @remarks do not invoke this until after etch runtime is initialized, since | |
| * encoding functions use etch_malloc(), which requires the global cache. | |
| * @remarks this uses the linux/unix idiom for enumerating files in a directory. | |
| * etch emulations of those routines for windows are provided following. | |
| */ | |
| int etchlog_purgefiles (char* dirpath, const int count, const int option) | |
| { | |
| int purgedcount = 0; | |
| DIR* dirstream = NULL; | |
| dirent* direntry = NULL; | |
| assert(is_runtime_initialized); | |
| /* TODO not sure if we will be dealing with narrow or wide paths in | |
| * linux dirent - if narrow, we'll need to modify code a bit here. | |
| */ | |
| dirstream = opendir(dirpath); | |
| while(purgedcount < count) | |
| { | |
| if (NULL == (direntry = readdir (dirstream))) break; | |
| if (is_etch_logfilew (direntry->d_name)) | |
| { | |
| char* filepath = etchlog_make_logfilepathw (dirpath, direntry->d_name); | |
| if (0 == etch_deletefile (filepath)) /* delete this log file */ | |
| purgedcount++; | |
| etch_free (filepath); | |
| } | |
| } | |
| closedir(dirstream); /* free DIR memory */ | |
| return purgedcount; | |
| } | |
| #if IS_WINDOWS_ETCH | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
| * etch windows emulation of POSIX directory browsing functions | |
| * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
| */ | |
| /* turn off potentially unsafe function warnings for strcpy strcat wcscpy etc */ | |
| #pragma warning (disable:4996) | |
| /* | |
| * opendir() | |
| * etch win32 limited emulation of POSIX opendir(). | |
| * opens the specified "directory stream". the stream is positioned at the first entry. | |
| * @param dirname the narrow character directory path. assumed not a root or net path. | |
| * @return a DIR*, or NULL if error, in which case system errno indicates the specific | |
| * error. caller owns memory referenced by returned address, and is expected to | |
| * eventually relinquish this memory p by calling closedir(p). | |
| * @remarks closedir() must eventually be called on the returned DIR*, or the DIR will | |
| * leak. it is assumed herein that _UNICODE is #defined ahead of windows.h, such that | |
| * windows' FindFirstFile and FindNextFile operate on and return wide character strings. | |
| */ | |
| DIR* opendir (char* dirname) | |
| { | |
| DIR* dir = NULL; | |
| int myerrno = 0; | |
| wchar_t* wdirname = NULL; | |
| const char* SLASHASTER = "/*"; | |
| char *dircopy = NULL, *p = NULL; | |
| size_t namelen = dirname? strlen(dirname): 0; | |
| size_t dircopylen = 0, widestrbytelen = 0, dirstructlen = 0; | |
| struct stat st; memset(&st, 0, sizeof(struct stat)); | |
| do { | |
| if (0 == namelen) | |
| { myerrno = EINVAL; | |
| break; | |
| } | |
| /* clone caller's directory string */ | |
| dircopylen = namelen + strlen(SLASHASTER) + sizeof('\0'); | |
| dircopy = etch_malloc(dircopylen, 0); | |
| memset(dircopy, 0, dircopylen); | |
| memcpy(dircopy, dirname, namelen); | |
| /* lose trailing slash if present */ | |
| p = dircopy + namelen - 1; | |
| if (*p == '/' || *p == '\\') | |
| { *p = '/0'; | |
| namelen--; | |
| } | |
| /* verify specified directory exists */ | |
| if (0 != stat (dircopy, &st) || 0 == (st.st_mode & S_IFDIR)) | |
| { myerrno = ENOTDIR; | |
| break; | |
| } | |
| /* add slash-asterisk expected by FindFirstFile() */ | |
| strcat(dircopy, SLASHASTER); | |
| /* get an etch_malloc'ed unicode version of new path string | |
| * since we're assuming _UNICODE was defined to windows.h */ | |
| if (0 != etch_ansi_to_unicode (&wdirname, dircopy)) | |
| { myerrno = EIO; | |
| break; | |
| } | |
| /* allocate DIR big enough to contain unicode directory string */ | |
| widestrbytelen = etch_get_unicode_bytecount (wdirname); | |
| dirstructlen = sizeof(DIR) + widestrbytelen; | |
| dir = etch_malloc (dirstructlen, 0); | |
| memset(dir, 0, dirstructlen); | |
| dir->dirhandle = INVALID_HANDLE_VALUE; /* first file indicator */ | |
| wcscpy(dir->dirname, wdirname); | |
| } while(0); | |
| /* free work memory, fyi OK if null */ | |
| etch_free(dircopy); | |
| etch_free(wdirname); | |
| errno = myerrno; | |
| return dir; /* caller owns dir and must closedir() to free it */ | |
| } | |
| /* | |
| * readdir() | |
| * etch win32 emulation of POSIX readdir(). | |
| * gets the next file from the specified directory stream. | |
| * this implementation is not reentrant. caller does not own the memory | |
| * referenced by the returned address. | |
| * @param dir the "directory stream" created by opendir(). caller retains. | |
| * @return a pointer to a structure representing the directory entry at the current | |
| * position in the specified directory stream, and position the directory stream | |
| * at the next entry; or NULL if no more entries in the directory stream. | |
| * caller does not own this memory, function is not reentrant. | |
| * @remarks regarding type of entity referenced by returned dirent->d_name, | |
| * at this writing we will only return files, not directories, since that is our | |
| * current need. not completely sure how this works in linux, presumably d_name | |
| * can be a file or a directory, and caller parses the name? not sure atm, but | |
| * since we're writing this to scan the log directory and we have no need to | |
| * recurse subdirectories, we can omit code to handle subdirectories for now. | |
| */ | |
| dirent* readdir (DIR* dir) | |
| { | |
| static WIN32_FIND_DATA fd; | |
| int is_gotfile = FALSE, myerrno = dir? 0: EBADF; | |
| while(!myerrno && !is_gotfile) | |
| { | |
| if (dir->dirhandle == INVALID_HANDLE_VALUE) /* requesting first file in stream? */ | |
| if (INVALID_HANDLE_VALUE != (dir->dirhandle = FindFirstFile(dir->dirname, &fd))) | |
| is_gotfile = TRUE; | |
| else; | |
| else | |
| if (FindNextFile (dir->dirhandle, &fd)) | |
| is_gotfile = TRUE; | |
| if (is_gotfile) /* no doubt there are more attributes we should test for here */ | |
| if (fd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN)) | |
| is_gotfile = FALSE; | |
| else; | |
| else switch(GetLastError()) | |
| { case ERROR_NO_MORE_FILES: case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: | |
| myerrno = ENOENT; | |
| break; | |
| default: myerrno = EINVAL; | |
| } | |
| } | |
| errno = myerrno; | |
| return myerrno? NULL: (dirent*) &fd.cFileName; | |
| } | |
| /* | |
| * closedir() | |
| * etch win32 emulation of POSIX function closedir(). | |
| * @param dir the "directory stream" allocated by opendir(). this memory is freed here. | |
| * @return 0. | |
| * &remarks caller relinquishes memory referenced by dir parameter. | |
| */ | |
| int closedir (DIR* dir) | |
| { | |
| if (dir && dir->dirhandle != INVALID_HANDLE_VALUE) | |
| FindClose (dir->dirhandle); | |
| etch_free (dir); | |
| return 0; | |
| } | |
| /* | |
| * rewinddir() | |
| * etch win32 emulation of POSIX function rewinddir(). | |
| * @param dir the "directory stream" allocated by opendir(). caller retains. | |
| * this "directory stream" is reset to the beginning, i.e. prior to first file. | |
| */ | |
| void rewinddir (DIR *dir) | |
| { | |
| if (NULL == dir) return; | |
| if (dir->dirhandle && dir->dirhandle != INVALID_HANDLE_VALUE) | |
| FindClose(dir->dirhandle); | |
| dir->dirhandle = INVALID_HANDLE_VALUE; | |
| } | |
| #endif /* IS_WINDOWS_ETCH */ |