ed.c 19 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * Copyright (c) 2002 by David I. Bell
  4. * Permission is granted to use, distribute, or modify this source,
  5. * provided that this copyright notice remains intact.
  6. *
  7. * The "ed" built-in command (much simplified)
  8. */
  9. //config:config ED
  10. //config: bool "ed"
  11. //config: default y
  12. //config: help
  13. //config: The original 1970's Unix text editor, from the days of teletypes.
  14. //config: Small, simple, evil. Part of SUSv3. If you're not already using
  15. //config: this, you don't need it.
  16. //kbuild:lib-$(CONFIG_ED) += ed.o
  17. //applet:IF_ED(APPLET(ed, BB_DIR_BIN, BB_SUID_DROP))
  18. //usage:#define ed_trivial_usage ""
  19. //usage:#define ed_full_usage ""
  20. #include "libbb.h"
  21. #include "common_bufsiz.h"
  22. typedef struct LINE {
  23. struct LINE *next;
  24. struct LINE *prev;
  25. int len;
  26. char data[1];
  27. } LINE;
  28. #define searchString bb_common_bufsiz1
  29. enum {
  30. USERSIZE = COMMON_BUFSIZE > 1024 ? 1024
  31. : COMMON_BUFSIZE - 1, /* max line length typed in by user */
  32. INITBUF_SIZE = 1024, /* initial buffer size */
  33. };
  34. struct globals {
  35. int curNum;
  36. int lastNum;
  37. int bufUsed;
  38. int bufSize;
  39. LINE *curLine;
  40. char *bufBase;
  41. char *bufPtr;
  42. char *fileName;
  43. LINE lines;
  44. smallint dirty;
  45. int marks[26];
  46. };
  47. #define G (*ptr_to_globals)
  48. #define curLine (G.curLine )
  49. #define bufBase (G.bufBase )
  50. #define bufPtr (G.bufPtr )
  51. #define fileName (G.fileName )
  52. #define curNum (G.curNum )
  53. #define lastNum (G.lastNum )
  54. #define bufUsed (G.bufUsed )
  55. #define bufSize (G.bufSize )
  56. #define dirty (G.dirty )
  57. #define lines (G.lines )
  58. #define marks (G.marks )
  59. #define INIT_G() do { \
  60. setup_common_bufsiz(); \
  61. SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
  62. } while (0)
  63. static void doCommands(void);
  64. static void subCommand(const char *cmd, int num1, int num2);
  65. static int getNum(const char **retcp, smallint *retHaveNum, int *retNum);
  66. static int setCurNum(int num);
  67. static void addLines(int num);
  68. static int insertLine(int num, const char *data, int len);
  69. static void deleteLines(int num1, int num2);
  70. static int printLines(int num1, int num2, int expandFlag);
  71. static int writeLines(const char *file, int num1, int num2);
  72. static int readLines(const char *file, int num);
  73. static int searchLines(const char *str, int num1, int num2);
  74. static LINE *findLine(int num);
  75. static int findString(const LINE *lp, const char * str, int len, int offset);
  76. static int bad_nums(int num1, int num2, const char *for_what)
  77. {
  78. if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
  79. bb_error_msg("bad line range for %s", for_what);
  80. return 1;
  81. }
  82. return 0;
  83. }
  84. static char *skip_blank(const char *cp)
  85. {
  86. while (isblank(*cp))
  87. cp++;
  88. return (char *)cp;
  89. }
  90. int ed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  91. int ed_main(int argc UNUSED_PARAM, char **argv)
  92. {
  93. INIT_G();
  94. bufSize = INITBUF_SIZE;
  95. bufBase = xmalloc(bufSize);
  96. bufPtr = bufBase;
  97. lines.next = &lines;
  98. lines.prev = &lines;
  99. if (argv[1]) {
  100. fileName = xstrdup(argv[1]);
  101. if (!readLines(fileName, 1)) {
  102. return EXIT_SUCCESS;
  103. }
  104. if (lastNum)
  105. setCurNum(1);
  106. dirty = FALSE;
  107. }
  108. doCommands();
  109. return EXIT_SUCCESS;
  110. }
  111. /*
  112. * Read commands until we are told to stop.
  113. */
  114. static void doCommands(void)
  115. {
  116. const char *cp;
  117. char *endbuf, buf[USERSIZE];
  118. int len, num1, num2;
  119. smallint have1, have2;
  120. while (TRUE) {
  121. /* Returns:
  122. * -1 on read errors or EOF, or on bare Ctrl-D.
  123. * 0 on ctrl-C,
  124. * >0 length of input string, including terminating '\n'
  125. */
  126. len = read_line_input(NULL, ": ", buf, sizeof(buf), /*timeout*/ -1);
  127. if (len <= 0)
  128. return;
  129. endbuf = &buf[len - 1];
  130. while ((endbuf > buf) && isblank(endbuf[-1]))
  131. endbuf--;
  132. *endbuf = '\0';
  133. cp = skip_blank(buf);
  134. have1 = FALSE;
  135. have2 = FALSE;
  136. if ((curNum == 0) && (lastNum > 0)) {
  137. curNum = 1;
  138. curLine = lines.next;
  139. }
  140. if (!getNum(&cp, &have1, &num1))
  141. continue;
  142. cp = skip_blank(cp);
  143. if (*cp == ',') {
  144. cp++;
  145. if (!getNum(&cp, &have2, &num2))
  146. continue;
  147. if (!have1)
  148. num1 = 1;
  149. if (!have2)
  150. num2 = lastNum;
  151. have1 = TRUE;
  152. have2 = TRUE;
  153. }
  154. if (!have1)
  155. num1 = curNum;
  156. if (!have2)
  157. num2 = num1;
  158. switch (*cp++) {
  159. case 'a':
  160. addLines(num1 + 1);
  161. break;
  162. case 'c':
  163. deleteLines(num1, num2);
  164. addLines(num1);
  165. break;
  166. case 'd':
  167. deleteLines(num1, num2);
  168. break;
  169. case 'f':
  170. if (*cp && !isblank(*cp)) {
  171. bb_error_msg("bad file command");
  172. break;
  173. }
  174. cp = skip_blank(cp);
  175. if (*cp == '\0') {
  176. if (fileName)
  177. printf("\"%s\"\n", fileName);
  178. else
  179. puts("No file name");
  180. break;
  181. }
  182. free(fileName);
  183. fileName = xstrdup(cp);
  184. break;
  185. case 'i':
  186. addLines(num1);
  187. break;
  188. case 'k':
  189. cp = skip_blank(cp);
  190. if ((*cp < 'a') || (*cp > 'z') || cp[1]) {
  191. bb_error_msg("bad mark name");
  192. break;
  193. }
  194. marks[*cp - 'a'] = num2;
  195. break;
  196. case 'l':
  197. printLines(num1, num2, TRUE);
  198. break;
  199. case 'p':
  200. printLines(num1, num2, FALSE);
  201. break;
  202. case 'q':
  203. cp = skip_blank(cp);
  204. if (have1 || *cp) {
  205. bb_error_msg("bad quit command");
  206. break;
  207. }
  208. if (!dirty)
  209. return;
  210. len = read_line_input(NULL, "Really quit? ", buf, 16, /*timeout*/ -1);
  211. /* read error/EOF - no way to continue */
  212. if (len < 0)
  213. return;
  214. cp = skip_blank(buf);
  215. if ((*cp | 0x20) == 'y') /* Y or y */
  216. return;
  217. break;
  218. case 'r':
  219. if (*cp && !isblank(*cp)) {
  220. bb_error_msg("bad read command");
  221. break;
  222. }
  223. cp = skip_blank(cp);
  224. if (*cp == '\0') {
  225. bb_error_msg("no file name");
  226. break;
  227. }
  228. if (!have1)
  229. num1 = lastNum;
  230. if (readLines(cp, num1 + 1))
  231. break;
  232. if (fileName == NULL)
  233. fileName = xstrdup(cp);
  234. break;
  235. case 's':
  236. subCommand(cp, num1, num2);
  237. break;
  238. case 'w':
  239. if (*cp && !isblank(*cp)) {
  240. bb_error_msg("bad write command");
  241. break;
  242. }
  243. cp = skip_blank(cp);
  244. if (!have1) {
  245. num1 = 1;
  246. num2 = lastNum;
  247. }
  248. if (*cp == '\0')
  249. cp = fileName;
  250. if (cp == NULL) {
  251. bb_error_msg("no file name specified");
  252. break;
  253. }
  254. writeLines(cp, num1, num2);
  255. break;
  256. case 'z':
  257. switch (*cp) {
  258. case '-':
  259. printLines(curNum - 21, curNum, FALSE);
  260. break;
  261. case '.':
  262. printLines(curNum - 11, curNum + 10, FALSE);
  263. break;
  264. default:
  265. printLines(curNum, curNum + 21, FALSE);
  266. break;
  267. }
  268. break;
  269. case '.':
  270. if (have1) {
  271. bb_error_msg("no arguments allowed");
  272. break;
  273. }
  274. printLines(curNum, curNum, FALSE);
  275. break;
  276. case '-':
  277. if (setCurNum(curNum - 1))
  278. printLines(curNum, curNum, FALSE);
  279. break;
  280. case '=':
  281. printf("%d\n", num1);
  282. break;
  283. case '\0':
  284. if (have1) {
  285. printLines(num2, num2, FALSE);
  286. break;
  287. }
  288. if (setCurNum(curNum + 1))
  289. printLines(curNum, curNum, FALSE);
  290. break;
  291. default:
  292. bb_error_msg("unimplemented command");
  293. break;
  294. }
  295. }
  296. }
  297. /*
  298. * Do the substitute command.
  299. * The current line is set to the last substitution done.
  300. */
  301. static void subCommand(const char *cmd, int num1, int num2)
  302. {
  303. char *cp, *oldStr, *newStr, buf[USERSIZE];
  304. int delim, oldLen, newLen, deltaLen, offset;
  305. LINE *lp, *nlp;
  306. int globalFlag, printFlag, didSub, needPrint;
  307. if (bad_nums(num1, num2, "substitute"))
  308. return;
  309. globalFlag = FALSE;
  310. printFlag = FALSE;
  311. didSub = FALSE;
  312. needPrint = FALSE;
  313. /*
  314. * Copy the command so we can modify it.
  315. */
  316. strcpy(buf, cmd);
  317. cp = buf;
  318. if (isblank(*cp) || (*cp == '\0')) {
  319. bb_error_msg("bad delimiter for substitute");
  320. return;
  321. }
  322. delim = *cp++;
  323. oldStr = cp;
  324. cp = strchr(cp, delim);
  325. if (cp == NULL) {
  326. bb_error_msg("missing 2nd delimiter for substitute");
  327. return;
  328. }
  329. *cp++ = '\0';
  330. newStr = cp;
  331. cp = strchr(cp, delim);
  332. if (cp)
  333. *cp++ = '\0';
  334. else
  335. cp = (char*)"";
  336. while (*cp) switch (*cp++) {
  337. case 'g':
  338. globalFlag = TRUE;
  339. break;
  340. case 'p':
  341. printFlag = TRUE;
  342. break;
  343. default:
  344. bb_error_msg("unknown option for substitute");
  345. return;
  346. }
  347. if (*oldStr == '\0') {
  348. if (searchString[0] == '\0') {
  349. bb_error_msg("no previous search string");
  350. return;
  351. }
  352. oldStr = searchString;
  353. }
  354. if (oldStr != searchString)
  355. strcpy(searchString, oldStr);
  356. lp = findLine(num1);
  357. if (lp == NULL)
  358. return;
  359. oldLen = strlen(oldStr);
  360. newLen = strlen(newStr);
  361. deltaLen = newLen - oldLen;
  362. offset = 0;
  363. nlp = NULL;
  364. while (num1 <= num2) {
  365. offset = findString(lp, oldStr, oldLen, offset);
  366. if (offset < 0) {
  367. if (needPrint) {
  368. printLines(num1, num1, FALSE);
  369. needPrint = FALSE;
  370. }
  371. offset = 0;
  372. lp = lp->next;
  373. num1++;
  374. continue;
  375. }
  376. needPrint = printFlag;
  377. didSub = TRUE;
  378. dirty = TRUE;
  379. /*
  380. * If the replacement string is the same size or shorter
  381. * than the old string, then the substitution is easy.
  382. */
  383. if (deltaLen <= 0) {
  384. memcpy(&lp->data[offset], newStr, newLen);
  385. if (deltaLen) {
  386. memcpy(&lp->data[offset + newLen],
  387. &lp->data[offset + oldLen],
  388. lp->len - offset - oldLen);
  389. lp->len += deltaLen;
  390. }
  391. offset += newLen;
  392. if (globalFlag)
  393. continue;
  394. if (needPrint) {
  395. printLines(num1, num1, FALSE);
  396. needPrint = FALSE;
  397. }
  398. lp = lp->next;
  399. num1++;
  400. continue;
  401. }
  402. /*
  403. * The new string is larger, so allocate a new line
  404. * structure and use that. Link it in place of
  405. * the old line structure.
  406. */
  407. nlp = xmalloc(sizeof(LINE) + lp->len + deltaLen);
  408. nlp->len = lp->len + deltaLen;
  409. memcpy(nlp->data, lp->data, offset);
  410. memcpy(&nlp->data[offset], newStr, newLen);
  411. memcpy(&nlp->data[offset + newLen],
  412. &lp->data[offset + oldLen],
  413. lp->len - offset - oldLen);
  414. nlp->next = lp->next;
  415. nlp->prev = lp->prev;
  416. nlp->prev->next = nlp;
  417. nlp->next->prev = nlp;
  418. if (curLine == lp)
  419. curLine = nlp;
  420. free(lp);
  421. lp = nlp;
  422. offset += newLen;
  423. if (globalFlag)
  424. continue;
  425. if (needPrint) {
  426. printLines(num1, num1, FALSE);
  427. needPrint = FALSE;
  428. }
  429. lp = lp->next;
  430. num1++;
  431. }
  432. if (!didSub)
  433. bb_error_msg("no substitutions found for \"%s\"", oldStr);
  434. }
  435. /*
  436. * Search a line for the specified string starting at the specified
  437. * offset in the line. Returns the offset of the found string, or -1.
  438. */
  439. static int findString(const LINE *lp, const char *str, int len, int offset)
  440. {
  441. int left;
  442. const char *cp, *ncp;
  443. cp = &lp->data[offset];
  444. left = lp->len - offset;
  445. while (left >= len) {
  446. ncp = memchr(cp, *str, left);
  447. if (ncp == NULL)
  448. return -1;
  449. left -= (ncp - cp);
  450. if (left < len)
  451. return -1;
  452. cp = ncp;
  453. if (memcmp(cp, str, len) == 0)
  454. return (cp - lp->data);
  455. cp++;
  456. left--;
  457. }
  458. return -1;
  459. }
  460. /*
  461. * Add lines which are typed in by the user.
  462. * The lines are inserted just before the specified line number.
  463. * The lines are terminated by a line containing a single dot (ugly!),
  464. * or by an end of file.
  465. */
  466. static void addLines(int num)
  467. {
  468. int len;
  469. char buf[USERSIZE + 1];
  470. while (1) {
  471. /* Returns:
  472. * -1 on read errors or EOF, or on bare Ctrl-D.
  473. * 0 on ctrl-C,
  474. * >0 length of input string, including terminating '\n'
  475. */
  476. len = read_line_input(NULL, "", buf, sizeof(buf), /*timeout*/ -1);
  477. if (len <= 0) {
  478. /* Previously, ctrl-C was exiting to shell.
  479. * Now we exit to ed prompt. Is in important? */
  480. return;
  481. }
  482. if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0'))
  483. return;
  484. if (!insertLine(num++, buf, len))
  485. return;
  486. }
  487. }
  488. /*
  489. * Parse a line number argument if it is present. This is a sum
  490. * or difference of numbers, '.', '$', 'x, or a search string.
  491. * Returns TRUE if successful (whether or not there was a number).
  492. * Returns FALSE if there was a parsing error, with a message output.
  493. * Whether there was a number is returned indirectly, as is the number.
  494. * The character pointer which stopped the scan is also returned.
  495. */
  496. static int getNum(const char **retcp, smallint *retHaveNum, int *retNum)
  497. {
  498. const char *cp;
  499. char *endStr, str[USERSIZE];
  500. int value, num;
  501. smallint haveNum, minus;
  502. cp = *retcp;
  503. value = 0;
  504. haveNum = FALSE;
  505. minus = 0;
  506. while (TRUE) {
  507. cp = skip_blank(cp);
  508. switch (*cp) {
  509. case '.':
  510. haveNum = TRUE;
  511. num = curNum;
  512. cp++;
  513. break;
  514. case '$':
  515. haveNum = TRUE;
  516. num = lastNum;
  517. cp++;
  518. break;
  519. case '\'':
  520. cp++;
  521. if ((*cp < 'a') || (*cp > 'z')) {
  522. bb_error_msg("bad mark name");
  523. return FALSE;
  524. }
  525. haveNum = TRUE;
  526. num = marks[*cp++ - 'a'];
  527. break;
  528. case '/':
  529. strcpy(str, ++cp);
  530. endStr = strchr(str, '/');
  531. if (endStr) {
  532. *endStr++ = '\0';
  533. cp += (endStr - str);
  534. } else
  535. cp = "";
  536. num = searchLines(str, curNum, lastNum);
  537. if (num == 0)
  538. return FALSE;
  539. haveNum = TRUE;
  540. break;
  541. default:
  542. if (!isdigit(*cp)) {
  543. *retcp = cp;
  544. *retHaveNum = haveNum;
  545. *retNum = value;
  546. return TRUE;
  547. }
  548. num = 0;
  549. while (isdigit(*cp))
  550. num = num * 10 + *cp++ - '0';
  551. haveNum = TRUE;
  552. break;
  553. }
  554. value += (minus ? -num : num);
  555. cp = skip_blank(cp);
  556. switch (*cp) {
  557. case '-':
  558. minus = 1;
  559. cp++;
  560. break;
  561. case '+':
  562. minus = 0;
  563. cp++;
  564. break;
  565. default:
  566. *retcp = cp;
  567. *retHaveNum = haveNum;
  568. *retNum = value;
  569. return TRUE;
  570. }
  571. }
  572. }
  573. /*
  574. * Read lines from a file at the specified line number.
  575. * Returns TRUE if the file was successfully read.
  576. */
  577. static int readLines(const char *file, int num)
  578. {
  579. int fd, cc;
  580. int len, lineCount, charCount;
  581. char *cp;
  582. if ((num < 1) || (num > lastNum + 1)) {
  583. bb_error_msg("bad line for read");
  584. return FALSE;
  585. }
  586. fd = open(file, 0);
  587. if (fd < 0) {
  588. bb_simple_perror_msg(file);
  589. return FALSE;
  590. }
  591. bufPtr = bufBase;
  592. bufUsed = 0;
  593. lineCount = 0;
  594. charCount = 0;
  595. cc = 0;
  596. printf("\"%s\", ", file);
  597. fflush_all();
  598. do {
  599. cp = memchr(bufPtr, '\n', bufUsed);
  600. if (cp) {
  601. len = (cp - bufPtr) + 1;
  602. if (!insertLine(num, bufPtr, len)) {
  603. close(fd);
  604. return FALSE;
  605. }
  606. bufPtr += len;
  607. bufUsed -= len;
  608. charCount += len;
  609. lineCount++;
  610. num++;
  611. continue;
  612. }
  613. if (bufPtr != bufBase) {
  614. memcpy(bufBase, bufPtr, bufUsed);
  615. bufPtr = bufBase + bufUsed;
  616. }
  617. if (bufUsed >= bufSize) {
  618. len = (bufSize * 3) / 2;
  619. cp = xrealloc(bufBase, len);
  620. bufBase = cp;
  621. bufPtr = bufBase + bufUsed;
  622. bufSize = len;
  623. }
  624. cc = safe_read(fd, bufPtr, bufSize - bufUsed);
  625. bufUsed += cc;
  626. bufPtr = bufBase;
  627. } while (cc > 0);
  628. if (cc < 0) {
  629. bb_simple_perror_msg(file);
  630. close(fd);
  631. return FALSE;
  632. }
  633. if (bufUsed) {
  634. if (!insertLine(num, bufPtr, bufUsed)) {
  635. close(fd);
  636. return -1;
  637. }
  638. lineCount++;
  639. charCount += bufUsed;
  640. }
  641. close(fd);
  642. printf("%d lines%s, %d chars\n", lineCount,
  643. (bufUsed ? " (incomplete)" : ""), charCount);
  644. return TRUE;
  645. }
  646. /*
  647. * Write the specified lines out to the specified file.
  648. * Returns TRUE if successful, or FALSE on an error with a message output.
  649. */
  650. static int writeLines(const char *file, int num1, int num2)
  651. {
  652. LINE *lp;
  653. int fd, lineCount, charCount;
  654. if (bad_nums(num1, num2, "write"))
  655. return FALSE;
  656. lineCount = 0;
  657. charCount = 0;
  658. fd = creat(file, 0666);
  659. if (fd < 0) {
  660. bb_simple_perror_msg(file);
  661. return FALSE;
  662. }
  663. printf("\"%s\", ", file);
  664. fflush_all();
  665. lp = findLine(num1);
  666. if (lp == NULL) {
  667. close(fd);
  668. return FALSE;
  669. }
  670. while (num1++ <= num2) {
  671. if (full_write(fd, lp->data, lp->len) != lp->len) {
  672. bb_simple_perror_msg(file);
  673. close(fd);
  674. return FALSE;
  675. }
  676. charCount += lp->len;
  677. lineCount++;
  678. lp = lp->next;
  679. }
  680. if (close(fd) < 0) {
  681. bb_simple_perror_msg(file);
  682. return FALSE;
  683. }
  684. printf("%d lines, %d chars\n", lineCount, charCount);
  685. return TRUE;
  686. }
  687. /*
  688. * Print lines in a specified range.
  689. * The last line printed becomes the current line.
  690. * If expandFlag is TRUE, then the line is printed specially to
  691. * show magic characters.
  692. */
  693. static int printLines(int num1, int num2, int expandFlag)
  694. {
  695. const LINE *lp;
  696. const char *cp;
  697. int ch, count;
  698. if (bad_nums(num1, num2, "print"))
  699. return FALSE;
  700. lp = findLine(num1);
  701. if (lp == NULL)
  702. return FALSE;
  703. while (num1 <= num2) {
  704. if (!expandFlag) {
  705. write(STDOUT_FILENO, lp->data, lp->len);
  706. setCurNum(num1++);
  707. lp = lp->next;
  708. continue;
  709. }
  710. /*
  711. * Show control characters and characters with the
  712. * high bit set specially.
  713. */
  714. cp = lp->data;
  715. count = lp->len;
  716. if ((count > 0) && (cp[count - 1] == '\n'))
  717. count--;
  718. while (count-- > 0) {
  719. ch = (unsigned char) *cp++;
  720. fputc_printable(ch | PRINTABLE_META, stdout);
  721. }
  722. fputs("$\n", stdout);
  723. setCurNum(num1++);
  724. lp = lp->next;
  725. }
  726. return TRUE;
  727. }
  728. /*
  729. * Insert a new line with the specified text.
  730. * The line is inserted so as to become the specified line,
  731. * thus pushing any existing and further lines down one.
  732. * The inserted line is also set to become the current line.
  733. * Returns TRUE if successful.
  734. */
  735. static int insertLine(int num, const char *data, int len)
  736. {
  737. LINE *newLp, *lp;
  738. if ((num < 1) || (num > lastNum + 1)) {
  739. bb_error_msg("inserting at bad line number");
  740. return FALSE;
  741. }
  742. newLp = xmalloc(sizeof(LINE) + len - 1);
  743. memcpy(newLp->data, data, len);
  744. newLp->len = len;
  745. if (num > lastNum)
  746. lp = &lines;
  747. else {
  748. lp = findLine(num);
  749. if (lp == NULL) {
  750. free((char *) newLp);
  751. return FALSE;
  752. }
  753. }
  754. newLp->next = lp;
  755. newLp->prev = lp->prev;
  756. lp->prev->next = newLp;
  757. lp->prev = newLp;
  758. lastNum++;
  759. dirty = TRUE;
  760. return setCurNum(num);
  761. }
  762. /*
  763. * Delete lines from the given range.
  764. */
  765. static void deleteLines(int num1, int num2)
  766. {
  767. LINE *lp, *nlp, *plp;
  768. int count;
  769. if (bad_nums(num1, num2, "delete"))
  770. return;
  771. lp = findLine(num1);
  772. if (lp == NULL)
  773. return;
  774. if ((curNum >= num1) && (curNum <= num2)) {
  775. if (num2 < lastNum)
  776. setCurNum(num2 + 1);
  777. else if (num1 > 1)
  778. setCurNum(num1 - 1);
  779. else
  780. curNum = 0;
  781. }
  782. count = num2 - num1 + 1;
  783. if (curNum > num2)
  784. curNum -= count;
  785. lastNum -= count;
  786. while (count-- > 0) {
  787. nlp = lp->next;
  788. plp = lp->prev;
  789. plp->next = nlp;
  790. nlp->prev = plp;
  791. free(lp);
  792. lp = nlp;
  793. }
  794. dirty = TRUE;
  795. }
  796. /*
  797. * Search for a line which contains the specified string.
  798. * If the string is "", then the previously searched for string
  799. * is used. The currently searched for string is saved for future use.
  800. * Returns the line number which matches, or 0 if there was no match
  801. * with an error printed.
  802. */
  803. static NOINLINE int searchLines(const char *str, int num1, int num2)
  804. {
  805. const LINE *lp;
  806. int len;
  807. if (bad_nums(num1, num2, "search"))
  808. return 0;
  809. if (*str == '\0') {
  810. if (searchString[0] == '\0') {
  811. bb_error_msg("no previous search string");
  812. return 0;
  813. }
  814. str = searchString;
  815. }
  816. if (str != searchString)
  817. strcpy(searchString, str);
  818. len = strlen(str);
  819. lp = findLine(num1);
  820. if (lp == NULL)
  821. return 0;
  822. while (num1 <= num2) {
  823. if (findString(lp, str, len, 0) >= 0)
  824. return num1;
  825. num1++;
  826. lp = lp->next;
  827. }
  828. bb_error_msg("can't find string \"%s\"", str);
  829. return 0;
  830. }
  831. /*
  832. * Return a pointer to the specified line number.
  833. */
  834. static LINE *findLine(int num)
  835. {
  836. LINE *lp;
  837. int lnum;
  838. if ((num < 1) || (num > lastNum)) {
  839. bb_error_msg("line number %d does not exist", num);
  840. return NULL;
  841. }
  842. if (curNum <= 0) {
  843. curNum = 1;
  844. curLine = lines.next;
  845. }
  846. if (num == curNum)
  847. return curLine;
  848. lp = curLine;
  849. lnum = curNum;
  850. if (num < (curNum / 2)) {
  851. lp = lines.next;
  852. lnum = 1;
  853. } else if (num > ((curNum + lastNum) / 2)) {
  854. lp = lines.prev;
  855. lnum = lastNum;
  856. }
  857. while (lnum < num) {
  858. lp = lp->next;
  859. lnum++;
  860. }
  861. while (lnum > num) {
  862. lp = lp->prev;
  863. lnum--;
  864. }
  865. return lp;
  866. }
  867. /*
  868. * Set the current line number.
  869. * Returns TRUE if successful.
  870. */
  871. static int setCurNum(int num)
  872. {
  873. LINE *lp;
  874. lp = findLine(num);
  875. if (lp == NULL)
  876. return FALSE;
  877. curNum = num;
  878. curLine = lp;
  879. return TRUE;
  880. }