sed.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. sed.c
  5. Abstract:
  6. This module implements the sed (stream editor) utility.
  7. Author:
  8. Evan Green 11-Jul-2013
  9. Environment:
  10. POSIX
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include "sed.h"
  16. #include <assert.h>
  17. #include <errno.h>
  18. #include <getopt.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <unistd.h>
  22. #include "../swlib.h"
  23. //
  24. // ---------------------------------------------------------------- Definitions
  25. //
  26. //
  27. // ------------------------------------------------------ Data Type Definitions
  28. //
  29. //
  30. // ----------------------------------------------- Internal Function Prototypes
  31. //
  32. INT
  33. SedProcessInput (
  34. PSED_CONTEXT Context
  35. );
  36. INT
  37. SedProcessInputLine (
  38. PSED_CONTEXT Context
  39. );
  40. BOOL
  41. SedDoesAddressMatch (
  42. PSED_CONTEXT Context,
  43. PSED_COMMAND Command
  44. );
  45. BOOL
  46. SedCheckAddress (
  47. PSED_CONTEXT Context,
  48. PSED_ADDRESS Address
  49. );
  50. INT
  51. SedExecuteCommand (
  52. PSED_CONTEXT Context,
  53. PSED_COMMAND Command
  54. );
  55. //
  56. // -------------------------------------------------------------------- Globals
  57. //
  58. struct option SedLongOptions[] = {
  59. {"expression", required_argument, 0, 'e'},
  60. {"file", required_argument, 0, 'f'},
  61. {"quiet", no_argument, 0, 'n'},
  62. {"silent", no_argument, 0, 'n'},
  63. {"help", no_argument, 0, 'h'},
  64. {"version", no_argument, 0, 'V'},
  65. {NULL, 0, 0, 0}
  66. };
  67. //
  68. // ------------------------------------------------------------------ Functions
  69. //
  70. INT
  71. SedMain (
  72. INT ArgumentCount,
  73. CHAR **Arguments
  74. )
  75. /*++
  76. Routine Description:
  77. This routine implements the main entry point for the sed (stream editor)
  78. utility.
  79. Arguments:
  80. ArgumentCount - Supplies the number of arguments on the command line.
  81. Arguments - Supplies an array of pointers to strings representing the
  82. arguments.
  83. Return Value:
  84. 0 on success.
  85. Non-zero on failure.
  86. --*/
  87. {
  88. PSED_APPEND_ENTRY AppendEntry;
  89. PSTR Argument;
  90. ULONG ArgumentIndex;
  91. SED_CONTEXT Context;
  92. PSTR FirstSource;
  93. PSED_INPUT InputEntry;
  94. INT Option;
  95. BOOL ReadFromStandardIn;
  96. BOOL Result;
  97. PSED_SCRIPT_FRAGMENT ScriptFragment;
  98. BOOL ScriptRead;
  99. PSTR SecondSource;
  100. int Status;
  101. int TotalStatus;
  102. PSED_WRITE_FILE WriteFile;
  103. FirstSource = NULL;
  104. ReadFromStandardIn = TRUE;
  105. ScriptRead = FALSE;
  106. SecondSource = NULL;
  107. Status = 0;
  108. TotalStatus = 0;
  109. memset(&Context, 0, sizeof(SED_CONTEXT));
  110. Context.PrintLines = TRUE;
  111. Context.HeadCommand.Function.Type = SedFunctionGroup;
  112. INITIALIZE_LIST_HEAD(&(Context.HeadCommand.Function.U.ChildList));
  113. INITIALIZE_LIST_HEAD(&(Context.ScriptList));
  114. INITIALIZE_LIST_HEAD(&(Context.InputList));
  115. INITIALIZE_LIST_HEAD(&(Context.AppendList));
  116. INITIALIZE_LIST_HEAD(&(Context.WriteFileList));
  117. Context.StandardOut.File = stdout;
  118. Context.StandardOut.LineTerminated = TRUE;
  119. Context.ScriptString = SedCreateString(NULL, 0, TRUE);
  120. if (Context.ScriptString == NULL) {
  121. TotalStatus = ENOMEM;
  122. goto MainEnd;
  123. }
  124. Context.PatternSpace = SedCreateString(NULL, 0, TRUE);
  125. if (Context.PatternSpace == NULL) {
  126. TotalStatus = ENOMEM;
  127. goto MainEnd;
  128. }
  129. Context.HoldSpace = SedCreateString(NULL, 0, TRUE);
  130. if (Context.HoldSpace == NULL) {
  131. TotalStatus = ENOMEM;
  132. goto MainEnd;
  133. }
  134. //
  135. // Process the control arguments.
  136. //
  137. while (TRUE) {
  138. Option = getopt_long(ArgumentCount,
  139. Arguments,
  140. SED_OPTIONS_STRING,
  141. SedLongOptions,
  142. NULL);
  143. if (Option == -1) {
  144. break;
  145. }
  146. if ((Option == '?') || (Option == ':')) {
  147. Status = 1;
  148. return Status;
  149. }
  150. switch (Option) {
  151. case 'e':
  152. ScriptRead = TRUE;
  153. Argument = optarg;
  154. assert(Argument != NULL);
  155. Result = SedAddScriptString(&Context, Argument);
  156. if (Result == FALSE) {
  157. TotalStatus = 1;
  158. goto MainEnd;
  159. }
  160. break;
  161. case 'f':
  162. ScriptRead = TRUE;
  163. Argument = optarg;
  164. assert(Argument != NULL);
  165. Result = SedAddScriptFile(&Context, Argument);
  166. if (Result == FALSE) {
  167. TotalStatus = 1;
  168. goto MainEnd;
  169. }
  170. break;
  171. case 'n':
  172. Context.PrintLines = FALSE;
  173. break;
  174. case 'V':
  175. SwPrintVersion(SED_VERSION_MAJOR, SED_VERSION_MINOR);
  176. return 1;
  177. case 'h':
  178. printf(SED_USAGE);
  179. return 1;
  180. default:
  181. assert(FALSE);
  182. Status = 1;
  183. return Status;
  184. }
  185. }
  186. ArgumentIndex = optind;
  187. if (ArgumentIndex > ArgumentCount) {
  188. ArgumentIndex = ArgumentCount;
  189. }
  190. if (ArgumentIndex < ArgumentCount) {
  191. FirstSource = Arguments[ArgumentIndex];
  192. if (ArgumentIndex + 1 < ArgumentCount) {
  193. SecondSource = Arguments[ArgumentIndex + 1];
  194. }
  195. }
  196. //
  197. // If there were no scripts read, the first non-control argument is the
  198. // script.
  199. //
  200. if (ScriptRead == FALSE) {
  201. if (FirstSource == NULL) {
  202. SwPrintError(0, NULL, "Argument expected. Try --help for usage");
  203. TotalStatus = 1;
  204. goto MainEnd;
  205. }
  206. Result = SedAddScriptString(&Context, FirstSource);
  207. if (Result == FALSE) {
  208. goto MainEnd;
  209. }
  210. if (SecondSource != NULL) {
  211. ReadFromStandardIn = FALSE;
  212. }
  213. } else if (FirstSource != NULL) {
  214. ReadFromStandardIn = FALSE;
  215. }
  216. //
  217. // Parse the final script.
  218. //
  219. Result = SedParseScript(&Context, Context.ScriptString->Data);
  220. if (Result == FALSE) {
  221. goto MainEnd;
  222. }
  223. //
  224. // Reset the line number for the input files.
  225. //
  226. Context.LineNumber = 0;
  227. if (ReadFromStandardIn != FALSE) {
  228. //
  229. // Create a single input entry for standard in.
  230. //
  231. InputEntry = malloc(sizeof(SED_INPUT));
  232. if (InputEntry == NULL) {
  233. TotalStatus = ENOMEM;
  234. goto MainEnd;
  235. }
  236. InputEntry->File = stdin;
  237. INSERT_BEFORE(&(InputEntry->ListEntry), &(Context.InputList));
  238. Status = SedProcessInput(&Context);
  239. if (Status != 0) {
  240. TotalStatus = Status;
  241. }
  242. goto MainEnd;
  243. }
  244. //
  245. // Loop through the remaining arguments to create the input entries and
  246. // figure out which input file contains the last line.
  247. //
  248. while (ArgumentIndex < ArgumentCount) {
  249. Argument = Arguments[ArgumentIndex];
  250. ArgumentIndex += 1;
  251. //
  252. // Skip over the script itself.
  253. //
  254. if ((ScriptRead == FALSE) && (Argument == FirstSource)) {
  255. continue;
  256. }
  257. InputEntry = malloc(sizeof(SED_INPUT));
  258. if (InputEntry == NULL) {
  259. TotalStatus = ENOMEM;
  260. goto MainEnd;
  261. }
  262. InputEntry->File = fopen(Argument, "r");
  263. if (InputEntry->File != NULL) {
  264. INSERT_BEFORE(&(InputEntry->ListEntry), &(Context.InputList));
  265. } else {
  266. free(InputEntry);
  267. }
  268. }
  269. //
  270. // Let sed process all this.
  271. //
  272. Status = SedProcessInput(&Context);
  273. if (Status != 0) {
  274. TotalStatus = Status;
  275. goto MainEnd;
  276. }
  277. MainEnd:
  278. if (Context.ScriptString != NULL) {
  279. SedDestroyString(Context.ScriptString);
  280. }
  281. if (Context.PatternSpace != NULL) {
  282. SedDestroyString(Context.PatternSpace);
  283. }
  284. if (Context.HoldSpace != NULL) {
  285. SedDestroyString(Context.HoldSpace);
  286. }
  287. SedDestroyCommands(&Context);
  288. while (LIST_EMPTY(&(Context.ScriptList)) == FALSE) {
  289. ScriptFragment = LIST_VALUE(Context.ScriptList.Next,
  290. SED_SCRIPT_FRAGMENT,
  291. ListEntry);
  292. LIST_REMOVE(&(ScriptFragment->ListEntry));
  293. free(ScriptFragment);
  294. }
  295. while (LIST_EMPTY(&(Context.InputList)) == FALSE) {
  296. InputEntry = LIST_VALUE(Context.InputList.Next, SED_INPUT, ListEntry);
  297. LIST_REMOVE(&(InputEntry->ListEntry));
  298. if ((InputEntry->File != NULL) && (InputEntry->File != stdin)) {
  299. fclose(InputEntry->File);
  300. }
  301. free(InputEntry);
  302. }
  303. while (LIST_EMPTY(&(Context.AppendList)) == FALSE) {
  304. AppendEntry = LIST_VALUE(Context.AppendList.Next,
  305. SED_APPEND_ENTRY,
  306. ListEntry);
  307. LIST_REMOVE(&(AppendEntry->ListEntry));
  308. free(AppendEntry);
  309. }
  310. while (LIST_EMPTY(&(Context.WriteFileList)) == FALSE) {
  311. WriteFile = LIST_VALUE(Context.WriteFileList.Next,
  312. SED_WRITE_FILE,
  313. ListEntry);
  314. LIST_REMOVE(&(WriteFile->ListEntry));
  315. if (WriteFile->Name != NULL) {
  316. SedDestroyString(WriteFile->Name);
  317. }
  318. if (WriteFile->File != NULL) {
  319. fclose(WriteFile->File);
  320. }
  321. free(WriteFile);
  322. }
  323. if ((TotalStatus == 0) && (Status != 0)) {
  324. TotalStatus = Status;
  325. }
  326. return TotalStatus;
  327. }
  328. INT
  329. SedReadLine (
  330. PSED_CONTEXT Context
  331. )
  332. /*++
  333. Routine Description:
  334. This routine reads a new line into the pattern space, sans its trailing
  335. newline.
  336. Arguments:
  337. Context - Supplies a pointer to the application context.
  338. Return Value:
  339. 0 on success.
  340. Non-zero on failure.
  341. --*/
  342. {
  343. PSED_APPEND_ENTRY AppendEntry;
  344. int Character;
  345. FILE *File;
  346. PSED_INPUT InputEntry;
  347. PSED_STRING Pattern;
  348. FILE *ReadFile;
  349. BOOL Result;
  350. INT Status;
  351. Pattern = Context->PatternSpace;
  352. Status = 0;
  353. //
  354. // Deal with any appended stuff.
  355. //
  356. while (LIST_EMPTY(&(Context->AppendList)) == FALSE) {
  357. AppendEntry = LIST_VALUE(Context->AppendList.Next,
  358. SED_APPEND_ENTRY,
  359. ListEntry);
  360. LIST_REMOVE(&(AppendEntry->ListEntry));
  361. if (AppendEntry->Type == SedFunctionPrintTextAtLineEnd) {
  362. printf("%s\n", AppendEntry->StringOrPath->Data);
  363. } else {
  364. assert(AppendEntry->Type == SedFunctionReadFile);
  365. //
  366. // Write the file to standard out.
  367. //
  368. ReadFile = fopen(AppendEntry->StringOrPath->Data, "r");
  369. if (ReadFile != NULL) {
  370. while (TRUE) {
  371. Character = fgetc(ReadFile);
  372. if (Character == EOF) {
  373. break;
  374. }
  375. fputc(Character, stdout);
  376. }
  377. fclose(ReadFile);
  378. }
  379. }
  380. free(AppendEntry);
  381. }
  382. //
  383. // If there is no current file, get the first one.
  384. //
  385. InputEntry = Context->CurrentInput;
  386. if (InputEntry == NULL) {
  387. if (LIST_EMPTY(&(Context->InputList)) != FALSE) {
  388. Context->Done = TRUE;
  389. goto ReadLineEnd;
  390. }
  391. InputEntry = LIST_VALUE(Context->InputList.Next, SED_INPUT, ListEntry);
  392. Context->CurrentInput = InputEntry;
  393. }
  394. File = InputEntry->File;
  395. //
  396. // If the last line variable is already set, then this is toast.
  397. //
  398. if (Context->LastLine != FALSE) {
  399. Context->Done = TRUE;
  400. goto ReadLineEnd;
  401. }
  402. Context->LineNumber += 1;
  403. while (TRUE) {
  404. Character = fgetc(File);
  405. if (Character == EOF) {
  406. //
  407. // Return this as a line for sure, but return it as the last line
  408. // if this is the last file in the input. Otherwise, move on to
  409. // the next input.
  410. //
  411. if (InputEntry->ListEntry.Next == &(Context->InputList)) {
  412. Context->LastLine = TRUE;
  413. } else {
  414. InputEntry = LIST_VALUE(InputEntry->ListEntry.Next,
  415. SED_INPUT,
  416. ListEntry);
  417. Context->CurrentInput = InputEntry;
  418. File = InputEntry->File;
  419. }
  420. //
  421. // If no input was received yet, keep going.
  422. //
  423. if (Pattern->Size == 1) {
  424. if (Context->LastLine != FALSE) {
  425. Context->Done = TRUE;
  426. } else {
  427. continue;
  428. }
  429. }
  430. Context->LineTerminator = EOF;
  431. break;
  432. }
  433. if ((Character == '\n') || (Character == '\0')) {
  434. Context->LineTerminator = Character;
  435. Character = fgetc(File);
  436. //
  437. // A newline at the end of a file does not count as a new line.
  438. // Examples: an empty file has zero lines, and a file with a single
  439. // newline in it has one. But a file with some characters and no
  440. // newline also has one line.
  441. //
  442. if (Character == EOF) {
  443. if (InputEntry->ListEntry.Next == &(Context->InputList)) {
  444. Context->LastLine = TRUE;
  445. } else {
  446. InputEntry = LIST_VALUE(InputEntry->ListEntry.Next,
  447. SED_INPUT,
  448. ListEntry);
  449. Context->CurrentInput = InputEntry;
  450. }
  451. } else {
  452. ungetc(Character, File);
  453. }
  454. break;
  455. }
  456. Result = SedAppendString(Context->PatternSpace, (PSTR)&Character, 1);
  457. if (Result == FALSE) {
  458. Status = ENOMEM;
  459. goto ReadLineEnd;
  460. }
  461. }
  462. ReadLineEnd:
  463. return Status;
  464. }
  465. //
  466. // --------------------------------------------------------- Internal Functions
  467. //
  468. INT
  469. SedProcessInput (
  470. PSED_CONTEXT Context
  471. )
  472. /*++
  473. Routine Description:
  474. This routine runs the sed scripts against the input files.
  475. Arguments:
  476. Context - Supplies a pointer to the application context.
  477. Return Value:
  478. 0 on success.
  479. Non-zero on failure.
  480. --*/
  481. {
  482. INT Status;
  483. Status = 0;
  484. //
  485. // Loop processing lines.
  486. //
  487. while (Context->Quit == FALSE) {
  488. Status = SedReadLine(Context);
  489. if (Status != 0) {
  490. goto ProcessInputEnd;
  491. }
  492. if (Context->Done != FALSE) {
  493. break;
  494. }
  495. Context->SkipPrint = FALSE;
  496. Status = SedProcessInputLine(Context);
  497. if (Status != 0) {
  498. goto ProcessInputEnd;
  499. }
  500. //
  501. // Print the pattern space if the flag is set.
  502. //
  503. if ((Context->PrintLines != FALSE) &&
  504. (Context->SkipPrint == FALSE)) {
  505. SedPrint(Context,
  506. Context->PatternSpace->Data,
  507. Context->LineTerminator);
  508. }
  509. //
  510. // Clear the pattern space.
  511. //
  512. assert(Context->PatternSpace->Size != 0);
  513. Context->PatternSpace->Data[0] = '\0';
  514. Context->PatternSpace->Size = 1;
  515. }
  516. ProcessInputEnd:
  517. return Status;
  518. }
  519. INT
  520. SedProcessInputLine (
  521. PSED_CONTEXT Context
  522. )
  523. /*++
  524. Routine Description:
  525. This routine runs the loaded scripts on a single line of the pattern space.
  526. Arguments:
  527. Context - Supplies a pointer to the application context.
  528. Return Value:
  529. 0 on success.
  530. Non-zero on failure.
  531. --*/
  532. {
  533. PSED_COMMAND Command;
  534. PSED_COMMAND NextCommand;
  535. INT Status;
  536. Status = 0;
  537. Context->TestResult = FALSE;
  538. if (LIST_EMPTY(&(Context->HeadCommand.Function.U.ChildList)) != FALSE) {
  539. return 0;
  540. }
  541. Context->NextCommand =
  542. LIST_VALUE(Context->HeadCommand.Function.U.ChildList.Next,
  543. SED_COMMAND,
  544. ListEntry);
  545. //
  546. // Loop processing commands.
  547. //
  548. while (Context->NextCommand != NULL) {
  549. if ((Context->Done != FALSE) || (Context->Quit != FALSE)) {
  550. break;
  551. }
  552. Command = Context->NextCommand;
  553. //
  554. // Fill in the next command by moving on to the next sibling, or up the
  555. // chain if necessary. Stop if the head command is reached.
  556. //
  557. NextCommand = Command;
  558. while (NextCommand != &(Context->HeadCommand)) {
  559. //
  560. // If there's a sibling, go to it.
  561. //
  562. if (NextCommand->ListEntry.Next !=
  563. &(NextCommand->Parent->Function.U.ChildList)) {
  564. NextCommand = LIST_VALUE(NextCommand->ListEntry.Next,
  565. SED_COMMAND,
  566. ListEntry);
  567. break;
  568. //
  569. // Move up to the parent.
  570. //
  571. } else {
  572. NextCommand = NextCommand->Parent;
  573. }
  574. }
  575. if (NextCommand == &(Context->HeadCommand)) {
  576. NextCommand = NULL;
  577. }
  578. Context->NextCommand = NextCommand;
  579. //
  580. // Process the command.
  581. //
  582. Status = SedExecuteCommand(Context, Command);
  583. if (Status != 0) {
  584. goto ProcessInputLineEnd;
  585. }
  586. }
  587. ProcessInputLineEnd:
  588. return Status;
  589. }
  590. BOOL
  591. SedDoesAddressMatch (
  592. PSED_CONTEXT Context,
  593. PSED_COMMAND Command
  594. )
  595. /*++
  596. Routine Description:
  597. This routine determines if the given command matches the current address.
  598. Arguments:
  599. Context - Supplies a pointer to the application context.
  600. Command - Supplies a pointer to the command potentially being executed.
  601. Return Value:
  602. 0 on success.
  603. Non-zero on failure.
  604. --*/
  605. {
  606. BOOL Result;
  607. Result = FALSE;
  608. if (Command->AddressCount == 0) {
  609. Result = TRUE;
  610. } else if (Command->AddressCount == 1) {
  611. Result = SedCheckAddress(Context, &(Command->Address[0]));
  612. } else {
  613. assert(Command->AddressCount == 2);
  614. if (Command->Active != FALSE) {
  615. if (SedCheckAddress(Context, &(Command->Address[1])) != FALSE) {
  616. Command->Active = FALSE;
  617. }
  618. Result = TRUE;
  619. } else {
  620. if (SedCheckAddress(Context, &(Command->Address[0])) != FALSE) {
  621. Command->Active = TRUE;
  622. Result = TRUE;
  623. }
  624. }
  625. }
  626. if (Command->AddressNegated != FALSE) {
  627. Result = !Result;
  628. }
  629. return Result;
  630. }
  631. BOOL
  632. SedCheckAddress (
  633. PSED_CONTEXT Context,
  634. PSED_ADDRESS Address
  635. )
  636. /*++
  637. Routine Description:
  638. This routine determines if the given address matches the current context.
  639. Arguments:
  640. Context - Supplies a pointer to the application context.
  641. Address - Supplies a pointer to the address to check.
  642. Return Value:
  643. 0 on success.
  644. Non-zero on failure.
  645. --*/
  646. {
  647. int Status;
  648. switch (Address->Type) {
  649. case SedAddressNumber:
  650. if (Address->U.Line == Context->LineNumber) {
  651. return TRUE;
  652. }
  653. break;
  654. case SedAddressLastLine:
  655. if (Context->LastLine != FALSE) {
  656. return TRUE;
  657. }
  658. break;
  659. case SedAddressExpression:
  660. assert((Context->PatternSpace->Size != 0) &&
  661. (Context->PatternSpace->Data[Context->PatternSpace->Size - 1] ==
  662. '\0'));
  663. Status = regexec(&(Address->U.Expression),
  664. Context->PatternSpace->Data,
  665. 0,
  666. NULL,
  667. 0);
  668. if (Status == 0) {
  669. return TRUE;
  670. }
  671. break;
  672. default:
  673. assert(FALSE);
  674. break;
  675. }
  676. return FALSE;
  677. }
  678. INT
  679. SedExecuteCommand (
  680. PSED_CONTEXT Context,
  681. PSED_COMMAND Command
  682. )
  683. /*++
  684. Routine Description:
  685. This routine runs the given command on the current pattern space.
  686. Arguments:
  687. Context - Supplies a pointer to the application context.
  688. Command - Supplies a pointer to the command to run.
  689. Return Value:
  690. 0 on success.
  691. Non-zero error code on failure.
  692. --*/
  693. {
  694. BOOL AddressMatch;
  695. PSED_EXECUTE_FUNCTION ExecuteRoutine;
  696. //
  697. // Figure out if the address matches the line.
  698. //
  699. AddressMatch = SedDoesAddressMatch(Context, Command);
  700. if (AddressMatch == FALSE) {
  701. return 0;
  702. }
  703. assert((Command->Function.Type != SedFunctionInvalid) &&
  704. (Command->Function.Type < SedFunctionCount));
  705. ExecuteRoutine = SedFunctionTable[Command->Function.Type];
  706. return ExecuteRoutine(Context, Command);
  707. }