2 * Copyright (c) 1993 by David I. Bell
3 * Permission is granted to use, distribute, or modify this source,
4 * provided that this copyright notice remains intact.
6 * The "ed" built-in command (much simplified)
12 #define USERSIZE 1024 /* max line length typed in by user */
13 #define INITBUFSIZE 1024 /* initial buffer size */
19 typedef struct LINE LINE
;
34 static char *filename
;
35 static char searchstring
[USERSIZE
];
43 static void docommands();
44 static void subcommand();
46 static BOOL
setcurnum();
47 static BOOL
initedit();
48 static void termedit();
49 static void addlines();
50 static BOOL
insertline();
51 static BOOL
deletelines();
52 static BOOL
printlines();
53 static BOOL
writelines();
54 static BOOL
readlines();
55 static NUM
searchlines();
56 static LEN
findstring();
57 static LINE
*findline();
68 filename
= strdup(argv
[1]);
69 if (filename
== NULL
) {
70 fprintf(stderr
, "No memory\n");
75 if (!readlines(filename
, 1)) {
93 * Read commands until we are told to stop.
111 if (fgets(buf
, sizeof(buf
), stdin
) == NULL
)
120 fprintf(stderr
, "Command line too long\n");
123 } while ((len
!= EOF
) && (len
!= '\n'));
128 while ((cp
> buf
) && isblank(cp
[-1]))
139 if ((curnum
== 0) && (lastnum
> 0)) {
141 curline
= lines
.next
;
144 if (!getnum(&cp
, &have1
, &num1
))
152 if (!getnum(&cp
, &have2
, &num2
))
177 deletelines(num1
, num2
);
182 deletelines(num1
, num2
);
186 if (*cp
&& !isblank(*cp
)) {
187 fprintf(stderr
, "Bad file command\n");
195 printf("\"%s\"\n", filename
);
197 printf("No filename\n");
203 fprintf(stderr
, "No memory for filename\n");
220 if ((*cp
< 'a') || (*cp
> 'a') || cp
[1]) {
221 fprintf(stderr
, "Bad mark name\n");
225 marks
[*cp
- 'a'] = num2
;
229 printlines(num1
, num2
, TRUE
);
233 printlines(num1
, num2
, FALSE
);
240 fprintf(stderr
, "Bad quit command\n");
247 printf("Really quit? ");
251 fgets(buf
, sizeof(buf
), stdin
);
255 if ((*cp
== 'y') || (*cp
== 'Y'))
260 if (*cp
&& !isblank(*cp
)) {
261 fprintf(stderr
, "Bad read command\n");
268 fprintf(stderr
, "No filename\n");
275 if (readlines(cp
, num1
+ 1))
278 if (filename
== NULL
)
279 filename
= strdup(cp
);
283 subcommand(cp
, num1
, num2
);
287 if (*cp
&& !isblank(*cp
)) {
288 fprintf(stderr
, "Bad write command\n");
302 fprintf(stderr
, "No file name specified\n");
306 writelines(cp
, num1
, num2
);
312 printlines(curnum
-21, curnum
, FALSE
);
315 printlines(curnum
-11, curnum
+10, FALSE
);
318 printlines(curnum
, curnum
+21, FALSE
);
325 fprintf(stderr
, "No arguments allowed\n");
328 printlines(curnum
, curnum
, FALSE
);
332 if (setcurnum(curnum
- 1))
333 printlines(curnum
, curnum
, FALSE
);
337 printf("%d\n", num1
);
342 printlines(num2
, num2
, FALSE
);
346 if (setcurnum(curnum
+ 1))
347 printlines(curnum
, curnum
, FALSE
);
351 fprintf(stderr
, "Unimplemented command\n");
359 * Do the substitute command.
360 * The current line is set to the last substitution done.
363 subcommand(cp
, num1
, num2
)
382 if ((num1
< 1) || (num2
> lastnum
) || (num1
> num2
)) {
383 fprintf(stderr
, "Bad line range for substitute\n");
392 if (isblank(*cp
) || (*cp
== '\0')) {
393 fprintf(stderr
, "Bad delimiter for substitute\n");
400 cp
= strchr(cp
, delim
);
402 fprintf(stderr
, "Missing 2nd delimiter for substitute\n");
408 cp
= strchr(cp
, delim
);
414 while (*cp
) switch (*cp
++) {
424 fprintf(stderr
, "Unknown option for substitute\n");
428 if (*oldstr
== '\0') {
429 if (searchstring
[0] == '\0') {
430 fprintf(stderr
, "No previous search string\n");
433 oldstr
= searchstring
;
436 if (oldstr
!= searchstring
)
437 strcpy(searchstring
, oldstr
);
443 oldlen
= strlen(oldstr
);
444 newlen
= strlen(newstr
);
445 deltalen
= newlen
- oldlen
;
448 while (num1
<= num2
) {
449 offset
= findstring(lp
, oldstr
, oldlen
, offset
);
452 printlines(num1
, num1
, FALSE
);
462 needprint
= printflag
;
467 * If the replacement string is the same size or shorter
468 * than the old string, then the substitution is easy.
471 memcpy(&lp
->data
[offset
], newstr
, newlen
);
474 memcpy(&lp
->data
[offset
+ newlen
],
475 &lp
->data
[offset
+ oldlen
],
476 lp
->len
- offset
- oldlen
);
486 printlines(num1
, num1
, FALSE
);
496 * The new string is larger, so allocate a new line
497 * structure and use that. Link it in in place of
498 * the old line structure.
500 nlp
= (LINE
*) malloc(sizeof(LINE
) + lp
->len
+ deltalen
);
502 fprintf(stderr
, "Cannot get memory for line\n");
505 nlp
->len
= lp
->len
+ deltalen
;
507 memcpy(nlp
->data
, lp
->data
, offset
);
509 memcpy(&nlp
->data
[offset
], newstr
, newlen
);
511 memcpy(&nlp
->data
[offset
+ newlen
],
512 &lp
->data
[offset
+ oldlen
],
513 lp
->len
- offset
- oldlen
);
515 nlp
->next
= lp
->next
;
516 nlp
->prev
= lp
->prev
;
517 nlp
->prev
->next
= nlp
;
518 nlp
->next
->prev
= nlp
;
532 printlines(num1
, num1
, FALSE
);
541 fprintf(stderr
, "No substitutions found for \"%s\"\n", oldstr
);
546 * Search a line for the specified string starting at the specified
547 * offset in the line. Returns the offset of the found string, or -1.
550 findstring(lp
, str
, len
, offset
)
560 cp
= &lp
->data
[offset
];
561 left
= lp
->len
- offset
;
563 while (left
>= len
) {
564 ncp
= memchr(cp
, *str
, left
);
573 if (memcmp(cp
, str
, len
) == 0)
574 return (cp
- lp
->data
);
585 * Add lines which are typed in by the user.
586 * The lines are inserted just before the specified line number.
587 * The lines are terminated by a line containing a single dot (ugly!),
588 * or by an end of file.
595 char buf
[USERSIZE
+ 1];
597 while (fgets(buf
, sizeof(buf
), stdin
)) {
598 if ((buf
[0] == '.') && (buf
[1] == '\n') && (buf
[2] == '\0'))
605 if (buf
[len
- 1] != '\n') {
606 fprintf(stderr
, "Line too long\n");
609 } while ((len
!= EOF
) && (len
!= '\n'));
613 if (!insertline(num
++, buf
, len
))
620 * Parse a line number argument if it is present. This is a sum
621 * or difference of numbers, '.', '$', 'x, or a search string.
622 * Returns TRUE if successful (whether or not there was a number).
623 * Returns FALSE if there was a parsing error, with a message output.
624 * Whether there was a number is returned indirectly, as is the number.
625 * The character pointer which stopped the scan is also returned.
628 getnum(retcp
, rethavenum
, retnum
)
664 if ((*cp
< 'a') || (*cp
> 'z')) {
665 fprintf(stderr
, "Bad mark name\n");
670 num
= marks
[*cp
++ - 'a'];
675 cp
= strchr(str
, '/');
680 num
= searchlines(str
, curnum
, lastnum
);
688 if (!isdecimal(*cp
)) {
690 *rethavenum
= havenum
;
696 while (isdecimal(*cp
))
697 num
= num
* 10 + *cp
++ - '0';
719 *rethavenum
= havenum
;
728 * Initialize everything for editing.
735 bufsize
= INITBUFSIZE
;
736 bufbase
= malloc(bufsize
);
737 if (bufbase
== NULL
) {
738 fprintf(stderr
, "No memory for buffer\n");
753 searchstring
[0] = '\0';
755 for (i
= 0; i
< 26; i
++)
777 searchstring
[0] = '\0';
780 deletelines(1, lastnum
);
789 * Read lines from a file at the specified line number.
790 * Returns TRUE if the file was successfully read.
804 if ((num
< 1) || (num
> lastnum
+ 1)) {
805 fprintf(stderr
, "Bad line for read\n");
820 printf("\"%s\", ", file
);
825 printf("INTERRUPTED, ");
830 cp
= memchr(bufptr
, '\n', bufused
);
832 len
= (cp
- bufptr
) + 1;
833 if (!insertline(num
, bufptr
, len
)) {
846 if (bufptr
!= bufbase
) {
847 memcpy(bufbase
, bufptr
, bufused
);
848 bufptr
= bufbase
+ bufused
;
851 if (bufused
>= bufsize
) {
852 len
= (bufsize
* 3) / 2;
853 cp
= realloc(bufbase
, len
);
855 fprintf(stderr
, "No memory for buffer\n");
861 bufptr
= bufbase
+ bufused
;
865 cc
= read(fd
, bufptr
, bufsize
- bufused
);
878 if (!insertline(num
, bufptr
, bufused
)) {
883 charcount
+= bufused
;
888 printf("%d lines%s, %d chars\n", linecount
,
889 (bufused
? " (incomplete)" : ""), charcount
);
896 * Write the specified lines out to the specified file.
897 * Returns TRUE if successful, or FALSE on an error with a message output.
900 writelines(file
, num1
, num2
)
910 if ((num1
< 1) || (num2
> lastnum
) || (num1
> num2
)) {
911 fprintf(stderr
, "Bad line range for write\n");
918 fd
= creat(file
, 0666);
924 printf("\"%s\", ", file
);
933 while (num1
++ <= num2
) {
934 if (write(fd
, lp
->data
, lp
->len
) != lp
->len
) {
940 charcount
+= lp
->len
;
950 printf("%d lines, %d chars\n", linecount
, charcount
);
957 * Print lines in a specified range.
958 * The last line printed becomes the current line.
959 * If expandflag is TRUE, then the line is printed specially to
960 * show magic characters.
963 printlines(num1
, num2
, expandflag
)
973 if ((num1
< 1) || (num2
> lastnum
) || (num1
> num2
)) {
974 fprintf(stderr
, "Bad line range for print\n");
982 while (!intflag
&& (num1
<= num2
)) {
984 write(STDOUT
, lp
->data
, lp
->len
);
991 * Show control characters and characters with the
992 * high bit set specially.
996 if ((count
> 0) && (cp
[count
- 1] == '\n'))
999 while (count
-- > 0) {
1002 fputs("M-", stdout
);
1015 fputs("$\n", stdout
);
1026 * Insert a new line with the specified text.
1027 * The line is inserted so as to become the specified line,
1028 * thus pushing any existing and further lines down one.
1029 * The inserted line is also set to become the current line.
1030 * Returns TRUE if successful.
1033 insertline(num
, data
, len
)
1041 if ((num
< 1) || (num
> lastnum
+ 1)) {
1042 fprintf(stderr
, "Inserting at bad line number\n");
1046 newlp
= (LINE
*) malloc(sizeof(LINE
) + len
- 1);
1047 if (newlp
== NULL
) {
1048 fprintf(stderr
, "Failed to allocate memory for line\n");
1052 memcpy(newlp
->data
, data
, len
);
1060 free((char *) newlp
);
1066 newlp
->prev
= lp
->prev
;
1067 lp
->prev
->next
= newlp
;
1073 return setcurnum(num
);
1078 * Delete lines from the given range.
1081 deletelines(num1
, num2
)
1090 if ((num1
< 1) || (num2
> lastnum
) || (num1
> num2
)) {
1091 fprintf(stderr
, "Bad line numbers for delete\n");
1095 lp
= findline(num1
);
1099 if ((curnum
>= num1
) && (curnum
<= num2
)) {
1101 setcurnum(num2
+ 1);
1103 setcurnum(num1
- 1);
1108 count
= num2
- num1
+ 1;
1115 while (count
-- > 0) {
1134 * Search for a line which contains the specified string.
1135 * If the string is NULL, then the previously searched for string
1136 * is used. The currently searched for string is saved for future use.
1137 * Returns the line number which matches, or 0 if there was no match
1138 * with an error printed.
1141 searchlines(str
, num1
, num2
)
1149 if ((num1
< 1) || (num2
> lastnum
) || (num1
> num2
)) {
1150 fprintf(stderr
, "Bad line numbers for search\n");
1155 if (searchstring
[0] == '\0') {
1156 fprintf(stderr
, "No previous search string\n");
1162 if (str
!= searchstring
)
1163 strcpy(searchstring
, str
);
1167 lp
= findline(num1
);
1171 while (num1
<= num2
) {
1172 if (findstring(lp
, str
, len
, 0) >= 0)
1179 fprintf(stderr
, "Cannot find string \"%s\"\n", str
);
1186 * Return a pointer to the specified line number.
1195 if ((num
< 1) || (num
> lastnum
)) {
1196 fprintf(stderr
, "Line number %d does not exist\n", num
);
1202 curline
= lines
.next
;
1211 if (num
< (curnum
/ 2)) {
1215 else if (num
> ((curnum
+ lastnum
) / 2)) {
1220 while (lnum
< num
) {
1225 while (lnum
> num
) {
1235 * Set the current line number.
1236 * Returns TRUE if successful.