diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2017-07-27 11:17:15 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2017-07-27 11:17:15 +0200 |
commit | 5ea50697fd13eb1caeecf3ceb8d03a1e7f578406 (patch) | |
tree | 07bdc37fd1cde19904789424ed4bf254416eb988 | |
parent | 8cae43c5d732e86b8a668013b957fdb6363c8388 (diff) | |
download | busybox-w32-5ea50697fd13eb1caeecf3ceb8d03a1e7f578406.tar.gz busybox-w32-5ea50697fd13eb1caeecf3ceb8d03a1e7f578406.tar.bz2 busybox-w32-5ea50697fd13eb1caeecf3ceb8d03a1e7f578406.zip |
ed: fix --help and reorder functions, no code changes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | editors/ed.c | 1064 |
1 files changed, 516 insertions, 548 deletions
diff --git a/editors/ed.c b/editors/ed.c index c594d3da1..a2a389c2b 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,7 +78,6 @@ 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) | 81 | static char *skip_blank(const char *cp) |
101 | { | 82 | { |
102 | while (isblank(*cp)) | 83 | while (isblank(*cp)) |
@@ -104,413 +85,49 @@ static char *skip_blank(const char *cp) | |||
104 | return (char *)cp; | 85 | return (char *)cp; |
105 | } | 86 | } |
106 | 87 | ||
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 | /* | 88 | /* |
134 | * Read commands until we are told to stop. | 89 | * Return a pointer to the specified line number. |
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 | /* | ||
345 | * Do the substitute command. | ||
346 | * The current line is set to the last substitution done. | ||
347 | */ | 90 | */ |
348 | static void subCommand(const char *cmd, int num1, int num2) | 91 | static LINE *findLine(int num) |
349 | { | 92 | { |
350 | char *cp, *oldStr, *newStr, buf[USERSIZE]; | 93 | LINE *lp; |
351 | int delim, oldLen, newLen, deltaLen, offset; | 94 | 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 | 95 | ||
369 | if (isblank(*cp) || (*cp == '\0')) { | 96 | if ((num < 1) || (num > lastNum)) { |
370 | bb_error_msg("bad delimiter for substitute"); | 97 | bb_error_msg("line number %d does not exist", num); |
371 | return; | 98 | return NULL; |
372 | } | 99 | } |
373 | 100 | ||
374 | delim = *cp++; | 101 | if (curNum <= 0) { |
375 | oldStr = cp; | 102 | curNum = 1; |
376 | 103 | curLine = lines.next; | |
377 | cp = strchr(cp, delim); | ||
378 | if (cp == NULL) { | ||
379 | bb_error_msg("missing 2nd delimiter for substitute"); | ||
380 | return; | ||
381 | } | 104 | } |
382 | 105 | ||
383 | *cp++ = '\0'; | 106 | if (num == curNum) |
384 | 107 | 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 | 108 | ||
405 | if (*oldStr == '\0') { | 109 | lp = curLine; |
406 | if (searchString[0] == '\0') { | 110 | lnum = curNum; |
407 | bb_error_msg("no previous search string"); | 111 | if (num < (curNum / 2)) { |
408 | return; | 112 | lp = lines.next; |
409 | } | 113 | lnum = 1; |
410 | oldStr = searchString; | 114 | } else if (num > ((curNum + lastNum) / 2)) { |
115 | lp = lines.prev; | ||
116 | lnum = lastNum; | ||
411 | } | 117 | } |
412 | 118 | ||
413 | if (oldStr != searchString) | 119 | 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; | 120 | lp = lp->next; |
506 | num1++; | 121 | lnum++; |
507 | } | 122 | } |
508 | 123 | ||
509 | if (!didSub) | 124 | while (lnum > num) { |
510 | bb_error_msg("no substitutions found for \"%s\"", oldStr); | 125 | lp = lp->prev; |
126 | lnum--; | ||
127 | } | ||
128 | return lp; | ||
511 | } | 129 | } |
512 | 130 | ||
513 | |||
514 | /* | 131 | /* |
515 | * Search a line for the specified string starting at the specified | 132 | * 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. | 133 | * offset in the line. Returns the offset of the found string, or -1. |
@@ -540,37 +157,48 @@ static int findString(const LINE *lp, const char *str, int len, int offset) | |||
540 | return -1; | 157 | return -1; |
541 | } | 158 | } |
542 | 159 | ||
543 | |||
544 | /* | 160 | /* |
545 | * Add lines which are typed in by the user. | 161 | * Search for a line which contains the specified string. |
546 | * The lines are inserted just before the specified line number. | 162 | * If the string is "", then the previously searched for string |
547 | * The lines are terminated by a line containing a single dot (ugly!), | 163 | * is used. The currently searched for string is saved for future use. |
548 | * or by an end of file. | 164 | * Returns the line number which matches, or 0 if there was no match |
165 | * with an error printed. | ||
549 | */ | 166 | */ |
550 | static void addLines(int num) | 167 | static NOINLINE int searchLines(const char *str, int num1, int num2) |
551 | { | 168 | { |
169 | const LINE *lp; | ||
552 | int len; | 170 | int len; |
553 | char buf[USERSIZE + 1]; | ||
554 | 171 | ||
555 | while (1) { | 172 | if (bad_nums(num1, num2, "search")) |
556 | /* Returns: | 173 | return 0; |
557 | * -1 on read errors or EOF, or on bare Ctrl-D. | 174 | |
558 | * 0 on ctrl-C, | 175 | if (*str == '\0') { |
559 | * >0 length of input string, including terminating '\n' | 176 | if (searchString[0] == '\0') { |
560 | */ | 177 | bb_error_msg("no previous search string"); |
561 | len = read_line_input(NULL, "", buf, sizeof(buf), /*timeout*/ -1); | 178 | 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 | } | 179 | } |
567 | if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0')) | 180 | str = searchString; |
568 | return; | ||
569 | if (!insertLine(num++, buf, len)) | ||
570 | return; | ||
571 | } | 181 | } |
572 | } | ||
573 | 182 | ||
183 | if (str != searchString) | ||
184 | strcpy(searchString, str); | ||
185 | |||
186 | len = strlen(str); | ||
187 | |||
188 | lp = findLine(num1); | ||
189 | if (lp == NULL) | ||
190 | return 0; | ||
191 | |||
192 | while (num1 <= num2) { | ||
193 | if (findString(lp, str, len, 0) >= 0) | ||
194 | return num1; | ||
195 | num1++; | ||
196 | lp = lp->next; | ||
197 | } | ||
198 | |||
199 | bb_error_msg("can't find string \"%s\"", str); | ||
200 | return 0; | ||
201 | } | ||
574 | 202 | ||
575 | /* | 203 | /* |
576 | * Parse a line number argument if it is present. This is a sum | 204 | * Parse a line number argument if it is present. This is a sum |
@@ -670,6 +298,92 @@ static int getNum(const char **retcp, smallint *retHaveNum, int *retNum) | |||
670 | } | 298 | } |
671 | } | 299 | } |
672 | 300 | ||
301 | /* | ||
302 | * Set the current line number. | ||
303 | * Returns TRUE if successful. | ||
304 | */ | ||
305 | static int setCurNum(int num) | ||
306 | { | ||
307 | LINE *lp; | ||
308 | |||
309 | lp = findLine(num); | ||
310 | if (lp == NULL) | ||
311 | return FALSE; | ||
312 | curNum = num; | ||
313 | curLine = lp; | ||
314 | return TRUE; | ||
315 | } | ||
316 | |||
317 | /* | ||
318 | * Insert a new line with the specified text. | ||
319 | * The line is inserted so as to become the specified line, | ||
320 | * thus pushing any existing and further lines down one. | ||
321 | * The inserted line is also set to become the current line. | ||
322 | * Returns TRUE if successful. | ||
323 | */ | ||
324 | static int insertLine(int num, const char *data, int len) | ||
325 | { | ||
326 | LINE *newLp, *lp; | ||
327 | |||
328 | if ((num < 1) || (num > lastNum + 1)) { | ||
329 | bb_error_msg("inserting at bad line number"); | ||
330 | return FALSE; | ||
331 | } | ||
332 | |||
333 | newLp = xmalloc(sizeof(LINE) + len - 1); | ||
334 | |||
335 | memcpy(newLp->data, data, len); | ||
336 | newLp->len = len; | ||
337 | |||
338 | if (num > lastNum) | ||
339 | lp = &lines; | ||
340 | else { | ||
341 | lp = findLine(num); | ||
342 | if (lp == NULL) { | ||
343 | free((char *) newLp); | ||
344 | return FALSE; | ||
345 | } | ||
346 | } | ||
347 | |||
348 | newLp->next = lp; | ||
349 | newLp->prev = lp->prev; | ||
350 | lp->prev->next = newLp; | ||
351 | lp->prev = newLp; | ||
352 | |||
353 | lastNum++; | ||
354 | dirty = TRUE; | ||
355 | return setCurNum(num); | ||
356 | } | ||
357 | |||
358 | /* | ||
359 | * Add lines which are typed in by the user. | ||
360 | * The lines are inserted just before the specified line number. | ||
361 | * The lines are terminated by a line containing a single dot (ugly!), | ||
362 | * or by an end of file. | ||
363 | */ | ||
364 | static void addLines(int num) | ||
365 | { | ||
366 | int len; | ||
367 | char buf[USERSIZE + 1]; | ||
368 | |||
369 | while (1) { | ||
370 | /* Returns: | ||
371 | * -1 on read errors or EOF, or on bare Ctrl-D. | ||
372 | * 0 on ctrl-C, | ||
373 | * >0 length of input string, including terminating '\n' | ||
374 | */ | ||
375 | len = read_line_input(NULL, "", buf, sizeof(buf), /*timeout*/ -1); | ||
376 | if (len <= 0) { | ||
377 | /* Previously, ctrl-C was exiting to shell. | ||
378 | * Now we exit to ed prompt. Is in important? */ | ||
379 | return; | ||
380 | } | ||
381 | if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0')) | ||
382 | return; | ||
383 | if (!insertLine(num++, buf, len)) | ||
384 | return; | ||
385 | } | ||
386 | } | ||
673 | 387 | ||
674 | /* | 388 | /* |
675 | * Read lines from a file at the specified line number. | 389 | * Read lines from a file at the specified line number. |
@@ -759,7 +473,6 @@ static int readLines(const char *file, int num) | |||
759 | return TRUE; | 473 | return TRUE; |
760 | } | 474 | } |
761 | 475 | ||
762 | |||
763 | /* | 476 | /* |
764 | * Write the specified lines out to the specified file. | 477 | * Write the specified lines out to the specified file. |
765 | * Returns TRUE if successful, or FALSE on an error with a message output. | 478 | * Returns TRUE if successful, or FALSE on an error with a message output. |
@@ -810,7 +523,6 @@ static int writeLines(const char *file, int num1, int num2) | |||
810 | return TRUE; | 523 | return TRUE; |
811 | } | 524 | } |
812 | 525 | ||
813 | |||
814 | /* | 526 | /* |
815 | * Print lines in a specified range. | 527 | * Print lines in a specified range. |
816 | * The last line printed becomes the current line. | 528 | * The last line printed becomes the current line. |
@@ -862,49 +574,6 @@ static int printLines(int num1, int num2, int expandFlag) | |||
862 | return TRUE; | 574 | return TRUE; |
863 | } | 575 | } |
864 | 576 | ||
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 | /* | 577 | /* |
909 | * Delete lines from the given range. | 578 | * Delete lines from the given range. |
910 | */ | 579 | */ |
@@ -946,107 +615,406 @@ static void deleteLines(int num1, int num2) | |||
946 | dirty = TRUE; | 615 | dirty = TRUE; |
947 | } | 616 | } |
948 | 617 | ||
949 | |||
950 | /* | 618 | /* |
951 | * Search for a line which contains the specified string. | 619 | * Do the substitute command. |
952 | * If the string is "", then the previously searched for string | 620 | * 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 | */ | 621 | */ |
957 | static NOINLINE int searchLines(const char *str, int num1, int num2) | 622 | static void subCommand(const char *cmd, int num1, int num2) |
958 | { | 623 | { |
959 | const LINE *lp; | 624 | char *cp, *oldStr, *newStr, buf[USERSIZE]; |
960 | int len; | 625 | int delim, oldLen, newLen, deltaLen, offset; |
626 | LINE *lp, *nlp; | ||
627 | int globalFlag, printFlag, didSub, needPrint; | ||
961 | 628 | ||
962 | if (bad_nums(num1, num2, "search")) | 629 | if (bad_nums(num1, num2, "substitute")) |
963 | return 0; | 630 | return; |
964 | 631 | ||
965 | if (*str == '\0') { | 632 | globalFlag = FALSE; |
633 | printFlag = FALSE; | ||
634 | didSub = FALSE; | ||
635 | needPrint = FALSE; | ||
636 | |||
637 | /* | ||
638 | * Copy the command so we can modify it. | ||
639 | */ | ||
640 | strcpy(buf, cmd); | ||
641 | cp = buf; | ||
642 | |||
643 | if (isblank(*cp) || (*cp == '\0')) { | ||
644 | bb_error_msg("bad delimiter for substitute"); | ||
645 | return; | ||
646 | } | ||
647 | |||
648 | delim = *cp++; | ||
649 | oldStr = cp; | ||
650 | |||
651 | cp = strchr(cp, delim); | ||
652 | if (cp == NULL) { | ||
653 | bb_error_msg("missing 2nd delimiter for substitute"); | ||
654 | return; | ||
655 | } | ||
656 | |||
657 | *cp++ = '\0'; | ||
658 | |||
659 | newStr = cp; | ||
660 | cp = strchr(cp, delim); | ||
661 | |||
662 | if (cp) | ||
663 | *cp++ = '\0'; | ||
664 | else | ||
665 | cp = (char*)""; | ||
666 | |||
667 | while (*cp) switch (*cp++) { | ||
668 | case 'g': | ||
669 | globalFlag = TRUE; | ||
670 | break; | ||
671 | case 'p': | ||
672 | printFlag = TRUE; | ||
673 | break; | ||
674 | default: | ||
675 | bb_error_msg("unknown option for substitute"); | ||
676 | return; | ||
677 | } | ||
678 | |||
679 | if (*oldStr == '\0') { | ||
966 | if (searchString[0] == '\0') { | 680 | if (searchString[0] == '\0') { |
967 | bb_error_msg("no previous search string"); | 681 | bb_error_msg("no previous search string"); |
968 | return 0; | 682 | return; |
969 | } | 683 | } |
970 | str = searchString; | 684 | oldStr = searchString; |
971 | } | 685 | } |
972 | 686 | ||
973 | if (str != searchString) | 687 | if (oldStr != searchString) |
974 | strcpy(searchString, str); | 688 | strcpy(searchString, oldStr); |
975 | |||
976 | len = strlen(str); | ||
977 | 689 | ||
978 | lp = findLine(num1); | 690 | lp = findLine(num1); |
979 | if (lp == NULL) | 691 | if (lp == NULL) |
980 | return 0; | 692 | return; |
693 | |||
694 | oldLen = strlen(oldStr); | ||
695 | newLen = strlen(newStr); | ||
696 | deltaLen = newLen - oldLen; | ||
697 | offset = 0; | ||
698 | nlp = NULL; | ||
981 | 699 | ||
982 | while (num1 <= num2) { | 700 | while (num1 <= num2) { |
983 | if (findString(lp, str, len, 0) >= 0) | 701 | offset = findString(lp, oldStr, oldLen, offset); |
984 | return num1; | 702 | |
985 | num1++; | 703 | if (offset < 0) { |
704 | if (needPrint) { | ||
705 | printLines(num1, num1, FALSE); | ||
706 | needPrint = FALSE; | ||
707 | } | ||
708 | offset = 0; | ||
709 | lp = lp->next; | ||
710 | num1++; | ||
711 | continue; | ||
712 | } | ||
713 | |||
714 | needPrint = printFlag; | ||
715 | didSub = TRUE; | ||
716 | dirty = TRUE; | ||
717 | |||
718 | /* | ||
719 | * If the replacement string is the same size or shorter | ||
720 | * than the old string, then the substitution is easy. | ||
721 | */ | ||
722 | if (deltaLen <= 0) { | ||
723 | memcpy(&lp->data[offset], newStr, newLen); | ||
724 | if (deltaLen) { | ||
725 | memcpy(&lp->data[offset + newLen], | ||
726 | &lp->data[offset + oldLen], | ||
727 | lp->len - offset - oldLen); | ||
728 | |||
729 | lp->len += deltaLen; | ||
730 | } | ||
731 | offset += newLen; | ||
732 | if (globalFlag) | ||
733 | continue; | ||
734 | if (needPrint) { | ||
735 | printLines(num1, num1, FALSE); | ||
736 | needPrint = FALSE; | ||
737 | } | ||
738 | lp = lp->next; | ||
739 | num1++; | ||
740 | continue; | ||
741 | } | ||
742 | |||
743 | /* | ||
744 | * The new string is larger, so allocate a new line | ||
745 | * structure and use that. Link it in place of | ||
746 | * the old line structure. | ||
747 | */ | ||
748 | nlp = xmalloc(sizeof(LINE) + lp->len + deltaLen); | ||
749 | |||
750 | nlp->len = lp->len + deltaLen; | ||
751 | |||
752 | memcpy(nlp->data, lp->data, offset); | ||
753 | memcpy(&nlp->data[offset], newStr, newLen); | ||
754 | memcpy(&nlp->data[offset + newLen], | ||
755 | &lp->data[offset + oldLen], | ||
756 | lp->len - offset - oldLen); | ||
757 | |||
758 | nlp->next = lp->next; | ||
759 | nlp->prev = lp->prev; | ||
760 | nlp->prev->next = nlp; | ||
761 | nlp->next->prev = nlp; | ||
762 | |||
763 | if (curLine == lp) | ||
764 | curLine = nlp; | ||
765 | |||
766 | free(lp); | ||
767 | lp = nlp; | ||
768 | |||
769 | offset += newLen; | ||
770 | |||
771 | if (globalFlag) | ||
772 | continue; | ||
773 | |||
774 | if (needPrint) { | ||
775 | printLines(num1, num1, FALSE); | ||
776 | needPrint = FALSE; | ||
777 | } | ||
778 | |||
986 | lp = lp->next; | 779 | lp = lp->next; |
780 | num1++; | ||
987 | } | 781 | } |
988 | 782 | ||
989 | bb_error_msg("can't find string \"%s\"", str); | 783 | if (!didSub) |
990 | return 0; | 784 | bb_error_msg("no substitutions found for \"%s\"", oldStr); |
991 | } | 785 | } |
992 | 786 | ||
993 | |||
994 | /* | 787 | /* |
995 | * Return a pointer to the specified line number. | 788 | * Read commands until we are told to stop. |
996 | */ | 789 | */ |
997 | static LINE *findLine(int num) | 790 | static void doCommands(void) |
998 | { | 791 | { |
999 | LINE *lp; | 792 | const char *cp; |
1000 | int lnum; | 793 | char *endbuf, buf[USERSIZE]; |
794 | int len, num1, num2; | ||
795 | smallint have1, have2; | ||
1001 | 796 | ||
1002 | if ((num < 1) || (num > lastNum)) { | 797 | while (TRUE) { |
1003 | bb_error_msg("line number %d does not exist", num); | 798 | /* Returns: |
1004 | return NULL; | 799 | * -1 on read errors or EOF, or on bare Ctrl-D. |
1005 | } | 800 | * 0 on ctrl-C, |
801 | * >0 length of input string, including terminating '\n' | ||
802 | */ | ||
803 | len = read_line_input(NULL, ": ", buf, sizeof(buf), /*timeout*/ -1); | ||
804 | if (len <= 0) | ||
805 | return; | ||
806 | endbuf = &buf[len - 1]; | ||
807 | while ((endbuf > buf) && isblank(endbuf[-1])) | ||
808 | endbuf--; | ||
809 | *endbuf = '\0'; | ||
1006 | 810 | ||
1007 | if (curNum <= 0) { | 811 | cp = skip_blank(buf); |
1008 | curNum = 1; | 812 | have1 = FALSE; |
1009 | curLine = lines.next; | 813 | have2 = FALSE; |
1010 | } | ||
1011 | 814 | ||
1012 | if (num == curNum) | 815 | if ((curNum == 0) && (lastNum > 0)) { |
1013 | return curLine; | 816 | curNum = 1; |
817 | curLine = lines.next; | ||
818 | } | ||
1014 | 819 | ||
1015 | lp = curLine; | 820 | if (!getNum(&cp, &have1, &num1)) |
1016 | lnum = curNum; | 821 | continue; |
1017 | if (num < (curNum / 2)) { | ||
1018 | lp = lines.next; | ||
1019 | lnum = 1; | ||
1020 | } else if (num > ((curNum + lastNum) / 2)) { | ||
1021 | lp = lines.prev; | ||
1022 | lnum = lastNum; | ||
1023 | } | ||
1024 | 822 | ||
1025 | while (lnum < num) { | 823 | cp = skip_blank(cp); |
1026 | lp = lp->next; | ||
1027 | lnum++; | ||
1028 | } | ||
1029 | 824 | ||
1030 | while (lnum > num) { | 825 | if (*cp == ',') { |
1031 | lp = lp->prev; | 826 | cp++; |
1032 | lnum--; | 827 | if (!getNum(&cp, &have2, &num2)) |
828 | continue; | ||
829 | if (!have1) | ||
830 | num1 = 1; | ||
831 | if (!have2) | ||
832 | num2 = lastNum; | ||
833 | have1 = TRUE; | ||
834 | have2 = TRUE; | ||
835 | } | ||
836 | if (!have1) | ||
837 | num1 = curNum; | ||
838 | if (!have2) | ||
839 | num2 = num1; | ||
840 | |||
841 | switch (*cp++) { | ||
842 | case 'a': | ||
843 | addLines(num1 + 1); | ||
844 | break; | ||
845 | |||
846 | case 'c': | ||
847 | deleteLines(num1, num2); | ||
848 | addLines(num1); | ||
849 | break; | ||
850 | |||
851 | case 'd': | ||
852 | deleteLines(num1, num2); | ||
853 | break; | ||
854 | |||
855 | case 'f': | ||
856 | if (*cp && !isblank(*cp)) { | ||
857 | bb_error_msg("bad file command"); | ||
858 | break; | ||
859 | } | ||
860 | cp = skip_blank(cp); | ||
861 | if (*cp == '\0') { | ||
862 | if (fileName) | ||
863 | printf("\"%s\"\n", fileName); | ||
864 | else | ||
865 | puts("No file name"); | ||
866 | break; | ||
867 | } | ||
868 | free(fileName); | ||
869 | fileName = xstrdup(cp); | ||
870 | break; | ||
871 | |||
872 | case 'i': | ||
873 | addLines(num1); | ||
874 | break; | ||
875 | |||
876 | case 'k': | ||
877 | cp = skip_blank(cp); | ||
878 | if ((*cp < 'a') || (*cp > 'z') || cp[1]) { | ||
879 | bb_error_msg("bad mark name"); | ||
880 | break; | ||
881 | } | ||
882 | marks[*cp - 'a'] = num2; | ||
883 | break; | ||
884 | |||
885 | case 'l': | ||
886 | printLines(num1, num2, TRUE); | ||
887 | break; | ||
888 | |||
889 | case 'p': | ||
890 | printLines(num1, num2, FALSE); | ||
891 | break; | ||
892 | |||
893 | case 'q': | ||
894 | cp = skip_blank(cp); | ||
895 | if (have1 || *cp) { | ||
896 | bb_error_msg("bad quit command"); | ||
897 | break; | ||
898 | } | ||
899 | if (!dirty) | ||
900 | return; | ||
901 | len = read_line_input(NULL, "Really quit? ", buf, 16, /*timeout*/ -1); | ||
902 | /* read error/EOF - no way to continue */ | ||
903 | if (len < 0) | ||
904 | return; | ||
905 | cp = skip_blank(buf); | ||
906 | if ((*cp | 0x20) == 'y') /* Y or y */ | ||
907 | return; | ||
908 | break; | ||
909 | |||
910 | case 'r': | ||
911 | if (*cp && !isblank(*cp)) { | ||
912 | bb_error_msg("bad read command"); | ||
913 | break; | ||
914 | } | ||
915 | cp = skip_blank(cp); | ||
916 | if (*cp == '\0') { | ||
917 | bb_error_msg("no file name"); | ||
918 | break; | ||
919 | } | ||
920 | if (!have1) | ||
921 | num1 = lastNum; | ||
922 | if (readLines(cp, num1 + 1)) | ||
923 | break; | ||
924 | if (fileName == NULL) | ||
925 | fileName = xstrdup(cp); | ||
926 | break; | ||
927 | |||
928 | case 's': | ||
929 | subCommand(cp, num1, num2); | ||
930 | break; | ||
931 | |||
932 | case 'w': | ||
933 | if (*cp && !isblank(*cp)) { | ||
934 | bb_error_msg("bad write command"); | ||
935 | break; | ||
936 | } | ||
937 | cp = skip_blank(cp); | ||
938 | if (!have1) { | ||
939 | num1 = 1; | ||
940 | num2 = lastNum; | ||
941 | } | ||
942 | if (*cp == '\0') | ||
943 | cp = fileName; | ||
944 | if (cp == NULL) { | ||
945 | bb_error_msg("no file name specified"); | ||
946 | break; | ||
947 | } | ||
948 | writeLines(cp, num1, num2); | ||
949 | break; | ||
950 | |||
951 | case 'z': | ||
952 | switch (*cp) { | ||
953 | case '-': | ||
954 | printLines(curNum - 21, curNum, FALSE); | ||
955 | break; | ||
956 | case '.': | ||
957 | printLines(curNum - 11, curNum + 10, FALSE); | ||
958 | break; | ||
959 | default: | ||
960 | printLines(curNum, curNum + 21, FALSE); | ||
961 | break; | ||
962 | } | ||
963 | break; | ||
964 | |||
965 | case '.': | ||
966 | if (have1) { | ||
967 | bb_error_msg("no arguments allowed"); | ||
968 | break; | ||
969 | } | ||
970 | printLines(curNum, curNum, FALSE); | ||
971 | break; | ||
972 | |||
973 | case '-': | ||
974 | if (setCurNum(curNum - 1)) | ||
975 | printLines(curNum, curNum, FALSE); | ||
976 | break; | ||
977 | |||
978 | case '=': | ||
979 | printf("%d\n", num1); | ||
980 | break; | ||
981 | case '\0': | ||
982 | if (have1) { | ||
983 | printLines(num2, num2, FALSE); | ||
984 | break; | ||
985 | } | ||
986 | if (setCurNum(curNum + 1)) | ||
987 | printLines(curNum, curNum, FALSE); | ||
988 | break; | ||
989 | |||
990 | default: | ||
991 | bb_error_msg("unimplemented command"); | ||
992 | break; | ||
993 | } | ||
1033 | } | 994 | } |
1034 | return lp; | ||
1035 | } | 995 | } |
1036 | 996 | ||
1037 | 997 | int ed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | |
1038 | /* | 998 | 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 | { | 999 | { |
1044 | LINE *lp; | 1000 | INIT_G(); |
1045 | 1001 | ||
1046 | lp = findLine(num); | 1002 | bufSize = INITBUF_SIZE; |
1047 | if (lp == NULL) | 1003 | bufBase = xmalloc(bufSize); |
1048 | return FALSE; | 1004 | bufPtr = bufBase; |
1049 | curNum = num; | 1005 | lines.next = &lines; |
1050 | curLine = lp; | 1006 | lines.prev = &lines; |
1051 | return TRUE; | 1007 | |
1008 | if (argv[1]) { | ||
1009 | fileName = xstrdup(argv[1]); | ||
1010 | if (!readLines(fileName, 1)) { | ||
1011 | return EXIT_SUCCESS; | ||
1012 | } | ||
1013 | if (lastNum) | ||
1014 | setCurNum(1); | ||
1015 | dirty = FALSE; | ||
1016 | } | ||
1017 | |||
1018 | doCommands(); | ||
1019 | return EXIT_SUCCESS; | ||
1052 | } | 1020 | } |