ed.patch 24 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489
  1. Index: editors/Makefile.in
  2. ===================================================================
  3. --- editors/Makefile.in (revision 10144)
  4. +++ editors/Makefile.in (working copy)
  5. @@ -24,8 +24,9 @@
  6. srcdir=$(top_srcdir)/editors
  7. EDITOR-y:=
  8. -EDITOR-$(CONFIG_AWK) += awk.o
  9. -EDITOR-$(CONFIG_PATCH) += patch.o
  10. +EDITOR-$(CONFIG_AWK) += awk.o
  11. +EDITOR-$(CONFIG_ED) += ed.o
  12. +EDITOR-$(CONFIG_PATCH) += patch.o
  13. EDITOR-$(CONFIG_SED) += sed.o
  14. EDITOR-$(CONFIG_VI) += vi.o
  15. EDITOR_SRC:= $(EDITOR-y)
  16. Index: editors/Config.in
  17. ===================================================================
  18. --- editors/Config.in (revision 10144)
  19. +++ editors/Config.in (working copy)
  20. @@ -20,6 +20,12 @@
  21. Enable math functions of the Awk programming language.
  22. NOTE: This will require libm to be present for linking.
  23. +config CONFIG_ED
  24. + bool "ed"
  25. + default n
  26. + help
  27. + ed
  28. +
  29. config CONFIG_PATCH
  30. bool "patch"
  31. default n
  32. Index: include/usage.h
  33. ===================================================================
  34. --- include/usage.h (revision 10151)
  35. +++ include/usage.h (working copy)
  36. @@ -556,6 +561,9 @@
  37. "$ echo \"Erik\\nis\\ncool\"\n" \
  38. "Erik\\nis\\ncool\n")
  39. +#define ed_trivial_usage ""
  40. +#define ed_full_usage ""
  41. +
  42. #define env_trivial_usage \
  43. "[-iu] [-] [name=value]... [command]"
  44. #define env_full_usage \
  45. Index: include/applets.h
  46. ===================================================================
  47. --- include/applets.h (revision 10151)
  48. +++ include/applets.h (working copy)
  49. @@ -179,6 +179,9 @@
  50. #ifdef CONFIG_ECHO
  51. APPLET(echo, echo_main, _BB_DIR_BIN, _BB_SUID_NEVER)
  52. #endif
  53. +#ifdef CONFIG_ED
  54. + APPLET(ed, ed_main, _BB_DIR_BIN, _BB_SUID_NEVER)
  55. +#endif
  56. #if defined(CONFIG_FEATURE_GREP_EGREP_ALIAS)
  57. APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
  58. #endif
  59. --- /dev/null 2005-04-24 01:00:01.350003056 -0400
  60. +++ ed.c 2005-04-24 01:38:51.000000000 -0400
  61. @@ -0,0 +1,1425 @@
  62. +/*
  63. + * Copyright (c) 2002 by David I. Bell
  64. + * Permission is granted to use, distribute, or modify this source,
  65. + * provided that this copyright notice remains intact.
  66. + *
  67. + * The "ed" built-in command (much simplified)
  68. + */
  69. +
  70. +#include <stdio.h>
  71. +#include <stdlib.h>
  72. +#include <unistd.h>
  73. +#include <fcntl.h>
  74. +#include <string.h>
  75. +#include <memory.h>
  76. +#include <time.h>
  77. +#include <ctype.h>
  78. +#include <sys/param.h>
  79. +#include <malloc.h>
  80. +#include "busybox.h"
  81. +
  82. +#define USERSIZE 1024 /* max line length typed in by user */
  83. +#define INITBUF_SIZE 1024 /* initial buffer size */
  84. +
  85. +typedef int BOOL;
  86. +typedef int NUM;
  87. +typedef int LEN;
  88. +
  89. +typedef struct LINE LINE;
  90. +struct LINE {
  91. + LINE *next;
  92. + LINE *prev;
  93. + LEN len;
  94. + char data[1];
  95. +};
  96. +
  97. +static LINE lines;
  98. +static LINE *curLine;
  99. +static NUM curNum;
  100. +static NUM lastNum;
  101. +static NUM marks[26];
  102. +static BOOL dirty;
  103. +static char *fileName;
  104. +static char searchString[USERSIZE];
  105. +
  106. +static char *bufBase;
  107. +static char *bufPtr;
  108. +static LEN bufUsed;
  109. +static LEN bufSize;
  110. +
  111. +static void doCommands(void);
  112. +static void subCommand(const char * cmd, NUM num1, NUM num2);
  113. +static BOOL getNum(const char ** retcp, BOOL * retHaveNum, NUM * retNum);
  114. +static BOOL setCurNum(NUM num);
  115. +static BOOL initEdit(void);
  116. +static void termEdit(void);
  117. +static void addLines(NUM num);
  118. +static BOOL insertLine(NUM num, const char * data, LEN len);
  119. +static BOOL deleteLines(NUM num1, NUM num2);
  120. +static BOOL printLines(NUM num1, NUM num2, BOOL expandFlag);
  121. +static BOOL writeLines(const char * file, NUM num1, NUM num2);
  122. +static BOOL readLines(const char * file, NUM num);
  123. +static NUM searchLines(const char * str, NUM num1, NUM num2);
  124. +static LINE * findLine(NUM num);
  125. +
  126. +static LEN findString(const LINE * lp, const char * str, LEN len, LEN offset);
  127. +
  128. +int ed_main(int argc, char **argv)
  129. +{
  130. + if (!initEdit())
  131. + return EXIT_FAILURE;
  132. +
  133. + if (argc > 1) {
  134. + fileName = strdup(argv[1]);
  135. +
  136. + if (fileName == NULL) {
  137. + bb_error_msg("No memory");
  138. + termEdit();
  139. + return EXIT_SUCCESS;
  140. + }
  141. +
  142. + if (!readLines(fileName, 1)) {
  143. + termEdit();
  144. + return EXIT_SUCCESS;
  145. + }
  146. +
  147. + if (lastNum)
  148. + setCurNum(1);
  149. +
  150. + dirty = FALSE;
  151. + }
  152. +
  153. + doCommands();
  154. +
  155. + termEdit();
  156. + return EXIT_SUCCESS;
  157. +}
  158. +
  159. +/*
  160. + * Read commands until we are told to stop.
  161. + */
  162. +static void doCommands(void)
  163. +{
  164. + const char * cp;
  165. + char * endbuf;
  166. + char * newname;
  167. + int len;
  168. + NUM num1;
  169. + NUM num2;
  170. + BOOL have1;
  171. + BOOL have2;
  172. + char buf[USERSIZE];
  173. +
  174. + while (TRUE)
  175. + {
  176. + printf(": ");
  177. + fflush(stdout);
  178. +
  179. + if (fgets(buf, sizeof(buf), stdin) == NULL)
  180. + return;
  181. +
  182. + len = strlen(buf);
  183. +
  184. + if (len == 0)
  185. + return;
  186. +
  187. + endbuf = &buf[len - 1];
  188. +
  189. + if (*endbuf != '\n')
  190. + {
  191. + bb_error_msg("Command line too long");
  192. +
  193. + do
  194. + {
  195. + len = fgetc(stdin);
  196. + }
  197. + while ((len != EOF) && (len != '\n'));
  198. +
  199. + continue;
  200. + }
  201. +
  202. + while ((endbuf > buf) && isblank(endbuf[-1]))
  203. + endbuf--;
  204. +
  205. + *endbuf = '\0';
  206. +
  207. + cp = buf;
  208. +
  209. + while (isblank(*cp))
  210. + cp++;
  211. +
  212. + have1 = FALSE;
  213. + have2 = FALSE;
  214. +
  215. + if ((curNum == 0) && (lastNum > 0))
  216. + {
  217. + curNum = 1;
  218. + curLine = lines.next;
  219. + }
  220. +
  221. + if (!getNum(&cp, &have1, &num1))
  222. + continue;
  223. +
  224. + while (isblank(*cp))
  225. + cp++;
  226. +
  227. + if (*cp == ',')
  228. + {
  229. + cp++;
  230. +
  231. + if (!getNum(&cp, &have2, &num2))
  232. + continue;
  233. +
  234. + if (!have1)
  235. + num1 = 1;
  236. +
  237. + if (!have2)
  238. + num2 = lastNum;
  239. +
  240. + have1 = TRUE;
  241. + have2 = TRUE;
  242. + }
  243. +
  244. + if (!have1)
  245. + num1 = curNum;
  246. +
  247. + if (!have2)
  248. + num2 = num1;
  249. +
  250. + switch (*cp++)
  251. + {
  252. + case 'a':
  253. + addLines(num1 + 1);
  254. + break;
  255. +
  256. + case 'c':
  257. + deleteLines(num1, num2);
  258. + addLines(num1);
  259. + break;
  260. +
  261. + case 'd':
  262. + deleteLines(num1, num2);
  263. + break;
  264. +
  265. + case 'f':
  266. + if (*cp && !isblank(*cp))
  267. + {
  268. + bb_error_msg("Bad file command");
  269. + break;
  270. + }
  271. +
  272. + while (isblank(*cp))
  273. + cp++;
  274. +
  275. + if (*cp == '\0')
  276. + {
  277. + if (fileName)
  278. + printf("\"%s\"\n", fileName);
  279. + else
  280. + printf("No file name\n");
  281. +
  282. + break;
  283. + }
  284. +
  285. + newname = strdup(cp);
  286. +
  287. + if (newname == NULL)
  288. + {
  289. + bb_error_msg("No memory for file name");
  290. + break;
  291. + }
  292. +
  293. + if (fileName)
  294. + free(fileName);
  295. +
  296. + fileName = newname;
  297. + break;
  298. +
  299. + case 'i':
  300. + addLines(num1);
  301. + break;
  302. +
  303. + case 'k':
  304. + while (isblank(*cp))
  305. + cp++;
  306. +
  307. + if ((*cp < 'a') || (*cp > 'a') || cp[1])
  308. + {
  309. + bb_error_msg("Bad mark name");
  310. + break;
  311. + }
  312. +
  313. + marks[*cp - 'a'] = num2;
  314. + break;
  315. +
  316. + case 'l':
  317. + printLines(num1, num2, TRUE);
  318. + break;
  319. +
  320. + case 'p':
  321. + printLines(num1, num2, FALSE);
  322. + break;
  323. +
  324. + case 'q':
  325. + while (isblank(*cp))
  326. + cp++;
  327. +
  328. + if (have1 || *cp)
  329. + {
  330. + bb_error_msg("Bad quit command");
  331. + break;
  332. + }
  333. +
  334. + if (!dirty)
  335. + return;
  336. +
  337. + printf("Really quit? ");
  338. + fflush(stdout);
  339. +
  340. + buf[0] = '\0';
  341. + fgets(buf, sizeof(buf), stdin);
  342. + cp = buf;
  343. +
  344. + while (isblank(*cp))
  345. + cp++;
  346. +
  347. + if ((*cp == 'y') || (*cp == 'Y'))
  348. + return;
  349. +
  350. + break;
  351. +
  352. + case 'r':
  353. + if (*cp && !isblank(*cp))
  354. + {
  355. + bb_error_msg("Bad read command");
  356. + break;
  357. + }
  358. +
  359. + while (isblank(*cp))
  360. + cp++;
  361. +
  362. + if (*cp == '\0')
  363. + {
  364. + bb_error_msg("No file name");
  365. + break;
  366. + }
  367. +
  368. + if (!have1)
  369. + num1 = lastNum;
  370. +
  371. + if (readLines(cp, num1 + 1))
  372. + break;
  373. +
  374. + if (fileName == NULL)
  375. + fileName = strdup(cp);
  376. +
  377. + break;
  378. +
  379. + case 's':
  380. + subCommand(cp, num1, num2);
  381. + break;
  382. +
  383. + case 'w':
  384. + if (*cp && !isblank(*cp))
  385. + {
  386. + bb_error_msg("Bad write command");
  387. + break;
  388. + }
  389. +
  390. + while (isblank(*cp))
  391. + cp++;
  392. +
  393. + if (!have1) {
  394. + num1 = 1;
  395. + num2 = lastNum;
  396. + }
  397. +
  398. + if (*cp == '\0')
  399. + cp = fileName;
  400. +
  401. + if (cp == NULL)
  402. + {
  403. + bb_error_msg("No file name specified");
  404. + break;
  405. + }
  406. +
  407. + writeLines(cp, num1, num2);
  408. + break;
  409. +
  410. + case 'z':
  411. + switch (*cp)
  412. + {
  413. + case '-':
  414. + printLines(curNum-21, curNum, FALSE);
  415. + break;
  416. + case '.':
  417. + printLines(curNum-11, curNum+10, FALSE);
  418. + break;
  419. + default:
  420. + printLines(curNum, curNum+21, FALSE);
  421. + break;
  422. + }
  423. + break;
  424. +
  425. + case '.':
  426. + if (have1)
  427. + {
  428. + bb_error_msg("No arguments allowed");
  429. + break;
  430. + }
  431. +
  432. + printLines(curNum, curNum, FALSE);
  433. + break;
  434. +
  435. + case '-':
  436. + if (setCurNum(curNum - 1))
  437. + printLines(curNum, curNum, FALSE);
  438. +
  439. + break;
  440. +
  441. + case '=':
  442. + printf("%d\n", num1);
  443. + break;
  444. +
  445. + case '\0':
  446. + if (have1)
  447. + {
  448. + printLines(num2, num2, FALSE);
  449. + break;
  450. + }
  451. +
  452. + if (setCurNum(curNum + 1))
  453. + printLines(curNum, curNum, FALSE);
  454. +
  455. + break;
  456. +
  457. + default:
  458. + bb_error_msg("Unimplemented command");
  459. + break;
  460. + }
  461. + }
  462. +}
  463. +
  464. +
  465. +/*
  466. + * Do the substitute command.
  467. + * The current line is set to the last substitution done.
  468. + */
  469. +static void
  470. +subCommand(const char * cmd, NUM num1, NUM num2)
  471. +{
  472. + int delim;
  473. + char * cp;
  474. + char * oldStr;
  475. + char * newStr;
  476. + LEN oldLen;
  477. + LEN newLen;
  478. + LEN deltaLen;
  479. + LEN offset;
  480. + LINE * lp;
  481. + LINE * nlp;
  482. + BOOL globalFlag;
  483. + BOOL printFlag;
  484. + BOOL didSub;
  485. + BOOL needPrint;
  486. + char buf[USERSIZE];
  487. +
  488. + if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
  489. + {
  490. + bb_error_msg("Bad line range for substitute");
  491. +
  492. + return;
  493. + }
  494. +
  495. + globalFlag = FALSE;
  496. + printFlag = FALSE;
  497. + didSub = FALSE;
  498. + needPrint = FALSE;
  499. +
  500. + /*
  501. + * Copy the command so we can modify it.
  502. + */
  503. + strcpy(buf, cmd);
  504. + cp = buf;
  505. +
  506. + if (isblank(*cp) || (*cp == '\0'))
  507. + {
  508. + bb_error_msg("Bad delimiter for substitute");
  509. +
  510. + return;
  511. + }
  512. +
  513. + delim = *cp++;
  514. + oldStr = cp;
  515. +
  516. + cp = strchr(cp, delim);
  517. +
  518. + if (cp == NULL)
  519. + {
  520. + bb_error_msg("Missing 2nd delimiter for substitute");
  521. +
  522. + return;
  523. + }
  524. +
  525. + *cp++ = '\0';
  526. +
  527. + newStr = cp;
  528. + cp = strchr(cp, delim);
  529. +
  530. + if (cp)
  531. + *cp++ = '\0';
  532. + else
  533. + cp = "";
  534. +
  535. + while (*cp) switch (*cp++)
  536. + {
  537. + case 'g':
  538. + globalFlag = TRUE;
  539. + break;
  540. +
  541. + case 'p':
  542. + printFlag = TRUE;
  543. + break;
  544. +
  545. + default:
  546. + bb_error_msg("Unknown option for substitute");
  547. +
  548. + return;
  549. + }
  550. +
  551. + if (*oldStr == '\0')
  552. + {
  553. + if (searchString[0] == '\0')
  554. + {
  555. + bb_error_msg("No previous search string");
  556. +
  557. + return;
  558. + }
  559. +
  560. + oldStr = searchString;
  561. + }
  562. +
  563. + if (oldStr != searchString)
  564. + strcpy(searchString, oldStr);
  565. +
  566. + lp = findLine(num1);
  567. +
  568. + if (lp == NULL)
  569. + return;
  570. +
  571. + oldLen = strlen(oldStr);
  572. + newLen = strlen(newStr);
  573. + deltaLen = newLen - oldLen;
  574. + offset = 0;
  575. + nlp = NULL;
  576. +
  577. + while (num1 <= num2)
  578. + {
  579. + offset = findString(lp, oldStr, oldLen, offset);
  580. +
  581. + if (offset < 0)
  582. + {
  583. + if (needPrint)
  584. + {
  585. + printLines(num1, num1, FALSE);
  586. + needPrint = FALSE;
  587. + }
  588. +
  589. + offset = 0;
  590. + lp = lp->next;
  591. + num1++;
  592. +
  593. + continue;
  594. + }
  595. +
  596. + needPrint = printFlag;
  597. + didSub = TRUE;
  598. + dirty = TRUE;
  599. +
  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. + {
  606. + memcpy(&lp->data[offset], newStr, newLen);
  607. +
  608. + if (deltaLen)
  609. + {
  610. + memcpy(&lp->data[offset + newLen],
  611. + &lp->data[offset + oldLen],
  612. + lp->len - offset - oldLen);
  613. +
  614. + lp->len += deltaLen;
  615. + }
  616. +
  617. + offset += newLen;
  618. +
  619. + if (globalFlag)
  620. + continue;
  621. +
  622. + if (needPrint)
  623. + {
  624. + printLines(num1, num1, FALSE);
  625. + needPrint = FALSE;
  626. + }
  627. +
  628. + lp = lp->next;
  629. + num1++;
  630. +
  631. + continue;
  632. + }
  633. +
  634. + /*
  635. + * The new string is larger, so allocate a new line
  636. + * structure and use that. Link it in in place of
  637. + * the old line structure.
  638. + */
  639. + nlp = (LINE *) malloc(sizeof(LINE) + lp->len + deltaLen);
  640. +
  641. + if (nlp == NULL)
  642. + {
  643. + bb_error_msg("Cannot get memory for line");
  644. +
  645. + return;
  646. + }
  647. +
  648. + nlp->len = lp->len + deltaLen;
  649. +
  650. + memcpy(nlp->data, lp->data, offset);
  651. +
  652. + memcpy(&nlp->data[offset], newStr, newLen);
  653. +
  654. + memcpy(&nlp->data[offset + newLen],
  655. + &lp->data[offset + oldLen],
  656. + lp->len - offset - oldLen);
  657. +
  658. + nlp->next = lp->next;
  659. + nlp->prev = lp->prev;
  660. + nlp->prev->next = nlp;
  661. + nlp->next->prev = nlp;
  662. +
  663. + if (curLine == lp)
  664. + curLine = nlp;
  665. +
  666. + free(lp);
  667. + lp = nlp;
  668. +
  669. + offset += newLen;
  670. +
  671. + if (globalFlag)
  672. + continue;
  673. +
  674. + if (needPrint)
  675. + {
  676. + printLines(num1, num1, FALSE);
  677. + needPrint = FALSE;
  678. + }
  679. +
  680. + lp = lp->next;
  681. + num1++;
  682. + }
  683. +
  684. + if (!didSub)
  685. + bb_error_msg("No substitutions found for \"%s\"", oldStr);
  686. +}
  687. +
  688. +
  689. +/*
  690. + * Search a line for the specified string starting at the specified
  691. + * offset in the line. Returns the offset of the found string, or -1.
  692. + */
  693. +static LEN
  694. +findString( const LINE * lp, const char * str, LEN len, LEN offset)
  695. +{
  696. + LEN left;
  697. + const char * cp;
  698. + const char * ncp;
  699. +
  700. + cp = &lp->data[offset];
  701. + left = lp->len - offset;
  702. +
  703. + while (left >= len)
  704. + {
  705. + ncp = memchr(cp, *str, left);
  706. +
  707. + if (ncp == NULL)
  708. + return -1;
  709. +
  710. + left -= (ncp - cp);
  711. +
  712. + if (left < len)
  713. + return -1;
  714. +
  715. + cp = ncp;
  716. +
  717. + if (memcmp(cp, str, len) == 0)
  718. + return (cp - lp->data);
  719. +
  720. + cp++;
  721. + left--;
  722. + }
  723. +
  724. + return -1;
  725. +}
  726. +
  727. +
  728. +/*
  729. + * Add lines which are typed in by the user.
  730. + * The lines are inserted just before the specified line number.
  731. + * The lines are terminated by a line containing a single dot (ugly!),
  732. + * or by an end of file.
  733. + */
  734. +static void
  735. +addLines(NUM num)
  736. +{
  737. + int len;
  738. + char buf[USERSIZE + 1];
  739. +
  740. + while (fgets(buf, sizeof(buf), stdin))
  741. + {
  742. + if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0'))
  743. + return;
  744. +
  745. + len = strlen(buf);
  746. +
  747. + if (len == 0)
  748. + return;
  749. +
  750. + if (buf[len - 1] != '\n')
  751. + {
  752. + bb_error_msg("Line too long");
  753. +
  754. + do
  755. + {
  756. + len = fgetc(stdin);
  757. + }
  758. + while ((len != EOF) && (len != '\n'));
  759. +
  760. + return;
  761. + }
  762. +
  763. + if (!insertLine(num++, buf, len))
  764. + return;
  765. + }
  766. +}
  767. +
  768. +
  769. +/*
  770. + * Parse a line number argument if it is present. This is a sum
  771. + * or difference of numbers, '.', '$', 'x, or a search string.
  772. + * Returns TRUE if successful (whether or not there was a number).
  773. + * Returns FALSE if there was a parsing error, with a message output.
  774. + * Whether there was a number is returned indirectly, as is the number.
  775. + * The character pointer which stopped the scan is also returned.
  776. + */
  777. +static BOOL
  778. +getNum(const char ** retcp, BOOL * retHaveNum, NUM * retNum)
  779. +{
  780. + const char * cp;
  781. + char * endStr;
  782. + char str[USERSIZE];
  783. + BOOL haveNum;
  784. + NUM value;
  785. + NUM num;
  786. + NUM sign;
  787. +
  788. + cp = *retcp;
  789. + haveNum = FALSE;
  790. + value = 0;
  791. + sign = 1;
  792. +
  793. + while (TRUE)
  794. + {
  795. + while (isblank(*cp))
  796. + cp++;
  797. +
  798. + switch (*cp)
  799. + {
  800. + case '.':
  801. + haveNum = TRUE;
  802. + num = curNum;
  803. + cp++;
  804. + break;
  805. +
  806. + case '$':
  807. + haveNum = TRUE;
  808. + num = lastNum;
  809. + cp++;
  810. + break;
  811. +
  812. + case '\'':
  813. + cp++;
  814. +
  815. + if ((*cp < 'a') || (*cp > 'z'))
  816. + {
  817. + bb_error_msg("Bad mark name");
  818. +
  819. + return FALSE;
  820. + }
  821. +
  822. + haveNum = TRUE;
  823. + num = marks[*cp++ - 'a'];
  824. + break;
  825. +
  826. + case '/':
  827. + strcpy(str, ++cp);
  828. + endStr = strchr(str, '/');
  829. +
  830. + if (endStr)
  831. + {
  832. + *endStr++ = '\0';
  833. + cp += (endStr - str);
  834. + }
  835. + else
  836. + cp = "";
  837. +
  838. + num = searchLines(str, curNum, lastNum);
  839. +
  840. + if (num == 0)
  841. + return FALSE;
  842. +
  843. + haveNum = TRUE;
  844. + break;
  845. +
  846. + default:
  847. + if (!isdigit(*cp))
  848. + {
  849. + *retcp = cp;
  850. + *retHaveNum = haveNum;
  851. + *retNum = value;
  852. +
  853. + return TRUE;
  854. + }
  855. +
  856. + num = 0;
  857. +
  858. + while (isdigit(*cp))
  859. + num = num * 10 + *cp++ - '0';
  860. +
  861. + haveNum = TRUE;
  862. + break;
  863. + }
  864. +
  865. + value += num * sign;
  866. +
  867. + while (isblank(*cp))
  868. + cp++;
  869. +
  870. + switch (*cp)
  871. + {
  872. + case '-':
  873. + sign = -1;
  874. + cp++;
  875. + break;
  876. +
  877. + case '+':
  878. + sign = 1;
  879. + cp++;
  880. + break;
  881. +
  882. + default:
  883. + *retcp = cp;
  884. + *retHaveNum = haveNum;
  885. + *retNum = value;
  886. +
  887. + return TRUE;
  888. + }
  889. + }
  890. +}
  891. +
  892. +
  893. +/*
  894. + * Initialize everything for editing.
  895. + */
  896. +static BOOL
  897. +initEdit(void)
  898. +{
  899. + int i;
  900. +
  901. + bufSize = INITBUF_SIZE;
  902. + bufBase = malloc(bufSize);
  903. +
  904. + if (bufBase == NULL)
  905. + {
  906. + bb_error_msg("No memory for buffer");
  907. +
  908. + return FALSE;
  909. + }
  910. +
  911. + bufPtr = bufBase;
  912. + bufUsed = 0;
  913. +
  914. + lines.next = &lines;
  915. + lines.prev = &lines;
  916. +
  917. + curLine = NULL;
  918. + curNum = 0;
  919. + lastNum = 0;
  920. + dirty = FALSE;
  921. + fileName = NULL;
  922. + searchString[0] = '\0';
  923. +
  924. + for (i = 0; i < 26; i++)
  925. + marks[i] = 0;
  926. +
  927. + return TRUE;
  928. +}
  929. +
  930. +
  931. +/*
  932. + * Finish editing.
  933. + */
  934. +static void
  935. +termEdit(void)
  936. +{
  937. + if (bufBase)
  938. + free(bufBase);
  939. +
  940. + bufBase = NULL;
  941. + bufPtr = NULL;
  942. + bufSize = 0;
  943. + bufUsed = 0;
  944. +
  945. + if (fileName)
  946. + free(fileName);
  947. +
  948. + fileName = NULL;
  949. +
  950. + searchString[0] = '\0';
  951. +
  952. + if (lastNum)
  953. + deleteLines(1, lastNum);
  954. +
  955. + lastNum = 0;
  956. + curNum = 0;
  957. + curLine = NULL;
  958. +}
  959. +
  960. +
  961. +/*
  962. + * Read lines from a file at the specified line number.
  963. + * Returns TRUE if the file was successfully read.
  964. + */
  965. +static BOOL
  966. +readLines(const char * file, NUM num)
  967. +{
  968. + int fd;
  969. + int cc;
  970. + LEN len;
  971. + LEN lineCount;
  972. + LEN charCount;
  973. + char * cp;
  974. +
  975. + if ((num < 1) || (num > lastNum + 1))
  976. + {
  977. + bb_error_msg("Bad line for read");
  978. +
  979. + return FALSE;
  980. + }
  981. +
  982. + fd = open(file, 0);
  983. +
  984. + if (fd < 0)
  985. + {
  986. + perror(file);
  987. +
  988. + return FALSE;
  989. + }
  990. +
  991. + bufPtr = bufBase;
  992. + bufUsed = 0;
  993. + lineCount = 0;
  994. + charCount = 0;
  995. + cc = 0;
  996. +
  997. + printf("\"%s\", ", file);
  998. + fflush(stdout);
  999. +
  1000. + do
  1001. + {
  1002. + cp = memchr(bufPtr, '\n', bufUsed);
  1003. +
  1004. + if (cp)
  1005. + {
  1006. + len = (cp - bufPtr) + 1;
  1007. +
  1008. + if (!insertLine(num, bufPtr, len))
  1009. + {
  1010. + close(fd);
  1011. +
  1012. + return FALSE;
  1013. + }
  1014. +
  1015. + bufPtr += len;
  1016. + bufUsed -= len;
  1017. + charCount += len;
  1018. + lineCount++;
  1019. + num++;
  1020. +
  1021. + continue;
  1022. + }
  1023. +
  1024. + if (bufPtr != bufBase)
  1025. + {
  1026. + memcpy(bufBase, bufPtr, bufUsed);
  1027. + bufPtr = bufBase + bufUsed;
  1028. + }
  1029. +
  1030. + if (bufUsed >= bufSize)
  1031. + {
  1032. + len = (bufSize * 3) / 2;
  1033. + cp = realloc(bufBase, len);
  1034. +
  1035. + if (cp == NULL)
  1036. + {
  1037. + bb_error_msg("No memory for buffer");
  1038. + close(fd);
  1039. +
  1040. + return FALSE;
  1041. + }
  1042. +
  1043. + bufBase = cp;
  1044. + bufPtr = bufBase + bufUsed;
  1045. + bufSize = len;
  1046. + }
  1047. +
  1048. + cc = read(fd, bufPtr, bufSize - bufUsed);
  1049. + bufUsed += cc;
  1050. + bufPtr = bufBase;
  1051. +
  1052. + }
  1053. + while (cc > 0);
  1054. +
  1055. + if (cc < 0)
  1056. + {
  1057. + perror(file);
  1058. + close(fd);
  1059. +
  1060. + return FALSE;
  1061. + }
  1062. +
  1063. + if (bufUsed)
  1064. + {
  1065. + if (!insertLine(num, bufPtr, bufUsed))
  1066. + {
  1067. + close(fd);
  1068. +
  1069. + return -1;
  1070. + }
  1071. +
  1072. + lineCount++;
  1073. + charCount += bufUsed;
  1074. + }
  1075. +
  1076. + close(fd);
  1077. +
  1078. + printf("%d lines%s, %d chars\n", lineCount,
  1079. + (bufUsed ? " (incomplete)" : ""), charCount);
  1080. +
  1081. + return TRUE;
  1082. +}
  1083. +
  1084. +
  1085. +/*
  1086. + * Write the specified lines out to the specified file.
  1087. + * Returns TRUE if successful, or FALSE on an error with a message output.
  1088. + */
  1089. +static BOOL
  1090. +writeLines(const char * file, NUM num1, NUM num2)
  1091. +{
  1092. + int fd;
  1093. + LINE * lp;
  1094. + LEN lineCount;
  1095. + LEN charCount;
  1096. +
  1097. + if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
  1098. + {
  1099. + bb_error_msg("Bad line range for write");
  1100. +
  1101. + return FALSE;
  1102. + }
  1103. +
  1104. + lineCount = 0;
  1105. + charCount = 0;
  1106. +
  1107. + fd = creat(file, 0666);
  1108. +
  1109. + if (fd < 0) {
  1110. + perror(file);
  1111. +
  1112. + return FALSE;
  1113. + }
  1114. +
  1115. + printf("\"%s\", ", file);
  1116. + fflush(stdout);
  1117. +
  1118. + lp = findLine(num1);
  1119. +
  1120. + if (lp == NULL)
  1121. + {
  1122. + close(fd);
  1123. +
  1124. + return FALSE;
  1125. + }
  1126. +
  1127. + while (num1++ <= num2)
  1128. + {
  1129. + if (write(fd, lp->data, lp->len) != lp->len)
  1130. + {
  1131. + perror(file);
  1132. + close(fd);
  1133. +
  1134. + return FALSE;
  1135. + }
  1136. +
  1137. + charCount += lp->len;
  1138. + lineCount++;
  1139. + lp = lp->next;
  1140. + }
  1141. +
  1142. + if (close(fd) < 0)
  1143. + {
  1144. + perror(file);
  1145. +
  1146. + return FALSE;
  1147. + }
  1148. +
  1149. + printf("%d lines, %d chars\n", lineCount, charCount);
  1150. +
  1151. + return TRUE;
  1152. +}
  1153. +
  1154. +
  1155. +/*
  1156. + * Print lines in a specified range.
  1157. + * The last line printed becomes the current line.
  1158. + * If expandFlag is TRUE, then the line is printed specially to
  1159. + * show magic characters.
  1160. + */
  1161. +static BOOL
  1162. +printLines(NUM num1, NUM num2, BOOL expandFlag)
  1163. +{
  1164. + const LINE * lp;
  1165. + const unsigned char * cp;
  1166. + int ch;
  1167. + LEN count;
  1168. +
  1169. + if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
  1170. + {
  1171. + bb_error_msg("Bad line range for print");
  1172. +
  1173. + return FALSE;
  1174. + }
  1175. +
  1176. + lp = findLine(num1);
  1177. +
  1178. + if (lp == NULL)
  1179. + return FALSE;
  1180. +
  1181. + while (num1 <= num2)
  1182. + {
  1183. + if (!expandFlag)
  1184. + {
  1185. + write(1, lp->data, lp->len);
  1186. + setCurNum(num1++);
  1187. + lp = lp->next;
  1188. +
  1189. + continue;
  1190. + }
  1191. +
  1192. + /*
  1193. + * Show control characters and characters with the
  1194. + * high bit set specially.
  1195. + */
  1196. + cp = lp->data;
  1197. + count = lp->len;
  1198. +
  1199. + if ((count > 0) && (cp[count - 1] == '\n'))
  1200. + count--;
  1201. +
  1202. + while (count-- > 0)
  1203. + {
  1204. + ch = *cp++;
  1205. +
  1206. + if (ch & 0x80)
  1207. + {
  1208. + fputs("M-", stdout);
  1209. + ch &= 0x7f;
  1210. + }
  1211. +
  1212. + if (ch < ' ')
  1213. + {
  1214. + fputc('^', stdout);
  1215. + ch += '@';
  1216. + }
  1217. +
  1218. + if (ch == 0x7f)
  1219. + {
  1220. + fputc('^', stdout);
  1221. + ch = '?';
  1222. + }
  1223. +
  1224. + fputc(ch, stdout);
  1225. + }
  1226. +
  1227. + fputs("$\n", stdout);
  1228. +
  1229. + setCurNum(num1++);
  1230. + lp = lp->next;
  1231. + }
  1232. +
  1233. + return TRUE;
  1234. +}
  1235. +
  1236. +
  1237. +/*
  1238. + * Insert a new line with the specified text.
  1239. + * The line is inserted so as to become the specified line,
  1240. + * thus pushing any existing and further lines down one.
  1241. + * The inserted line is also set to become the current line.
  1242. + * Returns TRUE if successful.
  1243. + */
  1244. +static BOOL
  1245. +insertLine(NUM num, const char * data, LEN len)
  1246. +{
  1247. + LINE * newLp;
  1248. + LINE * lp;
  1249. +
  1250. + if ((num < 1) || (num > lastNum + 1))
  1251. + {
  1252. + bb_error_msg("Inserting at bad line number");
  1253. +
  1254. + return FALSE;
  1255. + }
  1256. +
  1257. + newLp = (LINE *) malloc(sizeof(LINE) + len - 1);
  1258. +
  1259. + if (newLp == NULL)
  1260. + {
  1261. + bb_error_msg("Failed to allocate memory for line");
  1262. +
  1263. + return FALSE;
  1264. + }
  1265. +
  1266. + memcpy(newLp->data, data, len);
  1267. + newLp->len = len;
  1268. +
  1269. + if (num > lastNum)
  1270. + lp = &lines;
  1271. + else
  1272. + {
  1273. + lp = findLine(num);
  1274. +
  1275. + if (lp == NULL)
  1276. + {
  1277. + free((char *) newLp);
  1278. +
  1279. + return FALSE;
  1280. + }
  1281. + }
  1282. +
  1283. + newLp->next = lp;
  1284. + newLp->prev = lp->prev;
  1285. + lp->prev->next = newLp;
  1286. + lp->prev = newLp;
  1287. +
  1288. + lastNum++;
  1289. + dirty = TRUE;
  1290. +
  1291. + return setCurNum(num);
  1292. +}
  1293. +
  1294. +
  1295. +/*
  1296. + * Delete lines from the given range.
  1297. + */
  1298. +static BOOL
  1299. +deleteLines(NUM num1, NUM num2)
  1300. +{
  1301. + LINE * lp;
  1302. + LINE * nlp;
  1303. + LINE * plp;
  1304. + NUM count;
  1305. +
  1306. + if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
  1307. + {
  1308. + bb_error_msg("Bad line numbers for delete");
  1309. +
  1310. + return FALSE;
  1311. + }
  1312. +
  1313. + lp = findLine(num1);
  1314. +
  1315. + if (lp == NULL)
  1316. + return FALSE;
  1317. +
  1318. + if ((curNum >= num1) && (curNum <= num2))
  1319. + {
  1320. + if (num2 < lastNum)
  1321. + setCurNum(num2 + 1);
  1322. + else if (num1 > 1)
  1323. + setCurNum(num1 - 1);
  1324. + else
  1325. + curNum = 0;
  1326. + }
  1327. +
  1328. + count = num2 - num1 + 1;
  1329. +
  1330. + if (curNum > num2)
  1331. + curNum -= count;
  1332. +
  1333. + lastNum -= count;
  1334. +
  1335. + while (count-- > 0)
  1336. + {
  1337. + nlp = lp->next;
  1338. + plp = lp->prev;
  1339. + plp->next = nlp;
  1340. + nlp->prev = plp;
  1341. + lp->next = NULL;
  1342. + lp->prev = NULL;
  1343. + lp->len = 0;
  1344. + free(lp);
  1345. + lp = nlp;
  1346. + }
  1347. +
  1348. + dirty = TRUE;
  1349. +
  1350. + return TRUE;
  1351. +}
  1352. +
  1353. +
  1354. +/*
  1355. + * Search for a line which contains the specified string.
  1356. + * If the string is NULL, then the previously searched for string
  1357. + * is used. The currently searched for string is saved for future use.
  1358. + * Returns the line number which matches, or 0 if there was no match
  1359. + * with an error printed.
  1360. + */
  1361. +static NUM
  1362. +searchLines(const char * str, NUM num1, NUM num2)
  1363. +{
  1364. + const LINE * lp;
  1365. + int len;
  1366. +
  1367. + if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
  1368. + {
  1369. + bb_error_msg("Bad line numbers for search");
  1370. +
  1371. + return 0;
  1372. + }
  1373. +
  1374. + if (*str == '\0')
  1375. + {
  1376. + if (searchString[0] == '\0')
  1377. + {
  1378. + bb_error_msg("No previous search string");
  1379. +
  1380. + return 0;
  1381. + }
  1382. +
  1383. + str = searchString;
  1384. + }
  1385. +
  1386. + if (str != searchString)
  1387. + strcpy(searchString, str);
  1388. +
  1389. + len = strlen(str);
  1390. +
  1391. + lp = findLine(num1);
  1392. +
  1393. + if (lp == NULL)
  1394. + return 0;
  1395. +
  1396. + while (num1 <= num2)
  1397. + {
  1398. + if (findString(lp, str, len, 0) >= 0)
  1399. + return num1;
  1400. +
  1401. + num1++;
  1402. + lp = lp->next;
  1403. + }
  1404. +
  1405. + bb_error_msg("Cannot find string \"%s\"", str);
  1406. +
  1407. + return 0;
  1408. +}
  1409. +
  1410. +
  1411. +/*
  1412. + * Return a pointer to the specified line number.
  1413. + */
  1414. +static LINE *
  1415. +findLine(NUM num)
  1416. +{
  1417. + LINE * lp;
  1418. + NUM lnum;
  1419. +
  1420. + if ((num < 1) || (num > lastNum))
  1421. + {
  1422. + bb_error_msg("Line number %d does not exist", num);
  1423. +
  1424. + return NULL;
  1425. + }
  1426. +
  1427. + if (curNum <= 0)
  1428. + {
  1429. + curNum = 1;
  1430. + curLine = lines.next;
  1431. + }
  1432. +
  1433. + if (num == curNum)
  1434. + return curLine;
  1435. +
  1436. + lp = curLine;
  1437. + lnum = curNum;
  1438. +
  1439. + if (num < (curNum / 2))
  1440. + {
  1441. + lp = lines.next;
  1442. + lnum = 1;
  1443. + }
  1444. + else if (num > ((curNum + lastNum) / 2))
  1445. + {
  1446. + lp = lines.prev;
  1447. + lnum = lastNum;
  1448. + }
  1449. +
  1450. + while (lnum < num)
  1451. + {
  1452. + lp = lp->next;
  1453. + lnum++;
  1454. + }
  1455. +
  1456. + while (lnum > num)
  1457. + {
  1458. + lp = lp->prev;
  1459. + lnum--;
  1460. + }
  1461. +
  1462. + return lp;
  1463. +}
  1464. +
  1465. +
  1466. +/*
  1467. + * Set the current line number.
  1468. + * Returns TRUE if successful.
  1469. + */
  1470. +static BOOL
  1471. +setCurNum(NUM num)
  1472. +{
  1473. + LINE * lp;
  1474. +
  1475. + lp = findLine(num);
  1476. +
  1477. + if (lp == NULL)
  1478. + return FALSE;
  1479. +
  1480. + curNum = num;
  1481. + curLine = lp;
  1482. +
  1483. + return TRUE;
  1484. +}
  1485. +
  1486. +/* END CODE */