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