diff options
Diffstat (limited to 'editors')
-rw-r--r-- | editors/awk.c | 3 | ||||
-rw-r--r-- | editors/ed.c | 1118 |
2 files changed, 542 insertions, 579 deletions
diff --git a/editors/awk.c b/editors/awk.c index 602a1d5e7..56688d72c 100644 --- a/editors/awk.c +++ b/editors/awk.c | |||
@@ -11,8 +11,7 @@ | |||
11 | //config: bool "awk (22 kb)" | 11 | //config: bool "awk (22 kb)" |
12 | //config: default y | 12 | //config: default y |
13 | //config: help | 13 | //config: help |
14 | //config: Awk is used as a pattern scanning and processing language. This is | 14 | //config: Awk is used as a pattern scanning and processing language. |
15 | //config: the BusyBox implementation of that programming language. | ||
16 | //config: | 15 | //config: |
17 | //config:config FEATURE_AWK_LIBM | 16 | //config:config FEATURE_AWK_LIBM |
18 | //config: bool "Enable math functions (requires libm)" | 17 | //config: bool "Enable math functions (requires libm)" |
diff --git a/editors/ed.c b/editors/ed.c index c594d3da1..7f21ded92 100644 --- a/editors/ed.c +++ b/editors/ed.c | |||
@@ -6,7 +6,6 @@ | |||
6 | * | 6 | * |
7 | * The "ed" built-in command (much simplified) | 7 | * The "ed" built-in command (much simplified) |
8 | */ | 8 | */ |
9 | |||
10 | //config:config ED | 9 | //config:config ED |
11 | //config: bool "ed (25 kb)" | 10 | //config: bool "ed (25 kb)" |
12 | //config: default y | 11 | //config: default y |
@@ -19,7 +18,7 @@ | |||
19 | 18 | ||
20 | //applet:IF_ED(APPLET(ed, BB_DIR_BIN, BB_SUID_DROP)) | 19 | //applet:IF_ED(APPLET(ed, BB_DIR_BIN, BB_SUID_DROP)) |
21 | 20 | ||
22 | //usage:#define ed_trivial_usage "" | 21 | //usage:#define ed_trivial_usage "[FILE]" |
23 | //usage:#define ed_full_usage "" | 22 | //usage:#define ed_full_usage "" |
24 | 23 | ||
25 | #include "libbb.h" | 24 | #include "libbb.h" |
@@ -32,7 +31,6 @@ typedef struct LINE { | |||
32 | char data[1]; | 31 | char data[1]; |
33 | } LINE; | 32 | } LINE; |
34 | 33 | ||
35 | |||
36 | #define searchString bb_common_bufsiz1 | 34 | #define searchString bb_common_bufsiz1 |
37 | 35 | ||
38 | enum { | 36 | enum { |
@@ -71,22 +69,6 @@ struct globals { | |||
71 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ | 69 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ |
72 | } while (0) | 70 | } while (0) |
73 | 71 | ||
74 | |||
75 | static void doCommands(void); | ||
76 | static void subCommand(const char *cmd, int num1, int num2); | ||
77 | static int getNum(const char **retcp, smallint *retHaveNum, int *retNum); | ||
78 | static int setCurNum(int num); | ||
79 | static void addLines(int num); | ||
80 | static int insertLine(int num, const char *data, int len); | ||
81 | static void deleteLines(int num1, int num2); | ||
82 | static int printLines(int num1, int num2, int expandFlag); | ||
83 | static int writeLines(const char *file, int num1, int num2); | ||
84 | static int readLines(const char *file, int num); | ||
85 | static int searchLines(const char *str, int num1, int num2); | ||
86 | static LINE *findLine(int num); | ||
87 | static int findString(const LINE *lp, const char * str, int len, int offset); | ||
88 | |||
89 | |||
90 | static int bad_nums(int num1, int num2, const char *for_what) | 72 | static int bad_nums(int num1, int num2, const char *for_what) |
91 | { | 73 | { |
92 | if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { | 74 | if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { |
@@ -96,421 +78,49 @@ static int bad_nums(int num1, int num2, const char *for_what) | |||
96 | return 0; | 78 | return 0; |
97 | } | 79 | } |
98 | 80 | ||
99 | |||
100 | static char *skip_blank(const char *cp) | ||
101 | { | ||
102 | while (isblank(*cp)) | ||
103 | cp++; | ||
104 | return (char *)cp; | ||
105 | } | ||
106 | |||
107 | |||
108 | int ed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
109 | int ed_main(int argc UNUSED_PARAM, char **argv) | ||
110 | { | ||
111 | INIT_G(); | ||
112 | |||
113 | bufSize = INITBUF_SIZE; | ||
114 | bufBase = xmalloc(bufSize); | ||
115 | bufPtr = bufBase; | ||
116 | lines.next = &lines; | ||
117 | lines.prev = &lines; | ||
118 | |||
119 | if (argv[1]) { | ||
120 | fileName = xstrdup(argv[1]); | ||
121 | if (!readLines(fileName, 1)) { | ||
122 | return EXIT_SUCCESS; | ||
123 | } | ||
124 | if (lastNum) | ||
125 | setCurNum(1); | ||
126 | dirty = FALSE; | ||
127 | } | ||
128 | |||
129 | doCommands(); | ||
130 | return EXIT_SUCCESS; | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * Read commands until we are told to stop. | ||
135 | */ | ||
136 | static void doCommands(void) | ||
137 | { | ||
138 | const char *cp; | ||
139 | char *endbuf, buf[USERSIZE]; | ||
140 | int len, num1, num2; | ||
141 | smallint have1, have2; | ||
142 | |||
143 | while (TRUE) { | ||
144 | /* Returns: | ||
145 | * -1 on read errors or EOF, or on bare Ctrl-D. | ||
146 | * 0 on ctrl-C, | ||
147 | * >0 length of input string, including terminating '\n' | ||
148 | */ | ||
149 | len = read_line_input(NULL, ": ", buf, sizeof(buf), /*timeout*/ -1); | ||
150 | if (len <= 0) | ||
151 | return; | ||
152 | endbuf = &buf[len - 1]; | ||
153 | while ((endbuf > buf) && isblank(endbuf[-1])) | ||
154 | endbuf--; | ||
155 | *endbuf = '\0'; | ||
156 | |||
157 | cp = skip_blank(buf); | ||
158 | have1 = FALSE; | ||
159 | have2 = FALSE; | ||
160 | |||
161 | if ((curNum == 0) && (lastNum > 0)) { | ||
162 | curNum = 1; | ||
163 | curLine = lines.next; | ||
164 | } | ||
165 | |||
166 | if (!getNum(&cp, &have1, &num1)) | ||
167 | continue; | ||
168 | |||
169 | cp = skip_blank(cp); | ||
170 | |||
171 | if (*cp == ',') { | ||
172 | cp++; | ||
173 | if (!getNum(&cp, &have2, &num2)) | ||
174 | continue; | ||
175 | if (!have1) | ||
176 | num1 = 1; | ||
177 | if (!have2) | ||
178 | num2 = lastNum; | ||
179 | have1 = TRUE; | ||
180 | have2 = TRUE; | ||
181 | } | ||
182 | if (!have1) | ||
183 | num1 = curNum; | ||
184 | if (!have2) | ||
185 | num2 = num1; | ||
186 | |||
187 | switch (*cp++) { | ||
188 | case 'a': | ||
189 | addLines(num1 + 1); | ||
190 | break; | ||
191 | |||
192 | case 'c': | ||
193 | deleteLines(num1, num2); | ||
194 | addLines(num1); | ||
195 | break; | ||
196 | |||
197 | case 'd': | ||
198 | deleteLines(num1, num2); | ||
199 | break; | ||
200 | |||
201 | case 'f': | ||
202 | if (*cp && !isblank(*cp)) { | ||
203 | bb_error_msg("bad file command"); | ||
204 | break; | ||
205 | } | ||
206 | cp = skip_blank(cp); | ||
207 | if (*cp == '\0') { | ||
208 | if (fileName) | ||
209 | printf("\"%s\"\n", fileName); | ||
210 | else | ||
211 | puts("No file name"); | ||
212 | break; | ||
213 | } | ||
214 | free(fileName); | ||
215 | fileName = xstrdup(cp); | ||
216 | break; | ||
217 | |||
218 | case 'i': | ||
219 | addLines(num1); | ||
220 | break; | ||
221 | |||
222 | case 'k': | ||
223 | cp = skip_blank(cp); | ||
224 | if ((*cp < 'a') || (*cp > 'z') || cp[1]) { | ||
225 | bb_error_msg("bad mark name"); | ||
226 | break; | ||
227 | } | ||
228 | marks[*cp - 'a'] = num2; | ||
229 | break; | ||
230 | |||
231 | case 'l': | ||
232 | printLines(num1, num2, TRUE); | ||
233 | break; | ||
234 | |||
235 | case 'p': | ||
236 | printLines(num1, num2, FALSE); | ||
237 | break; | ||
238 | |||
239 | case 'q': | ||
240 | cp = skip_blank(cp); | ||
241 | if (have1 || *cp) { | ||
242 | bb_error_msg("bad quit command"); | ||
243 | break; | ||
244 | } | ||
245 | if (!dirty) | ||
246 | return; | ||
247 | len = read_line_input(NULL, "Really quit? ", buf, 16, /*timeout*/ -1); | ||
248 | /* read error/EOF - no way to continue */ | ||
249 | if (len < 0) | ||
250 | return; | ||
251 | cp = skip_blank(buf); | ||
252 | if ((*cp | 0x20) == 'y') /* Y or y */ | ||
253 | return; | ||
254 | break; | ||
255 | |||
256 | case 'r': | ||
257 | if (*cp && !isblank(*cp)) { | ||
258 | bb_error_msg("bad read command"); | ||
259 | break; | ||
260 | } | ||
261 | cp = skip_blank(cp); | ||
262 | if (*cp == '\0') { | ||
263 | bb_error_msg("no file name"); | ||
264 | break; | ||
265 | } | ||
266 | if (!have1) | ||
267 | num1 = lastNum; | ||
268 | if (readLines(cp, num1 + 1)) | ||
269 | break; | ||
270 | if (fileName == NULL) | ||
271 | fileName = xstrdup(cp); | ||
272 | break; | ||
273 | |||
274 | case 's': | ||
275 | subCommand(cp, num1, num2); | ||
276 | break; | ||
277 | |||
278 | case 'w': | ||
279 | if (*cp && !isblank(*cp)) { | ||
280 | bb_error_msg("bad write command"); | ||
281 | break; | ||
282 | } | ||
283 | cp = skip_blank(cp); | ||
284 | if (!have1) { | ||
285 | num1 = 1; | ||
286 | num2 = lastNum; | ||
287 | } | ||
288 | if (*cp == '\0') | ||
289 | cp = fileName; | ||
290 | if (cp == NULL) { | ||
291 | bb_error_msg("no file name specified"); | ||
292 | break; | ||
293 | } | ||
294 | writeLines(cp, num1, num2); | ||
295 | break; | ||
296 | |||
297 | case 'z': | ||
298 | switch (*cp) { | ||
299 | case '-': | ||
300 | printLines(curNum - 21, curNum, FALSE); | ||
301 | break; | ||
302 | case '.': | ||
303 | printLines(curNum - 11, curNum + 10, FALSE); | ||
304 | break; | ||
305 | default: | ||
306 | printLines(curNum, curNum + 21, FALSE); | ||
307 | break; | ||
308 | } | ||
309 | break; | ||
310 | |||
311 | case '.': | ||
312 | if (have1) { | ||
313 | bb_error_msg("no arguments allowed"); | ||
314 | break; | ||
315 | } | ||
316 | printLines(curNum, curNum, FALSE); | ||
317 | break; | ||
318 | |||
319 | case '-': | ||
320 | if (setCurNum(curNum - 1)) | ||
321 | printLines(curNum, curNum, FALSE); | ||
322 | break; | ||
323 | |||
324 | case '=': | ||
325 | printf("%d\n", num1); | ||
326 | break; | ||
327 | case '\0': | ||
328 | if (have1) { | ||
329 | printLines(num2, num2, FALSE); | ||
330 | break; | ||
331 | } | ||
332 | if (setCurNum(curNum + 1)) | ||
333 | printLines(curNum, curNum, FALSE); | ||
334 | break; | ||
335 | |||
336 | default: | ||
337 | bb_error_msg("unimplemented command"); | ||
338 | break; | ||
339 | } | ||
340 | } | ||
341 | } | ||
342 | |||
343 | |||
344 | /* | 81 | /* |
345 | * Do the substitute command. | 82 | * Return a pointer to the specified line number. |
346 | * The current line is set to the last substitution done. | ||
347 | */ | 83 | */ |
348 | static void subCommand(const char *cmd, int num1, int num2) | 84 | static LINE *findLine(int num) |
349 | { | 85 | { |
350 | char *cp, *oldStr, *newStr, buf[USERSIZE]; | 86 | LINE *lp; |
351 | int delim, oldLen, newLen, deltaLen, offset; | 87 | int lnum; |
352 | LINE *lp, *nlp; | ||
353 | int globalFlag, printFlag, didSub, needPrint; | ||
354 | |||
355 | if (bad_nums(num1, num2, "substitute")) | ||
356 | return; | ||
357 | |||
358 | globalFlag = FALSE; | ||
359 | printFlag = FALSE; | ||
360 | didSub = FALSE; | ||
361 | needPrint = FALSE; | ||
362 | |||
363 | /* | ||
364 | * Copy the command so we can modify it. | ||
365 | */ | ||
366 | strcpy(buf, cmd); | ||
367 | cp = buf; | ||
368 | 88 | ||
369 | if (isblank(*cp) || (*cp == '\0')) { | 89 | if ((num < 1) || (num > lastNum)) { |
370 | bb_error_msg("bad delimiter for substitute"); | 90 | bb_error_msg("line number %d does not exist", num); |
371 | return; | 91 | return NULL; |
372 | } | 92 | } |
373 | 93 | ||
374 | delim = *cp++; | 94 | if (curNum <= 0) { |
375 | oldStr = cp; | 95 | curNum = 1; |
376 | 96 | curLine = lines.next; | |
377 | cp = strchr(cp, delim); | ||
378 | if (cp == NULL) { | ||
379 | bb_error_msg("missing 2nd delimiter for substitute"); | ||
380 | return; | ||
381 | } | 97 | } |
382 | 98 | ||
383 | *cp++ = '\0'; | 99 | if (num == curNum) |
384 | 100 | return curLine; | |
385 | newStr = cp; | ||
386 | cp = strchr(cp, delim); | ||
387 | |||
388 | if (cp) | ||
389 | *cp++ = '\0'; | ||
390 | else | ||
391 | cp = (char*)""; | ||
392 | |||
393 | while (*cp) switch (*cp++) { | ||
394 | case 'g': | ||
395 | globalFlag = TRUE; | ||
396 | break; | ||
397 | case 'p': | ||
398 | printFlag = TRUE; | ||
399 | break; | ||
400 | default: | ||
401 | bb_error_msg("unknown option for substitute"); | ||
402 | return; | ||
403 | } | ||
404 | 101 | ||
405 | if (*oldStr == '\0') { | 102 | lp = curLine; |
406 | if (searchString[0] == '\0') { | 103 | lnum = curNum; |
407 | bb_error_msg("no previous search string"); | 104 | if (num < (curNum / 2)) { |
408 | return; | 105 | lp = lines.next; |
409 | } | 106 | lnum = 1; |
410 | oldStr = searchString; | 107 | } else if (num > ((curNum + lastNum) / 2)) { |
108 | lp = lines.prev; | ||
109 | lnum = lastNum; | ||
411 | } | 110 | } |
412 | 111 | ||
413 | if (oldStr != searchString) | 112 | while (lnum < num) { |
414 | strcpy(searchString, oldStr); | ||
415 | |||
416 | lp = findLine(num1); | ||
417 | if (lp == NULL) | ||
418 | return; | ||
419 | |||
420 | oldLen = strlen(oldStr); | ||
421 | newLen = strlen(newStr); | ||
422 | deltaLen = newLen - oldLen; | ||
423 | offset = 0; | ||
424 | nlp = NULL; | ||
425 | |||
426 | while (num1 <= num2) { | ||
427 | offset = findString(lp, oldStr, oldLen, offset); | ||
428 | |||
429 | if (offset < 0) { | ||
430 | if (needPrint) { | ||
431 | printLines(num1, num1, FALSE); | ||
432 | needPrint = FALSE; | ||
433 | } | ||
434 | offset = 0; | ||
435 | lp = lp->next; | ||
436 | num1++; | ||
437 | continue; | ||
438 | } | ||
439 | |||
440 | needPrint = printFlag; | ||
441 | didSub = TRUE; | ||
442 | dirty = TRUE; | ||
443 | |||
444 | /* | ||
445 | * If the replacement string is the same size or shorter | ||
446 | * than the old string, then the substitution is easy. | ||
447 | */ | ||
448 | if (deltaLen <= 0) { | ||
449 | memcpy(&lp->data[offset], newStr, newLen); | ||
450 | if (deltaLen) { | ||
451 | memcpy(&lp->data[offset + newLen], | ||
452 | &lp->data[offset + oldLen], | ||
453 | lp->len - offset - oldLen); | ||
454 | |||
455 | lp->len += deltaLen; | ||
456 | } | ||
457 | offset += newLen; | ||
458 | if (globalFlag) | ||
459 | continue; | ||
460 | if (needPrint) { | ||
461 | printLines(num1, num1, FALSE); | ||
462 | needPrint = FALSE; | ||
463 | } | ||
464 | lp = lp->next; | ||
465 | num1++; | ||
466 | continue; | ||
467 | } | ||
468 | |||
469 | /* | ||
470 | * The new string is larger, so allocate a new line | ||
471 | * structure and use that. Link it in place of | ||
472 | * the old line structure. | ||
473 | */ | ||
474 | nlp = xmalloc(sizeof(LINE) + lp->len + deltaLen); | ||
475 | |||
476 | nlp->len = lp->len + deltaLen; | ||
477 | |||
478 | memcpy(nlp->data, lp->data, offset); | ||
479 | memcpy(&nlp->data[offset], newStr, newLen); | ||
480 | memcpy(&nlp->data[offset + newLen], | ||
481 | &lp->data[offset + oldLen], | ||
482 | lp->len - offset - oldLen); | ||
483 | |||
484 | nlp->next = lp->next; | ||
485 | nlp->prev = lp->prev; | ||
486 | nlp->prev->next = nlp; | ||
487 | nlp->next->prev = nlp; | ||
488 | |||
489 | if (curLine == lp) | ||
490 | curLine = nlp; | ||
491 | |||
492 | free(lp); | ||
493 | lp = nlp; | ||
494 | |||
495 | offset += newLen; | ||
496 | |||
497 | if (globalFlag) | ||
498 | continue; | ||
499 | |||
500 | if (needPrint) { | ||
501 | printLines(num1, num1, FALSE); | ||
502 | needPrint = FALSE; | ||
503 | } | ||
504 | |||
505 | lp = lp->next; | 113 | lp = lp->next; |
506 | num1++; | 114 | lnum++; |
507 | } | 115 | } |
508 | 116 | ||
509 | if (!didSub) | 117 | while (lnum > num) { |
510 | bb_error_msg("no substitutions found for \"%s\"", oldStr); | 118 | lp = lp->prev; |
119 | lnum--; | ||
120 | } | ||
121 | return lp; | ||
511 | } | 122 | } |
512 | 123 | ||
513 | |||
514 | /* | 124 | /* |
515 | * Search a line for the specified string starting at the specified | 125 | * Search a line for the specified string starting at the specified |
516 | * offset in the line. Returns the offset of the found string, or -1. | 126 | * offset in the line. Returns the offset of the found string, or -1. |
@@ -521,15 +131,13 @@ static int findString(const LINE *lp, const char *str, int len, int offset) | |||
521 | const char *cp, *ncp; | 131 | const char *cp, *ncp; |
522 | 132 | ||
523 | cp = &lp->data[offset]; | 133 | cp = &lp->data[offset]; |
524 | left = lp->len - offset; | 134 | left = lp->len - offset - len; |
525 | 135 | ||
526 | while (left >= len) { | 136 | while (left >= 0) { |
527 | ncp = memchr(cp, *str, left); | 137 | ncp = memchr(cp, str[0], left + 1); |
528 | if (ncp == NULL) | 138 | if (ncp == NULL) |
529 | return -1; | 139 | return -1; |
530 | left -= (ncp - cp); | 140 | left -= (ncp - cp); |
531 | if (left < len) | ||
532 | return -1; | ||
533 | cp = ncp; | 141 | cp = ncp; |
534 | if (memcmp(cp, str, len) == 0) | 142 | if (memcmp(cp, str, len) == 0) |
535 | return (cp - lp->data); | 143 | return (cp - lp->data); |
@@ -540,60 +148,69 @@ static int findString(const LINE *lp, const char *str, int len, int offset) | |||
540 | return -1; | 148 | return -1; |
541 | } | 149 | } |
542 | 150 | ||
543 | |||
544 | /* | 151 | /* |
545 | * Add lines which are typed in by the user. | 152 | * Search for a line which contains the specified string. |
546 | * The lines are inserted just before the specified line number. | 153 | * If the string is "", then the previously searched for string |
547 | * The lines are terminated by a line containing a single dot (ugly!), | 154 | * is used. The currently searched for string is saved for future use. |
548 | * or by an end of file. | 155 | * Returns the line number which matches, or 0 if there was no match |
156 | * with an error printed. | ||
549 | */ | 157 | */ |
550 | static void addLines(int num) | 158 | static NOINLINE int searchLines(const char *str, int num1, int num2) |
551 | { | 159 | { |
160 | const LINE *lp; | ||
552 | int len; | 161 | int len; |
553 | char buf[USERSIZE + 1]; | ||
554 | 162 | ||
555 | while (1) { | 163 | if (bad_nums(num1, num2, "search")) |
556 | /* Returns: | 164 | return 0; |
557 | * -1 on read errors or EOF, or on bare Ctrl-D. | 165 | |
558 | * 0 on ctrl-C, | 166 | if (*str == '\0') { |
559 | * >0 length of input string, including terminating '\n' | 167 | if (searchString[0] == '\0') { |
560 | */ | 168 | bb_error_msg("no previous search string"); |
561 | len = read_line_input(NULL, "", buf, sizeof(buf), /*timeout*/ -1); | 169 | return 0; |
562 | if (len <= 0) { | ||
563 | /* Previously, ctrl-C was exiting to shell. | ||
564 | * Now we exit to ed prompt. Is in important? */ | ||
565 | return; | ||
566 | } | 170 | } |
567 | if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0')) | 171 | str = searchString; |
568 | return; | ||
569 | if (!insertLine(num++, buf, len)) | ||
570 | return; | ||
571 | } | 172 | } |
572 | } | ||
573 | 173 | ||
174 | if (str != searchString) | ||
175 | strcpy(searchString, str); | ||
176 | |||
177 | len = strlen(str); | ||
178 | |||
179 | lp = findLine(num1); | ||
180 | if (lp == NULL) | ||
181 | return 0; | ||
182 | |||
183 | while (num1 <= num2) { | ||
184 | if (findString(lp, str, len, 0) >= 0) | ||
185 | return num1; | ||
186 | num1++; | ||
187 | lp = lp->next; | ||
188 | } | ||
189 | |||
190 | bb_error_msg("can't find string \"%s\"", str); | ||
191 | return 0; | ||
192 | } | ||
574 | 193 | ||
575 | /* | 194 | /* |
576 | * Parse a line number argument if it is present. This is a sum | 195 | * Parse a line number argument if it is present. This is a sum |
577 | * or difference of numbers, '.', '$', 'x, or a search string. | 196 | * or difference of numbers, ".", "$", "'c", or a search string. |
578 | * Returns TRUE if successful (whether or not there was a number). | 197 | * Returns pointer which stopped the scan if successful |
579 | * Returns FALSE if there was a parsing error, with a message output. | 198 | * (whether or not there was a number). |
199 | * Returns NULL if there was a parsing error, with a message output. | ||
580 | * Whether there was a number is returned indirectly, as is the number. | 200 | * Whether there was a number is returned indirectly, as is the number. |
581 | * The character pointer which stopped the scan is also returned. | ||
582 | */ | 201 | */ |
583 | static int getNum(const char **retcp, smallint *retHaveNum, int *retNum) | 202 | static const char* getNum(const char *cp, smallint *retHaveNum, int *retNum) |
584 | { | 203 | { |
585 | const char *cp; | ||
586 | char *endStr, str[USERSIZE]; | 204 | char *endStr, str[USERSIZE]; |
587 | int value, num; | 205 | int value, num; |
588 | smallint haveNum, minus; | 206 | smallint haveNum, minus; |
589 | 207 | ||
590 | cp = *retcp; | ||
591 | value = 0; | 208 | value = 0; |
592 | haveNum = FALSE; | 209 | haveNum = FALSE; |
593 | minus = 0; | 210 | minus = 0; |
594 | 211 | ||
595 | while (TRUE) { | 212 | while (TRUE) { |
596 | cp = skip_blank(cp); | 213 | cp = skip_whitespace(cp); |
597 | 214 | ||
598 | switch (*cp) { | 215 | switch (*cp) { |
599 | case '.': | 216 | case '.': |
@@ -610,12 +227,13 @@ static int getNum(const char **retcp, smallint *retHaveNum, int *retNum) | |||
610 | 227 | ||
611 | case '\'': | 228 | case '\'': |
612 | cp++; | 229 | cp++; |
613 | if ((*cp < 'a') || (*cp > 'z')) { | 230 | if ((unsigned)(*cp - 'a') >= 26) { |
614 | bb_error_msg("bad mark name"); | 231 | bb_error_msg("bad mark name"); |
615 | return FALSE; | 232 | return NULL; |
616 | } | 233 | } |
617 | haveNum = TRUE; | 234 | haveNum = TRUE; |
618 | num = marks[*cp++ - 'a']; | 235 | num = marks[(unsigned)(*cp - 'a')]; |
236 | cp++; | ||
619 | break; | 237 | break; |
620 | 238 | ||
621 | case '/': | 239 | case '/': |
@@ -628,16 +246,15 @@ static int getNum(const char **retcp, smallint *retHaveNum, int *retNum) | |||
628 | cp = ""; | 246 | cp = ""; |
629 | num = searchLines(str, curNum, lastNum); | 247 | num = searchLines(str, curNum, lastNum); |
630 | if (num == 0) | 248 | if (num == 0) |
631 | return FALSE; | 249 | return NULL; |
632 | haveNum = TRUE; | 250 | haveNum = TRUE; |
633 | break; | 251 | break; |
634 | 252 | ||
635 | default: | 253 | default: |
636 | if (!isdigit(*cp)) { | 254 | if (!isdigit(*cp)) { |
637 | *retcp = cp; | ||
638 | *retHaveNum = haveNum; | 255 | *retHaveNum = haveNum; |
639 | *retNum = value; | 256 | *retNum = value; |
640 | return TRUE; | 257 | return cp; |
641 | } | 258 | } |
642 | num = 0; | 259 | num = 0; |
643 | while (isdigit(*cp)) | 260 | while (isdigit(*cp)) |
@@ -648,7 +265,7 @@ static int getNum(const char **retcp, smallint *retHaveNum, int *retNum) | |||
648 | 265 | ||
649 | value += (minus ? -num : num); | 266 | value += (minus ? -num : num); |
650 | 267 | ||
651 | cp = skip_blank(cp); | 268 | cp = skip_whitespace(cp); |
652 | 269 | ||
653 | switch (*cp) { | 270 | switch (*cp) { |
654 | case '-': | 271 | case '-': |
@@ -662,14 +279,99 @@ static int getNum(const char **retcp, smallint *retHaveNum, int *retNum) | |||
662 | break; | 279 | break; |
663 | 280 | ||
664 | default: | 281 | default: |
665 | *retcp = cp; | ||
666 | *retHaveNum = haveNum; | 282 | *retHaveNum = haveNum; |
667 | *retNum = value; | 283 | *retNum = value; |
668 | return TRUE; | 284 | return cp; |
669 | } | 285 | } |
670 | } | 286 | } |
671 | } | 287 | } |
672 | 288 | ||
289 | /* | ||
290 | * Set the current line number. | ||
291 | * Returns TRUE if successful. | ||
292 | */ | ||
293 | static int setCurNum(int num) | ||
294 | { | ||
295 | LINE *lp; | ||
296 | |||
297 | lp = findLine(num); | ||
298 | if (lp == NULL) | ||
299 | return FALSE; | ||
300 | curNum = num; | ||
301 | curLine = lp; | ||
302 | return TRUE; | ||
303 | } | ||
304 | |||
305 | /* | ||
306 | * Insert a new line with the specified text. | ||
307 | * The line is inserted so as to become the specified line, | ||
308 | * thus pushing any existing and further lines down one. | ||
309 | * The inserted line is also set to become the current line. | ||
310 | * Returns TRUE if successful. | ||
311 | */ | ||
312 | static int insertLine(int num, const char *data, int len) | ||
313 | { | ||
314 | LINE *newLp, *lp; | ||
315 | |||
316 | if ((num < 1) || (num > lastNum + 1)) { | ||
317 | bb_error_msg("inserting at bad line number"); | ||
318 | return FALSE; | ||
319 | } | ||
320 | |||
321 | newLp = xmalloc(sizeof(LINE) + len - 1); | ||
322 | |||
323 | memcpy(newLp->data, data, len); | ||
324 | newLp->len = len; | ||
325 | |||
326 | if (num > lastNum) | ||
327 | lp = &lines; | ||
328 | else { | ||
329 | lp = findLine(num); | ||
330 | if (lp == NULL) { | ||
331 | free((char *) newLp); | ||
332 | return FALSE; | ||
333 | } | ||
334 | } | ||
335 | |||
336 | newLp->next = lp; | ||
337 | newLp->prev = lp->prev; | ||
338 | lp->prev->next = newLp; | ||
339 | lp->prev = newLp; | ||
340 | |||
341 | lastNum++; | ||
342 | dirty = TRUE; | ||
343 | return setCurNum(num); | ||
344 | } | ||
345 | |||
346 | /* | ||
347 | * Add lines which are typed in by the user. | ||
348 | * The lines are inserted just before the specified line number. | ||
349 | * The lines are terminated by a line containing a single dot (ugly!), | ||
350 | * or by an end of file. | ||
351 | */ | ||
352 | static void addLines(int num) | ||
353 | { | ||
354 | int len; | ||
355 | char buf[USERSIZE + 1]; | ||
356 | |||
357 | while (1) { | ||
358 | /* Returns: | ||
359 | * -1 on read errors or EOF, or on bare Ctrl-D. | ||
360 | * 0 on ctrl-C, | ||
361 | * >0 length of input string, including terminating '\n' | ||
362 | */ | ||
363 | len = read_line_input(NULL, "", buf, sizeof(buf), /*timeout*/ -1); | ||
364 | if (len <= 0) { | ||
365 | /* Previously, ctrl-C was exiting to shell. | ||
366 | * Now we exit to ed prompt. Is in important? */ | ||
367 | return; | ||
368 | } | ||
369 | if (buf[0] == '.' && buf[1] == '\n' && buf[2] == '\0') | ||
370 | return; | ||
371 | if (!insertLine(num++, buf, len)) | ||
372 | return; | ||
373 | } | ||
374 | } | ||
673 | 375 | ||
674 | /* | 376 | /* |
675 | * Read lines from a file at the specified line number. | 377 | * Read lines from a file at the specified line number. |
@@ -759,7 +461,6 @@ static int readLines(const char *file, int num) | |||
759 | return TRUE; | 461 | return TRUE; |
760 | } | 462 | } |
761 | 463 | ||
762 | |||
763 | /* | 464 | /* |
764 | * Write the specified lines out to the specified file. | 465 | * Write the specified lines out to the specified file. |
765 | * Returns TRUE if successful, or FALSE on an error with a message output. | 466 | * Returns TRUE if successful, or FALSE on an error with a message output. |
@@ -810,7 +511,6 @@ static int writeLines(const char *file, int num1, int num2) | |||
810 | return TRUE; | 511 | return TRUE; |
811 | } | 512 | } |
812 | 513 | ||
813 | |||
814 | /* | 514 | /* |
815 | * Print lines in a specified range. | 515 | * Print lines in a specified range. |
816 | * The last line printed becomes the current line. | 516 | * The last line printed becomes the current line. |
@@ -862,49 +562,6 @@ static int printLines(int num1, int num2, int expandFlag) | |||
862 | return TRUE; | 562 | return TRUE; |
863 | } | 563 | } |
864 | 564 | ||
865 | |||
866 | /* | ||
867 | * Insert a new line with the specified text. | ||
868 | * The line is inserted so as to become the specified line, | ||
869 | * thus pushing any existing and further lines down one. | ||
870 | * The inserted line is also set to become the current line. | ||
871 | * Returns TRUE if successful. | ||
872 | */ | ||
873 | static int insertLine(int num, const char *data, int len) | ||
874 | { | ||
875 | LINE *newLp, *lp; | ||
876 | |||
877 | if ((num < 1) || (num > lastNum + 1)) { | ||
878 | bb_error_msg("inserting at bad line number"); | ||
879 | return FALSE; | ||
880 | } | ||
881 | |||
882 | newLp = xmalloc(sizeof(LINE) + len - 1); | ||
883 | |||
884 | memcpy(newLp->data, data, len); | ||
885 | newLp->len = len; | ||
886 | |||
887 | if (num > lastNum) | ||
888 | lp = &lines; | ||
889 | else { | ||
890 | lp = findLine(num); | ||
891 | if (lp == NULL) { | ||
892 | free((char *) newLp); | ||
893 | return FALSE; | ||
894 | } | ||
895 | } | ||
896 | |||
897 | newLp->next = lp; | ||
898 | newLp->prev = lp->prev; | ||
899 | lp->prev->next = newLp; | ||
900 | lp->prev = newLp; | ||
901 | |||
902 | lastNum++; | ||
903 | dirty = TRUE; | ||
904 | return setCurNum(num); | ||
905 | } | ||
906 | |||
907 | |||
908 | /* | 565 | /* |
909 | * Delete lines from the given range. | 566 | * Delete lines from the given range. |
910 | */ | 567 | */ |
@@ -946,107 +603,414 @@ static void deleteLines(int num1, int num2) | |||
946 | dirty = TRUE; | 603 | dirty = TRUE; |
947 | } | 604 | } |
948 | 605 | ||
949 | |||
950 | /* | 606 | /* |
951 | * Search for a line which contains the specified string. | 607 | * Do the substitute command. |
952 | * If the string is "", then the previously searched for string | 608 | * The current line is set to the last substitution done. |
953 | * is used. The currently searched for string is saved for future use. | ||
954 | * Returns the line number which matches, or 0 if there was no match | ||
955 | * with an error printed. | ||
956 | */ | 609 | */ |
957 | static NOINLINE int searchLines(const char *str, int num1, int num2) | 610 | static void subCommand(const char *cmd, int num1, int num2) |
958 | { | 611 | { |
959 | const LINE *lp; | 612 | char *cp, *oldStr, *newStr, buf[USERSIZE]; |
960 | int len; | 613 | int delim, oldLen, newLen, deltaLen, offset; |
614 | LINE *lp, *nlp; | ||
615 | int globalFlag, printFlag, didSub, needPrint; | ||
961 | 616 | ||
962 | if (bad_nums(num1, num2, "search")) | 617 | if (bad_nums(num1, num2, "substitute")) |
963 | return 0; | 618 | return; |
964 | 619 | ||
965 | if (*str == '\0') { | 620 | globalFlag = FALSE; |
621 | printFlag = FALSE; | ||
622 | didSub = FALSE; | ||
623 | needPrint = FALSE; | ||
624 | |||
625 | /* | ||
626 | * Copy the command so we can modify it. | ||
627 | */ | ||
628 | strcpy(buf, cmd); | ||
629 | cp = buf; | ||
630 | |||
631 | if (isblank(*cp) || (*cp == '\0')) { | ||
632 | bb_error_msg("bad delimiter for substitute"); | ||
633 | return; | ||
634 | } | ||
635 | |||
636 | delim = *cp++; | ||
637 | oldStr = cp; | ||
638 | |||
639 | cp = strchr(cp, delim); | ||
640 | if (cp == NULL) { | ||
641 | bb_error_msg("missing 2nd delimiter for substitute"); | ||
642 | return; | ||
643 | } | ||
644 | |||
645 | *cp++ = '\0'; | ||
646 | |||
647 | newStr = cp; | ||
648 | cp = strchr(cp, delim); | ||
649 | |||
650 | if (cp) | ||
651 | *cp++ = '\0'; | ||
652 | else | ||
653 | cp = (char*)""; | ||
654 | |||
655 | while (*cp) switch (*cp++) { | ||
656 | case 'g': | ||
657 | globalFlag = TRUE; | ||
658 | break; | ||
659 | case 'p': | ||
660 | printFlag = TRUE; | ||
661 | break; | ||
662 | default: | ||
663 | bb_error_msg("unknown option for substitute"); | ||
664 | return; | ||
665 | } | ||
666 | |||
667 | if (*oldStr == '\0') { | ||
966 | if (searchString[0] == '\0') { | 668 | if (searchString[0] == '\0') { |
967 | bb_error_msg("no previous search string"); | 669 | bb_error_msg("no previous search string"); |
968 | return 0; | 670 | return; |
969 | } | 671 | } |
970 | str = searchString; | 672 | oldStr = searchString; |
971 | } | 673 | } |
972 | 674 | ||
973 | if (str != searchString) | 675 | if (oldStr != searchString) |
974 | strcpy(searchString, str); | 676 | strcpy(searchString, oldStr); |
975 | |||
976 | len = strlen(str); | ||
977 | 677 | ||
978 | lp = findLine(num1); | 678 | lp = findLine(num1); |
979 | if (lp == NULL) | 679 | if (lp == NULL) |
980 | return 0; | 680 | return; |
681 | |||
682 | oldLen = strlen(oldStr); | ||
683 | newLen = strlen(newStr); | ||
684 | deltaLen = newLen - oldLen; | ||
685 | offset = 0; | ||
686 | nlp = NULL; | ||
981 | 687 | ||
982 | while (num1 <= num2) { | 688 | while (num1 <= num2) { |
983 | if (findString(lp, str, len, 0) >= 0) | 689 | offset = findString(lp, oldStr, oldLen, offset); |
984 | return num1; | 690 | |
985 | num1++; | 691 | if (offset < 0) { |
692 | if (needPrint) { | ||
693 | printLines(num1, num1, FALSE); | ||
694 | needPrint = FALSE; | ||
695 | } | ||
696 | offset = 0; | ||
697 | lp = lp->next; | ||
698 | num1++; | ||
699 | continue; | ||
700 | } | ||
701 | |||
702 | needPrint = printFlag; | ||
703 | didSub = TRUE; | ||
704 | dirty = TRUE; | ||
705 | |||
706 | /* | ||
707 | * If the replacement string is the same size or shorter | ||
708 | * than the old string, then the substitution is easy. | ||
709 | */ | ||
710 | if (deltaLen <= 0) { | ||
711 | memcpy(&lp->data[offset], newStr, newLen); | ||
712 | if (deltaLen) { | ||
713 | memcpy(&lp->data[offset + newLen], | ||
714 | &lp->data[offset + oldLen], | ||
715 | lp->len - offset - oldLen); | ||
716 | |||
717 | lp->len += deltaLen; | ||
718 | } | ||
719 | offset += newLen; | ||
720 | if (globalFlag) | ||
721 | continue; | ||
722 | if (needPrint) { | ||
723 | printLines(num1, num1, FALSE); | ||
724 | needPrint = FALSE; | ||
725 | } | ||
726 | lp = lp->next; | ||
727 | num1++; | ||
728 | continue; | ||
729 | } | ||
730 | |||
731 | /* | ||
732 | * The new string is larger, so allocate a new line | ||
733 | * structure and use that. Link it in place of | ||
734 | * the old line structure. | ||
735 | */ | ||
736 | nlp = xmalloc(sizeof(LINE) + lp->len + deltaLen); | ||
737 | |||
738 | nlp->len = lp->len + deltaLen; | ||
739 | |||
740 | memcpy(nlp->data, lp->data, offset); | ||
741 | memcpy(&nlp->data[offset], newStr, newLen); | ||
742 | memcpy(&nlp->data[offset + newLen], | ||
743 | &lp->data[offset + oldLen], | ||
744 | lp->len - offset - oldLen); | ||
745 | |||
746 | nlp->next = lp->next; | ||
747 | nlp->prev = lp->prev; | ||
748 | nlp->prev->next = nlp; | ||
749 | nlp->next->prev = nlp; | ||
750 | |||
751 | if (curLine == lp) | ||
752 | curLine = nlp; | ||
753 | |||
754 | free(lp); | ||
755 | lp = nlp; | ||
756 | |||
757 | offset += newLen; | ||
758 | |||
759 | if (globalFlag) | ||
760 | continue; | ||
761 | |||
762 | if (needPrint) { | ||
763 | printLines(num1, num1, FALSE); | ||
764 | needPrint = FALSE; | ||
765 | } | ||
766 | |||
986 | lp = lp->next; | 767 | lp = lp->next; |
768 | num1++; | ||
987 | } | 769 | } |
988 | 770 | ||
989 | bb_error_msg("can't find string \"%s\"", str); | 771 | if (!didSub) |
990 | return 0; | 772 | bb_error_msg("no substitutions found for \"%s\"", oldStr); |
991 | } | 773 | } |
992 | 774 | ||
993 | |||
994 | /* | 775 | /* |
995 | * Return a pointer to the specified line number. | 776 | * Read commands until we are told to stop. |
996 | */ | 777 | */ |
997 | static LINE *findLine(int num) | 778 | static void doCommands(void) |
998 | { | 779 | { |
999 | LINE *lp; | 780 | while (TRUE) { |
1000 | int lnum; | 781 | char buf[USERSIZE]; |
782 | const char *cp; | ||
783 | int len; | ||
784 | int n, num1, num2; | ||
785 | smallint h, have1, have2; | ||
1001 | 786 | ||
1002 | if ((num < 1) || (num > lastNum)) { | 787 | /* Returns: |
1003 | bb_error_msg("line number %d does not exist", num); | 788 | * -1 on read errors or EOF, or on bare Ctrl-D. |
1004 | return NULL; | 789 | * 0 on ctrl-C, |
1005 | } | 790 | * >0 length of input string, including terminating '\n' |
791 | */ | ||
792 | len = read_line_input(NULL, ": ", buf, sizeof(buf), /*timeout*/ -1); | ||
793 | if (len <= 0) | ||
794 | return; | ||
795 | while (len && isspace(buf[--len])) | ||
796 | buf[len] = '\0'; | ||
1006 | 797 | ||
1007 | if (curNum <= 0) { | 798 | if ((curNum == 0) && (lastNum > 0)) { |
1008 | curNum = 1; | 799 | curNum = 1; |
1009 | curLine = lines.next; | 800 | curLine = lines.next; |
1010 | } | 801 | } |
1011 | 802 | ||
1012 | if (num == curNum) | 803 | have1 = FALSE; |
1013 | return curLine; | 804 | have2 = FALSE; |
805 | /* Don't pass &haveN, &numN to getNum() since this forces | ||
806 | * compiler to keep them on stack, not in registers, | ||
807 | * which is usually quite suboptimal. | ||
808 | * Using intermediate variables shrinks code by ~150 bytes. | ||
809 | */ | ||
810 | cp = getNum(skip_whitespace(buf), &h, &n); | ||
811 | if (!cp) | ||
812 | continue; | ||
813 | have1 = h; | ||
814 | num1 = n; | ||
815 | cp = skip_whitespace(cp); | ||
816 | if (*cp == ',') { | ||
817 | cp = getNum(cp + 1, &h, &n); | ||
818 | if (!cp) | ||
819 | continue; | ||
820 | num2 = n; | ||
821 | if (!have1) | ||
822 | num1 = 1; | ||
823 | if (!h) | ||
824 | num2 = lastNum; | ||
825 | have1 = TRUE; | ||
826 | have2 = TRUE; | ||
827 | } | ||
828 | if (!have1) | ||
829 | num1 = curNum; | ||
830 | if (!have2) | ||
831 | num2 = num1; | ||
1014 | 832 | ||
1015 | lp = curLine; | 833 | switch (*cp++) { |
1016 | lnum = curNum; | 834 | case 'a': |
1017 | if (num < (curNum / 2)) { | 835 | addLines(num1 + 1); |
1018 | lp = lines.next; | 836 | break; |
1019 | lnum = 1; | ||
1020 | } else if (num > ((curNum + lastNum) / 2)) { | ||
1021 | lp = lines.prev; | ||
1022 | lnum = lastNum; | ||
1023 | } | ||
1024 | 837 | ||
1025 | while (lnum < num) { | 838 | case 'c': |
1026 | lp = lp->next; | 839 | deleteLines(num1, num2); |
1027 | lnum++; | 840 | addLines(num1); |
1028 | } | 841 | break; |
1029 | 842 | ||
1030 | while (lnum > num) { | 843 | case 'd': |
1031 | lp = lp->prev; | 844 | deleteLines(num1, num2); |
1032 | lnum--; | 845 | break; |
846 | |||
847 | case 'f': | ||
848 | if (*cp != '\0' && *cp != ' ') { | ||
849 | bb_error_msg("bad file command"); | ||
850 | break; | ||
851 | } | ||
852 | cp = skip_whitespace(cp); | ||
853 | if (*cp == '\0') { | ||
854 | if (fileName) | ||
855 | printf("\"%s\"\n", fileName); | ||
856 | else | ||
857 | puts("No file name"); | ||
858 | break; | ||
859 | } | ||
860 | free(fileName); | ||
861 | fileName = xstrdup(cp); | ||
862 | break; | ||
863 | |||
864 | case 'i': | ||
865 | if (!have1 && lastNum == 0) | ||
866 | num1 = 1; | ||
867 | addLines(num1); | ||
868 | break; | ||
869 | |||
870 | case 'k': | ||
871 | cp = skip_whitespace(cp); | ||
872 | if ((unsigned)(*cp - 'a') >= 26 || cp[1]) { | ||
873 | bb_error_msg("bad mark name"); | ||
874 | break; | ||
875 | } | ||
876 | marks[(unsigned)(*cp - 'a')] = num2; | ||
877 | break; | ||
878 | |||
879 | case 'l': | ||
880 | printLines(num1, num2, TRUE); | ||
881 | break; | ||
882 | |||
883 | case 'p': | ||
884 | printLines(num1, num2, FALSE); | ||
885 | break; | ||
886 | |||
887 | case 'q': | ||
888 | cp = skip_whitespace(cp); | ||
889 | if (have1 || *cp) { | ||
890 | bb_error_msg("bad quit command"); | ||
891 | break; | ||
892 | } | ||
893 | if (!dirty) | ||
894 | return; | ||
895 | len = read_line_input(NULL, "Really quit? ", buf, 16, /*timeout*/ -1); | ||
896 | /* read error/EOF - no way to continue */ | ||
897 | if (len < 0) | ||
898 | return; | ||
899 | cp = skip_whitespace(buf); | ||
900 | if ((*cp | 0x20) == 'y') /* Y or y */ | ||
901 | return; | ||
902 | break; | ||
903 | |||
904 | case 'r': | ||
905 | if (*cp != '\0' && *cp != ' ') { | ||
906 | bb_error_msg("bad read command"); | ||
907 | break; | ||
908 | } | ||
909 | cp = skip_whitespace(cp); | ||
910 | if (*cp == '\0') { | ||
911 | bb_error_msg("no file name"); | ||
912 | break; | ||
913 | } | ||
914 | if (!have1) | ||
915 | num1 = lastNum; | ||
916 | if (readLines(cp, num1 + 1)) | ||
917 | break; | ||
918 | if (fileName == NULL) | ||
919 | fileName = xstrdup(cp); | ||
920 | break; | ||
921 | |||
922 | case 's': | ||
923 | subCommand(cp, num1, num2); | ||
924 | break; | ||
925 | |||
926 | case 'w': | ||
927 | if (*cp != '\0' && *cp != ' ') { | ||
928 | bb_error_msg("bad write command"); | ||
929 | break; | ||
930 | } | ||
931 | cp = skip_whitespace(cp); | ||
932 | if (*cp == '\0') { | ||
933 | cp = fileName; | ||
934 | if (!cp) { | ||
935 | bb_error_msg("no file name specified"); | ||
936 | break; | ||
937 | } | ||
938 | } | ||
939 | if (!have1) { | ||
940 | num1 = 1; | ||
941 | num2 = lastNum; | ||
942 | dirty = FALSE; | ||
943 | } | ||
944 | writeLines(cp, num1, num2); | ||
945 | break; | ||
946 | |||
947 | case 'z': | ||
948 | switch (*cp) { | ||
949 | case '-': | ||
950 | printLines(curNum - 21, curNum, FALSE); | ||
951 | break; | ||
952 | case '.': | ||
953 | printLines(curNum - 11, curNum + 10, FALSE); | ||
954 | break; | ||
955 | default: | ||
956 | printLines(curNum, curNum + 21, FALSE); | ||
957 | break; | ||
958 | } | ||
959 | break; | ||
960 | |||
961 | case '.': | ||
962 | if (have1) { | ||
963 | bb_error_msg("no arguments allowed"); | ||
964 | break; | ||
965 | } | ||
966 | printLines(curNum, curNum, FALSE); | ||
967 | break; | ||
968 | |||
969 | case '-': | ||
970 | if (setCurNum(curNum - 1)) | ||
971 | printLines(curNum, curNum, FALSE); | ||
972 | break; | ||
973 | |||
974 | case '=': | ||
975 | printf("%d\n", num1); | ||
976 | break; | ||
977 | case '\0': | ||
978 | if (have1) { | ||
979 | printLines(num2, num2, FALSE); | ||
980 | break; | ||
981 | } | ||
982 | if (setCurNum(curNum + 1)) | ||
983 | printLines(curNum, curNum, FALSE); | ||
984 | break; | ||
985 | |||
986 | default: | ||
987 | bb_error_msg("unimplemented command"); | ||
988 | break; | ||
989 | } | ||
1033 | } | 990 | } |
1034 | return lp; | ||
1035 | } | 991 | } |
1036 | 992 | ||
1037 | 993 | int ed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | |
1038 | /* | 994 | int ed_main(int argc UNUSED_PARAM, char **argv) |
1039 | * Set the current line number. | ||
1040 | * Returns TRUE if successful. | ||
1041 | */ | ||
1042 | static int setCurNum(int num) | ||
1043 | { | 995 | { |
1044 | LINE *lp; | 996 | INIT_G(); |
1045 | 997 | ||
1046 | lp = findLine(num); | 998 | bufSize = INITBUF_SIZE; |
1047 | if (lp == NULL) | 999 | bufBase = xmalloc(bufSize); |
1048 | return FALSE; | 1000 | bufPtr = bufBase; |
1049 | curNum = num; | 1001 | lines.next = &lines; |
1050 | curLine = lp; | 1002 | lines.prev = &lines; |
1051 | return TRUE; | 1003 | |
1004 | if (argv[1]) { | ||
1005 | fileName = xstrdup(argv[1]); | ||
1006 | if (!readLines(fileName, 1)) { | ||
1007 | return EXIT_SUCCESS; | ||
1008 | } | ||
1009 | if (lastNum) | ||
1010 | setCurNum(1); | ||
1011 | dirty = FALSE; | ||
1012 | } | ||
1013 | |||
1014 | doCommands(); | ||
1015 | return EXIT_SUCCESS; | ||
1052 | } | 1016 | } |