~ubuntu-branches/ubuntu/wily/gargoyle-free/wily-proposed

« back to all changes in this revision

Viewing changes to tads/tads2/osnoui.c

  • Committer: Bazaar Package Importer
  • Author(s): Sylvain Beucler
  • Date: 2009-09-11 20:09:43 UTC
  • Revision ID: james.westby@ubuntu.com-20090911200943-idgzoyupq6650zpn
Tags: upstream-2009-08-25
ImportĀ upstreamĀ versionĀ 2009-08-25

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 */
 
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
 */
 
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
 */
 
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
 */
 
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
 */
 
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
 *   Canonicalize a path: remove ".." and "." relative elements 
 
247
 */
 
248
void canonicalize_path(char *path)
 
249
{
 
250
    char *p;
 
251
    char *start;
 
252
    
 
253
    /* keep going until we're done */
 
254
    for (start = p = path ; ; ++p)
 
255
    {
 
256
        /* if it's a separator, note it and process the path element */
 
257
        if (*p == '\\' || *p == '/' || *p == '\0')
 
258
        {
 
259
            /* 
 
260
             *   check the path element that's ending here to see if it's a
 
261
             *   relative item - either "." or ".." 
 
262
             */
 
263
            if (p - start == 1 && *start == '.')
 
264
            {
 
265
                /* 
 
266
                 *   we have a '.' element - simply remove it along with the
 
267
                 *   path separator that follows 
 
268
                 */
 
269
                if (*p == '\\' || *p == '/')
 
270
                    memmove(start, p + 1, strlen(p+1) + 1);
 
271
                else if (start > path)
 
272
                    *(start - 1) = '\0';
 
273
                else
 
274
                    *start = '\0';
 
275
            }
 
276
            else if (p - start == 2 && *start == '.' && *(start+1) == '.')
 
277
            {
 
278
                char *prv;
 
279
                
 
280
                /* 
 
281
                 *   we have a '..' element - find the previous path element,
 
282
                 *   if any, and remove it, along with the '..' and the
 
283
                 *   subsequent separator 
 
284
                 */
 
285
                for (prv = start ;
 
286
                     prv > path && (*(prv-1) != '\\' || *(prv-1) == '/') ;
 
287
                     --prv) ;
 
288
 
 
289
                /* if we found a separator, remove the previous element */
 
290
                if (prv > start)
 
291
                {
 
292
                    if (*p == '\\' || *p == '/')
 
293
                        memmove(prv, p + 1, strlen(p+1) + 1);
 
294
                    else if (start > path)
 
295
                        *(start - 1) = '\0';
 
296
                    else
 
297
                        *start = '\0';
 
298
                }
 
299
            }
 
300
 
 
301
            /* note the start of the next element */
 
302
            start = p + 1;
 
303
        }
 
304
 
 
305
        /* stop at the end of the string */
 
306
        if (*p == '\0')
 
307
            break;
 
308
    }
 
309
}
 
310
 
 
311
/*
 
312
 *   Build a full path name given a path and a filename 
 
313
 */
 
314
void os_build_full_path(char *fullpathbuf, size_t fullpathbuflen,
 
315
                        const char *path, const char *filename)
 
316
{
 
317
    size_t plen, flen;
 
318
    int add_sep;
 
319
 
 
320
    /* 
 
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.
 
324
     *   
 
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.  
 
327
     */
 
328
    plen = strlen(path);
 
329
    add_sep = (plen != 0
 
330
               && path[plen-1] != OSPATHCHAR
 
331
               && strchr(OSPATHALT, path[plen-1]) == 0);
 
332
    
 
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);
 
337
 
 
338
    /* add the path separator if necessary (and if there's room) */
 
339
    if (add_sep && plen + 2 < fullpathbuflen)
 
340
        fullpathbuf[plen++] = OSPATHCHAR;
 
341
 
 
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);
 
347
 
 
348
    /* add a null terminator */
 
349
    fullpathbuf[plen + flen] = '\0';
 
350
 
 
351
    /* canonicalize the result */
 
352
    canonicalize_path(fullpathbuf);
 
353
}
 
354
 
 
355
/*
 
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.
 
359
 *   
 
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. 
 
363
 */
 
364
int os_is_file_absolute(const char *fname)
 
365
{
 
366
    /* if the name starts with a path separator, it's absolute */
 
367
    if (fname[0] == OSPATHCHAR || strchr(OSPATHALT, fname[0])  != 0)
 
368
        return TRUE;
 
369
 
 
370
#ifdef MSDOS
 
371
    /* on DOS, a file is absolute if it starts with a drive letter */
 
372
    if (isalpha(fname[0]) && fname[1] == ':')
 
373
        return TRUE;
 
374
#endif /* MSDOS */
 
375
 
 
376
    /* the path is relative */
 
377
    return FALSE;
 
378
}
 
379
 
 
380
 
 
381
#endif /* USE_DOSEXT */
 
382
 
 
383
/* ------------------------------------------------------------------------ */
 
384
 
 
385
/*
 
386
 *   A port can define USE_TIMERAND if it wishes to randomize from the
 
387
 *   system clock.  This should be usable by most ports.  
 
388
 */
 
389
#ifdef USE_TIMERAND
 
390
# include <time.h>
 
391
 
 
392
void os_rand(long *seed)
 
393
{
 
394
    time_t t;
 
395
    
 
396
    time( &t );
 
397
    *seed = (long)t;
 
398
}
 
399
#endif /* USE_TIMERAND */
 
400
 
 
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)
 
405
{
 
406
    char   *e;
 
407
    
 
408
    if ( !(e = getenv(pathvar)) )
 
409
        return(0);
 
410
    for ( ;; )
 
411
    {
 
412
        char   *sep;
 
413
        size_t  len;
 
414
        
 
415
        if ( (sep = strchr(e, OSPATHSEP)) )
 
416
        {
 
417
            len = (size_t)(sep-e);
 
418
            if (!len) continue;
 
419
        }
 
420
        else
 
421
        {
 
422
            len = strlen(e);
 
423
            if (!len) break;
 
424
        }
 
425
        memcpy(buf, e, len);
 
426
        if (buf[len-1] != OSPATHCHAR && !strchr(OSPATHALT, buf[len-1]))
 
427
            buf[len++] = OSPATHCHAR;
 
428
        memcpy(buf+len, fname, flen);
 
429
        buf[len+flen] = 0;
 
430
        if (osfacc(buf) == 0) return(1);
 
431
        if (!sep) break;
 
432
        e = sep+1;
 
433
    }
 
434
    return(0);
 
435
}
 
436
#endif /* USE_PATHSEARCH */
 
437
 
 
438
/*
 
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.
 
442
 *   
 
443
 *   Search the following areas for the file: current directory, program
 
444
 *   directory (as derived from argv[0]), and the TADS path.  
 
445
 */
 
446
int os_locate(const char *fname, int flen, const char *arg0,
 
447
              char *buf, size_t bufsiz)
 
448
{
 
449
    /* Check the current directory */
 
450
    if (osfacc(fname) == 0)
 
451
    {
 
452
        memcpy(buf, fname, flen);
 
453
        buf[flen] = 0;
 
454
        return(1);
 
455
    }
 
456
    
 
457
    /* Check the program directory */
 
458
    if (arg0 && *arg0)
 
459
    {
 
460
        const char *p;
 
461
        
 
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));
 
465
              --p )
 
466
            ;
 
467
        
 
468
        /* don't bother if there's no directory on argv[0] */
 
469
        if (p > arg0)
 
470
        {
 
471
            size_t  len = (size_t)(p - arg0);
 
472
            
 
473
            memcpy(buf, arg0, len);
 
474
            memcpy(buf+len, fname, flen);
 
475
            buf[len+flen] = 0;
 
476
            if (osfacc(buf) == 0) return(1);
 
477
        }
 
478
    }
 
479
    
 
480
#ifdef USE_PATHSEARCH
 
481
    /* Check TADS path */
 
482
    if ( pathfind(fname, flen, "TADS", buf, bufsiz) )
 
483
        return(1);
 
484
#endif /* USE_PATHSEARCH */
 
485
    
 
486
    return(0);
 
487
}
 
488
 
 
489
/* ------------------------------------------------------------------------ */
 
490
/*
 
491
 *   Temporary files
 
492
 *   
 
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).
 
499
 *   
 
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
 
505
 *   in your makefile.  
 
506
 */
 
507
#ifndef OSNOUI_OMIT_TEMPFILE
 
508
 
 
509
/*
 
510
 *   Create and open a temporary file 
 
511
 */
 
512
osfildef *os_create_tempfile(const char *swapname, char *buf)
 
513
{
 
514
    osfildef *fp;
 
515
 
 
516
    /* if a name wasn't provided, generate a name */
 
517
    if (swapname == 0)
 
518
    {
 
519
        int     attempt;
 
520
        size_t  len;
 
521
        time_t  timer;
 
522
        int     found;
 
523
        int     tries;
 
524
 
 
525
        /* 
 
526
         *   try once with the temporary file path; if that fails, simply
 
527
         *   try using the current working directory 
 
528
         */
 
529
        for (found = FALSE, tries = 0 ; !found && tries < 2 ; ++tries)
 
530
        {
 
531
            /* 
 
532
             *   If this is the first try, get the appropriate path for a
 
533
             *   temp file.  If that failed, try using the current
 
534
             *   directory. 
 
535
             */
 
536
            if (tries == 0)
 
537
            {
 
538
                /* get the temporary path */
 
539
                os_get_tmp_path(buf);
 
540
                len = strlen(buf);
 
541
            }
 
542
            else
 
543
            {
 
544
                /* 
 
545
                 *   the temporary path didn't work, so this time try it in
 
546
                 *   the current directory 
 
547
                 */
 
548
                buf[0] = '\0';
 
549
                len = 0;
 
550
            }
 
551
 
 
552
            /* get the current time, as a basis for a unique identifier */
 
553
            time(&timer);
 
554
 
 
555
            /* try until we find a non-existent filename */
 
556
            for (attempt = 0 ; attempt < 100 ; ++attempt)
 
557
            {
 
558
                /* generate a name based on time and try number */
 
559
                sprintf(buf + len, "SW%06ld.%03d",
 
560
                        (long)timer % 999999, attempt);
 
561
                
 
562
                /* check to see if a file by this name already exists */
 
563
                if (osfacc(buf))
 
564
                {
 
565
                    /* the file doesn't exist - try creating it */
 
566
                    fp = osfoprwtb(buf, OSFTSWAP);
 
567
 
 
568
                    /* if that succeeded, return this file */
 
569
                    if (fp != 0)
 
570
                    {
 
571
                        /* set the file's type in the OS, if necessary */
 
572
                        os_settype(buf, OSFTSWAP);
 
573
 
 
574
                        /* we're done - return the open file handle */
 
575
                        return fp;
 
576
                    }
 
577
                }
 
578
            }
 
579
        }
 
580
 
 
581
        /* we failed to find a name we could use - give up */
 
582
        return 0;
 
583
    }
 
584
    else
 
585
    {
 
586
        /* open the file they gave us */
 
587
        fp = osfoprwtb(swapname, OSFTSWAP);
 
588
 
 
589
        /* set the file's type in the OS, if necessary */
 
590
        os_settype(swapname, OSFTSWAP);
 
591
 
 
592
        /* return the file pointer */
 
593
        return fp;
 
594
    }
 
595
}
 
596
 
 
597
/*
 
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. 
 
603
 */
 
604
int osfdel_temp(const char *fname)
 
605
{
 
606
    /* delete the file using the normal mechanism */
 
607
    return osfdel(fname);
 
608
}
 
609
 
 
610
#endif /* OSNOUI_OMIT_TEMPFILE */
 
611
 
 
612
/* ------------------------------------------------------------------------ */
 
613
 
 
614
/*
 
615
 *   print a null-terminated string to osfildef* file 
 
616
 */
 
617
void os_fprintz(osfildef *fp, const char *str)
 
618
{
 
619
    fprintf(fp, "%s", str);
 
620
}
 
621
 
 
622
/* 
 
623
 *   print a counted-length string (which might not be null-terminated) to a
 
624
 *   file 
 
625
 */
 
626
void os_fprint(osfildef *fp, const char *str, size_t len)
 
627
{
 
628
    fprintf(fp, "%.*s", (int)len, str);
 
629
}
 
630
 
 
631
#ifdef MSDOS
 
632
/*
 
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.  
 
636
 */
 
637
void os_get_tmp_path(char *buf)
 
638
{
 
639
    static char *vars[] =
 
640
    {
 
641
        "TEMPDIR",
 
642
        "TMPDIR",
 
643
        "TEMP",
 
644
        "TMP",
 
645
        0
 
646
    };
 
647
    char **varp;
 
648
 
 
649
    /* look for an environment variable from our list */
 
650
    for (varp = vars ; *varp ; ++varp)
 
651
    {
 
652
        char *val;
 
653
 
 
654
        /* get this variable's value */
 
655
        val = getenv(*varp);
 
656
        if (val)
 
657
        {
 
658
            size_t  len;
 
659
 
 
660
            /* use this value */
 
661
            strcpy(buf, val);
 
662
 
 
663
            /* add a backslash if necessary */
 
664
            if ((len = strlen(buf)) != 0
 
665
                && buf[len-1] != '/' && buf[len-1] != '\\')
 
666
            {
 
667
                buf[len] = '\\';
 
668
                buf[len+1] = '\0';
 
669
            }
 
670
 
 
671
            /* use this value */
 
672
            return;
 
673
        }
 
674
    }
 
675
 
 
676
    /* didn't find anything - leave the prefix empty */
 
677
    buf[0] = '\0';
 
678
}
 
679
#endif /* MSDOS */
 
680
 
 
681
#ifdef USE_NULLSTYPE
 
682
void os_settype(const char *f, os_filetype_t t)
 
683
{
 
684
    /* nothing needs to be done on this system */
 
685
}
 
686
#endif /* USE_NULLSTYPE */
 
687
 
 
688
/*
 
689
 *   Convert an OS filename path to a relative URL 
 
690
 */
 
691
void os_cvt_dir_url(char *result_buf, size_t result_buf_size,
 
692
                    const char *src_path, int end_sep)
 
693
{
 
694
    char *dst;
 
695
    const char *src;
 
696
    size_t rem;
 
697
    int last_was_sep;
 
698
    static const char quoted[] = ":%";
 
699
 
 
700
    /*
 
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.  
 
704
     */
 
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)
 
708
    {
 
709
        /* presume this will not be a path separator character */
 
710
        last_was_sep = FALSE;
 
711
 
 
712
        /* 
 
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
 
716
         *   unchanged.  
 
717
         */
 
718
        if (strchr(OSPATHURL, *src) != 0)
 
719
        {
 
720
            /* add the URL-style path separator instead of the local one */
 
721
            *dst = '/';
 
722
 
 
723
            /* note that we just added a path separator character */
 
724
            last_was_sep = TRUE;
 
725
        }
 
726
        else if (strchr(quoted, *src) != 0)
 
727
        {
 
728
            /* if we have room for three characters, add the "%" sequence */
 
729
            if (rem > 3)
 
730
            {
 
731
                int dig;
 
732
                
 
733
                /* add the '%' */
 
734
                *dst++ = '%';
 
735
 
 
736
                /* add the high-order digit */
 
737
                dig = ((int)(unsigned char)*src) >> 4;
 
738
                *dst++ = (dig < 10 ? dig + '0' : dig + 'A' - 10);
 
739
 
 
740
                /* add the low-order digit */
 
741
                dig = ((int)(unsigned char)*src) & 0x0F;
 
742
                *dst = (dig < 10 ? dig + '0' : dig + 'A' - 10);
 
743
 
 
744
                /* deduct the extra two characters beyond the expected one */
 
745
                rem -= 2;
 
746
            }
 
747
        }
 
748
        else
 
749
        {
 
750
            /* add the character unchanged */
 
751
            *dst = *src;
 
752
        }
 
753
    }
 
754
 
 
755
    /* 
 
756
     *   add an additional ending separator if desired and if the last
 
757
     *   character wasn't a separator 
 
758
     */
 
759
    if (end_sep && rem > 1 && !last_was_sep)
 
760
        *dst++ = '/';
 
761
 
 
762
    /* add a null terminator and we're done */
 
763
    *dst = '\0';
 
764
}
 
765
 
 
766
/*
 
767
 *   Convert a relative URL to a relative file system path name 
 
768
 */
 
769
void os_cvt_url_dir(char *result_buf, size_t result_buf_size,
 
770
                    const char *src_url, int end_sep)
 
771
{
 
772
    char *dst;
 
773
    const char *src;
 
774
    size_t rem;
 
775
    int last_was_sep;
 
776
 
 
777
    /*
 
778
     *   Run through the source buffer, copying characters to the output
 
779
     *   buffer.  If we encounter a '/', convert it to a path separator
 
780
     *   character.
 
781
     */
 
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)
 
785
    {
 
786
        /* 
 
787
         *   replace slashes with path separators; expand '%' sequences; copy
 
788
         *   all other characters unchanged 
 
789
         */
 
790
        if (*src == '/')
 
791
        {
 
792
            *dst = OSPATHCHAR;
 
793
            last_was_sep = TRUE;
 
794
        }
 
795
        else if (*src == '%'
 
796
                 && isxdigit((uchar)*(src+1))
 
797
                 && isxdigit((uchar)*(src+2)))
 
798
        {
 
799
            unsigned char c;
 
800
            unsigned char acc;
 
801
 
 
802
            /* convert the '%xx' sequence to its character code */
 
803
            c = *++src;
 
804
            acc = (c - (c >= 'A' && c <= 'F'
 
805
                        ? 'A' - 10
 
806
                        : c >= 'a' && c <= 'f'
 
807
                        ? 'a' - 10
 
808
                        : '0')) << 4;
 
809
            c = *++src;
 
810
            acc += (c - (c >= 'A' && c <= 'F'
 
811
                         ? 'A' - 10
 
812
                         : c >= 'a' && c <= 'f'
 
813
                         ? 'a' - 10
 
814
                         : '0'));
 
815
 
 
816
            /* set the character */
 
817
            *dst = acc;
 
818
 
 
819
            /* it's not a separator */
 
820
            last_was_sep = FALSE;
 
821
        }
 
822
        else
 
823
        {
 
824
            *dst = *src;
 
825
            last_was_sep = FALSE;
 
826
        }
 
827
    }
 
828
 
 
829
    /* 
 
830
     *   add an additional ending separator if desired and if the last
 
831
     *   character wasn't a separator 
 
832
     */
 
833
    if (end_sep && rem > 1 && !last_was_sep)
 
834
        *dst++ = OSPATHCHAR;
 
835
 
 
836
    /* add a null terminator and we're done */
 
837
    *dst = '\0';
 
838
}
 
839
 
 
840
 
 
841
/* ------------------------------------------------------------------------ */
 
842
/*
 
843
 *   Service routine for searching - build the full output path name.  (This
 
844
 *   is used in the various DOS/Windows builds.)  
 
845
 */
 
846
#if defined(TURBO) || defined(DJGPP) || defined(MICROSOFT) || defined(MSOS2)
 
847
 
 
848
static void oss_build_outpathbuf(char *outpathbuf, size_t outpathbufsiz,
 
849
                                 const char *path, const char *fname)
 
850
{
 
851
    /* if there's a full path buffer, build the full path */
 
852
    if (outpathbuf != 0)
 
853
    {
 
854
        size_t lp;
 
855
        size_t lf;
 
856
 
 
857
        /* copy the path prefix */
 
858
        lp = strlen(path);
 
859
        if (lp > outpathbufsiz - 1)
 
860
            lp = outpathbufsiz - 1;
 
861
        memcpy(outpathbuf, path, lp);
 
862
 
 
863
        /* add the filename if there's any room */
 
864
        lf = strlen(fname);
 
865
        if (lf > outpathbufsiz - lp - 1)
 
866
            lf = outpathbufsiz - lp - 1;
 
867
        memcpy(outpathbuf + lp, fname, lf);
 
868
 
 
869
        /* null-terminate the result */
 
870
        outpathbuf[lp + lf] = '\0';
 
871
    }
 
872
}
 
873
 
 
874
#endif /* TURBO || DJGPP || MICROSOFT || MSOS2 */
 
875
 
 
876
/* ------------------------------------------------------------------------ */
 
877
/*
 
878
 *   Borland C implementation of directory functions 
 
879
 */
 
880
#if defined(TURBO) || defined(DJGPP)
 
881
 
 
882
#include <dir.h>
 
883
#include <dos.h>
 
884
 
 
885
/*
 
886
 *   search context structure 
 
887
 */
 
888
struct oss_find_ctx_t
 
889
{
 
890
    /* C library find-file block */
 
891
    struct ffblk ff;
 
892
 
 
893
    /* original search path prefix (we'll allocate more to fit the string) */
 
894
    char path[1];
 
895
};
 
896
 
 
897
/*
 
898
 *   find first matching file 
 
899
 */
 
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)
 
903
{
 
904
    struct oss_find_ctx_t *ctx;
 
905
    char realpat[OSFNMAX];
 
906
    size_t l;
 
907
    size_t path_len;
 
908
    const char *lastsep;
 
909
    const char *p;
 
910
 
 
911
    /* 
 
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 
 
915
     */
 
916
    strcpy(realpat, dir);
 
917
    if ((l = strlen(realpat)) != 0 && realpat[l - 1] != '\\')
 
918
        realpat[l++] = '\\';
 
919
    if (pattern == 0)
 
920
        strcpy(realpat + l, "*.*");
 
921
    else
 
922
        strcpy(realpat + l, pattern);
 
923
 
 
924
    /* find the last separator in the original path */
 
925
    for (p = realpat, lastsep = 0 ; *p != '\0' ; ++p)
 
926
    {
 
927
        /* if this is a separator, remember it */
 
928
        if (*p == '\\' || *p == '/' || *p == ':')
 
929
            lastsep = p;
 
930
    }
 
931
 
 
932
    /* 
 
933
     *   if we found a separator, the path prefix is everything up to and
 
934
     *   including the separator; otherwise, there's no path prefix 
 
935
     */
 
936
    if (lastsep != 0)
 
937
    {
 
938
        /* the length of the path includes the separator */
 
939
        path_len = lastsep + 1 - realpat;
 
940
    }
 
941
    else
 
942
    {
 
943
        /* there's no path prefix - everything is in the current directory */
 
944
        path_len = 0;
 
945
    }
 
946
 
 
947
    /* allocate a context */
 
948
    ctx = (struct oss_find_ctx_t *)malloc(sizeof(struct oss_find_ctx_t)
 
949
                                          + path_len);
 
950
 
 
951
    /* copy the path to the context */
 
952
    memcpy(ctx->path, realpat, path_len);
 
953
    ctx->path[path_len] = '\0';
 
954
 
 
955
    /* call DOS to search for a matching file */
 
956
    if (findfirst(realpat, &ctx->ff, FA_DIREC))
 
957
    {
 
958
        /* no dice - delete the context and return failure */
 
959
        free(ctx);
 
960
        return 0;
 
961
    }
 
962
 
 
963
    /* skip files with the HIDDEN or SYSTEM attributes */
 
964
    while ((ctx->ff.ff_attrib & (FA_HIDDEN | FA_SYSTEM)) != 0)
 
965
    {
 
966
        /* skip to the next file */
 
967
        if (findnext(&ctx->ff))
 
968
        {
 
969
            /* no more files - delete the context and give up */
 
970
            free(ctx);
 
971
            return 0;
 
972
        }
 
973
    }
 
974
 
 
975
    /* copy the filename into the caller's buffer */
 
976
    l = strlen(ctx->ff.ff_name);
 
977
    if (l > outbufsiz - 1)
 
978
        l = outbufsiz - 1;
 
979
    memcpy(outbuf, ctx->ff.ff_name, l);
 
980
    outbuf[l] = '\0';
 
981
 
 
982
    /* build the full path, if desired */
 
983
    oss_build_outpathbuf(outpathbuf, outpathbufsiz,
 
984
                         ctx->path, ctx->ff.ff_name);
 
985
 
 
986
    /* return the directory indication  */
 
987
    *isdir = (ctx->ff.ff_attrib & FA_DIREC) != 0;
 
988
 
 
989
    /* 
 
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
 
992
     *   search 
 
993
     */
 
994
    return ctx;
 
995
}
 
996
 
 
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)
 
1000
{
 
1001
    struct oss_find_ctx_t *ctx = (struct oss_find_ctx_t *)ctx0;
 
1002
    size_t l;
 
1003
 
 
1004
    /* if the search has already ended, do nothing */
 
1005
    if (ctx == 0)
 
1006
        return 0;
 
1007
 
 
1008
    /* keep going until we find a non-hidden, non-system file */
 
1009
    do
 
1010
    {
 
1011
        /* try searching for the next file */
 
1012
        if (findnext(&ctx->ff))
 
1013
        {
 
1014
            /* no more files - delete the context and give up */
 
1015
            free(ctx);
 
1016
            
 
1017
            /* return null to indicate that the search is finished */
 
1018
            return 0;
 
1019
        }
 
1020
    } while ((ctx->ff.ff_attrib & (FA_HIDDEN | FA_SYSTEM)) != 0);
 
1021
    
 
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);
 
1026
    outbuf[l] = '\0';
 
1027
 
 
1028
    /* return the directory indication  */
 
1029
    *isdir = (ctx->ff.ff_attrib & FA_DIREC) != 0;
 
1030
 
 
1031
    /* build the full path, if desired */
 
1032
    oss_build_outpathbuf(outpathbuf, outpathbufsiz,
 
1033
                         ctx->path, ctx->ff.ff_name);
 
1034
 
 
1035
    /* 
 
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 
 
1040
     */
 
1041
    return ctx;
 
1042
}
 
1043
 
 
1044
/* cancel a search */
 
1045
void os_find_close(void *ctx0)
 
1046
{
 
1047
    struct oss_find_ctx_t *ctx = (struct oss_find_ctx_t *)ctx0;
 
1048
 
 
1049
    /* if the search was already cancelled, do nothing */
 
1050
    if (ctx == 0)
 
1051
        return;
 
1052
 
 
1053
    /* delete the search context */
 
1054
    free(ctx);
 
1055
}
 
1056
 
 
1057
/*
 
1058
 *   Time-zone initialization 
 
1059
 */
 
1060
void os_tzset()
 
1061
{
 
1062
    /* let the run-time library initialize the time zone */
 
1063
    tzset();
 
1064
}
 
1065
 
 
1066
/* ------------------------------------------------------------------------ */
 
1067
/*
 
1068
 *   Get file times 
 
1069
 */
 
1070
 
 
1071
/*
 
1072
 *   get file creation time 
 
1073
 */
 
1074
int os_get_file_cre_time(os_file_time_t *t, const char *fname)
 
1075
{
 
1076
    struct stat info;
 
1077
 
 
1078
    /* get the file information */
 
1079
    if (stat(fname, &info))
 
1080
        return 1;
 
1081
 
 
1082
    /* set the creation time in the return structure */
 
1083
    t->t = info.st_ctime;
 
1084
    return 0;
 
1085
}
 
1086
 
 
1087
/*
 
1088
 *   get file modification time 
 
1089
 */
 
1090
int os_get_file_mod_time(os_file_time_t *t, const char *fname)
 
1091
{
 
1092
    struct stat info;
 
1093
 
 
1094
    /* get the file information */
 
1095
    if (stat(fname, &info))
 
1096
        return 1;
 
1097
 
 
1098
    /* set the modification time in the return structure */
 
1099
    t->t = info.st_mtime;
 
1100
    return 0;
 
1101
}
 
1102
 
 
1103
/* 
 
1104
 *   get file last access time 
 
1105
 */
 
1106
int os_get_file_acc_time(os_file_time_t *t, const char *fname)
 
1107
{
 
1108
    struct stat info;
 
1109
 
 
1110
    /* get the file information */
 
1111
    if (stat(fname, &info))
 
1112
        return 1;
 
1113
 
 
1114
    /* set the access time in the return structure */
 
1115
    t->t = info.st_atime;
 
1116
    return 0;
 
1117
}
 
1118
 
 
1119
/*
 
1120
 *   compare two file time structures 
 
1121
 */
 
1122
int os_cmp_file_times(const os_file_time_t *a, const os_file_time_t *b)
 
1123
{
 
1124
    if (a->t < b->t)
 
1125
        return -1;
 
1126
    else if (a->t == b->t)
 
1127
        return 0;
 
1128
    else
 
1129
        return 1;
 
1130
}
 
1131
 
 
1132
#endif /* TURBO || DJGPP */
 
1133
 
 
1134
/* ------------------------------------------------------------------------ */
 
1135
/*
 
1136
 *   Microsoft C implementation of directory functions 
 
1137
 */
 
1138
#ifdef MICROSOFT
 
1139
 
 
1140
typedef struct
 
1141
{
 
1142
    /* search handle */
 
1143
    long handle;
 
1144
 
 
1145
    /* found data structure */
 
1146
    struct _finddata_t data;
 
1147
 
 
1148
    /* full original search path */
 
1149
    char path[1];
 
1150
} ossfcx;
 
1151
 
 
1152
/*
 
1153
 *   find first matching file 
 
1154
 */
 
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)
 
1158
{
 
1159
    ossfcx *ctx;
 
1160
    char realpat[OSFNMAX];
 
1161
    size_t l;
 
1162
    size_t path_len;
 
1163
    const char *lastsep;
 
1164
    const char *p;
 
1165
    
 
1166
    /* 
 
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 
 
1170
     */
 
1171
    strcpy(realpat, dir);
 
1172
    if ((l = strlen(realpat)) != 0 && realpat[l - 1] != '\\')
 
1173
        realpat[l++] = '\\';
 
1174
    if (pattern == 0)
 
1175
        strcpy(realpat + l, "*");
 
1176
    else
 
1177
        strcpy(realpat + l, pattern);
 
1178
 
 
1179
    /* find the last separator in the original path */
 
1180
    for (p = realpat, lastsep = 0 ; *p != '\0' ; ++p)
 
1181
    {
 
1182
        /* if this is a separator, remember it */
 
1183
        if (*p == '\\' || *p == '/' || *p == ':')
 
1184
            lastsep = p;
 
1185
    }
 
1186
 
 
1187
    /* 
 
1188
     *   if we found a separator, the path prefix is everything up to and
 
1189
     *   including the separator; otherwise, there's no path prefix 
 
1190
     */
 
1191
    if (lastsep != 0)
 
1192
    {
 
1193
        /* the length of the path includes the separator */
 
1194
        path_len = lastsep + 1 - realpat;
 
1195
    }
 
1196
    else
 
1197
    {
 
1198
        /* there's no path prefix - everything is in the current directory */
 
1199
        path_len = 0;
 
1200
    }
 
1201
 
 
1202
    /* allocate a context */
 
1203
    ctx = (ossfcx *)malloc(sizeof(ossfcx) + path_len);
 
1204
 
 
1205
    /* copy the path to the context */
 
1206
    memcpy(ctx->path, realpat, path_len);
 
1207
    ctx->path[path_len] = '\0';
 
1208
 
 
1209
    /* call DOS to search for a matching file */
 
1210
    ctx->handle = _findfirst(realpat, &ctx->data);
 
1211
    if (ctx->handle == -1L)
 
1212
    {
 
1213
        /* no dice - delete the context and return failure */
 
1214
        free(ctx);
 
1215
        return 0;
 
1216
    }
 
1217
 
 
1218
    /* skip files with HIDDEN or SYSTEM attributes */
 
1219
    while ((ctx->data.attrib & (_A_HIDDEN | _A_SYSTEM)) != 0)
 
1220
    {
 
1221
        /* skip this file */
 
1222
        if (_findnext(ctx->handle, &ctx->data) != 0)
 
1223
        {
 
1224
            /* no more files - close up shop and return failure */
 
1225
            _findclose(ctx->handle);
 
1226
            free(ctx);
 
1227
            return 0;
 
1228
        }
 
1229
    }
 
1230
 
 
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);
 
1235
    outbuf[l] = '\0';
 
1236
 
 
1237
    /* return the directory indication  */
 
1238
    *isdir = (ctx->data.attrib & _A_SUBDIR) != 0;
 
1239
 
 
1240
    /* build the full path, if desired */
 
1241
    oss_build_outpathbuf(outpathbuf, outpathbufsiz,
 
1242
                         ctx->path, ctx->data.name);
 
1243
    
 
1244
    /* 
 
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
 
1247
     *   search 
 
1248
     */
 
1249
    return ctx;
 
1250
}
 
1251
 
 
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)
 
1255
{
 
1256
    ossfcx *ctx = (ossfcx *)ctx0;
 
1257
    size_t l;
 
1258
 
 
1259
    /* if the search has already ended, do nothing */
 
1260
    if (ctx == 0)
 
1261
        return 0;
 
1262
 
 
1263
    /* keep going until we find a non-system, non-hidden file */
 
1264
    do
 
1265
    {
 
1266
        /* try searching for the next file */
 
1267
        if (_findnext(ctx->handle, &ctx->data) != 0)
 
1268
        {
 
1269
            /* no more files - close the search and delete the context */
 
1270
            _findclose(ctx->handle);
 
1271
            free(ctx);
 
1272
            
 
1273
            /* return null to indicate that the search is finished */
 
1274
            return 0;
 
1275
        }
 
1276
    } while ((ctx->data.attrib & (_A_HIDDEN | _A_SYSTEM)) != 0);
 
1277
 
 
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);
 
1282
    outbuf[l] = '\0';
 
1283
 
 
1284
    /* return the directory indication  */
 
1285
    *isdir = (ctx->data.attrib & _A_SUBDIR) != 0;
 
1286
 
 
1287
    /* build the full path, if desired */
 
1288
    oss_build_outpathbuf(outpathbuf, outpathbufsiz,
 
1289
                         ctx->path, ctx->data.name);
 
1290
 
 
1291
    /* 
 
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 
 
1296
     */
 
1297
    return ctx;
 
1298
}
 
1299
 
 
1300
/* cancel a search */
 
1301
void os_find_close(void *ctx0)
 
1302
{
 
1303
    ossfcx *ctx = (ossfcx *)ctx0;
 
1304
    
 
1305
    /* if the search was already cancelled, do nothing */
 
1306
    if (ctx == 0)
 
1307
        return;
 
1308
 
 
1309
    /* close the search and delete the context structure */
 
1310
    _findclose(ctx->handle);
 
1311
    free(ctx);
 
1312
}
 
1313
 
 
1314
/*
 
1315
 *   Time-zone initialization 
 
1316
 */
 
1317
void os_tzset()
 
1318
{
 
1319
    /* let the run-time library initialize the time zone */
 
1320
    tzset();
 
1321
}
 
1322
 
 
1323
/* ------------------------------------------------------------------------ */
 
1324
/*
 
1325
 *   Get file times 
 
1326
 */
 
1327
 
 
1328
/*
 
1329
 *   get file creation time 
 
1330
 */
 
1331
int os_get_file_cre_time(os_file_time_t *t, const char *fname)
 
1332
{
 
1333
    struct stat info;
 
1334
 
 
1335
    /* get the file information */
 
1336
    if (stat(fname, &info))
 
1337
        return 1;
 
1338
 
 
1339
    /* set the creation time in the return structure */
 
1340
    t->t = info.st_ctime;
 
1341
    return 0;
 
1342
}
 
1343
 
 
1344
/*
 
1345
 *   get file modification time 
 
1346
 */
 
1347
int os_get_file_mod_time(os_file_time_t *t, const char *fname)
 
1348
{
 
1349
    struct stat info;
 
1350
 
 
1351
    /* get the file information */
 
1352
    if (stat(fname, &info))
 
1353
        return 1;
 
1354
 
 
1355
    /* set the modification time in the return structure */
 
1356
    t->t = info.st_mtime;
 
1357
    return 0;
 
1358
}
 
1359
 
 
1360
/* 
 
1361
 *   get file last access time 
 
1362
 */
 
1363
int os_get_file_acc_time(os_file_time_t *t, const char *fname)
 
1364
{
 
1365
    struct stat info;
 
1366
 
 
1367
    /* get the file information */
 
1368
    if (stat(fname, &info))
 
1369
        return 1;
 
1370
 
 
1371
    /* set the access time in the return structure */
 
1372
    t->t = info.st_atime;
 
1373
    return 0;
 
1374
}
 
1375
 
 
1376
/*
 
1377
 *   compare two file time structures 
 
1378
 */
 
1379
int os_cmp_file_times(const os_file_time_t *a, const os_file_time_t *b)
 
1380
{
 
1381
    if (a->t < b->t)
 
1382
        return -1;
 
1383
    else if (a->t == b->t)
 
1384
        return 0;
 
1385
    else
 
1386
        return 1;
 
1387
}
 
1388
 
 
1389
#endif /* MICROSOFT */
 
1390
 
 
1391
/* ------------------------------------------------------------------------ */
 
1392
/*
 
1393
 *   OS/2 implementation of directory functions 
 
1394
 */
 
1395
#ifdef MSOS2
 
1396
 
 
1397
/* C library context structure used for file searches */
 
1398
#ifdef __32BIT__
 
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__ */
 
1407
 
 
1408
typedef struct
 
1409
{
 
1410
    /* C library context structure */
 
1411
    oss_c_ffcx ff;
 
1412
 
 
1413
    /* original search path */
 
1414
    char path[1];
 
1415
} ossfcx;
 
1416
 
 
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)
 
1420
{
 
1421
    ossfcx  *ctx;
 
1422
    char     realpat[OSFNMAX];
 
1423
    size_t   l;
 
1424
    HDIR     hdir = HDIR_SYSTEM;
 
1425
    count_t  scnt = 1;
 
1426
    size_t path_len;
 
1427
    const char *lastsep;
 
1428
    const char *p;
 
1429
 
 
1430
    /* 
 
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 
 
1434
     */
 
1435
    strcpy(realpat, dir);
 
1436
    if ((l = strlen(realpat)) != 0 && realpat[l - 1] != '\\')
 
1437
        realpat[l++] = '\\';
 
1438
    if (pattern == 0)
 
1439
        strcpy(realpat + l, "*");
 
1440
    else
 
1441
        strcpy(realpat + l, pattern);
 
1442
 
 
1443
    /* find the last separator in the original path */
 
1444
    for (p = realpat, lastsep = 0 ; *p != '\0' ; ++p)
 
1445
    {
 
1446
        /* if this is a separator, remember it */
 
1447
        if (*p == '\\' || *p == '/' || *p == ':')
 
1448
            lastsep = p;
 
1449
    }
 
1450
 
 
1451
    /* 
 
1452
     *   if we found a separator, the path prefix is everything up to and
 
1453
     *   including the separator; otherwise, there's no path prefix 
 
1454
     */
 
1455
    if (lastsep != 0)
 
1456
    {
 
1457
        /* the length of the path includes the separator */
 
1458
        path_len = lastsep + 1 - realpat;
 
1459
    }
 
1460
    else
 
1461
    {
 
1462
        /* there's no path prefix - everything is in the current directory */
 
1463
        path_len = 0;
 
1464
    }
 
1465
 
 
1466
    /* allocate a context */
 
1467
    ctx = (ossfcx *)malloc(sizeof(ossfcx) + path_len);
 
1468
 
 
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))
 
1472
    {
 
1473
        /* no dice - delete the context and return failure */
 
1474
        free(ctx);
 
1475
        return 0;
 
1476
    }
 
1477
 
 
1478
    /* if it's a HIDDEN or SYSTEM file, skip it */
 
1479
    while ((ctx->ff.attrFile & (FILE_HIDDEN | FILE_SYSTEM)) != 0)
 
1480
    {
 
1481
        /* skip to the next file */
 
1482
        if (DosFindNext(HDIR_SYSTEM, &ctx->ff, sizeof(ctx->ff), &scnt))
 
1483
        {
 
1484
            /* no more files - delete the context and give up */
 
1485
            free(ctx);
 
1486
            return 0;
 
1487
        }
 
1488
    }
 
1489
 
 
1490
    /* copy the path to the context */
 
1491
    memcpy(ctx->path, realpat, path_len);
 
1492
    ctx->path[path_len] = '\0';
 
1493
 
 
1494
    /* return the name */
 
1495
    l = ctx->ff.cchName;
 
1496
    if (l > outbufsiz - 1) l = outbufsiz - 1;
 
1497
    memcpy(outbuf, ctx->ff.achName, l);
 
1498
    outbuf[l] = '\0';
 
1499
 
 
1500
    /* return the directory indication  */
 
1501
    *isdir = (ctx->ff.attrFile & FILE_DIRECTORY) != 0;
 
1502
 
 
1503
    /* build the full path, if desired */
 
1504
    oss_build_outpathbuf(outpathbuf, outpathbufsiz,
 
1505
                         ctx->path, ctx->ff.data.name);
 
1506
 
 
1507
    /* 
 
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
 
1510
     *   search 
 
1511
     */
 
1512
    return ctx;
 
1513
}
 
1514
 
 
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)
 
1518
{
 
1519
    ossfcx *ctx = (ossfcx *)ctx0;
 
1520
    size_t l;
 
1521
 
 
1522
    /* if the search has already ended, do nothing */
 
1523
    if (ctx == 0)
 
1524
        return 0;
 
1525
 
 
1526
    /* keep going until we find a non-system, non-hidden file */
 
1527
    do
 
1528
    {
 
1529
        /* try searching for the next file */
 
1530
        if (DosFindNext(HDIR_SYSTEM, &ctx->ff, sizeof(ctx->ff), &scnt))
 
1531
        {
 
1532
            /* no more files - delete the context and give up */
 
1533
            free(ctx);
 
1534
            
 
1535
            /* return null to indicate that the search is finished */
 
1536
            return 0;
 
1537
        }
 
1538
    } while ((ctx->ff.attrFile & (FILE_HIDDEN | FILE_SYSTEM)) != 0);
 
1539
 
 
1540
    /* return the name */
 
1541
    l = ctx->ff.cchName;
 
1542
    if (l > outbufsiz - 1) l = outbufsiz - 1;
 
1543
    memcpy(outbuf, ctx->ff.achName, l);
 
1544
    outbuf[l] = '\0';
 
1545
 
 
1546
    /* return the directory indication  */
 
1547
    *isdir = (ctx->ff.attrFile & FILE_DIRECTORY) != 0;
 
1548
 
 
1549
    /* build the full path, if desired */
 
1550
    oss_build_outpathbuf(outpathbuf, outpathbufsiz,
 
1551
                         ctx->ff.path, ctx->ff.data.name);
 
1552
 
 
1553
    /* 
 
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 
 
1558
     */
 
1559
    return ctx;
 
1560
}
 
1561
 
 
1562
/* cancel a search */
 
1563
void os_find_close(void *ctx0)
 
1564
{
 
1565
    ossfcx *ctx = (ossfcx *)ctx0;
 
1566
 
 
1567
    /* if the search was already cancelled, do nothing */
 
1568
    if (ctx == 0)
 
1569
        return;
 
1570
 
 
1571
    /* delete the context structure */
 
1572
    free(ctx);
 
1573
}
 
1574
 
 
1575
#endif /* MSOS2 */
 
1576
 
 
1577
#ifdef MSDOS
 
1578
/*
 
1579
 *   Check for a special filename 
 
1580
 */
 
1581
enum os_specfile_t os_is_special_file(const char *fname)
 
1582
{
 
1583
    /* check for '.' */
 
1584
    if (strcmp(fname, ".") == 0)
 
1585
        return OS_SPECFILE_SELF;
 
1586
 
 
1587
    /* check for '..' */
 
1588
    if (strcmp(fname, "..") == 0)
 
1589
        return OS_SPECFILE_PARENT;
 
1590
 
 
1591
    /* not a special file */
 
1592
    return OS_SPECFILE_NONE;
 
1593
}
 
1594
 
 
1595
#endif /* MSDOS */