Added version information.
[wine] / dlls / ntdll / path.c
1 /*
2  * Ntdll path functions
3  *
4  * Copyright 2002, 2003 Alexandre Julliard
5  * Copyright 2003 Eric Pouech
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 "winternl.h"
25 #include "wine/unicode.h"
26 #include "wine/debug.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(file);
29
30 #define IS_SEPARATOR(ch)  ((ch) == '\\' || (ch) == '/')
31
32 /***********************************************************************
33  *             RtlDetermineDosPathNameType_U   (NTDLL.@)
34  */
35 DOS_PATHNAME_TYPE WINAPI RtlDetermineDosPathNameType_U( PCWSTR path )
36 {
37     if (IS_SEPARATOR(path[0]))
38     {
39         if (!IS_SEPARATOR(path[1])) return ABSOLUTE_PATH;       /* "/foo" */
40         if (path[2] != '.') return UNC_PATH;                    /* "//foo" */
41         if (IS_SEPARATOR(path[3])) return DEVICE_PATH;          /* "//./foo" */
42         if (path[3]) return UNC_PATH;                           /* "//.foo" */
43         return UNC_DOT_PATH;                                    /* "//." */
44     }
45     else
46     {
47         if (!path[0] || path[1] != ':') return RELATIVE_PATH;   /* "foo" */
48         if (IS_SEPARATOR(path[2])) return ABSOLUTE_DRIVE_PATH;  /* "c:/foo" */
49         return RELATIVE_DRIVE_PATH;                             /* "c:foo" */
50     }
51 }
52
53
54 /***********************************************************************
55  *             RtlIsDosDeviceName_U   (NTDLL.@)
56  *
57  * Check if the given DOS path contains a DOS device name.
58  *
59  * Returns the length of the device name in the low word and its
60  * position in the high word (both in bytes, not WCHARs), or 0 if no
61  * device name is found.
62  */
63 ULONG WINAPI RtlIsDosDeviceName_U( PCWSTR dos_name )
64 {
65     static const WCHAR consoleW[] = {'\\','\\','.','\\','C','O','N',0};
66     static const WCHAR auxW[3] = {'A','U','X'};
67     static const WCHAR comW[3] = {'C','O','M'};
68     static const WCHAR conW[3] = {'C','O','N'};
69     static const WCHAR lptW[3] = {'L','P','T'};
70     static const WCHAR nulW[3] = {'N','U','L'};
71     static const WCHAR prnW[3] = {'P','R','N'};
72
73     const WCHAR *start, *end, *p;
74
75     switch(RtlDetermineDosPathNameType_U( dos_name ))
76     {
77     case INVALID_PATH:
78     case UNC_PATH:
79         return 0;
80     case DEVICE_PATH:
81         if (!strcmpiW( dos_name, consoleW ))
82             return MAKELONG( sizeof(conW), 4 * sizeof(WCHAR) );  /* 4 is length of \\.\ prefix */
83         return 0;
84     default:
85         break;
86     }
87
88     end = dos_name + strlenW(dos_name) - 1;
89     if (end >= dos_name && *end == ':') end--;  /* remove trailing ':' */
90
91     /* find start of file name */
92     for (start = end; start >= dos_name; start--)
93     {
94         if (IS_SEPARATOR(start[0])) break;
95         /* check for ':' but ignore if before extension (for things like NUL:.txt) */
96         if (start[0] == ':' && start[1] != '.') break;
97     }
98     start++;
99
100     /* remove extension */
101     if ((p = strchrW( start, '.' )))
102     {
103         end = p - 1;
104         if (end >= dos_name && *end == ':') end--;  /* remove trailing ':' before extension */
105     }
106     else
107     {
108         /* no extension, remove trailing spaces */
109         while (end >= dos_name && *end == ' ') end--;
110     }
111
112     /* now we have a potential device name between start and end, check it */
113     switch(end - start + 1)
114     {
115     case 3:
116         if (strncmpiW( start, auxW, 3 ) &&
117             strncmpiW( start, conW, 3 ) &&
118             strncmpiW( start, nulW, 3 ) &&
119             strncmpiW( start, prnW, 3 )) break;
120         return MAKELONG( 3 * sizeof(WCHAR), (start - dos_name) * sizeof(WCHAR) );
121     case 4:
122         if (strncmpiW( start, comW, 3 ) && strncmpiW( start, lptW, 3 )) break;
123         if (*end <= '0' || *end > '9') break;
124         return MAKELONG( 4 * sizeof(WCHAR), (start - dos_name) * sizeof(WCHAR) );
125     default:  /* can't match anything */
126         break;
127     }
128     return 0;
129 }
130
131
132 /******************************************************************
133  *             RtlIsNameLegalDOS8Dot3   (NTDLL.@)
134  *
135  * Returns TRUE iff unicode is a valid DOS (8+3) name.
136  * If the name is valid, oem gets filled with the corresponding OEM string
137  * spaces is set to TRUE if unicode contains spaces
138  */
139 BOOLEAN WINAPI RtlIsNameLegalDOS8Dot3( const UNICODE_STRING *unicode,
140                                        OEM_STRING *oem, BOOLEAN *spaces )
141 {
142     int dot = -1;
143     unsigned int i;
144     char buffer[12];
145     OEM_STRING oem_str;
146     BOOLEAN got_space = FALSE;
147
148     if (!oem)
149     {
150         oem_str.Length = sizeof(buffer);
151         oem_str.MaximumLength = sizeof(buffer);
152         oem_str.Buffer = buffer;
153         oem = &oem_str;
154     }
155     if (RtlUpcaseUnicodeStringToCountedOemString( oem, unicode, FALSE ) != STATUS_SUCCESS)
156         return FALSE;
157
158     if (oem->Length > 12) return FALSE;
159
160     /* a starting . is invalid, except for . and .. */
161     if (oem->Buffer[0] == '.')
162     {
163         if (oem->Length != 1 && (oem->Length != 2 || oem->Buffer[1] != '.')) return FALSE;
164         if (spaces) *spaces = FALSE;
165         return TRUE;
166     }
167
168     for (i = 0; i < oem->Length; i++)
169     {
170         switch (oem->Buffer[i])
171         {
172         case ' ':
173             /* leading/trailing spaces not allowed */
174             if (!i || i == oem->Length-1 || oem->Buffer[i+1] == '.') return FALSE;
175             got_space = TRUE;
176             break;
177         case '.':
178             if (dot != -1) return FALSE;
179             dot = i;
180             break;
181         default:
182             /* FIXME: check for invalid chars */
183             break;
184         }
185     }
186     /* check file part is shorter than 8, extension shorter than 3
187      * dot cannot be last in string
188      */
189     if (dot == -1)
190     {
191         if (oem->Length > 8) return FALSE;
192     }
193     else
194     {
195         if (dot > 8 || (oem->Length - dot > 4) || dot == oem->Length - 1) return FALSE;
196     }
197     if (spaces) *spaces = got_space;
198     return TRUE;
199 }