blob: 3087fb0b9f14797fb75391ef1d096a530bb52155 [file] [log] [blame]
Bernhard Reutner-Fischerd9ed35c2006-05-19 12:38:30 +00001/* vi: set sw=4 ts=4: */
Rob Landley3b890392006-05-04 20:56:43 +00002/*
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
maxwen27116ba2015-08-14 21:41:28 +020010//config:config ED
11//config: bool "ed"
12//config: default y
13//config: help
14//config: The original 1970's Unix text editor, from the days of teletypes.
15//config: Small, simple, evil. Part of SUSv3. If you're not already using
16//config: this, you don't need it.
17
18//kbuild:lib-$(CONFIG_ED) += ed.o
19
20//applet:IF_ED(APPLET(ed, BB_DIR_BIN, BB_SUID_DROP))
21
Pere Orga6a3e01d2011-04-01 22:56:30 +020022//usage:#define ed_trivial_usage ""
23//usage:#define ed_full_usage ""
24
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000025#include "libbb.h"
Rob Landley3b890392006-05-04 20:56:43 +000026
Denis Vlasenkod9391b12007-09-25 11:55:57 +000027typedef struct LINE {
28 struct LINE *next;
29 struct LINE *prev;
30 int len;
31 char data[1];
32} LINE;
33
34
Denis Vlasenko74324c82007-06-04 10:16:52 +000035#define searchString bb_common_bufsiz1
36
Denis Vlasenko55f30b02007-03-24 22:42:29 +000037enum {
Denis Vlasenko74324c82007-06-04 10:16:52 +000038 USERSIZE = sizeof(searchString) > 1024 ? 1024
39 : sizeof(searchString) - 1, /* max line length typed in by user */
Denis Vlasenko55f30b02007-03-24 22:42:29 +000040 INITBUF_SIZE = 1024, /* initial buffer size */
41};
42
Denis Vlasenkod9391b12007-09-25 11:55:57 +000043struct globals {
44 int curNum;
45 int lastNum;
46 int bufUsed;
47 int bufSize;
48 LINE *curLine;
49 char *bufBase;
50 char *bufPtr;
51 char *fileName;
52 LINE lines;
53 smallint dirty;
54 int marks[26];
55};
56#define G (*ptr_to_globals)
57#define curLine (G.curLine )
58#define bufBase (G.bufBase )
59#define bufPtr (G.bufPtr )
60#define fileName (G.fileName )
61#define curNum (G.curNum )
62#define lastNum (G.lastNum )
63#define bufUsed (G.bufUsed )
64#define bufSize (G.bufSize )
65#define dirty (G.dirty )
66#define lines (G.lines )
67#define marks (G.marks )
68#define INIT_G() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +000069 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
Denis Vlasenkod9391b12007-09-25 11:55:57 +000070} while (0)
Rob Landley3b890392006-05-04 20:56:43 +000071
Rob Landley3b890392006-05-04 20:56:43 +000072
73static void doCommands(void);
74static void subCommand(const char *cmd, int num1, int num2);
Denis Vlasenkod9391b12007-09-25 11:55:57 +000075static int getNum(const char **retcp, smallint *retHaveNum, int *retNum);
Rob Landley3b890392006-05-04 20:56:43 +000076static int setCurNum(int num);
Rob Landley3b890392006-05-04 20:56:43 +000077static void addLines(int num);
78static int insertLine(int num, const char *data, int len);
Denis Vlasenkod9391b12007-09-25 11:55:57 +000079static void deleteLines(int num1, int num2);
Rob Landley3b890392006-05-04 20:56:43 +000080static int printLines(int num1, int num2, int expandFlag);
81static int writeLines(const char *file, int num1, int num2);
82static int readLines(const char *file, int num);
83static int searchLines(const char *str, int num1, int num2);
84static LINE *findLine(int num);
Rob Landley3b890392006-05-04 20:56:43 +000085static int findString(const LINE *lp, const char * str, int len, int offset);
86
Denis Vlasenkod9391b12007-09-25 11:55:57 +000087
88static int bad_nums(int num1, int num2, const char *for_what)
89{
90 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
91 bb_error_msg("bad line range for %s", for_what);
92 return 1;
93 }
94 return 0;
95}
96
97
98static char *skip_blank(const char *cp)
99{
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000100 while (isblank(*cp))
101 cp++;
102 return (char *)cp;
103}
104
105
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000106int ed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000107int ed_main(int argc UNUSED_PARAM, char **argv)
Rob Landley3b890392006-05-04 20:56:43 +0000108{
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000109 INIT_G();
110
111 bufSize = INITBUF_SIZE;
112 bufBase = xmalloc(bufSize);
113 bufPtr = bufBase;
114 lines.next = &lines;
115 lines.prev = &lines;
Rob Landley3b890392006-05-04 20:56:43 +0000116
Denis Vlasenko1d426652008-03-17 09:09:09 +0000117 if (argv[1]) {
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000118 fileName = xstrdup(argv[1]);
Rob Landley3b890392006-05-04 20:56:43 +0000119 if (!readLines(fileName, 1)) {
Rob Landley3b890392006-05-04 20:56:43 +0000120 return EXIT_SUCCESS;
121 }
Rob Landley3b890392006-05-04 20:56:43 +0000122 if (lastNum)
123 setCurNum(1);
Rob Landley3b890392006-05-04 20:56:43 +0000124 dirty = FALSE;
125 }
126
127 doCommands();
Rob Landley3b890392006-05-04 20:56:43 +0000128 return EXIT_SUCCESS;
129}
130
131/*
132 * Read commands until we are told to stop.
133 */
134static void doCommands(void)
135{
136 const char *cp;
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000137 char *endbuf, buf[USERSIZE];
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000138 int len, num1, num2;
139 smallint have1, have2;
Rob Landley3b890392006-05-04 20:56:43 +0000140
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000141 while (TRUE) {
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000142 /* Returns:
143 * -1 on read errors or EOF, or on bare Ctrl-D.
144 * 0 on ctrl-C,
145 * >0 length of input string, including terminating '\n'
146 */
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100147 len = read_line_input(NULL, ": ", buf, sizeof(buf), /*timeout*/ -1);
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000148 if (len <= 0)
Rob Landley3b890392006-05-04 20:56:43 +0000149 return;
Rob Landley3b890392006-05-04 20:56:43 +0000150 endbuf = &buf[len - 1];
Rob Landley3b890392006-05-04 20:56:43 +0000151 while ((endbuf > buf) && isblank(endbuf[-1]))
152 endbuf--;
Rob Landley3b890392006-05-04 20:56:43 +0000153 *endbuf = '\0';
154
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000155 cp = skip_blank(buf);
Rob Landley3b890392006-05-04 20:56:43 +0000156 have1 = FALSE;
157 have2 = FALSE;
158
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000159 if ((curNum == 0) && (lastNum > 0)) {
Rob Landley3b890392006-05-04 20:56:43 +0000160 curNum = 1;
161 curLine = lines.next;
162 }
163
164 if (!getNum(&cp, &have1, &num1))
165 continue;
166
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000167 cp = skip_blank(cp);
Rob Landley3b890392006-05-04 20:56:43 +0000168
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000169 if (*cp == ',') {
Rob Landley3b890392006-05-04 20:56:43 +0000170 cp++;
Rob Landley3b890392006-05-04 20:56:43 +0000171 if (!getNum(&cp, &have2, &num2))
172 continue;
Rob Landley3b890392006-05-04 20:56:43 +0000173 if (!have1)
174 num1 = 1;
Rob Landley3b890392006-05-04 20:56:43 +0000175 if (!have2)
176 num2 = lastNum;
Rob Landley3b890392006-05-04 20:56:43 +0000177 have1 = TRUE;
178 have2 = TRUE;
179 }
Rob Landley3b890392006-05-04 20:56:43 +0000180 if (!have1)
181 num1 = curNum;
Rob Landley3b890392006-05-04 20:56:43 +0000182 if (!have2)
183 num2 = num1;
184
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000185 switch (*cp++) {
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000186 case 'a':
187 addLines(num1 + 1);
188 break;
Rob Landley3b890392006-05-04 20:56:43 +0000189
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000190 case 'c':
191 deleteLines(num1, num2);
192 addLines(num1);
193 break;
Rob Landley3b890392006-05-04 20:56:43 +0000194
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000195 case 'd':
196 deleteLines(num1, num2);
197 break;
Rob Landley3b890392006-05-04 20:56:43 +0000198
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000199 case 'f':
200 if (*cp && !isblank(*cp)) {
201 bb_error_msg("bad file command");
Rob Landley3b890392006-05-04 20:56:43 +0000202 break;
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000203 }
204 cp = skip_blank(cp);
205 if (*cp == '\0') {
206 if (fileName)
207 printf("\"%s\"\n", fileName);
208 else
209 printf("No file name\n");
Rob Landley3b890392006-05-04 20:56:43 +0000210 break;
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000211 }
212 free(fileName);
213 fileName = xstrdup(cp);
214 break;
Rob Landley3b890392006-05-04 20:56:43 +0000215
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000216 case 'i':
217 addLines(num1);
218 break;
219
220 case 'k':
221 cp = skip_blank(cp);
222 if ((*cp < 'a') || (*cp > 'z') || cp[1]) {
223 bb_error_msg("bad mark name");
Rob Landley3b890392006-05-04 20:56:43 +0000224 break;
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000225 }
226 marks[*cp - 'a'] = num2;
227 break;
Rob Landley3b890392006-05-04 20:56:43 +0000228
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000229 case 'l':
230 printLines(num1, num2, TRUE);
231 break;
232
233 case 'p':
234 printLines(num1, num2, FALSE);
235 break;
236
237 case 'q':
238 cp = skip_blank(cp);
239 if (have1 || *cp) {
240 bb_error_msg("bad quit command");
Rob Landley3b890392006-05-04 20:56:43 +0000241 break;
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000242 }
243 if (!dirty)
244 return;
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100245 len = read_line_input(NULL, "Really quit? ", buf, 16, /*timeout*/ -1);
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000246 /* read error/EOF - no way to continue */
247 if (len < 0)
248 return;
249 cp = skip_blank(buf);
250 if ((*cp | 0x20) == 'y') /* Y or y */
251 return;
252 break;
Rob Landley3b890392006-05-04 20:56:43 +0000253
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000254 case 'r':
255 if (*cp && !isblank(*cp)) {
256 bb_error_msg("bad read command");
Rob Landley3b890392006-05-04 20:56:43 +0000257 break;
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000258 }
259 cp = skip_blank(cp);
260 if (*cp == '\0') {
261 bb_error_msg("no file name");
Rob Landley3b890392006-05-04 20:56:43 +0000262 break;
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000263 }
264 if (!have1)
265 num1 = lastNum;
266 if (readLines(cp, num1 + 1))
Rob Landley3b890392006-05-04 20:56:43 +0000267 break;
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000268 if (fileName == NULL)
269 fileName = xstrdup(cp);
270 break;
Rob Landley3b890392006-05-04 20:56:43 +0000271
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000272 case 's':
273 subCommand(cp, num1, num2);
274 break;
275
276 case 'w':
277 if (*cp && !isblank(*cp)) {
278 bb_error_msg("bad write command");
Rob Landley3b890392006-05-04 20:56:43 +0000279 break;
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000280 }
281 cp = skip_blank(cp);
282 if (!have1) {
283 num1 = 1;
284 num2 = lastNum;
285 }
286 if (*cp == '\0')
287 cp = fileName;
288 if (cp == NULL) {
289 bb_error_msg("no file name specified");
Rob Landley3b890392006-05-04 20:56:43 +0000290 break;
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000291 }
292 writeLines(cp, num1, num2);
293 break;
Rob Landley3b890392006-05-04 20:56:43 +0000294
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000295 case 'z':
296 switch (*cp) {
Rob Landley3b890392006-05-04 20:56:43 +0000297 case '-':
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000298 printLines(curNum - 21, curNum, FALSE);
Rob Landley3b890392006-05-04 20:56:43 +0000299 break;
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000300 case '.':
301 printLines(curNum - 11, curNum + 10, FALSE);
Rob Landley3b890392006-05-04 20:56:43 +0000302 break;
Rob Landley3b890392006-05-04 20:56:43 +0000303 default:
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000304 printLines(curNum, curNum + 21, FALSE);
Rob Landley3b890392006-05-04 20:56:43 +0000305 break;
Denis Vlasenkoc4523c22008-03-28 02:24:59 +0000306 }
307 break;
308
309 case '.':
310 if (have1) {
311 bb_error_msg("no arguments allowed");
312 break;
313 }
314 printLines(curNum, curNum, FALSE);
315 break;
316
317 case '-':
318 if (setCurNum(curNum - 1))
319 printLines(curNum, curNum, FALSE);
320 break;
321
322 case '=':
323 printf("%d\n", num1);
324 break;
325 case '\0':
326 if (have1) {
327 printLines(num2, num2, FALSE);
328 break;
329 }
330 if (setCurNum(curNum + 1))
331 printLines(curNum, curNum, FALSE);
332 break;
333
334 default:
335 bb_error_msg("unimplemented command");
336 break;
Rob Landley3b890392006-05-04 20:56:43 +0000337 }
338 }
339}
340
341
342/*
343 * Do the substitute command.
344 * The current line is set to the last substitution done.
345 */
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000346static void subCommand(const char *cmd, int num1, int num2)
Rob Landley3b890392006-05-04 20:56:43 +0000347{
348 char *cp, *oldStr, *newStr, buf[USERSIZE];
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000349 int delim, oldLen, newLen, deltaLen, offset;
Rob Landley3b890392006-05-04 20:56:43 +0000350 LINE *lp, *nlp;
351 int globalFlag, printFlag, didSub, needPrint;
352
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000353 if (bad_nums(num1, num2, "substitute"))
Rob Landley3b890392006-05-04 20:56:43 +0000354 return;
Rob Landley3b890392006-05-04 20:56:43 +0000355
356 globalFlag = FALSE;
357 printFlag = FALSE;
358 didSub = FALSE;
359 needPrint = FALSE;
360
361 /*
362 * Copy the command so we can modify it.
363 */
364 strcpy(buf, cmd);
365 cp = buf;
366
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000367 if (isblank(*cp) || (*cp == '\0')) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000368 bb_error_msg("bad delimiter for substitute");
Rob Landley3b890392006-05-04 20:56:43 +0000369 return;
370 }
371
372 delim = *cp++;
373 oldStr = cp;
374
375 cp = strchr(cp, delim);
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000376 if (cp == NULL) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000377 bb_error_msg("missing 2nd delimiter for substitute");
Rob Landley3b890392006-05-04 20:56:43 +0000378 return;
379 }
380
381 *cp++ = '\0';
382
383 newStr = cp;
384 cp = strchr(cp, delim);
385
386 if (cp)
387 *cp++ = '\0';
388 else
Denis Vlasenkoa41fdf32007-01-29 22:51:00 +0000389 cp = (char*)"";
Rob Landley3b890392006-05-04 20:56:43 +0000390
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000391 while (*cp) switch (*cp++) {
Rob Landley3b890392006-05-04 20:56:43 +0000392 case 'g':
393 globalFlag = TRUE;
394 break;
Rob Landley3b890392006-05-04 20:56:43 +0000395 case 'p':
396 printFlag = TRUE;
397 break;
Rob Landley3b890392006-05-04 20:56:43 +0000398 default:
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000399 bb_error_msg("unknown option for substitute");
Rob Landley3b890392006-05-04 20:56:43 +0000400 return;
401 }
402
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000403 if (*oldStr == '\0') {
404 if (searchString[0] == '\0') {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000405 bb_error_msg("no previous search string");
Rob Landley3b890392006-05-04 20:56:43 +0000406 return;
407 }
Rob Landley3b890392006-05-04 20:56:43 +0000408 oldStr = searchString;
409 }
410
411 if (oldStr != searchString)
412 strcpy(searchString, oldStr);
413
414 lp = findLine(num1);
Rob Landley3b890392006-05-04 20:56:43 +0000415 if (lp == NULL)
416 return;
417
418 oldLen = strlen(oldStr);
419 newLen = strlen(newStr);
420 deltaLen = newLen - oldLen;
421 offset = 0;
422 nlp = NULL;
423
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000424 while (num1 <= num2) {
Rob Landley3b890392006-05-04 20:56:43 +0000425 offset = findString(lp, oldStr, oldLen, offset);
426
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000427 if (offset < 0) {
428 if (needPrint) {
Rob Landley3b890392006-05-04 20:56:43 +0000429 printLines(num1, num1, FALSE);
430 needPrint = FALSE;
431 }
Rob Landley3b890392006-05-04 20:56:43 +0000432 offset = 0;
433 lp = lp->next;
434 num1++;
Rob Landley3b890392006-05-04 20:56:43 +0000435 continue;
436 }
437
438 needPrint = printFlag;
439 didSub = TRUE;
440 dirty = TRUE;
441
442 /*
443 * If the replacement string is the same size or shorter
444 * than the old string, then the substitution is easy.
445 */
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000446 if (deltaLen <= 0) {
Rob Landley3b890392006-05-04 20:56:43 +0000447 memcpy(&lp->data[offset], newStr, newLen);
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000448 if (deltaLen) {
Rob Landley3b890392006-05-04 20:56:43 +0000449 memcpy(&lp->data[offset + newLen],
450 &lp->data[offset + oldLen],
451 lp->len - offset - oldLen);
452
453 lp->len += deltaLen;
454 }
Rob Landley3b890392006-05-04 20:56:43 +0000455 offset += newLen;
Rob Landley3b890392006-05-04 20:56:43 +0000456 if (globalFlag)
457 continue;
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000458 if (needPrint) {
Rob Landley3b890392006-05-04 20:56:43 +0000459 printLines(num1, num1, FALSE);
460 needPrint = FALSE;
461 }
Rob Landley3b890392006-05-04 20:56:43 +0000462 lp = lp->next;
463 num1++;
Rob Landley3b890392006-05-04 20:56:43 +0000464 continue;
465 }
466
467 /*
468 * The new string is larger, so allocate a new line
Marek Polacekb0b88842011-04-16 17:33:43 +0200469 * structure and use that. Link it in place of
Rob Landley3b890392006-05-04 20:56:43 +0000470 * the old line structure.
471 */
Denys Vlasenko90377872010-01-08 09:07:50 +0100472 nlp = xmalloc(sizeof(LINE) + lp->len + deltaLen);
Rob Landley3b890392006-05-04 20:56:43 +0000473
474 nlp->len = lp->len + deltaLen;
475
476 memcpy(nlp->data, lp->data, offset);
Rob Landley3b890392006-05-04 20:56:43 +0000477 memcpy(&nlp->data[offset], newStr, newLen);
Rob Landley3b890392006-05-04 20:56:43 +0000478 memcpy(&nlp->data[offset + newLen],
479 &lp->data[offset + oldLen],
480 lp->len - offset - oldLen);
481
482 nlp->next = lp->next;
483 nlp->prev = lp->prev;
484 nlp->prev->next = nlp;
485 nlp->next->prev = nlp;
486
487 if (curLine == lp)
488 curLine = nlp;
489
490 free(lp);
491 lp = nlp;
492
493 offset += newLen;
494
495 if (globalFlag)
496 continue;
497
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000498 if (needPrint) {
Rob Landley3b890392006-05-04 20:56:43 +0000499 printLines(num1, num1, FALSE);
500 needPrint = FALSE;
501 }
502
503 lp = lp->next;
504 num1++;
505 }
506
507 if (!didSub)
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000508 bb_error_msg("no substitutions found for \"%s\"", oldStr);
Rob Landley3b890392006-05-04 20:56:43 +0000509}
510
511
512/*
513 * Search a line for the specified string starting at the specified
514 * offset in the line. Returns the offset of the found string, or -1.
515 */
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000516static int findString(const LINE *lp, const char *str, int len, int offset)
Rob Landley3b890392006-05-04 20:56:43 +0000517{
518 int left;
519 const char *cp, *ncp;
520
521 cp = &lp->data[offset];
522 left = lp->len - offset;
523
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000524 while (left >= len) {
Rob Landley3b890392006-05-04 20:56:43 +0000525 ncp = memchr(cp, *str, left);
Rob Landley3b890392006-05-04 20:56:43 +0000526 if (ncp == NULL)
527 return -1;
Rob Landley3b890392006-05-04 20:56:43 +0000528 left -= (ncp - cp);
Rob Landley3b890392006-05-04 20:56:43 +0000529 if (left < len)
530 return -1;
Rob Landley3b890392006-05-04 20:56:43 +0000531 cp = ncp;
Rob Landley3b890392006-05-04 20:56:43 +0000532 if (memcmp(cp, str, len) == 0)
533 return (cp - lp->data);
Rob Landley3b890392006-05-04 20:56:43 +0000534 cp++;
535 left--;
536 }
537
538 return -1;
539}
540
541
542/*
543 * Add lines which are typed in by the user.
544 * The lines are inserted just before the specified line number.
545 * The lines are terminated by a line containing a single dot (ugly!),
546 * or by an end of file.
547 */
548static void addLines(int num)
549{
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000550 int len;
551 char buf[USERSIZE + 1];
Rob Landley3b890392006-05-04 20:56:43 +0000552
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000553 while (1) {
554 /* Returns:
555 * -1 on read errors or EOF, or on bare Ctrl-D.
556 * 0 on ctrl-C,
557 * >0 length of input string, including terminating '\n'
558 */
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100559 len = read_line_input(NULL, "", buf, sizeof(buf), /*timeout*/ -1);
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000560 if (len <= 0) {
561 /* Previously, ctrl-C was exiting to shell.
562 * Now we exit to ed prompt. Is in important? */
Rob Landley3b890392006-05-04 20:56:43 +0000563 return;
564 }
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000565 if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0'))
566 return;
Rob Landley3b890392006-05-04 20:56:43 +0000567 if (!insertLine(num++, buf, len))
568 return;
569 }
570}
571
572
573/*
574 * Parse a line number argument if it is present. This is a sum
575 * or difference of numbers, '.', '$', 'x, or a search string.
Bernhard Reutner-Fischerd9ed35c2006-05-19 12:38:30 +0000576 * Returns TRUE if successful (whether or not there was a number).
Rob Landley3b890392006-05-04 20:56:43 +0000577 * Returns FALSE if there was a parsing error, with a message output.
578 * Whether there was a number is returned indirectly, as is the number.
579 * The character pointer which stopped the scan is also returned.
580 */
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000581static int getNum(const char **retcp, smallint *retHaveNum, int *retNum)
Rob Landley3b890392006-05-04 20:56:43 +0000582{
583 const char *cp;
584 char *endStr, str[USERSIZE];
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000585 int value, num;
586 smallint haveNum, minus;
Rob Landley3b890392006-05-04 20:56:43 +0000587
588 cp = *retcp;
Rob Landley3b890392006-05-04 20:56:43 +0000589 value = 0;
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000590 haveNum = FALSE;
591 minus = 0;
Rob Landley3b890392006-05-04 20:56:43 +0000592
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000593 while (TRUE) {
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000594 cp = skip_blank(cp);
Rob Landley3b890392006-05-04 20:56:43 +0000595
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000596 switch (*cp) {
Rob Landley3b890392006-05-04 20:56:43 +0000597 case '.':
598 haveNum = TRUE;
599 num = curNum;
600 cp++;
601 break;
602
603 case '$':
604 haveNum = TRUE;
605 num = lastNum;
606 cp++;
607 break;
608
609 case '\'':
610 cp++;
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000611 if ((*cp < 'a') || (*cp > 'z')) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000612 bb_error_msg("bad mark name");
Rob Landley3b890392006-05-04 20:56:43 +0000613 return FALSE;
614 }
Rob Landley3b890392006-05-04 20:56:43 +0000615 haveNum = TRUE;
616 num = marks[*cp++ - 'a'];
617 break;
618
619 case '/':
620 strcpy(str, ++cp);
621 endStr = strchr(str, '/');
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000622 if (endStr) {
Rob Landley3b890392006-05-04 20:56:43 +0000623 *endStr++ = '\0';
624 cp += (endStr - str);
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000625 } else
Rob Landley3b890392006-05-04 20:56:43 +0000626 cp = "";
Rob Landley3b890392006-05-04 20:56:43 +0000627 num = searchLines(str, curNum, lastNum);
Rob Landley3b890392006-05-04 20:56:43 +0000628 if (num == 0)
629 return FALSE;
Rob Landley3b890392006-05-04 20:56:43 +0000630 haveNum = TRUE;
631 break;
632
633 default:
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000634 if (!isdigit(*cp)) {
Rob Landley3b890392006-05-04 20:56:43 +0000635 *retcp = cp;
636 *retHaveNum = haveNum;
637 *retNum = value;
Rob Landley3b890392006-05-04 20:56:43 +0000638 return TRUE;
639 }
Rob Landley3b890392006-05-04 20:56:43 +0000640 num = 0;
Rob Landley3b890392006-05-04 20:56:43 +0000641 while (isdigit(*cp))
642 num = num * 10 + *cp++ - '0';
Rob Landley3b890392006-05-04 20:56:43 +0000643 haveNum = TRUE;
644 break;
645 }
646
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000647 value += (minus ? -num : num);
Rob Landley3b890392006-05-04 20:56:43 +0000648
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000649 cp = skip_blank(cp);
Rob Landley3b890392006-05-04 20:56:43 +0000650
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000651 switch (*cp) {
Rob Landley3b890392006-05-04 20:56:43 +0000652 case '-':
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000653 minus = 1;
Rob Landley3b890392006-05-04 20:56:43 +0000654 cp++;
655 break;
656
657 case '+':
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000658 minus = 0;
Rob Landley3b890392006-05-04 20:56:43 +0000659 cp++;
660 break;
661
662 default:
663 *retcp = cp;
664 *retHaveNum = haveNum;
665 *retNum = value;
Rob Landley3b890392006-05-04 20:56:43 +0000666 return TRUE;
667 }
668 }
669}
670
671
672/*
Rob Landley3b890392006-05-04 20:56:43 +0000673 * Read lines from a file at the specified line number.
674 * Returns TRUE if the file was successfully read.
675 */
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000676static int readLines(const char *file, int num)
Rob Landley3b890392006-05-04 20:56:43 +0000677{
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000678 int fd, cc;
Rob Landley3b890392006-05-04 20:56:43 +0000679 int len, lineCount, charCount;
680 char *cp;
681
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000682 if ((num < 1) || (num > lastNum + 1)) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000683 bb_error_msg("bad line for read");
Rob Landley3b890392006-05-04 20:56:43 +0000684 return FALSE;
685 }
686
687 fd = open(file, 0);
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000688 if (fd < 0) {
Marek Polacek7dd61e32010-09-12 17:06:43 +0200689 bb_simple_perror_msg(file);
Rob Landley3b890392006-05-04 20:56:43 +0000690 return FALSE;
691 }
692
693 bufPtr = bufBase;
694 bufUsed = 0;
695 lineCount = 0;
696 charCount = 0;
697 cc = 0;
698
699 printf("\"%s\", ", file);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100700 fflush_all();
Rob Landley3b890392006-05-04 20:56:43 +0000701
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000702 do {
Rob Landley3b890392006-05-04 20:56:43 +0000703 cp = memchr(bufPtr, '\n', bufUsed);
704
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000705 if (cp) {
Rob Landley3b890392006-05-04 20:56:43 +0000706 len = (cp - bufPtr) + 1;
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000707 if (!insertLine(num, bufPtr, len)) {
Rob Landley3b890392006-05-04 20:56:43 +0000708 close(fd);
Rob Landley3b890392006-05-04 20:56:43 +0000709 return FALSE;
710 }
Rob Landley3b890392006-05-04 20:56:43 +0000711 bufPtr += len;
712 bufUsed -= len;
713 charCount += len;
714 lineCount++;
715 num++;
Rob Landley3b890392006-05-04 20:56:43 +0000716 continue;
717 }
718
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000719 if (bufPtr != bufBase) {
Rob Landley3b890392006-05-04 20:56:43 +0000720 memcpy(bufBase, bufPtr, bufUsed);
721 bufPtr = bufBase + bufUsed;
722 }
723
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000724 if (bufUsed >= bufSize) {
Rob Landley3b890392006-05-04 20:56:43 +0000725 len = (bufSize * 3) / 2;
Denys Vlasenko90377872010-01-08 09:07:50 +0100726 cp = xrealloc(bufBase, len);
Rob Landley3b890392006-05-04 20:56:43 +0000727 bufBase = cp;
728 bufPtr = bufBase + bufUsed;
729 bufSize = len;
730 }
731
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000732 cc = safe_read(fd, bufPtr, bufSize - bufUsed);
Rob Landley3b890392006-05-04 20:56:43 +0000733 bufUsed += cc;
734 bufPtr = bufBase;
735
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000736 } while (cc > 0);
Rob Landley3b890392006-05-04 20:56:43 +0000737
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000738 if (cc < 0) {
Marek Polacek7dd61e32010-09-12 17:06:43 +0200739 bb_simple_perror_msg(file);
Rob Landley3b890392006-05-04 20:56:43 +0000740 close(fd);
Rob Landley3b890392006-05-04 20:56:43 +0000741 return FALSE;
742 }
743
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000744 if (bufUsed) {
745 if (!insertLine(num, bufPtr, bufUsed)) {
Rob Landley3b890392006-05-04 20:56:43 +0000746 close(fd);
Rob Landley3b890392006-05-04 20:56:43 +0000747 return -1;
748 }
Rob Landley3b890392006-05-04 20:56:43 +0000749 lineCount++;
750 charCount += bufUsed;
751 }
752
753 close(fd);
754
755 printf("%d lines%s, %d chars\n", lineCount,
756 (bufUsed ? " (incomplete)" : ""), charCount);
757
758 return TRUE;
759}
760
761
762/*
763 * Write the specified lines out to the specified file.
764 * Returns TRUE if successful, or FALSE on an error with a message output.
765 */
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000766static int writeLines(const char *file, int num1, int num2)
Rob Landley3b890392006-05-04 20:56:43 +0000767{
768 LINE *lp;
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000769 int fd, lineCount, charCount;
Rob Landley3b890392006-05-04 20:56:43 +0000770
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000771 if (bad_nums(num1, num2, "write"))
Rob Landley3b890392006-05-04 20:56:43 +0000772 return FALSE;
Rob Landley3b890392006-05-04 20:56:43 +0000773
774 lineCount = 0;
775 charCount = 0;
776
777 fd = creat(file, 0666);
Rob Landley3b890392006-05-04 20:56:43 +0000778 if (fd < 0) {
Marek Polacek7dd61e32010-09-12 17:06:43 +0200779 bb_simple_perror_msg(file);
Rob Landley3b890392006-05-04 20:56:43 +0000780 return FALSE;
781 }
782
783 printf("\"%s\", ", file);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100784 fflush_all();
Rob Landley3b890392006-05-04 20:56:43 +0000785
786 lp = findLine(num1);
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000787 if (lp == NULL) {
Rob Landley3b890392006-05-04 20:56:43 +0000788 close(fd);
Rob Landley3b890392006-05-04 20:56:43 +0000789 return FALSE;
790 }
791
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000792 while (num1++ <= num2) {
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000793 if (full_write(fd, lp->data, lp->len) != lp->len) {
Marek Polacek7dd61e32010-09-12 17:06:43 +0200794 bb_simple_perror_msg(file);
Rob Landley3b890392006-05-04 20:56:43 +0000795 close(fd);
Rob Landley3b890392006-05-04 20:56:43 +0000796 return FALSE;
797 }
Rob Landley3b890392006-05-04 20:56:43 +0000798 charCount += lp->len;
799 lineCount++;
800 lp = lp->next;
801 }
802
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000803 if (close(fd) < 0) {
Marek Polacek7dd61e32010-09-12 17:06:43 +0200804 bb_simple_perror_msg(file);
Rob Landley3b890392006-05-04 20:56:43 +0000805 return FALSE;
806 }
807
808 printf("%d lines, %d chars\n", lineCount, charCount);
Rob Landley3b890392006-05-04 20:56:43 +0000809 return TRUE;
810}
811
812
813/*
814 * Print lines in a specified range.
815 * The last line printed becomes the current line.
816 * If expandFlag is TRUE, then the line is printed specially to
817 * show magic characters.
818 */
819static int printLines(int num1, int num2, int expandFlag)
820{
821 const LINE *lp;
822 const char *cp;
823 int ch, count;
824
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000825 if (bad_nums(num1, num2, "print"))
Rob Landley3b890392006-05-04 20:56:43 +0000826 return FALSE;
Rob Landley3b890392006-05-04 20:56:43 +0000827
828 lp = findLine(num1);
Rob Landley3b890392006-05-04 20:56:43 +0000829 if (lp == NULL)
830 return FALSE;
831
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000832 while (num1 <= num2) {
833 if (!expandFlag) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +0000834 write(STDOUT_FILENO, lp->data, lp->len);
Rob Landley3b890392006-05-04 20:56:43 +0000835 setCurNum(num1++);
836 lp = lp->next;
Rob Landley3b890392006-05-04 20:56:43 +0000837 continue;
838 }
839
840 /*
841 * Show control characters and characters with the
842 * high bit set specially.
843 */
844 cp = lp->data;
845 count = lp->len;
846
847 if ((count > 0) && (cp[count - 1] == '\n'))
848 count--;
849
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000850 while (count-- > 0) {
Denis Vlasenkod3c042f2007-12-30 01:59:53 +0000851 ch = (unsigned char) *cp++;
852 fputc_printable(ch | PRINTABLE_META, stdout);
Rob Landley3b890392006-05-04 20:56:43 +0000853 }
854
855 fputs("$\n", stdout);
856
857 setCurNum(num1++);
858 lp = lp->next;
859 }
860
861 return TRUE;
862}
863
864
865/*
866 * Insert a new line with the specified text.
867 * The line is inserted so as to become the specified line,
868 * thus pushing any existing and further lines down one.
869 * The inserted line is also set to become the current line.
870 * Returns TRUE if successful.
871 */
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000872static int insertLine(int num, const char *data, int len)
Rob Landley3b890392006-05-04 20:56:43 +0000873{
874 LINE *newLp, *lp;
875
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000876 if ((num < 1) || (num > lastNum + 1)) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000877 bb_error_msg("inserting at bad line number");
Rob Landley3b890392006-05-04 20:56:43 +0000878 return FALSE;
879 }
880
Denys Vlasenko90377872010-01-08 09:07:50 +0100881 newLp = xmalloc(sizeof(LINE) + len - 1);
Rob Landley3b890392006-05-04 20:56:43 +0000882
883 memcpy(newLp->data, data, len);
884 newLp->len = len;
885
886 if (num > lastNum)
887 lp = &lines;
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000888 else {
Rob Landley3b890392006-05-04 20:56:43 +0000889 lp = findLine(num);
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000890 if (lp == NULL) {
Rob Landley3b890392006-05-04 20:56:43 +0000891 free((char *) newLp);
Rob Landley3b890392006-05-04 20:56:43 +0000892 return FALSE;
893 }
894 }
895
896 newLp->next = lp;
897 newLp->prev = lp->prev;
898 lp->prev->next = newLp;
899 lp->prev = newLp;
900
901 lastNum++;
902 dirty = TRUE;
Rob Landley3b890392006-05-04 20:56:43 +0000903 return setCurNum(num);
904}
905
906
907/*
908 * Delete lines from the given range.
909 */
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000910static void deleteLines(int num1, int num2)
Rob Landley3b890392006-05-04 20:56:43 +0000911{
912 LINE *lp, *nlp, *plp;
913 int count;
914
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000915 if (bad_nums(num1, num2, "delete"))
916 return;
Rob Landley3b890392006-05-04 20:56:43 +0000917
918 lp = findLine(num1);
Rob Landley3b890392006-05-04 20:56:43 +0000919 if (lp == NULL)
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000920 return;
Rob Landley3b890392006-05-04 20:56:43 +0000921
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000922 if ((curNum >= num1) && (curNum <= num2)) {
Rob Landley3b890392006-05-04 20:56:43 +0000923 if (num2 < lastNum)
924 setCurNum(num2 + 1);
925 else if (num1 > 1)
926 setCurNum(num1 - 1);
927 else
928 curNum = 0;
929 }
930
931 count = num2 - num1 + 1;
Rob Landley3b890392006-05-04 20:56:43 +0000932 if (curNum > num2)
933 curNum -= count;
Rob Landley3b890392006-05-04 20:56:43 +0000934 lastNum -= count;
935
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000936 while (count-- > 0) {
Rob Landley3b890392006-05-04 20:56:43 +0000937 nlp = lp->next;
938 plp = lp->prev;
939 plp->next = nlp;
940 nlp->prev = plp;
Rob Landley3b890392006-05-04 20:56:43 +0000941 free(lp);
942 lp = nlp;
943 }
944
945 dirty = TRUE;
Rob Landley3b890392006-05-04 20:56:43 +0000946}
947
948
949/*
950 * Search for a line which contains the specified string.
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000951 * If the string is "", then the previously searched for string
Rob Landley3b890392006-05-04 20:56:43 +0000952 * is used. The currently searched for string is saved for future use.
953 * Returns the line number which matches, or 0 if there was no match
954 * with an error printed.
955 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +0200956static NOINLINE int searchLines(const char *str, int num1, int num2)
Rob Landley3b890392006-05-04 20:56:43 +0000957{
958 const LINE *lp;
959 int len;
960
Denis Vlasenkod9391b12007-09-25 11:55:57 +0000961 if (bad_nums(num1, num2, "search"))
Rob Landley3b890392006-05-04 20:56:43 +0000962 return 0;
Rob Landley3b890392006-05-04 20:56:43 +0000963
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000964 if (*str == '\0') {
965 if (searchString[0] == '\0') {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +0000966 bb_error_msg("no previous search string");
Rob Landley3b890392006-05-04 20:56:43 +0000967 return 0;
968 }
Rob Landley3b890392006-05-04 20:56:43 +0000969 str = searchString;
970 }
971
972 if (str != searchString)
973 strcpy(searchString, str);
974
975 len = strlen(str);
976
977 lp = findLine(num1);
Rob Landley3b890392006-05-04 20:56:43 +0000978 if (lp == NULL)
979 return 0;
980
Denis Vlasenko610c4aa2006-11-30 20:57:50 +0000981 while (num1 <= num2) {
Rob Landley3b890392006-05-04 20:56:43 +0000982 if (findString(lp, str, len, 0) >= 0)
983 return num1;
Rob Landley3b890392006-05-04 20:56:43 +0000984 num1++;
985 lp = lp->next;
986 }
987
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100988 bb_error_msg("can't find string \"%s\"", str);
Rob Landley3b890392006-05-04 20:56:43 +0000989 return 0;
990}
991
992
993/*
994 * Return a pointer to the specified line number.
995 */
996static LINE *findLine(int num)
997{
998 LINE *lp;
999 int lnum;
1000
Denis Vlasenko610c4aa2006-11-30 20:57:50 +00001001 if ((num < 1) || (num > lastNum)) {
Denis Vlasenkod3d004d2006-10-27 09:02:31 +00001002 bb_error_msg("line number %d does not exist", num);
Rob Landley3b890392006-05-04 20:56:43 +00001003 return NULL;
1004 }
1005
Denis Vlasenko610c4aa2006-11-30 20:57:50 +00001006 if (curNum <= 0) {
Rob Landley3b890392006-05-04 20:56:43 +00001007 curNum = 1;
1008 curLine = lines.next;
1009 }
1010
1011 if (num == curNum)
1012 return curLine;
1013
1014 lp = curLine;
1015 lnum = curNum;
Denis Vlasenko610c4aa2006-11-30 20:57:50 +00001016 if (num < (curNum / 2)) {
Rob Landley3b890392006-05-04 20:56:43 +00001017 lp = lines.next;
1018 lnum = 1;
Denis Vlasenkod9391b12007-09-25 11:55:57 +00001019 } else if (num > ((curNum + lastNum) / 2)) {
Rob Landley3b890392006-05-04 20:56:43 +00001020 lp = lines.prev;
1021 lnum = lastNum;
1022 }
1023
Denis Vlasenko610c4aa2006-11-30 20:57:50 +00001024 while (lnum < num) {
Rob Landley3b890392006-05-04 20:56:43 +00001025 lp = lp->next;
1026 lnum++;
1027 }
1028
Denis Vlasenko610c4aa2006-11-30 20:57:50 +00001029 while (lnum > num) {
Rob Landley3b890392006-05-04 20:56:43 +00001030 lp = lp->prev;
1031 lnum--;
1032 }
Rob Landley3b890392006-05-04 20:56:43 +00001033 return lp;
1034}
1035
1036
1037/*
1038 * Set the current line number.
1039 * Returns TRUE if successful.
1040 */
1041static int setCurNum(int num)
1042{
1043 LINE *lp;
1044
1045 lp = findLine(num);
Rob Landley3b890392006-05-04 20:56:43 +00001046 if (lp == NULL)
1047 return FALSE;
Rob Landley3b890392006-05-04 20:56:43 +00001048 curNum = num;
1049 curLine = lp;
Rob Landley3b890392006-05-04 20:56:43 +00001050 return TRUE;
1051}