ed.c 20 KB

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