Release 960805
[wine] / files / file.c
1 /*
2  * File handling functions
3  *
4  * Copyright 1993 John Burton
5  * Copyright 1996 Alexandre Julliard
6  */
7
8 #include <ctype.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/errno.h>
15 #include <sys/stat.h>
16 #include <time.h>
17 #include <unistd.h>
18 #include <utime.h>
19
20 #include "windows.h"
21 #include "directory.h"
22 #include "dos_fs.h"
23 #include "drive.h"
24 #include "global.h"
25 #include "msdos.h"
26 #include "options.h"
27 #include "ldt.h"
28 #include "task.h"
29 #include "string32.h"
30 #include "stddebug.h"
31 #include "debug.h"
32 #include "xmalloc.h"
33
34 #define MAX_OPEN_FILES 64  /* Max. open files for all tasks; must be <255 */
35
36 typedef struct tagDOS_FILE
37 {
38     struct tagDOS_FILE *next;
39     int                 count;        /* Usage count (0 if free) */
40     int                 unix_handle;
41     int                 mode;
42     char               *unix_name;
43     WORD                filedate;
44     WORD                filetime;
45 } DOS_FILE;
46
47 /* Global files array */
48 static DOS_FILE DOSFiles[MAX_OPEN_FILES];
49
50 static DOS_FILE *FILE_First = DOSFiles;
51 static DOS_FILE *FILE_LastUsed = DOSFiles;
52
53 /* Small file handles array for boot-up, before the first PDB is created */
54 #define MAX_BOOT_HANDLES  4
55 static BYTE bootFileHandles[MAX_BOOT_HANDLES] = { 0xff, 0xff, 0xff, 0xff };
56
57 /***********************************************************************
58  *           FILE_Alloc
59  *
60  * Allocate a DOS file.
61  */
62 static DOS_FILE *FILE_Alloc(void)
63 {
64     DOS_FILE *file = FILE_First;
65     if (file) FILE_First = file->next;
66     else if (FILE_LastUsed >= &DOSFiles[MAX_OPEN_FILES-1])
67     {
68         DOS_ERROR( ER_TooManyOpenFiles, EC_ProgramError, SA_Abort, EL_Disk );
69         return NULL;
70     }
71     else file = ++FILE_LastUsed;
72     file->count = 1;
73     file->unix_handle = -1;
74     file->unix_name = NULL;
75     return file;
76 }
77
78
79 /***********************************************************************
80  *           FILE_Close
81  *
82  * Close a DOS file.
83  */
84 static BOOL FILE_Close( DOS_FILE *file )
85 {
86     if (!file->count) return FALSE;
87     if (--file->count > 0) return TRUE;
88     /* Now really close the file */
89     if (file->unix_handle != -1) close( file->unix_handle );
90     if (file->unix_name) free( file->unix_name );
91     file->next = FILE_First;
92     FILE_First = file;
93     return TRUE;
94 }
95
96
97 /***********************************************************************
98  *           FILE_GetPDBFiles
99  *
100  * Return a pointer to the current PDB files array.
101  */
102 static void FILE_GetPDBFiles( BYTE **files, WORD *nbFiles )
103 {
104     PDB *pdb;
105
106     if ((pdb = (PDB *)GlobalLock16( GetCurrentPDB() )) != NULL)
107     {
108         *files = PTR_SEG_TO_LIN( pdb->fileHandlesPtr );
109         *nbFiles = pdb->nbFiles;
110     }
111     else
112     {
113         *files = bootFileHandles;
114         *nbFiles = MAX_BOOT_HANDLES;
115     }
116 }
117
118
119 /***********************************************************************
120  *           FILE_AllocTaskHandle
121  *
122  * Allocate a task file handle for a DOS file.
123  */
124 static HFILE FILE_AllocTaskHandle( DOS_FILE *dos_file )
125 {
126     BYTE *files, *fp;
127     WORD i, nbFiles;
128
129     FILE_GetPDBFiles( &files, &nbFiles );
130     fp = files + 1;  /* Don't use handle 0, as some programs don't like it */
131     for (i = nbFiles - 1; (i > 0) && (*fp != 0xff); i--, fp++);
132     if (!i)
133     {  /* No more handles or files */
134         DOS_ERROR( ER_TooManyOpenFiles, EC_ProgramError, SA_Abort, EL_Disk );
135         return -1;
136     }
137     *fp = dos_file ? (BYTE)(dos_file - DOSFiles) : 0;
138     dprintf_file(stddeb, 
139        "FILE_AllocTaskHandle: returning task handle %d, dos_file %d, file %d of %d \n", 
140              (fp - files), *fp, nbFiles - i, nbFiles  );
141     return (HFILE)(fp - files);
142 }
143
144
145 /***********************************************************************
146  *           FILE_FreeTaskHandle
147  *
148  * Free a per-task file handle.
149  */
150 static void FILE_FreeTaskHandle( HFILE handle )
151 {
152     BYTE *files;
153     WORD nbFiles;
154
155     FILE_GetPDBFiles( &files, &nbFiles );
156     dprintf_file( stddeb,"FILE_FreeTaskHandle: dos=%d file=%d\n",
157                   handle, files[handle] );
158     if ((handle < 0) || (handle >= (INT)nbFiles))
159     {
160         fprintf( stderr, "FILE_FreeTaskHandle: invalid file handle %d\n",
161                  handle );
162         return;
163     }
164     files[handle] = 0xff;
165 }
166
167
168 /***********************************************************************
169  *           FILE_SetTaskHandle
170  *
171  * Set the value of a task handle (no error checking).
172  */
173 static void FILE_SetTaskHandle( HFILE handle, DOS_FILE *file )
174 {
175     BYTE *files;
176     WORD nbFiles;
177
178     FILE_GetPDBFiles( &files, &nbFiles );
179     files[handle] = (BYTE)(file - DOSFiles);
180 }
181
182
183 /***********************************************************************
184  *           FILE_GetFile
185  *
186  * Return the DOS file associated to a task file handle.
187  */
188 static DOS_FILE *FILE_GetFile( HFILE handle )
189 {
190     BYTE *files;
191     WORD nbFiles;
192     DOS_FILE *file;
193
194     FILE_GetPDBFiles( &files, &nbFiles );
195     if ((handle < 0) || (handle >= (INT)nbFiles) ||
196         (files[handle] >= MAX_OPEN_FILES) ||
197         !(file = &DOSFiles[files[handle]])->count)
198     {
199         DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
200         return NULL;
201     }
202     return file;
203 }
204
205
206 int FILE_GetUnixHandle( HFILE hFile )
207 {
208     DOS_FILE *file;
209
210     if (!(file = FILE_GetFile( hFile ))) return -1;
211     return file->unix_handle;
212 }
213
214
215 /***********************************************************************
216  *           FILE_CloseAllFiles
217  *
218  * Close all open files of a given PDB. Used on task termination.
219  */
220 void FILE_CloseAllFiles( HANDLE hPDB )
221 {
222     BYTE *files;
223     WORD count;
224     PDB *pdb = (PDB *)GlobalLock16( hPDB );
225
226     if (!pdb) return;
227     files = PTR_SEG_TO_LIN( pdb->fileHandlesPtr );
228     dprintf_file(stddeb,"FILE_CloseAllFiles: closing %d files\n",pdb->nbFiles);
229     for (count = pdb->nbFiles; count > 0; count--, files++)
230     {
231         if (*files < MAX_OPEN_FILES) FILE_Close( &DOSFiles[*files] );
232         *files = 0xff;
233     }
234 }
235
236
237 /***********************************************************************
238  *           FILE_SetDosError
239  *
240  * Set the DOS error code from errno.
241  */
242 void FILE_SetDosError(void)
243 {
244     dprintf_file(stddeb, "FILE_SetDosError: errno = %d\n", errno );
245     switch (errno)
246     {
247     case EAGAIN:
248         DOS_ERROR( ER_ShareViolation, EC_Temporary, SA_Retry, EL_Disk );
249         break;
250     case EBADF:
251         DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
252         break;
253     case ENOSPC:
254         DOS_ERROR( ER_DiskFull, EC_MediaError, SA_Abort, EL_Disk );
255         break;
256     case EACCES:
257     case EPERM:
258     case EROFS:
259         DOS_ERROR( ER_AccessDenied, EC_AccessDenied, SA_Abort, EL_Disk );
260         break;
261     case EBUSY:
262         DOS_ERROR( ER_LockViolation, EC_AccessDenied, SA_Abort, EL_Disk );
263         break;
264     case ENOENT:
265         DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
266         break;
267     case EISDIR:
268         DOS_ERROR( ER_CanNotMakeDir, EC_AccessDenied, SA_Abort, EL_Unknown );
269         break;
270     case ENFILE:
271     case EMFILE:
272         DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Unknown );
273         break;
274     case EEXIST:
275         DOS_ERROR( ER_FileExists, EC_Exists, SA_Abort, EL_Disk );
276         break;
277     default:
278         perror( "int21: unknown errno" );
279         DOS_ERROR( ER_GeneralFailure, EC_SystemFailure, SA_Abort, EL_Unknown );
280         break;
281     }
282 }
283
284
285 /***********************************************************************
286  *           FILE_DupUnixHandle
287  *
288  * Duplicate a Unix handle into a task handle.
289  */
290 HFILE FILE_DupUnixHandle( int fd )
291 {
292     HFILE handle;
293     DOS_FILE *file;
294
295     if (!(file = FILE_Alloc())) return HFILE_ERROR;
296     if ((file->unix_handle = dup(fd)) == -1)
297     {
298         FILE_SetDosError();
299         FILE_Close( file );
300         return HFILE_ERROR;
301     }
302     if ((handle = FILE_AllocTaskHandle( file )) == HFILE_ERROR)
303         FILE_Close( file );
304     return handle;
305 }
306
307
308 /***********************************************************************
309  *           FILE_OpenUnixFile
310  */
311 static DOS_FILE *FILE_OpenUnixFile( const char *name, int mode )
312 {
313     DOS_FILE *file;
314     struct stat st;
315
316     if (!(file = FILE_Alloc())) return NULL;
317     if ((file->unix_handle = open( name, mode )) == -1)
318     {
319         if (Options.allowReadOnly && (mode == O_RDWR))
320         {
321             if ((file->unix_handle = open( name, O_RDONLY )) != -1)
322                 fprintf( stderr, "Warning: could not open %s for writing, opening read-only.\n", name );
323         }
324     }
325     if ((file->unix_handle == -1) || (fstat( file->unix_handle, &st ) == -1))
326     {
327         FILE_SetDosError();
328         FILE_Close( file );
329         return NULL;
330     }
331     if (S_ISDIR(st.st_mode))
332     {
333         DOS_ERROR( ER_AccessDenied, EC_AccessDenied, SA_Abort, EL_Disk );
334         FILE_Close( file );
335         return NULL;
336     }
337
338     /* File opened OK, now fill the DOS_FILE */
339
340     file->unix_name = xstrdup( name );
341     DOSFS_ToDosDateTime( st.st_mtime, &file->filedate, &file->filetime );
342     return file;
343 }
344
345
346 /***********************************************************************
347  *           FILE_Open
348  */
349 static DOS_FILE *FILE_Open( LPCSTR path, int mode )
350 {
351     const char *unixName;
352
353     dprintf_file(stddeb, "FILE_Open: '%s' %04x\n", path, mode );
354     if ((unixName = DOSFS_IsDevice( path )) != NULL)
355     {
356         dprintf_file( stddeb, "FILE_Open: opening device '%s'\n", unixName );
357         if (!unixName[0])  /* Non-existing device */
358         {
359             dprintf_file(stddeb, "FILE_Open: Non-existing device\n");
360             DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
361             return NULL;
362         }
363     }
364     else if (!(unixName = DOSFS_GetUnixFileName( path, TRUE ))) return NULL;
365     return FILE_OpenUnixFile( unixName, mode );
366 }
367
368
369 /***********************************************************************
370  *           FILE_Create
371  */
372 static DOS_FILE *FILE_Create( LPCSTR path, int mode, int unique )
373 {
374     DOS_FILE *file;
375     const char *unixName;
376
377     dprintf_file(stddeb, "FILE_Create: '%s' %04x %d\n", path, mode, unique );
378
379     if ((unixName = DOSFS_IsDevice( path )) != NULL)
380     {
381         dprintf_file(stddeb, "FILE_Create: creating device '%s'!\n", unixName);
382         DOS_ERROR( ER_AccessDenied, EC_NotFound, SA_Abort, EL_Disk );
383         return NULL;
384     }
385
386     if (!(file = FILE_Alloc())) return NULL;
387
388     if (!(unixName = DOSFS_GetUnixFileName( path, FALSE )))
389     {
390         FILE_Close( file );
391         return NULL;
392     }
393     if ((file->unix_handle = open( unixName,
394                            O_CREAT | O_TRUNC | O_RDWR | (unique ? O_EXCL : 0),
395                            mode )) == -1)
396     {
397         FILE_SetDosError();
398         FILE_Close( file );
399         return NULL;
400     } 
401
402     /* File created OK, now fill the DOS_FILE */
403
404     file->unix_name = xstrdup( unixName );
405     DOSFS_ToDosDateTime( time(NULL), &file->filedate, &file->filetime );
406     return file;
407 }
408
409
410 /***********************************************************************
411  *           FILE_Stat
412  *
413  * Stat a Unix path name. Return 1 if OK.
414  */
415 int FILE_Stat( LPCSTR unixName, BYTE *pattr, DWORD *psize,
416                WORD *pdate, WORD *ptime )
417 {
418     struct stat st;
419
420     if (stat( unixName, &st ) == -1)
421     {
422         FILE_SetDosError();
423         return 0;
424     }
425     if (pattr) *pattr = FA_ARCHIVE | (S_ISDIR(st.st_mode) ? FA_DIRECTORY : 0);
426     if (psize) *psize = S_ISDIR(st.st_mode) ? 0 : st.st_size;
427     DOSFS_ToDosDateTime( st.st_mtime, pdate, ptime );
428     return 1;
429 }
430
431
432 /***********************************************************************
433  *           FILE_GetDateTime
434  *
435  * Get the date and time of a file.
436  */
437 int FILE_GetDateTime( HFILE hFile, WORD *pdate, WORD *ptime, BOOL refresh )
438 {
439     DOS_FILE *file;
440
441     if (!(file = FILE_GetFile( hFile ))) return 0;
442     if (refresh)
443     {
444         struct stat st;
445         if (fstat( file->unix_handle, &st ) == -1)
446         {
447             FILE_SetDosError();
448             return 0;
449         }
450         DOSFS_ToDosDateTime( st.st_mtime, &file->filedate, &file->filetime );
451     }
452     *pdate = file->filedate;
453     *ptime = file->filetime;
454     return 1;
455 }
456
457
458 /***********************************************************************
459  *           FILE_SetDateTime
460  *
461  * Set the date and time of a file.
462  */
463 int FILE_SetDateTime( HFILE hFile, WORD date, WORD time )
464 {
465     DOS_FILE *file;
466     struct tm newtm;
467     struct utimbuf filetime;
468
469     if (!(file = FILE_GetFile( hFile ))) return 0;
470     newtm.tm_sec  = (time & 0x1f) * 2;
471     newtm.tm_min  = (time >> 5) & 0x3f;
472     newtm.tm_hour = (time >> 11);
473     newtm.tm_mday = (date & 0x1f);
474     newtm.tm_mon  = ((date >> 5) & 0x0f) - 1;
475     newtm.tm_year = (date >> 9) + 80;
476
477     filetime.actime = filetime.modtime = mktime( &newtm );
478     if (utime( file->unix_name, &filetime ) != -1) return 1;
479     FILE_SetDosError();
480     return 0;
481 }
482
483
484 /***********************************************************************
485  *           FILE_Dup
486  *
487  * dup() function for DOS handles.
488  */
489 HFILE FILE_Dup( HFILE hFile )
490 {
491     DOS_FILE *file;
492     HFILE handle;
493
494     dprintf_file( stddeb, "FILE_Dup for handle %d\n", hFile );
495     if (!(file = FILE_GetFile( hFile ))) return HFILE_ERROR;
496     if ((handle = FILE_AllocTaskHandle( file )) != HFILE_ERROR) file->count++;
497     dprintf_file( stddeb, "FILE_Dup return handle %d\n", handle );
498     return handle;
499 }
500
501
502 /***********************************************************************
503  *           FILE_Dup2
504  *
505  * dup2() function for DOS handles.
506  */
507 HFILE FILE_Dup2( HFILE hFile1, HFILE hFile2 )
508 {
509     DOS_FILE *file;
510     PDB *pdb = (PDB *)GlobalLock16( GetCurrentPDB() );
511     BYTE *files = PTR_SEG_TO_LIN( pdb->fileHandlesPtr );
512
513     dprintf_file( stddeb, "FILE_Dup2 for handle %d\n", hFile1 );
514     if (!(file = FILE_GetFile( hFile1 ))) return HFILE_ERROR;
515
516     if ((hFile2 < 0) || (hFile2 >= (INT)pdb->nbFiles))
517     {
518         DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
519         return HFILE_ERROR;
520     }
521     if (files[hFile2] < MAX_OPEN_FILES)
522     {
523         dprintf_file( stddeb, "FILE_Dup2 closing old handle2 %d\n",
524                       files[hFile2] );
525         FILE_Close( &DOSFiles[files[hFile2]] );
526     }
527     files[hFile2] = (BYTE)(file - DOSFiles);
528     file->count++;
529     dprintf_file( stddeb, "FILE_Dup2 return handle2 %d\n", hFile2 );
530     return hFile2;
531 }
532
533
534 /***********************************************************************
535  *           FILE_Read
536  */
537 INT32 FILE_Read( HFILE hFile, LPVOID buffer, UINT32 count )
538 {
539     DOS_FILE *file;
540     INT32 result;
541
542     dprintf_file( stddeb, "FILE_Read: %d %p %d\n", hFile, buffer, count );
543     if (!(file = FILE_GetFile( hFile ))) return -1;
544     if (!count) return 0;
545     if ((result = read( file->unix_handle, buffer, count )) == -1)
546         FILE_SetDosError();
547     return result;
548 }
549
550
551 /***********************************************************************
552  *           GetTempFileName16   (KERNEL.97)
553  */
554 UINT16 GetTempFileName16( BYTE drive, LPCSTR prefix, UINT16 unique,
555                           LPSTR buffer )
556 {
557     char temppath[144];
558
559     if ((drive & TF_FORCEDRIVE) &&
560         !DRIVE_IsValid( toupper(drive & ~TF_FORCEDRIVE) - 'A' ))
561     {
562         drive &= ~TF_FORCEDRIVE;
563         fprintf( stderr, "Warning: GetTempFileName: invalid drive %d specified\n",
564                  drive );
565     }
566
567     if (drive & TF_FORCEDRIVE)
568         sprintf(temppath,"%c:", drive & ~TF_FORCEDRIVE );
569     else
570     {
571         GetTempPath32A( 132, temppath );
572         strcat( temppath, "\\" );
573     }
574     return (UINT16)GetTempFileName32A( temppath, prefix, unique, buffer );
575 }
576
577
578 /***********************************************************************
579  *           GetTempFileName32A   (KERNEL32.290)
580  */
581 UINT32 GetTempFileName32A( LPCSTR path, LPCSTR prefix, UINT32 unique,
582                            LPSTR buffer)
583 {
584     LPSTR p;
585     int i;
586     UINT32 num = unique ? (unique & 0xffff) : time(NULL) & 0xffff;
587
588     if (!path) return 0;
589     strcpy( buffer, path );
590     p = buffer + strlen(buffer);
591     *p++ = '~';
592     for (i = 3; (i > 0) && (*prefix); i--) *p++ = *prefix++;
593     sprintf( p, "%04x.tmp", num );
594
595     if (unique)
596     {
597         lstrcpyn32A( buffer, DOSFS_GetDosTrueName( buffer, FALSE ), 144 );
598         dprintf_file( stddeb, "GetTempFileName: returning %s\n", buffer );
599         return unique;
600     }
601
602     /* Now try to create it */
603
604     do
605     {
606         DOS_FILE *file;
607         if ((file = FILE_Create( buffer, 0666, TRUE )) != NULL)
608         {  /* We created it */
609             dprintf_file( stddeb, "GetTempFileName: created %s\n", buffer );
610             FILE_Close( file );
611             break;
612         }
613         if (DOS_ExtendedError != ER_FileExists) break;  /* No need to go on */
614         num++;
615         sprintf( p, "%04x.tmp", num );
616     } while (num != (unique & 0xffff));
617
618     lstrcpyn32A( buffer, DOSFS_GetDosTrueName( buffer, FALSE ), 144 );
619     dprintf_file( stddeb, "GetTempFileName: returning %s\n", buffer );
620     return num;
621 }
622
623
624 /***********************************************************************
625  *           GetTempFileName32W   (KERNEL32.291)
626  */
627 UINT32 GetTempFileName32W( LPCWSTR path, LPCWSTR prefix, UINT32 unique,
628                            LPWSTR buffer )
629 {
630     LPSTR   patha,prefixa;
631     char    buffera[144];
632     UINT32  ret;
633
634     if (!path) return 0;
635     patha       = STRING32_DupUniToAnsi(path);
636     prefixa     = STRING32_DupUniToAnsi(prefix);
637     ret         = GetTempFileName32A( patha, prefixa, unique, buffera );
638     STRING32_AnsiToUni( buffer, buffera );
639     free(patha);
640     free(prefixa);
641     return ret;
642 }
643
644
645 /***********************************************************************
646  *           OpenFile   (KERNEL.74) (KERNEL32.396)
647  */
648 HFILE OpenFile( LPCSTR name, OFSTRUCT *ofs, UINT32 mode )
649 {
650     DOS_FILE *file;
651     HFILE hFileRet;
652     WORD filedatetime[2];
653     const char *unixName, *dosName;
654     char *p;
655     int len, i, unixMode;
656
657     ofs->cBytes = sizeof(OFSTRUCT);
658     ofs->nErrCode = 0;
659     if (mode & OF_REOPEN) name = ofs->szPathName;
660     dprintf_file( stddeb, "OpenFile: %s %04x\n", name, mode );
661
662     /* First allocate a task handle */
663
664     if ((hFileRet = FILE_AllocTaskHandle( NULL )) == HFILE_ERROR)
665     {
666         ofs->nErrCode = DOS_ExtendedError;
667         dprintf_file( stddeb, "OpenFile: no more task handles.\n" );
668         return HFILE_ERROR;
669     }
670
671     /* OF_PARSE simply fills the structure */
672
673     if (mode & OF_PARSE)
674     {
675         if (!(dosName = DOSFS_GetDosTrueName( name, FALSE ))) goto error;
676         lstrcpyn32A( ofs->szPathName, dosName, sizeof(ofs->szPathName) );
677         ofs->fFixedDisk = (GetDriveType16( dosName[0]-'A' ) != DRIVE_REMOVABLE);
678         dprintf_file( stddeb, "OpenFile(%s): OF_PARSE, res = '%s', %d\n",
679                       name, ofs->szPathName, hFileRet );
680         /* Return the handle, but close it first */
681         FILE_FreeTaskHandle( hFileRet );
682 /*        return hFileRet; */
683         return 0;  /* Progman seems to like this better */
684     }
685
686     /* OF_CREATE is completely different from all other options, so
687        handle it first */
688
689     if (mode & OF_CREATE)
690     {
691         if (!(file = FILE_Create( name, 0666, FALSE ))) goto error;
692         lstrcpyn32A( ofs->szPathName, DOSFS_GetDosTrueName( name, FALSE ),
693                      sizeof(ofs->szPathName) );
694         goto success;
695     }
696
697     /* Now look for the file */
698
699     /* First try the current directory */
700
701     lstrcpyn32A( ofs->szPathName, name, sizeof(ofs->szPathName) );
702     if ((unixName = DOSFS_GetUnixFileName( ofs->szPathName, TRUE )) != NULL)
703         goto found;
704
705     /* Now try some different paths if none was specified */
706
707     if ((mode & OF_SEARCH) && !(mode & OF_REOPEN))
708     {
709         if (name[1] == ':') name += 2;
710         if ((p = strrchr( name, '\\' ))) name = p + 1;
711         if ((p = strrchr( name, '/' ))) name = p + 1;
712         if (!name[0]) goto not_found;
713     }
714     else
715     {
716         if ((name[1] == ':') || strchr( name, '/' ) || strchr( name, '\\' ))
717             goto not_found;
718     }
719
720     if ((len = sizeof(ofs->szPathName) - strlen(name) - 1) < 0) goto not_found;
721
722     /* Try the Windows directory */
723
724     GetWindowsDirectory( ofs->szPathName, len );
725     strcat( ofs->szPathName, "\\" );
726     strcat( ofs->szPathName, name );
727     if ((unixName = DOSFS_GetUnixFileName( ofs->szPathName, TRUE )) != NULL)
728         goto found;
729
730     /* Try the Windows system directory */
731
732     GetSystemDirectory32A( ofs->szPathName, len );
733     strcat( ofs->szPathName, "\\" );
734     strcat( ofs->szPathName, name );
735     if ((unixName = DOSFS_GetUnixFileName( ofs->szPathName, TRUE )) != NULL)
736         goto found;
737
738     /* Try the path of the current executable */
739
740     if (GetCurrentTask())
741     {
742         GetModuleFileName( GetCurrentTask(), ofs->szPathName, len );
743         if ((p = strrchr( ofs->szPathName, '\\' )))
744         {
745             strcpy( p + 1, name );
746             if ((unixName = DOSFS_GetUnixFileName( ofs->szPathName, TRUE )))
747                 goto found;
748         }
749     }
750
751     /* Try all directories in path */
752
753     for (i = 0; ; i++)
754     {
755         if (!DIR_GetDosPath( i, ofs->szPathName, len )) goto not_found;
756         strcat( ofs->szPathName, "\\" );
757         strcat( ofs->szPathName, name );
758         if ((unixName = DOSFS_GetUnixFileName( ofs->szPathName, TRUE)) != NULL)
759             break;
760     }
761
762 found:
763     dprintf_file( stddeb, "OpenFile: found '%s'\n", unixName );
764     lstrcpyn32A(ofs->szPathName, DOSFS_GetDosTrueName( ofs->szPathName, FALSE),
765                 sizeof(ofs->szPathName) );
766
767     if (mode & OF_DELETE)
768     {
769         if (unlink( unixName ) == -1) goto not_found;
770         dprintf_file( stddeb, "OpenFile(%s): OF_DELETE return = OK\n", name);
771         /* Return the handle, but close it first */
772         FILE_FreeTaskHandle( hFileRet );
773         return hFileRet;
774     }
775
776     switch(mode & 3)
777     {
778     case OF_WRITE:
779         unixMode = O_WRONLY; break;
780     case OF_READWRITE:
781         unixMode = O_RDWR; break;
782     case OF_READ:
783     default:
784         unixMode = O_RDONLY; break;
785     }
786
787     if (!(file = FILE_OpenUnixFile( unixName, unixMode ))) goto not_found;
788     filedatetime[0] = file->filedate;
789     filedatetime[1] = file->filetime;
790     if ((mode & OF_VERIFY) && (mode & OF_REOPEN))
791     {
792         if (memcmp( ofs->reserved, filedatetime, sizeof(ofs->reserved) ))
793         {
794             FILE_Close( file );
795             dprintf_file( stddeb, "OpenFile(%s): OF_VERIFY failed\n", name );
796             /* FIXME: what error here? */
797             DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
798             goto error;
799         }
800     }
801     memcpy( ofs->reserved, filedatetime, sizeof(ofs->reserved) );
802
803     if (mode & OF_EXIST)
804     {
805         FILE_Close( file );
806         /* Return the handle, but close it first */
807         FILE_FreeTaskHandle( hFileRet );
808         return hFileRet;
809     }
810
811 success:  /* We get here if the open was successful */
812     dprintf_file( stddeb, "OpenFile(%s): OK, return = %d\n", name, hFileRet );
813     FILE_SetTaskHandle( hFileRet, file );
814     return hFileRet;
815
816 not_found:  /* We get here if the file does not exist */
817     dprintf_file( stddeb, "OpenFile: '%s' not found\n", name );
818     DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
819     /* fall through */
820
821 error:  /* We get here if there was an error opening the file */
822     ofs->nErrCode = DOS_ExtendedError;
823     dprintf_file( stddeb, "OpenFile(%s): return = HFILE_ERROR\n", name );
824     FILE_FreeTaskHandle( hFileRet );
825     return HFILE_ERROR;
826 }
827
828 /***********************************************************************
829  *           SearchPath32A   (KERNEL32.447)
830  * Code borrowed from OpenFile above.
831  */
832 DWORD SearchPath32A(
833         LPCSTR path,LPCSTR fn,LPCSTR ext,DWORD buflen,LPSTR buf,LPSTR *lastpart
834 ) {
835     LPCSTR      unixName;
836     INT32       len;
837     char        testpath[1000]; /* should be enough for now */
838     char        *name,*p;
839     int         i;
840
841     if (ext==NULL)
842         ext = "";
843     name=(char*)xmalloc(strlen(fn)+strlen(ext)+1);
844     strcpy(name,fn);
845     strcat(name,ext);
846
847     dprintf_file(stddeb,"SearchPath32A(%s,%s,%s,%ld,%p,%p)\n",
848         path,fn,ext,buflen,buf,lastpart
849     );
850     if (path) {
851         strcpy(testpath,path);
852         strcat(testpath,"\\");
853         strcat(testpath,name);
854         if ((unixName=DOSFS_GetUnixFileName((LPCSTR)testpath,TRUE))!=NULL) {
855             goto found;
856         } else
857             return 0;
858     }
859     if ((len=sizeof(testpath)-strlen(name)-1)<0)
860         return 0;
861
862     /* Try the path of the current executable */
863     if (GetCurrentTask()) {
864         GetModuleFileName(GetCurrentTask(),testpath,len);
865         if ((p=strrchr(testpath,'\\'))) {
866             strcpy(p+1,name);
867             if ((unixName=DOSFS_GetUnixFileName((LPCSTR)testpath,TRUE)))
868                 goto found;
869         }
870     }
871
872     /* Try the current directory */
873     lstrcpyn32A(testpath,name,sizeof(testpath) );
874     if ((unixName=DOSFS_GetUnixFileName((LPCSTR)testpath,TRUE))!=NULL)
875         goto found;
876
877     /* Try the Windows directory */
878     GetWindowsDirectory(testpath,len);
879     strcat(testpath,"\\");
880     strcat(testpath,name);
881     if ((unixName = DOSFS_GetUnixFileName((LPCSTR)testpath,TRUE))!=NULL)
882         goto found;
883
884     /* Try the Windows system directory */
885     GetSystemDirectory32A(testpath,len);
886     strcat(testpath,"\\");
887     strcat(testpath,name);
888     if ((unixName=DOSFS_GetUnixFileName((LPCSTR)testpath,TRUE))!=NULL)
889         goto found;
890
891     /* Try all directories in path */
892
893     for (i=0;;i++)
894     {
895         if (!DIR_GetDosPath(i,testpath,len)) 
896                 return 0;
897         strcat(testpath,"\\");
898         strcat(testpath,name);
899         if ((unixName=DOSFS_GetUnixFileName((LPCSTR)testpath,TRUE))!=NULL)
900             break;
901     }
902
903 found:
904     strncpy(buf,testpath,buflen);
905     if (NULL!=(p=strrchr(testpath,'\\')))
906         p=p+1;
907     else
908         p=testpath;
909     if (p-testpath<buflen)
910         *lastpart=(p-testpath)+buf;
911     else
912         *lastpart=NULL;
913     dprintf_file(stddeb,"       -> found %s,last part is %s\n",testpath,p);
914     return strlen(testpath);
915 }
916
917 /***********************************************************************
918  *           SearchPath32W   (KERNEL32.448)
919  */
920 DWORD SearchPath32W(
921         LPCWSTR path,LPCWSTR fn,LPCWSTR ext,DWORD buflen,LPWSTR buf,
922         LPWSTR *lastpart
923 ) {
924         LPSTR   pathA = path?STRING32_DupUniToAnsi(path):NULL;
925         LPSTR   fnA = STRING32_DupUniToAnsi(fn);
926         LPSTR   extA = ext?STRING32_DupUniToAnsi(fn):NULL;
927         LPSTR   lastpartA;
928         LPSTR   bufA = (char*)xmalloc(buflen+1);
929         DWORD   ret;
930
931         ret=SearchPath32A(pathA,fnA,extA,buflen,bufA,&lastpartA);
932         lstrcpynAtoW(buf,bufA,buflen);
933         if (lastpartA)
934                 *lastpart = buf+(lastpartA-bufA);
935         else
936                 *lastpart = NULL;
937         free(bufA);
938         free(fnA);
939         if (pathA) free(pathA);
940         if (extA) free(extA);
941         return ret;
942 }
943
944 /***********************************************************************
945  *           _lclose   (KERNEL.81) (KERNEL32.592)
946  */
947 HFILE _lclose( HFILE hFile )
948 {
949     DOS_FILE *file;
950
951     dprintf_file( stddeb, "_lclose: handle %d\n", hFile );
952     if (!(file = FILE_GetFile( hFile ))) return HFILE_ERROR;
953     FILE_Close( file );
954     FILE_FreeTaskHandle( hFile );
955     return 0;
956 }
957
958
959 /***********************************************************************
960  *           _lread   (KERNEL.82)
961  */
962 INT _lread( HFILE hFile, SEGPTR buffer, WORD count )
963 {
964     return (INT)_hread( hFile, buffer, (LONG)count );
965 }
966
967
968 /***********************************************************************
969  *           _lcreat   (KERNEL.83) (KERNEL32.593)
970  */
971 HFILE _lcreat( LPCSTR path, INT32 attr )
972 {
973     DOS_FILE *file;
974     HFILE handle;
975     int mode;
976     
977     dprintf_file( stddeb, "_lcreat: %s %02x\n", path, attr );
978     mode = (attr & 1) ? 0444 : 0666;
979     if (!(file = FILE_Create( path, mode, FALSE ))) return HFILE_ERROR;
980     if ((handle = FILE_AllocTaskHandle( file )) == HFILE_ERROR)
981         FILE_Close( file );
982     return handle;
983 }
984
985
986 /***********************************************************************
987  *           _lcreat_uniq   (Not a Windows API)
988  */
989 HFILE _lcreat_uniq( LPCSTR path, INT32 attr )
990 {
991     DOS_FILE *file;
992     HFILE handle;
993     int mode;
994     
995     dprintf_file( stddeb, "_lcreat: %s %02x\n", path, attr );
996     mode = (attr & 1) ? 0444 : 0666;
997     if (!(file = FILE_Create( path, mode, TRUE ))) return HFILE_ERROR;
998     if ((handle = FILE_AllocTaskHandle( file )) == HFILE_ERROR)
999         FILE_Close( file );
1000     return handle;
1001 }
1002
1003
1004 /***********************************************************************
1005  *           _llseek   (KERNEL.84)
1006  */
1007 LONG _llseek( HFILE hFile, LONG lOffset, INT nOrigin )
1008 {
1009     DOS_FILE *file;
1010     int origin, result;
1011
1012     dprintf_file( stddeb, "_llseek: handle %d, offset %ld, origin %d\n", 
1013                   hFile, lOffset, nOrigin);
1014
1015     if (!(file = FILE_GetFile( hFile ))) return HFILE_ERROR;
1016     switch(nOrigin)
1017     {
1018         case 1:  origin = SEEK_CUR; break;
1019         case 2:  origin = SEEK_END; break;
1020         default: origin = SEEK_SET; break;
1021     }
1022
1023     if ((result = lseek( file->unix_handle, lOffset, origin )) == -1)
1024         FILE_SetDosError();
1025     return result;
1026 }
1027
1028
1029 /***********************************************************************
1030  *           _lopen   (KERNEL.85) (KERNEL32.595)
1031  */
1032 HFILE _lopen( LPCSTR path, INT32 mode )
1033 {
1034     DOS_FILE *file;
1035     int unixMode;
1036     HFILE handle;
1037
1038     dprintf_file(stddeb, "_lopen('%s',%04x)\n", path, mode );
1039
1040     switch(mode & 3)
1041     {
1042     case OF_WRITE:
1043         unixMode = O_WRONLY | O_TRUNC;
1044         break;
1045     case OF_READWRITE:
1046         unixMode = O_RDWR;
1047         break;
1048     case OF_READ:
1049     default:
1050         unixMode = O_RDONLY;
1051         break;
1052     }
1053     if (!(file = FILE_Open( path, unixMode ))) return HFILE_ERROR;
1054     if ((handle = FILE_AllocTaskHandle( file )) == HFILE_ERROR)
1055         FILE_Close( file );
1056     return handle;
1057 }
1058
1059
1060 /***********************************************************************
1061  *           _lwrite   (KERNEL.86)
1062  */
1063 INT _lwrite( HFILE hFile, LPCSTR buffer, WORD count )
1064 {
1065     return (INT)_hwrite( hFile, buffer, (LONG)count );
1066 }
1067
1068
1069 /***********************************************************************
1070  *           _hread   (KERNEL.349)
1071  */
1072 LONG _hread( HFILE hFile, SEGPTR buffer, LONG count )
1073 {
1074 #ifndef WINELIB
1075     LONG maxlen;
1076
1077     dprintf_file( stddeb, "_hread: %d %08lx %ld\n",
1078                   hFile, (DWORD)buffer, count );
1079
1080     /* Some programs pass a count larger than the allocated buffer */
1081     maxlen = GetSelectorLimit( SELECTOROF(buffer) ) - OFFSETOF(buffer) + 1;
1082     if (count > maxlen) count = maxlen;
1083 #endif
1084     return FILE_Read( hFile, PTR_SEG_TO_LIN(buffer), count );
1085 }
1086
1087
1088 /***********************************************************************
1089  *           _hwrite   (KERNEL.350)
1090  */
1091 LONG _hwrite( HFILE hFile, LPCSTR buffer, LONG count )
1092 {
1093     DOS_FILE *file;
1094     LONG result;
1095
1096     dprintf_file( stddeb, "_hwrite: %d %p %ld\n", hFile, buffer, count );
1097
1098     if (!(file = FILE_GetFile( hFile ))) return HFILE_ERROR;
1099     
1100     if (count == 0)  /* Expand or truncate at current position */
1101         result = ftruncate( file->unix_handle,
1102                             lseek( file->unix_handle, 0, SEEK_CUR ) );
1103     else
1104         result = write( file->unix_handle, buffer, count );
1105
1106     if (result == -1) FILE_SetDosError();
1107     return result;
1108 }
1109
1110
1111 /***********************************************************************
1112  *           SetHandleCount   (KERNEL.199)
1113  */
1114 WORD SetHandleCount( WORD count )
1115 {
1116     HANDLE hPDB = GetCurrentPDB();
1117     PDB *pdb = (PDB *)GlobalLock16( hPDB );
1118     BYTE *files = PTR_SEG_TO_LIN( pdb->fileHandlesPtr );
1119     WORD i;
1120
1121     dprintf_file( stddeb, "SetHandleCount(%d)\n", count );
1122
1123     if (count < 20) count = 20;  /* No point in going below 20 */
1124     else if (count > 254) count = 254;
1125
1126     /* If shrinking the table, make sure all extra file handles are closed */
1127     if (count < pdb->nbFiles)
1128     {
1129         for (i = count; i < pdb->nbFiles; i++)
1130             if (files[i] != 0xff)  /* File open */
1131             {
1132                 DOS_ERROR( ER_TooManyOpenFiles, EC_ProgramError,
1133                            SA_Abort, EL_Disk );
1134                 return pdb->nbFiles;
1135             }
1136     }
1137
1138     if (count == 20)
1139     {
1140         if (pdb->nbFiles > 20)
1141         {
1142             memcpy( pdb->fileHandles, files, 20 );
1143 #ifdef WINELIB
1144             GlobalFree32( (HGLOBAL32)pdb->fileHandlesPtr );
1145             pdb->fileHandlesPtr = (SEGPTR)pdb->fileHandles;
1146 #else
1147             GlobalFree16( GlobalHandle16( SELECTOROF(pdb->fileHandlesPtr) ));
1148             pdb->fileHandlesPtr = (SEGPTR)MAKELONG( 0x18,
1149                                                    GlobalHandleToSel( hPDB ) );
1150 #endif
1151             pdb->nbFiles = 20;
1152         }
1153     }
1154     else  /* More than 20, need a new file handles table */
1155     {
1156         BYTE *newfiles;
1157 #ifdef WINELIB
1158         newfiles = (BYTE *)GlobalAlloc32( GMEM_FIXED, count );
1159 #else
1160         HANDLE newhandle = GlobalAlloc16( GMEM_MOVEABLE, count );
1161         if (!newhandle)
1162         {
1163             DOS_ERROR( ER_OutOfMemory, EC_OutOfResource, SA_Abort, EL_Memory );
1164             return pdb->nbFiles;
1165         }
1166         newfiles = (BYTE *)GlobalLock16( newhandle );
1167 #endif  /* WINELIB */
1168         if (count > pdb->nbFiles)
1169         {
1170             memcpy( newfiles, files, pdb->nbFiles );
1171             memset( newfiles + pdb->nbFiles, 0xff, count - pdb->nbFiles );
1172         }
1173         else memcpy( newfiles, files, count );
1174 #ifdef WINELIB
1175         if (pdb->nbFiles > 20) GlobalFree32( (HGLOBAL32)pdb->fileHandlesPtr );
1176         pdb->fileHandlesPtr = (SEGPTR)newfiles;
1177 #else
1178         if (pdb->nbFiles > 20)
1179             GlobalFree16( GlobalHandle16( SELECTOROF(pdb->fileHandlesPtr) ));
1180         pdb->fileHandlesPtr = WIN16_GlobalLock16( newhandle );
1181 #endif  /* WINELIB */
1182         pdb->nbFiles = count;
1183     }
1184     return pdb->nbFiles;
1185 }
1186
1187
1188 /***********************************************************************
1189  *           FlushFileBuffers   (KERNEL32.133)
1190  */
1191 BOOL32 FlushFileBuffers( HFILE hFile )
1192 {
1193     DOS_FILE *file;
1194
1195     dprintf_file( stddeb, "FlushFileBuffers(%d)\n", hFile );
1196     if (!(file = FILE_GetFile( hFile ))) return FALSE;
1197     if (fsync( file->unix_handle ) != -1) return TRUE;
1198     FILE_SetDosError();
1199     return FALSE;
1200 }
1201
1202
1203 /***********************************************************************
1204  *           DeleteFile16   (KERNEL.146)
1205  */
1206 BOOL16 DeleteFile16( LPCSTR path )
1207 {
1208     return DeleteFile32A( path );
1209 }
1210
1211
1212 /***********************************************************************
1213  *           DeleteFile32A   (KERNEL32.71)
1214  */
1215 BOOL32 DeleteFile32A( LPCSTR path )
1216 {
1217     const char *unixName;
1218
1219     dprintf_file(stddeb, "DeleteFile: '%s'\n", path );
1220
1221     if ((unixName = DOSFS_IsDevice( path )) != NULL)
1222     {
1223         dprintf_file(stddeb, "DeleteFile: removing device '%s'!\n", unixName);
1224         DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
1225         return FALSE;
1226     }
1227
1228     if (!(unixName = DOSFS_GetUnixFileName( path, TRUE ))) return FALSE;
1229     if (unlink( unixName ) == -1)
1230     {
1231         FILE_SetDosError();
1232         return FALSE;
1233     }
1234     return TRUE;
1235 }
1236
1237
1238 /***********************************************************************
1239  *           DeleteFile32W   (KERNEL32.72)
1240  */
1241 BOOL32 DeleteFile32W( LPCWSTR path )
1242 {
1243     LPSTR xpath = STRING32_DupUniToAnsi(path);
1244     BOOL32 ret = RemoveDirectory32A( xpath );
1245     free(xpath);
1246     return ret;
1247 }
1248
1249
1250 /***********************************************************************
1251  *           CreateDirectory16   (KERNEL.144)
1252  */
1253 BOOL16 CreateDirectory16( LPCSTR path, LPVOID dummy )
1254 {
1255     dprintf_file( stddeb,"CreateDirectory16(%s,%p)\n", path, dummy );
1256     return (BOOL16)CreateDirectory32A( path, NULL );
1257 }
1258
1259
1260 /***********************************************************************
1261  *           CreateDirectory32A   (KERNEL32.39)
1262  */
1263 BOOL32 CreateDirectory32A( LPCSTR path, LPSECURITY_ATTRIBUTES lpsecattribs )
1264 {
1265     const char *unixName;
1266
1267     dprintf_file( stddeb, "CreateDirectory32A(%s,%p)\n", path, lpsecattribs );
1268     if ((unixName = DOSFS_IsDevice( path )) != NULL)
1269     {
1270         dprintf_file(stddeb, "CreateDirectory: device '%s'!\n", unixName);
1271         DOS_ERROR( ER_AccessDenied, EC_AccessDenied, SA_Abort, EL_Disk );
1272         return FALSE;
1273     }
1274     if (!(unixName = DOSFS_GetUnixFileName( path, FALSE ))) return 0;
1275     if ((mkdir( unixName, 0777 ) == -1) && (errno != EEXIST))
1276     {
1277         FILE_SetDosError();
1278         return FALSE;
1279     }
1280     return TRUE;
1281 }
1282
1283
1284 /***********************************************************************
1285  *           CreateDirectory32W   (KERNEL32.42)
1286  */
1287 BOOL32 CreateDirectory32W( LPCWSTR path, LPSECURITY_ATTRIBUTES lpsecattribs )
1288 {
1289     LPSTR xpath = STRING32_DupUniToAnsi(path);
1290     BOOL32 ret = CreateDirectory32A(xpath,lpsecattribs);
1291     free(xpath);
1292     return ret;
1293 }
1294
1295
1296 /***********************************************************************
1297  *           RemoveDirectory16   (KERNEL)
1298  */
1299 BOOL16 RemoveDirectory16( LPCSTR path )
1300 {
1301     return (BOOL16)RemoveDirectory32A( path );
1302 }
1303
1304
1305 /***********************************************************************
1306  *           RemoveDirectory32A   (KERNEL32.437)
1307  */
1308 BOOL32 RemoveDirectory32A( LPCSTR path )
1309 {
1310     const char *unixName;
1311
1312     dprintf_file(stddeb, "RemoveDirectory: '%s'\n", path );
1313
1314     if ((unixName = DOSFS_IsDevice( path )) != NULL)
1315     {
1316         dprintf_file(stddeb, "RemoveDirectory: device '%s'!\n", unixName);
1317         DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
1318         return FALSE;
1319     }
1320     if (!(unixName = DOSFS_GetUnixFileName( path, TRUE ))) return FALSE;
1321     if (rmdir( unixName ) == -1)
1322     {
1323         FILE_SetDosError();
1324         return FALSE;
1325     }
1326     return TRUE;
1327 }
1328
1329
1330 /***********************************************************************
1331  *           RemoveDirectory32W   (KERNEL32.438)
1332  */
1333 BOOL32 RemoveDirectory32W( LPCWSTR path )
1334 {
1335     LPSTR xpath = STRING32_DupUniToAnsi(path);
1336     BOOL32 ret = RemoveDirectory32A( xpath );
1337     free(xpath);
1338     return ret;
1339 }