Added a winebuild man page.
[wine] / files / dos_fs.c
1 /*
2  * DOS file system functions
3  *
4  * Copyright 1993 Erik Bos
5  * Copyright 1996 Alexandre Julliard
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23
24 #include <sys/types.h>
25 #include <ctype.h>
26 #include <dirent.h>
27 #include <errno.h>
28 #ifdef HAVE_SYS_ERRNO_H
29 #include <sys/errno.h>
30 #endif
31 #include <fcntl.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <sys/stat.h>
35 #ifdef HAVE_SYS_IOCTL_H
36 #include <sys/ioctl.h>
37 #endif
38 #include <time.h>
39 #include <unistd.h>
40
41 #include "windef.h"
42 #include "winerror.h"
43 #include "wingdi.h"
44
45 #include "wine/unicode.h"
46 #include "wine/winbase16.h"
47 #include "drive.h"
48 #include "file.h"
49 #include "heap.h"
50 #include "msdos.h"
51 #include "ntddk.h"
52 #include "wine/server.h"
53 #include "msvcrt/excpt.h"
54
55 #include "smb.h"
56
57 #include "wine/debug.h"
58
59 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
60 WINE_DECLARE_DEBUG_CHANNEL(file);
61
62 /* Define the VFAT ioctl to get both short and long file names */
63 /* FIXME: is it possible to get this to work on other systems? */
64 #ifdef linux
65 /* We want the real kernel dirent structure, not the libc one */
66 typedef struct
67 {
68     long d_ino;
69     long d_off;
70     unsigned short d_reclen;
71     char d_name[256];
72 } KERNEL_DIRENT;
73
74 #define VFAT_IOCTL_READDIR_BOTH  _IOR('r', 1, KERNEL_DIRENT [2] )
75
76 #else   /* linux */
77 #undef VFAT_IOCTL_READDIR_BOTH  /* just in case... */
78 #endif  /* linux */
79
80 /* Chars we don't want to see in DOS file names */
81 #define INVALID_DOS_CHARS  "*?<>|\"+=,;[] \345"
82
83 static const DOS_DEVICE DOSFS_Devices[] =
84 /* name, device flags (see Int 21/AX=0x4400) */
85 {
86     { "CON",            0xc0d3 },
87     { "PRN",            0xa0c0 },
88     { "NUL",            0x80c4 },
89     { "AUX",            0x80c0 },
90     { "LPT1",           0xa0c0 },
91     { "LPT2",           0xa0c0 },
92     { "LPT3",           0xa0c0 },
93     { "LPT4",           0xc0d3 },
94     { "COM1",           0x80c0 },
95     { "COM2",           0x80c0 },
96     { "COM3",           0x80c0 },
97     { "COM4",           0x80c0 },
98     { "SCSIMGR$",       0xc0c0 },
99     { "HPSCAN",         0xc0c0 },
100     { "EMMXXXX0",       0x0000 }
101 };
102
103 #define GET_DRIVE(path) \
104     (((path)[1] == ':') ? FILE_toupper((path)[0]) - 'A' : DOSFS_CurDrive)
105
106 /* Directory info for DOSFS_ReadDir */
107 typedef struct
108 {
109     DIR           *dir;
110 #ifdef VFAT_IOCTL_READDIR_BOTH
111     int            fd;
112     char           short_name[12];
113     KERNEL_DIRENT  dirent[2];
114 #endif
115 } DOS_DIR;
116
117 /* Info structure for FindFirstFile handle */
118 typedef struct
119 {
120     LPSTR path;
121     LPSTR long_mask;
122     LPSTR short_mask;
123     BYTE  attr;
124     int   drive;
125     int   cur_pos;
126     union
127     {
128         DOS_DIR *dos_dir;
129         SMB_DIR *smb_dir;
130     } u;
131 } FIND_FIRST_INFO;
132
133
134 static WINE_EXCEPTION_FILTER(page_fault)
135 {
136     if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
137         return EXCEPTION_EXECUTE_HANDLER;
138     return EXCEPTION_CONTINUE_SEARCH;
139 }
140
141
142 /***********************************************************************
143  *           DOSFS_ValidDOSName
144  *
145  * Return 1 if Unix file 'name' is also a valid MS-DOS name
146  * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
147  * File name can be terminated by '\0', '\\' or '/'.
148  */
149 static int DOSFS_ValidDOSName( const char *name, int ignore_case )
150 {
151     static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
152     const char *p = name;
153     const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
154     int len = 0;
155
156     if (*p == '.')
157     {
158         /* Check for "." and ".." */
159         p++;
160         if (*p == '.') p++;
161         /* All other names beginning with '.' are invalid */
162         return (IS_END_OF_NAME(*p));
163     }
164     while (!IS_END_OF_NAME(*p))
165     {
166         if (strchr( invalid, *p )) return 0;  /* Invalid char */
167         if (*p == '.') break;  /* Start of the extension */
168         if (++len > 8) return 0;  /* Name too long */
169         p++;
170     }
171     if (*p != '.') return 1;  /* End of name */
172     p++;
173     if (IS_END_OF_NAME(*p)) return 0;  /* Empty extension not allowed */
174     len = 0;
175     while (!IS_END_OF_NAME(*p))
176     {
177         if (strchr( invalid, *p )) return 0;  /* Invalid char */
178         if (*p == '.') return 0;  /* Second extension not allowed */
179         if (++len > 3) return 0;  /* Extension too long */
180         p++;
181     }
182     return 1;
183 }
184
185
186 /***********************************************************************
187  *           DOSFS_ToDosFCBFormat
188  *
189  * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
190  * expanding wild cards and converting to upper-case in the process.
191  * File name can be terminated by '\0', '\\' or '/'.
192  * Return FALSE if the name is not a valid DOS name.
193  * 'buffer' must be at least 12 characters long.
194  */
195 BOOL DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
196 {
197     static const char invalid_chars[] = INVALID_DOS_CHARS;
198     const char *p = name;
199     int i;
200
201     /* Check for "." and ".." */
202     if (*p == '.')
203     {
204         p++;
205         strcpy( buffer, ".          " );
206         if (*p == '.')
207         {
208             buffer[1] = '.';
209             p++;
210         }
211         return (!*p || (*p == '/') || (*p == '\\'));
212     }
213
214     for (i = 0; i < 8; i++)
215     {
216         switch(*p)
217         {
218         case '\0':
219         case '\\':
220         case '/':
221         case '.':
222             buffer[i] = ' ';
223             break;
224         case '?':
225             p++;
226             /* fall through */
227         case '*':
228             buffer[i] = '?';
229             break;
230         default:
231             if (strchr( invalid_chars, *p )) return FALSE;
232             buffer[i] = FILE_toupper(*p);
233             p++;
234             break;
235         }
236     }
237
238     if (*p == '*')
239     {
240         /* Skip all chars after wildcard up to first dot */
241         while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
242     }
243     else
244     {
245         /* Check if name too long */
246         if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
247     }
248     if (*p == '.') p++;  /* Skip dot */
249
250     for (i = 8; i < 11; i++)
251     {
252         switch(*p)
253         {
254         case '\0':
255         case '\\':
256         case '/':
257             buffer[i] = ' ';
258             break;
259         case '.':
260             return FALSE;  /* Second extension not allowed */
261         case '?':
262             p++;
263             /* fall through */
264         case '*':
265             buffer[i] = '?';
266             break;
267         default:
268             if (strchr( invalid_chars, *p )) return FALSE;
269             buffer[i] = FILE_toupper(*p);
270             p++;
271             break;
272         }
273     }
274     buffer[11] = '\0';
275
276     /* at most 3 character of the extension are processed
277      * is something behind this ?
278      */
279     while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
280     return IS_END_OF_NAME(*p);
281 }
282
283
284 /***********************************************************************
285  *           DOSFS_ToDosDTAFormat
286  *
287  * Convert a file name from FCB to DTA format (name.ext, null-terminated)
288  * converting to upper-case in the process.
289  * File name can be terminated by '\0', '\\' or '/'.
290  * 'buffer' must be at least 13 characters long.
291  */
292 static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
293 {
294     char *p;
295
296     memcpy( buffer, name, 8 );
297     p = buffer + 8;
298     while ((p > buffer) && (p[-1] == ' ')) p--;
299     *p++ = '.';
300     memcpy( p, name + 8, 3 );
301     p += 3;
302     while (p[-1] == ' ') p--;
303     if (p[-1] == '.') p--;
304     *p = '\0';
305 }
306
307
308 /***********************************************************************
309  *           DOSFS_MatchShort
310  *
311  * Check a DOS file name against a mask (both in FCB format).
312  */
313 static int DOSFS_MatchShort( const char *mask, const char *name )
314 {
315     int i;
316     for (i = 11; i > 0; i--, mask++, name++)
317         if ((*mask != '?') && (*mask != *name)) return 0;
318     return 1;
319 }
320
321
322 /***********************************************************************
323  *           DOSFS_MatchLong
324  *
325  * Check a long file name against a mask.
326  *
327  * Tests (done in W95 DOS shell - case insensitive):
328  * *.txt                        test1.test.txt                          *
329  * *st1*                        test1.txt                               *
330  * *.t??????.t*                 test1.ta.tornado.txt                    *
331  * *tornado*                    test1.ta.tornado.txt                    *
332  * t*t                          test1.ta.tornado.txt                    *
333  * ?est*                        test1.txt                               *
334  * ?est???                      test1.txt                               -
335  * *test1.txt*                  test1.txt                               *
336  * h?l?o*t.dat                  hellothisisatest.dat                    *
337  */
338 static int DOSFS_MatchLong( const char *mask, const char *name,
339                             int case_sensitive )
340 {
341     const char *lastjoker = NULL;
342     const char *next_to_retry = NULL;
343
344     if (!strcmp( mask, "*.*" )) return 1;
345     while (*name && *mask)
346     {
347         if (*mask == '*')
348         {
349             mask++;
350             while (*mask == '*') mask++;  /* Skip consecutive '*' */
351             lastjoker = mask;
352             if (!*mask) return 1; /* end of mask is all '*', so match */
353
354             /* skip to the next match after the joker(s) */
355             if (case_sensitive) while (*name && (*name != *mask)) name++;
356             else while (*name && (FILE_toupper(*name) != FILE_toupper(*mask))) name++;
357
358             if (!*name) break;
359             next_to_retry = name;
360         }
361         else if (*mask != '?')
362         {
363             int mismatch = 0;
364             if (case_sensitive)
365             {
366                 if (*mask != *name) mismatch = 1;
367             }
368             else
369             {
370                 if (FILE_toupper(*mask) != FILE_toupper(*name)) mismatch = 1;
371             }
372             if (!mismatch)
373             {
374                 mask++;
375                 name++;
376                 if (*mask == '\0')
377                 {
378                     if (*name == '\0')
379                         return 1;
380                     if (lastjoker)
381                         mask = lastjoker;
382                 }
383             }
384             else /* mismatch ! */
385             {
386                 if (lastjoker) /* we had an '*', so we can try unlimitedly */
387                 {
388                     mask = lastjoker;
389
390                     /* this scan sequence was a mismatch, so restart
391                      * 1 char after the first char we checked last time */
392                     next_to_retry++;
393                     name = next_to_retry;
394                 }
395                 else
396                     return 0; /* bad luck */
397             }
398         }
399         else /* '?' */
400         {
401             mask++;
402             name++;
403         }
404     }
405     while ((*mask == '.') || (*mask == '*'))
406         mask++;  /* Ignore trailing '.' or '*' in mask */
407     return (!*name && !*mask);
408 }
409
410
411 /***********************************************************************
412  *           DOSFS_OpenDir
413  */
414 static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
415 {
416     DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) );
417     if (!dir)
418     {
419         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
420         return NULL;
421     }
422
423     /* Treat empty path as root directory. This simplifies path split into
424        directory and mask in several other places */
425     if (!*path) path = "/";
426
427 #ifdef VFAT_IOCTL_READDIR_BOTH
428
429     /* Check if the VFAT ioctl is supported on this directory */
430
431     if ((dir->fd = open( path, O_RDONLY )) != -1)
432     {
433         if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
434         {
435             close( dir->fd );
436             dir->fd = -1;
437         }
438         else
439         {
440             /* Set the file pointer back at the start of the directory */
441             lseek( dir->fd, 0, SEEK_SET );
442             dir->dir = NULL;
443             return dir;
444         }
445     }
446 #endif  /* VFAT_IOCTL_READDIR_BOTH */
447
448     /* Now use the standard opendir/readdir interface */
449
450     if (!(dir->dir = opendir( path )))
451     {
452         HeapFree( GetProcessHeap(), 0, dir );
453         return NULL;
454     }
455     return dir;
456 }
457
458
459 /***********************************************************************
460  *           DOSFS_CloseDir
461  */
462 static void DOSFS_CloseDir( DOS_DIR *dir )
463 {
464 #ifdef VFAT_IOCTL_READDIR_BOTH
465     if (dir->fd != -1) close( dir->fd );
466 #endif  /* VFAT_IOCTL_READDIR_BOTH */
467     if (dir->dir) closedir( dir->dir );
468     HeapFree( GetProcessHeap(), 0, dir );
469 }
470
471
472 /***********************************************************************
473  *           DOSFS_ReadDir
474  */
475 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
476                              LPCSTR *short_name )
477 {
478     struct dirent *dirent;
479
480 #ifdef VFAT_IOCTL_READDIR_BOTH
481     if (dir->fd != -1)
482     {
483         if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
484             if (!dir->dirent[0].d_reclen) return FALSE;
485             if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
486                 dir->short_name[0] = '\0';
487             *short_name = dir->short_name;
488             if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
489             else *long_name = dir->dirent[0].d_name;
490             return TRUE;
491         }
492     }
493 #endif  /* VFAT_IOCTL_READDIR_BOTH */
494
495     if (!(dirent = readdir( dir->dir ))) return FALSE;
496     *long_name  = dirent->d_name;
497     *short_name = NULL;
498     return TRUE;
499 }
500
501
502 /***********************************************************************
503  *           DOSFS_Hash
504  *
505  * Transform a Unix file name into a hashed DOS name. If the name is a valid
506  * DOS name, it is converted to upper-case; otherwise it is replaced by a
507  * hashed version that fits in 8.3 format.
508  * File name can be terminated by '\0', '\\' or '/'.
509  * 'buffer' must be at least 13 characters long.
510  */
511 static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format,
512                         BOOL ignore_case )
513 {
514     static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
515     static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
516
517     const char *p, *ext;
518     char *dst;
519     unsigned short hash;
520     int i;
521
522     if (dir_format) strcpy( buffer, "           " );
523
524     if (DOSFS_ValidDOSName( name, ignore_case ))
525     {
526         /* Check for '.' and '..' */
527         if (*name == '.')
528         {
529             buffer[0] = '.';
530             if (!dir_format) buffer[1] = buffer[2] = '\0';
531             if (name[1] == '.') buffer[1] = '.';
532             return;
533         }
534
535         /* Simply copy the name, converting to uppercase */
536
537         for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
538             *dst++ = FILE_toupper(*name);
539         if (*name == '.')
540         {
541             if (dir_format) dst = buffer + 8;
542             else *dst++ = '.';
543             for (name++; !IS_END_OF_NAME(*name); name++)
544                 *dst++ = FILE_toupper(*name);
545         }
546         if (!dir_format) *dst = '\0';
547         return;
548     }
549
550     /* Compute the hash code of the file name */
551     /* If you know something about hash functions, feel free to */
552     /* insert a better algorithm here... */
553     if (ignore_case)
554     {
555         for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
556             hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p) ^ (FILE_tolower(p[1]) << 8);
557         hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p); /* Last character*/
558     }
559     else
560     {
561         for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
562             hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
563         hash = (hash << 3) ^ (hash >> 5) ^ *p;  /* Last character */
564     }
565
566     /* Find last dot for start of the extension */
567     for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
568         if (*p == '.') ext = p;
569     if (ext && IS_END_OF_NAME(ext[1]))
570         ext = NULL;  /* Empty extension ignored */
571
572     /* Copy first 4 chars, replacing invalid chars with '_' */
573     for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
574     {
575         if (IS_END_OF_NAME(*p) || (p == ext)) break;
576         *dst++ = strchr( invalid_chars, *p ) ? '_' : FILE_toupper(*p);
577     }
578     /* Pad to 5 chars with '~' */
579     while (i-- >= 0) *dst++ = '~';
580
581     /* Insert hash code converted to 3 ASCII chars */
582     *dst++ = hash_chars[(hash >> 10) & 0x1f];
583     *dst++ = hash_chars[(hash >> 5) & 0x1f];
584     *dst++ = hash_chars[hash & 0x1f];
585
586     /* Copy the first 3 chars of the extension (if any) */
587     if (ext)
588     {
589         if (!dir_format) *dst++ = '.';
590         for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
591             *dst++ = strchr( invalid_chars, *ext ) ? '_' : FILE_toupper(*ext);
592     }
593     if (!dir_format) *dst = '\0';
594 }
595
596
597 /***********************************************************************
598  *           DOSFS_FindUnixName
599  *
600  * Find the Unix file name in a given directory that corresponds to
601  * a file name (either in Unix or DOS format).
602  * File name can be terminated by '\0', '\\' or '/'.
603  * Return TRUE if OK, FALSE if no file name matches.
604  *
605  * 'long_buf' must be at least 'long_len' characters long. If the long name
606  * turns out to be larger than that, the function returns FALSE.
607  * 'short_buf' must be at least 13 characters long.
608  */
609 BOOL DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
610                            INT long_len, LPSTR short_buf, BOOL ignore_case)
611 {
612     DOS_DIR *dir;
613     LPCSTR long_name, short_name;
614     char dos_name[12], tmp_buf[13];
615     BOOL ret;
616
617     const char *p = strchr( name, '/' );
618     int len = p ? (int)(p - name) : strlen(name);
619     if ((p = strchr( name, '\\' ))) len = min( (int)(p - name), len );
620     /* Ignore trailing dots and spaces */
621     while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
622     if (long_len < len + 1) return FALSE;
623
624     TRACE("%s,%s\n", path, name );
625
626     if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
627
628     if (!(dir = DOSFS_OpenDir( path )))
629     {
630         WARN("(%s,%s): can't open dir: %s\n",
631                        path, name, strerror(errno) );
632         return FALSE;
633     }
634
635     while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
636     {
637         /* Check against Unix name */
638         if (len == strlen(long_name))
639         {
640             if (!ignore_case)
641             {
642                 if (!strncmp( long_name, name, len )) break;
643             }
644             else
645             {
646                 if (!FILE_strncasecmp( long_name, name, len )) break;
647             }
648         }
649         if (dos_name[0])
650         {
651             /* Check against hashed DOS name */
652             if (!short_name)
653             {
654                 DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
655                 short_name = tmp_buf;
656             }
657             if (!strcmp( dos_name, short_name )) break;
658         }
659     }
660     if (ret)
661     {
662         if (long_buf) strcpy( long_buf, long_name );
663         if (short_buf)
664         {
665             if (short_name)
666                 DOSFS_ToDosDTAFormat( short_name, short_buf );
667             else
668                 DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
669         }
670         TRACE("(%s,%s) -> %s (%s)\n",
671               path, name, long_name, short_buf ? short_buf : "***");
672     }
673     else
674         WARN("'%s' not found in '%s'\n", name, path);
675     DOSFS_CloseDir( dir );
676     return ret;
677 }
678
679
680 /***********************************************************************
681  *           DOSFS_GetDevice
682  *
683  * Check if a DOS file name represents a DOS device and return the device.
684  */
685 const DOS_DEVICE *DOSFS_GetDevice( const char *name )
686 {
687     int i;
688     const char *p;
689
690     if (!name) return NULL; /* if FILE_DupUnixHandle was used */
691     if (name[0] && (name[1] == ':')) name += 2;
692     if ((p = strrchr( name, '/' ))) name = p + 1;
693     if ((p = strrchr( name, '\\' ))) name = p + 1;
694     for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
695     {
696         const char *dev = DOSFS_Devices[i].name;
697         if (!FILE_strncasecmp( dev, name, strlen(dev) ))
698         {
699             p = name + strlen( dev );
700             if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
701         }
702     }
703     return NULL;
704 }
705
706
707 /***********************************************************************
708  *           DOSFS_GetDeviceByHandle
709  */
710 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HFILE hFile )
711 {
712     const DOS_DEVICE *ret = NULL;
713     SERVER_START_REQ( get_file_info )
714     {
715         req->handle = hFile;
716         if (!wine_server_call( req ) && (reply->type == FILE_TYPE_UNKNOWN))
717         {
718             if ((reply->attr >= 0) &&
719                 (reply->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
720                 ret = &DOSFS_Devices[reply->attr];
721         }
722     }
723     SERVER_END_REQ;
724     return ret;
725 }
726
727
728 /**************************************************************************
729  *         DOSFS_CreateCommPort
730  */
731 static HANDLE DOSFS_CreateCommPort(LPCSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
732 {
733     HANDLE ret;
734     char devname[40];
735
736     TRACE_(file)("%s %lx %lx\n", name, access, attributes);
737
738     PROFILE_GetWineIniString("serialports",name,"",devname,sizeof devname);
739     if(!devname[0])
740         return 0;
741
742     TRACE("opening %s as %s\n", devname, name);
743
744     SERVER_START_REQ( create_serial )
745     {
746         req->access  = access;
747         req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
748         req->attributes = attributes;
749         req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
750         wine_server_add_data( req, devname, strlen(devname) );
751         SetLastError(0);
752         wine_server_call_err( req );
753         ret = reply->handle;
754     }
755     SERVER_END_REQ;
756
757     if(!ret)
758         ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
759     else
760         TRACE("return %08X\n", ret );
761     return ret;
762 }
763
764 /***********************************************************************
765  *           DOSFS_OpenDevice
766  *
767  * Open a DOS device. This might not map 1:1 into the UNIX device concept.
768  * Returns 0 on failure.
769  */
770 HANDLE DOSFS_OpenDevice( const char *name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
771 {
772     int i;
773     const char *p;
774     HANDLE handle;
775
776     if (name[0] && (name[1] == ':')) name += 2;
777     if ((p = strrchr( name, '/' ))) name = p + 1;
778     if ((p = strrchr( name, '\\' ))) name = p + 1;
779     for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
780     {
781         const char *dev = DOSFS_Devices[i].name;
782         if (!FILE_strncasecmp( dev, name, strlen(dev) ))
783         {
784             p = name + strlen( dev );
785             if (!*p || (*p == '.') || (*p == ':')) {
786                 /* got it */
787                 if (!strcmp(DOSFS_Devices[i].name,"NUL"))
788                     return FILE_CreateFile( "/dev/null", access,
789                                             FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
790                                             OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
791                 if (!strcmp(DOSFS_Devices[i].name,"CON")) {
792                         HANDLE to_dup;
793                         switch (access & (GENERIC_READ|GENERIC_WRITE)) {
794                         case GENERIC_READ:
795                                 to_dup = GetStdHandle( STD_INPUT_HANDLE );
796                                 break;
797                         case GENERIC_WRITE:
798                                 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
799                                 break;
800                         default:
801                                 FIXME("can't open CON read/write\n");
802                                 return 0;
803                         }
804                         if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
805                                               &handle, 0,
806                                               sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
807                                               DUPLICATE_SAME_ACCESS ))
808                             handle = 0;
809                         return handle;
810                 }
811                 if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") ||
812                     !strcmp(DOSFS_Devices[i].name,"HPSCAN") ||
813                     !strcmp(DOSFS_Devices[i].name,"EMMXXXX0"))
814                 {
815                     return FILE_CreateDevice( i, access, sa );
816                 }
817
818                 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
819                     return handle;
820                 FIXME("device open %s not supported (yet)\n",DOSFS_Devices[i].name);
821                 return 0;
822             }
823         }
824     }
825     return 0;
826 }
827
828
829 /***********************************************************************
830  *           DOSFS_GetPathDrive
831  *
832  * Get the drive specified by a given path name (DOS or Unix format).
833  */
834 static int DOSFS_GetPathDrive( const char **name )
835 {
836     int drive;
837     const char *p = *name;
838
839     if (*p && (p[1] == ':'))
840     {
841         drive = FILE_toupper(*p) - 'A';
842         *name += 2;
843     }
844     else if (*p == '/') /* Absolute Unix path? */
845     {
846         if ((drive = DRIVE_FindDriveRoot( name )) == -1)
847         {
848             MESSAGE("Warning: %s not accessible from a configured DOS drive\n", *name );
849             /* Assume it really was a DOS name */
850             drive = DRIVE_GetCurrentDrive();
851         }
852     }
853     else drive = DRIVE_GetCurrentDrive();
854
855     if (!DRIVE_IsValid(drive))
856     {
857         SetLastError( ERROR_INVALID_DRIVE );
858         return -1;
859     }
860     return drive;
861 }
862
863
864 /***********************************************************************
865  *           DOSFS_GetFullName
866  *
867  * Convert a file name (DOS or mixed DOS/Unix format) to a valid
868  * Unix name / short DOS name pair.
869  * Return FALSE if one of the path components does not exist. The last path
870  * component is only checked if 'check_last' is non-zero.
871  * The buffers pointed to by 'long_buf' and 'short_buf' must be
872  * at least MAX_PATHNAME_LEN long.
873  */
874 BOOL DOSFS_GetFullName( LPCSTR name, BOOL check_last, DOS_FULL_NAME *full )
875 {
876     BOOL found;
877     UINT flags;
878     char *p_l, *p_s, *root;
879
880     TRACE("%s (last=%d)\n", name, check_last );
881
882     if ((!*name) || (*name=='\n'))
883     { /* error code for Win98 */
884         SetLastError(ERROR_BAD_PATHNAME);
885         return FALSE;
886     }
887
888     if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
889     flags = DRIVE_GetFlags( full->drive );
890
891     lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
892                  sizeof(full->long_name) );
893     if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
894     else root = full->long_name;  /* root directory */
895
896     strcpy( full->short_name, "A:\\" );
897     full->short_name[0] += full->drive;
898
899     if ((*name == '\\') || (*name == '/'))  /* Absolute path */
900     {
901         while ((*name == '\\') || (*name == '/')) name++;
902     }
903     else  /* Relative path */
904     {
905         lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
906                      sizeof(full->long_name) - (root - full->long_name) - 1 );
907         if (root[1]) *root = '/';
908         lstrcpynA( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
909                      sizeof(full->short_name) - 3 );
910     }
911
912     p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
913                              : full->long_name;
914     p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
915                               : full->short_name + 2;
916     found = TRUE;
917
918     while (*name && found)
919     {
920         /* Check for '.' and '..' */
921
922         if (*name == '.')
923         {
924             if (IS_END_OF_NAME(name[1]))
925             {
926                 name++;
927                 while ((*name == '\\') || (*name == '/')) name++;
928                 continue;
929             }
930             else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
931             {
932                 name += 2;
933                 while ((*name == '\\') || (*name == '/')) name++;
934                 while ((p_l > root) && (*p_l != '/')) p_l--;
935                 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
936                 *p_l = *p_s = '\0';  /* Remove trailing separator */
937                 continue;
938             }
939         }
940
941         /* Make sure buffers are large enough */
942
943         if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
944             (p_l >= full->long_name + sizeof(full->long_name) - 1))
945         {
946             SetLastError( ERROR_PATH_NOT_FOUND );
947             return FALSE;
948         }
949
950         /* Get the long and short name matching the file name */
951
952         if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
953                          sizeof(full->long_name) - (p_l - full->long_name) - 1,
954                          p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
955         {
956             *p_l++ = '/';
957             p_l   += strlen(p_l);
958             *p_s++ = '\\';
959             p_s   += strlen(p_s);
960             while (!IS_END_OF_NAME(*name)) name++;
961         }
962         else if (!check_last)
963         {
964             *p_l++ = '/';
965             *p_s++ = '\\';
966             while (!IS_END_OF_NAME(*name) &&
967                    (p_s < full->short_name + sizeof(full->short_name) - 1) &&
968                    (p_l < full->long_name + sizeof(full->long_name) - 1))
969             {
970                 *p_s++ = FILE_tolower(*name);
971                 /* If the drive is case-sensitive we want to create new */
972                 /* files in lower-case otherwise we can't reopen them   */
973                 /* under the same short name. */
974                 if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = FILE_tolower(*name);
975                 else *p_l++ = *name;
976                 name++;
977             }
978             /* Ignore trailing dots and spaces */
979             while(p_l[-1] == '.' || p_l[-1] == ' ') {
980                 --p_l;
981                 --p_s;
982             }
983             *p_l = *p_s = '\0';
984         }
985         while ((*name == '\\') || (*name == '/')) name++;
986     }
987
988     if (!found)
989     {
990         if (check_last)
991         {
992             SetLastError( ERROR_FILE_NOT_FOUND );
993             return FALSE;
994         }
995         if (*name)  /* Not last */
996         {
997             SetLastError( ERROR_PATH_NOT_FOUND );
998             return FALSE;
999         }
1000     }
1001     if (!full->long_name[0]) strcpy( full->long_name, "/" );
1002     if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
1003     TRACE("returning %s = %s\n", full->long_name, full->short_name );
1004     return TRUE;
1005 }
1006
1007
1008 /***********************************************************************
1009  *           GetShortPathNameA   (KERNEL32.@)
1010  *
1011  * NOTES
1012  *  observed:
1013  *  longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1014  *  longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
1015  *
1016  * more observations ( with NT 3.51 (WinDD) ):
1017  * longpath <= 8.3 -> just copy longpath to shortpath
1018  * longpath > 8.3  ->
1019  *             a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
1020  *             b) file does exist     -> set the short filename.
1021  * - trailing slashes are reproduced in the short name, even if the
1022  *   file is not a directory
1023  * - the absolute/relative path of the short name is reproduced like found
1024  *   in the long name
1025  * - longpath and shortpath may have the same address
1026  * Peter Ganten, 1999
1027  */
1028 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
1029                                   DWORD shortlen )
1030 {
1031     DOS_FULL_NAME full_name;
1032     LPSTR tmpshortpath;
1033     DWORD sp = 0, lp = 0;
1034     int tmplen, drive;
1035     UINT flags;
1036     BOOL unixabsolute = *longpath == '/';
1037
1038     TRACE("%s\n", debugstr_a(longpath));
1039
1040     if (!longpath) {
1041       SetLastError(ERROR_INVALID_PARAMETER);
1042       return 0;
1043     }
1044     if (!longpath[0]) {
1045       SetLastError(ERROR_BAD_PATHNAME);
1046       return 0;
1047     }
1048
1049     if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
1050       SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
1051       return 0;
1052     }
1053
1054     /* check for drive letter */
1055     if (!unixabsolute && longpath[1] == ':' ) {
1056       tmpshortpath[0] = longpath[0];
1057       tmpshortpath[1] = ':';
1058       sp = 2;
1059     }
1060
1061     if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
1062     flags = DRIVE_GetFlags ( drive );
1063
1064     if (unixabsolute && drive != DRIVE_GetCurrentDrive()) {
1065       tmpshortpath[0] = drive + 'A';
1066       tmpshortpath[1] = ':';
1067       sp = 2;
1068     }
1069
1070     while ( longpath[lp] ) {
1071
1072       /* check for path delimiters and reproduce them */
1073       if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
1074         if (!sp || tmpshortpath[sp-1]!= '\\')
1075         {
1076             /* strip double "\\" */
1077             tmpshortpath[sp] = '\\';
1078             sp++;
1079         }
1080         tmpshortpath[sp]=0;/*terminate string*/
1081         lp++;
1082         continue;
1083       }
1084
1085       tmplen = strcspn ( longpath + lp, "\\/" );
1086       lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
1087
1088       /* Check, if the current element is a valid dos name */
1089       if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
1090         sp += tmplen;
1091         lp += tmplen;
1092         continue;
1093       }
1094
1095       /* Check if the file exists and use the existing file name */
1096       if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
1097         strcpy( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
1098         sp += strlen ( tmpshortpath+sp );
1099         lp += tmplen;
1100         continue;
1101       }
1102
1103       TRACE("not found!\n" );
1104       SetLastError ( ERROR_FILE_NOT_FOUND );
1105       return 0;
1106     }
1107     tmpshortpath[sp] = 0;
1108
1109     lstrcpynA ( shortpath, tmpshortpath, shortlen );
1110     TRACE("returning %s\n", debugstr_a(shortpath) );
1111     tmplen = strlen ( tmpshortpath );
1112     HeapFree ( GetProcessHeap(), 0, tmpshortpath );
1113
1114     return tmplen;
1115 }
1116
1117
1118 /***********************************************************************
1119  *           GetShortPathNameW   (KERNEL32.@)
1120  */
1121 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
1122                                   DWORD shortlen )
1123 {
1124     LPSTR longpathA, shortpathA;
1125     DWORD ret = 0;
1126
1127     longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
1128     shortpathA = HeapAlloc ( GetProcessHeap(), 0, shortlen );
1129
1130     ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
1131     if (shortlen > 0 && !MultiByteToWideChar( CP_ACP, 0, shortpathA, -1, shortpath, shortlen ))
1132         shortpath[shortlen-1] = 0;
1133     HeapFree( GetProcessHeap(), 0, longpathA );
1134     HeapFree( GetProcessHeap(), 0, shortpathA );
1135
1136     return ret;
1137 }
1138
1139
1140 /***********************************************************************
1141  *           GetLongPathNameA   (KERNEL32.@)
1142  *
1143  * NOTES
1144  *  observed (Win2000):
1145  *  shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1146  *  shortpath="":   LastError=ERROR_PATH_NOT_FOUND, ret=0
1147  */
1148 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
1149                                   DWORD longlen )
1150 {
1151     DOS_FULL_NAME full_name;
1152     char *p, *r, *ll, *ss;
1153
1154     if (!shortpath) {
1155       SetLastError(ERROR_INVALID_PARAMETER);
1156       return 0;
1157     }
1158     if (!shortpath[0]) {
1159       SetLastError(ERROR_PATH_NOT_FOUND);
1160       return 0;
1161     }
1162
1163     if(shortpath[0]=='\\' && shortpath[1]=='\\')
1164     {
1165         ERR("UNC pathname %s\n",debugstr_a(shortpath));
1166         lstrcpynA( longpath, full_name.short_name, longlen );
1167         return lstrlenA(longpath);
1168     }
1169
1170     if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
1171     lstrcpynA( longpath, full_name.short_name, longlen );
1172
1173     /* Do some hackery to get the long filename. */
1174
1175     if (longpath) {
1176      ss=longpath+strlen(longpath);
1177      ll=full_name.long_name+strlen(full_name.long_name);
1178      p=NULL;
1179      while (ss>=longpath)
1180      {
1181        /* FIXME: aren't we more paranoid, than needed? */
1182        while ((ss[0]=='\\') && (ss>=longpath)) ss--;
1183        p=ss;
1184        while ((ss[0]!='\\') && (ss>=longpath)) ss--;
1185        if (ss>=longpath)
1186          {
1187          /* FIXME: aren't we more paranoid, than needed? */
1188          while ((ll[0]=='/') && (ll>=full_name.long_name)) ll--;
1189          while ((ll[0]!='/') && (ll>=full_name.long_name)) ll--;
1190          if (ll<full_name.long_name)
1191               {
1192               ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
1193                   ,ss ,ll );
1194               return 0;
1195               }
1196          }
1197      }
1198
1199    /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
1200       if (p && p[2])
1201         {
1202         p+=1;
1203         if ((p-longpath)>0) longlen -= (p-longpath);
1204         lstrcpynA( p, ll , longlen);
1205
1206         /* Now, change all '/' to '\' */
1207         for (r=p; r<(p+longlen); r++ )
1208           if (r[0]=='/') r[0]='\\';
1209         return strlen(longpath) - strlen(p) + longlen;
1210         }
1211     }
1212
1213     return strlen(longpath);
1214 }
1215
1216
1217 /***********************************************************************
1218  *           GetLongPathNameW   (KERNEL32.@)
1219  */
1220 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
1221                                   DWORD longlen )
1222 {
1223     DOS_FULL_NAME full_name;
1224     DWORD ret = 0;
1225     LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
1226
1227     /* FIXME: is it correct to always return a fully qualified short path? */
1228     if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
1229     {
1230         ret = strlen( full_name.short_name );
1231         if (longlen > 0 && !MultiByteToWideChar( CP_ACP, 0, full_name.long_name, -1,
1232                                                  longpath, longlen ))
1233             longpath[longlen-1] = 0;
1234     }
1235     HeapFree( GetProcessHeap(), 0, shortpathA );
1236     return ret;
1237 }
1238
1239
1240 /***********************************************************************
1241  *           DOSFS_DoGetFullPathName
1242  *
1243  * Implementation of GetFullPathNameA/W.
1244  *
1245  * bon@elektron 000331:
1246  * A test for GetFullPathName with many pathological cases
1247  * now gives identical output for Wine and OSR2
1248  */
1249 static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
1250                                       BOOL unicode )
1251 {
1252     DWORD ret;
1253     DOS_FULL_NAME full_name;
1254     char *p,*q;
1255     const char * root;
1256     char drivecur[]="c:.";
1257     char driveletter=0;
1258     int namelen,drive=0;
1259
1260     if (!name[0]) return 0;
1261
1262     TRACE("passed '%s'\n", name);
1263
1264     if (name[1]==':')
1265       /*drive letter given */
1266       {
1267         driveletter = name[0];
1268       }
1269     if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
1270       /*absolute path given */
1271       {
1272         lstrcpynA(full_name.short_name,name,MAX_PATHNAME_LEN);
1273         drive = (int)FILE_toupper(name[0]) - 'A';
1274       }
1275     else
1276       {
1277         if (driveletter)
1278           drivecur[0]=driveletter;
1279         else if ((name[0]=='\\') || (name[0]=='/'))
1280           strcpy(drivecur,"\\");
1281         else
1282           strcpy(drivecur,".");
1283
1284         if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
1285           {
1286             FIXME("internal: error getting drive/path\n");
1287             return 0;
1288           }
1289         /* find path that drive letter substitutes*/
1290         drive = (int)FILE_toupper(full_name.short_name[0]) -0x41;
1291         root= DRIVE_GetRoot(drive);
1292         if (!root)
1293           {
1294             FIXME("internal: error getting DOS Drive Root\n");
1295             return 0;
1296           }
1297         if (!strcmp(root,"/"))
1298           {
1299             /* we have just the last / and we need it. */
1300             p= full_name.long_name;
1301           }
1302         else
1303           {
1304             p= full_name.long_name +strlen(root);
1305           }
1306         /* append long name (= unix name) to drive */
1307         lstrcpynA(full_name.short_name+2,p,MAX_PATHNAME_LEN-3);
1308         /* append name to treat */
1309         namelen= strlen(full_name.short_name);
1310         p = (char*)name;
1311         if (driveletter)
1312           p += +2; /* skip drive name when appending */
1313         if (namelen +2  + strlen(p) > MAX_PATHNAME_LEN)
1314           {
1315             FIXME("internal error: buffer too small\n");
1316              return 0;
1317           }
1318         full_name.short_name[namelen++] ='\\';
1319         full_name.short_name[namelen] = 0;
1320         lstrcpynA(full_name.short_name +namelen,p,MAX_PATHNAME_LEN-namelen);
1321       }
1322     /* reverse all slashes */
1323     for (p=full_name.short_name;
1324          p < full_name.short_name+strlen(full_name.short_name);
1325          p++)
1326       {
1327         if ( *p == '/' )
1328           *p = '\\';
1329       }
1330      /* Use memmove, as areas overlap */
1331      /* Delete .. */
1332     while ((p = strstr(full_name.short_name,"\\..\\")))
1333       {
1334         if (p > full_name.short_name+2)
1335           {
1336             *p = 0;
1337             q = strrchr(full_name.short_name,'\\');
1338             memmove(q+1,p+4,strlen(p+4)+1);
1339           }
1340         else
1341           {
1342             memmove(full_name.short_name+3,p+4,strlen(p+4)+1);
1343           }
1344       }
1345     if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
1346         {
1347           /* This case istn't treated yet : c:..\test */
1348           memmove(full_name.short_name+2,full_name.short_name+4,
1349                   strlen(full_name.short_name+4)+1);
1350         }
1351      /* Delete . */
1352     while ((p = strstr(full_name.short_name,"\\.\\")))
1353       {
1354         *(p+1) = 0;
1355         memmove(p+1,p+3,strlen(p+3)+1);
1356       }
1357     if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
1358         for (p = full_name.short_name; *p; p++) *p = FILE_toupper(*p);
1359     namelen=strlen(full_name.short_name);
1360     if (!strcmp(full_name.short_name+namelen-3,"\\.."))
1361         {
1362           /* one more strange case: "c:\test\test1\.."
1363            return "c:\test" */
1364           *(full_name.short_name+namelen-3)=0;
1365           q = strrchr(full_name.short_name,'\\');
1366           *q =0;
1367         }
1368     if (full_name.short_name[namelen-1]=='.')
1369         full_name.short_name[(namelen--)-1] =0;
1370     if (!driveletter)
1371       if (full_name.short_name[namelen-1]=='\\')
1372         full_name.short_name[(namelen--)-1] =0;
1373     TRACE("got %s\n",full_name.short_name);
1374
1375     /* If the lpBuffer buffer is too small, the return value is the
1376     size of the buffer, in characters, required to hold the path
1377     plus the terminating \0 (tested against win95osr2, bon 001118)
1378     . */
1379     ret = strlen(full_name.short_name);
1380     if (ret >= len )
1381       {
1382         /* don't touch anything when the buffer is not large enough */
1383         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1384         return ret+1;
1385       }
1386     if (result)
1387     {
1388         if (unicode)
1389             MultiByteToWideChar( CP_ACP, 0, full_name.short_name, -1, (LPWSTR)result, len );
1390         else
1391             lstrcpynA( result, full_name.short_name, len );
1392     }
1393
1394     TRACE("returning '%s'\n", full_name.short_name );
1395     return ret;
1396 }
1397
1398
1399 /***********************************************************************
1400  *           GetFullPathNameA   (KERNEL32.@)
1401  * NOTES
1402  *   if the path closed with '\', *lastpart is 0
1403  */
1404 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
1405                                  LPSTR *lastpart )
1406 {
1407     DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
1408     if (ret && (ret<=len) && buffer && lastpart)
1409     {
1410         LPSTR p = buffer + strlen(buffer);
1411
1412         if (*p != '\\')
1413         {
1414           while ((p > buffer + 2) && (*p != '\\')) p--;
1415           *lastpart = p + 1;
1416         }
1417         else *lastpart = NULL;
1418     }
1419     return ret;
1420 }
1421
1422
1423 /***********************************************************************
1424  *           GetFullPathNameW   (KERNEL32.@)
1425  */
1426 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
1427                                  LPWSTR *lastpart )
1428 {
1429     LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
1430     DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
1431     HeapFree( GetProcessHeap(), 0, nameA );
1432     if (ret && (ret<=len) && buffer && lastpart)
1433     {
1434         LPWSTR p = buffer + strlenW(buffer);
1435         if (*p != (WCHAR)'\\')
1436         {
1437             while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
1438             *lastpart = p + 1;
1439         }
1440         else *lastpart = NULL;
1441     }
1442     return ret;
1443 }
1444
1445
1446 /***********************************************************************
1447  *           wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1448  *
1449  * Return the full Unix file name for a given path.
1450  */
1451 BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
1452 {
1453     BOOL ret;
1454     DOS_FULL_NAME path;
1455     if ((ret = DOSFS_GetFullName( dos, FALSE, &path ))) lstrcpynA( buffer, path.long_name, len );
1456     return ret;
1457 }
1458
1459
1460 /***********************************************************************
1461  *           DOSFS_FindNextEx
1462  */
1463 static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
1464 {
1465     DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY | FILE_ATTRIBUTE_SYMLINK;
1466     UINT flags = DRIVE_GetFlags( info->drive );
1467     char *p, buffer[MAX_PATHNAME_LEN];
1468     const char *drive_path;
1469     int drive_root;
1470     LPCSTR long_name, short_name;
1471     BY_HANDLE_FILE_INFORMATION fileinfo;
1472     char dos_name[13];
1473
1474     if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
1475     {
1476         if (info->cur_pos) return 0;
1477         entry->dwFileAttributes  = FILE_ATTRIBUTE_LABEL;
1478         RtlSecondsSince1970ToTime( (time_t)0, &entry->ftCreationTime );
1479         RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastAccessTime );
1480         RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastWriteTime );
1481         entry->nFileSizeHigh     = 0;
1482         entry->nFileSizeLow      = 0;
1483         entry->dwReserved0       = 0;
1484         entry->dwReserved1       = 0;
1485         DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
1486         strcpy( entry->cAlternateFileName, entry->cFileName );
1487         info->cur_pos++;
1488         TRACE("returning %s (%s) as label\n",
1489                entry->cFileName, entry->cAlternateFileName);
1490         return 1;
1491     }
1492
1493     drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
1494     while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
1495     drive_root = !*drive_path;
1496
1497     lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
1498     strcat( buffer, "/" );
1499     p = buffer + strlen(buffer);
1500
1501     while (DOSFS_ReadDir( info->u.dos_dir, &long_name, &short_name ))
1502     {
1503         info->cur_pos++;
1504
1505         /* Don't return '.' and '..' in the root of the drive */
1506         if (drive_root && (long_name[0] == '.') &&
1507             (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
1508             continue;
1509
1510         /* Check the long mask */
1511
1512         if (info->long_mask)
1513         {
1514             if (!DOSFS_MatchLong( info->long_mask, long_name,
1515                                   flags & DRIVE_CASE_SENSITIVE )) continue;
1516         }
1517
1518         /* Check the short mask */
1519
1520         if (info->short_mask)
1521         {
1522             if (!short_name)
1523             {
1524                 DOSFS_Hash( long_name, dos_name, TRUE,
1525                             !(flags & DRIVE_CASE_SENSITIVE) );
1526                 short_name = dos_name;
1527             }
1528             if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
1529         }
1530
1531         /* Check the file attributes */
1532
1533         lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
1534         if (!FILE_Stat( buffer, &fileinfo ))
1535         {
1536             WARN("can't stat %s\n", buffer);
1537             continue;
1538         }
1539         if ((fileinfo.dwFileAttributes & FILE_ATTRIBUTE_SYMLINK) &&
1540             (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1541         {
1542             static int show_dir_symlinks = -1;
1543             if (show_dir_symlinks == -1)
1544                 show_dir_symlinks = PROFILE_GetWineIniBool("wine", "ShowDirSymlinks", 0);
1545             if (!show_dir_symlinks) continue;
1546         }
1547
1548         if (fileinfo.dwFileAttributes & ~attr) continue;
1549
1550         /* We now have a matching entry; fill the result and return */
1551
1552         entry->dwFileAttributes = fileinfo.dwFileAttributes;
1553         entry->ftCreationTime   = fileinfo.ftCreationTime;
1554         entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
1555         entry->ftLastWriteTime  = fileinfo.ftLastWriteTime;
1556         entry->nFileSizeHigh    = fileinfo.nFileSizeHigh;
1557         entry->nFileSizeLow     = fileinfo.nFileSizeLow;
1558
1559         if (short_name)
1560             DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
1561         else
1562             DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
1563                         !(flags & DRIVE_CASE_SENSITIVE) );
1564
1565         lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
1566         if (!(flags & DRIVE_CASE_PRESERVING)) _strlwr( entry->cFileName );
1567         TRACE("returning %s (%s) %02lx %ld\n",
1568               entry->cFileName, entry->cAlternateFileName,
1569               entry->dwFileAttributes, entry->nFileSizeLow );
1570         return 1;
1571     }
1572     return 0;  /* End of directory */
1573 }
1574
1575 /***********************************************************************
1576  *           DOSFS_FindNext
1577  *
1578  * Find the next matching file. Return the number of entries read to find
1579  * the matching one, or 0 if no more entries.
1580  * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
1581  * file name mask. Either or both can be NULL.
1582  *
1583  * NOTE: This is supposed to be only called by the int21 emulation
1584  *       routines. Thus, we should own the Win16Mutex anyway.
1585  *       Nevertheless, we explicitly enter it to ensure the static
1586  *       directory cache is protected.
1587  */
1588 int DOSFS_FindNext( const char *path, const char *short_mask,
1589                     const char *long_mask, int drive, BYTE attr,
1590                     int skip, WIN32_FIND_DATAA *entry )
1591 {
1592     static FIND_FIRST_INFO info;
1593     LPCSTR short_name, long_name;
1594     int count;
1595
1596     _EnterWin16Lock();
1597
1598     /* Check the cached directory */
1599     if (!(info.u.dos_dir && info.path == path && info.short_mask == short_mask
1600                    && info.long_mask == long_mask && info.drive == drive
1601                    && info.attr == attr && info.cur_pos <= skip))
1602     {
1603         /* Not in the cache, open it anew */
1604         if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1605
1606         info.path = (LPSTR)path;
1607         info.long_mask = (LPSTR)long_mask;
1608         info.short_mask = (LPSTR)short_mask;
1609         info.attr = attr;
1610         info.drive = drive;
1611         info.cur_pos = 0;
1612         info.u.dos_dir = DOSFS_OpenDir( info.path );
1613     }
1614
1615     /* Skip to desired position */
1616     while (info.cur_pos < skip)
1617         if (info.u.dos_dir && DOSFS_ReadDir( info.u.dos_dir, &long_name, &short_name ))
1618             info.cur_pos++;
1619         else
1620             break;
1621
1622     if (info.u.dos_dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
1623         count = info.cur_pos - skip;
1624     else
1625         count = 0;
1626
1627     if (!count)
1628     {
1629         if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
1630         memset( &info, '\0', sizeof(info) );
1631     }
1632
1633     _LeaveWin16Lock();
1634
1635     return count;
1636 }
1637
1638 /*************************************************************************
1639  *           FindFirstFileExA  (KERNEL32.@)
1640  */
1641 HANDLE WINAPI FindFirstFileExA(
1642         LPCSTR lpFileName,
1643         FINDEX_INFO_LEVELS fInfoLevelId,
1644         LPVOID lpFindFileData,
1645         FINDEX_SEARCH_OPS fSearchOp,
1646         LPVOID lpSearchFilter,
1647         DWORD dwAdditionalFlags)
1648 {
1649     HGLOBAL handle;
1650     FIND_FIRST_INFO *info;
1651
1652     if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
1653     {
1654         FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
1655         return INVALID_HANDLE_VALUE;
1656     }
1657
1658     switch(fInfoLevelId)
1659     {
1660       case FindExInfoStandard:
1661         {
1662           WIN32_FIND_DATAA * data = (WIN32_FIND_DATAA *) lpFindFileData;
1663           data->dwReserved0 = data->dwReserved1 = 0x0;
1664           if (!lpFileName) return 0;
1665           if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
1666           {
1667               ERR("UNC path name\n");
1668               if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1669
1670               info = (FIND_FIRST_INFO *)GlobalLock( handle );
1671               info->u.smb_dir = SMB_FindFirst(lpFileName);
1672               if(info->u.smb_dir < 0)
1673               {
1674                  GlobalUnlock( handle );
1675                  GlobalFree(handle);
1676                  break;
1677               }
1678
1679               info->drive = -1;
1680
1681               GlobalUnlock( handle );
1682           }
1683           else
1684           {
1685             DOS_FULL_NAME full_name;
1686
1687           if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
1688           if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
1689           info = (FIND_FIRST_INFO *)GlobalLock( handle );
1690           info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
1691           strcpy( info->path, full_name.long_name );
1692           info->long_mask = strrchr( info->path, '/' );
1693           *(info->long_mask++) = '\0';
1694           info->short_mask = NULL;
1695           info->attr = 0xff;
1696           if (lpFileName[0] && (lpFileName[1] == ':'))
1697               info->drive = FILE_toupper(*lpFileName) - 'A';
1698           else info->drive = DRIVE_GetCurrentDrive();
1699           info->cur_pos = 0;
1700
1701             info->u.dos_dir = DOSFS_OpenDir( info->path );
1702           GlobalUnlock( handle );
1703           }
1704
1705           if (!FindNextFileA( handle, data ))
1706           {
1707               FindClose( handle );
1708               SetLastError( ERROR_NO_MORE_FILES );
1709               break;
1710           }
1711           return handle;
1712         }
1713         break;
1714       default:
1715         FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1716     }
1717     return INVALID_HANDLE_VALUE;
1718 }
1719
1720 /*************************************************************************
1721  *           FindFirstFileA   (KERNEL32.@)
1722  */
1723 HANDLE WINAPI FindFirstFileA(
1724         LPCSTR lpFileName,
1725         WIN32_FIND_DATAA *lpFindData )
1726 {
1727     return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
1728                             FindExSearchNameMatch, NULL, 0);
1729 }
1730
1731 /*************************************************************************
1732  *           FindFirstFileExW   (KERNEL32.@)
1733  */
1734 HANDLE WINAPI FindFirstFileExW(
1735         LPCWSTR lpFileName,
1736         FINDEX_INFO_LEVELS fInfoLevelId,
1737         LPVOID lpFindFileData,
1738         FINDEX_SEARCH_OPS fSearchOp,
1739         LPVOID lpSearchFilter,
1740         DWORD dwAdditionalFlags)
1741 {
1742     HANDLE handle;
1743     WIN32_FIND_DATAA dataA;
1744     LPVOID _lpFindFileData;
1745     LPSTR pathA;
1746
1747     switch(fInfoLevelId)
1748     {
1749       case FindExInfoStandard:
1750         {
1751           _lpFindFileData = &dataA;
1752         }
1753         break;
1754       default:
1755         FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1756         return INVALID_HANDLE_VALUE;
1757     }
1758
1759     pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName );
1760     handle = FindFirstFileExA(pathA, fInfoLevelId, _lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
1761     HeapFree( GetProcessHeap(), 0, pathA );
1762     if (handle == INVALID_HANDLE_VALUE) return handle;
1763
1764     switch(fInfoLevelId)
1765     {
1766       case FindExInfoStandard:
1767         {
1768           WIN32_FIND_DATAW *dataW = (WIN32_FIND_DATAW*) lpFindFileData;
1769           dataW->dwFileAttributes = dataA.dwFileAttributes;
1770           dataW->ftCreationTime   = dataA.ftCreationTime;
1771           dataW->ftLastAccessTime = dataA.ftLastAccessTime;
1772           dataW->ftLastWriteTime  = dataA.ftLastWriteTime;
1773           dataW->nFileSizeHigh    = dataA.nFileSizeHigh;
1774           dataW->nFileSizeLow     = dataA.nFileSizeLow;
1775           MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1776                                dataW->cFileName, sizeof(dataW->cFileName)/sizeof(WCHAR) );
1777           MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1778                                dataW->cAlternateFileName,
1779                                sizeof(dataW->cAlternateFileName)/sizeof(WCHAR) );
1780         }
1781         break;
1782       default:
1783         FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
1784         return INVALID_HANDLE_VALUE;
1785     }
1786     return handle;
1787 }
1788
1789 /*************************************************************************
1790  *           FindFirstFileW   (KERNEL32.@)
1791  */
1792 HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
1793 {
1794     return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
1795                             FindExSearchNameMatch, NULL, 0);
1796 }
1797
1798 /*************************************************************************
1799  *           FindNextFileA   (KERNEL32.@)
1800  */
1801 BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
1802 {
1803     FIND_FIRST_INFO *info;
1804     BOOL ret = FALSE;
1805     DWORD gle = ERROR_NO_MORE_FILES;
1806
1807     if ((handle == INVALID_HANDLE_VALUE) ||
1808        !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1809     {
1810         SetLastError( ERROR_INVALID_HANDLE );
1811         return ret;
1812     }
1813     if (info->drive == -1)
1814     {
1815         ret = SMB_FindNext( info->u.smb_dir, data );
1816         if(!ret)
1817         {
1818             SMB_CloseDir( info->u.smb_dir );
1819             HeapFree( GetProcessHeap(), 0, info->path );
1820         }
1821         goto done;
1822     }
1823     else if (!info->path || !info->u.dos_dir)
1824     {
1825         goto done;
1826     }
1827     else if (!DOSFS_FindNextEx( info, data ))
1828     {
1829         DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
1830         HeapFree( GetProcessHeap(), 0, info->path );
1831         info->path = info->long_mask = NULL;
1832     }
1833     else
1834         ret = TRUE;
1835 done:
1836     GlobalUnlock( handle );
1837     if( !ret ) SetLastError( gle );
1838     return ret;
1839 }
1840
1841
1842 /*************************************************************************
1843  *           FindNextFileW   (KERNEL32.@)
1844  */
1845 BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
1846 {
1847     WIN32_FIND_DATAA dataA;
1848     if (!FindNextFileA( handle, &dataA )) return FALSE;
1849     data->dwFileAttributes = dataA.dwFileAttributes;
1850     data->ftCreationTime   = dataA.ftCreationTime;
1851     data->ftLastAccessTime = dataA.ftLastAccessTime;
1852     data->ftLastWriteTime  = dataA.ftLastWriteTime;
1853     data->nFileSizeHigh    = dataA.nFileSizeHigh;
1854     data->nFileSizeLow     = dataA.nFileSizeLow;
1855     MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
1856                          data->cFileName, sizeof(data->cFileName)/sizeof(WCHAR) );
1857     MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
1858                          data->cAlternateFileName,
1859                          sizeof(data->cAlternateFileName)/sizeof(WCHAR) );
1860     return TRUE;
1861 }
1862
1863 /*************************************************************************
1864  *           FindClose   (KERNEL32.@)
1865  */
1866 BOOL WINAPI FindClose( HANDLE handle )
1867 {
1868     FIND_FIRST_INFO *info;
1869
1870     if (handle == INVALID_HANDLE_VALUE) goto error;
1871
1872     __TRY
1873     {
1874         if ((info = (FIND_FIRST_INFO *)GlobalLock( handle )))
1875         {
1876             if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
1877             if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
1878         }
1879     }
1880     __EXCEPT(page_fault)
1881     {
1882         WARN("Illegal handle %x\n", handle);
1883         SetLastError( ERROR_INVALID_HANDLE );
1884         return FALSE;
1885     }
1886     __ENDTRY
1887     if (!info) goto error;
1888     GlobalUnlock( handle );
1889     GlobalFree( handle );
1890     return TRUE;
1891
1892  error:
1893     SetLastError( ERROR_INVALID_HANDLE );
1894     return FALSE;
1895 }
1896
1897 /***********************************************************************
1898  *           DOSFS_UnixTimeToFileTime
1899  *
1900  * Convert a Unix time to FILETIME format.
1901  * The FILETIME structure is a 64-bit value representing the number of
1902  * 100-nanosecond intervals since January 1, 1601, 0:00.
1903  * 'remainder' is the nonnegative number of 100-ns intervals
1904  * corresponding to the time fraction smaller than 1 second that
1905  * couldn't be stored in the time_t value.
1906  */
1907 void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
1908                                DWORD remainder )
1909 {
1910     /* NOTES:
1911
1912        CONSTANTS:
1913        The time difference between 1 January 1601, 00:00:00 and
1914        1 January 1970, 00:00:00 is 369 years, plus the leap years
1915        from 1604 to 1968, excluding 1700, 1800, 1900.
1916        This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
1917        of 134774 days.
1918
1919        Any day in that period had 24 * 60 * 60 = 86400 seconds.
1920
1921        The time difference is 134774 * 86400 * 10000000, which can be written
1922        116444736000000000
1923        27111902 * 2^32 + 3577643008
1924        413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
1925
1926        If you find that these constants are buggy, please change them in all
1927        instances in both conversion functions.
1928
1929        VERSIONS:
1930        There are two versions, one of them uses long long variables and
1931        is presumably faster but not ISO C. The other one uses standard C
1932        data types and operations but relies on the assumption that negative
1933        numbers are stored as 2's complement (-1 is 0xffff....). If this
1934        assumption is violated, dates before 1970 will not convert correctly.
1935        This should however work on any reasonable architecture where WINE
1936        will run.
1937
1938        DETAILS:
1939
1940        Take care not to remove the casts. I have tested these functions
1941        (in both versions) for a lot of numbers. I would be interested in
1942        results on other compilers than GCC.
1943
1944        The operations have been designed to account for the possibility
1945        of 64-bit time_t in future UNICES. Even the versions without
1946        internal long long numbers will work if time_t only is 64 bit.
1947        A 32-bit shift, which was necessary for that operation, turned out
1948        not to work correctly in GCC, besides giving the warning. So I
1949        used a double 16-bit shift instead. Numbers are in the ISO version
1950        represented by three limbs, the most significant with 32 bit, the
1951        other two with 16 bit each.
1952
1953        As the modulo-operator % is not well-defined for negative numbers,
1954        negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
1955
1956        There might be quicker ways to do this in C. Certainly so in
1957        assembler.
1958
1959        Claus Fischer, fischer@iue.tuwien.ac.at
1960        */
1961
1962 #if SIZEOF_LONG_LONG >= 8
1963 #  define USE_LONG_LONG 1
1964 #else
1965 #  define USE_LONG_LONG 0
1966 #endif
1967
1968 #if USE_LONG_LONG               /* gcc supports long long type */
1969
1970     long long int t = unix_time;
1971     t *= 10000000;
1972     t += 116444736000000000LL;
1973     t += remainder;
1974     filetime->dwLowDateTime  = (UINT)t;
1975     filetime->dwHighDateTime = (UINT)(t >> 32);
1976
1977 #else  /* ISO version */
1978
1979     UINT a0;                    /* 16 bit, low    bits */
1980     UINT a1;                    /* 16 bit, medium bits */
1981     UINT a2;                    /* 32 bit, high   bits */
1982
1983     /* Copy the unix time to a2/a1/a0 */
1984     a0 =  unix_time & 0xffff;
1985     a1 = (unix_time >> 16) & 0xffff;
1986     /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
1987        Do not replace this by >> 32, it gives a compiler warning and it does
1988        not work. */
1989     a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
1990           ~((~unix_time >> 16) >> 16));
1991
1992     /* Multiply a by 10000000 (a = a2/a1/a0)
1993        Split the factor into 10000 * 1000 which are both less than 0xffff. */
1994     a0 *= 10000;
1995     a1 = a1 * 10000 + (a0 >> 16);
1996     a2 = a2 * 10000 + (a1 >> 16);
1997     a0 &= 0xffff;
1998     a1 &= 0xffff;
1999
2000     a0 *= 1000;
2001     a1 = a1 * 1000 + (a0 >> 16);
2002     a2 = a2 * 1000 + (a1 >> 16);
2003     a0 &= 0xffff;
2004     a1 &= 0xffff;
2005
2006     /* Add the time difference and the remainder */
2007     a0 += 32768 + (remainder & 0xffff);
2008     a1 += 54590 + (remainder >> 16   ) + (a0 >> 16);
2009     a2 += 27111902                     + (a1 >> 16);
2010     a0 &= 0xffff;
2011     a1 &= 0xffff;
2012
2013     /* Set filetime */
2014     filetime->dwLowDateTime  = (a1 << 16) + a0;
2015     filetime->dwHighDateTime = a2;
2016 #endif
2017 }
2018
2019
2020 /***********************************************************************
2021  *           DOSFS_FileTimeToUnixTime
2022  *
2023  * Convert a FILETIME format to Unix time.
2024  * If not NULL, 'remainder' contains the fractional part of the filetime,
2025  * in the range of [0..9999999] (even if time_t is negative).
2026  */
2027 time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
2028 {
2029     /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
2030 #if USE_LONG_LONG
2031
2032     long long int t = filetime->dwHighDateTime;
2033     t <<= 32;
2034     t += (UINT)filetime->dwLowDateTime;
2035     t -= 116444736000000000LL;
2036     if (t < 0)
2037     {
2038         if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
2039         return -1 - ((-t - 1) / 10000000);
2040     }
2041     else
2042     {
2043         if (remainder) *remainder = t % 10000000;
2044         return t / 10000000;
2045     }
2046
2047 #else  /* ISO version */
2048
2049     UINT a0;                    /* 16 bit, low    bits */
2050     UINT a1;                    /* 16 bit, medium bits */
2051     UINT a2;                    /* 32 bit, high   bits */
2052     UINT r;                     /* remainder of division */
2053     unsigned int carry;         /* carry bit for subtraction */
2054     int negative;               /* whether a represents a negative value */
2055
2056     /* Copy the time values to a2/a1/a0 */
2057     a2 =  (UINT)filetime->dwHighDateTime;
2058     a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
2059     a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
2060
2061     /* Subtract the time difference */
2062     if (a0 >= 32768           ) a0 -=             32768        , carry = 0;
2063     else                        a0 += (1 << 16) - 32768        , carry = 1;
2064
2065     if (a1 >= 54590    + carry) a1 -=             54590 + carry, carry = 0;
2066     else                        a1 += (1 << 16) - 54590 - carry, carry = 1;
2067
2068     a2 -= 27111902 + carry;
2069
2070     /* If a is negative, replace a by (-1-a) */
2071     negative = (a2 >= ((UINT)1) << 31);
2072     if (negative)
2073     {
2074         /* Set a to -a - 1 (a is a2/a1/a0) */
2075         a0 = 0xffff - a0;
2076         a1 = 0xffff - a1;
2077         a2 = ~a2;
2078     }
2079
2080     /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
2081        Split the divisor into 10000 * 1000 which are both less than 0xffff. */
2082     a1 += (a2 % 10000) << 16;
2083     a2 /=       10000;
2084     a0 += (a1 % 10000) << 16;
2085     a1 /=       10000;
2086     r   =  a0 % 10000;
2087     a0 /=       10000;
2088
2089     a1 += (a2 % 1000) << 16;
2090     a2 /=       1000;
2091     a0 += (a1 % 1000) << 16;
2092     a1 /=       1000;
2093     r  += (a0 % 1000) * 10000;
2094     a0 /=       1000;
2095
2096     /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
2097     if (negative)
2098     {
2099         /* Set a to -a - 1 (a is a2/a1/a0) */
2100         a0 = 0xffff - a0;
2101         a1 = 0xffff - a1;
2102         a2 = ~a2;
2103
2104         r  = 9999999 - r;
2105     }
2106
2107     if (remainder) *remainder = r;
2108
2109     /* Do not replace this by << 32, it gives a compiler warning and it does
2110        not work. */
2111     return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
2112 #endif
2113 }
2114
2115
2116 /***********************************************************************
2117  *           MulDiv   (KERNEL32.@)
2118  * RETURNS
2119  *      Result of multiplication and division
2120  *      -1: Overflow occurred or Divisor was 0
2121  */
2122 INT WINAPI MulDiv(
2123              INT nMultiplicand,
2124              INT nMultiplier,
2125              INT nDivisor)
2126 {
2127 #if SIZEOF_LONG_LONG >= 8
2128     long long ret;
2129
2130     if (!nDivisor) return -1;
2131
2132     /* We want to deal with a positive divisor to simplify the logic. */
2133     if (nDivisor < 0)
2134     {
2135       nMultiplicand = - nMultiplicand;
2136       nDivisor = -nDivisor;
2137     }
2138
2139     /* If the result is positive, we "add" to round. else, we subtract to round. */
2140     if ( ( (nMultiplicand <  0) && (nMultiplier <  0) ) ||
2141          ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2142       ret = (((long long)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2143     else
2144       ret = (((long long)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2145
2146     if ((ret > 2147483647) || (ret < -2147483647)) return -1;
2147     return ret;
2148 #else
2149     if (!nDivisor) return -1;
2150
2151     /* We want to deal with a positive divisor to simplify the logic. */
2152     if (nDivisor < 0)
2153     {
2154       nMultiplicand = - nMultiplicand;
2155       nDivisor = -nDivisor;
2156     }
2157
2158     /* If the result is positive, we "add" to round. else, we subtract to round. */
2159     if ( ( (nMultiplicand <  0) && (nMultiplier <  0) ) ||
2160          ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
2161       return ((nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
2162
2163     return ((nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
2164
2165 #endif
2166 }
2167
2168
2169 /***********************************************************************
2170  *           DosDateTimeToFileTime   (KERNEL32.@)
2171  */
2172 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
2173 {
2174     struct tm newtm;
2175 #ifndef HAVE_TIMEGM
2176     struct tm *gtm;
2177     time_t time1, time2;
2178 #endif
2179
2180     newtm.tm_sec  = (fattime & 0x1f) * 2;
2181     newtm.tm_min  = (fattime >> 5) & 0x3f;
2182     newtm.tm_hour = (fattime >> 11);
2183     newtm.tm_mday = (fatdate & 0x1f);
2184     newtm.tm_mon  = ((fatdate >> 5) & 0x0f) - 1;
2185     newtm.tm_year = (fatdate >> 9) + 80;
2186 #ifdef HAVE_TIMEGM
2187     RtlSecondsSince1970ToTime( timegm(&newtm), ft );
2188 #else
2189     time1 = mktime(&newtm);
2190     gtm = gmtime(&time1);
2191     time2 = mktime(gtm);
2192     RtlSecondsSince1970ToTime( 2*time1-time2, ft );
2193 #endif
2194     return TRUE;
2195 }
2196
2197
2198 /***********************************************************************
2199  *           FileTimeToDosDateTime   (KERNEL32.@)
2200  */
2201 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
2202                                      LPWORD fattime )
2203 {
2204     time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
2205     struct tm *tm = gmtime( &unixtime );
2206     if (fattime)
2207         *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
2208     if (fatdate)
2209         *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
2210                    + tm->tm_mday;
2211     return TRUE;
2212 }
2213
2214
2215 /***********************************************************************
2216  *           LocalFileTimeToFileTime   (KERNEL32.@)
2217  */
2218 BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
2219                                        LPFILETIME utcft )
2220 {
2221     struct tm *xtm;
2222     DWORD remainder;
2223     time_t utctime;
2224
2225     /* Converts from local to UTC. */
2226     time_t localtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
2227     xtm = gmtime( &localtime );
2228     utctime = mktime(xtm);
2229     if(xtm->tm_isdst > 0) utctime-=3600;
2230     DOSFS_UnixTimeToFileTime( utctime, utcft, remainder );
2231     return TRUE;
2232 }
2233
2234
2235 /***********************************************************************
2236  *           FileTimeToLocalFileTime   (KERNEL32.@)
2237  */
2238 BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
2239                                        LPFILETIME localft )
2240 {
2241     DWORD remainder;
2242     /* Converts from UTC to local. */
2243     time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
2244 #ifdef HAVE_TIMEGM
2245     struct tm *xtm = localtime( &unixtime );
2246     time_t localtime;
2247
2248     localtime = timegm(xtm);
2249     DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
2250
2251 #else
2252     struct tm *xtm;
2253     time_t time;
2254
2255     xtm = gmtime( &unixtime );
2256     time = mktime(xtm);
2257     if(xtm->tm_isdst > 0) time-=3600;
2258     DOSFS_UnixTimeToFileTime( 2*unixtime-time, localft, remainder );
2259 #endif
2260     return TRUE;
2261 }
2262
2263
2264 /***********************************************************************
2265  *           FileTimeToSystemTime   (KERNEL32.@)
2266  */
2267 BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
2268 {
2269     struct tm *xtm;
2270     DWORD remainder;
2271     time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
2272     xtm = gmtime(&xtime);
2273     syst->wYear         = xtm->tm_year+1900;
2274     syst->wMonth        = xtm->tm_mon + 1;
2275     syst->wDayOfWeek    = xtm->tm_wday;
2276     syst->wDay          = xtm->tm_mday;
2277     syst->wHour         = xtm->tm_hour;
2278     syst->wMinute       = xtm->tm_min;
2279     syst->wSecond       = xtm->tm_sec;
2280     syst->wMilliseconds = remainder / 10000;
2281     return TRUE;
2282 }
2283
2284 /***********************************************************************
2285  *           QueryDosDeviceA   (KERNEL32.@)
2286  *
2287  * returns array of strings terminated by \0, terminated by \0
2288  */
2289 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
2290 {
2291     LPSTR s;
2292     char  buffer[200];
2293
2294     TRACE("(%s,...)\n", devname ? devname : "<null>");
2295     if (!devname) {
2296         /* return known MSDOS devices */
2297         static const char devices[24] = "CON\0COM1\0COM2\0LPT1\0NUL\0\0";
2298         memcpy( target, devices, min(bufsize,sizeof(devices)) );
2299         return min(bufsize,sizeof(devices));
2300     }
2301     /* In theory all that are possible and have been defined.
2302      * Now just those below, since mirc uses it to check for special files.
2303      *
2304      * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
2305      *  but currently we just ignore that.)
2306      */
2307 #define CHECK(x) (strstr(devname,#x)==devname)
2308     if (CHECK(con) || CHECK(com) || CHECK(lpt) || CHECK(nul)) {
2309         strcpy(buffer,"\\DEV\\");
2310         strcat(buffer,devname);
2311         if ((s=strchr(buffer,':'))) *s='\0';
2312         lstrcpynA(target,buffer,bufsize);
2313         return strlen(buffer)+1;
2314     } else {
2315         if (strchr(devname,':') || devname[0]=='\\') {
2316             /* This might be a DOS device we do not handle yet ... */
2317             FIXME("(%s) not detected as DOS device!\n",devname);
2318         }
2319         SetLastError(ERROR_DEV_NOT_EXIST);
2320         return 0;
2321     }
2322
2323 }
2324
2325
2326 /***********************************************************************
2327  *           QueryDosDeviceW   (KERNEL32.@)
2328  *
2329  * returns array of strings terminated by \0, terminated by \0
2330  */
2331 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
2332 {
2333     LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
2334     LPSTR targetA = (LPSTR)HeapAlloc(GetProcessHeap(),0,bufsize);
2335     DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
2336
2337     ret = MultiByteToWideChar( CP_ACP, 0, targetA, ret, target, bufsize );
2338     if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
2339     if (targetA) HeapFree(GetProcessHeap(),0,targetA);
2340     return ret;
2341 }
2342
2343
2344 /***********************************************************************
2345  *           SystemTimeToFileTime   (KERNEL32.@)
2346  */
2347 BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
2348 {
2349 #ifdef HAVE_TIMEGM
2350     struct tm xtm;
2351     time_t utctime;
2352 #else
2353     struct tm xtm,*utc_tm;
2354     time_t localtim,utctime;
2355 #endif
2356
2357     xtm.tm_year = syst->wYear-1900;
2358     xtm.tm_mon  = syst->wMonth - 1;
2359     xtm.tm_wday = syst->wDayOfWeek;
2360     xtm.tm_mday = syst->wDay;
2361     xtm.tm_hour = syst->wHour;
2362     xtm.tm_min  = syst->wMinute;
2363     xtm.tm_sec  = syst->wSecond; /* this is UTC */
2364     xtm.tm_isdst = -1;
2365 #ifdef HAVE_TIMEGM
2366     utctime = timegm(&xtm);
2367     DOSFS_UnixTimeToFileTime( utctime, ft,
2368                               syst->wMilliseconds * 10000 );
2369 #else
2370     localtim = mktime(&xtm);    /* now we've got local time */
2371     utc_tm = gmtime(&localtim);
2372     utctime = mktime(utc_tm);
2373     DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
2374                               syst->wMilliseconds * 10000 );
2375 #endif
2376     return TRUE;
2377 }
2378
2379 /***********************************************************************
2380  *           DefineDosDeviceA       (KERNEL32.@)
2381  */
2382 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
2383         FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
2384         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2385         return FALSE;
2386 }
2387
2388 /*
2389    --- 16 bit functions ---
2390 */
2391
2392 /*************************************************************************
2393  *           FindFirstFile   (KERNEL.413)
2394  */
2395 HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
2396 {
2397     DOS_FULL_NAME full_name;
2398     HGLOBAL16 handle;
2399     FIND_FIRST_INFO *info;
2400
2401     data->dwReserved0 = data->dwReserved1 = 0x0;
2402     if (!path) return 0;
2403     if (!DOSFS_GetFullName( path, FALSE, &full_name ))
2404         return INVALID_HANDLE_VALUE16;
2405     if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
2406         return INVALID_HANDLE_VALUE16;
2407     info = (FIND_FIRST_INFO *)GlobalLock16( handle );
2408     info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
2409     strcpy( info->path, full_name.long_name );
2410     info->long_mask = strrchr( info->path, '/' );
2411     if (info->long_mask )
2412         *(info->long_mask++) = '\0';
2413     info->short_mask = NULL;
2414     info->attr = 0xff;
2415     if (path[0] && (path[1] == ':')) info->drive = FILE_toupper(*path) - 'A';
2416     else info->drive = DRIVE_GetCurrentDrive();
2417     info->cur_pos = 0;
2418
2419     info->u.dos_dir = DOSFS_OpenDir( info->path );
2420
2421     GlobalUnlock16( handle );
2422     if (!FindNextFile16( handle, data ))
2423     {
2424         FindClose16( handle );
2425         SetLastError( ERROR_NO_MORE_FILES );
2426         return INVALID_HANDLE_VALUE16;
2427     }
2428     return handle;
2429 }
2430
2431 /*************************************************************************
2432  *           FindNextFile   (KERNEL.414)
2433  */
2434 BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
2435 {
2436     FIND_FIRST_INFO *info;
2437
2438     if ((handle == INVALID_HANDLE_VALUE16) ||
2439        !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2440     {
2441         SetLastError( ERROR_INVALID_HANDLE );
2442         return FALSE;
2443     }
2444     GlobalUnlock16( handle );
2445     if (!info->path || !info->u.dos_dir)
2446     {
2447         SetLastError( ERROR_NO_MORE_FILES );
2448         return FALSE;
2449     }
2450     if (!DOSFS_FindNextEx( info, data ))
2451     {
2452         DOSFS_CloseDir( info->u.dos_dir ); info->u.dos_dir = NULL;
2453         HeapFree( GetProcessHeap(), 0, info->path );
2454         info->path = info->long_mask = NULL;
2455         SetLastError( ERROR_NO_MORE_FILES );
2456         return FALSE;
2457     }
2458     return TRUE;
2459 }
2460
2461 /*************************************************************************
2462  *           FindClose   (KERNEL.415)
2463  */
2464 BOOL16 WINAPI FindClose16( HANDLE16 handle )
2465 {
2466     FIND_FIRST_INFO *info;
2467
2468     if ((handle == INVALID_HANDLE_VALUE16) ||
2469         !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
2470     {
2471         SetLastError( ERROR_INVALID_HANDLE );
2472         return FALSE;
2473     }
2474     if (info->u.dos_dir) DOSFS_CloseDir( info->u.dos_dir );
2475     if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
2476     GlobalUnlock16( handle );
2477     GlobalFree16( handle );
2478     return TRUE;
2479 }