3
"$Header: d:/cvsroot/tads/TADS2/OSNOUI.C,v 1.3 1999/07/11 00:46:30 MJRoberts Exp $";
7
* Copyright (c) 1997, 2002 Michael J. Roberts. All Rights Reserved.
9
* Please see the accompanying license file, LICENSE.TXT, for information
10
* on using and copying this software.
14
osnoui.c - general-purpose implementations of OS routines with no UI
16
This file provides implementations for certain OS routines that have
17
no UI component and can be implemented in general for a range of
22
04/11/99 CNebel - Improve const-ness; fix C++ errors.
23
11/02/97 MJRoberts - Creation
41
* Ports with MS-DOS-like file systems (Atari ST, OS/2, Macintosh, and,
42
* of course, MS-DOS itself) can use the os_defext and os_remext
43
* routines below by defining USE_DOSEXT. Unix and VMS filenames will
44
* also be parsed correctly by these implementations, but untranslated
45
* VMS logical names may not be.
50
* os_defext(fn, ext) should append the default extension ext to the
51
* filename in fn. It is assumed that the buffer at fn is big enough to
52
* hold the added characters in the extension. The result should be
53
* null-terminated. When an extension is already present in the
54
* filename at fn, no action should be taken. On systems without an
55
* analogue of extensions, this routine should do nothing.
57
void os_defext(char *fn, const char *ext)
62
* Scan backwards from the end of the string, looking for the last
63
* dot ('.') in the filename. Stop if we encounter a path separator
64
* character of some kind, because that means we reached the start
65
* of the filename without encountering a period.
70
/* on to the previous character */
74
* if it's a period, return without doing anything - this
75
* filename already has an extension, so don't apply a default
81
* if this is a path separator character, we're no longer in the
82
* filename, so stop looking for a period
84
if (*p == OSPATHCHAR || strchr(OSPATHALT, *p) != 0)
88
/* we didn't find an extension - add the dot and the extension */
94
* Add an extension, even if the filename currently has one
96
void os_addext(char *fn, const char *ext)
103
* os_remext(fn) removes the extension from fn, if present. The buffer
104
* at fn should be modified in place. If no extension is present, no
105
* action should be taken. For systems without an analogue of
106
* extensions, this routine should do nothing.
108
void os_remext(char *fn)
112
/* scan backwards from the end of the string, looking for a dot */
116
/* move to the previous character */
119
/* if it's a period, we've found the extension */
122
/* terminate the string here to remove the extension */
130
* if this is a path separator, there's no extension, so we can
133
if (*p == OSPATHCHAR || strchr(OSPATHALT, *p) != 0)
139
* Get a pointer to the root name portion of a filename. Note that this
140
* implementation is included in the ifdef USE_DOSEXT section, since it
141
* seems safe to assume that if a platform has filenames that are
142
* sufficiently DOS-like for the extension parsing routines, the same
143
* will be true of path parsing.
145
char *os_get_root_name(char *buf)
149
/* scan the name for path separators */
150
for (rootname = buf ; *buf != '\0' ; ++buf)
152
/* if this is a path separator, remember it */
153
if (*buf == OSPATHCHAR || strchr(OSPATHALT, *buf) != 0)
156
* It's a path separators - for now, assume the root will
157
* start at the next character after this separator. If we
158
* find another separator later, we'll forget about this one
159
* and use the later one instead.
165
/* return the last root name candidate */
170
* Extract the path from a filename
172
void os_get_path_name(char *pathbuf, size_t pathbuflen, const char *fname)
179
/* find the last separator in the filename */
180
for (p = fname, lastsep = fname ; *p != '\0' ; ++p)
183
* if it's a path separator character, remember it as the last one
186
if (*p == OSPATHCHAR || strchr(OSPATHALT, *p) != 0)
190
/* get the length of the prefix, not including the separator */
191
len = lastsep - fname;
194
* Normally, we don't include the last path separator in the path; for
195
* example, on Unix, the path of "/a/b/c" is "/a/b", not "/a/b/".
196
* However, on Unix/DOS-like file systems, a root path *does* require
197
* the last path separator: the path of "/a" is "/", not an empty
198
* string. So, we need to check to see if the file is in a root path,
199
* and if so, include the final path separator character in the path.
201
for (p = fname, root_path = FALSE ; p != lastsep ; ++p)
204
* if this is NOT a path separator character, we don't have all
205
* path separator characters before the filename, so we don't have
208
if (*p != OSPATHCHAR && strchr(OSPATHALT, *p) == 0)
210
/* note that we don't have a root path */
213
/* no need to look any further */
218
/* if we have a root path, keep the final path separator in the path */
224
* On DOS, we have a special case: if the path is of the form "x:\",
225
* where "x" is any letter, then we have a root filename and we want to
226
* include the backslash.
228
if (lastsep == fname + 2
229
&& isalpha(fname[0]) && fname[1] == ':' && fname[2] == '\\')
231
/* we have an absolute path - use the full "x:\" sequence */
236
/* make sure it fits in our buffer (with a null terminator) */
237
if (len > pathbuflen - 1)
238
len = pathbuflen - 1;
240
/* copy it and null-terminate it */
241
memcpy(pathbuf, fname, len);
246
* Canonicalize a path: remove ".." and "." relative elements
248
void canonicalize_path(char *path)
253
/* keep going until we're done */
254
for (start = p = path ; ; ++p)
256
/* if it's a separator, note it and process the path element */
257
if (*p == '\\' || *p == '/' || *p == '\0')
260
* check the path element that's ending here to see if it's a
261
* relative item - either "." or ".."
263
if (p - start == 1 && *start == '.')
266
* we have a '.' element - simply remove it along with the
267
* path separator that follows
269
if (*p == '\\' || *p == '/')
270
memmove(start, p + 1, strlen(p+1) + 1);
271
else if (start > path)
276
else if (p - start == 2 && *start == '.' && *(start+1) == '.')
281
* we have a '..' element - find the previous path element,
282
* if any, and remove it, along with the '..' and the
283
* subsequent separator
286
prv > path && (*(prv-1) != '\\' || *(prv-1) == '/') ;
289
/* if we found a separator, remove the previous element */
292
if (*p == '\\' || *p == '/')
293
memmove(prv, p + 1, strlen(p+1) + 1);
294
else if (start > path)
301
/* note the start of the next element */
305
/* stop at the end of the string */
312
* Build a full path name given a path and a filename
314
void os_build_full_path(char *fullpathbuf, size_t fullpathbuflen,
315
const char *path, const char *filename)
321
* Note whether we need to add a separator. If the path prefix ends
322
* in a separator, don't add another; otherwise, add the standard
323
* system separator character.
325
* Do not add a separator if the path is completely empty, since
326
* this simply means that we want to use the current directory.
330
&& path[plen-1] != OSPATHCHAR
331
&& strchr(OSPATHALT, path[plen-1]) == 0);
333
/* copy the path to the full path buffer, limiting to the buffer length */
334
if (plen > fullpathbuflen - 1)
335
plen = fullpathbuflen - 1;
336
memcpy(fullpathbuf, path, plen);
338
/* add the path separator if necessary (and if there's room) */
339
if (add_sep && plen + 2 < fullpathbuflen)
340
fullpathbuf[plen++] = OSPATHCHAR;
342
/* add the filename after the path, if there's room */
343
flen = strlen(filename);
344
if (flen > fullpathbuflen - plen - 1)
345
flen = fullpathbuflen - plen - 1;
346
memcpy(fullpathbuf + plen, filename, flen);
348
/* add a null terminator */
349
fullpathbuf[plen + flen] = '\0';
351
/* canonicalize the result */
352
canonicalize_path(fullpathbuf);
356
* Determine if a path is absolute or relative. If the path starts with
357
* a path separator character, we consider it absolute, otherwise we
358
* consider it relative.
360
* Note that, on DOS, an absolute path can also follow a drive letter.
361
* So, if the path contains a letter followed by a colon, we'll consider
362
* the path to be absolute.
364
int os_is_file_absolute(const char *fname)
366
/* if the name starts with a path separator, it's absolute */
367
if (fname[0] == OSPATHCHAR || strchr(OSPATHALT, fname[0]) != 0)
371
/* on DOS, a file is absolute if it starts with a drive letter */
372
if (isalpha(fname[0]) && fname[1] == ':')
376
/* the path is relative */
381
#endif /* USE_DOSEXT */
383
/* ------------------------------------------------------------------------ */
386
* A port can define USE_TIMERAND if it wishes to randomize from the
387
* system clock. This should be usable by most ports.
392
void os_rand(long *seed)
399
#endif /* USE_TIMERAND */
401
#ifdef USE_PATHSEARCH
402
/* search a path specified in the environment for a tads file */
403
static int pathfind(const char *fname, int flen, const char *pathvar,
404
char *buf, size_t bufsiz)
408
if ( !(e = getenv(pathvar)) )
415
if ( (sep = strchr(e, OSPATHSEP)) )
417
len = (size_t)(sep-e);
426
if (buf[len-1] != OSPATHCHAR && !strchr(OSPATHALT, buf[len-1]))
427
buf[len++] = OSPATHCHAR;
428
memcpy(buf+len, fname, flen);
430
if (osfacc(buf) == 0) return(1);
436
#endif /* USE_PATHSEARCH */
439
* Look for a tads-related file in the standard locations and, if the
440
* search is successful, store the result file name in the given buffer.
441
* Return 1 if the file was located, 0 if not.
443
* Search the following areas for the file: current directory, program
444
* directory (as derived from argv[0]), and the TADS path.
446
int os_locate(const char *fname, int flen, const char *arg0,
447
char *buf, size_t bufsiz)
449
/* Check the current directory */
450
if (osfacc(fname) == 0)
452
memcpy(buf, fname, flen);
457
/* Check the program directory */
462
/* find the end of the directory name of argv[0] */
463
for ( p = arg0 + strlen(arg0);
464
p > arg0 && *(p-1) != OSPATHCHAR && !strchr(OSPATHALT, *(p-1));
468
/* don't bother if there's no directory on argv[0] */
471
size_t len = (size_t)(p - arg0);
473
memcpy(buf, arg0, len);
474
memcpy(buf+len, fname, flen);
476
if (osfacc(buf) == 0) return(1);
480
#ifdef USE_PATHSEARCH
481
/* Check TADS path */
482
if ( pathfind(fname, flen, "TADS", buf, bufsiz) )
484
#endif /* USE_PATHSEARCH */
489
/* ------------------------------------------------------------------------ */
493
* This default implementation is layered on the normal osifc file APIs, so
494
* our temp files are nothing special: they're just ordinary files that we
495
* put in a special directory (the temp path), and which we count on our
496
* caller to delete before the app terminates (which assumes that the app
497
* terminates normally, AND that our portable caller correctly keeps track
498
* of every temp file it creates and explicitly deletes it).
500
* On systems that have native temp file support, you might want to use the
501
* native API instead, since native temp file APIs often have the useful
502
* distinction that they'll automatically delete any outstanding temp files
503
* on application termination, even on crashy exits. To remove these
504
* default implementations from the build, #define OSNOUI_OMIT_TEMPFILE
507
#ifndef OSNOUI_OMIT_TEMPFILE
510
* Create and open a temporary file
512
osfildef *os_create_tempfile(const char *swapname, char *buf)
516
/* if a name wasn't provided, generate a name */
526
* try once with the temporary file path; if that fails, simply
527
* try using the current working directory
529
for (found = FALSE, tries = 0 ; !found && tries < 2 ; ++tries)
532
* If this is the first try, get the appropriate path for a
533
* temp file. If that failed, try using the current
538
/* get the temporary path */
539
os_get_tmp_path(buf);
545
* the temporary path didn't work, so this time try it in
546
* the current directory
552
/* get the current time, as a basis for a unique identifier */
555
/* try until we find a non-existent filename */
556
for (attempt = 0 ; attempt < 100 ; ++attempt)
558
/* generate a name based on time and try number */
559
sprintf(buf + len, "SW%06ld.%03d",
560
(long)timer % 999999, attempt);
562
/* check to see if a file by this name already exists */
565
/* the file doesn't exist - try creating it */
566
fp = osfoprwtb(buf, OSFTSWAP);
568
/* if that succeeded, return this file */
571
/* set the file's type in the OS, if necessary */
572
os_settype(buf, OSFTSWAP);
574
/* we're done - return the open file handle */
581
/* we failed to find a name we could use - give up */
586
/* open the file they gave us */
587
fp = osfoprwtb(swapname, OSFTSWAP);
589
/* set the file's type in the OS, if necessary */
590
os_settype(swapname, OSFTSWAP);
592
/* return the file pointer */
598
* Delete a temporary file. Since our generic implementation of
599
* os_create_tempfile() simply uses osfoprwtb() to open the file, a
600
* temporary file's handle is not any different from any other file
601
* handle - in particular, the OS doesn't automatically delete the file
602
* when closed. Hence, we need to delete the file explicitly here.
604
int osfdel_temp(const char *fname)
606
/* delete the file using the normal mechanism */
607
return osfdel(fname);
610
#endif /* OSNOUI_OMIT_TEMPFILE */
612
/* ------------------------------------------------------------------------ */
615
* print a null-terminated string to osfildef* file
617
void os_fprintz(osfildef *fp, const char *str)
619
fprintf(fp, "%s", str);
623
* print a counted-length string (which might not be null-terminated) to a
626
void os_fprint(osfildef *fp, const char *str, size_t len)
628
fprintf(fp, "%.*s", (int)len, str);
633
* MS-DOS implementation - Get the temporary file path. Tries getting
634
* the values of various environment variables that are typically used
635
* to define the location for temporary files.
637
void os_get_tmp_path(char *buf)
639
static char *vars[] =
649
/* look for an environment variable from our list */
650
for (varp = vars ; *varp ; ++varp)
654
/* get this variable's value */
663
/* add a backslash if necessary */
664
if ((len = strlen(buf)) != 0
665
&& buf[len-1] != '/' && buf[len-1] != '\\')
676
/* didn't find anything - leave the prefix empty */
682
void os_settype(const char *f, os_filetype_t t)
684
/* nothing needs to be done on this system */
686
#endif /* USE_NULLSTYPE */
689
* Convert an OS filename path to a relative URL
691
void os_cvt_dir_url(char *result_buf, size_t result_buf_size,
692
const char *src_path, int end_sep)
698
static const char quoted[] = ":%";
701
* Run through the source buffer, copying characters to the output
702
* buffer. If we encounter a path separator character, replace it
703
* with a forward slash.
705
for (last_was_sep = FALSE, dst = result_buf, src = src_path,
706
rem = result_buf_size ;
707
*src != '\0' && rem > 1 ; ++dst, ++src, --rem)
709
/* presume this will not be a path separator character */
710
last_was_sep = FALSE;
713
* If this is a local path separator character, replace it with the
714
* URL-style path separator character. If it's an illegal URL
715
* character, quote it with "%" notation. Otherwise, copy it
718
if (strchr(OSPATHURL, *src) != 0)
720
/* add the URL-style path separator instead of the local one */
723
/* note that we just added a path separator character */
726
else if (strchr(quoted, *src) != 0)
728
/* if we have room for three characters, add the "%" sequence */
736
/* add the high-order digit */
737
dig = ((int)(unsigned char)*src) >> 4;
738
*dst++ = (dig < 10 ? dig + '0' : dig + 'A' - 10);
740
/* add the low-order digit */
741
dig = ((int)(unsigned char)*src) & 0x0F;
742
*dst = (dig < 10 ? dig + '0' : dig + 'A' - 10);
744
/* deduct the extra two characters beyond the expected one */
750
/* add the character unchanged */
756
* add an additional ending separator if desired and if the last
757
* character wasn't a separator
759
if (end_sep && rem > 1 && !last_was_sep)
762
/* add a null terminator and we're done */
767
* Convert a relative URL to a relative file system path name
769
void os_cvt_url_dir(char *result_buf, size_t result_buf_size,
770
const char *src_url, int end_sep)
778
* Run through the source buffer, copying characters to the output
779
* buffer. If we encounter a '/', convert it to a path separator
782
for (last_was_sep = FALSE, dst = result_buf, src = src_url,
783
rem = result_buf_size ;
784
*src != '\0' && rem > 1 ; ++dst, ++src, --rem)
787
* replace slashes with path separators; expand '%' sequences; copy
788
* all other characters unchanged
796
&& isxdigit((uchar)*(src+1))
797
&& isxdigit((uchar)*(src+2)))
802
/* convert the '%xx' sequence to its character code */
804
acc = (c - (c >= 'A' && c <= 'F'
806
: c >= 'a' && c <= 'f'
810
acc += (c - (c >= 'A' && c <= 'F'
812
: c >= 'a' && c <= 'f'
816
/* set the character */
819
/* it's not a separator */
820
last_was_sep = FALSE;
825
last_was_sep = FALSE;
830
* add an additional ending separator if desired and if the last
831
* character wasn't a separator
833
if (end_sep && rem > 1 && !last_was_sep)
836
/* add a null terminator and we're done */
841
/* ------------------------------------------------------------------------ */
843
* Service routine for searching - build the full output path name. (This
844
* is used in the various DOS/Windows builds.)
846
#if defined(TURBO) || defined(DJGPP) || defined(MICROSOFT) || defined(MSOS2)
848
static void oss_build_outpathbuf(char *outpathbuf, size_t outpathbufsiz,
849
const char *path, const char *fname)
851
/* if there's a full path buffer, build the full path */
857
/* copy the path prefix */
859
if (lp > outpathbufsiz - 1)
860
lp = outpathbufsiz - 1;
861
memcpy(outpathbuf, path, lp);
863
/* add the filename if there's any room */
865
if (lf > outpathbufsiz - lp - 1)
866
lf = outpathbufsiz - lp - 1;
867
memcpy(outpathbuf + lp, fname, lf);
869
/* null-terminate the result */
870
outpathbuf[lp + lf] = '\0';
874
#endif /* TURBO || DJGPP || MICROSOFT || MSOS2 */
876
/* ------------------------------------------------------------------------ */
878
* Borland C implementation of directory functions
880
#if defined(TURBO) || defined(DJGPP)
886
* search context structure
888
struct oss_find_ctx_t
890
/* C library find-file block */
893
/* original search path prefix (we'll allocate more to fit the string) */
898
* find first matching file
900
void *os_find_first_file(const char *dir, const char *pattern, char *outbuf,
901
size_t outbufsiz, int *isdir,
902
char *outpathbuf, size_t outpathbufsiz)
904
struct oss_find_ctx_t *ctx;
905
char realpat[OSFNMAX];
912
* construct the filename pattern from the directory and pattern
913
* segments, using "*" as the default wildcard pattern if no
914
* explicit pattern was provided
916
strcpy(realpat, dir);
917
if ((l = strlen(realpat)) != 0 && realpat[l - 1] != '\\')
920
strcpy(realpat + l, "*.*");
922
strcpy(realpat + l, pattern);
924
/* find the last separator in the original path */
925
for (p = realpat, lastsep = 0 ; *p != '\0' ; ++p)
927
/* if this is a separator, remember it */
928
if (*p == '\\' || *p == '/' || *p == ':')
933
* if we found a separator, the path prefix is everything up to and
934
* including the separator; otherwise, there's no path prefix
938
/* the length of the path includes the separator */
939
path_len = lastsep + 1 - realpat;
943
/* there's no path prefix - everything is in the current directory */
947
/* allocate a context */
948
ctx = (struct oss_find_ctx_t *)malloc(sizeof(struct oss_find_ctx_t)
951
/* copy the path to the context */
952
memcpy(ctx->path, realpat, path_len);
953
ctx->path[path_len] = '\0';
955
/* call DOS to search for a matching file */
956
if (findfirst(realpat, &ctx->ff, FA_DIREC))
958
/* no dice - delete the context and return failure */
963
/* skip files with the HIDDEN or SYSTEM attributes */
964
while ((ctx->ff.ff_attrib & (FA_HIDDEN | FA_SYSTEM)) != 0)
966
/* skip to the next file */
967
if (findnext(&ctx->ff))
969
/* no more files - delete the context and give up */
975
/* copy the filename into the caller's buffer */
976
l = strlen(ctx->ff.ff_name);
977
if (l > outbufsiz - 1)
979
memcpy(outbuf, ctx->ff.ff_name, l);
982
/* build the full path, if desired */
983
oss_build_outpathbuf(outpathbuf, outpathbufsiz,
984
ctx->path, ctx->ff.ff_name);
986
/* return the directory indication */
987
*isdir = (ctx->ff.ff_attrib & FA_DIREC) != 0;
990
* return the context - it will be freed later when find-next
991
* reaches the last file or find-close is called to cancel the
997
/* find the next file */
998
void *os_find_next_file(void *ctx0, char *outbuf, size_t outbufsiz,
999
int *isdir, char *outpathbuf, size_t outpathbufsiz)
1001
struct oss_find_ctx_t *ctx = (struct oss_find_ctx_t *)ctx0;
1004
/* if the search has already ended, do nothing */
1008
/* keep going until we find a non-hidden, non-system file */
1011
/* try searching for the next file */
1012
if (findnext(&ctx->ff))
1014
/* no more files - delete the context and give up */
1017
/* return null to indicate that the search is finished */
1020
} while ((ctx->ff.ff_attrib & (FA_HIDDEN | FA_SYSTEM)) != 0);
1022
/* return the name */
1023
l = strlen(ctx->ff.ff_name);
1024
if (l > outbufsiz - 1) l = outbufsiz - 1;
1025
memcpy(outbuf, ctx->ff.ff_name, l);
1028
/* return the directory indication */
1029
*isdir = (ctx->ff.ff_attrib & FA_DIREC) != 0;
1031
/* build the full path, if desired */
1032
oss_build_outpathbuf(outpathbuf, outpathbufsiz,
1033
ctx->path, ctx->ff.ff_name);
1036
* indicate that the search was successful by returning the non-null
1037
* context pointer -- the context has been updated for the new
1038
* position in the search, so we just return the same context
1039
* structure that we've been using all along
1044
/* cancel a search */
1045
void os_find_close(void *ctx0)
1047
struct oss_find_ctx_t *ctx = (struct oss_find_ctx_t *)ctx0;
1049
/* if the search was already cancelled, do nothing */
1053
/* delete the search context */
1058
* Time-zone initialization
1062
/* let the run-time library initialize the time zone */
1066
/* ------------------------------------------------------------------------ */
1072
* get file creation time
1074
int os_get_file_cre_time(os_file_time_t *t, const char *fname)
1078
/* get the file information */
1079
if (stat(fname, &info))
1082
/* set the creation time in the return structure */
1083
t->t = info.st_ctime;
1088
* get file modification time
1090
int os_get_file_mod_time(os_file_time_t *t, const char *fname)
1094
/* get the file information */
1095
if (stat(fname, &info))
1098
/* set the modification time in the return structure */
1099
t->t = info.st_mtime;
1104
* get file last access time
1106
int os_get_file_acc_time(os_file_time_t *t, const char *fname)
1110
/* get the file information */
1111
if (stat(fname, &info))
1114
/* set the access time in the return structure */
1115
t->t = info.st_atime;
1120
* compare two file time structures
1122
int os_cmp_file_times(const os_file_time_t *a, const os_file_time_t *b)
1126
else if (a->t == b->t)
1132
#endif /* TURBO || DJGPP */
1134
/* ------------------------------------------------------------------------ */
1136
* Microsoft C implementation of directory functions
1145
/* found data structure */
1146
struct _finddata_t data;
1148
/* full original search path */
1153
* find first matching file
1155
void *os_find_first_file(const char *dir, const char *pattern, char *outbuf,
1156
size_t outbufsiz, int *isdir,
1157
char *outpathbuf, size_t outpathbufsiz)
1160
char realpat[OSFNMAX];
1163
const char *lastsep;
1167
* construct the filename pattern from the directory and pattern
1168
* segments, using "*" as the default wildcard pattern if no
1169
* explicit pattern was provided
1171
strcpy(realpat, dir);
1172
if ((l = strlen(realpat)) != 0 && realpat[l - 1] != '\\')
1173
realpat[l++] = '\\';
1175
strcpy(realpat + l, "*");
1177
strcpy(realpat + l, pattern);
1179
/* find the last separator in the original path */
1180
for (p = realpat, lastsep = 0 ; *p != '\0' ; ++p)
1182
/* if this is a separator, remember it */
1183
if (*p == '\\' || *p == '/' || *p == ':')
1188
* if we found a separator, the path prefix is everything up to and
1189
* including the separator; otherwise, there's no path prefix
1193
/* the length of the path includes the separator */
1194
path_len = lastsep + 1 - realpat;
1198
/* there's no path prefix - everything is in the current directory */
1202
/* allocate a context */
1203
ctx = (ossfcx *)malloc(sizeof(ossfcx) + path_len);
1205
/* copy the path to the context */
1206
memcpy(ctx->path, realpat, path_len);
1207
ctx->path[path_len] = '\0';
1209
/* call DOS to search for a matching file */
1210
ctx->handle = _findfirst(realpat, &ctx->data);
1211
if (ctx->handle == -1L)
1213
/* no dice - delete the context and return failure */
1218
/* skip files with HIDDEN or SYSTEM attributes */
1219
while ((ctx->data.attrib & (_A_HIDDEN | _A_SYSTEM)) != 0)
1221
/* skip this file */
1222
if (_findnext(ctx->handle, &ctx->data) != 0)
1224
/* no more files - close up shop and return failure */
1225
_findclose(ctx->handle);
1231
/* return the name */
1232
l = strlen(ctx->data.name);
1233
if (l > outbufsiz - 1) l = outbufsiz - 1;
1234
memcpy(outbuf, ctx->data.name, l);
1237
/* return the directory indication */
1238
*isdir = (ctx->data.attrib & _A_SUBDIR) != 0;
1240
/* build the full path, if desired */
1241
oss_build_outpathbuf(outpathbuf, outpathbufsiz,
1242
ctx->path, ctx->data.name);
1245
* return the context - it will be freed later when find-next
1246
* reaches the last file or find-close is called to cancel the
1252
/* find the next file */
1253
void *os_find_next_file(void *ctx0, char *outbuf, size_t outbufsiz,
1254
int *isdir, char *outpathbuf, size_t outpathbufsiz)
1256
ossfcx *ctx = (ossfcx *)ctx0;
1259
/* if the search has already ended, do nothing */
1263
/* keep going until we find a non-system, non-hidden file */
1266
/* try searching for the next file */
1267
if (_findnext(ctx->handle, &ctx->data) != 0)
1269
/* no more files - close the search and delete the context */
1270
_findclose(ctx->handle);
1273
/* return null to indicate that the search is finished */
1276
} while ((ctx->data.attrib & (_A_HIDDEN | _A_SYSTEM)) != 0);
1278
/* return the name */
1279
l = strlen(ctx->data.name);
1280
if (l > outbufsiz - 1) l = outbufsiz - 1;
1281
memcpy(outbuf, ctx->data.name, l);
1284
/* return the directory indication */
1285
*isdir = (ctx->data.attrib & _A_SUBDIR) != 0;
1287
/* build the full path, if desired */
1288
oss_build_outpathbuf(outpathbuf, outpathbufsiz,
1289
ctx->path, ctx->data.name);
1292
* indicate that the search was successful by returning the non-null
1293
* context pointer -- the context has been updated for the new
1294
* position in the search, so we just return the same context
1295
* structure that we've been using all along
1300
/* cancel a search */
1301
void os_find_close(void *ctx0)
1303
ossfcx *ctx = (ossfcx *)ctx0;
1305
/* if the search was already cancelled, do nothing */
1309
/* close the search and delete the context structure */
1310
_findclose(ctx->handle);
1315
* Time-zone initialization
1319
/* let the run-time library initialize the time zone */
1323
/* ------------------------------------------------------------------------ */
1329
* get file creation time
1331
int os_get_file_cre_time(os_file_time_t *t, const char *fname)
1335
/* get the file information */
1336
if (stat(fname, &info))
1339
/* set the creation time in the return structure */
1340
t->t = info.st_ctime;
1345
* get file modification time
1347
int os_get_file_mod_time(os_file_time_t *t, const char *fname)
1351
/* get the file information */
1352
if (stat(fname, &info))
1355
/* set the modification time in the return structure */
1356
t->t = info.st_mtime;
1361
* get file last access time
1363
int os_get_file_acc_time(os_file_time_t *t, const char *fname)
1367
/* get the file information */
1368
if (stat(fname, &info))
1371
/* set the access time in the return structure */
1372
t->t = info.st_atime;
1377
* compare two file time structures
1379
int os_cmp_file_times(const os_file_time_t *a, const os_file_time_t *b)
1383
else if (a->t == b->t)
1389
#endif /* MICROSOFT */
1391
/* ------------------------------------------------------------------------ */
1393
* OS/2 implementation of directory functions
1397
/* C library context structure used for file searches */
1399
# define DosFindFirst(a,b,c,d,e,f) DosFindFirst(a,b,c,d,e,f,FIL_STANDARD)
1400
typedef FILEFINDBUF3 oss_c_ffcx;
1401
typedef ULONG count_t;
1402
#else /* !__32BIT__ */
1403
# define DosFindFirst(a,b,c,d,e,f) DosFindFirst(a,b,c,d,e,f,0L)
1404
typedef FILEFINDBUF oss_c_ffcx;
1405
typedef USHORT count_t;
1406
#endif /* __32BIT__ */
1410
/* C library context structure */
1413
/* original search path */
1417
void *os_find_first_file(const char *dir, const char *pattern, char *outbuf,
1418
size_t outbufsiz, int *isdir,
1419
char *outpathbuf, size_t outpathbufsiz)
1422
char realpat[OSFNMAX];
1424
HDIR hdir = HDIR_SYSTEM;
1427
const char *lastsep;
1431
* construct the filename pattern from the directory and pattern
1432
* segments, using "*" as the default wildcard pattern if no
1433
* explicit pattern was provided
1435
strcpy(realpat, dir);
1436
if ((l = strlen(realpat)) != 0 && realpat[l - 1] != '\\')
1437
realpat[l++] = '\\';
1439
strcpy(realpat + l, "*");
1441
strcpy(realpat + l, pattern);
1443
/* find the last separator in the original path */
1444
for (p = realpat, lastsep = 0 ; *p != '\0' ; ++p)
1446
/* if this is a separator, remember it */
1447
if (*p == '\\' || *p == '/' || *p == ':')
1452
* if we found a separator, the path prefix is everything up to and
1453
* including the separator; otherwise, there's no path prefix
1457
/* the length of the path includes the separator */
1458
path_len = lastsep + 1 - realpat;
1462
/* there's no path prefix - everything is in the current directory */
1466
/* allocate a context */
1467
ctx = (ossfcx *)malloc(sizeof(ossfcx) + path_len);
1469
/* call DOS to search for a matching file */
1470
if (DosFindFirst(realpat, &hdir, FILE_DIRECTORY | FILE_NORMAL,
1471
&ctx->ff, sizeof(ctx->ff), &scnt))
1473
/* no dice - delete the context and return failure */
1478
/* if it's a HIDDEN or SYSTEM file, skip it */
1479
while ((ctx->ff.attrFile & (FILE_HIDDEN | FILE_SYSTEM)) != 0)
1481
/* skip to the next file */
1482
if (DosFindNext(HDIR_SYSTEM, &ctx->ff, sizeof(ctx->ff), &scnt))
1484
/* no more files - delete the context and give up */
1490
/* copy the path to the context */
1491
memcpy(ctx->path, realpat, path_len);
1492
ctx->path[path_len] = '\0';
1494
/* return the name */
1495
l = ctx->ff.cchName;
1496
if (l > outbufsiz - 1) l = outbufsiz - 1;
1497
memcpy(outbuf, ctx->ff.achName, l);
1500
/* return the directory indication */
1501
*isdir = (ctx->ff.attrFile & FILE_DIRECTORY) != 0;
1503
/* build the full path, if desired */
1504
oss_build_outpathbuf(outpathbuf, outpathbufsiz,
1505
ctx->path, ctx->ff.data.name);
1508
* return the context - it will be freed later when find-next
1509
* reaches the last file or find-close is called to cancel the
1515
/* find the next file */
1516
void *os_find_next_file(void *ctx0, char *outbuf, size_t outbufsiz,
1517
int *isdir, char *outpathbuf, size_t outpathbufsiz)
1519
ossfcx *ctx = (ossfcx *)ctx0;
1522
/* if the search has already ended, do nothing */
1526
/* keep going until we find a non-system, non-hidden file */
1529
/* try searching for the next file */
1530
if (DosFindNext(HDIR_SYSTEM, &ctx->ff, sizeof(ctx->ff), &scnt))
1532
/* no more files - delete the context and give up */
1535
/* return null to indicate that the search is finished */
1538
} while ((ctx->ff.attrFile & (FILE_HIDDEN | FILE_SYSTEM)) != 0);
1540
/* return the name */
1541
l = ctx->ff.cchName;
1542
if (l > outbufsiz - 1) l = outbufsiz - 1;
1543
memcpy(outbuf, ctx->ff.achName, l);
1546
/* return the directory indication */
1547
*isdir = (ctx->ff.attrFile & FILE_DIRECTORY) != 0;
1549
/* build the full path, if desired */
1550
oss_build_outpathbuf(outpathbuf, outpathbufsiz,
1551
ctx->ff.path, ctx->ff.data.name);
1554
* indicate that the search was successful by returning the non-null
1555
* context pointer -- the context has been updated for the new
1556
* position in the search, so we just return the same context
1557
* structure that we've been using all along
1562
/* cancel a search */
1563
void os_find_close(void *ctx0)
1565
ossfcx *ctx = (ossfcx *)ctx0;
1567
/* if the search was already cancelled, do nothing */
1571
/* delete the context structure */
1579
* Check for a special filename
1581
enum os_specfile_t os_is_special_file(const char *fname)
1584
if (strcmp(fname, ".") == 0)
1585
return OS_SPECFILE_SELF;
1587
/* check for '..' */
1588
if (strcmp(fname, "..") == 0)
1589
return OS_SPECFILE_PARENT;
1591
/* not a special file */
1592
return OS_SPECFILE_NONE;