sedfunc.c 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. sedfunc.c
  5. Abstract:
  6. This module implements the actual editing functions for the sed utility.
  7. Author:
  8. Evan Green 12-Jul-2013
  9. Environment:
  10. POSIX
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include "sed.h"
  16. #include <assert.h>
  17. #include <ctype.h>
  18. #include <errno.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <unistd.h>
  22. #include "../swlib.h"
  23. //
  24. // ---------------------------------------------------------------- Definitions
  25. //
  26. //
  27. // Define the number of columns to print when displaying with the 'l' (ell)
  28. // command.
  29. //
  30. #define SED_PRINT_COLUMNS 80
  31. //
  32. // ------------------------------------------------------ Data Type Definitions
  33. //
  34. //
  35. // ----------------------------------------------- Internal Function Prototypes
  36. //
  37. INT
  38. SedExecuteGroup (
  39. PSED_CONTEXT Context,
  40. PSED_COMMAND Command
  41. );
  42. INT
  43. SedExecuteAppend (
  44. PSED_CONTEXT Context,
  45. PSED_COMMAND Command
  46. );
  47. INT
  48. SedExecuteBranchOrTest (
  49. PSED_CONTEXT Context,
  50. PSED_COMMAND Command
  51. );
  52. INT
  53. SedExecuteDeleteAndPrintText (
  54. PSED_CONTEXT Context,
  55. PSED_COMMAND Command
  56. );
  57. INT
  58. SedExecuteDelete (
  59. PSED_CONTEXT Context,
  60. PSED_COMMAND Command
  61. );
  62. INT
  63. SedExecuteHoldSpaceToPattern (
  64. PSED_CONTEXT Context,
  65. PSED_COMMAND Command
  66. );
  67. INT
  68. SedExecutePatternSpaceToHold (
  69. PSED_CONTEXT Context,
  70. PSED_COMMAND Command
  71. );
  72. INT
  73. SedExecutePrint (
  74. PSED_CONTEXT Context,
  75. PSED_COMMAND Command
  76. );
  77. INT
  78. SedExecutePrintEscapedText (
  79. PSED_CONTEXT Context,
  80. PSED_COMMAND Command
  81. );
  82. INT
  83. SedExecuteMoveToNextLine (
  84. PSED_CONTEXT Context,
  85. PSED_COMMAND Command
  86. );
  87. INT
  88. SedExecuteWritePatternSpace (
  89. PSED_CONTEXT Context,
  90. PSED_COMMAND Command
  91. );
  92. INT
  93. SedExecuteQuit (
  94. PSED_CONTEXT Context,
  95. PSED_COMMAND Command
  96. );
  97. INT
  98. SedExecuteSubstitute (
  99. PSED_CONTEXT Context,
  100. PSED_COMMAND Command
  101. );
  102. INT
  103. SedExecuteWriteFile (
  104. PSED_CONTEXT Context,
  105. PSED_COMMAND Command
  106. );
  107. INT
  108. SedExecuteExchangePatternAndHold (
  109. PSED_CONTEXT Context,
  110. PSED_COMMAND Command
  111. );
  112. INT
  113. SedExecuteSubstituteCharacters (
  114. PSED_CONTEXT Context,
  115. PSED_COMMAND Command
  116. );
  117. INT
  118. SedExecuteNop (
  119. PSED_CONTEXT Context,
  120. PSED_COMMAND Command
  121. );
  122. INT
  123. SedExecuteWriteLineNumber (
  124. PSED_CONTEXT Context,
  125. PSED_COMMAND Command
  126. );
  127. //
  128. // -------------------------------------------------------------------- Globals
  129. //
  130. PSED_EXECUTE_FUNCTION SedFunctionTable[SedFunctionCount] = {
  131. NULL,
  132. SedExecuteGroup,
  133. SedExecuteAppend,
  134. SedExecuteBranchOrTest,
  135. SedExecuteDeleteAndPrintText,
  136. SedExecuteDelete,
  137. SedExecuteDelete,
  138. SedExecuteHoldSpaceToPattern,
  139. SedExecuteHoldSpaceToPattern,
  140. SedExecutePatternSpaceToHold,
  141. SedExecutePatternSpaceToHold,
  142. SedExecutePrint,
  143. SedExecutePrintEscapedText,
  144. SedExecuteMoveToNextLine,
  145. SedExecuteMoveToNextLine,
  146. SedExecuteWritePatternSpace,
  147. SedExecuteWritePatternSpace,
  148. SedExecuteQuit,
  149. SedExecuteAppend,
  150. SedExecuteSubstitute,
  151. SedExecuteBranchOrTest,
  152. SedExecuteWriteFile,
  153. SedExecuteExchangePatternAndHold,
  154. SedExecuteSubstituteCharacters,
  155. SedExecuteNop,
  156. SedExecuteWriteLineNumber,
  157. SedExecuteNop,
  158. };
  159. //
  160. // ------------------------------------------------------------------ Functions
  161. //
  162. INT
  163. SedExecuteGroup (
  164. PSED_CONTEXT Context,
  165. PSED_COMMAND Command
  166. )
  167. /*++
  168. Routine Description:
  169. This routine executes a group command.
  170. Arguments:
  171. Context - Supplies a pointer to the application context.
  172. Command - Supplies a pointer to the command to execute.
  173. Return Value:
  174. 0 on success.
  175. Non-zero error code on failure.
  176. --*/
  177. {
  178. assert(Command->Function.Type == SedFunctionGroup);
  179. //
  180. // Set the next command to be the first child.
  181. //
  182. if (LIST_EMPTY(&(Command->Function.U.ChildList)) == FALSE) {
  183. Context->NextCommand = LIST_VALUE(Command->Function.U.ChildList.Next,
  184. SED_COMMAND,
  185. ListEntry);
  186. }
  187. return 0;
  188. }
  189. INT
  190. SedExecuteAppend (
  191. PSED_CONTEXT Context,
  192. PSED_COMMAND Command
  193. )
  194. /*++
  195. Routine Description:
  196. This routine executes "print text at line end" or "read file" command.
  197. Arguments:
  198. Context - Supplies a pointer to the application context.
  199. Command - Supplies a pointer to the command to execute.
  200. Return Value:
  201. 0 on success.
  202. Non-zero error code on failure.
  203. --*/
  204. {
  205. PSED_APPEND_ENTRY AppendEntry;
  206. assert((Command->Function.Type == SedFunctionPrintTextAtLineEnd) ||
  207. (Command->Function.Type == SedFunctionReadFile));
  208. if (Command->Function.U.StringArgument == NULL) {
  209. return 0;
  210. }
  211. //
  212. // Allocate and fill out an append entry structure.
  213. //
  214. AppendEntry = malloc(sizeof(SED_APPEND_ENTRY));
  215. if (AppendEntry == NULL) {
  216. return ENOMEM;
  217. }
  218. AppendEntry->Type = Command->Function.Type;
  219. AppendEntry->StringOrPath = Command->Function.U.StringArgument;
  220. //
  221. // Stick the command on the end of the append list.
  222. //
  223. INSERT_BEFORE(&(AppendEntry->ListEntry), &(Context->AppendList));
  224. return 0;
  225. }
  226. INT
  227. SedExecuteBranchOrTest (
  228. PSED_CONTEXT Context,
  229. PSED_COMMAND Command
  230. )
  231. /*++
  232. Routine Description:
  233. This routine executes a branch command or a test command.
  234. Arguments:
  235. Context - Supplies a pointer to the application context.
  236. Command - Supplies a pointer to the command to execute.
  237. Return Value:
  238. 0 on success.
  239. Non-zero error code on failure.
  240. --*/
  241. {
  242. int Match;
  243. PSED_STRING Name;
  244. PSED_COMMAND NextCommand;
  245. assert((Command->Function.Type == SedFunctionBranch) ||
  246. (Command->Function.Type == SedFunctionTest));
  247. Name = Command->Function.U.StringArgument;
  248. //
  249. // For tests, check the test result, which indicates whether anything has
  250. // been substituted since the last line was read from the input or the
  251. // last test command.
  252. //
  253. if (Command->Function.Type == SedFunctionTest) {
  254. if (Context->TestResult == FALSE) {
  255. return 0;
  256. }
  257. Context->TestResult = FALSE;
  258. }
  259. //
  260. // Loop looking for an entry with the given label.
  261. //
  262. NextCommand = Command;
  263. Command = &(Context->HeadCommand);
  264. while (NextCommand != NULL) {
  265. if ((Command->Function.Type == SedFunctionGroup) &&
  266. (LIST_EMPTY(&(Command->Function.U.ChildList)) == FALSE)) {
  267. NextCommand = LIST_VALUE(Command->Function.U.ChildList.Next,
  268. SED_COMMAND,
  269. ListEntry);
  270. } else {
  271. //
  272. // Fill in the next command by moving on to the next sibling, or up
  273. // the chain if necessary. Stop if the head command is reached.
  274. //
  275. while (NextCommand != &(Context->HeadCommand)) {
  276. //
  277. // If there's a sibling, go to it.
  278. //
  279. if (NextCommand->ListEntry.Next !=
  280. &(NextCommand->Parent->Function.U.ChildList)) {
  281. NextCommand = LIST_VALUE(NextCommand->ListEntry.Next,
  282. SED_COMMAND,
  283. ListEntry);
  284. break;
  285. //
  286. // Move up to the parent.
  287. //
  288. } else {
  289. NextCommand = NextCommand->Parent;
  290. }
  291. }
  292. }
  293. if (NextCommand == &(Context->HeadCommand)) {
  294. NextCommand = NULL;
  295. }
  296. //
  297. // Check it out if this is a label.
  298. //
  299. if (Command->Function.Type == SedFunctionLabel) {
  300. //
  301. // If they're both null, this is a match.
  302. //
  303. if ((Name == NULL) &&
  304. (Command->Function.U.StringArgument == NULL)) {
  305. break;
  306. }
  307. //
  308. // If they're both not null, test to see if the strings are equal.
  309. //
  310. if ((Name != NULL) &&
  311. (Command->Function.U.StringArgument != NULL)) {
  312. Match = strcmp(Name->Data,
  313. Command->Function.U.StringArgument->Data);
  314. if (Match == 0) {
  315. break;
  316. }
  317. }
  318. }
  319. //
  320. // Move on to the next command.
  321. //
  322. Command = NextCommand;
  323. }
  324. //
  325. // If the label wasn't found, it branches to the end of the script, which
  326. // is null.
  327. //
  328. assert((Command == NULL) || (Command->Function.Type == SedFunctionLabel));
  329. Context->NextCommand = Command;
  330. return 0;
  331. }
  332. INT
  333. SedExecuteDeleteAndPrintText (
  334. PSED_CONTEXT Context,
  335. PSED_COMMAND Command
  336. )
  337. /*++
  338. Routine Description:
  339. This routine executes a "delete and print text", which deletes the current
  340. pattern space. For a 0 or 1 address match, it prints the text. For a 2
  341. address match, it prints the text at the end of the range.
  342. Arguments:
  343. Context - Supplies a pointer to the application context.
  344. Command - Supplies a pointer to the command to execute.
  345. Return Value:
  346. 0 on success.
  347. Non-zero error code on failure.
  348. --*/
  349. {
  350. assert(Command->Function.Type == SedFunctionDeleteAndPrintText);
  351. //
  352. // Delete the pattern space.
  353. //
  354. assert(Context->PatternSpace->Size != 0);
  355. Context->PatternSpace->Data[0] = '\0';
  356. Context->PatternSpace->Size = 1;
  357. //
  358. // Print the text if this isn't a 2-address form or if the active flag is
  359. // off in the two address form.
  360. //
  361. if ((Command->AddressCount < 2) || (Command->Active == FALSE)) {
  362. if (Command->Function.U.StringArgument != NULL) {
  363. SedPrint(Context, Command->Function.U.StringArgument->Data, EOF);
  364. }
  365. }
  366. Context->NextCommand = NULL;
  367. Context->SkipPrint = TRUE;
  368. return 0;
  369. }
  370. INT
  371. SedExecuteDelete (
  372. PSED_CONTEXT Context,
  373. PSED_COMMAND Command
  374. )
  375. /*++
  376. Routine Description:
  377. This routine executes a "delete pattern space and start next cycle" command
  378. or a "delete pattern space up to the next newline and start next cycle"
  379. command.
  380. Arguments:
  381. Context - Supplies a pointer to the application context.
  382. Command - Supplies a pointer to the command to execute.
  383. Return Value:
  384. 0 on success.
  385. Non-zero error code on failure.
  386. --*/
  387. {
  388. PSED_STRING Pattern;
  389. UINTN Size;
  390. assert((Command->Function.Type == SedFunctionDelete) ||
  391. (Command->Function.Type == SedFunctionDeleteToNewline));
  392. Pattern = Context->PatternSpace;
  393. assert(Pattern->Size != 0);
  394. //
  395. // Delete up to the next newline in the pattern space.
  396. //
  397. if (Command->Function.Type == SedFunctionDeleteToNewline) {
  398. Size = 0;
  399. while ((Size < Pattern->Size) && (Pattern->Data[Size] != '\n')) {
  400. Size += 1;
  401. }
  402. if (Size == Pattern->Size) {
  403. Pattern->Data[0] = '\0';
  404. Pattern->Size = 1;
  405. } else {
  406. SwStringRemoveRegion(Pattern->Data, &(Pattern->Size), 0, Size + 1);
  407. }
  408. //
  409. // Just delete the whole line.
  410. //
  411. } else {
  412. Pattern->Data[0] = '\0';
  413. Pattern->Size = 1;
  414. }
  415. //
  416. // If there's nothing left, go to the end of this cycle.
  417. //
  418. if (Pattern->Size == 1) {
  419. Context->NextCommand = NULL;
  420. Context->SkipPrint = TRUE;
  421. //
  422. // Move to the top of the cycle with the input that's there.
  423. //
  424. } else {
  425. Context->NextCommand = LIST_VALUE(
  426. Context->HeadCommand.Function.U.ChildList.Next,
  427. SED_COMMAND,
  428. ListEntry);
  429. }
  430. return 0;
  431. }
  432. INT
  433. SedExecuteHoldSpaceToPattern (
  434. PSED_CONTEXT Context,
  435. PSED_COMMAND Command
  436. )
  437. /*++
  438. Routine Description:
  439. This routine executes a "replace pattern space with hold space" or
  440. "append newline plus hold space to pattern space" command.
  441. Arguments:
  442. Context - Supplies a pointer to the application context.
  443. Command - Supplies a pointer to the command to execute.
  444. Return Value:
  445. 0 on success.
  446. Non-zero error code on failure.
  447. --*/
  448. {
  449. PSED_STRING Hold;
  450. PSED_STRING Pattern;
  451. BOOL Result;
  452. assert((Command->Function.Type == SedFunctionReplacePatternWithHold) ||
  453. (Command->Function.Type == SedFunctionAppendHoldToPattern));
  454. Hold = Context->HoldSpace;
  455. Pattern = Context->PatternSpace;
  456. assert(Hold->Size != 0);
  457. assert(Pattern->Size != 0);
  458. //
  459. // If appending, add a newline.
  460. //
  461. if (Command->Function.Type == SedFunctionAppendHoldToPattern) {
  462. Result = SedAppendString(Pattern, "\n", 1);
  463. if (Result == FALSE) {
  464. return ENOMEM;
  465. }
  466. //
  467. // If replacing, delete the pattern space.
  468. //
  469. } else {
  470. Pattern->Data[0] = '\0';
  471. Pattern->Size = 1;
  472. }
  473. //
  474. // Now append the hold space.
  475. //
  476. Result = SedAppendString(Pattern, Hold->Data, Hold->Size);
  477. if (Result == FALSE) {
  478. return ENOMEM;
  479. }
  480. return 0;
  481. }
  482. INT
  483. SedExecutePatternSpaceToHold (
  484. PSED_CONTEXT Context,
  485. PSED_COMMAND Command
  486. )
  487. /*++
  488. Routine Description:
  489. This routine executes a "replace hold space with pattern space" or
  490. "append newline plus pattern space to hold space" command.
  491. Arguments:
  492. Context - Supplies a pointer to the application context.
  493. Command - Supplies a pointer to the command to execute.
  494. Return Value:
  495. 0 on success.
  496. Non-zero error code on failure.
  497. --*/
  498. {
  499. PSED_STRING Hold;
  500. PSED_STRING Pattern;
  501. BOOL Result;
  502. assert((Command->Function.Type == SedFunctionReplaceHoldWithPattern) ||
  503. (Command->Function.Type == SedFunctionAppendPatternToHold));
  504. Hold = Context->HoldSpace;
  505. Pattern = Context->PatternSpace;
  506. assert(Hold->Size != 0);
  507. assert(Pattern->Size != 0);
  508. //
  509. // If appending, add a newline.
  510. //
  511. if (Command->Function.Type == SedFunctionAppendPatternToHold) {
  512. Result = SedAppendString(Hold, "\n", 1);
  513. if (Result == FALSE) {
  514. return ENOMEM;
  515. }
  516. //
  517. // If replacing, delete the pattern space.
  518. //
  519. } else {
  520. Hold->Data[0] = '\0';
  521. Hold->Size = 1;
  522. }
  523. //
  524. // Now append the pattern space.
  525. //
  526. Result = SedAppendString(Hold, Pattern->Data, Pattern->Size);
  527. if (Result == FALSE) {
  528. return ENOMEM;
  529. }
  530. return 0;
  531. }
  532. INT
  533. SedExecutePrint (
  534. PSED_CONTEXT Context,
  535. PSED_COMMAND Command
  536. )
  537. /*++
  538. Routine Description:
  539. This routine executes a print (i) command.
  540. Arguments:
  541. Context - Supplies a pointer to the application context.
  542. Command - Supplies a pointer to the command to execute.
  543. Return Value:
  544. 0 on success.
  545. Non-zero error code on failure.
  546. --*/
  547. {
  548. assert(Command->Function.Type == SedFunctionPrintText);
  549. if (Command->Function.U.StringArgument != NULL) {
  550. SedPrint(Context, Command->Function.U.StringArgument->Data, '\n');
  551. }
  552. return 0;
  553. }
  554. INT
  555. SedExecutePrintEscapedText (
  556. PSED_CONTEXT Context,
  557. PSED_COMMAND Command
  558. )
  559. /*++
  560. Routine Description:
  561. This routine executes a print (i) command.
  562. Arguments:
  563. Context - Supplies a pointer to the application context.
  564. Command - Supplies a pointer to the command to execute.
  565. Return Value:
  566. 0 on success.
  567. Non-zero error code on failure.
  568. --*/
  569. {
  570. CHAR Character;
  571. ULONG Column;
  572. UINTN Index;
  573. PSED_STRING Pattern;
  574. assert(Command->Function.Type == SedFunctionWritePatternEscaped);
  575. Pattern = Context->PatternSpace;
  576. assert(Pattern->Size != 0);
  577. Column = 0;
  578. for (Index = 0; Index < Pattern->Size - 1; Index += 1) {
  579. Character = Pattern->Data[Index];
  580. //
  581. // If it's printable, just print it out straight up.
  582. //
  583. if (isprint(Character)) {
  584. if (Column >= SED_PRINT_COLUMNS) {
  585. printf("\\\n");
  586. Column = 0;
  587. }
  588. printf("%c", Character);
  589. Column += 1;
  590. //
  591. // Print out an escape sequence if it's one of the common ones.
  592. //
  593. } else if ((Character == '\\') || (Character == '\a') ||
  594. (Character == '\b') || (Character == '\f') ||
  595. (Character == '\r') || (Character == '\v') ||
  596. (Character == '\n')) {
  597. if (Column + 1 >= SED_PRINT_COLUMNS) {
  598. printf("\\\n");
  599. Column = 0;
  600. }
  601. if (Character == '\\') {
  602. Character = '\\';
  603. } else if (Character == '\a') {
  604. Character = 'a';
  605. } else if (Character == '\b') {
  606. Character = 'b';
  607. } else if (Character == '\f') {
  608. Character = 'f';
  609. } else if (Character == '\r') {
  610. Character = 'r';
  611. } else if (Character == '\t') {
  612. Character = 't';
  613. } else if (Character == '\v') {
  614. Character = 'v';
  615. } else if (Character == '\n') {
  616. Character = 'n';
  617. }
  618. printf("\\%c", Character);
  619. Column += 2;
  620. //
  621. // It's a weird character, print out its octal representation.
  622. //
  623. } else {
  624. if (Column + 3 > SED_PRINT_COLUMNS) {
  625. printf("\\\n");
  626. Column = 0;
  627. }
  628. printf("\\%03o", Character);
  629. }
  630. }
  631. printf("$\n");
  632. Context->StandardOut.LineTerminated = TRUE;
  633. return 0;
  634. }
  635. INT
  636. SedExecuteMoveToNextLine (
  637. PSED_CONTEXT Context,
  638. PSED_COMMAND Command
  639. )
  640. /*++
  641. Routine Description:
  642. This routine executes a "move to next line" command, which breaks out of
  643. the current cycle and moves to the next.
  644. Arguments:
  645. Context - Supplies a pointer to the application context.
  646. Command - Supplies a pointer to the command to execute.
  647. Return Value:
  648. 0 on success.
  649. Non-zero error code on failure.
  650. --*/
  651. {
  652. CHAR Character;
  653. PSED_STRING Pattern;
  654. INT Status;
  655. assert((Command->Function.Type == SedFunctionMoveToNextLine) ||
  656. (Command->Function.Type == SedFunctionAppendNextLine));
  657. Pattern = Context->PatternSpace;
  658. if (Command->Function.Type == SedFunctionMoveToNextLine) {
  659. //
  660. // If directed, print the pattern space.
  661. //
  662. if (Context->PrintLines != FALSE) {
  663. SedPrint(Context, Pattern->Data, Context->LineTerminator);
  664. }
  665. //
  666. // Clear the current pattern space.
  667. //
  668. Pattern->Data[0] = '\0';
  669. Pattern->Size = 1;
  670. } else {
  671. Character = '\n';
  672. Status = SedAppendString(Pattern, &Character, 1);
  673. if (Status == FALSE) {
  674. return ENOMEM;
  675. }
  676. }
  677. //
  678. // Append the next line.
  679. //
  680. Status = SedReadLine(Context);
  681. if (Status != 0) {
  682. return Status;
  683. }
  684. //
  685. // If there was no more input, then move to the end of the script.
  686. //
  687. if (Context->Done != FALSE) {
  688. Context->NextCommand = NULL;
  689. Context->SkipPrint = TRUE;
  690. }
  691. return 0;
  692. }
  693. INT
  694. SedExecuteWritePatternSpace (
  695. PSED_CONTEXT Context,
  696. PSED_COMMAND Command
  697. )
  698. /*++
  699. Routine Description:
  700. This routine executes a command to write all (p) or part (P) of the pattern
  701. space to standard out.
  702. Arguments:
  703. Context - Supplies a pointer to the application context.
  704. Command - Supplies a pointer to the command to execute.
  705. Return Value:
  706. 0 on success.
  707. Non-zero error code on failure.
  708. --*/
  709. {
  710. UINTN Index;
  711. PSED_STRING Pattern;
  712. assert((Command->Function.Type == SedFunctionWritePattern) ||
  713. (Command->Function.Type == SedFunctionWritePatternToNewline));
  714. Pattern = Context->PatternSpace;
  715. assert(Pattern->Size != 0);
  716. if (Command->Function.Type == SedFunctionWritePattern) {
  717. printf("%s\n", Pattern->Data);
  718. } else {
  719. Index = 0;
  720. while ((Index < Pattern->Size) && (Pattern->Data[Index] != '\n')) {
  721. Index += 1;
  722. }
  723. if (Index == Pattern->Size) {
  724. SedWrite(&(Context->StandardOut),
  725. Pattern->Data,
  726. Pattern->Size - 1,
  727. '\n');
  728. } else {
  729. SedWrite(&(Context->StandardOut),
  730. Pattern->Data,
  731. Index,
  732. '\n');
  733. }
  734. }
  735. return 0;
  736. }
  737. INT
  738. SedExecuteQuit (
  739. PSED_CONTEXT Context,
  740. PSED_COMMAND Command
  741. )
  742. /*++
  743. Routine Description:
  744. This routine executes a quit command.
  745. Arguments:
  746. Context - Supplies a pointer to the application context.
  747. Command - Supplies a pointer to the command to execute.
  748. Return Value:
  749. 0 on success.
  750. Non-zero error code on failure.
  751. --*/
  752. {
  753. Context->Quit = TRUE;
  754. return 0;
  755. }
  756. INT
  757. SedExecuteSubstitute (
  758. PSED_CONTEXT Context,
  759. PSED_COMMAND Command
  760. )
  761. /*++
  762. Routine Description:
  763. This routine executes a substitute command.
  764. Arguments:
  765. Context - Supplies a pointer to the application context.
  766. Command - Supplies a pointer to the command to execute.
  767. Return Value:
  768. 0 on success.
  769. Non-zero error code on failure.
  770. --*/
  771. {
  772. UINTN InputEnd;
  773. UINTN InputStart;
  774. ULONG Occurrence;
  775. PSED_STRING Pattern;
  776. UINTN PatternOffset;
  777. PSTR Replace;
  778. ULONG ReplaceEnd;
  779. PSED_STRING Replacement;
  780. CHAR ReplacementCharacter;
  781. UINTN ReplacementIndex;
  782. UINTN ReplacementSize;
  783. ULONG ReplaceStart;
  784. int Result;
  785. LONG Subexpression;
  786. PSED_SUBSTITUTE Substitute;
  787. BOOL SubstitutionMade;
  788. UINTN TotalEndOffset;
  789. UINTN TotalStartOffset;
  790. BOOL WasBackslash;
  791. Pattern = Context->PatternSpace;
  792. ReplaceEnd = 0;
  793. Replacement = NULL;
  794. ReplaceStart = 0;
  795. Substitute = &(Command->Function.U.Substitute);
  796. SubstitutionMade = FALSE;
  797. assert(Command->Function.Type == SedFunctionSubstitute);
  798. //
  799. // Loop making subsitutions.
  800. //
  801. Occurrence = 0;
  802. PatternOffset = 0;
  803. while (PatternOffset < Pattern->Size) {
  804. Result = regexec(&(Substitute->Expression),
  805. Pattern->Data + PatternOffset,
  806. Substitute->MatchCount,
  807. Substitute->Matches,
  808. 0);
  809. //
  810. // If there was no match, stop now.
  811. //
  812. if (Result != 0) {
  813. break;
  814. }
  815. //
  816. // If there's a specific occurrence number and this isn't it, continue
  817. // on.
  818. //
  819. Occurrence += 1;
  820. if ((Substitute->OccurrenceNumber != 0) &&
  821. (Substitute->OccurrenceNumber != Occurrence)) {
  822. PatternOffset += Substitute->Matches[0].rm_eo;
  823. continue;
  824. }
  825. //
  826. // Generate the replacement. Start off by creating a copy of the
  827. // replacement template.
  828. //
  829. Replacement = SedCreateString(Substitute->Replacement->Data,
  830. Substitute->Replacement->Size,
  831. TRUE);
  832. //
  833. // Loop through the replacement converting as necessary.
  834. //
  835. ReplacementIndex = 0;
  836. WasBackslash = FALSE;
  837. Replace = Replacement->Data;
  838. ReplacementCharacter = 0;
  839. InputStart = 0;
  840. InputEnd = 0;
  841. while (ReplacementIndex < Replacement->Size) {
  842. if (WasBackslash != FALSE) {
  843. if (*Replace == '\\') {
  844. ReplacementCharacter = '\\';
  845. } else if (*Replace == 'a') {
  846. ReplacementCharacter = '\a';
  847. } else if (*Replace == 'b') {
  848. ReplacementCharacter = '\b';
  849. } else if (*Replace == 'f') {
  850. ReplacementCharacter = '\f';
  851. } else if (*Replace == 'r') {
  852. ReplacementCharacter = '\r';
  853. } else if (*Replace == 't') {
  854. ReplacementCharacter = '\t';
  855. } else if (*Replace == 'v') {
  856. ReplacementCharacter = '\v';
  857. } else if (*Replace == 'n') {
  858. ReplacementCharacter = '\n';
  859. } else if (isdigit(*Replace)) {
  860. Subexpression = *Replace - '0';
  861. if ((Substitute->Matches[Subexpression].rm_so != -1) &&
  862. (Substitute->Matches[Subexpression].rm_eo != -1)) {
  863. InputStart = Substitute->Matches[Subexpression].rm_so;
  864. InputEnd = Substitute->Matches[Subexpression].rm_eo;
  865. } else {
  866. InputStart = 0;
  867. InputEnd = 0;
  868. }
  869. ReplaceStart = ReplacementIndex - 1;
  870. ReplaceEnd = ReplacementIndex + 1;
  871. } else {
  872. ReplacementCharacter = *Replace;
  873. }
  874. if (ReplacementCharacter != 0) {
  875. Result = SwStringReplaceRegion(&(Replacement->Data),
  876. &(Replacement->Size),
  877. &(Replacement->Capacity),
  878. ReplacementIndex - 1,
  879. ReplacementIndex + 1,
  880. &ReplacementCharacter,
  881. 2);
  882. if (Result == FALSE) {
  883. Result = ENOMEM;
  884. goto ExecuteSubstituteEnd;
  885. }
  886. //
  887. // Back up since new characters just shifted down.
  888. //
  889. ReplacementIndex -= 1;
  890. Replace = Replacement->Data + ReplacementIndex;
  891. ReplacementCharacter = 0;
  892. }
  893. //
  894. // If it wasn't a backslash, the only special character is an
  895. // ampersand, which matches the whole thing.
  896. //
  897. } else {
  898. if (*Replace == '&') {
  899. assert((Substitute->Matches[0].rm_so != -1) &&
  900. (Substitute->Matches[0].rm_eo != -1));
  901. InputStart = Substitute->Matches[0].rm_so;
  902. InputEnd = Substitute->Matches[0].rm_eo;
  903. ReplaceStart = ReplacementIndex;
  904. ReplaceEnd = ReplacementIndex + 1;
  905. }
  906. }
  907. //
  908. // If someone requested a replacement with part of the input
  909. // string, go for it.
  910. //
  911. if (ReplaceStart != ReplaceEnd) {
  912. assert((InputStart + PatternOffset < Pattern->Size) &&
  913. (InputEnd + PatternOffset <= Pattern->Size));
  914. //
  915. // The pattern offset is added because the regular expression
  916. // was searched with the input plus the pattern offset.
  917. //
  918. TotalStartOffset = InputStart + PatternOffset;
  919. Result = SwStringReplaceRegion(&(Replacement->Data),
  920. &(Replacement->Size),
  921. &(Replacement->Capacity),
  922. ReplaceStart,
  923. ReplaceEnd,
  924. Pattern->Data + TotalStartOffset,
  925. InputEnd - InputStart + 1);
  926. if (Result == FALSE) {
  927. Result = ENOMEM;
  928. goto ExecuteSubstituteEnd;
  929. }
  930. //
  931. // Reset the current index as the string was just moved around
  932. // and may have been replaced entirely.
  933. //
  934. ReplacementIndex = ReplaceStart + (InputEnd - InputStart) - 1;
  935. Replace = Replacement->Data + ReplacementIndex;
  936. ReplaceStart = 0;
  937. ReplaceEnd = 0;
  938. }
  939. if (*Replace == '\\') {
  940. WasBackslash = !WasBackslash;
  941. } else {
  942. WasBackslash = FALSE;
  943. }
  944. Replace += 1;
  945. ReplacementIndex += 1;
  946. }
  947. //
  948. // Now that the replacement string has finally been created, replace
  949. // the portion of the pattern that matched with the replacement string.
  950. // Again, the matches are all relative to the pattern offset.
  951. //
  952. ReplacementSize = Replacement->Size;
  953. assert((ReplacementSize != 0) &&
  954. (Substitute->Matches[0].rm_so != -1) &&
  955. (Substitute->Matches[0].rm_eo != -1));
  956. TotalStartOffset = Substitute->Matches[0].rm_so + PatternOffset;
  957. TotalEndOffset = Substitute->Matches[0].rm_eo + PatternOffset;
  958. Result = SwStringReplaceRegion(&(Pattern->Data),
  959. &(Pattern->Size),
  960. &(Pattern->Capacity),
  961. TotalStartOffset,
  962. TotalEndOffset,
  963. Replacement->Data,
  964. ReplacementSize);
  965. if (Result == FALSE) {
  966. Result = ENOMEM;
  967. goto ExecuteSubstituteEnd;
  968. }
  969. //
  970. // Move to the end of the replacement for the next substitution.
  971. //
  972. PatternOffset = TotalStartOffset + ReplacementSize - 1;
  973. SubstitutionMade = TRUE;
  974. SedDestroyString(Replacement);
  975. Replacement = NULL;
  976. //
  977. // If the global flag is off, stop.
  978. //
  979. if ((Substitute->Flags & SED_SUBSTITUTE_FLAG_GLOBAL) == 0) {
  980. break;
  981. }
  982. //
  983. // Handle an empty match.
  984. //
  985. if (Substitute->Matches[0].rm_so == Substitute->Matches[0].rm_eo) {
  986. //
  987. // If the match was empty and the end of the string, then stop, as
  988. // the end of the string is just going to match empty again.
  989. //
  990. if (PatternOffset == Pattern->Size - 1) {
  991. break;
  992. //
  993. // If the replacement was empty, then move the pattern forward
  994. // to prevent replacing empty with empty forever.
  995. //
  996. } else if (ReplacementSize == 1) {
  997. PatternOffset += 1;
  998. }
  999. }
  1000. }
  1001. //
  1002. // If a substitution was made and the caller wants it printed or written
  1003. // to a file, do that now.
  1004. //
  1005. if ((SubstitutionMade != FALSE) &&
  1006. ((Substitute->Flags & SED_SUBSTITUTE_FLAG_PRINT) != 0)) {
  1007. SedPrint(Context, Pattern->Data, Context->LineTerminator);
  1008. }
  1009. if ((SubstitutionMade != FALSE) &&
  1010. ((Substitute->Flags & SED_SUBSTITUTE_FLAG_WRITE) != 0)) {
  1011. Result = SedWrite(Substitute->WriteFile,
  1012. Pattern->Data,
  1013. Pattern->Size - 1,
  1014. Context->LineTerminator);
  1015. if (Result != 0) {
  1016. goto ExecuteSubstituteEnd;
  1017. }
  1018. }
  1019. //
  1020. // Mark if a substitution was made for any future test commands.
  1021. //
  1022. Context->TestResult = SubstitutionMade;
  1023. Result = 0;
  1024. ExecuteSubstituteEnd:
  1025. if (Replacement != NULL) {
  1026. SedDestroyString(Replacement);
  1027. }
  1028. return Result;
  1029. }
  1030. INT
  1031. SedExecuteWriteFile (
  1032. PSED_CONTEXT Context,
  1033. PSED_COMMAND Command
  1034. )
  1035. /*++
  1036. Routine Description:
  1037. This routine executes a "write to file" command, which writes the pattern
  1038. space plus a newline.
  1039. Arguments:
  1040. Context - Supplies a pointer to the application context.
  1041. Command - Supplies a pointer to the command to execute.
  1042. Return Value:
  1043. 0 on success.
  1044. Non-zero error code on failure.
  1045. --*/
  1046. {
  1047. INT Result;
  1048. PSED_WRITE_FILE WriteFile;
  1049. assert(Command->Function.Type == SedFunctionWriteFile);
  1050. assert(Context->PatternSpace->Size != 0);
  1051. WriteFile = Command->Function.U.WriteFile;
  1052. Result = SedWrite(WriteFile,
  1053. Context->PatternSpace->Data,
  1054. Context->PatternSpace->Size - 1,
  1055. Context->LineTerminator);
  1056. return Result;
  1057. }
  1058. INT
  1059. SedExecuteExchangePatternAndHold (
  1060. PSED_CONTEXT Context,
  1061. PSED_COMMAND Command
  1062. )
  1063. /*++
  1064. Routine Description:
  1065. This routine executes the "exchange pattern space and hold space" command,
  1066. which does exactly what it sounds like.
  1067. Arguments:
  1068. Context - Supplies a pointer to the application context.
  1069. Command - Supplies a pointer to the command to execute.
  1070. Return Value:
  1071. 0 on success.
  1072. Non-zero error code on failure.
  1073. --*/
  1074. {
  1075. PSED_STRING Swap;
  1076. assert(Command->Function.Type == SedFunctionExchangePatternAndHold);
  1077. Swap = Context->PatternSpace;
  1078. Context->PatternSpace = Context->HoldSpace;
  1079. Context->HoldSpace = Swap;
  1080. return 0;
  1081. }
  1082. INT
  1083. SedExecuteSubstituteCharacters (
  1084. PSED_CONTEXT Context,
  1085. PSED_COMMAND Command
  1086. )
  1087. /*++
  1088. Routine Description:
  1089. This routine executes the "substitute characters" command, which replaces
  1090. every occurrence of a character found in the first set with the replacement
  1091. character specified by the second set.
  1092. Arguments:
  1093. Context - Supplies a pointer to the application context.
  1094. Command - Supplies a pointer to the command to execute.
  1095. Return Value:
  1096. 0 on success.
  1097. Non-zero error code on failure.
  1098. --*/
  1099. {
  1100. UINTN CharacterCount;
  1101. UINTN CharacterIndex;
  1102. PSTR Characters;
  1103. PSTR Pattern;
  1104. PSTR Replacement;
  1105. assert(Command->Function.Type == SedFunctionSubstituteCharacters);
  1106. Pattern = Context->PatternSpace->Data;
  1107. CharacterCount =
  1108. Command->Function.U.CharacterSubstitute.Characters->Size - 1;
  1109. assert(CharacterCount ==
  1110. Command->Function.U.CharacterSubstitute.Replacement->Size - 1);
  1111. Characters = Command->Function.U.CharacterSubstitute.Characters->Data;
  1112. Replacement = Command->Function.U.CharacterSubstitute.Replacement->Data;
  1113. while (*Pattern != '\0') {
  1114. for (CharacterIndex = 0;
  1115. CharacterIndex < CharacterCount;
  1116. CharacterIndex += 1) {
  1117. if (*Pattern == Characters[CharacterIndex]) {
  1118. *Pattern = Replacement[CharacterIndex];
  1119. assert(*Pattern != '\0');
  1120. break;
  1121. }
  1122. }
  1123. Pattern += 1;
  1124. }
  1125. return 0;
  1126. }
  1127. INT
  1128. SedExecuteNop (
  1129. PSED_CONTEXT Context,
  1130. PSED_COMMAND Command
  1131. )
  1132. /*++
  1133. Routine Description:
  1134. This routine executes a "no-op" command, which does nothing.
  1135. Arguments:
  1136. Context - Supplies a pointer to the application context.
  1137. Command - Supplies a pointer to the command to execute.
  1138. Return Value:
  1139. 0 on success.
  1140. Non-zero error code on failure.
  1141. --*/
  1142. {
  1143. assert((Command->Function.Type == SedFunctionNop) ||
  1144. (Command->Function.Type == SedFunctionLabel));
  1145. return 0;
  1146. }
  1147. INT
  1148. SedExecuteWriteLineNumber (
  1149. PSED_CONTEXT Context,
  1150. PSED_COMMAND Command
  1151. )
  1152. /*++
  1153. Routine Description:
  1154. This routine executes a "write line number", which prints out the decimal
  1155. line number of the input plus a newline.
  1156. Arguments:
  1157. Context - Supplies a pointer to the application context.
  1158. Command - Supplies a pointer to the command to execute.
  1159. Return Value:
  1160. 0 on success.
  1161. Non-zero error code on failure.
  1162. --*/
  1163. {
  1164. CHAR LineNumberBuffer[100];
  1165. assert(Command->Function.Type == SedFunctionWriteLineNumber);
  1166. snprintf(LineNumberBuffer,
  1167. sizeof(LineNumberBuffer),
  1168. "%lld",
  1169. Context->LineNumber);
  1170. SedPrint(Context, LineNumberBuffer, '\n');
  1171. return 0;
  1172. }
  1173. //
  1174. // --------------------------------------------------------- Internal Functions
  1175. //