- Work around problem in NSIS installers which can't handle 1 char at
[wine/multimedia.git] / files / dos_fs.c
blob057ea2054de0d5f9b7a4e234e5a163e83e3a434d
1 /*
2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
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.
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.
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
22 #include "config.h"
23 #include "wine/port.h"
25 #include <sys/types.h>
26 #include <ctype.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #ifdef HAVE_SYS_ERRNO_H
30 #include <sys/errno.h>
31 #endif
32 #include <fcntl.h>
33 #include <stdarg.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <sys/stat.h>
37 #ifdef HAVE_SYS_IOCTL_H
38 #include <sys/ioctl.h>
39 #endif
40 #ifdef HAVE_LINUX_IOCTL_H
41 #include <linux/ioctl.h>
42 #endif
43 #include <time.h>
44 #ifdef HAVE_UNISTD_H
45 # include <unistd.h>
46 #endif
48 #define NONAMELESSUNION
49 #define NONAMELESSSTRUCT
50 #include "ntstatus.h"
51 #include "windef.h"
52 #include "winbase.h"
53 #include "winerror.h"
54 #include "wingdi.h"
56 #include "wine/unicode.h"
57 #include "wine/winbase16.h"
58 #include "file.h"
59 #include "winreg.h"
60 #include "winternl.h"
61 #include "wine/server.h"
62 #include "wine/exception.h"
63 #include "excpt.h"
65 #include "smb.h"
67 #include "wine/debug.h"
69 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
70 WINE_DECLARE_DEBUG_CHANNEL(file);
72 /* Define the VFAT ioctl to get both short and long file names */
73 /* FIXME: is it possible to get this to work on other systems? */
74 #ifdef linux
75 /* We want the real kernel dirent structure, not the libc one */
76 typedef struct
78 long d_ino;
79 long d_off;
80 unsigned short d_reclen;
81 char d_name[256];
82 } KERNEL_DIRENT;
84 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
86 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
87 #ifndef O_DIRECTORY
88 # define O_DIRECTORY 0200000 /* must be directory */
89 #endif
91 #else /* linux */
92 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
93 #endif /* linux */
95 /* Chars we don't want to see in DOS file names */
96 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
98 /* at some point we may want to allow Winelib apps to set this */
99 static const BOOL is_case_sensitive = FALSE;
102 * Directory info for DOSFS_ReadDir
103 * contains the names of *all* the files in the directory
105 typedef struct
107 int used;
108 int size;
109 WCHAR names[1];
110 } DOS_DIR;
113 /* return non-zero if c is the end of a directory name */
114 static inline int is_end_of_name(WCHAR c)
116 return !c || (c == '/') || (c == '\\');
119 /***********************************************************************
120 * DOSFS_ValidDOSName
122 * Return 1 if Unix file 'name' is also a valid MS-DOS name
123 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
124 * File name can be terminated by '\0', '\\' or '/'.
126 static int DOSFS_ValidDOSName( LPCWSTR name )
128 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
129 const WCHAR *p = name;
130 const char *invalid = !is_case_sensitive ? (invalid_chars + 26) : invalid_chars;
131 int len = 0;
133 if (*p == '.')
135 /* Check for "." and ".." */
136 p++;
137 if (*p == '.') p++;
138 /* All other names beginning with '.' are invalid */
139 return (is_end_of_name(*p));
141 while (!is_end_of_name(*p))
143 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
144 if (*p == '.') break; /* Start of the extension */
145 if (++len > 8) return 0; /* Name too long */
146 p++;
148 if (*p != '.') return 1; /* End of name */
149 p++;
150 if (is_end_of_name(*p)) return 0; /* Empty extension not allowed */
151 len = 0;
152 while (!is_end_of_name(*p))
154 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
155 if (*p == '.') return 0; /* Second extension not allowed */
156 if (++len > 3) return 0; /* Extension too long */
157 p++;
159 return 1;
163 /***********************************************************************
164 * DOSFS_ToDosFCBFormat
166 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
167 * expanding wild cards and converting to upper-case in the process.
168 * File name can be terminated by '\0', '\\' or '/'.
169 * Return FALSE if the name is not a valid DOS name.
170 * 'buffer' must be at least 12 characters long.
172 static BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
174 static const char invalid_chars[] = INVALID_DOS_CHARS;
175 LPCWSTR p = name;
176 int i;
178 /* Check for "." and ".." */
179 if (*p == '.')
181 p++;
182 buffer[0] = '.';
183 for(i = 1; i < 11; i++) buffer[i] = ' ';
184 buffer[11] = 0;
185 if (*p == '.')
187 buffer[1] = '.';
188 p++;
190 return (!*p || (*p == '/') || (*p == '\\'));
193 for (i = 0; i < 8; i++)
195 switch(*p)
197 case '\0':
198 case '\\':
199 case '/':
200 case '.':
201 buffer[i] = ' ';
202 break;
203 case '?':
204 p++;
205 /* fall through */
206 case '*':
207 buffer[i] = '?';
208 break;
209 default:
210 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
211 buffer[i] = toupperW(*p);
212 p++;
213 break;
217 if (*p == '*')
219 /* Skip all chars after wildcard up to first dot */
220 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
222 else
224 /* Check if name too long */
225 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
227 if (*p == '.') p++; /* Skip dot */
229 for (i = 8; i < 11; i++)
231 switch(*p)
233 case '\0':
234 case '\\':
235 case '/':
236 buffer[i] = ' ';
237 break;
238 case '.':
239 return FALSE; /* Second extension not allowed */
240 case '?':
241 p++;
242 /* fall through */
243 case '*':
244 buffer[i] = '?';
245 break;
246 default:
247 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
248 buffer[i] = toupperW(*p);
249 p++;
250 break;
253 buffer[11] = '\0';
255 /* at most 3 character of the extension are processed
256 * is something behind this ?
258 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
259 return is_end_of_name(*p);
263 /***********************************************************************
264 * DOSFS_ToDosDTAFormat
266 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
267 * converting to upper-case in the process.
268 * File name can be terminated by '\0', '\\' or '/'.
269 * 'buffer' must be at least 13 characters long.
271 static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
273 LPWSTR p;
275 memcpy( buffer, name, 8 * sizeof(WCHAR) );
276 p = buffer + 8;
277 while ((p > buffer) && (p[-1] == ' ')) p--;
278 *p++ = '.';
279 memcpy( p, name + 8, 3 * sizeof(WCHAR) );
280 p += 3;
281 while (p[-1] == ' ') p--;
282 if (p[-1] == '.') p--;
283 *p = '\0';
287 /***********************************************************************
288 * DOSFS_AddDirEntry
290 * Used to construct an array of filenames in DOSFS_OpenDir
292 static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
294 int extra1 = strlenW(name) + 1;
295 int extra2 = strlenW(dosname) + 1;
297 /* if we need more, at minimum double the size */
298 if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
300 int more = (*dir)->size;
301 DOS_DIR *t;
303 if(more<(extra1+extra2))
304 more = extra1+extra2;
306 t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) +
307 ((*dir)->size + more)*sizeof(WCHAR) );
308 if(!t)
310 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
311 ERR("Out of memory caching directory structure %d %d %d\n",
312 (*dir)->size, more, (*dir)->used);
313 return FALSE;
315 (*dir) = t;
316 (*dir)->size += more;
319 /* at this point, the dir structure is big enough to hold these names */
320 strcpyW(&(*dir)->names[(*dir)->used], name);
321 (*dir)->used += extra1;
322 strcpyW(&(*dir)->names[(*dir)->used], dosname);
323 (*dir)->used += extra2;
325 return TRUE;
329 /***********************************************************************
330 * DOSFS_OpenDir_VFAT
332 static BOOL DOSFS_OpenDir_VFAT(DOS_DIR **dir, const char *unix_path)
334 #ifdef VFAT_IOCTL_READDIR_BOTH
335 KERNEL_DIRENT de[2];
336 int fd = open( unix_path, O_RDONLY|O_DIRECTORY );
337 BOOL r = TRUE;
339 /* Check if the VFAT ioctl is supported on this directory */
341 if ( fd<0 )
342 return FALSE;
344 while (1)
346 WCHAR long_name[MAX_PATH];
347 WCHAR short_name[12];
349 r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
350 if(!r)
351 break;
352 if (!de[0].d_reclen)
353 break;
354 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
355 if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
356 short_name[0] = '\0';
357 if (de[1].d_name[0])
358 MultiByteToWideChar(CP_UNIXCP, 0, de[1].d_name, -1, long_name, MAX_PATH);
359 else
360 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
361 r = DOSFS_AddDirEntry(dir, long_name, short_name );
362 if(!r)
363 break;
365 if(r)
367 static const WCHAR empty_strW[] = { 0 };
368 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
370 close(fd);
371 return r;
372 #else
373 return FALSE;
374 #endif /* VFAT_IOCTL_READDIR_BOTH */
378 /***********************************************************************
379 * DOSFS_OpenDir_Normal
381 * Now use the standard opendir/readdir interface
383 static BOOL DOSFS_OpenDir_Normal( DOS_DIR **dir, const char *unix_path )
385 DIR *unixdir = opendir( unix_path );
386 BOOL r = TRUE;
387 static const WCHAR empty_strW[] = { 0 };
389 if(!unixdir)
390 return FALSE;
391 while(1)
393 WCHAR long_name[MAX_PATH];
394 struct dirent *de = readdir(unixdir);
396 if(!de)
397 break;
398 MultiByteToWideChar(CP_UNIXCP, 0, de->d_name, -1, long_name, MAX_PATH);
399 r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
400 if(!r)
401 break;
403 if(r)
404 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
405 closedir(unixdir);
406 return r;
409 /***********************************************************************
410 * DOSFS_OpenDir
412 static DOS_DIR *DOSFS_OpenDir( const char *unix_path )
414 const int init_size = 0x100;
415 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size*sizeof (WCHAR));
416 BOOL r;
418 TRACE("%s\n",debugstr_a(unix_path));
420 if (!dir)
422 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
423 return NULL;
425 dir->used = 0;
426 dir->size = init_size;
428 /* Treat empty path as root directory. This simplifies path split into
429 directory and mask in several other places */
430 if (!*unix_path) unix_path = "/";
432 r = DOSFS_OpenDir_VFAT( &dir, unix_path);
434 if(!r)
435 r = DOSFS_OpenDir_Normal( &dir, unix_path);
437 if(!r)
439 HeapFree(GetProcessHeap(), 0, dir);
440 return NULL;
442 dir->used = 0;
444 return dir;
448 /***********************************************************************
449 * DOSFS_CloseDir
451 static void DOSFS_CloseDir( DOS_DIR *dir )
453 HeapFree( GetProcessHeap(), 0, dir );
457 /***********************************************************************
458 * DOSFS_ReadDir
460 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
461 LPCWSTR *short_name )
463 LPCWSTR sn, ln;
465 if (!dir)
466 return FALSE;
468 /* the long pathname is first */
469 ln = &dir->names[dir->used];
470 if(ln[0])
471 *long_name = ln;
472 else
473 return FALSE;
474 dir->used += (strlenW(ln) + 1);
476 /* followed by the short path name */
477 sn = &dir->names[dir->used];
478 if(sn[0])
479 *short_name = sn;
480 else
481 *short_name = NULL;
482 dir->used += (strlenW(sn) + 1);
484 return TRUE;
488 /***********************************************************************
489 * DOSFS_Hash
491 * Transform a Unix file name into a hashed DOS name. If the name is a valid
492 * DOS name, it is converted to upper-case; otherwise it is replaced by a
493 * hashed version that fits in 8.3 format.
494 * File name can be terminated by '\0', '\\' or '/'.
495 * 'buffer' must be at least 13 characters long.
497 static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format )
499 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
500 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
502 LPCWSTR p, ext;
503 LPWSTR dst;
504 unsigned short hash;
505 int i;
507 if (dir_format)
509 for(i = 0; i < 11; i++) buffer[i] = ' ';
510 buffer[11] = 0;
513 if (DOSFS_ValidDOSName( name ))
515 /* Check for '.' and '..' */
516 if (*name == '.')
518 buffer[0] = '.';
519 if (!dir_format) buffer[1] = buffer[2] = '\0';
520 if (name[1] == '.') buffer[1] = '.';
521 return;
524 /* Simply copy the name, converting to uppercase */
526 for (dst = buffer; !is_end_of_name(*name) && (*name != '.'); name++)
527 *dst++ = toupperW(*name);
528 if (*name == '.')
530 if (dir_format) dst = buffer + 8;
531 else *dst++ = '.';
532 for (name++; !is_end_of_name(*name); name++)
533 *dst++ = toupperW(*name);
535 if (!dir_format) *dst = '\0';
536 return;
539 /* Compute the hash code of the file name */
540 /* If you know something about hash functions, feel free to */
541 /* insert a better algorithm here... */
542 if (!is_case_sensitive)
544 for (p = name, hash = 0xbeef; !is_end_of_name(p[1]); p++)
545 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
546 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
548 else
550 for (p = name, hash = 0xbeef; !is_end_of_name(p[1]); p++)
551 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
552 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
555 /* Find last dot for start of the extension */
556 for (p = name+1, ext = NULL; !is_end_of_name(*p); p++)
557 if (*p == '.') ext = p;
558 if (ext && is_end_of_name(ext[1]))
559 ext = NULL; /* Empty extension ignored */
561 /* Copy first 4 chars, replacing invalid chars with '_' */
562 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
564 if (is_end_of_name(*p) || (p == ext)) break;
565 *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
567 /* Pad to 5 chars with '~' */
568 while (i-- >= 0) *dst++ = '~';
570 /* Insert hash code converted to 3 ASCII chars */
571 *dst++ = hash_chars[(hash >> 10) & 0x1f];
572 *dst++ = hash_chars[(hash >> 5) & 0x1f];
573 *dst++ = hash_chars[hash & 0x1f];
575 /* Copy the first 3 chars of the extension (if any) */
576 if (ext)
578 if (!dir_format) *dst++ = '.';
579 for (i = 3, ext++; (i > 0) && !is_end_of_name(*ext); i--, ext++)
580 *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
582 if (!dir_format) *dst = '\0';
586 /***********************************************************************
587 * DOSFS_FindUnixName
589 * Find the Unix file name in a given directory that corresponds to
590 * a file name (either in Unix or DOS format).
591 * File name can be terminated by '\0', '\\' or '/'.
592 * Return TRUE if OK, FALSE if no file name matches.
594 * 'long_buf' must be at least 'long_len' characters long. If the long name
595 * turns out to be larger than that, the function returns FALSE.
596 * 'short_buf' must be at least 13 characters long.
598 BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
599 INT long_len, LPWSTR short_buf )
601 DOS_DIR *dir;
602 LPCWSTR long_name, short_name;
603 WCHAR dos_name[12], tmp_buf[13];
604 BOOL ret;
606 LPCWSTR p = strchrW( name, '/' );
607 int len = p ? (int)(p - name) : strlenW(name);
608 if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
609 /* Ignore trailing dots and spaces */
610 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
611 if (long_len < len + 1) return FALSE;
613 TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
615 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
617 if (!(dir = DOSFS_OpenDir( path->long_name )))
619 WARN("(%s,%s): can't open dir: %s\n",
620 path->long_name, debugstr_w(name), strerror(errno) );
621 return FALSE;
624 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
626 /* Check against Unix name */
627 if (len == strlenW(long_name))
629 if (is_case_sensitive)
631 if (!strncmpW( long_name, name, len )) break;
633 else
635 if (!strncmpiW( long_name, name, len )) break;
638 if (dos_name[0])
640 /* Check against hashed DOS name */
641 if (!short_name)
643 DOSFS_Hash( long_name, tmp_buf, TRUE );
644 short_name = tmp_buf;
646 if (!strcmpW( dos_name, short_name )) break;
649 if (ret)
651 if (long_buf) WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1, long_buf, long_len, NULL, NULL);
652 if (short_buf)
654 if (short_name)
655 DOSFS_ToDosDTAFormat( short_name, short_buf );
656 else
657 DOSFS_Hash( long_name, short_buf, FALSE );
659 TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
660 debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
662 else
663 WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
664 DOSFS_CloseDir( dir );
665 return ret;
669 /***********************************************************************
670 * DOSFS_GetPathDrive
672 * Get the drive specified by a given path name (DOS or Unix format).
674 static int DOSFS_GetPathDrive( LPCWSTR *name )
676 int drive;
677 LPCWSTR p = *name;
679 if (*p && (p[1] == ':'))
681 drive = toupperW(*p) - 'A';
682 *name += 2;
684 else if (*p == '/') /* Absolute Unix path? */
686 if ((drive = DRIVE_FindDriveRootW( name )) == -1)
688 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
689 /* Assume it really was a DOS name */
690 drive = DRIVE_GetCurrentDrive();
693 else drive = DRIVE_GetCurrentDrive();
695 if (!DRIVE_IsValid(drive))
697 SetLastError( ERROR_INVALID_DRIVE );
698 return -1;
700 return drive;
704 /***********************************************************************
705 * DOSFS_GetFullName
707 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
708 * Unix name / short DOS name pair.
709 * Return FALSE if one of the path components does not exist. The last path
710 * component is only checked if 'check_last' is non-zero.
711 * The buffers pointed to by 'long_buf' and 'short_buf' must be
712 * at least MAX_PATHNAME_LEN long.
714 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
716 BOOL found;
717 char *p_l, *root;
718 LPWSTR p_s;
719 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
720 static const WCHAR dos_rootW[] = {'\\',0};
722 TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
724 if ((!*name) || (*name=='\n'))
725 { /* error code for Win98 */
726 SetLastError(ERROR_BAD_PATHNAME);
727 return FALSE;
730 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
732 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
733 sizeof(full->long_name) );
734 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
735 else root = full->long_name; /* root directory */
737 strcpyW( full->short_name, driveA_rootW );
738 full->short_name[0] += full->drive;
740 if ((*name == '\\') || (*name == '/')) /* Absolute path */
742 while ((*name == '\\') || (*name == '/')) name++;
744 else /* Relative path */
746 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
747 sizeof(full->long_name) - (root - full->long_name) - 1 );
748 if (root[1]) *root = '/';
749 lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
750 sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
753 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
754 : full->long_name;
755 p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
756 : full->short_name + 2;
757 found = TRUE;
759 while (*name && found)
761 /* Check for '.' and '..' */
763 if (*name == '.')
765 if (is_end_of_name(name[1]))
767 name++;
768 while ((*name == '\\') || (*name == '/')) name++;
769 continue;
771 else if ((name[1] == '.') && is_end_of_name(name[2]))
773 name += 2;
774 while ((*name == '\\') || (*name == '/')) name++;
775 while ((p_l > root) && (*p_l != '/')) p_l--;
776 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
777 *p_l = *p_s = '\0'; /* Remove trailing separator */
778 continue;
782 /* Make sure buffers are large enough */
784 if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
785 (p_l >= full->long_name + sizeof(full->long_name) - 1))
787 SetLastError( ERROR_PATH_NOT_FOUND );
788 return FALSE;
791 /* Get the long and short name matching the file name */
793 if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
794 sizeof(full->long_name) - (p_l - full->long_name) - 1, p_s + 1 )))
796 *p_l++ = '/';
797 p_l += strlen(p_l);
798 *p_s++ = '\\';
799 p_s += strlenW(p_s);
800 while (!is_end_of_name(*name)) name++;
802 else if (!check_last)
804 *p_l++ = '/';
805 *p_s++ = '\\';
806 while (!is_end_of_name(*name) &&
807 (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
808 (p_l < full->long_name + sizeof(full->long_name) - 1))
810 WCHAR wch;
811 *p_s++ = tolowerW(*name);
812 /* If the drive is case-sensitive we want to create new */
813 /* files in lower-case otherwise we can't reopen them */
814 /* under the same short name. */
815 if (is_case_sensitive) wch = tolowerW(*name);
816 else wch = *name;
817 p_l += WideCharToMultiByte(CP_UNIXCP, 0, &wch, 1, p_l, 2, NULL, NULL);
818 name++;
820 /* Ignore trailing dots and spaces */
821 while(p_l[-1] == '.' || p_l[-1] == ' ') {
822 --p_l;
823 --p_s;
825 *p_l = '\0';
826 *p_s = '\0';
828 while ((*name == '\\') || (*name == '/')) name++;
831 if (!found)
833 if (check_last)
835 SetLastError( ERROR_FILE_NOT_FOUND );
836 return FALSE;
838 if (*name) /* Not last */
840 SetLastError( ERROR_PATH_NOT_FOUND );
841 return FALSE;
844 if (!full->long_name[0]) strcpy( full->long_name, "/" );
845 if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
846 TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
847 return TRUE;
851 /***********************************************************************
852 * MulDiv (KERNEL32.@)
853 * RETURNS
854 * Result of multiplication and division
855 * -1: Overflow occurred or Divisor was 0
857 INT WINAPI MulDiv(
858 INT nMultiplicand,
859 INT nMultiplier,
860 INT nDivisor)
862 LONGLONG ret;
864 if (!nDivisor) return -1;
866 /* We want to deal with a positive divisor to simplify the logic. */
867 if (nDivisor < 0)
869 nMultiplicand = - nMultiplicand;
870 nDivisor = -nDivisor;
873 /* If the result is positive, we "add" to round. else, we subtract to round. */
874 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
875 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
876 ret = (((LONGLONG)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
877 else
878 ret = (((LONGLONG)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
880 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
881 return ret;
885 /***********************************************************************
886 * DosDateTimeToFileTime (KERNEL32.@)
888 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
890 struct tm newtm;
891 #ifndef HAVE_TIMEGM
892 struct tm *gtm;
893 time_t time1, time2;
894 #endif
896 newtm.tm_sec = (fattime & 0x1f) * 2;
897 newtm.tm_min = (fattime >> 5) & 0x3f;
898 newtm.tm_hour = (fattime >> 11);
899 newtm.tm_mday = (fatdate & 0x1f);
900 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
901 newtm.tm_year = (fatdate >> 9) + 80;
902 #ifdef HAVE_TIMEGM
903 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
904 #else
905 time1 = mktime(&newtm);
906 gtm = gmtime(&time1);
907 time2 = mktime(gtm);
908 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
909 #endif
910 return TRUE;
914 /***********************************************************************
915 * FileTimeToDosDateTime (KERNEL32.@)
917 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
918 LPWORD fattime )
920 LARGE_INTEGER li;
921 ULONG t;
922 time_t unixtime;
923 struct tm* tm;
925 li.u.LowPart = ft->dwLowDateTime;
926 li.u.HighPart = ft->dwHighDateTime;
927 RtlTimeToSecondsSince1970( &li, &t );
928 unixtime = t;
929 tm = gmtime( &unixtime );
930 if (fattime)
931 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
932 if (fatdate)
933 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
934 + tm->tm_mday;
935 return TRUE;