3
0

ed.c 20 KB

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