Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
7.0 KiB

  1. /***
  2. *splitpath.c - break down path name into components
  3. *
  4. * Copyright (c) 1987-2001, Microsoft Corporation. All rights reserved.
  5. *
  6. *Purpose:
  7. * To provide support for accessing the individual components of an
  8. * arbitrary path name
  9. *
  10. *Revision History:
  11. * 06-14-87 DFW initial implementation
  12. * 09-23-87 JCR Removed 'const' from declarations (fixed cl warnings)
  13. * 12-11-87 JCR Added "_LOAD_DS" to declaration
  14. * 11-20-89 GJF Fixed indents, copyright. Added const attribute to
  15. * type of path.
  16. * 03-15-90 GJF Replaced _LOAD_DS with _CALLTYPE1 and added #include
  17. * <cruntime.h>.
  18. * 07-25-90 SBM Removed redundant include (stdio.h), replaced local
  19. * MIN macro with standard min macro
  20. * 10-04-90 GJF New-style function declarator.
  21. * 01-22-91 GJF ANSI naming.
  22. * 11-20-92 KRS Port _MBCS support from 16-bit tree.
  23. * 05-12-93 KRS Add fix for MBCS max path handling.
  24. * 12-07-93 CFW Wide char enable.
  25. * 10-15-95 BWT _NTSUBSET_ doesn't do MBCS here.
  26. * 09-09-96 JWM Test length of input string before accessing (Orion 7985).
  27. * 04-28-98 GJF No more _ISLEADBYTE macro.
  28. *
  29. *******************************************************************************/
  30. #ifdef _NTSUBSET_
  31. #undef _MBCS
  32. #endif
  33. #include <cruntime.h>
  34. #include <stdlib.h>
  35. #include <string.h>
  36. #ifdef _MBCS
  37. #include <mbstring.h>
  38. #include <mbctype.h>
  39. #include <mbdata.h>
  40. #endif
  41. #include <tchar.h>
  42. /***
  43. *_splitpath() - split a path name into its individual components
  44. *
  45. *Purpose:
  46. * to split a path name into its individual components
  47. *
  48. *Entry:
  49. * path - pointer to path name to be parsed
  50. * drive - pointer to buffer for drive component, if any
  51. * dir - pointer to buffer for subdirectory component, if any
  52. * fname - pointer to buffer for file base name component, if any
  53. * ext - pointer to buffer for file name extension component, if any
  54. *
  55. *Exit:
  56. * drive - pointer to drive string. Includes ':' if a drive was given.
  57. * dir - pointer to subdirectory string. Includes leading and trailing
  58. * '/' or '\', if any.
  59. * fname - pointer to file base name
  60. * ext - pointer to file extension, if any. Includes leading '.'.
  61. *
  62. *Exceptions:
  63. *
  64. *******************************************************************************/
  65. void __cdecl _tsplitpath (
  66. register const _TSCHAR *path,
  67. _TSCHAR *drive,
  68. _TSCHAR *dir,
  69. _TSCHAR *fname,
  70. _TSCHAR *ext
  71. )
  72. {
  73. register _TSCHAR *p;
  74. _TSCHAR *last_slash = NULL, *dot = NULL;
  75. unsigned len;
  76. /* we assume that the path argument has the following form, where any
  77. * or all of the components may be missing.
  78. *
  79. * <drive><dir><fname><ext>
  80. *
  81. * and each of the components has the following expected form(s)
  82. *
  83. * drive:
  84. * 0 to _MAX_DRIVE-1 characters, the last of which, if any, is a
  85. * ':'
  86. * dir:
  87. * 0 to _MAX_DIR-1 characters in the form of an absolute path
  88. * (leading '/' or '\') or relative path, the last of which, if
  89. * any, must be a '/' or '\'. E.g -
  90. * absolute path:
  91. * \top\next\last\ ; or
  92. * /top/next/last/
  93. * relative path:
  94. * top\next\last\ ; or
  95. * top/next/last/
  96. * Mixed use of '/' and '\' within a path is also tolerated
  97. * fname:
  98. * 0 to _MAX_FNAME-1 characters not including the '.' character
  99. * ext:
  100. * 0 to _MAX_EXT-1 characters where, if any, the first must be a
  101. * '.'
  102. *
  103. */
  104. /* extract drive letter and :, if any */
  105. if ((_tcslen(path) >= (_MAX_DRIVE - 2)) && (*(path + _MAX_DRIVE - 2) == _T(':'))) {
  106. if (drive) {
  107. _tcsncpy(drive, path, _MAX_DRIVE - 1);
  108. *(drive + _MAX_DRIVE-1) = _T('\0');
  109. }
  110. path += _MAX_DRIVE - 1;
  111. }
  112. else if (drive) {
  113. *drive = _T('\0');
  114. }
  115. /* extract path string, if any. Path now points to the first character
  116. * of the path, if any, or the filename or extension, if no path was
  117. * specified. Scan ahead for the last occurence, if any, of a '/' or
  118. * '\' path separator character. If none is found, there is no path.
  119. * We will also note the last '.' character found, if any, to aid in
  120. * handling the extension.
  121. */
  122. for (last_slash = NULL, p = (_TSCHAR *)path; *p; p++) {
  123. #ifdef _MBCS
  124. if (_ismbblead(*p))
  125. p++;
  126. else {
  127. #endif
  128. if (*p == _T('/') || *p == _T('\\'))
  129. /* point to one beyond for later copy */
  130. last_slash = p + 1;
  131. else if (*p == _T('.'))
  132. dot = p;
  133. #ifdef _MBCS
  134. }
  135. #endif
  136. }
  137. if (last_slash) {
  138. /* found a path - copy up through last_slash or max. characters
  139. * allowed, whichever is smaller
  140. */
  141. if (dir) {
  142. len = __min((unsigned)(((char *)last_slash - (char *)path) / sizeof(_TSCHAR)),
  143. (_MAX_DIR - 1));
  144. _tcsncpy(dir, path, len);
  145. *(dir + len) = _T('\0');
  146. }
  147. path = last_slash;
  148. }
  149. else if (dir) {
  150. /* no path found */
  151. *dir = _T('\0');
  152. }
  153. /* extract file name and extension, if any. Path now points to the
  154. * first character of the file name, if any, or the extension if no
  155. * file name was given. Dot points to the '.' beginning the extension,
  156. * if any.
  157. */
  158. if (dot && (dot >= path)) {
  159. /* found the marker for an extension - copy the file name up to
  160. * the '.'.
  161. */
  162. if (fname) {
  163. len = __min((unsigned)(((char *)dot - (char *)path) / sizeof(_TSCHAR)),
  164. (_MAX_FNAME - 1));
  165. _tcsncpy(fname, path, len);
  166. *(fname + len) = _T('\0');
  167. }
  168. /* now we can get the extension - remember that p still points
  169. * to the terminating nul character of path.
  170. */
  171. if (ext) {
  172. len = __min((unsigned)(((char *)p - (char *)dot) / sizeof(_TSCHAR)),
  173. (_MAX_EXT - 1));
  174. _tcsncpy(ext, dot, len);
  175. *(ext + len) = _T('\0');
  176. }
  177. }
  178. else {
  179. /* found no extension, give empty extension and copy rest of
  180. * string into fname.
  181. */
  182. if (fname) {
  183. len = __min((unsigned)(((char *)p - (char *)path) / sizeof(_TSCHAR)),
  184. (_MAX_FNAME - 1));
  185. _tcsncpy(fname, path, len);
  186. *(fname + len) = _T('\0');
  187. }
  188. if (ext) {
  189. *ext = _T('\0');
  190. }
  191. }
  192. }