xref: /reactos/sdk/lib/ucrt/misc/getcwd.cpp (revision fe11f7a2)
1 /***
2 *getcwd.cpp - get current working directory
3 *
4 *       Copyright (c) Microsoft Corporation. All rights reserved.
5 *
6 *Purpose:
7 *
8 *       contains functions _getcwd, _getdcwd and _getcdrv for getting the
9 *       current working directory.  getcwd gets the c.w.d. for the default disk
10 *       drive, whereas _getdcwd allows one to get the c.w.d. for whatever disk
11 *       drive is specified. _getcdrv gets the current drive.
12 *
13 *******************************************************************************/
14 #include <corecrt_internal.h>
15 #include <corecrt_internal_traits.h>
16 #include <direct.h>
17 #include <errno.h>
18 #include <malloc.h>
19 #include <stdlib.h>
20 
21 
22 
23 // Tests whether the specified drive number is valid.  Returns zero if the drive
24 // does not exist; returns one if the drive exists.  Pass zero as an argument to
25 // check the default drive.
26 static int __cdecl is_valid_drive(unsigned const drive_number) throw()
27 {
28     if (drive_number > 26)
29     {
30         _doserrno = ERROR_INVALID_DRIVE;
31         _VALIDATE_RETURN(("Invalid Drive Index" ,0), EACCES, 0);
32     }
33 
34     if (drive_number == 0)
35         return 1;
36 
37 #pragma warning(suppress:__WARNING_UNUSED_ASSIGNMENT) // 28931
38     wchar_t const drive_letter   = static_cast<wchar_t>(L'A' + drive_number - 1);
39     wchar_t const drive_string[] = { drive_letter, L':', L'\\', L'\0' };
40 
41     UINT const drive_type = GetDriveTypeW(drive_string);
42     if (drive_type == DRIVE_UNKNOWN || drive_type == DRIVE_NO_ROOT_DIR)
43         return 0;
44 
45     return 1;
46 }
47 
48 
49 
50 /***
51 *_TSCHAR *_getcwd(pnbuf, maxlen) - get current working directory of default drive
52 *
53 *Purpose:
54 *       _getcwd gets the current working directory for the user,
55 *       placing it in the buffer pointed to by pnbuf. If the length
56 *       of the string exceeds the length of the buffer, maxlen,
57 *       then nullptr is returned. If pnbuf = nullptr, a buffer of at
58 *       least size maxlen is automatically allocated using
59 *       malloc() -- a pointer to which is returned by _getcwd().
60 *       An entry point "_getdcwd()" is defined which takes the above
61 *       parameters, plus a drive number.  "_getcwd()" is implemented
62 *       as a call to "_getdcwd()" with the default drive (0).
63 *
64 *       side effects: no global data is used or affected
65 *
66 *Entry:
67 *       _TSCHAR *pnbuf = pointer to a buffer maintained by the user;
68 *       int maxlen = length of the buffer pointed to by pnbuf;
69 *
70 *Exit:
71 *       Returns pointer to the buffer containing the c.w.d. name
72 *       (same as pnbuf if non-nullptr; otherwise, malloc is
73 *       used to allocate a buffer)
74 *
75 *Exceptions:
76 *
77 *******************************************************************************/
78 
79 /***
80 *_TSCHAR *_getdcwd(drive, pnbuf, maxlen) - get c.w.d. for given drive
81 *
82 *Purpose:
83 *       _getdcwd gets the current working directory for the user,
84 *       placing it in the buffer pointed to by pnbuf. If the length
85 *       of the string exceeds the length of the buffer, maxlen,
86 *       then nullptr is returned. If pnbuf = nullptr, a buffer of at
87 *       least size maxlen is automatically allocated using
88 *       malloc() -- a pointer to which is returned by _getdcwd().
89 *
90 *       side effects: no global data is used or affected
91 *
92 *Entry:
93 *       int drive   - number of the drive being inquired about
94 *                     0 = default, 1 = 'a:', 2 = 'b:', etc.
95 *       _TSCHAR *pnbuf - pointer to a buffer maintained by the user;
96 *       int maxlen  - length of the buffer pointed to by pnbuf;
97 *
98 *Exit:
99 *       Returns pointer to the buffer containing the c.w.d. name
100 *       (same as pnbuf if non-nullptr; otherwise, malloc is
101 *       used to allocate a buffer)
102 *
103 *Exceptions:
104 *
105 *******************************************************************************/
106 
107 template <typename Character>
108 _Success_(return != 0)
109 _Ret_z_
110 static Character* __cdecl common_getdcwd(
111     int                                            drive_number,
112     _Out_writes_opt_z_(max_count) Character*       user_buffer,
113     int                                      const max_count,
114     int                                      const block_use,
115     _In_opt_z_ char const*                   const file_name,
116     int                                      const line_number
117     ) throw()
118 {
119     typedef __crt_char_traits<Character> traits;
120 
121     _VALIDATE_RETURN(max_count >= 0, EINVAL, nullptr);
122 
123     if (drive_number != 0)
124     {
125         // A drive other than the default drive was requested; make sure it
126         // is a valid drive number:
127         if (!is_valid_drive(drive_number))
128         {
129             _doserrno = ERROR_INVALID_DRIVE;
130             _VALIDATE_RETURN(("Invalid Drive", 0), EACCES, nullptr);
131         }
132     }
133     else
134     {
135         // Otherwise, get the drive number of the default drive:
136         drive_number = _getdrive();
137     }
138 
139     Character drive_string[4];
140     if (drive_number != 0)
141     {
142         drive_string[0] = static_cast<Character>('A' - 1 + drive_number);
143         drive_string[1] = ':';
144         drive_string[2] = '.';
145         drive_string[3] = '\0';
146     }
147     else
148     {
149         drive_string[0] = '.';
150         drive_string[1] = '\0';
151     }
152 
153     if (user_buffer == nullptr)
154     {   // Always new memory suitable for debug mode and releasing to the user.
155         __crt_public_win32_buffer<Character> buffer(
156             __crt_win32_buffer_debug_info(block_use, file_name, line_number)
157         );
158         buffer.allocate(max_count);
159         if (!traits::get_full_path_name(drive_string, buffer))
160         {
161             return buffer.detach();
162         }
163         return nullptr;
164     }
165 
166     // Using user buffer. Fail if not enough space.
167     _VALIDATE_RETURN(max_count > 0, EINVAL, nullptr);
168     user_buffer[0] = '\0';
169 
170     __crt_no_alloc_win32_buffer<Character> buffer(user_buffer, max_count);
171     if (!traits::get_full_path_name(drive_string, buffer))
172     {
173         return user_buffer;
174     }
175     return nullptr;
176 };
177 
178 
179 
180 extern "C" char* __cdecl _getcwd(
181     char* const user_buffer,
182     int   const max_length
183     )
184 {
185     return common_getdcwd(0, user_buffer, max_length, _NORMAL_BLOCK, nullptr, 0);
186 }
187 
188 extern "C" wchar_t* __cdecl _wgetcwd(
189     wchar_t* const user_buffer,
190     int      const max_length
191     )
192 {
193     return common_getdcwd(0, user_buffer, max_length, _NORMAL_BLOCK, nullptr, 0);
194 }
195 
196 extern "C" char* __cdecl _getdcwd(
197     int   const drive_number,
198     char* const user_buffer,
199     int   const max_length
200     )
201 {
202     return common_getdcwd(drive_number, user_buffer, max_length, _NORMAL_BLOCK, nullptr, 0);
203 }
204 
205 extern "C" wchar_t* __cdecl _wgetdcwd(
206     int      const drive_number,
207     wchar_t* const user_buffer,
208     int      const max_length
209     )
210 {
211     return common_getdcwd(drive_number, user_buffer, max_length, _NORMAL_BLOCK, nullptr, 0);
212 }
213 
214 #ifdef _DEBUG
215 
216 #undef _getcwd_dbg
217 #undef _getdcwd_dbg
218 
219 extern "C" char* __cdecl _getcwd_dbg(
220     char*       const user_buffer,
221     int         const max_length,
222     int         const block_use,
223     char const* const file_name,
224     int         const line_number
225     )
226 {
227     return common_getdcwd(0, user_buffer, max_length, block_use, file_name, line_number);
228 }
229 
230 extern "C" wchar_t* __cdecl _wgetcwd_dbg(
231     wchar_t*       const user_buffer,
232     int            const max_length,
233     int            const block_use,
234     char const*    const file_name,
235     int            const line_number
236     )
237 {
238     return common_getdcwd(0, user_buffer, max_length, block_use, file_name, line_number);
239 }
240 
241 extern "C" char* __cdecl _getdcwd_dbg(
242     int         const drive_number,
243     char*       const user_buffer,
244     int         const max_length,
245     int         const block_use,
246     char const* const file_name,
247     int         const line_number
248     )
249 {
250     return common_getdcwd(drive_number, user_buffer, max_length, block_use, file_name, line_number);
251 }
252 
253 extern "C" wchar_t* __cdecl _wgetdcwd_dbg(
254     int         const drive_number,
255     wchar_t*    const user_buffer,
256     int         const max_length,
257     int         const block_use,
258     char const* const file_name,
259     int         const line_number
260     )
261 {
262     return common_getdcwd(drive_number, user_buffer, max_length, block_use, file_name, line_number);
263 }
264 
265 #endif // _DEBUG
266