[Tizen] Unify dnetmemoryenumlib terms to match the codebase (#291)
[platform/upstream/coreclr.git] / src / utilcode / splitpath.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 /***
5 *splitpath.c - break down path name into components
6 *
7
8 *
9 *Purpose:
10 *       To provide support for accessing the individual components of an
11 *       arbitrary path name
12 *
13 *******************************************************************************/
14 #include "stdafx.h"
15 #include "winwrap.h"
16 #include "utilcode.h"
17 #include "sstring.h"
18
19
20 /***
21 *SplitPath() - split a path name into its individual components
22 *
23 *Purpose:
24 *       to split a path name into its individual components
25 *
26 *Entry:
27 *       path  - pointer to path name to be parsed
28 *       drive - pointer to buffer for drive component, if any
29 *       dir   - pointer to buffer for subdirectory component, if any
30 *       fname - pointer to buffer for file base name component, if any
31 *       ext   - pointer to buffer for file name extension component, if any
32 *
33 *Exit:
34 *       drive - pointer to drive string.  Includes ':' if a drive was given.
35 *       dir   - pointer to subdirectory string.  Includes leading and trailing
36 *           '/' or '\', if any.
37 *       fname - pointer to file base name
38 *       ext   - pointer to file extension, if any.  Includes leading '.'.
39 *
40 *Exceptions:
41 *
42 *******************************************************************************/
43
44 void SplitPath(
45         const WCHAR *path,
46         __inout_z __inout_ecount_opt(driveSizeInWords) WCHAR *drive, int driveSizeInWords,
47         __inout_z __inout_ecount_opt(dirSizeInWords) WCHAR *dir, int dirSizeInWords,
48         __inout_z __inout_ecount_opt(fnameSizeInWords) WCHAR *fname, size_t fnameSizeInWords,
49         __inout_z __inout_ecount_opt(extSizeInWords) WCHAR *ext, size_t extSizeInWords)
50 {
51     WRAPPER_NO_CONTRACT;
52
53     LPCWSTR _wszDrive, _wszDir, _wszFileName, _wszExt;
54     size_t _cchDrive, _cchDir, _cchFileName, _cchExt;
55
56     SplitPathInterior(path,
57                       &_wszDrive, &_cchDrive,
58                       &_wszDir, &_cchDir,
59                       &_wszFileName, &_cchFileName,
60                       &_wszExt, &_cchExt);
61
62     if (drive && _wszDrive)
63         wcsncpy_s(drive, driveSizeInWords, _wszDrive, min(_cchDrive, _MAX_DRIVE));
64
65     if (dir && _wszDir)
66         wcsncpy_s(dir, dirSizeInWords, _wszDir, min(_cchDir, _MAX_DIR));
67
68     if (fname && _wszFileName)
69         wcsncpy_s(fname, fnameSizeInWords, _wszFileName, min(_cchFileName, _MAX_FNAME));
70
71     if (ext && _wszExt)
72         wcsncpy_s(ext, extSizeInWords, _wszExt, min(_cchExt, _MAX_EXT));
73 }
74
75 //*******************************************************************************
76 // A much more sensible version that just points to each section of the string.
77 //*******************************************************************************
78 void    SplitPathInterior(
79     __in      LPCWSTR wszPath,
80     __out_opt LPCWSTR *pwszDrive,    __out_opt size_t *pcchDrive,
81     __out_opt LPCWSTR *pwszDir,      __out_opt size_t *pcchDir,
82     __out_opt LPCWSTR *pwszFileName, __out_opt size_t *pcchFileName,
83     __out_opt LPCWSTR *pwszExt,      __out_opt size_t *pcchExt)
84 {
85     LIMITED_METHOD_CONTRACT;
86
87     // Arguments must come in valid pairs
88     _ASSERTE(!!pwszDrive == !!pcchDrive);
89     _ASSERTE(!!pwszDir == !!pcchDir);
90     _ASSERTE(!!pwszFileName == !!pcchFileName);
91     _ASSERTE(!!pwszExt == !!pcchExt);
92
93     WCHAR *p;
94     LPCWSTR last_slash = NULL, dot = NULL;
95
96     /* we assume that the path argument has the following form, where any
97      * or all of the components may be missing.
98      *
99      *  <drive><dir><fname><ext>
100      *
101      * and each of the components has the following expected form(s)
102      *
103      *  drive:
104      *  0 to _MAX_DRIVE-1 characters, the last of which, if any, is a
105      *  ':'
106      *  dir:
107      *  0 to _MAX_DIR-1 characters in the form of an absolute path
108      *  (leading '/' or '\') or relative path, the last of which, if
109      *  any, must be a '/' or '\'.  E.g -
110      *  absolute path:
111      *      \top\next\last\     ; or
112      *      /top/next/last/
113      *  relative path:
114      *      top\next\last\  ; or
115      *      top/next/last/
116      *  Mixed use of '/' and '\' within a path is also tolerated
117      *  fname:
118      *  0 to _MAX_FNAME-1 characters not including the '.' character
119      *  ext:
120      *  0 to _MAX_EXT-1 characters where, if any, the first must be a
121      *  '.'
122      *
123      */
124
125     /* extract drive letter and :, if any */
126
127     if ((wcslen(wszPath) > (_MAX_DRIVE - 2)) && (*(wszPath + _MAX_DRIVE - 2) == _T(':'))) {
128         if (pwszDrive && pcchDrive) {
129             *pwszDrive = wszPath;
130             *pcchDrive = _MAX_DRIVE - 1;
131         }
132         wszPath += _MAX_DRIVE - 1;
133     }
134     else if (pwszDrive && pcchDrive) {
135         *pwszDrive = NULL;
136         *pcchDrive = 0;
137     }
138
139     /* extract path string, if any.  Path now points to the first character
140      * of the path, if any, or the filename or extension, if no path was
141      * specified.  Scan ahead for the last occurence, if any, of a '/' or
142      * '\' path separator character.  If none is found, there is no path.
143      * We will also note the last '.' character found, if any, to aid in
144      * handling the extension.
145      */
146
147     for (last_slash = NULL, p = (WCHAR *)wszPath; *p; p++) {
148 #ifdef _MBCS
149         if (_ISLEADBYTE (*p))
150             p++;
151         else {
152 #endif  /* _MBCS */
153         if (*p == _T('/') || *p == _T('\\'))
154             /* point to one beyond for later copy */
155             last_slash = p + 1;
156         else if (*p == _T('.'))
157             dot = p;
158 #ifdef _MBCS
159         }
160 #endif  /* _MBCS */
161     }
162
163     if (last_slash) {
164         /* found a path - copy up through last_slash or max. characters
165          * allowed, whichever is smaller
166          */
167
168         if (pwszDir && pcchDir) {
169             *pwszDir = wszPath;
170             *pcchDir = last_slash - wszPath;
171         }
172         wszPath = last_slash;
173     }
174     else if (pwszDir && pcchDir) {
175         *pwszDir = NULL;
176         *pcchDir = 0;
177     }
178
179     /* extract file name and extension, if any.  Path now points to the
180      * first character of the file name, if any, or the extension if no
181      * file name was given.  Dot points to the '.' beginning the extension,
182      * if any.
183      */
184
185     if (dot && (dot >= wszPath)) {
186         /* found the marker for an extension - copy the file name up to
187          * the '.'.
188          */
189         if (pwszFileName && pcchFileName) {
190             *pwszFileName = wszPath;
191             *pcchFileName = dot - wszPath;
192         }
193         /* now we can get the extension - remember that p still points
194          * to the terminating nul character of path.
195          */
196         if (pwszExt && pcchExt) {
197             *pwszExt = dot;
198             *pcchExt = p - dot;
199         }
200     }
201     else {
202         /* found no extension, give empty extension and copy rest of
203          * string into fname.
204          */
205         if (pwszFileName && pcchFileName) {
206             *pwszFileName = wszPath;
207             *pcchFileName = p - wszPath;
208         }
209         if (pwszExt && pcchExt) {
210             *pwszExt = NULL;
211             *pcchExt = 0;
212         }
213     }
214 }
215
216 /***
217 *SplitPath() - split a path name into its individual components
218 *
219 *Purpose:
220 *       to split a path name into its individual components
221 *
222 *Entry:
223 *       path  - SString representing the path name to be parsed
224 *       drive - Out SString for drive component
225 *       dir   - Out SString for subdirectory component
226 *       fname - Out SString for file base name component
227 *       ext   - Out SString for file name extension component
228 *
229 *Exit:
230 *       drive - Drive string.  Includes ':' if a drive was given.
231 *       dir   - Subdirectory string.  Includes leading and trailing
232 *           '/' or '\', if any.
233 *       fname - File base name
234 *       ext   - File extension, if any.  Includes leading '.'.
235 *
236 *Exceptions:
237 *
238 *******************************************************************************/
239
240 void    SplitPath(__in SString const &path,
241                   __inout_opt SString *drive,
242                   __inout_opt SString *dir,
243                   __inout_opt SString *fname,
244                   __inout_opt SString *ext)
245 {
246     LPCWSTR wzDrive, wzDir, wzFname, wzExt;
247     size_t cchDrive, cchDir, cchFname, cchExt;
248
249     SplitPathInterior(path,
250             &wzDrive, &cchDrive,
251             &wzDir, &cchDir,
252             &wzFname, &cchFname,
253             &wzExt, &cchExt);
254
255     if (drive != NULL)
256         drive->Set(wzDrive, (COUNT_T)cchDrive);
257
258     if (dir != NULL)
259         dir->Set(wzDir, (COUNT_T)cchDir);
260
261     if (fname != NULL)
262         fname->Set(wzFname, (COUNT_T)cchFname);
263
264     if (ext != NULL)
265         ext->Set(wzExt, (COUNT_T)cchExt);
266 }
267