#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/OSNOUI.C,v 1.3 1999/07/11 00:46:30 MJRoberts Exp $"; #endif /* * Copyright (c) 1997, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name osnoui.c - general-purpose implementations of OS routines with no UI Function This file provides implementations for certain OS routines that have no UI component and can be implemented in general for a range of operating systems. Notes Modified 04/11/99 CNebel - Improve const-ness; fix C++ errors. 11/02/97 MJRoberts - Creation */ #include #ifdef MSDOS #include #endif #include #include #include #include #include #include #include "os.h" #include "run.h" /* * Ports with MS-DOS-like file systems (Atari ST, OS/2, Macintosh, and, * of course, MS-DOS itself) can use the os_defext and os_remext * routines below by defining USE_DOSEXT. Unix and VMS filenames will * also be parsed correctly by these implementations, but untranslated * VMS logical names may not be. */ #ifdef USE_DOSEXT /* * os_defext(fn, ext) should append the default extension ext to the * filename in fn. It is assumed that the buffer at fn is big enough to * hold the added characters in the extension. The result should be * null-terminated. When an extension is already present in the * filename at fn, no action should be taken. On systems without an * analogue of extensions, this routine should do nothing. */ void os_defext(char *fn, const char *ext) { char *p; /* * Scan backwards from the end of the string, looking for the last * dot ('.') in the filename. Stop if we encounter a path separator * character of some kind, because that means we reached the start * of the filename without encountering a period. */ p = fn + strlen(fn); while (p > fn) { /* on to the previous character */ p--; /* * if it's a period, return without doing anything - this * filename already has an extension, so don't apply a default */ if (*p == '.') return; /* * if this is a path separator character, we're no longer in the * filename, so stop looking for a period */ if (*p == OSPATHCHAR || strchr(OSPATHALT, *p) != 0) break; } /* we didn't find an extension - add the dot and the extension */ strcat(fn, "."); strcat(fn, ext); } /* * Add an extension, even if the filename currently has one */ void os_addext(char *fn, const char *ext) { strcat(fn, "."); strcat(fn, ext); } /* * os_remext(fn) removes the extension from fn, if present. The buffer * at fn should be modified in place. If no extension is present, no * action should be taken. For systems without an analogue of * extensions, this routine should do nothing. */ void os_remext(char *fn) { char *p; /* scan backwards from the end of the string, looking for a dot */ p = fn + strlen(fn); while ( p>fn ) { /* move to the previous character */ p--; /* if it's a period, we've found the extension */ if ( *p=='.' ) { /* terminate the string here to remove the extension */ *p = '\0'; /* we're done */ return; } /* * if this is a path separator, there's no extension, so we can * stop looking */ if (*p == OSPATHCHAR || strchr(OSPATHALT, *p) != 0) return; } } /* * Get a pointer to the root name portion of a filename. Note that this * implementation is included in the ifdef USE_DOSEXT section, since it * seems safe to assume that if a platform has filenames that are * sufficiently DOS-like for the extension parsing routines, the same * will be true of path parsing. */ char *os_get_root_name(char *buf) { char *rootname; /* scan the name for path separators */ for (rootname = buf ; *buf != '\0' ; ++buf) { /* if this is a path separator, remember it */ if (*buf == OSPATHCHAR || strchr(OSPATHALT, *buf) != 0) { /* * It's a path separators - for now, assume the root will * start at the next character after this separator. If we * find another separator later, we'll forget about this one * and use the later one instead. */ rootname = buf + 1; } } /* return the last root name candidate */ return rootname; } /* * Extract the path from a filename */ void os_get_path_name(char *pathbuf, size_t pathbuflen, const char *fname) { const char *lastsep; const char *p; size_t len; int root_path; /* find the last separator in the filename */ for (p = fname, lastsep = fname ; *p != '\0' ; ++p) { /* * if it's a path separator character, remember it as the last one * we've found so far */ if (*p == OSPATHCHAR || strchr(OSPATHALT, *p) != 0) lastsep = p; } /* get the length of the prefix, not including the separator */ len = lastsep - fname; /* * Normally, we don't include the last path separator in the path; for * example, on Unix, the path of "/a/b/c" is "/a/b", not "/a/b/". * However, on Unix/DOS-like file systems, a root path *does* require * the last path separator: the path of "/a" is "/", not an empty * string. So, we need to check to see if the file is in a root path, * and if so, include the final path separator character in the path. */ for (p = fname, root_path = FALSE ; p != lastsep ; ++p) { /* * if this is NOT a path separator character, we don't have all * path separator characters before the filename, so we don't have * a root path */ if (*p != OSPATHCHAR && strchr(OSPATHALT, *p) == 0) { /* note that we don't have a root path */ root_path = FALSE; /* no need to look any further */ break; } } /* if we have a root path, keep the final path separator in the path */ if (root_path) ++len; #ifdef MSDOS /* * On DOS, we have a special case: if the path is of the form "x:\", * where "x" is any letter, then we have a root filename and we want to * include the backslash. */ if (lastsep == fname + 2 && isalpha(fname[0]) && fname[1] == ':' && fname[2] == '\\') { /* we have an absolute path - use the full "x:\" sequence */ len = 3; } #endif /* make sure it fits in our buffer (with a null terminator) */ if (len > pathbuflen - 1) len = pathbuflen - 1; /* copy it and null-terminate it */ memcpy(pathbuf, fname, len); pathbuf[len] = '\0'; } /* * Build a full path name given a path and a filename */ void os_build_full_path(char *fullpathbuf, size_t fullpathbuflen, const char *path, const char *filename) { size_t plen, flen; int add_sep; /* * Note whether we need to add a separator. If the path prefix ends * in a separator, don't add another; otherwise, add the standard * system separator character. * * Do not add a separator if the path is completely empty, since * this simply means that we want to use the current directory. */ plen = strlen(path); add_sep = (plen != 0 && path[plen-1] != OSPATHCHAR && strchr(OSPATHALT, path[plen-1]) == 0); /* copy the path to the full path buffer, limiting to the buffer length */ if (plen > fullpathbuflen - 1) plen = fullpathbuflen - 1; memcpy(fullpathbuf, path, plen); /* add the path separator if necessary (and if there's room) */ if (add_sep && plen + 2 < fullpathbuflen) fullpathbuf[plen++] = OSPATHCHAR; /* add the filename after the path, if there's room */ flen = strlen(filename); if (flen > fullpathbuflen - plen - 1) flen = fullpathbuflen - plen - 1; memcpy(fullpathbuf + plen, filename, flen); /* add a null terminator */ fullpathbuf[plen + flen] = '\0'; } /* * Determine if a path is absolute or relative. If the path starts with * a path separator character, we consider it absolute, otherwise we * consider it relative. * * Note that, on DOS, an absolute path can also follow a drive letter. * So, if the path contains a letter followed by a colon, we'll consider * the path to be absolute. */ int os_is_file_absolute(const char *fname) { /* if the name starts with a path separator, it's absolute */ if (fname[0] == OSPATHCHAR || strchr(OSPATHALT, fname[0]) != 0) return TRUE; #ifdef MSDOS /* on DOS, a file is absolute if it starts with a drive letter */ if (isalpha(fname[0]) && fname[1] == ':') return TRUE; #endif /* MSDOS */ /* the path is relative */ return FALSE; } #endif /* USE_DOSEXT */ /* ------------------------------------------------------------------------ */ /* * A port can define USE_TIMERAND if it wishes to randomize from the * system clock. This should be usable by most ports. */ #ifdef USE_TIMERAND # include void os_rand(long *seed) { time_t t; time( &t ); *seed = (long)t; } #endif /* USE_TIMERAND */ #ifdef USE_PATHSEARCH /* search a path specified in the environment for a tads file */ static int pathfind(const char *fname, int flen, const char *pathvar, char *buf, size_t bufsiz) { char *e; if ( !(e = getenv(pathvar)) ) return(0); for ( ;; ) { char *sep; size_t len; if ( (sep = strchr(e, OSPATHSEP)) ) { len = (size_t)(sep-e); if (!len) continue; } else { len = strlen(e); if (!len) break; } memcpy(buf, e, len); if (buf[len-1] != OSPATHCHAR && !strchr(OSPATHALT, buf[len-1])) buf[len++] = OSPATHCHAR; memcpy(buf+len, fname, flen); buf[len+flen] = 0; if (osfacc(buf) == 0) return(1); if (!sep) break; e = sep+1; } return(0); } #endif /* USE_PATHSEARCH */ /* * Look for a tads-related file in the standard locations and, if the * search is successful, store the result file name in the given buffer. * Return 1 if the file was located, 0 if not. * * Search the following areas for the file: current directory, program * directory (as derived from argv[0]), and the TADS path. */ int os_locate(const char *fname, int flen, const char *arg0, char *buf, size_t bufsiz) { /* Check the current directory */ if (osfacc(fname) == 0) { memcpy(buf, fname, flen); buf[flen] = 0; return(1); } /* Check the program directory */ if (arg0 && *arg0) { const char *p; /* find the end of the directory name of argv[0] */ for ( p = arg0 + strlen(arg0); p > arg0 && *(p-1) != OSPATHCHAR && !strchr(OSPATHALT, *(p-1)); --p ) ; /* don't bother if there's no directory on argv[0] */ if (p > arg0) { size_t len = (size_t)(p - arg0); memcpy(buf, arg0, len); memcpy(buf+len, fname, flen); buf[len+flen] = 0; if (osfacc(buf) == 0) return(1); } } #ifdef USE_PATHSEARCH /* Check TADS path */ if ( pathfind(fname, flen, "TADS", buf, bufsiz) ) return(1); #endif /* USE_PATHSEARCH */ return(0); } /* * Create and open a temporary file */ osfildef *os_create_tempfile(const char *swapname, char *buf) { osfildef *fp; /* if a name wasn't provided, generate a name */ if (swapname == 0) { int attempt; size_t len; time_t timer; int found; int tries; /* * try once with the temporary file path; if that fails, simply * try using the current working directory */ for (found = FALSE, tries = 0 ; !found && tries < 2 ; ++tries) { /* * If this is the first try, get the appropriate path for a * temp file. If that failed, try using the current * directory. */ if (tries == 0) { /* get the temporary path */ os_get_tmp_path(buf); len = strlen(buf); } else { /* * the temporary path didn't work, so this time try it in * the current directory */ buf[0] = '\0'; len = 0; } /* get the current time, as a basis for a unique identifier */ time(&timer); /* try until we find a non-existent filename */ for (attempt = 0 ; attempt < 100 ; ++attempt) { /* generate a name based on time and try number */ sprintf(buf + len, "SW%06ld.%03d", (long)timer % 999999, attempt); /* check to see if a file by this name already exists */ if (osfacc(buf)) { /* the file doesn't exist - try creating it */ fp = osfoprwtb(buf, OSFTSWAP); /* if that succeeded, return this file */ if (fp != 0) { /* set the file's type in the OS, if necessary */ os_settype(buf, OSFTSWAP); /* we're done - return the open file handle */ return fp; } } } } /* we failed to find a name we could use - give up */ return 0; } else { /* open the file they gave us */ fp = osfoprwtb(swapname, OSFTSWAP); /* set the file's type in the OS, if necessary */ os_settype(swapname, OSFTSWAP); /* return the file pointer */ return fp; } } /* * Delete a temporary file. Since our generic implementation of * os_create_tempfile() simply uses osfoprwtb() to open the file, a * temporary file's handle is not any different from any other file * handle - in particular, the OS doesn't automatically delete the file * when closed. Hence, we need to delete the file explicitly here. */ int osfdel_temp(const char *fname) { /* delete the file using the normal mechanism */ return osfdel(fname); } /* * print a null-terminated string to osfildef* file */ void os_fprintz(osfildef *fp, const char *str) { fprintf(fp, "%s", str); } /* * print a counted-length string (which might not be null-terminated) to a * file */ void os_fprint(osfildef *fp, const char *str, size_t len) { fprintf(fp, "%.*s", (int)len, str); } #ifdef MSDOS /* * MS-DOS implementation - Get the temporary file path. Tries getting * the values of various environment variables that are typically used * to define the location for temporary files. */ void os_get_tmp_path(char *buf) { static char *vars[] = { "TEMPDIR", "TMPDIR", "TEMP", "TMP", 0 }; char **varp; /* look for an environment variable from our list */ for (varp = vars ; *varp ; ++varp) { char *val; /* get this variable's value */ val = getenv(*varp); if (val) { size_t len; /* use this value */ strcpy(buf, val); /* add a backslash if necessary */ if ((len = strlen(buf)) != 0 && buf[len-1] != '/' && buf[len-1] != '\\') { buf[len] = '\\'; buf[len+1] = '\0'; } /* use this value */ return; } } /* didn't find anything - leave the prefix empty */ buf[0] = '\0'; } #endif /* MSDOS */ #ifdef USE_NULLSTYPE void os_settype(const char *f, os_filetype_t t) { /* nothing needs to be done on this system */ } #endif /* USE_NULLSTYPE */ /* * Convert an OS filename path to a relative URL */ void os_cvt_dir_url(char *result_buf, size_t result_buf_size, const char *src_path, int end_sep) { char *dst; const char *src; size_t rem; int last_was_sep; static const char quoted[] = ":%"; /* * Run through the source buffer, copying characters to the output * buffer. If we encounter a path separator character, replace it * with a forward slash. */ for (last_was_sep = FALSE, dst = result_buf, src = src_path, rem = result_buf_size ; *src != '\0' && rem > 1 ; ++dst, ++src, --rem) { /* presume this will not be a path separator character */ last_was_sep = FALSE; /* * If this is a local path separator character, replace it with the * URL-style path separator character. If it's an illegal URL * character, quote it with "%" notation. Otherwise, copy it * unchanged. */ if (strchr(OSPATHURL, *src) != 0) { /* add the URL-style path separator instead of the local one */ *dst = '/'; /* note that we just added a path separator character */ last_was_sep = TRUE; } else if (strchr(quoted, *src) != 0) { /* if we have room for three characters, add the "%" sequence */ if (rem > 3) { int dig; /* add the '%' */ *dst++ = '%'; /* add the high-order digit */ dig = ((int)(unsigned char)*src) >> 4; *dst++ = (dig < 10 ? dig + '0' : dig + 'A' - 10); /* add the low-order digit */ dig = ((int)(unsigned char)*src) & 0x0F; *dst = (dig < 10 ? dig + '0' : dig + 'A' - 10); /* deduct the extra two characters beyond the expected one */ rem -= 2; } } else { /* add the character unchanged */ *dst = *src; } } /* * add an additional ending separator if desired and if the last * character wasn't a separator */ if (end_sep && rem > 1 && !last_was_sep) *dst++ = '/'; /* add a null terminator and we're done */ *dst = '\0'; } /* * Convert a relative URL to a relative file system path name */ void os_cvt_url_dir(char *result_buf, size_t result_buf_size, const char *src_url, int end_sep) { char *dst; const char *src; size_t rem; int last_was_sep; /* * Run through the source buffer, copying characters to the output * buffer. If we encounter a '/', convert it to a path separator * character. */ for (last_was_sep = FALSE, dst = result_buf, src = src_url, rem = result_buf_size ; *src != '\0' && rem > 1 ; ++dst, ++src, --rem) { /* * replace slashes with path separators; expand '%' sequences; copy * all other characters unchanged */ if (*src == '/') { *dst = OSPATHCHAR; last_was_sep = TRUE; } else if (*src == '%' && isxdigit((uchar)*(src+1)) && isxdigit((uchar)*(src+2))) { unsigned char c; unsigned char acc; /* convert the '%xx' sequence to its character code */ c = *++src; acc = (c - (c >= 'A' && c <= 'F' ? 'A' - 10 : c >= 'a' && c <= 'f' ? 'a' - 10 : '0')) << 4; c = *++src; acc += (c - (c >= 'A' && c <= 'F' ? 'A' - 10 : c >= 'a' && c <= 'f' ? 'a' - 10 : '0')); /* set the character */ *dst = acc; /* it's not a separator */ last_was_sep = FALSE; } else { *dst = *src; last_was_sep = FALSE; } } /* * add an additional ending separator if desired and if the last * character wasn't a separator */ if (end_sep && rem > 1 && !last_was_sep) *dst++ = OSPATHCHAR; /* add a null terminator and we're done */ *dst = '\0'; } /* ------------------------------------------------------------------------ */ /* * service routine for searching - build the full output path name */ static void oss_build_outpathbuf(char *outpathbuf, size_t outpathbufsiz, const char *path, const char *fname) { /* if there's a full path buffer, build the full path */ if (outpathbuf != 0) { size_t lp; size_t lf; /* copy the path prefix */ lp = strlen(path); if (lp > outpathbufsiz - 1) lp = outpathbufsiz - 1; memcpy(outpathbuf, path, lp); /* add the filename if there's any room */ lf = strlen(fname); if (lf > outpathbufsiz - lp - 1) lf = outpathbufsiz - lp - 1; memcpy(outpathbuf + lp, fname, lf); /* null-terminate the result */ outpathbuf[lp + lf] = '\0'; } } /* ------------------------------------------------------------------------ */ /* * Borland C implementation of directory functions */ #if defined(TURBO) || defined(DJGPP) #include #include /* * search context structure */ struct oss_find_ctx_t { /* C library find-file block */ struct ffblk ff; /* original search path prefix (we'll allocate more to fit the string) */ char path[1]; }; /* * find first matching file */ void *os_find_first_file(const char *dir, const char *pattern, char *outbuf, size_t outbufsiz, int *isdir, char *outpathbuf, size_t outpathbufsiz) { struct oss_find_ctx_t *ctx; char realpat[OSFNMAX]; size_t l; size_t path_len; const char *lastsep; const char *p; /* * construct the filename pattern from the directory and pattern * segments, using "*" as the default wildcard pattern if no * explicit pattern was provided */ strcpy(realpat, dir); if ((l = strlen(realpat)) != 0 && realpat[l - 1] != '\\') realpat[l++] = '\\'; if (pattern == 0) strcpy(realpat + l, "*.*"); else strcpy(realpat + l, pattern); /* find the last separator in the original path */ for (p = realpat, lastsep = 0 ; *p != '\0' ; ++p) { /* if this is a separator, remember it */ if (*p == '\\' || *p == '/' || *p == ':') lastsep = p; } /* * if we found a separator, the path prefix is everything up to and * including the separator; otherwise, there's no path prefix */ if (lastsep != 0) { /* the length of the path includes the separator */ path_len = lastsep + 1 - realpat; } else { /* there's no path prefix - everything is in the current directory */ path_len = 0; } /* allocate a context */ ctx = (struct oss_find_ctx_t *)malloc(sizeof(struct oss_find_ctx_t) + path_len); /* copy the path to the context */ memcpy(ctx->path, realpat, path_len); ctx->path[path_len] = '\0'; /* call DOS to search for a matching file */ if (findfirst(realpat, &ctx->ff, FA_DIREC)) { /* no dice - delete the context and return failure */ free(ctx); return 0; } /* copy the filename into the caller's buffer */ l = strlen(ctx->ff.ff_name); if (l > outbufsiz - 1) l = outbufsiz - 1; memcpy(outbuf, ctx->ff.ff_name, l); outbuf[l] = '\0'; /* build the full path, if desired */ oss_build_outpathbuf(outpathbuf, outpathbufsiz, ctx->path, ctx->ff.ff_name); /* return the directory indication */ *isdir = (ctx->ff.ff_attrib & FA_DIREC) != 0; /* * return the context - it will be freed later when find-next * reaches the last file or find-close is called to cancel the * search */ return ctx; } /* find the next file */ void *os_find_next_file(void *ctx0, char *outbuf, size_t outbufsiz, int *isdir, char *outpathbuf, size_t outpathbufsiz) { struct oss_find_ctx_t *ctx = (struct oss_find_ctx_t *)ctx0; size_t l; /* if the search has already ended, do nothing */ if (ctx == 0) return 0; /* try searching for the next file */ if (findnext(&ctx->ff)) { /* no more files - delete the context and give up */ free(ctx); /* return null to indicate that the search is finished */ return 0; } /* return the name */ l = strlen(ctx->ff.ff_name); if (l > outbufsiz - 1) l = outbufsiz - 1; memcpy(outbuf, ctx->ff.ff_name, l); outbuf[l] = '\0'; /* return the directory indication */ *isdir = (ctx->ff.ff_attrib & FA_DIREC) != 0; /* build the full path, if desired */ oss_build_outpathbuf(outpathbuf, outpathbufsiz, ctx->path, ctx->ff.ff_name); /* * indicate that the search was successful by returning the non-null * context pointer -- the context has been updated for the new * position in the search, so we just return the same context * structure that we've been using all along */ return ctx; } /* cancel a search */ void os_find_close(void *ctx0) { struct oss_find_ctx_t *ctx = (struct oss_find_ctx_t *)ctx0; /* if the search was already cancelled, do nothing */ if (ctx == 0) return; /* delete the search context */ free(ctx); } /* * Time-zone initialization */ void os_tzset() { /* let the run-time library initialize the time zone */ tzset(); } /* ------------------------------------------------------------------------ */ /* * Get file times */ /* * get file creation time */ int os_get_file_cre_time(os_file_time_t *t, const char *fname) { struct stat info; /* get the file information */ if (stat(fname, &info)) return 1; /* set the creation time in the return structure */ t->t = info.st_ctime; return 0; } /* * get file modification time */ int os_get_file_mod_time(os_file_time_t *t, const char *fname) { struct stat info; /* get the file information */ if (stat(fname, &info)) return 1; /* set the modification time in the return structure */ t->t = info.st_mtime; return 0; } /* * get file last access time */ int os_get_file_acc_time(os_file_time_t *t, const char *fname) { struct stat info; /* get the file information */ if (stat(fname, &info)) return 1; /* set the access time in the return structure */ t->t = info.st_atime; return 0; } /* * compare two file time structures */ int os_cmp_file_times(const os_file_time_t *a, const os_file_time_t *b) { if (a->t < b->t) return -1; else if (a->t == b->t) return 0; else return 1; } #endif /* TURBO || DJGPP */ /* ------------------------------------------------------------------------ */ /* * Microsoft C implementation of directory functions */ #ifdef MICROSOFT typedef struct { /* search handle */ long handle; /* found data structure */ struct _finddata_t data; /* full original search path */ char path[1]; } ossfcx; /* * find first matching file */ void *os_find_first_file(const char *dir, const char *pattern, char *outbuf, size_t outbufsiz, int *isdir, char *outpathbuf, size_t outpathbufsiz) { ossfcx *ctx; char realpat[OSFNMAX]; size_t l; size_t path_len; const char *lastsep; const char *p; /* * construct the filename pattern from the directory and pattern * segments, using "*" as the default wildcard pattern if no * explicit pattern was provided */ strcpy(realpat, dir); if ((l = strlen(realpat)) != 0 && realpat[l - 1] != '\\') realpat[l++] = '\\'; if (pattern == 0) strcpy(realpat + l, "*"); else strcpy(realpat + l, pattern); /* find the last separator in the original path */ for (p = realpat, lastsep = 0 ; *p != '\0' ; ++p) { /* if this is a separator, remember it */ if (*p == '\\' || *p == '/' || *p == ':') lastsep = p; } /* * if we found a separator, the path prefix is everything up to and * including the separator; otherwise, there's no path prefix */ if (lastsep != 0) { /* the length of the path includes the separator */ path_len = lastsep + 1 - realpat; } else { /* there's no path prefix - everything is in the current directory */ path_len = 0; } /* allocate a context */ ctx = (ossfcx *)malloc(sizeof(ossfcx) + path_len); /* copy the path to the context */ memcpy(ctx->path, realpat, path_len); ctx->path[path_len] = '\0'; /* call DOS to search for a matching file */ ctx->handle = _findfirst(realpat, &ctx->data); if (ctx->handle == -1L) { /* no dice - delete the context and return failure */ free(ctx); return 0; } /* return the name */ l = strlen(ctx->data.name); if (l > outbufsiz - 1) l = outbufsiz - 1; memcpy(outbuf, ctx->data.name, l); outbuf[l] = '\0'; /* return the directory indication */ *isdir = (ctx->data.attrib & _A_SUBDIR) != 0; /* build the full path, if desired */ oss_build_outpathbuf(outpathbuf, outpathbufsiz, ctx->path, ctx->data.name); /* * return the context - it will be freed later when find-next * reaches the last file or find-close is called to cancel the * search */ return ctx; } /* find the next file */ void *os_find_next_file(void *ctx0, char *outbuf, size_t outbufsiz, int *isdir, char *outpathbuf, size_t outpathbufsiz) { ossfcx *ctx = (ossfcx *)ctx0; size_t l; /* if the search has already ended, do nothing */ if (ctx == 0) return 0; /* try searching for the next file */ if (_findnext(ctx->handle, &ctx->data) != 0) { /* no more files - close the search and delete the context */ _findclose(ctx->handle); free(ctx); /* return null to indicate that the search is finished */ return 0; } /* return the name */ l = strlen(ctx->data.name); if (l > outbufsiz - 1) l = outbufsiz - 1; memcpy(outbuf, ctx->data.name, l); outbuf[l] = '\0'; /* return the directory indication */ *isdir = (ctx->data.attrib & _A_SUBDIR) != 0; /* build the full path, if desired */ oss_build_outpathbuf(outpathbuf, outpathbufsiz, ctx->path, ctx->data.name); /* * indicate that the search was successful by returning the non-null * context pointer -- the context has been updated for the new * position in the search, so we just return the same context * structure that we've been using all along */ return ctx; } /* cancel a search */ void os_find_close(void *ctx0) { ossfcx *ctx = (ossfcx *)ctx0; /* if the search was already cancelled, do nothing */ if (ctx == 0) return; /* close the search and delete the context structure */ _findclose(ctx->handle); free(ctx); } /* * Time-zone initialization */ void os_tzset() { /* let the run-time library initialize the time zone */ tzset(); } /* ------------------------------------------------------------------------ */ /* * Get file times */ /* * get file creation time */ int os_get_file_cre_time(os_file_time_t *t, const char *fname) { struct stat info; /* get the file information */ if (stat(fname, &info)) return 1; /* set the creation time in the return structure */ t->t = info.st_ctime; return 0; } /* * get file modification time */ int os_get_file_mod_time(os_file_time_t *t, const char *fname) { struct stat info; /* get the file information */ if (stat(fname, &info)) return 1; /* set the modification time in the return structure */ t->t = info.st_mtime; return 0; } /* * get file last access time */ int os_get_file_acc_time(os_file_time_t *t, const char *fname) { struct stat info; /* get the file information */ if (stat(fname, &info)) return 1; /* set the access time in the return structure */ t->t = info.st_atime; return 0; } /* * compare two file time structures */ int os_cmp_file_times(const os_file_time_t *a, const os_file_time_t *b) { if (a->t < b->t) return -1; else if (a->t == b->t) return 0; else return 1; } #endif /* MICROSOFT */ /* ------------------------------------------------------------------------ */ /* * OS/2 implementation of directory functions */ #ifdef MSOS2 /* C library context structure used for file searches */ #ifdef __32BIT__ # define DosFindFirst(a,b,c,d,e,f) DosFindFirst(a,b,c,d,e,f,FIL_STANDARD) typedef FILEFINDBUF3 oss_c_ffcx; typedef ULONG count_t; #else /* !__32BIT__ */ # define DosFindFirst(a,b,c,d,e,f) DosFindFirst(a,b,c,d,e,f,0L) typedef FILEFINDBUF oss_c_ffcx; typedef USHORT count_t; #endif /* __32BIT__ */ typedef struct { /* C library context structure */ oss_c_ffcx ff; /* original search path */ char path[1]; } ossfcx; void *os_find_first_file(const char *dir, const char *pattern, char *outbuf, size_t outbufsiz, int *isdir, char *outpathbuf, size_t outpathbufsiz) { ossfcx *ctx; char realpat[OSFNMAX]; size_t l; HDIR hdir = HDIR_SYSTEM; count_t scnt = 1; size_t path_len; const char *lastsep; const char *p; /* * construct the filename pattern from the directory and pattern * segments, using "*" as the default wildcard pattern if no * explicit pattern was provided */ strcpy(realpat, dir); if ((l = strlen(realpat)) != 0 && realpat[l - 1] != '\\') realpat[l++] = '\\'; if (pattern == 0) strcpy(realpat + l, "*"); else strcpy(realpat + l, pattern); /* find the last separator in the original path */ for (p = realpat, lastsep = 0 ; *p != '\0' ; ++p) { /* if this is a separator, remember it */ if (*p == '\\' || *p == '/' || *p == ':') lastsep = p; } /* * if we found a separator, the path prefix is everything up to and * including the separator; otherwise, there's no path prefix */ if (lastsep != 0) { /* the length of the path includes the separator */ path_len = lastsep + 1 - realpat; } else { /* there's no path prefix - everything is in the current directory */ path_len = 0; } /* allocate a context */ ctx = (ossfcx *)malloc(sizeof(ossfcx) + path_len); /* call DOS to search for a matching file */ if (DosFindFirst(realpat, &hdir, FILE_DIRECTORY | FILE_NORMAL, &ctx->ff, sizeof(ctx->ff), &scnt)) { /* no dice - delete the context and return failure */ free(ctx); return 0; } /* copy the path to the context */ memcpy(ctx->path, realpat, path_len); ctx->path[path_len] = '\0'; /* return the name */ l = ctx->ff.cchName; if (l > outbufsiz - 1) l = outbufsiz - 1; memcpy(outbuf, ctx->ff.achName, l); outbuf[l] = '\0'; /* return the directory indication */ *isdir = (ctx->ff.attrFile & FILE_DIRECTORY) != 0; /* build the full path, if desired */ oss_build_outpathbuf(outpathbuf, outpathbufsiz, ctx->path, ctx->ff.data.name); /* * return the context - it will be freed later when find-next * reaches the last file or find-close is called to cancel the * search */ return ctx; } /* find the next file */ void *os_find_next_file(void *ctx0, char *outbuf, size_t outbufsiz, int *isdir, char *outpathbuf, size_t outpathbufsiz) { ossfcx *ctx = (ossfcx *)ctx0; size_t l; /* if the search has already ended, do nothing */ if (ctx == 0) return 0; /* try searching for the next file */ if (DosFindNext(HDIR_SYSTEM, &ctx->ff, sizeof(ctx->ff), &scnt)) { /* no more files - delete the context and give up */ free(ctx); /* return null to indicate that the search is finished */ return 0; } /* return the name */ l = ctx->ff.cchName; if (l > outbufsiz - 1) l = outbufsiz - 1; memcpy(outbuf, ctx->ff.achName, l); outbuf[l] = '\0'; /* return the directory indication */ *isdir = (ctx->ff.attrFile & FILE_DIRECTORY) != 0; /* build the full path, if desired */ oss_build_outpathbuf(outpathbuf, outpathbufsiz, ctx->ff.path, ctx->ff.data.name); /* * indicate that the search was successful by returning the non-null * context pointer -- the context has been updated for the new * position in the search, so we just return the same context * structure that we've been using all along */ return ctx; } /* cancel a search */ void os_find_close(void *ctx0) { ossfcx *ctx = (ossfcx *)ctx0; /* if the search was already cancelled, do nothing */ if (ctx == 0) return; /* delete the context structure */ free(ctx); } #endif /* MSOS2 */ #ifdef MSDOS /* * Check for a special filename */ enum os_specfile_t os_is_special_file(const char *fname) { /* check for '.' */ if (strcmp(fname, ".") == 0) return OS_SPECFILE_SELF; /* check for '..' */ if (strcmp(fname, "..") == 0) return OS_SPECFILE_PARENT; /* not a special file */ return OS_SPECFILE_NONE; } #endif /* MSDOS */