path.c 64 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. path.c
  5. Abstract:
  6. This module implements path traversal and other path utilities for the
  7. shell.
  8. Author:
  9. Evan Green 11-Jun-2013
  10. Environment:
  11. POSIX
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. #include <ctype.h>
  17. #include <dirent.h>
  18. #include <sys/stat.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <unistd.h>
  22. #include <errno.h>
  23. #include <assert.h>
  24. #include "sh.h"
  25. #include "../swlib.h"
  26. //
  27. // --------------------------------------------------------------------- Macros
  28. //
  29. //
  30. // This macro determines if the given path string starts with a relative
  31. // component, a dot or a dot dot.
  32. //
  33. #define PATH_IS_RELATIVE_TO_CURRENT(_Path) \
  34. (((_Path)[0] == '.') && \
  35. (((_Path)[1] == '\0') || ((_Path)[1] == '/') || \
  36. (((_Path)[1] == '.') && \
  37. (((_Path)[2] == '\0') || ((_Path)[2] == '/')))))
  38. //
  39. // ---------------------------------------------------------------- Definitions
  40. //
  41. #define SHELL_DIRECTORY_NAMES_INITIAL_LENGTH 256
  42. #define SHELL_DIRECTORY_INITIAL_ELEMENT_COUNT 16
  43. #define SHELL_INITIAL_PATH_BUFFER_SIZE 256
  44. #define SHELL_INITIAL_PATH_LIST_SIZE 16
  45. //
  46. // ------------------------------------------------------ Data Type Definitions
  47. //
  48. //
  49. // ----------------------------------------------- Internal Function Prototypes
  50. //
  51. BOOL
  52. ShExpandPath (
  53. PSHELL Shell,
  54. PSTR Prefix,
  55. ULONG PrefixSize,
  56. PSTR Field,
  57. PSTR *FilesStringBuffer,
  58. PULONG FilesStringBufferSize,
  59. PSTR **FilesArray,
  60. PULONG FilesArrayCount
  61. );
  62. BOOL
  63. ShArePatternCharactersInPath (
  64. PSTR Path,
  65. ULONG PathSize
  66. );
  67. BOOL
  68. ShPathCombineLists (
  69. PSTR *ListBuffer,
  70. PULONG ListBufferSize,
  71. PULONG ListBufferCapacity,
  72. PSTR **List,
  73. PULONG ListSize,
  74. PULONG ListCapacity,
  75. PSTR SecondListBuffer,
  76. ULONG SecondListBufferSize,
  77. PSTR *SecondList,
  78. ULONG SecondListSize
  79. );
  80. BOOL
  81. ShLocateDirectoryOnCdPath (
  82. PSHELL Shell,
  83. PSTR Directory,
  84. UINTN DirectorySize,
  85. PSTR *FullDirectoryPath,
  86. PUINTN FullDirectoryPathSize
  87. );
  88. INT
  89. ShCleanLogicalDirectoryPath (
  90. PSTR PathString,
  91. UINTN PathStringSize,
  92. PSTR *CleanedPathString,
  93. PUINTN CleanedPathStringSize
  94. );
  95. PSTR
  96. ShPathGetNextComponent (
  97. PSTR Field,
  98. PBOOL HasMetaCharacters
  99. );
  100. int
  101. ShPathCompareStrings (
  102. const void *LeftString,
  103. const void *RightString
  104. );
  105. //
  106. // -------------------------------------------------------------------- Globals
  107. //
  108. //
  109. // ------------------------------------------------------------------ Functions
  110. //
  111. BOOL
  112. ShGetCurrentDirectory (
  113. PSTR *Directory,
  114. PUINTN DirectorySize
  115. )
  116. /*++
  117. Routine Description:
  118. This routine gets a listing of the files in the current directory.
  119. Arguments:
  120. Directory - Supplies a pointer where the current directory path will be
  121. returned. The caller is responsible for freeing this memory.
  122. DirectorySize - Supplies a pointer where the size of the directory string
  123. including the null terminator will be returned on success.
  124. Return Value:
  125. TRUE on success.
  126. FALSE on failure.
  127. --*/
  128. {
  129. PSTR Buffer;
  130. UINTN Capacity;
  131. unsigned long PathSize;
  132. *Directory = NULL;
  133. *DirectorySize = 0;
  134. Capacity = SHELL_INITIAL_PATH_BUFFER_SIZE;
  135. Buffer = malloc(Capacity);
  136. if (Buffer == NULL) {
  137. return FALSE;
  138. }
  139. while (TRUE) {
  140. if (getcwd(Buffer, Capacity) != Buffer) {
  141. if (errno != ERANGE) {
  142. free(Buffer);
  143. return FALSE;
  144. }
  145. Capacity *= 2;
  146. Buffer = realloc(Buffer, Capacity);
  147. if (Buffer == NULL) {
  148. return FALSE;
  149. }
  150. } else {
  151. *Directory = Buffer;
  152. PathSize = strlen(Buffer) + 1;
  153. ShFixUpPath(Directory, &PathSize);
  154. *DirectorySize = PathSize;
  155. return TRUE;
  156. }
  157. }
  158. return FALSE;
  159. }
  160. BOOL
  161. ShGetDirectoryListing (
  162. PSTR DirectoryPath,
  163. PSTR *FileNamesBuffer,
  164. PSHELL_DIRECTORY_ENTRY *Elements,
  165. PULONG ElementCount
  166. )
  167. /*++
  168. Routine Description:
  169. This routine gets a listing of the files in the current directory.
  170. Arguments:
  171. DirectoryPath - Supplies a pointer to the string containing the directory
  172. to list.
  173. FileNamesBuffer - Supplies a pointer where a pointer to the files names
  174. will be returned on success. The elements array will contain pointers
  175. into the buffer. The caller is responsible for freeing this memory. A
  176. size is not returned because the caller is not expected to dereference
  177. into this memory directly.
  178. Elements - Supplies a pointer where the array of file names will be
  179. returned on success. The caller is responsible for freeing this buffer,
  180. which can be accomplished by freeing the first element.
  181. ElementCount - Supplies a pointer where the number of elements in the array
  182. will be returned.
  183. Return Value:
  184. TRUE on success.
  185. FALSE on failure.
  186. --*/
  187. {
  188. PSTR CurrentFileName;
  189. DIR *Directory;
  190. PSHELL_DIRECTORY_ENTRY Entries;
  191. struct dirent *Entry;
  192. UINTN EntryCapacity;
  193. UINTN EntrySize;
  194. PSTR FileNames;
  195. UINTN FileNamesCapacity;
  196. UINTN FileNamesSize;
  197. ULONG FixIndex;
  198. UINTN NameSize;
  199. UINTN NewBufferSize;
  200. UINTN OriginalFileNames;
  201. BOOL Result;
  202. Entries = NULL;
  203. EntryCapacity = 0;
  204. EntrySize = 0;
  205. FileNames = NULL;
  206. FileNamesSize = 0;
  207. FileNamesCapacity = 0;
  208. //
  209. // Use the current directory given no other information.
  210. //
  211. if (DirectoryPath == NULL) {
  212. DirectoryPath = ".";
  213. }
  214. //
  215. // Open up the directory and loop reading entries.
  216. //
  217. Directory = opendir(DirectoryPath);
  218. if (Directory == NULL) {
  219. Result = FALSE;
  220. goto GetDirectoryListingEnd;
  221. }
  222. while (TRUE) {
  223. Entry = readdir(Directory);
  224. if (Entry == NULL) {
  225. break;
  226. }
  227. //
  228. // Write the string to the big buffer, expanding the buffer if needed.
  229. //
  230. NameSize = strlen(Entry->d_name) + 1;
  231. if (FileNamesSize + NameSize > FileNamesCapacity) {
  232. NewBufferSize = FileNamesCapacity;
  233. if (NewBufferSize == 0) {
  234. assert(FileNames == NULL);
  235. NewBufferSize = SHELL_DIRECTORY_NAMES_INITIAL_LENGTH;
  236. }
  237. while (NewBufferSize < FileNamesSize + NameSize) {
  238. NewBufferSize *= 2;
  239. }
  240. OriginalFileNames = (UINTN)FileNames;
  241. FileNames = realloc(FileNames, NewBufferSize);
  242. if (FileNames == NULL) {
  243. Result = FALSE;
  244. goto GetDirectoryListingEnd;
  245. }
  246. FileNamesCapacity = NewBufferSize;
  247. //
  248. // Fix up all the entries so far.
  249. //
  250. for (FixIndex = 0; FixIndex < EntrySize; FixIndex += 1) {
  251. Entries[FixIndex].Name = FileNames +
  252. ((UINTN)(Entries[FixIndex].Name) -
  253. OriginalFileNames);
  254. }
  255. }
  256. CurrentFileName = FileNames + FileNamesSize;
  257. strcpy(CurrentFileName, Entry->d_name);
  258. FileNamesSize += NameSize;
  259. assert(FileNamesSize <= FileNamesCapacity);
  260. //
  261. // Write the entry to the array, expanding it if needed.
  262. //
  263. if (EntrySize + 1 > EntryCapacity) {
  264. NewBufferSize = EntryCapacity * 2;
  265. if (NewBufferSize == 0) {
  266. assert(Entries == NULL);
  267. NewBufferSize = SHELL_DIRECTORY_INITIAL_ELEMENT_COUNT;
  268. }
  269. Entries = realloc(Entries,
  270. NewBufferSize * sizeof(SHELL_DIRECTORY_ENTRY));
  271. if (Entries == NULL) {
  272. Result = FALSE;
  273. goto GetDirectoryListingEnd;
  274. }
  275. EntryCapacity = NewBufferSize;
  276. }
  277. Entries[EntrySize].Name = CurrentFileName;
  278. Entries[EntrySize].NameSize = NameSize;
  279. EntrySize += 1;
  280. }
  281. Result = TRUE;
  282. GetDirectoryListingEnd:
  283. if (Directory != NULL) {
  284. closedir(Directory);
  285. }
  286. if (Result == FALSE) {
  287. if (Entries != NULL) {
  288. free(Entries);
  289. Entries = NULL;
  290. }
  291. EntryCapacity = 0;
  292. EntrySize = 0;
  293. if (FileNames != NULL) {
  294. free(FileNames);
  295. FileNames = NULL;
  296. }
  297. FileNamesSize = 0;
  298. FileNamesCapacity = 0;
  299. }
  300. *FileNamesBuffer = FileNames;
  301. *Elements = Entries;
  302. *ElementCount = EntrySize;
  303. return Result;
  304. }
  305. BOOL
  306. ShPerformPathExpansions (
  307. PSHELL Shell,
  308. PSTR *StringBuffer,
  309. PUINTN StringBufferSize,
  310. PSTR **FieldArray,
  311. PULONG FieldArrayCount
  312. )
  313. /*++
  314. Routine Description:
  315. This routine performs pathname expansion on the fields in the given field
  316. array.
  317. Arguments:
  318. Shell - Supplies a pointer to the shell.
  319. StringBuffer - Supplies a pointer where the address of the fields string
  320. buffer is on input. On output, this may contain a different buffer that
  321. all the fields point into.
  322. StringBufferSize - Supplies a pointer that contains the size of the fields
  323. string buffer. This value will be updated to reflect the new size.
  324. FieldArray - Supplies a pointer to the array of string pointers of the
  325. fields on input. This array may get replaced if more elements need to
  326. be added for paths.
  327. FieldArrayCount - Supplies a pointer that contains the number of field
  328. elements on input. This value will be updated to reflect the number of
  329. fields after pathname expansion.
  330. Return Value:
  331. TRUE on success.
  332. FALSE on failure.
  333. --*/
  334. {
  335. ULONG Delta;
  336. ULONG FieldCapacity;
  337. ULONG FieldCount;
  338. ULONG FieldEnd;
  339. ULONG FieldIndex;
  340. ULONG FieldOffset;
  341. PSTR *Fields;
  342. ULONG FileCount;
  343. ULONG FileOffset;
  344. PSTR *Files;
  345. PSTR FilesString;
  346. ULONG FilesStringSize;
  347. ULONG FixIndex;
  348. PVOID NewBuffer;
  349. UINTN OriginalStringAddress;
  350. BOOL Result;
  351. PSTR String;
  352. UINTN StringCapacity;
  353. UINTN StringSize;
  354. FieldCount = *FieldArrayCount;
  355. FieldCapacity = FieldCount;
  356. Fields = *FieldArray;
  357. Files = NULL;
  358. FilesString = NULL;
  359. String = *StringBuffer;
  360. StringSize = *StringBufferSize;
  361. StringCapacity = StringSize;
  362. for (FieldIndex = 0; FieldIndex < FieldCount; FieldIndex += 1) {
  363. //
  364. // Get the array of files that expand out under this path.
  365. //
  366. Result = ShExpandPath(Shell,
  367. NULL,
  368. 0,
  369. Fields[FieldIndex],
  370. &FilesString,
  371. &FilesStringSize,
  372. &Files,
  373. &FileCount);
  374. if (Result == FALSE) {
  375. ShPrintTrace(Shell,
  376. "Failed to expand path '%s'",
  377. Fields[FieldIndex]);
  378. goto PerformPathExpansionsEnd;
  379. }
  380. //
  381. // If this expands out to zero files, leave the field alone and move
  382. // on.
  383. //
  384. if (FileCount == 0) {
  385. continue;
  386. }
  387. //
  388. // Sort the array. Scripts have come to rely on this behavior.
  389. //
  390. qsort(Files, FileCount, sizeof(char *), ShPathCompareStrings);
  391. //
  392. // Replace the portion of the string.
  393. //
  394. OriginalStringAddress = (UINTN)String;
  395. FieldOffset = (UINTN)(Fields[FieldIndex]) - OriginalStringAddress;
  396. FieldEnd = FieldOffset + strlen(Fields[FieldIndex]);
  397. Result = SwStringReplaceRegion(&String,
  398. &StringSize,
  399. &StringCapacity,
  400. FieldOffset,
  401. FieldEnd,
  402. FilesString,
  403. FilesStringSize);
  404. if (Result == FALSE) {
  405. goto PerformPathExpansionsEnd;
  406. }
  407. FilesStringSize -= 1;
  408. //
  409. // Expand the capacity of the fields if needed.
  410. //
  411. if (FieldCount + FileCount > FieldCapacity) {
  412. while (FieldCount + FileCount + 1 > FieldCapacity) {
  413. FieldCapacity *= 2;
  414. }
  415. NewBuffer = realloc(Fields, FieldCapacity * sizeof(PSTR));
  416. if (Fields == NULL) {
  417. Result = FALSE;
  418. goto PerformPathExpansionsEnd;
  419. }
  420. Fields = NewBuffer;
  421. memset(Fields + FieldCount,
  422. 0,
  423. (FieldCapacity - FieldCount) * sizeof(PSTR));
  424. }
  425. //
  426. // Fix up the field pointers in three parts. The first part is fields
  427. // after the expansion, which need to be both shifted out in index and
  428. // adjusted to the new string and new size of that string.
  429. //
  430. Delta = FilesStringSize - (FieldEnd - FieldOffset);
  431. FieldCount += FileCount - 1;
  432. for (FixIndex = FieldCount - 1;
  433. FixIndex >= FieldIndex + FileCount;
  434. FixIndex -= 1) {
  435. FieldOffset = (UINTN)(Fields[FixIndex - (FileCount - 1)]) -
  436. OriginalStringAddress;
  437. Fields[FixIndex] = String + FieldOffset + Delta;
  438. }
  439. //
  440. // Fix up the fields that came before the expansion, which only really
  441. // have to watch out for being in a new buffer potentially.
  442. //
  443. for (FixIndex = 0; FixIndex < FieldIndex; FixIndex += 1) {
  444. FieldOffset = (UINTN)(Fields[FixIndex]) - OriginalStringAddress;
  445. Fields[FixIndex] = String + FieldOffset;
  446. }
  447. //
  448. // Fix up fields in the expansion.
  449. //
  450. FieldOffset = (UINTN)(Fields[FieldIndex]) - OriginalStringAddress;
  451. for (FixIndex = FieldIndex;
  452. FixIndex < FieldIndex + FileCount;
  453. FixIndex += 1) {
  454. //
  455. // Get the offset of this file from the file string buffer.
  456. //
  457. FileOffset = ((UINTN)(Files[FixIndex - FieldIndex]) -
  458. (UINTN)FilesString);
  459. //
  460. // The offset into the big string is the original offset of this
  461. // field plus the offset into the file buffer that got spliced in.
  462. //
  463. Fields[FixIndex] = String + FieldOffset + FileOffset;
  464. }
  465. //
  466. // Set the next field index to look at to just after this new rash of
  467. // things.
  468. //
  469. FieldIndex += FileCount - 1;
  470. }
  471. Result = TRUE;
  472. PerformPathExpansionsEnd:
  473. if (FilesString != NULL) {
  474. free(FilesString);
  475. }
  476. if (Files != NULL) {
  477. free(Files);
  478. }
  479. *StringBuffer = String;
  480. *StringBufferSize = StringSize;
  481. *FieldArray = Fields;
  482. *FieldArrayCount = FieldCount;
  483. return Result;
  484. }
  485. BOOL
  486. ShLocateCommand (
  487. PSHELL Shell,
  488. PSTR Command,
  489. ULONG CommandSize,
  490. BOOL MustBeExecutable,
  491. PSTR *FullCommand,
  492. PULONG FullCommandSize,
  493. PINT ReturnValue
  494. )
  495. /*++
  496. Routine Description:
  497. This routine locates a command using the PATH environment variable.
  498. Arguments:
  499. Shell - Supplies a pointer to the shell.
  500. Command - Supplies a pointer to the command as seen from the command line.
  501. CommandSize - Supplies the size of the command string in bytes.
  502. MustBeExecutable - Supplies a boolean indicating if the given file must be
  503. executable or not.
  504. FullCommand - Supplies a pointer where a pointer to the full command string
  505. will be returned on success. If this is not the same pointer as the
  506. command string then the caller is responsible for freeing this buffer.
  507. FullCommandSize - Supplies a pointer where the size of the full command
  508. string will be returned.
  509. ReturnValue - Supplies a pointer where a premature return value will be
  510. returned. If this is not zero, then it contains the value that should
  511. be returned without trying to execute the command. On success, this
  512. variable will not be touched.
  513. Return Value:
  514. TRUE on success.
  515. FALSE on failure.
  516. --*/
  517. {
  518. PSTR CompletePath;
  519. ULONG CompletePathSize;
  520. PSTR CurrentPath;
  521. ULONG CurrentPathSize;
  522. PSTR ExtendedPath;
  523. PSTR Extension;
  524. ULONG ExtensionIndex;
  525. ULONG ExtensionLength;
  526. PSTR *ExtensionList;
  527. unsigned int ExtensionListCount;
  528. CHAR ListSeparator;
  529. PSTR NextListSeparator;
  530. PSTR Path;
  531. UINTN PathSize;
  532. BOOL Result;
  533. struct stat Stat;
  534. INT Status;
  535. *FullCommand = NULL;
  536. *FullCommandSize = 0;
  537. *ReturnValue = 0;
  538. CompletePath = NULL;
  539. ExtendedPath = NULL;
  540. ShGetExecutableExtensions(&ExtensionList, &ExtensionListCount);
  541. ListSeparator = PATH_LIST_SEPARATOR;
  542. if (ShExecutableBitSupported == 0) {
  543. MustBeExecutable = FALSE;
  544. }
  545. //
  546. // If there command has a slash, then don't use the path variable, just go
  547. // for it directly.
  548. //
  549. if (SwDoesPathHaveSeparators(Command) != 0) {
  550. Status = SwStat(Command, TRUE, &Stat);
  551. if ((Status == 0) && (S_ISREG(Stat.st_mode)) &&
  552. ((MustBeExecutable == FALSE) || ((Stat.st_mode & S_IXUSR) != 0))) {
  553. *FullCommand = Command;
  554. *FullCommandSize = CommandSize;
  555. Result = TRUE;
  556. goto LocateCommandEnd;
  557. }
  558. //
  559. // Fail if the file is there but not executable.
  560. //
  561. if ((Status == 0) && (MustBeExecutable != FALSE) &&
  562. ((Stat.st_mode & S_IXUSR) == 0)) {
  563. *ReturnValue = SHELL_ERROR_EXECUTE;
  564. Result = TRUE;
  565. goto LocateCommandEnd;
  566. }
  567. //
  568. // Try that same thing with all the different extensions on it.
  569. //
  570. for (ExtensionIndex = 0;
  571. ExtensionIndex < ExtensionListCount;
  572. ExtensionIndex += 1) {
  573. Extension = ExtensionList[ExtensionIndex];
  574. ExtensionLength = strlen(Extension);
  575. ExtendedPath = malloc(CommandSize + ExtensionLength);
  576. if (ExtendedPath == NULL) {
  577. Result = FALSE;
  578. goto LocateCommandEnd;
  579. }
  580. memcpy(ExtendedPath, Command, CommandSize - 1);
  581. memcpy(ExtendedPath + CommandSize - 1, Extension, ExtensionLength);
  582. ExtendedPath[CommandSize + ExtensionLength - 1] = '\0';
  583. Status = SwStat(ExtendedPath, TRUE, &Stat);
  584. if ((Status == 0) && (S_ISREG(Stat.st_mode)) &&
  585. ((MustBeExecutable == FALSE) ||
  586. ((Stat.st_mode & S_IXUSR) != 0))) {
  587. *FullCommand = ExtendedPath;
  588. *FullCommandSize = CompletePathSize + ExtensionLength;
  589. ExtendedPath = NULL;
  590. Result = TRUE;
  591. goto LocateCommandEnd;
  592. }
  593. free(ExtendedPath);
  594. ExtendedPath = NULL;
  595. }
  596. *ReturnValue = SHELL_ERROR_OPEN;
  597. Result = TRUE;
  598. goto LocateCommandEnd;
  599. }
  600. //
  601. // Get the PATH environment variable.
  602. //
  603. Result = ShGetVariable(Shell,
  604. SHELL_PATH,
  605. sizeof(SHELL_PATH),
  606. &Path,
  607. &PathSize);
  608. //
  609. // If the path variable couldn't be found or is empty, then just return.
  610. //
  611. if ((Result == FALSE) || (Path == NULL) || (PathSize <= 1)) {
  612. Status = SwStat(Command, TRUE, &Stat);
  613. if ((Status < 0) || (!S_ISREG(Stat.st_mode))) {
  614. *ReturnValue = SHELL_ERROR_OPEN;
  615. } else if ((MustBeExecutable != FALSE) &&
  616. ((Stat.st_mode & S_IXUSR) == 0)) {
  617. *ReturnValue = SHELL_ERROR_EXECUTE;
  618. } else {
  619. *FullCommand = Command;
  620. *FullCommandSize = CommandSize;
  621. }
  622. Result = TRUE;
  623. goto LocateCommandEnd;
  624. }
  625. //
  626. // Loop through each entry in the path.
  627. //
  628. CurrentPath = Path;
  629. NextListSeparator = strchr(CurrentPath, ListSeparator);
  630. while (TRUE) {
  631. if (NextListSeparator == NULL) {
  632. CurrentPathSize = PathSize - ((UINTN)CurrentPath - (UINTN)Path);
  633. } else {
  634. CurrentPathSize = (UINTN)NextListSeparator - (UINTN)CurrentPath;
  635. }
  636. if (CurrentPathSize == 0) {
  637. CurrentPath = ".";
  638. CurrentPathSize = sizeof(".");
  639. }
  640. //
  641. // Make a complete command path out of this path entry and the command.
  642. //
  643. Result = SwAppendPath(CurrentPath,
  644. CurrentPathSize,
  645. Command,
  646. CommandSize,
  647. &CompletePath,
  648. &CompletePathSize);
  649. if (Result == FALSE) {
  650. goto LocateCommandEnd;
  651. }
  652. //
  653. // Figure out if this is something legit.
  654. //
  655. Status = SwStat(CompletePath, TRUE, &Stat);
  656. if ((Status == 0) && (S_ISREG(Stat.st_mode)) &&
  657. ((MustBeExecutable == FALSE) ||
  658. ((Stat.st_mode & S_IXUSR) != 0))) {
  659. *FullCommand = CompletePath;
  660. *FullCommandSize = CompletePathSize;
  661. CompletePath = NULL;
  662. Result = TRUE;
  663. goto LocateCommandEnd;
  664. }
  665. //
  666. // Try that same thing with all the different extensions on it.
  667. //
  668. for (ExtensionIndex = 0;
  669. ExtensionIndex < ExtensionListCount;
  670. ExtensionIndex += 1) {
  671. Extension = ExtensionList[ExtensionIndex];
  672. ExtensionLength = strlen(Extension);
  673. ExtendedPath = malloc(CompletePathSize + ExtensionLength);
  674. if (ExtendedPath == NULL) {
  675. Result = FALSE;
  676. goto LocateCommandEnd;
  677. }
  678. memcpy(ExtendedPath, CompletePath, CompletePathSize - 1);
  679. memcpy(ExtendedPath + CompletePathSize - 1,
  680. Extension,
  681. ExtensionLength);
  682. ExtendedPath[CompletePathSize + ExtensionLength - 1] = '\0';
  683. Status = SwStat(ExtendedPath, TRUE, &Stat);
  684. if ((Status == 0) && (S_ISREG(Stat.st_mode)) &&
  685. ((MustBeExecutable == FALSE) ||
  686. ((Stat.st_mode & S_IXUSR) != 0))) {
  687. *FullCommand = ExtendedPath;
  688. *FullCommandSize = CompletePathSize + ExtensionLength;
  689. ExtendedPath = NULL;
  690. Result = TRUE;
  691. goto LocateCommandEnd;
  692. }
  693. free(ExtendedPath);
  694. ExtendedPath = NULL;
  695. }
  696. free(CompletePath);
  697. CompletePath = NULL;
  698. //
  699. // If this was the last entry, stop.
  700. //
  701. if (NextListSeparator == NULL) {
  702. break;
  703. }
  704. //
  705. // Move to the next path.
  706. //
  707. CurrentPath = NextListSeparator + 1;
  708. NextListSeparator = strchr(CurrentPath, ListSeparator);
  709. }
  710. //
  711. // Nothing was found.
  712. //
  713. *ReturnValue = SHELL_ERROR_OPEN;
  714. Result = TRUE;
  715. LocateCommandEnd:
  716. if (CompletePath != NULL) {
  717. free(CompletePath);
  718. }
  719. if (ExtendedPath != NULL) {
  720. free(ExtendedPath);
  721. }
  722. return Result;
  723. }
  724. INT
  725. ShBuiltinPwd (
  726. PSHELL Shell,
  727. INT ArgumentCount,
  728. PSTR *Arguments
  729. )
  730. /*++
  731. Routine Description:
  732. This routine implements the builtin pwd (print working directory) command.
  733. Arguments:
  734. Shell - Supplies a pointer to the shell being run in.
  735. ArgumentCount - Supplies the number of arguments on the command line.
  736. Arguments - Supplies the array of pointers to strings representing each
  737. argument.
  738. Return Value:
  739. 0 on success.
  740. Non-zero on failure.
  741. --*/
  742. {
  743. return SwPwdCommand(ArgumentCount, Arguments);
  744. }
  745. INT
  746. ShBuiltinCd (
  747. PSHELL Shell,
  748. INT ArgumentCount,
  749. PSTR *Arguments
  750. )
  751. /*++
  752. Routine Description:
  753. This routine implements the builtin cd (change directory) command.
  754. Arguments:
  755. Shell - Supplies a pointer to the shell being run in.
  756. ArgumentCount - Supplies the number of arguments on the command line.
  757. Arguments - Supplies the array of pointers to strings representing each
  758. argument.
  759. Return Value:
  760. 0 on success.
  761. Non-zero on failure.
  762. --*/
  763. {
  764. PSTR Argument;
  765. ULONG ArgumentIndex;
  766. ULONG ArgumentSize;
  767. PSTR CdPathDirectory;
  768. UINTN CdPathDirectorySize;
  769. ULONG CharacterIndex;
  770. PSTR CleanedDirectory;
  771. UINTN CleanedDirectorySize;
  772. PSTR CurrentDirectory;
  773. UINTN CurrentDirectorySize;
  774. PSTR Destination;
  775. UINTN DestinationSize;
  776. PSTR FullDirectory;
  777. ULONG FullDirectorySize;
  778. BOOL LogicalMode;
  779. PSTR NewOldCurrentDirectory;
  780. UINTN NewOldCurrentDirectorySize;
  781. BOOL RelativeToCurrent;
  782. BOOL Result;
  783. INT ReturnValue;
  784. BOOL UseOldWorkingDirectory;
  785. CdPathDirectory = NULL;
  786. CleanedDirectory = NULL;
  787. CurrentDirectory = NULL;
  788. FullDirectory = NULL;
  789. FullDirectorySize = 0;
  790. NewOldCurrentDirectory = NULL;
  791. ReturnValue = 1;
  792. //
  793. // Parse the arguments.
  794. //
  795. LogicalMode = TRUE;
  796. UseOldWorkingDirectory = FALSE;
  797. for (ArgumentIndex = 1; ArgumentIndex < ArgumentCount; ArgumentIndex += 1) {
  798. Argument = Arguments[ArgumentIndex];
  799. ArgumentSize = strlen(Argument);
  800. if (Argument[0] != '-') {
  801. break;
  802. }
  803. if (strcmp(Argument, "--") == 0) {
  804. break;
  805. }
  806. if (strcmp(Argument, "-") == 0) {
  807. UseOldWorkingDirectory = TRUE;
  808. continue;
  809. }
  810. for (CharacterIndex = 1;
  811. CharacterIndex < ArgumentSize;
  812. CharacterIndex += 1) {
  813. switch (Argument[CharacterIndex]) {
  814. case 'L':
  815. LogicalMode = TRUE;
  816. break;
  817. case 'P':
  818. LogicalMode = FALSE;
  819. break;
  820. default:
  821. PRINT_ERROR("cd: invalid option -%c.\n",
  822. Argument[CharacterIndex]);
  823. goto BuiltinCdEnd;
  824. }
  825. }
  826. }
  827. Destination = NULL;
  828. DestinationSize = 0;
  829. //
  830. // Use the old working directory if - was supplied.
  831. //
  832. if (UseOldWorkingDirectory != FALSE) {
  833. Result = ShGetVariable(Shell,
  834. SHELL_OLDPWD,
  835. sizeof(SHELL_OLDPWD),
  836. &Destination,
  837. &DestinationSize);
  838. if ((Result != FALSE) && (Destination != NULL)) {
  839. FullDirectory = SwStringDuplicate(Destination, DestinationSize);
  840. if (FullDirectory == NULL) {
  841. ReturnValue = ENOMEM;
  842. goto BuiltinCdEnd;
  843. }
  844. FullDirectorySize = DestinationSize;
  845. Destination = FullDirectory;
  846. }
  847. }
  848. //
  849. // Get the current directory as the future old directory.
  850. //
  851. ShGetVariable(Shell,
  852. SHELL_PWD,
  853. sizeof(SHELL_PWD),
  854. &NewOldCurrentDirectory,
  855. &NewOldCurrentDirectorySize);
  856. //
  857. // If there's no directory operand, use the value of HOME.
  858. //
  859. if ((Destination == NULL) && (ArgumentIndex == ArgumentCount)) {
  860. Result = ShGetVariable(Shell,
  861. SHELL_HOME,
  862. sizeof(SHELL_HOME),
  863. &Destination,
  864. &DestinationSize);
  865. if ((Result == FALSE) || (Destination == NULL)) {
  866. goto BuiltinCdEnd;
  867. }
  868. } else if (Destination == NULL) {
  869. Destination = Arguments[ArgumentIndex];
  870. DestinationSize = strlen(Destination) + 1;
  871. }
  872. assert(Destination != NULL);
  873. //
  874. // Perform some work on relative paths. Detect both paths that start with
  875. // slash, and the C: format of Windows.
  876. //
  877. if ((Destination[0] != '/') && (Destination[0] != '\0') &&
  878. (Destination[1] != ':')) {
  879. //
  880. // If the first component is a dot or dot dot, then it's a relative
  881. // directory, so ignore CDPATH.
  882. //
  883. FullDirectory = NULL;
  884. FullDirectorySize = 0;
  885. RelativeToCurrent = FALSE;
  886. if (PATH_IS_RELATIVE_TO_CURRENT(Destination)) {
  887. RelativeToCurrent = TRUE;
  888. }
  889. //
  890. // If the pathname does not begin with a slash or a dot, it's relative,
  891. // so try the paths in CDPATH.
  892. //
  893. if (RelativeToCurrent == FALSE) {
  894. Result = ShLocateDirectoryOnCdPath(Shell,
  895. Destination,
  896. DestinationSize,
  897. &CdPathDirectory,
  898. &CdPathDirectorySize);
  899. assert(((Result == FALSE) && (CdPathDirectory == NULL)) ||
  900. ((Result != FALSE) && (CdPathDirectory != NULL)));
  901. assert(CdPathDirectory != Destination);
  902. }
  903. //
  904. // If the path is relative to the current directory specifically or
  905. // CDPATH didn't turn up anything, append the current directory.
  906. //
  907. if (((CdPathDirectory == NULL) ||
  908. (PATH_IS_RELATIVE_TO_CURRENT(CdPathDirectory))) &&
  909. (NewOldCurrentDirectory != NULL)) {
  910. if (CdPathDirectory != NULL) {
  911. Destination = CdPathDirectory;
  912. DestinationSize = CdPathDirectorySize;
  913. }
  914. Result = SwAppendPath(NewOldCurrentDirectory,
  915. NewOldCurrentDirectorySize,
  916. Destination,
  917. DestinationSize,
  918. &FullDirectory,
  919. &FullDirectorySize);
  920. CurrentDirectory = NULL;
  921. if (Result == FALSE) {
  922. goto BuiltinCdEnd;
  923. }
  924. Destination = FullDirectory;
  925. DestinationSize = FullDirectorySize;
  926. //
  927. // If CDPATH did come up with something absolute, use it.
  928. //
  929. } else if (CdPathDirectory != NULL) {
  930. Destination = CdPathDirectory;
  931. DestinationSize = CdPathDirectorySize;
  932. }
  933. }
  934. //
  935. // If logical mode is on, clean up the path, removing dot components, dot-
  936. // dot components, and extra random slashes.
  937. //
  938. if (LogicalMode != FALSE) {
  939. ReturnValue = ShCleanLogicalDirectoryPath(Destination,
  940. DestinationSize,
  941. &CleanedDirectory,
  942. &CleanedDirectorySize);
  943. if (ReturnValue != 0) {
  944. goto BuiltinCdEnd;
  945. }
  946. Destination = CleanedDirectory;
  947. DestinationSize = CleanedDirectorySize;
  948. }
  949. //
  950. // Ok, let's change directories.
  951. //
  952. if (chdir(Destination) == -1) {
  953. PRINT_ERROR("cd: Failed to cd to '%s': %s.\n",
  954. Destination,
  955. strerror(errno));
  956. ReturnValue = errno;
  957. goto BuiltinCdEnd;
  958. }
  959. //
  960. // If in physical mode, ask the system where this all landed.
  961. //
  962. if (LogicalMode != FALSE) {
  963. CurrentDirectory = SwStringDuplicate(Destination, DestinationSize);
  964. if (CurrentDirectory == NULL) {
  965. PRINT_ERROR("cd: Allocation failure.\n");
  966. ReturnValue = 1;
  967. goto BuiltinCdEnd;
  968. }
  969. CurrentDirectorySize = DestinationSize;
  970. } else {
  971. Result = ShGetCurrentDirectory(&CurrentDirectory,
  972. &CurrentDirectorySize);
  973. if (Result == FALSE) {
  974. PRINT_ERROR("cd: Failed to get current directory after cd to %s.\n",
  975. Destination);
  976. ReturnValue = 1;
  977. goto BuiltinCdEnd;
  978. }
  979. }
  980. //
  981. // Update the old PWD variable. Make sure to do this before updating the
  982. // PWD variable.
  983. //
  984. ShSetVariable(Shell,
  985. SHELL_OLDPWD,
  986. sizeof(SHELL_OLDPWD),
  987. NewOldCurrentDirectory,
  988. NewOldCurrentDirectorySize);
  989. //
  990. // Update the PWD variable.
  991. //
  992. Result = ShSetVariable(Shell,
  993. SHELL_PWD,
  994. sizeof(SHELL_PWD),
  995. CurrentDirectory,
  996. CurrentDirectorySize);
  997. if (Result == FALSE) {
  998. ReturnValue = 1;
  999. goto BuiltinCdEnd;
  1000. }
  1001. //
  1002. // For whatever reason, when this argument is used print out the new
  1003. // current directory.
  1004. //
  1005. if (UseOldWorkingDirectory != FALSE) {
  1006. printf("%s\n", CurrentDirectory);
  1007. }
  1008. ReturnValue = 0;
  1009. BuiltinCdEnd:
  1010. if (CdPathDirectory != NULL) {
  1011. free(CdPathDirectory);
  1012. }
  1013. if (CleanedDirectory != NULL) {
  1014. free(CleanedDirectory);
  1015. }
  1016. if (CurrentDirectory != NULL) {
  1017. free(CurrentDirectory);
  1018. }
  1019. if (FullDirectory != NULL) {
  1020. free(FullDirectory);
  1021. }
  1022. return ReturnValue;
  1023. }
  1024. //
  1025. // --------------------------------------------------------- Internal Functions
  1026. //
  1027. BOOL
  1028. ShExpandPath (
  1029. PSHELL Shell,
  1030. PSTR Prefix,
  1031. ULONG PrefixSize,
  1032. PSTR Field,
  1033. PSTR *FilesStringBuffer,
  1034. PULONG FilesStringBufferSize,
  1035. PSTR **FilesArray,
  1036. PULONG FilesArrayCount
  1037. )
  1038. /*++
  1039. Routine Description:
  1040. This routine expands a path pattern.
  1041. Arguments:
  1042. Shell - Supplies a pointer to the shell.
  1043. Prefix - Supplies an optional pointer to the string containing the
  1044. expanded path so far.
  1045. PrefixSize - Supplies the size of the prefix in bytes including the null
  1046. terminator.
  1047. Field - Supplies a pointer to the string containing the path to expand.
  1048. FilesStringBuffer - Supplies a pointer where the string will be returned
  1049. containing all the matches. The caller is responsible for freeing this
  1050. memory.
  1051. FilesStringBufferSize - Supplies a pointer where the size of the files
  1052. string buffer will be returned on success.
  1053. FilesArray - Supplies a pointer where the array of pointers to the files
  1054. matching the expansion will be returned on success. The caller is
  1055. responsible for freeing this memory.
  1056. FilesArrayCount - Supplies a pointer where the number of files matching
  1057. will be returned on success.
  1058. Return Value:
  1059. TRUE on success.
  1060. FALSE on failure.
  1061. --*/
  1062. {
  1063. PSTR CompletePath;
  1064. ULONG CompletePathSize;
  1065. PSTR CompletePrefix;
  1066. PSTR Component;
  1067. UINTN ComponentLength;
  1068. PSTR FieldCopy;
  1069. UINTN FieldCopySize;
  1070. ULONG FileCount;
  1071. PSTR *Files;
  1072. ULONG FilesCapacity;
  1073. BOOL HasMetaCharacters;
  1074. PSTR InitialFieldCopy;
  1075. PSTR ListingBuffer;
  1076. ULONG ListingCount;
  1077. ULONG ListingIndex;
  1078. PSHELL_DIRECTORY_ENTRY Listings;
  1079. BOOL Match;
  1080. BOOL MustBeDirectory;
  1081. PSTR NextComponent;
  1082. PSTR RecursedBuffer;
  1083. ULONG RecursedBufferSize;
  1084. ULONG RecursedFileCount;
  1085. PSTR *RecursedFiles;
  1086. BOOL Result;
  1087. struct stat Stat;
  1088. int Status;
  1089. PSTR String;
  1090. ULONG StringCapacity;
  1091. ULONG StringSize;
  1092. CompletePath = NULL;
  1093. CompletePrefix = NULL;
  1094. FieldCopy = NULL;
  1095. FileCount = 0;
  1096. Files = NULL;
  1097. FilesCapacity = 0;
  1098. InitialFieldCopy = NULL;
  1099. ListingBuffer = NULL;
  1100. Listings = NULL;
  1101. String = NULL;
  1102. StringCapacity = 0;
  1103. StringSize = 0;
  1104. //
  1105. // If there's no prefix and it starts with an absolute path, advance beyond
  1106. // the slashes.
  1107. //
  1108. if (Prefix == NULL) {
  1109. FieldCopySize = strlen(Field) + 1;
  1110. if (ShArePatternCharactersInPath(Field, FieldCopySize) == FALSE) {
  1111. Result = TRUE;
  1112. goto ExpandPathEnd;
  1113. }
  1114. //
  1115. // Create a copy of the string and dequote it for path expansions.
  1116. //
  1117. InitialFieldCopy = strdup(Field);
  1118. if (InitialFieldCopy == NULL) {
  1119. Result = FALSE;
  1120. goto ExpandPathEnd;
  1121. }
  1122. FieldCopySize = strlen(Field);
  1123. ShStringDequote(InitialFieldCopy,
  1124. FieldCopySize + 1,
  1125. SHELL_DEQUOTE_FOR_PATTERN_MATCHING,
  1126. &FieldCopySize);
  1127. Field = InitialFieldCopy;
  1128. //
  1129. // If it's absolute, set the prefix and make it relative.
  1130. //
  1131. if (*Field == '/') {
  1132. while (*Field == '/') {
  1133. Field += 1;
  1134. }
  1135. Prefix = "/";
  1136. PrefixSize = sizeof("/");
  1137. }
  1138. }
  1139. //
  1140. // Determine where to split the remaining path.
  1141. //
  1142. NextComponent = ShPathGetNextComponent(Field, &HasMetaCharacters);
  1143. //
  1144. // There is a component, but if there is no prefix, figure one out.
  1145. //
  1146. if (Prefix == NULL) {
  1147. if (HasMetaCharacters == FALSE) {
  1148. assert(NextComponent != NULL);
  1149. FieldCopySize = NextComponent - Field;
  1150. assert(FieldCopySize != 0);
  1151. FieldCopy = strdup(Field);
  1152. if (FieldCopy == NULL) {
  1153. Result = FALSE;
  1154. goto ExpandPathEnd;
  1155. }
  1156. FieldCopy[FieldCopySize] = '\0';
  1157. FieldCopySize += 1;
  1158. Prefix = FieldCopy;
  1159. PrefixSize = FieldCopySize;
  1160. Field = NextComponent;
  1161. //
  1162. // Remove all the escaping backslashes from the prefix.
  1163. //
  1164. while (*Prefix != '\0') {
  1165. if (*Prefix == '\\') {
  1166. memmove(Prefix,
  1167. Prefix + 1,
  1168. &(FieldCopy[FieldCopySize]) - (Prefix + 1));
  1169. PrefixSize -= 1;
  1170. }
  1171. Prefix += 1;
  1172. }
  1173. Prefix = FieldCopy;
  1174. //
  1175. // This component is now the prefix, so get the next component.
  1176. //
  1177. while (*Field == '/') {
  1178. Field += 1;
  1179. }
  1180. NextComponent = ShPathGetNextComponent(Field,
  1181. &HasMetaCharacters);
  1182. assert(HasMetaCharacters != FALSE);
  1183. }
  1184. }
  1185. //
  1186. // If there are no metacharacters in this component, glom it on to the
  1187. // prefix and get the next component, which should have metacharacters.
  1188. //
  1189. if (HasMetaCharacters == FALSE) {
  1190. if (NextComponent == NULL) {
  1191. ComponentLength = strlen(Field);
  1192. } else {
  1193. ComponentLength = NextComponent - Field;
  1194. }
  1195. Result = SwAppendPath(Prefix,
  1196. PrefixSize,
  1197. Field,
  1198. ComponentLength + 1,
  1199. &CompletePrefix,
  1200. &PrefixSize);
  1201. if (Result == FALSE) {
  1202. goto ExpandPathEnd;
  1203. }
  1204. Prefix = CompletePrefix;
  1205. //
  1206. // If there is no next component, then just see if this file exists.
  1207. //
  1208. if (NextComponent == NULL) {
  1209. Status = SwStat(CompletePrefix, FALSE, &Stat);
  1210. if (Status == 0) {
  1211. Result = ShPathCombineLists(&String,
  1212. &StringSize,
  1213. &StringCapacity,
  1214. &Files,
  1215. &FileCount,
  1216. &FilesCapacity,
  1217. CompletePrefix,
  1218. PrefixSize,
  1219. &CompletePrefix,
  1220. 1);
  1221. if (Result == FALSE) {
  1222. goto ExpandPathEnd;
  1223. }
  1224. }
  1225. Result = TRUE;
  1226. goto ExpandPathEnd;
  1227. }
  1228. Field = NextComponent;
  1229. while (*Field == '/') {
  1230. Field += 1;
  1231. }
  1232. NextComponent = ShPathGetNextComponent(Field, &HasMetaCharacters);
  1233. assert(HasMetaCharacters != FALSE);
  1234. }
  1235. Component = Field;
  1236. while (*Component == '/') {
  1237. Component += 1;
  1238. }
  1239. MustBeDirectory = FALSE;
  1240. if (NextComponent != NULL) {
  1241. ComponentLength = NextComponent - Component;
  1242. MustBeDirectory = TRUE;
  1243. while (*NextComponent == '/') {
  1244. NextComponent += 1;
  1245. }
  1246. } else {
  1247. ComponentLength = strlen(Component);
  1248. }
  1249. if (ComponentLength == 0) {
  1250. Result = TRUE;
  1251. goto ExpandPathEnd;
  1252. }
  1253. //
  1254. // Get the directory contents of the prefix.
  1255. //
  1256. Result = ShGetDirectoryListing(Prefix,
  1257. &ListingBuffer,
  1258. &Listings,
  1259. &ListingCount);
  1260. if (Result == FALSE) {
  1261. Result = TRUE;
  1262. goto ExpandPathEnd;
  1263. }
  1264. for (ListingIndex = 0; ListingIndex < ListingCount; ListingIndex += 1) {
  1265. //
  1266. // If the listing doesn't match, just continue.
  1267. //
  1268. Match = SwDoesPathPatternMatch(Listings[ListingIndex].Name,
  1269. Listings[ListingIndex].NameSize,
  1270. Component,
  1271. ComponentLength + 1);
  1272. if (Match == FALSE) {
  1273. continue;
  1274. }
  1275. //
  1276. // Create the appended path.
  1277. //
  1278. Result = SwAppendPath(Prefix,
  1279. PrefixSize,
  1280. Listings[ListingIndex].Name,
  1281. Listings[ListingIndex].NameSize,
  1282. &CompletePath,
  1283. &CompletePathSize);
  1284. if (Result == FALSE) {
  1285. goto ExpandPathEnd;
  1286. }
  1287. if (MustBeDirectory != FALSE) {
  1288. Status = SwStat(CompletePath, TRUE, &Stat);
  1289. if (Status != 0) {
  1290. free(CompletePath);
  1291. CompletePath = NULL;
  1292. continue;
  1293. }
  1294. if (S_ISDIR(Stat.st_mode)) {
  1295. //
  1296. // It's a directory. If there's another path component,
  1297. // recurse to get all the files in that directory matching the
  1298. // pattern, then combine those results with the answer.
  1299. //
  1300. if ((NextComponent != NULL) && (*NextComponent != '\0')) {
  1301. Result = ShExpandPath(Shell,
  1302. CompletePath,
  1303. CompletePathSize,
  1304. NextComponent,
  1305. &RecursedBuffer,
  1306. &RecursedBufferSize,
  1307. &RecursedFiles,
  1308. &RecursedFileCount);
  1309. if (Result == FALSE) {
  1310. goto ExpandPathEnd;
  1311. }
  1312. Result = ShPathCombineLists(&String,
  1313. &StringSize,
  1314. &StringCapacity,
  1315. &Files,
  1316. &FileCount,
  1317. &FilesCapacity,
  1318. RecursedBuffer,
  1319. RecursedBufferSize,
  1320. RecursedFiles,
  1321. RecursedFileCount);
  1322. free(RecursedBuffer);
  1323. free(RecursedFiles);
  1324. if (Result == FALSE) {
  1325. goto ExpandPathEnd;
  1326. }
  1327. //
  1328. // There are no more components, so add this directory to
  1329. // the list of results.
  1330. //
  1331. } else {
  1332. Result = ShPathCombineLists(&String,
  1333. &StringSize,
  1334. &StringCapacity,
  1335. &Files,
  1336. &FileCount,
  1337. &FilesCapacity,
  1338. CompletePath,
  1339. CompletePathSize,
  1340. &CompletePath,
  1341. 1);
  1342. if (Result == FALSE) {
  1343. goto ExpandPathEnd;
  1344. }
  1345. }
  1346. }
  1347. //
  1348. // This doesn't have to be a directory, doesn't much matter what it is.
  1349. //
  1350. } else {
  1351. //
  1352. // It doesn't have to be a directory, which must mean there are no
  1353. // more components. Add this value to the list.
  1354. //
  1355. assert(NextComponent == NULL);
  1356. Result = ShPathCombineLists(&String,
  1357. &StringSize,
  1358. &StringCapacity,
  1359. &Files,
  1360. &FileCount,
  1361. &FilesCapacity,
  1362. CompletePath,
  1363. CompletePathSize,
  1364. &CompletePath,
  1365. 1);
  1366. if (Result == FALSE) {
  1367. goto ExpandPathEnd;
  1368. }
  1369. }
  1370. free(CompletePath);
  1371. CompletePath = NULL;
  1372. }
  1373. ExpandPathEnd:
  1374. if (InitialFieldCopy != NULL) {
  1375. free(InitialFieldCopy);
  1376. }
  1377. if (FieldCopy != NULL) {
  1378. free(FieldCopy);
  1379. }
  1380. if (CompletePath != NULL) {
  1381. free(CompletePath);
  1382. }
  1383. if (CompletePrefix != NULL) {
  1384. free(CompletePrefix);
  1385. }
  1386. if (Listings != NULL) {
  1387. free(Listings);
  1388. }
  1389. if (ListingBuffer != NULL) {
  1390. free(ListingBuffer);
  1391. }
  1392. if (Result == FALSE) {
  1393. if (Files != NULL) {
  1394. free(Files);
  1395. Files = NULL;
  1396. }
  1397. FileCount = 0;
  1398. if (String != NULL) {
  1399. free(String);
  1400. String = NULL;
  1401. }
  1402. StringSize = 0;
  1403. }
  1404. *FilesStringBuffer = String;
  1405. *FilesStringBufferSize = StringSize;
  1406. *FilesArray = Files;
  1407. *FilesArrayCount = FileCount;
  1408. return Result;
  1409. }
  1410. BOOL
  1411. ShArePatternCharactersInPath (
  1412. PSTR Path,
  1413. ULONG PathSize
  1414. )
  1415. /*++
  1416. Routine Description:
  1417. This routine determines if the given string contains any special pattern
  1418. characters: * ? or [ that not quoted by a backslash.
  1419. Arguments:
  1420. Path - Supplies a pointer to the path string. If a null terminator is
  1421. encountered, the search will stop.
  1422. PathSize - Supplies the size of the path string in bytes.
  1423. Return Value:
  1424. TRUE if the string contains any special character (which may or may not be
  1425. quoted).
  1426. FALSE if the string contains no special characters.
  1427. --*/
  1428. {
  1429. while (PathSize != 0) {
  1430. if (*Path == SHELL_CONTROL_ESCAPE) {
  1431. assert(PathSize >= 2);
  1432. Path += 2;
  1433. PathSize -= 2;
  1434. continue;
  1435. }
  1436. if ((*Path == '?') || (*Path == '*') || (*Path == '[')) {
  1437. return TRUE;
  1438. }
  1439. Path += 1;
  1440. PathSize -= 1;
  1441. }
  1442. return FALSE;
  1443. }
  1444. BOOL
  1445. ShPathCombineLists (
  1446. PSTR *ListBuffer,
  1447. PULONG ListBufferSize,
  1448. PULONG ListBufferCapacity,
  1449. PSTR **List,
  1450. PULONG ListSize,
  1451. PULONG ListCapacity,
  1452. PSTR SecondListBuffer,
  1453. ULONG SecondListBufferSize,
  1454. PSTR *SecondList,
  1455. ULONG SecondListSize
  1456. )
  1457. /*++
  1458. Routine Description:
  1459. This routine appends a path component to a path.
  1460. Arguments:
  1461. ListBuffer - Supplies a pointer that on input contains the pointer to the
  1462. string buffer all the list elements point to. This buffer may be
  1463. updated on output.
  1464. ListBufferSize - Supplies a pointer that on input contains the size of the
  1465. list buffer. This may be updated on output.
  1466. ListBufferCapacity - Supplies a pointer that on input contains the total
  1467. allocation size of the list buffer. This may be updated on output.
  1468. List - Supplies a pointer to the array of element pointers on input. This
  1469. will be updated on output to contain the updated list.
  1470. ListSize - Supplies a pointer that on input contains the number of elements
  1471. in the list. This will be updated on output.
  1472. ListCapacity - Supplies a pointer that on input contains the maximum
  1473. number of elements that could currently go in the list. This value may
  1474. be updated on output if the allocation is expanded.
  1475. SecondListBuffer - Supplies a pointer to the second list's string buffer.
  1476. SecondListBufferSize - Supplies the size of the second list's buffer in
  1477. bytes including the last null terminator.
  1478. SecondList - Supplies the second list to append to the first.
  1479. SecondListSize - Supplies the number of elements in the second list.
  1480. Return Value:
  1481. TRUE on success.
  1482. FALSE on failure.
  1483. --*/
  1484. {
  1485. ULONG ListIndex;
  1486. PSTR NewBuffer;
  1487. ULONG NewBufferCapacity;
  1488. ULONG NewBufferSize;
  1489. PSTR *NewList;
  1490. ULONG NewListCapacity;
  1491. ULONG NewListSize;
  1492. UINTN OriginalBufferAddress;
  1493. BOOL Result;
  1494. ULONG SizeNeeded;
  1495. NewBuffer = *ListBuffer;
  1496. OriginalBufferAddress = (UINTN)NewBuffer;
  1497. NewBufferCapacity = *ListBufferCapacity;
  1498. NewBufferSize = *ListBufferSize;
  1499. NewList = *List;
  1500. NewListSize = *ListSize;
  1501. NewListCapacity = *ListCapacity;
  1502. Result = FALSE;
  1503. //
  1504. // Create a new string containing both strings.
  1505. //
  1506. SizeNeeded = *ListBufferSize + SecondListBufferSize;
  1507. if (NewBufferCapacity == 0) {
  1508. NewBufferCapacity = SHELL_INITIAL_PATH_BUFFER_SIZE;
  1509. }
  1510. while (NewBufferCapacity < SizeNeeded) {
  1511. NewBufferCapacity *= 2;
  1512. }
  1513. if (NewBufferCapacity != *ListBufferCapacity) {
  1514. NewBuffer = realloc(NewBuffer, NewBufferCapacity);
  1515. if (NewBuffer == NULL) {
  1516. goto PathCombineListsEnd;
  1517. }
  1518. }
  1519. memcpy(NewBuffer + *ListBufferSize, SecondListBuffer, SecondListBufferSize);
  1520. NewBufferSize = SizeNeeded;
  1521. //
  1522. // Create the new combined array.
  1523. //
  1524. SizeNeeded = NewListSize + SecondListSize;
  1525. if (NewListCapacity == 0) {
  1526. NewListCapacity = SHELL_INITIAL_PATH_LIST_SIZE;
  1527. }
  1528. while (NewListCapacity < SizeNeeded) {
  1529. NewListCapacity *= 2;
  1530. }
  1531. if (NewListCapacity != *ListCapacity) {
  1532. NewList = realloc(NewList, NewListCapacity * sizeof(PSTR));
  1533. if (NewList == NULL) {
  1534. goto PathCombineListsEnd;
  1535. }
  1536. }
  1537. NewListSize = SizeNeeded;
  1538. //
  1539. // Fix up all the original pointers to point at the new buffer if the
  1540. // buffer was reallocated.
  1541. //
  1542. if (NewBufferCapacity != *ListBufferCapacity) {
  1543. for (ListIndex = 0; ListIndex < *ListSize; ListIndex += 1) {
  1544. NewList[ListIndex] = NewBuffer +
  1545. ((UINTN)(NewList[ListIndex]) -
  1546. OriginalBufferAddress);
  1547. }
  1548. }
  1549. //
  1550. // Add in all the new pointers.
  1551. //
  1552. for (ListIndex = *ListSize; ListIndex < NewListSize; ListIndex += 1) {
  1553. NewList[ListIndex] = NewBuffer + *ListBufferSize +
  1554. ((UINTN)SecondList[ListIndex - *ListSize] -
  1555. (UINTN)SecondListBuffer);
  1556. }
  1557. Result = TRUE;
  1558. PathCombineListsEnd:
  1559. if (Result == FALSE) {
  1560. if (NewBuffer != NULL) {
  1561. free(NewBuffer);
  1562. NewBuffer = NULL;
  1563. }
  1564. NewBufferSize = 0;
  1565. NewBufferCapacity = 0;
  1566. if (NewList != NULL) {
  1567. free(NewList);
  1568. NewList = NULL;
  1569. }
  1570. NewListSize = 0;
  1571. NewListCapacity = 0;
  1572. }
  1573. *ListBuffer = NewBuffer;
  1574. *ListBufferCapacity = NewBufferCapacity;
  1575. *ListBufferSize = NewBufferSize;
  1576. *List = NewList;
  1577. *ListSize = NewListSize;
  1578. *ListCapacity = NewListCapacity;
  1579. return Result;
  1580. }
  1581. BOOL
  1582. ShLocateDirectoryOnCdPath (
  1583. PSHELL Shell,
  1584. PSTR Directory,
  1585. UINTN DirectorySize,
  1586. PSTR *FullDirectoryPath,
  1587. PUINTN FullDirectoryPathSize
  1588. )
  1589. /*++
  1590. Routine Description:
  1591. This routine locates a directory using the CDPATH environment variable.
  1592. Arguments:
  1593. Shell - Supplies a pointer to the shell.
  1594. Directory - Supplies a pointer to the directory string.
  1595. DirectorySize - Supplies the size of the directory string in bytes.
  1596. FullDirectoryPath - Supplies a pointer where a pointer to the full directory
  1597. path will be returned on success. If this is not the same pointer as the
  1598. command string then the caller is responsible for freeing this buffer.
  1599. FullDirectoryPathSize - Supplies a pointer where the size of the full
  1600. command string will be returned.
  1601. Return Value:
  1602. TRUE on success.
  1603. FALSE on failure.
  1604. --*/
  1605. {
  1606. PSTR CompletePath;
  1607. ULONG CompletePathSize;
  1608. PSTR CurrentPath;
  1609. UINTN CurrentPathSize;
  1610. CHAR ListSeparator;
  1611. PSTR NextListSeparator;
  1612. PSTR Path;
  1613. UINTN PathSize;
  1614. BOOL Result;
  1615. struct stat Stat;
  1616. INT Status;
  1617. *FullDirectoryPath = NULL;
  1618. *FullDirectoryPathSize = 0;
  1619. ListSeparator = PATH_LIST_SEPARATOR;
  1620. //
  1621. // Get the CDPATH environment variable.
  1622. //
  1623. Result = ShGetVariable(Shell,
  1624. SHELL_CDPATH,
  1625. sizeof(SHELL_CDPATH),
  1626. &Path,
  1627. &PathSize);
  1628. //
  1629. // If the path variable couldn't be found or is empty, then just return.
  1630. //
  1631. if ((Result == FALSE) || (Path == NULL) || (PathSize <= 1)) {
  1632. Result = FALSE;
  1633. goto LocateDirectoryOnCdPathEnd;
  1634. }
  1635. //
  1636. // Loop through each entry in the path.
  1637. //
  1638. CurrentPath = Path;
  1639. NextListSeparator = strchr(CurrentPath, ListSeparator);
  1640. while (TRUE) {
  1641. if (NextListSeparator == NULL) {
  1642. CurrentPathSize = PathSize - ((UINTN)CurrentPath - (UINTN)Path);
  1643. } else {
  1644. CurrentPathSize = (UINTN)NextListSeparator - (UINTN)CurrentPath;
  1645. }
  1646. if (CurrentPathSize == 0) {
  1647. CurrentPath = ".";
  1648. CurrentPathSize = sizeof(".");
  1649. }
  1650. //
  1651. // Make a complete command path out of this path entry and the command.
  1652. //
  1653. Result = SwAppendPath(CurrentPath,
  1654. CurrentPathSize,
  1655. Directory,
  1656. DirectorySize,
  1657. &CompletePath,
  1658. &CompletePathSize);
  1659. if (Result == FALSE) {
  1660. goto LocateDirectoryOnCdPathEnd;
  1661. }
  1662. //
  1663. // Figure out if this is something legit.
  1664. //
  1665. Status = SwStat(CompletePath, TRUE, &Stat);
  1666. if ((Status == 0) && (S_ISDIR(Stat.st_mode))) {
  1667. *FullDirectoryPath = CompletePath;
  1668. *FullDirectoryPathSize = CompletePathSize;
  1669. Result = TRUE;
  1670. goto LocateDirectoryOnCdPathEnd;
  1671. }
  1672. free(CompletePath);
  1673. //
  1674. // If this was the last entry, stop.
  1675. //
  1676. if (NextListSeparator == NULL) {
  1677. break;
  1678. }
  1679. //
  1680. // Move to the next path.
  1681. //
  1682. CurrentPath = NextListSeparator + 1;
  1683. NextListSeparator = strchr(CurrentPath, ListSeparator);
  1684. }
  1685. //
  1686. // Nothing was found.
  1687. //
  1688. Result = FALSE;
  1689. LocateDirectoryOnCdPathEnd:
  1690. return Result;
  1691. }
  1692. INT
  1693. ShCleanLogicalDirectoryPath (
  1694. PSTR PathString,
  1695. UINTN PathStringSize,
  1696. PSTR *CleanedPathString,
  1697. PUINTN CleanedPathStringSize
  1698. )
  1699. /*++
  1700. Routine Description:
  1701. This routine cleans up a logical directory path. It removes unnecessary
  1702. slashes, removes dot components, and performs logical splicing of dot-dot
  1703. components, validating that each path referenced along the way points to
  1704. a directory.
  1705. Arguments:
  1706. PathString - Supplies a pointer to the path string.
  1707. PathStringSize - Supplies the size of the path string in bytes including
  1708. the null terminator.
  1709. CleanedPathString - Supplies a pointer where a new string will be returned
  1710. containing the cleaned path. The caller is responsible for freeing this
  1711. memory.
  1712. CleanedPathStringSize - Supplies a pointer where the size in bytes of the
  1713. returned cleaned string in bytes including the null terminator will
  1714. be returned.
  1715. Return Value:
  1716. 0 on success.
  1717. Returns an error code on failure.
  1718. --*/
  1719. {
  1720. UINTN ComponentSize;
  1721. PSTR CurrentOutput;
  1722. PSTR NextSeparator;
  1723. PSTR OriginalPathString;
  1724. PSTR Output;
  1725. UINTN OutputCapacity;
  1726. UINTN OutputSize;
  1727. UINTN RemainingSize;
  1728. INT Result;
  1729. struct stat Stat;
  1730. OriginalPathString = PathString;
  1731. //
  1732. // The cleaned path only gets smaller, except that it may add a trailing
  1733. // space.
  1734. //
  1735. OutputCapacity = PathStringSize + 2;
  1736. Output = malloc(OutputCapacity);
  1737. if (Output == NULL) {
  1738. Result = ENOMEM;
  1739. goto CleanLogicalDirectoryPathEnd;
  1740. }
  1741. CurrentOutput = Output;
  1742. OutputSize = 0;
  1743. if (*PathString == '/') {
  1744. *CurrentOutput = '/';
  1745. CurrentOutput += 1;
  1746. OutputSize += 1;
  1747. }
  1748. while (PathStringSize != 0) {
  1749. //
  1750. // Get past any separators. Add a single separator to the output if
  1751. // there isn't one already.
  1752. //
  1753. if (*PathString == '/') {
  1754. if ((OutputSize != 0) && (*(CurrentOutput - 1) != '/')) {
  1755. *CurrentOutput = '/';
  1756. CurrentOutput += 1;
  1757. OutputSize += 1;
  1758. }
  1759. while ((PathStringSize != 0) && (*PathString == '/')) {
  1760. PathString += 1;
  1761. PathStringSize -= 1;
  1762. }
  1763. }
  1764. //
  1765. // Find the next separator.
  1766. //
  1767. RemainingSize = PathStringSize;
  1768. NextSeparator = PathString;
  1769. while ((RemainingSize != 0) && (*NextSeparator != '/') &&
  1770. (*NextSeparator != '\0')) {
  1771. NextSeparator += 1;
  1772. RemainingSize -= 1;
  1773. }
  1774. ComponentSize = PathStringSize - RemainingSize;
  1775. //
  1776. // Skip any dot components.
  1777. //
  1778. if ((ComponentSize == 1) && (*PathString == '.')) {
  1779. PathStringSize = RemainingSize;
  1780. PathString = NextSeparator;
  1781. continue;
  1782. }
  1783. //
  1784. // If it's a dot-dot component, then test the path so far. If it's
  1785. // not a valid directory (following symlinks), then complain and exit.
  1786. //
  1787. if ((ComponentSize == 2) && (PathString[0] == '.') &&
  1788. (PathString[1] == '.')) {
  1789. //
  1790. // Terminate the string and check the output so far.
  1791. //
  1792. *CurrentOutput = '\0';
  1793. Result = SwStat(Output, TRUE, &Stat);
  1794. if ((Result == 0) && (!S_ISDIR(Stat.st_mode))) {
  1795. Result = ENOTDIR;
  1796. }
  1797. if (Result != 0) {
  1798. PRINT_ERROR("cd: %s: %s\n",
  1799. OriginalPathString,
  1800. strerror(Result));
  1801. goto CleanLogicalDirectoryPathEnd;
  1802. }
  1803. //
  1804. // Attempt to remove the most recently added component. Start by
  1805. // backing up over the separator, unless it's the very first root
  1806. // separator.
  1807. //
  1808. if ((OutputSize > 1) && (*(CurrentOutput - 1) == '/')) {
  1809. OutputSize -= 1;
  1810. CurrentOutput -= 1;
  1811. //
  1812. // Now back up until a separator or the beginning of the
  1813. // string is found.
  1814. //
  1815. while ((OutputSize > 0) && (*(CurrentOutput - 1) != '/')) {
  1816. CurrentOutput -= 1;
  1817. OutputSize -= 1;
  1818. }
  1819. //
  1820. // Also remove the separator before that, unless it's the first
  1821. // one.
  1822. //
  1823. if ((OutputSize > 1) && (*(CurrentOutput - 1) == '/')) {
  1824. OutputSize -= 1;
  1825. CurrentOutput -= 1;
  1826. }
  1827. }
  1828. //
  1829. // Move along.
  1830. //
  1831. PathStringSize = RemainingSize;
  1832. PathString = NextSeparator;
  1833. continue;
  1834. } else if (*PathString == '\0') {
  1835. break;
  1836. }
  1837. //
  1838. // It's a regular path component, jam it on there.
  1839. //
  1840. memcpy(CurrentOutput, PathString, ComponentSize);
  1841. CurrentOutput += ComponentSize;
  1842. OutputSize += ComponentSize;
  1843. PathStringSize = RemainingSize;
  1844. PathString = NextSeparator;
  1845. }
  1846. //
  1847. // Trim a trailing slash.
  1848. //
  1849. if ((OutputSize > 1) && (*(CurrentOutput - 1) == '/')) {
  1850. OutputSize -= 1;
  1851. CurrentOutput -= 1;
  1852. }
  1853. //
  1854. // Terminate the path.
  1855. //
  1856. *CurrentOutput = '\0';
  1857. OutputSize += 1;
  1858. Result = 0;
  1859. CleanLogicalDirectoryPathEnd:
  1860. if (Result != 0) {
  1861. if (Output != NULL) {
  1862. free(Output);
  1863. Output = NULL;
  1864. }
  1865. OutputSize = 0;
  1866. }
  1867. *CleanedPathString = Output;
  1868. *CleanedPathStringSize = OutputSize;
  1869. return Result;
  1870. }
  1871. PSTR
  1872. ShPathGetNextComponent (
  1873. PSTR Field,
  1874. PBOOL HasMetaCharacters
  1875. )
  1876. /*++
  1877. Routine Description:
  1878. This routine determines the next path component of the given path, honoring
  1879. backslashes.
  1880. Arguments:
  1881. Field - Supplies a pointer to the field to get the next path component of.
  1882. HasMetaCharacters - Supplies a pointer where a boolean will be returned
  1883. indicating if this component has metacharacters or not.
  1884. Return Value:
  1885. Returns a pointer to the path separator right before a component with a
  1886. metacharacter if there are no metacharacters in the first component.
  1887. Returns a pointer to the next path separator if this component has a meta-
  1888. character.
  1889. NULL if no metacharacters were found in this field region.
  1890. --*/
  1891. {
  1892. BOOL FoundMeta;
  1893. BOOL FoundSeparator;
  1894. PSTR LastSeparator;
  1895. BOOL WasBackslash;
  1896. *HasMetaCharacters = FALSE;
  1897. FoundMeta = FALSE;
  1898. LastSeparator = NULL;
  1899. WasBackslash = FALSE;
  1900. while (*Field != '\0') {
  1901. FoundSeparator = FALSE;
  1902. if (WasBackslash == FALSE) {
  1903. if ((*Field == '*') || (*Field == '?') || (*Field == '[')) {
  1904. FoundMeta = TRUE;
  1905. }
  1906. }
  1907. if (*Field == '/') {
  1908. FoundSeparator = TRUE;
  1909. }
  1910. if (*Field == '\\') {
  1911. WasBackslash = !WasBackslash;
  1912. } else {
  1913. WasBackslash = FALSE;
  1914. }
  1915. //
  1916. // Handle a path separator if one was found.
  1917. //
  1918. if (FoundSeparator != FALSE) {
  1919. //
  1920. // If there was a metacharacter and there was a previous component,
  1921. // return the previous component.
  1922. //
  1923. if (FoundMeta != FALSE) {
  1924. if (LastSeparator != NULL) {
  1925. return LastSeparator;
  1926. }
  1927. //
  1928. // This is the first component, so return the next
  1929. // metacharacter.
  1930. //
  1931. *HasMetaCharacters = TRUE;
  1932. return Field;
  1933. }
  1934. LastSeparator = Field;
  1935. }
  1936. Field += 1;
  1937. }
  1938. if (FoundMeta != FALSE) {
  1939. if (LastSeparator != NULL) {
  1940. return LastSeparator;
  1941. }
  1942. *HasMetaCharacters = TRUE;
  1943. }
  1944. return NULL;
  1945. }
  1946. int
  1947. ShPathCompareStrings (
  1948. const void *LeftPointer,
  1949. const void *RightPointer
  1950. )
  1951. /*++
  1952. Routine Description:
  1953. This routine compares two strings, using a function prototype compatible
  1954. with the qsort function.
  1955. Arguments:
  1956. LeftPointer - Supplies a pointer to a pointer to the left side of the
  1957. string comparison.
  1958. RightPointer - Supplies a pointer to a pointer to the right side of the
  1959. string comparison.
  1960. Return Value:
  1961. < 0 if the left is less than the right.
  1962. 0 if the strings are equal.
  1963. > 0 if the left side is greater than the right.
  1964. --*/
  1965. {
  1966. char **LeftStringPointer;
  1967. int Result;
  1968. char **RightStringPointer;
  1969. LeftStringPointer = (char **)LeftPointer;
  1970. RightStringPointer = (char **)RightPointer;
  1971. Result = strcmp(*LeftStringPointer, *RightStringPointer);
  1972. return Result;
  1973. }