1 #ifdef RCSID
2 static char RCSid[] =
3 "$Header: d:/cvsroot/tads/TADS2/OSNOUI.C,v 1.3 1999/07/11 00:46:30 MJRoberts Exp $";
4 #endif
5
6 /*
7 * Copyright (c) 1997, 2002 Michael J. Roberts. All Rights Reserved.
8 *
9 * Please see the accompanying license file, LICENSE.TXT, for information
10 * on using and copying this software.
11 */
12 /*
13 Name
14 osnoui.c - general-purpose implementations of OS routines with no UI
15 Function
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
18 operating systems.
19 Notes
20
21 Modified
22 04/11/99 CNebel - Improve const-ness; fix C++ errors.
23 11/02/97 MJRoberts - Creation
24 */
25
26 #include <stdio.h>
27 #ifdef MSDOS
28 #include <io.h>
29 #endif
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdarg.h>
33 #include <ctype.h>
34 #include <time.h>
35 #include <sys/stat.h>
36
37 #include "os.h"
38 #include "run.h"
39
40 /*
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.
46 */
47
48 #ifdef USE_DOSEXT
49 /*
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.
56 */
os_defext(char * fn,const char * ext)57 void os_defext(char *fn, const char *ext)
58 {
59 char *p;
60
61 /*
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.
66 */
67 p = fn + strlen(fn);
68 while (p > fn)
69 {
70 /* on to the previous character */
71 p--;
72
73 /*
74 * if it's a period, return without doing anything - this
75 * filename already has an extension, so don't apply a default
76 */
77 if (*p == '.')
78 return;
79
80 /*
81 * if this is a path separator character, we're no longer in the
82 * filename, so stop looking for a period
83 */
84 if (*p == OSPATHCHAR || strchr(OSPATHALT, *p) != 0)
85 break;
86 }
87
88 /* we didn't find an extension - add the dot and the extension */
89 strcat(fn, ".");
90 strcat(fn, ext);
91 }
92
93 /*
94 * Add an extension, even if the filename currently has one
95 */
os_addext(char * fn,const char * ext)96 void os_addext(char *fn, const char *ext)
97 {
98 strcat(fn, ".");
99 strcat(fn, ext);
100 }
101
102 /*
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.
107 */
os_remext(char * fn)108 void os_remext(char *fn)
109 {
110 char *p;
111
112 /* scan backwards from the end of the string, looking for a dot */
113 p = fn + strlen(fn);
114 while ( p>fn )
115 {
116 /* move to the previous character */
117 p--;
118
119 /* if it's a period, we've found the extension */
120 if ( *p=='.' )
121 {
122 /* terminate the string here to remove the extension */
123 *p = '\0';
124
125 /* we're done */
126 return;
127 }
128
129 /*
130 * if this is a path separator, there's no extension, so we can
131 * stop looking
132 */
133 if (*p == OSPATHCHAR || strchr(OSPATHALT, *p) != 0)
134 return;
135 }
136 }
137
138 /*
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.
144 */
os_get_root_name(char * buf)145 char *os_get_root_name(char *buf)
146 {
147 char *rootname;
148
149 /* scan the name for path separators */
150 for (rootname = buf ; *buf != '\0' ; ++buf)
151 {
152 /* if this is a path separator, remember it */
153 if (*buf == OSPATHCHAR || strchr(OSPATHALT, *buf) != 0)
154 {
155 /*
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.
160 */
161 rootname = buf + 1;
162 }
163 }
164
165 /* return the last root name candidate */
166 return rootname;
167 }
168
169 /*
170 * Extract the path from a filename
171 */
os_get_path_name(char * pathbuf,size_t pathbuflen,const char * fname)172 void os_get_path_name(char *pathbuf, size_t pathbuflen, const char *fname)
173 {
174 const char *lastsep;
175 const char *p;
176 size_t len;
177 int root_path;
178
179 /* find the last separator in the filename */
180 for (p = fname, lastsep = fname ; *p != '\0' ; ++p)
181 {
182 /*
183 * if it's a path separator character, remember it as the last one
184 * we've found so far
185 */
186 if (*p == OSPATHCHAR || strchr(OSPATHALT, *p) != 0)
187 lastsep = p;
188 }
189
190 /* get the length of the prefix, not including the separator */
191 len = lastsep - fname;
192
193 /*
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.
200 */
201 for (p = fname, root_path = FALSE ; p != lastsep ; ++p)
202 {
203 /*
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
206 * a root path
207 */
208 if (*p != OSPATHCHAR && strchr(OSPATHALT, *p) == 0)
209 {
210 /* note that we don't have a root path */
211 root_path = FALSE;
212
213 /* no need to look any further */
214 break;
215 }
216 }
217
218 /* if we have a root path, keep the final path separator in the path */
219 if (root_path)
220 ++len;
221
222 #ifdef MSDOS
223 /*
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.
227 */
228 if (lastsep == fname + 2
229 && isalpha(fname[0]) && fname[1] == ':' && fname[2] == '\\')
230 {
231 /* we have an absolute path - use the full "x:\" sequence */
232 len = 3;
233 }
234 #endif
235
236 /* make sure it fits in our buffer (with a null terminator) */
237 if (len > pathbuflen - 1)
238 len = pathbuflen - 1;
239
240 /* copy it and null-terminate it */
241 memcpy(pathbuf, fname, len);
242 pathbuf[len] = '\0';
243 }
244
245 /*
246 * Build a full path name given a path and a filename
247 */
os_build_full_path(char * fullpathbuf,size_t fullpathbuflen,const char * path,const char * filename)248 void os_build_full_path(char *fullpathbuf, size_t fullpathbuflen,
249 const char *path, const char *filename)
250 {
251 size_t plen, flen;
252 int add_sep;
253
254 /*
255 * Note whether we need to add a separator. If the path prefix ends
256 * in a separator, don't add another; otherwise, add the standard
257 * system separator character.
258 *
259 * Do not add a separator if the path is completely empty, since
260 * this simply means that we want to use the current directory.
261 */
262 plen = strlen(path);
263 add_sep = (plen != 0
264 && path[plen-1] != OSPATHCHAR
265 && strchr(OSPATHALT, path[plen-1]) == 0);
266
267 /* copy the path to the full path buffer, limiting to the buffer length */
268 if (plen > fullpathbuflen - 1)
269 plen = fullpathbuflen - 1;
270 memcpy(fullpathbuf, path, plen);
271
272 /* add the path separator if necessary (and if there's room) */
273 if (add_sep && plen + 2 < fullpathbuflen)
274 fullpathbuf[plen++] = OSPATHCHAR;
275
276 /* add the filename after the path, if there's room */
277 flen = strlen(filename);
278 if (flen > fullpathbuflen - plen - 1)
279 flen = fullpathbuflen - plen - 1;
280 memcpy(fullpathbuf + plen, filename, flen);
281
282 /* add a null terminator */
283 fullpathbuf[plen + flen] = '\0';
284 }
285
286 /*
287 * Determine if a path is absolute or relative. If the path starts with
288 * a path separator character, we consider it absolute, otherwise we
289 * consider it relative.
290 *
291 * Note that, on DOS, an absolute path can also follow a drive letter.
292 * So, if the path contains a letter followed by a colon, we'll consider
293 * the path to be absolute.
294 */
os_is_file_absolute(const char * fname)295 int os_is_file_absolute(const char *fname)
296 {
297 /* if the name starts with a path separator, it's absolute */
298 if (fname[0] == OSPATHCHAR || strchr(OSPATHALT, fname[0]) != 0)
299 return TRUE;
300
301 #ifdef MSDOS
302 /* on DOS, a file is absolute if it starts with a drive letter */
303 if (isalpha(fname[0]) && fname[1] == ':')
304 return TRUE;
305 #endif /* MSDOS */
306
307 /* the path is relative */
308 return FALSE;
309 }
310
311
312 #endif /* USE_DOSEXT */
313
314 /* ------------------------------------------------------------------------ */
315
316 /*
317 * A port can define USE_TIMERAND if it wishes to randomize from the
318 * system clock. This should be usable by most ports.
319 */
320 #ifdef USE_TIMERAND
321 # include <time.h>
322
os_rand(long * seed)323 void os_rand(long *seed)
324 {
325 time_t t;
326
327 time( &t );
328 *seed = (long)t;
329 }
330 #endif /* USE_TIMERAND */
331
332 #ifdef USE_PATHSEARCH
333 /* search a path specified in the environment for a tads file */
pathfind(const char * fname,int flen,const char * pathvar,char * buf,size_t bufsiz)334 static int pathfind(const char *fname, int flen, const char *pathvar,
335 char *buf, size_t bufsiz)
336 {
337 char *e;
338
339 if ( !(e = getenv(pathvar)) )
340 return(0);
341 for ( ;; )
342 {
343 char *sep;
344 size_t len;
345
346 if ( (sep = strchr(e, OSPATHSEP)) )
347 {
348 len = (size_t)(sep-e);
349 if (!len) continue;
350 }
351 else
352 {
353 len = strlen(e);
354 if (!len) break;
355 }
356 memcpy(buf, e, len);
357 if (buf[len-1] != OSPATHCHAR && !strchr(OSPATHALT, buf[len-1]))
358 buf[len++] = OSPATHCHAR;
359 memcpy(buf+len, fname, flen);
360 buf[len+flen] = 0;
361 if (osfacc(buf) == 0) return(1);
362 if (!sep) break;
363 e = sep+1;
364 }
365 return(0);
366 }
367 #endif /* USE_PATHSEARCH */
368
369 /*
370 * Look for a tads-related file in the standard locations and, if the
371 * search is successful, store the result file name in the given buffer.
372 * Return 1 if the file was located, 0 if not.
373 *
374 * Search the following areas for the file: current directory, program
375 * directory (as derived from argv[0]), and the TADS path.
376 */
os_locate(const char * fname,int flen,const char * arg0,char * buf,size_t bufsiz)377 int os_locate(const char *fname, int flen, const char *arg0,
378 char *buf, size_t bufsiz)
379 {
380 /* Check the current directory */
381 if (osfacc(fname) == 0)
382 {
383 memcpy(buf, fname, flen);
384 buf[flen] = 0;
385 return(1);
386 }
387
388 /* Check the program directory */
389 if (arg0 && *arg0)
390 {
391 const char *p;
392
393 /* find the end of the directory name of argv[0] */
394 for ( p = arg0 + strlen(arg0);
395 p > arg0 && *(p-1) != OSPATHCHAR && !strchr(OSPATHALT, *(p-1));
396 --p )
397 ;
398
399 /* don't bother if there's no directory on argv[0] */
400 if (p > arg0)
401 {
402 size_t len = (size_t)(p - arg0);
403
404 memcpy(buf, arg0, len);
405 memcpy(buf+len, fname, flen);
406 buf[len+flen] = 0;
407 if (osfacc(buf) == 0) return(1);
408 }
409 }
410
411 #ifdef USE_PATHSEARCH
412 /* Check TADS path */
413 if ( pathfind(fname, flen, "TADS", buf, bufsiz) )
414 return(1);
415 #endif /* USE_PATHSEARCH */
416
417 return(0);
418 }
419
420 /*
421 * Create and open a temporary file
422 */
os_create_tempfile(const char * swapname,char * buf)423 osfildef *os_create_tempfile(const char *swapname, char *buf)
424 {
425 osfildef *fp;
426
427 /* if a name wasn't provided, generate a name */
428 if (swapname == 0)
429 {
430 int attempt;
431 size_t len;
432 time_t timer;
433 int found;
434 int tries;
435
436 /*
437 * try once with the temporary file path; if that fails, simply
438 * try using the current working directory
439 */
440 for (found = FALSE, tries = 0 ; !found && tries < 2 ; ++tries)
441 {
442 /*
443 * If this is the first try, get the appropriate path for a
444 * temp file. If that failed, try using the current
445 * directory.
446 */
447 if (tries == 0)
448 {
449 /* get the temporary path */
450 os_get_tmp_path(buf);
451 len = strlen(buf);
452 }
453 else
454 {
455 /*
456 * the temporary path didn't work, so this time try it in
457 * the current directory
458 */
459 buf[0] = '\0';
460 len = 0;
461 }
462
463 /* get the current time, as a basis for a unique identifier */
464 time(&timer);
465
466 /* try until we find a non-existent filename */
467 for (attempt = 0 ; attempt < 100 ; ++attempt)
468 {
469 /* generate a name based on time and try number */
470 sprintf(buf + len, "SW%06ld.%03d",
471 (long)timer % 999999, attempt);
472
473 /* check to see if a file by this name already exists */
474 if (osfacc(buf))
475 {
476 /* the file doesn't exist - try creating it */
477 fp = osfoprwtb(buf, OSFTSWAP);
478
479 /* if that succeeded, return this file */
480 if (fp != 0)
481 {
482 /* set the file's type in the OS, if necessary */
483 os_settype(buf, OSFTSWAP);
484
485 /* we're done - return the open file handle */
486 return fp;
487 }
488 }
489 }
490 }
491
492 /* we failed to find a name we could use - give up */
493 return 0;
494 }
495 else
496 {
497 /* open the file they gave us */
498 fp = osfoprwtb(swapname, OSFTSWAP);
499
500 /* set the file's type in the OS, if necessary */
501 os_settype(swapname, OSFTSWAP);
502
503 /* return the file pointer */
504 return fp;
505 }
506 }
507
508 /*
509 * Delete a temporary file. Since our generic implementation of
510 * os_create_tempfile() simply uses osfoprwtb() to open the file, a
511 * temporary file's handle is not any different from any other file
512 * handle - in particular, the OS doesn't automatically delete the file
513 * when closed. Hence, we need to delete the file explicitly here.
514 */
osfdel_temp(const char * fname)515 int osfdel_temp(const char *fname)
516 {
517 /* delete the file using the normal mechanism */
518 return osfdel(fname);
519 }
520
521
522 /*
523 * print a null-terminated string to osfildef* file
524 */
os_fprintz(osfildef * fp,const char * str)525 void os_fprintz(osfildef *fp, const char *str)
526 {
527 fprintf(fp, "%s", str);
528 }
529
530 /*
531 * print a counted-length string (which might not be null-terminated) to a
532 * file
533 */
os_fprint(osfildef * fp,const char * str,size_t len)534 void os_fprint(osfildef *fp, const char *str, size_t len)
535 {
536 fprintf(fp, "%.*s", (int)len, str);
537 }
538
539 #ifdef MSDOS
540 /*
541 * MS-DOS implementation - Get the temporary file path. Tries getting
542 * the values of various environment variables that are typically used
543 * to define the location for temporary files.
544 */
os_get_tmp_path(char * buf)545 void os_get_tmp_path(char *buf)
546 {
547 static char *vars[] =
548 {
549 "TEMPDIR",
550 "TMPDIR",
551 "TEMP",
552 "TMP",
553 0
554 };
555 char **varp;
556
557 /* look for an environment variable from our list */
558 for (varp = vars ; *varp ; ++varp)
559 {
560 char *val;
561
562 /* get this variable's value */
563 val = getenv(*varp);
564 if (val)
565 {
566 size_t len;
567
568 /* use this value */
569 strcpy(buf, val);
570
571 /* add a backslash if necessary */
572 if ((len = strlen(buf)) != 0
573 && buf[len-1] != '/' && buf[len-1] != '\\')
574 {
575 buf[len] = '\\';
576 buf[len+1] = '\0';
577 }
578
579 /* use this value */
580 return;
581 }
582 }
583
584 /* didn't find anything - leave the prefix empty */
585 buf[0] = '\0';
586 }
587 #endif /* MSDOS */
588
589 #ifdef USE_NULLSTYPE
os_settype(const char * f,os_filetype_t t)590 void os_settype(const char *f, os_filetype_t t)
591 {
592 /* nothing needs to be done on this system */
593 }
594 #endif /* USE_NULLSTYPE */
595
596 /*
597 * Convert an OS filename path to a relative URL
598 */
os_cvt_dir_url(char * result_buf,size_t result_buf_size,const char * src_path,int end_sep)599 void os_cvt_dir_url(char *result_buf, size_t result_buf_size,
600 const char *src_path, int end_sep)
601 {
602 char *dst;
603 const char *src;
604 size_t rem;
605 int last_was_sep;
606 static const char quoted[] = ":%";
607
608 /*
609 * Run through the source buffer, copying characters to the output
610 * buffer. If we encounter a path separator character, replace it
611 * with a forward slash.
612 */
613 for (last_was_sep = FALSE, dst = result_buf, src = src_path,
614 rem = result_buf_size ;
615 *src != '\0' && rem > 1 ; ++dst, ++src, --rem)
616 {
617 /* presume this will not be a path separator character */
618 last_was_sep = FALSE;
619
620 /*
621 * If this is a local path separator character, replace it with the
622 * URL-style path separator character. If it's an illegal URL
623 * character, quote it with "%" notation. Otherwise, copy it
624 * unchanged.
625 */
626 if (strchr(OSPATHURL, *src) != 0)
627 {
628 /* add the URL-style path separator instead of the local one */
629 *dst = '/';
630
631 /* note that we just added a path separator character */
632 last_was_sep = TRUE;
633 }
634 else if (strchr(quoted, *src) != 0)
635 {
636 /* if we have room for three characters, add the "%" sequence */
637 if (rem > 3)
638 {
639 int dig;
640
641 /* add the '%' */
642 *dst++ = '%';
643
644 /* add the high-order digit */
645 dig = ((int)(unsigned char)*src) >> 4;
646 *dst++ = (dig < 10 ? dig + '0' : dig + 'A' - 10);
647
648 /* add the low-order digit */
649 dig = ((int)(unsigned char)*src) & 0x0F;
650 *dst = (dig < 10 ? dig + '0' : dig + 'A' - 10);
651
652 /* deduct the extra two characters beyond the expected one */
653 rem -= 2;
654 }
655 }
656 else
657 {
658 /* add the character unchanged */
659 *dst = *src;
660 }
661 }
662
663 /*
664 * add an additional ending separator if desired and if the last
665 * character wasn't a separator
666 */
667 if (end_sep && rem > 1 && !last_was_sep)
668 *dst++ = '/';
669
670 /* add a null terminator and we're done */
671 *dst = '\0';
672 }
673
674 /*
675 * Convert a relative URL to a relative file system path name
676 */
os_cvt_url_dir(char * result_buf,size_t result_buf_size,const char * src_url,int end_sep)677 void os_cvt_url_dir(char *result_buf, size_t result_buf_size,
678 const char *src_url, int end_sep)
679 {
680 char *dst;
681 const char *src;
682 size_t rem;
683 int last_was_sep;
684
685 /*
686 * Run through the source buffer, copying characters to the output
687 * buffer. If we encounter a '/', convert it to a path separator
688 * character.
689 */
690 for (last_was_sep = FALSE, dst = result_buf, src = src_url,
691 rem = result_buf_size ;
692 *src != '\0' && rem > 1 ; ++dst, ++src, --rem)
693 {
694 /*
695 * replace slashes with path separators; expand '%' sequences; copy
696 * all other characters unchanged
697 */
698 if (*src == '/')
699 {
700 *dst = OSPATHCHAR;
701 last_was_sep = TRUE;
702 }
703 else if (*src == '%'
704 && isxdigit((uchar)*(src+1))
705 && isxdigit((uchar)*(src+2)))
706 {
707 unsigned char c;
708 unsigned char acc;
709
710 /* convert the '%xx' sequence to its character code */
711 c = *++src;
712 acc = (c - (c >= 'A' && c <= 'F'
713 ? 'A' - 10
714 : c >= 'a' && c <= 'f'
715 ? 'a' - 10
716 : '0')) << 4;
717 c = *++src;
718 acc += (c - (c >= 'A' && c <= 'F'
719 ? 'A' - 10
720 : c >= 'a' && c <= 'f'
721 ? 'a' - 10
722 : '0'));
723
724 /* set the character */
725 *dst = acc;
726
727 /* it's not a separator */
728 last_was_sep = FALSE;
729 }
730 else
731 {
732 *dst = *src;
733 last_was_sep = FALSE;
734 }
735 }
736
737 /*
738 * add an additional ending separator if desired and if the last
739 * character wasn't a separator
740 */
741 if (end_sep && rem > 1 && !last_was_sep)
742 *dst++ = OSPATHCHAR;
743
744 /* add a null terminator and we're done */
745 *dst = '\0';
746 }
747
748
749 /* ------------------------------------------------------------------------ */
750 /*
751 * service routine for searching - build the full output path name
752 */
oss_build_outpathbuf(char * outpathbuf,size_t outpathbufsiz,const char * path,const char * fname)753 static void oss_build_outpathbuf(char *outpathbuf, size_t outpathbufsiz,
754 const char *path, const char *fname)
755 {
756 /* if there's a full path buffer, build the full path */
757 if (outpathbuf != 0)
758 {
759 size_t lp;
760 size_t lf;
761
762 /* copy the path prefix */
763 lp = strlen(path);
764 if (lp > outpathbufsiz - 1)
765 lp = outpathbufsiz - 1;
766 memcpy(outpathbuf, path, lp);
767
768 /* add the filename if there's any room */
769 lf = strlen(fname);
770 if (lf > outpathbufsiz - lp - 1)
771 lf = outpathbufsiz - lp - 1;
772 memcpy(outpathbuf + lp, fname, lf);
773
774 /* null-terminate the result */
775 outpathbuf[lp + lf] = '\0';
776 }
777 }
778
779 /* ------------------------------------------------------------------------ */
780 /*
781 * Borland C implementation of directory functions
782 */
783 #if defined(TURBO) || defined(DJGPP)
784
785 #include <dir.h>
786 #include <dos.h>
787
788 /*
789 * search context structure
790 */
791 struct oss_find_ctx_t
792 {
793 /* C library find-file block */
794 struct ffblk ff;
795
796 /* original search path prefix (we'll allocate more to fit the string) */
797 char path[1];
798 };
799
800 /*
801 * find first matching file
802 */
os_find_first_file(const char * dir,const char * pattern,char * outbuf,size_t outbufsiz,int * isdir,char * outpathbuf,size_t outpathbufsiz)803 void *os_find_first_file(const char *dir, const char *pattern, char *outbuf,
804 size_t outbufsiz, int *isdir,
805 char *outpathbuf, size_t outpathbufsiz)
806 {
807 struct oss_find_ctx_t *ctx;
808 char realpat[OSFNMAX];
809 size_t l;
810 size_t path_len;
811 const char *lastsep;
812 const char *p;
813
814 /*
815 * construct the filename pattern from the directory and pattern
816 * segments, using "*" as the default wildcard pattern if no
817 * explicit pattern was provided
818 */
819 strcpy(realpat, dir);
820 if ((l = strlen(realpat)) != 0 && realpat[l - 1] != '\\')
821 realpat[l++] = '\\';
822 if (pattern == 0)
823 strcpy(realpat + l, "*.*");
824 else
825 strcpy(realpat + l, pattern);
826
827 /* find the last separator in the original path */
828 for (p = realpat, lastsep = 0 ; *p != '\0' ; ++p)
829 {
830 /* if this is a separator, remember it */
831 if (*p == '\\' || *p == '/' || *p == ':')
832 lastsep = p;
833 }
834
835 /*
836 * if we found a separator, the path prefix is everything up to and
837 * including the separator; otherwise, there's no path prefix
838 */
839 if (lastsep != 0)
840 {
841 /* the length of the path includes the separator */
842 path_len = lastsep + 1 - realpat;
843 }
844 else
845 {
846 /* there's no path prefix - everything is in the current directory */
847 path_len = 0;
848 }
849
850 /* allocate a context */
851 ctx = (struct oss_find_ctx_t *)malloc(sizeof(struct oss_find_ctx_t)
852 + path_len);
853
854 /* copy the path to the context */
855 memcpy(ctx->path, realpat, path_len);
856 ctx->path[path_len] = '\0';
857
858 /* call DOS to search for a matching file */
859 if (findfirst(realpat, &ctx->ff, FA_DIREC))
860 {
861 /* no dice - delete the context and return failure */
862 free(ctx);
863 return 0;
864 }
865
866 /* copy the filename into the caller's buffer */
867 l = strlen(ctx->ff.ff_name);
868 if (l > outbufsiz - 1)
869 l = outbufsiz - 1;
870 memcpy(outbuf, ctx->ff.ff_name, l);
871 outbuf[l] = '\0';
872
873 /* build the full path, if desired */
874 oss_build_outpathbuf(outpathbuf, outpathbufsiz,
875 ctx->path, ctx->ff.ff_name);
876
877 /* return the directory indication */
878 *isdir = (ctx->ff.ff_attrib & FA_DIREC) != 0;
879
880 /*
881 * return the context - it will be freed later when find-next
882 * reaches the last file or find-close is called to cancel the
883 * search
884 */
885 return ctx;
886 }
887
888 /* find the next file */
os_find_next_file(void * ctx0,char * outbuf,size_t outbufsiz,int * isdir,char * outpathbuf,size_t outpathbufsiz)889 void *os_find_next_file(void *ctx0, char *outbuf, size_t outbufsiz,
890 int *isdir, char *outpathbuf, size_t outpathbufsiz)
891 {
892 struct oss_find_ctx_t *ctx = (struct oss_find_ctx_t *)ctx0;
893 size_t l;
894
895 /* if the search has already ended, do nothing */
896 if (ctx == 0)
897 return 0;
898
899 /* try searching for the next file */
900 if (findnext(&ctx->ff))
901 {
902 /* no more files - delete the context and give up */
903 free(ctx);
904
905 /* return null to indicate that the search is finished */
906 return 0;
907 }
908
909 /* return the name */
910 l = strlen(ctx->ff.ff_name);
911 if (l > outbufsiz - 1) l = outbufsiz - 1;
912 memcpy(outbuf, ctx->ff.ff_name, l);
913 outbuf[l] = '\0';
914
915 /* return the directory indication */
916 *isdir = (ctx->ff.ff_attrib & FA_DIREC) != 0;
917
918 /* build the full path, if desired */
919 oss_build_outpathbuf(outpathbuf, outpathbufsiz,
920 ctx->path, ctx->ff.ff_name);
921
922 /*
923 * indicate that the search was successful by returning the non-null
924 * context pointer -- the context has been updated for the new
925 * position in the search, so we just return the same context
926 * structure that we've been using all along
927 */
928 return ctx;
929 }
930
931 /* cancel a search */
os_find_close(void * ctx0)932 void os_find_close(void *ctx0)
933 {
934 struct oss_find_ctx_t *ctx = (struct oss_find_ctx_t *)ctx0;
935
936 /* if the search was already cancelled, do nothing */
937 if (ctx == 0)
938 return;
939
940 /* delete the search context */
941 free(ctx);
942 }
943
944 /*
945 * Time-zone initialization
946 */
os_tzset()947 void os_tzset()
948 {
949 /* let the run-time library initialize the time zone */
950 tzset();
951 }
952
953 /* ------------------------------------------------------------------------ */
954 /*
955 * Get file times
956 */
957
958 /*
959 * get file creation time
960 */
os_get_file_cre_time(os_file_time_t * t,const char * fname)961 int os_get_file_cre_time(os_file_time_t *t, const char *fname)
962 {
963 struct stat info;
964
965 /* get the file information */
966 if (stat(fname, &info))
967 return 1;
968
969 /* set the creation time in the return structure */
970 t->t = info.st_ctime;
971 return 0;
972 }
973
974 /*
975 * get file modification time
976 */
os_get_file_mod_time(os_file_time_t * t,const char * fname)977 int os_get_file_mod_time(os_file_time_t *t, const char *fname)
978 {
979 struct stat info;
980
981 /* get the file information */
982 if (stat(fname, &info))
983 return 1;
984
985 /* set the modification time in the return structure */
986 t->t = info.st_mtime;
987 return 0;
988 }
989
990 /*
991 * get file last access time
992 */
os_get_file_acc_time(os_file_time_t * t,const char * fname)993 int os_get_file_acc_time(os_file_time_t *t, const char *fname)
994 {
995 struct stat info;
996
997 /* get the file information */
998 if (stat(fname, &info))
999 return 1;
1000
1001 /* set the access time in the return structure */
1002 t->t = info.st_atime;
1003 return 0;
1004 }
1005
1006 /*
1007 * compare two file time structures
1008 */
os_cmp_file_times(const os_file_time_t * a,const os_file_time_t * b)1009 int os_cmp_file_times(const os_file_time_t *a, const os_file_time_t *b)
1010 {
1011 if (a->t < b->t)
1012 return -1;
1013 else if (a->t == b->t)
1014 return 0;
1015 else
1016 return 1;
1017 }
1018
1019 #endif /* TURBO || DJGPP */
1020
1021 /* ------------------------------------------------------------------------ */
1022 /*
1023 * Microsoft C implementation of directory functions
1024 */
1025 #ifdef MICROSOFT
1026
1027 typedef struct
1028 {
1029 /* search handle */
1030 long handle;
1031
1032 /* found data structure */
1033 struct _finddata_t data;
1034
1035 /* full original search path */
1036 char path[1];
1037 } ossfcx;
1038
1039 /*
1040 * find first matching file
1041 */
os_find_first_file(const char * dir,const char * pattern,char * outbuf,size_t outbufsiz,int * isdir,char * outpathbuf,size_t outpathbufsiz)1042 void *os_find_first_file(const char *dir, const char *pattern, char *outbuf,
1043 size_t outbufsiz, int *isdir,
1044 char *outpathbuf, size_t outpathbufsiz)
1045 {
1046 ossfcx *ctx;
1047 char realpat[OSFNMAX];
1048 size_t l;
1049 size_t path_len;
1050 const char *lastsep;
1051 const char *p;
1052
1053 /*
1054 * construct the filename pattern from the directory and pattern
1055 * segments, using "*" as the default wildcard pattern if no
1056 * explicit pattern was provided
1057 */
1058 strcpy(realpat, dir);
1059 if ((l = strlen(realpat)) != 0 && realpat[l - 1] != '\\')
1060 realpat[l++] = '\\';
1061 if (pattern == 0)
1062 strcpy(realpat + l, "*");
1063 else
1064 strcpy(realpat + l, pattern);
1065
1066 /* find the last separator in the original path */
1067 for (p = realpat, lastsep = 0 ; *p != '\0' ; ++p)
1068 {
1069 /* if this is a separator, remember it */
1070 if (*p == '\\' || *p == '/' || *p == ':')
1071 lastsep = p;
1072 }
1073
1074 /*
1075 * if we found a separator, the path prefix is everything up to and
1076 * including the separator; otherwise, there's no path prefix
1077 */
1078 if (lastsep != 0)
1079 {
1080 /* the length of the path includes the separator */
1081 path_len = lastsep + 1 - realpat;
1082 }
1083 else
1084 {
1085 /* there's no path prefix - everything is in the current directory */
1086 path_len = 0;
1087 }
1088
1089 /* allocate a context */
1090 ctx = (ossfcx *)malloc(sizeof(ossfcx) + path_len);
1091
1092 /* copy the path to the context */
1093 memcpy(ctx->path, realpat, path_len);
1094 ctx->path[path_len] = '\0';
1095
1096 /* call DOS to search for a matching file */
1097 ctx->handle = _findfirst(realpat, &ctx->data);
1098 if (ctx->handle == -1L)
1099 {
1100 /* no dice - delete the context and return failure */
1101 free(ctx);
1102 return 0;
1103 }
1104
1105 /* return the name */
1106 l = strlen(ctx->data.name);
1107 if (l > outbufsiz - 1) l = outbufsiz - 1;
1108 memcpy(outbuf, ctx->data.name, l);
1109 outbuf[l] = '\0';
1110
1111 /* return the directory indication */
1112 *isdir = (ctx->data.attrib & _A_SUBDIR) != 0;
1113
1114 /* build the full path, if desired */
1115 oss_build_outpathbuf(outpathbuf, outpathbufsiz,
1116 ctx->path, ctx->data.name);
1117
1118 /*
1119 * return the context - it will be freed later when find-next
1120 * reaches the last file or find-close is called to cancel the
1121 * search
1122 */
1123 return ctx;
1124 }
1125
1126 /* find the next file */
os_find_next_file(void * ctx0,char * outbuf,size_t outbufsiz,int * isdir,char * outpathbuf,size_t outpathbufsiz)1127 void *os_find_next_file(void *ctx0, char *outbuf, size_t outbufsiz,
1128 int *isdir, char *outpathbuf, size_t outpathbufsiz)
1129 {
1130 ossfcx *ctx = (ossfcx *)ctx0;
1131 size_t l;
1132
1133 /* if the search has already ended, do nothing */
1134 if (ctx == 0)
1135 return 0;
1136
1137 /* try searching for the next file */
1138 if (_findnext(ctx->handle, &ctx->data) != 0)
1139 {
1140 /* no more files - close the search and delete the context */
1141 _findclose(ctx->handle);
1142 free(ctx);
1143
1144 /* return null to indicate that the search is finished */
1145 return 0;
1146 }
1147
1148 /* return the name */
1149 l = strlen(ctx->data.name);
1150 if (l > outbufsiz - 1) l = outbufsiz - 1;
1151 memcpy(outbuf, ctx->data.name, l);
1152 outbuf[l] = '\0';
1153
1154 /* return the directory indication */
1155 *isdir = (ctx->data.attrib & _A_SUBDIR) != 0;
1156
1157 /* build the full path, if desired */
1158 oss_build_outpathbuf(outpathbuf, outpathbufsiz,
1159 ctx->path, ctx->data.name);
1160
1161 /*
1162 * indicate that the search was successful by returning the non-null
1163 * context pointer -- the context has been updated for the new
1164 * position in the search, so we just return the same context
1165 * structure that we've been using all along
1166 */
1167 return ctx;
1168 }
1169
1170 /* cancel a search */
os_find_close(void * ctx0)1171 void os_find_close(void *ctx0)
1172 {
1173 ossfcx *ctx = (ossfcx *)ctx0;
1174
1175 /* if the search was already cancelled, do nothing */
1176 if (ctx == 0)
1177 return;
1178
1179 /* close the search and delete the context structure */
1180 _findclose(ctx->handle);
1181 free(ctx);
1182 }
1183
1184 /*
1185 * Time-zone initialization
1186 */
os_tzset()1187 void os_tzset()
1188 {
1189 /* let the run-time library initialize the time zone */
1190 tzset();
1191 }
1192
1193 /* ------------------------------------------------------------------------ */
1194 /*
1195 * Get file times
1196 */
1197
1198 /*
1199 * get file creation time
1200 */
os_get_file_cre_time(os_file_time_t * t,const char * fname)1201 int os_get_file_cre_time(os_file_time_t *t, const char *fname)
1202 {
1203 struct stat info;
1204
1205 /* get the file information */
1206 if (stat(fname, &info))
1207 return 1;
1208
1209 /* set the creation time in the return structure */
1210 t->t = info.st_ctime;
1211 return 0;
1212 }
1213
1214 /*
1215 * get file modification time
1216 */
os_get_file_mod_time(os_file_time_t * t,const char * fname)1217 int os_get_file_mod_time(os_file_time_t *t, const char *fname)
1218 {
1219 struct stat info;
1220
1221 /* get the file information */
1222 if (stat(fname, &info))
1223 return 1;
1224
1225 /* set the modification time in the return structure */
1226 t->t = info.st_mtime;
1227 return 0;
1228 }
1229
1230 /*
1231 * get file last access time
1232 */
os_get_file_acc_time(os_file_time_t * t,const char * fname)1233 int os_get_file_acc_time(os_file_time_t *t, const char *fname)
1234 {
1235 struct stat info;
1236
1237 /* get the file information */
1238 if (stat(fname, &info))
1239 return 1;
1240
1241 /* set the access time in the return structure */
1242 t->t = info.st_atime;
1243 return 0;
1244 }
1245
1246 /*
1247 * compare two file time structures
1248 */
os_cmp_file_times(const os_file_time_t * a,const os_file_time_t * b)1249 int os_cmp_file_times(const os_file_time_t *a, const os_file_time_t *b)
1250 {
1251 if (a->t < b->t)
1252 return -1;
1253 else if (a->t == b->t)
1254 return 0;
1255 else
1256 return 1;
1257 }
1258
1259 #endif /* MICROSOFT */
1260
1261 /* ------------------------------------------------------------------------ */
1262 /*
1263 * OS/2 implementation of directory functions
1264 */
1265 #ifdef MSOS2
1266
1267 /* C library context structure used for file searches */
1268 #ifdef __32BIT__
1269 # define DosFindFirst(a,b,c,d,e,f) DosFindFirst(a,b,c,d,e,f,FIL_STANDARD)
1270 typedef FILEFINDBUF3 oss_c_ffcx;
1271 typedef ULONG count_t;
1272 #else /* !__32BIT__ */
1273 # define DosFindFirst(a,b,c,d,e,f) DosFindFirst(a,b,c,d,e,f,0L)
1274 typedef FILEFINDBUF oss_c_ffcx;
1275 typedef USHORT count_t;
1276 #endif /* __32BIT__ */
1277
1278 typedef struct
1279 {
1280 /* C library context structure */
1281 oss_c_ffcx ff;
1282
1283 /* original search path */
1284 char path[1];
1285 } ossfcx;
1286
os_find_first_file(const char * dir,const char * pattern,char * outbuf,size_t outbufsiz,int * isdir,char * outpathbuf,size_t outpathbufsiz)1287 void *os_find_first_file(const char *dir, const char *pattern, char *outbuf,
1288 size_t outbufsiz, int *isdir,
1289 char *outpathbuf, size_t outpathbufsiz)
1290 {
1291 ossfcx *ctx;
1292 char realpat[OSFNMAX];
1293 size_t l;
1294 HDIR hdir = HDIR_SYSTEM;
1295 count_t scnt = 1;
1296 size_t path_len;
1297 const char *lastsep;
1298 const char *p;
1299
1300 /*
1301 * construct the filename pattern from the directory and pattern
1302 * segments, using "*" as the default wildcard pattern if no
1303 * explicit pattern was provided
1304 */
1305 strcpy(realpat, dir);
1306 if ((l = strlen(realpat)) != 0 && realpat[l - 1] != '\\')
1307 realpat[l++] = '\\';
1308 if (pattern == 0)
1309 strcpy(realpat + l, "*");
1310 else
1311 strcpy(realpat + l, pattern);
1312
1313 /* find the last separator in the original path */
1314 for (p = realpat, lastsep = 0 ; *p != '\0' ; ++p)
1315 {
1316 /* if this is a separator, remember it */
1317 if (*p == '\\' || *p == '/' || *p == ':')
1318 lastsep = p;
1319 }
1320
1321 /*
1322 * if we found a separator, the path prefix is everything up to and
1323 * including the separator; otherwise, there's no path prefix
1324 */
1325 if (lastsep != 0)
1326 {
1327 /* the length of the path includes the separator */
1328 path_len = lastsep + 1 - realpat;
1329 }
1330 else
1331 {
1332 /* there's no path prefix - everything is in the current directory */
1333 path_len = 0;
1334 }
1335
1336 /* allocate a context */
1337 ctx = (ossfcx *)malloc(sizeof(ossfcx) + path_len);
1338
1339 /* call DOS to search for a matching file */
1340 if (DosFindFirst(realpat, &hdir, FILE_DIRECTORY | FILE_NORMAL,
1341 &ctx->ff, sizeof(ctx->ff), &scnt))
1342 {
1343 /* no dice - delete the context and return failure */
1344 free(ctx);
1345 return 0;
1346 }
1347
1348 /* copy the path to the context */
1349 memcpy(ctx->path, realpat, path_len);
1350 ctx->path[path_len] = '\0';
1351
1352 /* return the name */
1353 l = ctx->ff.cchName;
1354 if (l > outbufsiz - 1) l = outbufsiz - 1;
1355 memcpy(outbuf, ctx->ff.achName, l);
1356 outbuf[l] = '\0';
1357
1358 /* return the directory indication */
1359 *isdir = (ctx->ff.attrFile & FILE_DIRECTORY) != 0;
1360
1361 /* build the full path, if desired */
1362 oss_build_outpathbuf(outpathbuf, outpathbufsiz,
1363 ctx->path, ctx->ff.data.name);
1364
1365 /*
1366 * return the context - it will be freed later when find-next
1367 * reaches the last file or find-close is called to cancel the
1368 * search
1369 */
1370 return ctx;
1371 }
1372
1373 /* find the next file */
os_find_next_file(void * ctx0,char * outbuf,size_t outbufsiz,int * isdir,char * outpathbuf,size_t outpathbufsiz)1374 void *os_find_next_file(void *ctx0, char *outbuf, size_t outbufsiz,
1375 int *isdir, char *outpathbuf, size_t outpathbufsiz)
1376 {
1377 ossfcx *ctx = (ossfcx *)ctx0;
1378 size_t l;
1379
1380 /* if the search has already ended, do nothing */
1381 if (ctx == 0)
1382 return 0;
1383
1384 /* try searching for the next file */
1385 if (DosFindNext(HDIR_SYSTEM, &ctx->ff, sizeof(ctx->ff), &scnt))
1386 {
1387 /* no more files - delete the context and give up */
1388 free(ctx);
1389
1390 /* return null to indicate that the search is finished */
1391 return 0;
1392 }
1393
1394 /* return the name */
1395 l = ctx->ff.cchName;
1396 if (l > outbufsiz - 1) l = outbufsiz - 1;
1397 memcpy(outbuf, ctx->ff.achName, l);
1398 outbuf[l] = '\0';
1399
1400 /* return the directory indication */
1401 *isdir = (ctx->ff.attrFile & FILE_DIRECTORY) != 0;
1402
1403 /* build the full path, if desired */
1404 oss_build_outpathbuf(outpathbuf, outpathbufsiz,
1405 ctx->ff.path, ctx->ff.data.name);
1406
1407 /*
1408 * indicate that the search was successful by returning the non-null
1409 * context pointer -- the context has been updated for the new
1410 * position in the search, so we just return the same context
1411 * structure that we've been using all along
1412 */
1413 return ctx;
1414 }
1415
1416 /* cancel a search */
os_find_close(void * ctx0)1417 void os_find_close(void *ctx0)
1418 {
1419 ossfcx *ctx = (ossfcx *)ctx0;
1420
1421 /* if the search was already cancelled, do nothing */
1422 if (ctx == 0)
1423 return;
1424
1425 /* delete the context structure */
1426 free(ctx);
1427 }
1428
1429 #endif /* MSOS2 */
1430
1431 #ifdef MSDOS
1432 /*
1433 * Check for a special filename
1434 */
os_is_special_file(const char * fname)1435 enum os_specfile_t os_is_special_file(const char *fname)
1436 {
1437 /* check for '.' */
1438 if (strcmp(fname, ".") == 0)
1439 return OS_SPECFILE_SELF;
1440
1441 /* check for '..' */
1442 if (strcmp(fname, "..") == 0)
1443 return OS_SPECFILE_PARENT;
1444
1445 /* not a special file */
1446 return OS_SPECFILE_NONE;
1447 }
1448
1449 #endif /* MSDOS */
1450