- Fix _fullpath & splitpath, winapi_check fixes
[wine] / dlls / crtdll / dir.c
1 /*
2  * CRTDLL drive/directory functions
3  * 
4  * Copyright 1996,1998 Marcus Meissner
5  * Copyright 1996 Jukka Iivonen
6  * Copyright 1997,2000 Uwe Bonnes
7  * Copyright 2000 Jon Griffiths
8  *
9  *
10  * Implementation Notes:
11  * MT Safe.
12  */
13
14 #include "crtdll.h"
15 #include <errno.h>
16
17 #include "ntddk.h"
18 #include <time.h>
19
20 DEFAULT_DEBUG_CHANNEL(crtdll);
21
22 /* INTERNAL: Translate find_t to PWIN32_FIND_DATAA */
23 static void __CRTDLL__fttofd(LPWIN32_FIND_DATAA fd, find_t* ft)
24 {
25   DWORD dw;
26
27   /* Tested with crtdll.dll Version 2.50.4170 (NT) from win98 SE:
28    * attrib 0x80 (FILE_ATTRIBUTE_NORMAL)is returned as 0.
29    */
30   if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL)
31     ft->attrib = 0;
32   else
33     ft->attrib = fd->dwFileAttributes;
34
35   RtlTimeToSecondsSince1970( &fd->ftCreationTime, &dw );
36   ft->time_create = dw;
37   RtlTimeToSecondsSince1970( &fd->ftLastAccessTime, &dw );
38   ft->time_access = dw;
39   RtlTimeToSecondsSince1970( &fd->ftLastWriteTime, &dw );
40   ft->time_write = dw;
41   ft->size = fd->nFileSizeLow;
42   strcpy(ft->name, fd->cFileName);
43 }
44
45
46 /*********************************************************************
47  *                  _chdir           (CRTDLL.51)
48  *
49  * Change the current directory.
50  *
51  * PARAMS
52  *   newdir [in] Directory to change to
53  *
54  * RETURNS
55  * Sucess:  0
56  * 
57  * Failure: -1
58  */
59 INT __cdecl CRTDLL__chdir(LPCSTR newdir)
60 {
61   if (!SetCurrentDirectoryA(newdir))
62   {
63     __CRTDLL__set_errno(newdir?GetLastError():0);
64     return -1;
65   }
66   return 0;
67 }
68
69
70 /*********************************************************************
71  *                  _chdrive           (CRTDLL.52)
72  *
73  * Change the current drive.
74  *
75  * PARAMS
76  *   newdrive [in] new drive to change to, A: =1, B: =2, etc
77  *
78  * RETURNS
79  * Sucess:  0
80  * 
81  * Failure: 1 
82  */
83 BOOL __cdecl CRTDLL__chdrive(INT newdrive)
84 {
85   char buffer[3] = "A:";
86   buffer[0] += newdrive - 1;
87   if (!SetCurrentDirectoryA( buffer ))
88   {
89     __CRTDLL__set_errno(GetLastError());
90     if (newdrive <= 0)
91       CRTDLL_errno = EACCES;
92     return -1;
93   }
94   return 0;
95 }
96
97
98 /*********************************************************************
99  *                  _findclose     (CRTDLL.098)
100  * 
101  * Free the resources from a search handle created from _findfirst.
102  *
103  * PARAMS
104  *   hand [in]: Search handle to close
105  *
106  * RETURNS
107  * Success:  0
108  *
109  * Failure:  -1
110  */
111 INT __cdecl CRTDLL__findclose(DWORD hand)
112 {
113   TRACE(":handle %ld\n",hand);
114   if (!FindClose((HANDLE)hand))
115   {
116     __CRTDLL__set_errno(GetLastError());
117     return -1;
118   }
119   return 0;
120 }
121
122
123  /*********************************************************************
124  *                  _findfirst    (CRTDLL.099)
125  *
126  * Create and return a search handle for iterating through a file and
127  * directory list.
128  *
129  * PARAMS
130  *   fspec [in]  File specification string for search, e.g "C:\*.BAT"
131  * 
132  *   ft [out]    A pointer to a find_t structure to populate.
133  *
134  * RETURNS
135  * Success:  A handle for the search, suitable for passing to _findnext
136  *           or _findclose. Populates the members of ft with the details
137  *           of the first matching file.
138  *
139  * Failure:  -1.
140  */
141 DWORD __cdecl CRTDLL__findfirst(LPCSTR fspec, find_t* ft)
142 {
143   WIN32_FIND_DATAA find_data;
144   HANDLE hfind;
145
146   hfind  = FindFirstFileA(fspec, &find_data);
147   if (hfind == INVALID_HANDLE_VALUE)
148   {
149     __CRTDLL__set_errno(GetLastError());
150     return -1;
151   }
152   __CRTDLL__fttofd(&find_data,ft);
153   TRACE(":got handle %d\n",hfind);
154   return hfind;
155 }
156
157
158 /*********************************************************************
159  *                  _findnext     (CRTDLL.100)
160  * 
161  * Return the next matching file/directory from a search hadle.
162  *
163  * PARAMS
164  *   hand [in] Search handle from a pervious call to _findfirst
165  * 
166  *   ft [out]  A pointer to a find_t structure to populate.
167  *
168  * RETURNS
169  * Success:  0. Populates the members of ft with the details
170  *           of the first matching file
171  *
172  * Failure:  -1
173  */
174 INT __cdecl CRTDLL__findnext(DWORD hand, find_t * ft)
175 {
176   WIN32_FIND_DATAA find_data;
177
178   if (!FindNextFileA(hand, &find_data))
179   {
180     SetLastError(ERROR_INVALID_DRIVE);
181     __CRTDLL__set_errno(GetLastError());
182     return -1;
183   }
184
185   __CRTDLL__fttofd(&find_data,ft);
186   return 0;
187 }
188
189
190 /*********************************************************************
191  *                  _getcwd           (CRTDLL.120)
192  *
193  * Get the current directory.
194  *
195  * PARAMS
196  * buf [out]  A buffer to place the current directory name in
197  *
198  * size [in]  The size of buf.
199  *
200  * RETURNS
201  * Success: buf, or if buf is NULL, an allocated buffer
202  *
203  * Failure: NULL
204  */
205 CHAR* __cdecl CRTDLL__getcwd(LPSTR buf, INT size)
206 {
207   char dir[_MAX_PATH];
208   int dir_len = GetCurrentDirectoryA(MAX_PATH,dir);
209
210   if (dir_len < 1)
211     return NULL; /* FIXME: Real return value untested */
212
213   TRACE(":returning '%s'\n", dir);
214
215   if (!buf)
216   {
217     if (size < 0)
218       return CRTDLL__strdup(dir);
219     return __CRTDLL__strndup(dir,size);
220   }
221   if (dir_len >= size)
222   {
223     CRTDLL_errno = ERANGE;
224     return NULL; /* buf too small */
225   }
226   strcpy(buf,dir);
227   return buf;
228 }
229
230
231 /*********************************************************************
232  *                  _getdcwd           (CRTDLL.121)
233  *
234  * Get the current directory on a drive. A: =1, B: =2, etc.
235  * Passing drive 0 means the current drive.
236  */
237 CHAR* __cdecl CRTDLL__getdcwd(INT drive, LPSTR buf, INT size)
238 {
239   static CHAR* dummy;
240
241   TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size);
242
243   if (!drive || drive == CRTDLL__getdrive())
244     return CRTDLL__getcwd(buf,size); /* current */
245   else
246   {
247     char dir[_MAX_PATH];
248     char drivespec[4] = {'A', ':', '\\', 0};
249     int dir_len;
250
251     drivespec[0] += drive - 1;
252     if (GetDriveTypeA(drivespec) < DRIVE_REMOVABLE)
253     {
254       CRTDLL_errno = EACCES;
255       return NULL;
256     }
257
258     dir_len = GetFullPathNameA(drivespec,_MAX_PATH,dir,&dummy);
259     if (dir_len >= size || dir_len < 1)
260     {
261       CRTDLL_errno = ERANGE;
262       return NULL; /* buf too small */
263     }
264
265     TRACE(":returning '%s'\n", dir);
266     if (!buf)
267       return CRTDLL__strdup(dir); /* allocate */
268
269     strcpy(buf,dir);
270   }
271   return buf;
272 }
273
274
275 /*********************************************************************
276  *                  _getdiskfree     (CRTDLL.122)
277  *
278  * Get free disk space on given drive or the current drive.
279  *
280  */
281 UINT __cdecl CRTDLL__getdiskfree(UINT disk, diskfree_t* d)
282 {
283   char drivespec[4] = {'@', ':', '\\', 0};
284   DWORD ret[4];
285   UINT err;
286
287   if (disk > 26)
288     return ERROR_INVALID_PARAMETER; /* CRTDLL doesn't set errno here */
289
290   drivespec[0] += disk; /* make a drive letter */
291
292   if (GetDiskFreeSpaceA(disk==0?NULL:drivespec,ret,ret+1,ret+2,ret+3))
293   {
294     d->cluster_sectors = (unsigned)ret[0];
295     d->sector_bytes = (unsigned)ret[1];
296     d->available = (unsigned)ret[2];
297     d->num_clusters = (unsigned)ret[3];
298     return 0;
299   }
300   err = GetLastError();
301   __CRTDLL__set_errno(err);
302   return err;
303 }
304
305
306 /*********************************************************************
307  *                  _getdrive           (CRTDLL.124)
308  *
309  *  Return current drive, A: =1, B: =2, etc
310  */
311 INT __cdecl CRTDLL__getdrive(VOID)
312 {
313     char buffer[MAX_PATH];
314     if (!GetCurrentDirectoryA( sizeof(buffer), buffer )) return 0;
315     if (buffer[1] != ':') return 0;
316     return toupper(buffer[0]) - 'A' + 1;
317 }
318
319
320 /*********************************************************************
321  *                  _mkdir           (CRTDLL.234)
322  *
323  * Create a directory.
324  */
325 INT __cdecl CRTDLL__mkdir(LPCSTR newdir)
326 {
327   if (CreateDirectoryA(newdir,NULL))
328     return 0;
329
330   __CRTDLL__set_errno(GetLastError());
331   return -1;
332 }
333
334 /*********************************************************************
335  *                  _rmdir           (CRTDLL.255)
336  *
337  * Delete a directory 
338  *
339  */
340 INT __cdecl CRTDLL__rmdir(LPSTR dir)
341 {
342   if (RemoveDirectoryA(dir))
343     return 0;
344
345   __CRTDLL__set_errno(GetLastError());
346   return -1;
347 }
348