3
0

ed.c 19 KB

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