alias.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. alias.c
  5. Abstract:
  6. This module implements support for alias substitution in the shell.
  7. Author:
  8. Evan Green 12-Jun-2013
  9. Environment:
  10. POSIX
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include "sh.h"
  16. #include "shparse.h"
  17. #include "../swlib.h"
  18. #include <assert.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. //
  22. // ---------------------------------------------------------------- Definitions
  23. //
  24. //
  25. // ------------------------------------------------------ Data Type Definitions
  26. //
  27. //
  28. // ----------------------------------------------- Internal Function Prototypes
  29. //
  30. VOID
  31. ShDestroyAlias (
  32. PSHELL_ALIAS Alias
  33. );
  34. PSHELL_ALIAS
  35. ShLookupAlias (
  36. PSHELL Shell,
  37. PSTR Name,
  38. ULONG NameSize
  39. );
  40. BOOL
  41. ShPrintAlias (
  42. PSHELL_ALIAS Alias
  43. );
  44. //
  45. // -------------------------------------------------------------------- Globals
  46. //
  47. BOOL ShDebugAlias = FALSE;
  48. //
  49. // ------------------------------------------------------------------ Functions
  50. //
  51. BOOL
  52. ShPerformAliasSubstitution (
  53. PSHELL Shell
  54. )
  55. /*++
  56. Routine Description:
  57. This routine destroys all the aliases in a shell. It is usually called
  58. during cleanup.
  59. Arguments:
  60. Shell - Supplies a pointer to the shell whose aliases will be cleaned up.
  61. Return Value:
  62. TRUE on success (either looking it up and substituting or looking it up and
  63. finding nothing).
  64. FALSE on failure.
  65. --*/
  66. {
  67. PSHELL_ALIAS Alias;
  68. PSHELL_LEXER_STATE Lexer;
  69. BOOL Result;
  70. Lexer = &(Shell->Lexer);
  71. Alias = ShLookupAlias(Shell, Lexer->TokenBuffer, Lexer->TokenBufferSize);
  72. if (Alias == NULL) {
  73. Result = TRUE;
  74. goto PerformAliasSubstitutionEnd;
  75. }
  76. //
  77. // If this alias is recursive (ie ls='ls -la') then don't take the bait.
  78. //
  79. if (Alias == Lexer->LastAlias) {
  80. if (ShDebugAlias != FALSE) {
  81. ShPrintTrace(Shell, "AliasSkipped: %s\n", Lexer->LastAlias->Name);
  82. }
  83. Result = TRUE;
  84. goto PerformAliasSubstitutionEnd;
  85. }
  86. assert((Alias->Value != NULL) && (Alias->ValueSize >= 1));
  87. if (ShDebugAlias != FALSE) {
  88. ShPrintTrace(Shell,
  89. "Aliasing '%s', replacing with '%s'\n",
  90. Lexer->TokenBuffer,
  91. Alias->Value);
  92. }
  93. //
  94. // If the unput character is valid, it needs to be rolled back into the
  95. // input too before this replacement text is spliced in. Most of the time
  96. // this is easy, it can just be put in some earlier space in the buffer.
  97. //
  98. if (Lexer->UnputCharacterValid != FALSE) {
  99. if (Lexer->InputBufferNextIndex != 0) {
  100. Lexer->InputBufferNextIndex -= 1;
  101. Lexer->InputBuffer[Lexer->InputBufferNextIndex] =
  102. Lexer->UnputCharacter;
  103. //
  104. // There's no space for the unput character, so bring out the heavy
  105. // artillery.
  106. //
  107. } else {
  108. Result = SwStringReplaceRegion(&(Lexer->InputBuffer),
  109. &(Lexer->InputBufferSize),
  110. &(Lexer->InputBufferCapacity),
  111. 0,
  112. 0,
  113. &(Lexer->UnputCharacter),
  114. 2);
  115. if (Result == FALSE) {
  116. goto PerformAliasSubstitutionEnd;
  117. }
  118. }
  119. Lexer->UnputCharacterValid = FALSE;
  120. }
  121. //
  122. // The substitution needs to be performed. Put the value onto the input
  123. // buffer. Start by expanding the buffer if needed.
  124. //
  125. Result = SwStringReplaceRegion(&(Lexer->InputBuffer),
  126. &(Lexer->InputBufferSize),
  127. &(Lexer->InputBufferCapacity),
  128. Lexer->InputBufferNextIndex,
  129. Lexer->InputBufferNextIndex,
  130. Alias->Value,
  131. Alias->ValueSize);
  132. if (Result == FALSE) {
  133. goto PerformAliasSubstitutionEnd;
  134. }
  135. //
  136. // Clear out this input token, as it was replaced. Also mark this alias as
  137. // the previous one so that if the value of this alias is recursive this
  138. // doesn't result in an infinite loop.
  139. //
  140. Lexer->TokenBufferSize = 0;
  141. Lexer->TokenType = -1;
  142. Lexer->LastAlias = Alias;
  143. Result = TRUE;
  144. PerformAliasSubstitutionEnd:
  145. return Result;
  146. }
  147. VOID
  148. ShDestroyAliasList (
  149. PSHELL Shell
  150. )
  151. /*++
  152. Routine Description:
  153. This routine destroys all the aliases in a shell. It is usually called
  154. during cleanup.
  155. Arguments:
  156. Shell - Supplies a pointer to the shell whose aliases will be cleaned up.
  157. Return Value:
  158. None.
  159. --*/
  160. {
  161. PSHELL_ALIAS Alias;
  162. while (LIST_EMPTY(&(Shell->AliasList)) == FALSE) {
  163. Alias = LIST_VALUE(Shell->AliasList.Next, SHELL_ALIAS, ListEntry);
  164. LIST_REMOVE(&(Alias->ListEntry));
  165. ShDestroyAlias(Alias);
  166. }
  167. return;
  168. }
  169. INT
  170. ShBuiltinAlias (
  171. PSHELL Shell,
  172. INT ArgumentCount,
  173. PSTR *Arguments
  174. )
  175. /*++
  176. Routine Description:
  177. This routine implements the builtin alias statement.
  178. Arguments:
  179. Shell - Supplies a pointer to the shell being run in.
  180. ArgumentCount - Supplies the number of arguments on the command line.
  181. Arguments - Supplies the array of pointers to strings representing each
  182. argument. Arguments are in the form name=value, where name will get
  183. substituted for value when it is found as the first word in a command.
  184. Return Value:
  185. 0 on success.
  186. 1 if an alias was not found.
  187. --*/
  188. {
  189. PSHELL_ALIAS Alias;
  190. PSTR Argument;
  191. INT ArgumentIndex;
  192. UINTN ArgumentSize;
  193. PLIST_ENTRY CurrentEntry;
  194. PSTR Equals;
  195. PSTR Name;
  196. UINTN NameSize;
  197. BOOL Result;
  198. ULONG ReturnValue;
  199. PSTR Value;
  200. UINTN ValueSize;
  201. Alias = NULL;
  202. Name = NULL;
  203. Value = NULL;
  204. //
  205. // If there are no arguments, then print all the aliases.
  206. //
  207. if (ArgumentCount == 1) {
  208. CurrentEntry = Shell->AliasList.Next;
  209. while (CurrentEntry != &(Shell->AliasList)) {
  210. Alias = LIST_VALUE(CurrentEntry, SHELL_ALIAS, ListEntry);
  211. CurrentEntry = CurrentEntry->Next;
  212. Result = ShPrintAlias(Alias);
  213. if (Result == FALSE) {
  214. return 1;
  215. }
  216. }
  217. return 0;
  218. }
  219. //
  220. // Loop through each argument and create or print the alias.
  221. //
  222. ReturnValue = 0;
  223. for (ArgumentIndex = 1; ArgumentIndex < ArgumentCount; ArgumentIndex += 1) {
  224. Argument = Arguments[ArgumentIndex];
  225. ArgumentSize = strlen(Argument) + 1;
  226. Equals = strchr(Argument, '=');
  227. if ((Equals == NULL) || (Equals == Argument)) {
  228. Alias = ShLookupAlias(Shell, Argument, ArgumentSize);
  229. if (Alias == NULL) {
  230. PRINT_ERROR("Alias %s not found.\n", Argument);
  231. ReturnValue = 1;
  232. } else {
  233. ShPrintAlias(Alias);
  234. }
  235. Alias = NULL;
  236. continue;
  237. }
  238. //
  239. // Create or replace the alias.
  240. //
  241. NameSize = (UINTN)Equals - (UINTN)Argument + 1;
  242. Alias = ShLookupAlias(Shell, Argument, NameSize);
  243. if (Alias == NULL) {
  244. Alias = malloc(sizeof(SHELL_ALIAS));
  245. if (Alias == NULL) {
  246. ReturnValue = 1;
  247. goto BuiltinAliasEnd;
  248. }
  249. memset(Alias, 0, sizeof(SHELL_ALIAS));
  250. Name = SwStringDuplicate(Argument, NameSize);
  251. if (Name == NULL) {
  252. ReturnValue = 1;
  253. goto BuiltinAliasEnd;
  254. }
  255. Alias->Name = Name;
  256. Alias->NameSize = NameSize;
  257. }
  258. //
  259. // Create a copy of the value, and add a space onto the end of it.
  260. //
  261. ValueSize = ArgumentSize - NameSize + 1;
  262. assert(ValueSize >= 2);
  263. Value = malloc(ValueSize);
  264. if (Value == NULL) {
  265. ReturnValue = 1;
  266. goto BuiltinAliasEnd;
  267. }
  268. memcpy(Value, Equals + 1, ValueSize - 1);
  269. Value[ValueSize - 2] = ' ';
  270. Value[ValueSize - 1] = '\0';
  271. if (Alias->Value != NULL) {
  272. free(Alias->Value);
  273. }
  274. Alias->Value = Value;
  275. Alias->ValueSize = ValueSize;
  276. if (Alias->ListEntry.Next == NULL) {
  277. INSERT_BEFORE(&(Alias->ListEntry), &(Shell->AliasList));
  278. }
  279. Alias = NULL;
  280. Name = NULL;
  281. Value = NULL;
  282. }
  283. BuiltinAliasEnd:
  284. if (Alias != NULL) {
  285. free(Alias);
  286. }
  287. if (Name != NULL) {
  288. free(Name);
  289. }
  290. if (Value != NULL) {
  291. free(Value);
  292. }
  293. return ReturnValue;
  294. }
  295. INT
  296. ShBuiltinUnalias (
  297. PSHELL Shell,
  298. INT ArgumentCount,
  299. PSTR *Arguments
  300. )
  301. /*++
  302. Routine Description:
  303. This routine implements the builtin unalias statement.
  304. Arguments:
  305. Shell - Supplies a pointer to the shell being run in.
  306. ArgumentCount - Supplies the number of arguments on the command line.
  307. Arguments - Supplies the array of pointers to strings representing each
  308. argument. Arguments are in the form name=value, where name will get
  309. substituted for value when it is found as the first word in a command.
  310. Return Value:
  311. 0 on success.
  312. 1 if an alias was not found.
  313. --*/
  314. {
  315. PSHELL_ALIAS Alias;
  316. PSTR Argument;
  317. ULONG ArgumentIndex;
  318. INT ReturnValue;
  319. ReturnValue = 0;
  320. for (ArgumentIndex = 1; ArgumentIndex < ArgumentCount; ArgumentIndex += 1) {
  321. Argument = Arguments[ArgumentIndex];
  322. //
  323. // The -a flag destroys all aliases.
  324. //
  325. if (strcmp(Argument, "-a") == 0) {
  326. ShDestroyAliasList(Shell);
  327. return 0;
  328. }
  329. Alias = ShLookupAlias(Shell, Argument, strlen(Argument) + 1);
  330. if (Alias == NULL) {
  331. PRINT_ERROR("Alias %s not found.\n", Argument);
  332. ReturnValue = 1;
  333. } else {
  334. LIST_REMOVE(&(Alias->ListEntry));
  335. ShDestroyAlias(Alias);
  336. }
  337. }
  338. return ReturnValue;
  339. }
  340. PSHELL_ALIAS
  341. ShLookupAlias (
  342. PSHELL Shell,
  343. PSTR Name,
  344. ULONG NameSize
  345. )
  346. /*++
  347. Routine Description:
  348. This routine looks up the given name and tries to find an alias for it.
  349. Arguments:
  350. Shell - Supplies a pointer to the shell to search.
  351. Name - Supplies a pointer to the name to search for.
  352. NameSize - Supplies the size of the name buffer in bytes including the
  353. null terminator.
  354. Return Value:
  355. Returns a pointer to the alias matching the given name on success.
  356. NULL if no alias could be found matching the given name.
  357. --*/
  358. {
  359. PSHELL_ALIAS Alias;
  360. PLIST_ENTRY CurrentEntry;
  361. assert((Name != NULL) && (NameSize != 0));
  362. CurrentEntry = Shell->AliasList.Next;
  363. while (CurrentEntry != &(Shell->AliasList)) {
  364. Alias = LIST_VALUE(CurrentEntry, SHELL_ALIAS, ListEntry);
  365. CurrentEntry = CurrentEntry->Next;
  366. if (strncmp(Alias->Name, Name, NameSize - 1) == 0) {
  367. return Alias;
  368. }
  369. }
  370. return NULL;
  371. }
  372. //
  373. // --------------------------------------------------------- Internal Functions
  374. //
  375. VOID
  376. ShDestroyAlias (
  377. PSHELL_ALIAS Alias
  378. )
  379. /*++
  380. Routine Description:
  381. This routine frees a shell alias. It assumes the alias has already been
  382. removed from the shell's alias list.
  383. Arguments:
  384. Alias - Supplies a pointer to the alias to destroy.
  385. Return Value:
  386. None.
  387. --*/
  388. {
  389. if (Alias->Name != NULL) {
  390. free(Alias->Name);
  391. }
  392. if (Alias->Value != NULL) {
  393. free(Alias->Value);
  394. }
  395. free(Alias);
  396. return;
  397. }
  398. BOOL
  399. ShPrintAlias (
  400. PSHELL_ALIAS Alias
  401. )
  402. /*++
  403. Routine Description:
  404. This routine prints a shell alias.
  405. Arguments:
  406. Alias - Supplies a pointer to the alias to destroy.
  407. Return Value:
  408. TRUE on success.
  409. FALSE on failure.
  410. --*/
  411. {
  412. BOOL Result;
  413. PSTR Value;
  414. UINTN ValueSize;
  415. printf("%s=", Alias->Name);
  416. Result = ShStringFormatForReentry(Alias->Value,
  417. Alias->ValueSize,
  418. &Value,
  419. &ValueSize);
  420. if (Result == FALSE) {
  421. return FALSE;
  422. }
  423. printf("%s\n", Value);
  424. free(Value);
  425. return TRUE;
  426. }