cfad47cfa3/tads2/osnoui.c

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
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 */