/** * Locate a data file and open it. * * This function is used to open a dictionary file or a word file, * or any associated data file (like a post process knowledge file). * * It works as follows. If the file name begins with a "/", then * it's assumed to be an absolute file name and it tries to open * that exact file. * * Otherwise, it looks for the file in a sequence of directories, as * specified in the dictpath array, until it finds it. * * If it is still not found, it may be that the user specified a relative * path, so it tries to open the exact file. * * Associated data files are looked in the *same* directory in which the * first one was found (typically "en/4.0.dict"). The private static * "path_found" serves as a directory path cache which records where the * first file was found. The goal here is to avoid insanity due to * user's fractured installs. * If the filename argument is NULL, the function just invalidates this * directory path cache. */ void * object_open(const char *filename, void * (*opencb)(const char *, const void *), const void * user_data) { static char *path_found; /* directory path cache */ char *completename = NULL; void *fp = NULL; char *data_dir = NULL; const char **path = NULL; if (NULL == filename) { /* Invalidate the directory path cache */ free(path_found); path_found = NULL; return NULL; } if (NULL == path_found) { data_dir = dictionary_get_data_dir(); #ifdef _DEBUG prt_error("Info: data_dir=%s", (data_dir?data_dir:"NULL")); #endif } /* Look for absolute filename. * Unix: starts with leading slash. * Windows: starts with C:\ except that the drive letter may differ. * Note that only native windows C library uses backslashes; mingw * seems to use forward-slash, from what I can tell. */ if ((filename[0] == '/') #if defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) || ((filename[1] == ':') && ((filename[2] == '\\') || (filename[2] == '/'))) #endif ) { /* opencb() returns NULL if the file does not exist. */ fp = opencb(filename, user_data); #ifdef _DEBUG prt_error("Info: 1: object_open() trying %s=%d", filename, NULL!=fp); #endif } else { /* A path list in which to search for dictionaries. * path_found, data_dir or DEFAULTPATH may be NULL. */ const char *dictpath[] = { path_found, ".", "." DIR_SEPARATOR "data", "..", ".." DIR_SEPARATOR "data", data_dir, DEFAULTPATH, }; size_t i = sizeof(dictpath)/sizeof(dictpath[0]); for (path = dictpath; i-- > 0; path++) { if (NULL == *path) continue; free(completename); completename = join_path(*path, filename); fp = opencb(completename, user_data); #ifdef _DEBUG prt_error("Info: 2: object_open() trying %s=%d", completename, NULL!=fp); #endif if ((NULL != fp) || (NULL != path_found)) break; } } if (NULL == fp) { fp = opencb(filename, user_data); #ifdef _DEBUG prt_error("Info: 3: object_open() trying %s=%d", filename, NULL!=fp); #endif } else if (NULL == path_found) { size_t i; path_found = strdup((NULL != completename) ? completename : filename); if (0 < verbosity) prt_error("Info: Dictionary found at %s", path_found); for (i = 0; i < 2; i++) { char *root = strrchr(path_found, DIR_SEPARATOR[0]); if (NULL != root) *root = '\0'; } #ifdef _DEBUG prt_error("Info: object_open() path_found=%s", path_found); #endif } free(data_dir); free(completename); return fp; }
void * object_open(const char *filename, void * (*opencb)(const char *, const void *), const void * user_data) { /* Dictionary data directory path cache -- per-thread storage. */ static TLS char *path_found; char *completename = NULL; void *fp = NULL; char *data_dir = NULL; const char **path = NULL; if (NULL == filename) { /* Invalidate the dictionary data directory path cache. */ char *pf = path_found; path_found = NULL; free(pf); return NULL; } if (NULL == path_found) { data_dir = dictionary_get_data_dir(); if (verbosity_level(D_USER_FILES)) { char cwd[MAX_PATH_NAME]; char *cwdp = getcwd(cwd, sizeof(cwd)); prt_error("Debug: Current directory: %s\n", NULL == cwdp ? "NULL": cwdp); prt_error("Debug: Last-resort data directory: %s\n", data_dir ? data_dir : "NULL"); } } /* Look for absolute filename. * Unix: starts with leading slash. * Windows: starts with C:\ except that the drive letter may differ. */ if ((filename[0] == '/') #ifdef _WIN32 || ((filename[1] == ':') && ((filename[2] == '\\') || (filename[2] == '/'))) || (filename[0] == '\\') /* UNC path */ #endif /* _WIN32 */ ) { /* opencb() returns NULL if the file does not exist. */ fp = opencb(filename, user_data); lgdebug(D_USER_FILES, "Debug: Opening file %s%s\n", filename, NOTFOUND(fp)); } else { /* A path list in which to search for dictionaries. * path_found, data_dir or DEFAULTPATH may be NULL. */ const char *dictpath[] = { path_found, ".", "." DIR_SEPARATOR "data", "..", ".." DIR_SEPARATOR "data", data_dir, DEFAULTPATH, }; size_t i = sizeof(dictpath)/sizeof(dictpath[0]); for (path = dictpath; i-- > 0; path++) { if (NULL == *path) continue; free(completename); completename = join_path(*path, filename); fp = opencb(completename, user_data); lgdebug(D_USER_FILES, "Debug: Opening file %s%s\n", completename, NOTFOUND(fp)); if ((NULL != fp) || (NULL != path_found)) break; } } if (NULL == fp) { fp = opencb(filename, user_data); lgdebug(D_USER_FILES, "Debug: Opening file %s%s\n", filename, NOTFOUND(fp)); } else if (NULL == path_found) { char *pfnd = strdup((NULL != completename) ? completename : filename); if ((0 < verbosity) && (dict_file_open == opencb)) prt_error("Info: Dictionary found at %s\n", pfnd); for (size_t i = 0; i < 2; i++) { char *root = strrchr(pfnd, DIR_SEPARATOR[0]); if (NULL != root) *root = '\0'; } path_found = pfnd; } free(data_dir); free(completename); return fp; }