5800001 //----------------------------------------------------------
5800002 // 2009.08.18
5800003 // Modified by Daniele Giacomini for `os16', to
5800004 // harmonize with it, even, when possible, on coding
5800005 // style.
5800006 //
5800007 // The original was taken form ELKS sources:
5800008 // `elkscmd/misc_utils/ed.c'.
5800009 //----------------------------------------------------------
5800010 //
5800011 // Copyright (c) 1993 by David I. Bell
5800012 // Permission is granted to use, distribute, or modify
5800013 // this source, provided that this copyright notice
5800014 // remains intact.
5800015 //
5800016 // The "ed" built-in command (much simplified)
5800017 //
5800018 //----------------------------------------------------------
5800019
5800020 #include <stdio.h>
5800021 #include <ctype.h>
5800022 #include <unistd.h>
5800023 #include <stdbool.h>
5800024 #include <string.h>
5800025 #include <stdlib.h>
5800026 #include <fcntl.h>
5800027 //----------------------------------------------------------
5800028 #define isoctal(ch) (((ch) >= '0') && ((ch) <= '7'))
5800029 #define USERSIZE 1024 /* max line length typed in
5800030 by user */
5800031 #define INITBUFSIZE 1024 /* initial buffer size */
5800032 //----------------------------------------------------------
5800033 typedef int num_t;
5800034 typedef int len_t;
5800035 //
5800036 // The following is the type definition of structure
5800037 // `line_t', but the structure contains pointers to the
5800038 // same kind of type. With the compiler Bcc, it is the
5800039 // only way to declare it.
5800040 //
5800041 typedef struct line line_t;
5800042 //
5800043 struct line
5800044 {
5800045 line_t *next;
5800046 line_t *prev;
5800047 len_t len;
5800048 char data[1];
5800049 };
5800050 //
5800051 static line_t lines;
5800052 static line_t *curline;
5800053 static num_t curnum;
5800054 static num_t lastnum;
5800055 static num_t marks[26];
5800056 static bool dirty;
5800057 static char *filename;
5800058 static char searchstring[USERSIZE];
5800059 //
5800060 static char *bufbase;
5800061 static char *bufptr;
5800062 static len_t bufused;
5800063 static len_t bufsize;
5800064 //----------------------------------------------------------
5800065 static void docommands (void);
5800066 static void subcommand (char *cp, num_t num1, num_t num2);
5800067 static bool getnum (char **retcp, bool * rethavenum,
5800068 num_t * retnum);
5800069 static bool setcurnum (num_t num);
5800070 static bool initedit (void);
5800071 static void termedit (void);
5800072 static void addlines (num_t num);
5800073 static bool insertline (num_t num, char *data, len_t len);
5800074 static bool deletelines (num_t num1, num_t num2);
5800075 static bool printlines (num_t num1, num_t num2,
5800076 bool expandflag);
5800077 static bool writelines (char *file, num_t num1, num_t num2);
5800078 static bool readlines (char *file, num_t num);
5800079 static num_t searchlines (char *str, num_t num1,
5800080 num_t num2);
5800081 static len_t findstring (line_t * lp, char *str,
5800082 len_t len, len_t offset);
5800083 static line_t *findline (num_t num);
5800084 //----------------------------------------------------------
5800085 // Main.
5800086 //----------------------------------------------------------
5800087 int
5800088 main (int argc, char *argv[], char *envp[])
5800089 {
5800090 if (!initedit ())
5800091 return (2);
5800092 //
5800093 if (argc > 1)
5800094 {
5800095 filename = strdup (argv[1]);
5800096 if (filename == NULL)
5800097 {
5800098 fprintf (stderr, "No memory\n");
5800099 termedit ();
5800100 return (1);
5800101 }
5800102 //
5800103 if (!readlines (filename, 1))
5800104 {
5800105 termedit ();
5800106 return (0);
5800107 }
5800108 //
5800109 if (lastnum)
5800110 setcurnum (1);
5800111 //
5800112 dirty = false;
5800113 }
5800114 //
5800115 docommands ();
5800116 //
5800117 termedit ();
5800118 return (0);
5800119 }
5800120
5800121 //----------------------------------------------------------
5800122 // Read commands until we are told to stop.
5800123 //----------------------------------------------------------
5800124 void
5800125 docommands (void)
5800126 {
5800127 char *cp;
5800128 int len;
5800129 num_t num1;
5800130 num_t num2;
5800131 bool have1;
5800132 bool have2;
5800133 char buf[USERSIZE];
5800134 //
5800135 while (true)
5800136 {
5800137 printf (": ");
5800138 fflush (stdout);
5800139 //
5800140 if (fgets (buf, sizeof (buf), stdin) == NULL)
5800141 {
5800142 return;
5800143 }
5800144 //
5800145 len = strlen (buf);
5800146 if (len == 0)
5800147 {
5800148 return;
5800149 }
5800150 //
5800151 cp = &buf[len - 1];
5800152 if (*cp != '\n')
5800153 {
5800154 fprintf (stderr, "Command line too long\n");
5800155 do
5800156 {
5800157 len = fgetc (stdin);
5800158 }
5800159 while ((len != EOF) && (len != '\n'));
5800160 //
5800161 continue;
5800162 }
5800163 //
5800164 while ((cp > buf) && isblank (cp[-1]))
5800165 {
5800166 cp--;
5800167 }
5800168 //
5800169 *cp = '\0';
5800170 //
5800171 cp = buf;
5800172 //
5800173 while (isblank (*cp))
5800174 {
5800175 // *cp++;
5800176 cp++;
5800177 }
5800178 //
5800179 have1 = false;
5800180 have2 = false;
5800181 //
5800182 if ((curnum == 0) && (lastnum > 0))
5800183 {
5800184 curnum = 1;
5800185 curline = lines.next;
5800186 }
5800187 //
5800188 if (!getnum (&cp, &have1, &num1))
5800189 {
5800190 continue;
5800191 }
5800192 //
5800193 while (isblank (*cp))
5800194 {
5800195 cp++;
5800196 }
5800197 //
5800198 if (*cp == ',')
5800199 {
5800200 cp++;
5800201 if (!getnum (&cp, &have2, &num2))
5800202 {
5800203 continue;
5800204 }
5800205 //
5800206 if (!have1)
5800207 {
5800208 num1 = 1;
5800209 }
5800210 if (!have2)
5800211 {
5800212 num2 = lastnum;
5800213 }
5800214 have1 = true;
5800215 have2 = true;
5800216 }
5800217 //
5800218 if (!have1)
5800219 {
5800220 num1 = curnum;
5800221 }
5800222 if (!have2)
5800223 {
5800224 num2 = num1;
5800225 }
5800226 //
5800227 // Command interpretation switch.
5800228 //
5800229 switch (*cp++)
5800230 {
5800231 case 'a':
5800232 addlines (num1 + 1);
5800233 break;
5800234 //
5800235 case 'c':
5800236 deletelines (num1, num2);
5800237 addlines (num1);
5800238 break;
5800239 //
5800240 case 'd':
5800241 deletelines (num1, num2);
5800242 break;
5800243 //
5800244 case 'f':
5800245 if (*cp && !isblank (*cp))
5800246 {
5800247 fprintf (stderr, "Bad file command\n");
5800248 break;
5800249 }
5800250 //
5800251 while (isblank (*cp))
5800252 {
5800253 cp++;
5800254 }
5800255 if (*cp == '\0')
5800256 {
5800257 if (filename)
5800258 {
5800259 printf ("\"%s\"\n", filename);
5800260 }
5800261 else
5800262 {
5800263 printf ("No filename\n");
5800264 }
5800265 break;
5800266 }
5800267 //
5800268 cp = strdup (cp);
5800269 //
5800270 if (cp == NULL)
5800271 {
5800272 fprintf (stderr, "No memory for filename\n");
5800273 break;
5800274 }
5800275 //
5800276 if (filename)
5800277 {
5800278 free (filename);
5800279 }
5800280 //
5800281 filename = cp;
5800282 break;
5800283 //
5800284 case 'i':
5800285 addlines (num1);
5800286 break;
5800287 //
5800288 case 'k':
5800289 while (isblank (*cp))
5800290 {
5800291 cp++;
5800292 }
5800293 //
5800294 if ((*cp < 'a') || (*cp > 'a') || cp[1])
5800295 {
5800296 fprintf (stderr, "Bad mark name\n");
5800297 break;
5800298 }
5800299 //
5800300 marks[*cp - 'a'] = num2;
5800301 break;
5800302 //
5800303 case 'l':
5800304 printlines (num1, num2, true);
5800305 break;
5800306 //
5800307 case 'p':
5800308 printlines (num1, num2, false);
5800309 break;
5800310 //
5800311 case 'q':
5800312 while (isblank (*cp))
5800313 {
5800314 cp++;
5800315 }
5800316 //
5800317 if (have1 || *cp)
5800318 {
5800319 fprintf (stderr, "Bad quit command\n");
5800320 break;
5800321 }
5800322 //
5800323 if (!dirty)
5800324 {
5800325 return;
5800326 }
5800327 //
5800328 printf ("Really quit? ");
5800329 fflush (stdout);
5800330 //
5800331 buf[0] = '\0';
5800332 fgets (buf, sizeof (buf), stdin);
5800333 cp = buf;
5800334 //
5800335 while (isblank (*cp))
5800336 {
5800337 cp++;
5800338 }
5800339 //
5800340 if ((*cp == 'y') || (*cp == 'Y'))
5800341 {
5800342 return;
5800343 }
5800344 //
5800345 break;
5800346 //
5800347 case 'r':
5800348 if (*cp && !isblank (*cp))
5800349 {
5800350 fprintf (stderr, "Bad read command\n");
5800351 break;
5800352 }
5800353 //
5800354 while (isblank (*cp))
5800355 {
5800356 cp++;
5800357 }
5800358 //
5800359 if (*cp == '\0')
5800360 {
5800361 fprintf (stderr, "No filename\n");
5800362 break;
5800363 }
5800364 //
5800365 if (!have1)
5800366 {
5800367 num1 = lastnum;
5800368 }
5800369 //
5800370 // Open the file and add to the buffer
5800371 // at the next line.
5800372 //
5800373 if (readlines (cp, num1 + 1))
5800374 {
5800375 //
5800376 // If the file open fails, just
5800377 // break the command.
5800378 //
5800379 break;
5800380 }
5800381 //
5800382 // Set the default file name, if no
5800383 // previous name is available.
5800384 //
5800385 if (filename == NULL)
5800386 {
5800387 filename = strdup (cp);
5800388 }
5800389 //
5800390 break;
5800391
5800392 case 's':
5800393 subcommand (cp, num1, num2);
5800394 break;
5800395 //
5800396 case 'w':
5800397 if (*cp && !isblank (*cp))
5800398 {
5800399 fprintf (stderr, "Bad write command\n");
5800400 break;
5800401 }
5800402 //
5800403 while (isblank (*cp))
5800404 {
5800405 cp++;
5800406 }
5800407 //
5800408 if (!have1)
5800409 {
5800410 num1 = 1;
5800411 num2 = lastnum;
5800412 }
5800413 //
5800414 // If the file name is not specified, use
5800415 // the
5800416 // default one.
5800417 //
5800418 if (*cp == '\0')
5800419 {
5800420 cp = filename;
5800421 }
5800422 //
5800423 // If even the default file name is not
5800424 // specified,
5800425 // tell it.
5800426 //
5800427 if (cp == NULL)
5800428 {
5800429 fprintf (stderr, "No file name specified\n");
5800430 break;
5800431 }
5800432 //
5800433 // Write the file.
5800434 //
5800435 writelines (cp, num1, num2);
5800436 //
5800437 break;
5800438 //
5800439 case 'z':
5800440 switch (*cp)
5800441 {
5800442 case '-':
5800443 printlines (curnum - 21, curnum, false);
5800444 break;
5800445 case '.':
5800446 printlines (curnum - 11, curnum + 10, false);
5800447 break;
5800448 default:
5800449 printlines (curnum, curnum + 21, false);
5800450 break;
5800451 }
5800452 break;
5800453 //
5800454 case '.':
5800455 if (have1)
5800456 {
5800457 fprintf (stderr, "No arguments allowed\n");
5800458 break;
5800459 }
5800460 printlines (curnum, curnum, false);
5800461 break;
5800462 //
5800463 case '-':
5800464 if (setcurnum (curnum - 1))
5800465 {
5800466 printlines (curnum, curnum, false);
5800467 }
5800468 break;
5800469 //
5800470 case '=':
5800471 printf ("%d\n", num1);
5800472 break;
5800473 //
5800474 case '\0':
5800475 if (have1)
5800476 {
5800477 printlines (num2, num2, false);
5800478 break;
5800479 }
5800480 //
5800481 if (setcurnum (curnum + 1))
5800482 {
5800483 printlines (curnum, curnum, false);
5800484 }
5800485 break;
5800486 //
5800487 default:
5800488 fprintf (stderr, "Unimplemented command\n");
5800489 break;
5800490 }
5800491 }
5800492 }
5800493
5800494 //----------------------------------------------------------
5800495 // Do the substitute command.
5800496 // The current line is set to the last substitution
5800497 // done.
5800498 //----------------------------------------------------------
5800499 void
5800500 subcommand (char *cp, num_t num1, num_t num2)
5800501 {
5800502 int delim;
5800503 char *oldstr;
5800504 char *newstr;
5800505 len_t oldlen;
5800506 len_t newlen;
5800507 len_t deltalen;
5800508 len_t offset;
5800509 line_t *lp;
5800510 line_t *nlp;
5800511 bool globalflag;
5800512 bool printflag;
5800513 bool didsub;
5800514 bool needprint;
5800515
5800516 if ((num1 < 1) || (num2 > lastnum) || (num1 > num2))
5800517 {
5800518 fprintf (stderr, "Bad line range for substitute\n");
5800519 return;
5800520 }
5800521 //
5800522 globalflag = false;
5800523 printflag = false;
5800524 didsub = false;
5800525 needprint = false;
5800526 //
5800527 if (isblank (*cp) || (*cp == '\0'))
5800528 {
5800529 fprintf (stderr, "Bad delimiter for substitute\n");
5800530 return;
5800531 }
5800532 //
5800533 delim = *cp++;
5800534 oldstr = cp;
5800535 //
5800536 cp = strchr (cp, delim);
5800537 //
5800538 if (cp == NULL)
5800539 {
5800540 fprintf (stderr,
5800541 "Missing 2nd delimiter for " "substitute\n");
5800542 return;
5800543 }
5800544 //
5800545 *cp++ = '\0';
5800546 //
5800547 newstr = cp;
5800548 cp = strchr (cp, delim);
5800549 //
5800550 if (cp)
5800551 {
5800552 *cp++ = '\0';
5800553 }
5800554 else
5800555 {
5800556 cp = "";
5800557 }
5800558 while (*cp)
5800559 {
5800560 switch (*cp++)
5800561 {
5800562 case 'g':
5800563 globalflag = true;
5800564 break;
5800565 //
5800566 case 'p':
5800567 printflag = true;
5800568 break;
5800569 //
5800570 default:
5800571 fprintf (stderr,
5800572 "Unknown option for substitute\n");
5800573 return;
5800574 }
5800575 }
5800576 //
5800577 if (*oldstr == '\0')
5800578 {
5800579 if (searchstring[0] == '\0')
5800580 {
5800581 fprintf (stderr, "No previous search string\n");
5800582 return;
5800583 }
5800584 oldstr = searchstring;
5800585 }
5800586 //
5800587 if (oldstr != searchstring)
5800588 {
5800589 strcpy (searchstring, oldstr);
5800590 }
5800591 //
5800592 lp = findline (num1);
5800593 if (lp == NULL)
5800594 {
5800595 return;
5800596 }
5800597 //
5800598 oldlen = strlen (oldstr);
5800599 newlen = strlen (newstr);
5800600 deltalen = newlen - oldlen;
5800601 offset = 0;
5800602 //
5800603 while (num1 <= num2)
5800604 {
5800605 offset = findstring (lp, oldstr, oldlen, offset);
5800606 if (offset < 0)
5800607 {
5800608 if (needprint)
5800609 {
5800610 printlines (num1, num1, false);
5800611 needprint = false;
5800612 }
5800613 //
5800614 offset = 0;
5800615 lp = lp->next;
5800616 num1++;
5800617 continue;
5800618 }
5800619 //
5800620 needprint = printflag;
5800621 didsub = true;
5800622 dirty = true;
5800623
5800624 // ---------------------------------------------
5800625 // If the replacement string is the same size or
5800626 // shorter
5800627 // than the old string, then the substitution is
5800628 // easy.
5800629 // ---------------------------------------------
5800630
5800631 if (deltalen <= 0)
5800632 {
5800633 memcpy (&lp->data[offset], newstr, newlen);
5800634 //
5800635 if (deltalen)
5800636 {
5800637 memcpy (&lp->data[offset + newlen],
5800638 &lp->data[offset + oldlen],
5800639 lp->len - offset - oldlen);
5800640 //
5800641 lp->len += deltalen;
5800642 }
5800643 //
5800644 offset += newlen;
5800645 //
5800646 if (globalflag)
5800647 {
5800648 continue;
5800649 }
5800650 //
5800651 if (needprint)
5800652 {
5800653 printlines (num1, num1, false);
5800654 needprint = false;
5800655 }
5800656 //
5800657 lp = nlp->next;
5800658 num1++;
5800659 continue;
5800660 }
5800661
5800662 // ---------------------------------------------
5800663 // The new string is larger, so allocate a new
5800664 // line structure and use that.
5800665 // Link it in place of the old line structure.
5800666 // ---------------------------------------------
5800667
5800668 nlp =
5800669 (line_t *) malloc (sizeof (line_t) + lp->len +
5800670 deltalen);
5800671 //
5800672 if (nlp == NULL)
5800673 {
5800674 fprintf (stderr, "Cannot get memory for line\n");
5800675 return;
5800676 }
5800677 //
5800678 nlp->len = lp->len + deltalen;
5800679 //
5800680 memcpy (nlp->data, lp->data, offset);
5800681 //
5800682 memcpy (&nlp->data[offset], newstr, newlen);
5800683 //
5800684 memcpy (&nlp->data[offset + newlen],
5800685 &lp->data[offset + oldlen],
5800686 lp->len - offset - oldlen);
5800687 //
5800688 nlp->next = lp->next;
5800689 nlp->prev = lp->prev;
5800690 nlp->prev->next = nlp;
5800691 nlp->next->prev = nlp;
5800692 //
5800693 if (curline == lp)
5800694 {
5800695 curline = nlp;
5800696 }
5800697 //
5800698 free (lp);
5800699 lp = nlp;
5800700 //
5800701 offset += newlen;
5800702 //
5800703 if (globalflag)
5800704 {
5800705 continue;
5800706 }
5800707 //
5800708 if (needprint)
5800709 {
5800710 printlines (num1, num1, false);
5800711 needprint = false;
5800712 }
5800713 //
5800714 lp = lp->next;
5800715 num1++;
5800716 }
5800717 //
5800718 if (!didsub)
5800719 {
5800720 fprintf (stderr,
5800721 "No substitutions found for \"%s\"\n",
5800722 oldstr);
5800723 }
5800724 }
5800725
5800726 //----------------------------------------------------------
5800727 // Search a line for the specified string starting at
5800728 // the specified offset in the line. Returns the
5800729 // offset of the found string, or -1.
5800730 //----------------------------------------------------------
5800731 len_t
5800732 findstring (line_t * lp, char *str, len_t len, len_t offset)
5800733 {
5800734 len_t left;
5800735 char *cp;
5800736 char *ncp;
5800737 //
5800738 cp = &lp->data[offset];
5800739 left = lp->len - offset;
5800740 //
5800741 while (left >= len)
5800742 {
5800743 ncp = memchr (cp, *str, left);
5800744 if (ncp == NULL)
5800745 {
5800746 return (len_t) - 1;
5800747 }
5800748 //
5800749 left -= (ncp - cp);
5800750 if (left < len)
5800751 {
5800752 return (len_t) - 1;
5800753 }
5800754 //
5800755 cp = ncp;
5800756 if (memcmp (cp, str, len) == 0)
5800757 {
5800758 return (len_t) (cp - lp->data);
5800759 }
5800760 //
5800761 cp++;
5800762 left--;
5800763 }
5800764 //
5800765 return (len_t) - 1;
5800766 }
5800767
5800768 //----------------------------------------------------------
5800769 // Add lines which are typed in by the user.
5800770 // The lines are inserted just before the specified
5800771 // line number.
5800772 // The lines are terminated by a line containing a
5800773 // single dot (ugly!), or by an end of file.
5800774 //----------------------------------------------------------
5800775 void
5800776 addlines (num_t num)
5800777 {
5800778 int len;
5800779 char buf[USERSIZE + 1];
5800780 //
5800781 while (fgets (buf, sizeof (buf), stdin))
5800782 {
5800783 if ((buf[0] == '.') && (buf[1] == '\n')
5800784 && (buf[2] == '\0'))
5800785 {
5800786 return;
5800787 }
5800788 //
5800789 len = strlen (buf);
5800790 //
5800791 if (len == 0)
5800792 {
5800793 return;
5800794 }
5800795 //
5800796 if (buf[len - 1] != '\n')
5800797 {
5800798 fprintf (stderr, "Line too long\n");
5800799 //
5800800 do
5800801 {
5800802 len = fgetc (stdin);
5800803 }
5800804 while ((len != EOF) && (len != '\n'));
5800805 //
5800806 return;
5800807 }
5800808 //
5800809 if (!insertline (num++, buf, len))
5800810 {
5800811 return;
5800812 }
5800813 }
5800814 }
5800815
5800816 //----------------------------------------------------------
5800817 // Parse a line number argument if it is present. This
5800818 // is a sum or difference of numbers, '.', '$', 'x, or
5800819 // a search string.
5800820 // Returns true if successful (whether or not there was
5800821 // a number).
5800822 // Returns false if there was a parsing error, with a
5800823 // message output.
5800824 // Whether there was a number is returned indirectly,
5800825 // as is the number.
5800826 // The character pointer which stopped the scan is also
5800827 // returned.
5800828 //----------------------------------------------------------
5800829 static bool
5800830 getnum (char **retcp, bool * rethavenum, num_t * retnum)
5800831 {
5800832 char *cp;
5800833 char *str;
5800834 bool havenum;
5800835 num_t value;
5800836 num_t num;
5800837 num_t sign;
5800838 //
5800839 cp = *retcp;
5800840 havenum = false;
5800841 value = 0;
5800842 sign = 1;
5800843 //
5800844 while (true)
5800845 {
5800846 while (isblank (*cp))
5800847 {
5800848 cp++;
5800849 }
5800850 //
5800851 switch (*cp)
5800852 {
5800853 case '.':
5800854 havenum = true;
5800855 num = curnum;
5800856 cp++;
5800857 break;
5800858 //
5800859 case '$':
5800860 havenum = true;
5800861 num = lastnum;
5800862 cp++;
5800863 break;
5800864 //
5800865 case '\'':
5800866 cp++;
5800867 if ((*cp < 'a') || (*cp > 'z'))
5800868 {
5800869 fprintf (stderr, "Bad mark name\n");
5800870 return false;
5800871 }
5800872 //
5800873 havenum = true;
5800874 num = marks[*cp++ - 'a'];
5800875 break;
5800876 //
5800877 case '/':
5800878 str = ++cp;
5800879 cp = strchr (str, '/');
5800880 if (cp)
5800881 {
5800882 *cp++ = '\0';
5800883 }
5800884 else
5800885 {
5800886 cp = "";
5800887 }
5800888 num = searchlines (str, curnum, lastnum);
5800889 if (num == 0)
5800890 {
5800891 return false;
5800892 }
5800893 //
5800894 havenum = true;
5800895 break;
5800896 //
5800897 default:
5800898 if (!isdigit (*cp))
5800899 {
5800900 *retcp = cp;
5800901 *rethavenum = havenum;
5800902 *retnum = value;
5800903 return true;
5800904 }
5800905 //
5800906 num = 0;
5800907 while (isdigit (*cp))
5800908 {
5800909 num = num * 10 + *cp++ - '0';
5800910 }
5800911 havenum = true;
5800912 break;
5800913 }
5800914 //
5800915 value += num * sign;
5800916 //
5800917 while (isblank (*cp))
5800918 {
5800919 cp++;
5800920 }
5800921 //
5800922 switch (*cp)
5800923 {
5800924 case '-':
5800925 sign = -1;
5800926 cp++;
5800927 break;
5800928 //
5800929 case '+':
5800930 sign = 1;
5800931 cp++;
5800932 break;
5800933 //
5800934 default:
5800935 *retcp = cp;
5800936 *rethavenum = havenum;
5800937 *retnum = value;
5800938 return true;
5800939 }
5800940 }
5800941 }
5800942
5800943 //----------------------------------------------------------
5800944 // Initialize everything for editing.
5800945 //----------------------------------------------------------
5800946 bool
5800947 initedit (void)
5800948 {
5800949 int i;
5800950 //
5800951 bufsize = INITBUFSIZE;
5800952 bufbase = malloc (bufsize);
5800953 //
5800954 if (bufbase == NULL)
5800955 {
5800956 fprintf (stderr, "No memory for buffer\n");
5800957 return false;
5800958 }
5800959 //
5800960 bufptr = bufbase;
5800961 bufused = 0;
5800962 //
5800963 lines.next = &lines;
5800964 lines.prev = &lines;
5800965 //
5800966 curline = NULL;
5800967 curnum = 0;
5800968 lastnum = 0;
5800969 dirty = false;
5800970 filename = NULL;
5800971 searchstring[0] = '\0';
5800972 //
5800973 for (i = 0; i < 26; i++)
5800974 {
5800975 marks[i] = 0;
5800976 }
5800977 //
5800978 return true;
5800979 }
5800980
5800981 //----------------------------------------------------------
5800982 // Finish editing.
5800983 //----------------------------------------------------------
5800984 void
5800985 termedit (void)
5800986 {
5800987 if (bufbase)
5800988 free (bufbase);
5800989 bufbase = NULL;
5800990 //
5800991 bufptr = NULL;
5800992 bufsize = 0;
5800993 bufused = 0;
5800994 //
5800995 if (filename)
5800996 free (filename);
5800997 filename = NULL;
5800998 //
5800999 searchstring[0] = '\0';
5801000 //
5801001 if (lastnum)
5801002 deletelines (1, lastnum);
5801003 //
5801004 lastnum = 0;
5801005 curnum = 0;
5801006 curline = NULL;
5801007 }
5801008
5801009 //----------------------------------------------------------
5801010 // Read lines from a file at the specified line number.
5801011 // Returns true if the file was successfully read.
5801012 //----------------------------------------------------------
5801013 bool
5801014 readlines (char *file, num_t num)
5801015 {
5801016 int fd;
5801017 int cc;
5801018 len_t len;
5801019 len_t linecount;
5801020 len_t charcount;
5801021 char *cp;
5801022 //
5801023 if ((num < 1) || (num > lastnum + 1))
5801024 {
5801025 fprintf (stderr, "Bad line for read\n");
5801026 return false;
5801027 }
5801028 //
5801029 fd = open (file, O_RDONLY);
5801030 if (fd < 0)
5801031 {
5801032 perror (file);
5801033 return false;
5801034 }
5801035 //
5801036 bufptr = bufbase;
5801037 bufused = 0;
5801038 linecount = 0;
5801039 charcount = 0;
5801040 //
5801041 printf ("\"%s\", ", file);
5801042 fflush (stdout);
5801043 //
5801044 do
5801045 {
5801046 cp = memchr (bufptr, '\n', bufused);
5801047 if (cp)
5801048 {
5801049 len = (cp - bufptr) + 1;
5801050 //
5801051 if (!insertline (num, bufptr, len))
5801052 {
5801053 close (fd);
5801054 return false;
5801055 }
5801056 //
5801057 bufptr += len;
5801058 bufused -= len;
5801059 charcount += len;
5801060 linecount++;
5801061 num++;
5801062 continue;
5801063 }
5801064 //
5801065 if (bufptr != bufbase)
5801066 {
5801067 memcpy (bufbase, bufptr, bufused);
5801068 bufptr = bufbase + bufused;
5801069 }
5801070 //
5801071 if (bufused >= bufsize)
5801072 {
5801073 len = (bufsize * 3) / 2;
5801074 cp = realloc (bufbase, len);
5801075 if (cp == NULL)
5801076 {
5801077 fprintf (stderr, "No memory for buffer\n");
5801078 close (fd);
5801079 return false;
5801080 }
5801081 //
5801082 bufbase = cp;
5801083 bufptr = bufbase + bufused;
5801084 bufsize = len;
5801085 }
5801086 //
5801087 cc = read (fd, bufptr, bufsize - bufused);
5801088 bufused += cc;
5801089 bufptr = bufbase;
5801090 }
5801091 while (cc > 0);
5801092 //
5801093 if (cc < 0)
5801094 {
5801095 perror (file);
5801096 close (fd);
5801097 return false;
5801098 }
5801099 //
5801100 if (bufused)
5801101 {
5801102 if (!insertline (num, bufptr, bufused))
5801103 {
5801104 close (fd);
5801105 return -1;
5801106 }
5801107 linecount++;
5801108 charcount += bufused;
5801109 }
5801110 //
5801111 close (fd);
5801112 //
5801113 printf ("%d lines%s, %d chars\n",
5801114 linecount, (bufused ? " (incomplete)" : ""),
5801115 charcount);
5801116 //
5801117 return true;
5801118 }
5801119
5801120 //----------------------------------------------------------
5801121 // Write the specified lines out to the specified file.
5801122 // Returns true if successful, or false on an error
5801123 // with a message output.
5801124 //----------------------------------------------------------
5801125 bool
5801126 writelines (char *file, num_t num1, num_t num2)
5801127 {
5801128 int fd;
5801129 line_t *lp;
5801130 len_t linecount;
5801131 len_t charcount;
5801132 //
5801133 if ((num1 < 1) || (num2 > lastnum) || (num1 > num2))
5801134 {
5801135 fprintf (stderr, "Bad line range for write\n");
5801136 return false;
5801137 }
5801138 //
5801139 linecount = 0;
5801140 charcount = 0;
5801141 //
5801142 fd = creat (file, 0666);
5801143 if (fd < 0)
5801144 {
5801145 perror (file);
5801146 return false;
5801147 }
5801148 //
5801149 printf ("\"%s\", ", file);
5801150 fflush (stdout);
5801151 //
5801152 lp = findline (num1);
5801153 if (lp == NULL)
5801154 {
5801155 close (fd);
5801156 return false;
5801157 }
5801158 //
5801159 while (num1++ <= num2)
5801160 {
5801161 if (write (fd, lp->data, lp->len) != lp->len)
5801162 {
5801163 perror (file);
5801164 close (fd);
5801165 return false;
5801166 }
5801167 //
5801168 charcount += lp->len;
5801169 linecount++;
5801170 lp = lp->next;
5801171 }
5801172 //
5801173 if (close (fd) < 0)
5801174 {
5801175 perror (file);
5801176 return false;
5801177 }
5801178 //
5801179 printf ("%d lines, %d chars\n", linecount, charcount);
5801180 //
5801181 return true;
5801182 }
5801183
5801184 //----------------------------------------------------------
5801185 // Print lines in a specified range.
5801186 // The last line printed becomes the current line.
5801187 // If expandflag is true, then the line is printed
5801188 // specially to show magic characters.
5801189 //----------------------------------------------------------
5801190 bool
5801191 printlines (num_t num1, num_t num2, bool expandflag)
5801192 {
5801193 line_t *lp;
5801194 unsigned char *cp;
5801195 int ch;
5801196 len_t count;
5801197 //
5801198 if ((num1 < 1) || (num2 > lastnum) || (num1 > num2))
5801199 {
5801200 fprintf (stderr, "Bad line range for print\n");
5801201 return false;
5801202 }
5801203 //
5801204 lp = findline (num1);
5801205 if (lp == NULL)
5801206 {
5801207 return false;
5801208 }
5801209 //
5801210 while (num1 <= num2)
5801211 {
5801212 if (!expandflag)
5801213 {
5801214 write (STDOUT_FILENO, lp->data, lp->len);
5801215 setcurnum (num1++);
5801216 lp = lp->next;
5801217 continue;
5801218 }
5801219
5801220 // -----------------------------------------------
5801221 // Show control characters and characters with
5801222 // the high bit set specially.
5801223 // -----------------------------------------------
5801224
5801225 cp = (unsigned char *) lp->data;
5801226 count = lp->len;
5801227 //
5801228 if ((count > 0) && (cp[count - 1] == '\n'))
5801229 {
5801230 count--;
5801231 }
5801232 //
5801233 while (count-- > 0)
5801234 {
5801235 ch = *cp++;
5801236 if (ch & 0x80)
5801237 {
5801238 fputs ("M-", stdout);
5801239 ch &= 0x7f;
5801240 }
5801241 if (ch < ' ')
5801242 {
5801243 fputc ('^', stdout);
5801244 ch += '@';
5801245 }
5801246 if (ch == 0x7f)
5801247 {
5801248 fputc ('^', stdout);
5801249 ch = '?';
5801250 }
5801251 fputc (ch, stdout);
5801252 }
5801253 //
5801254 fputs ("$\n", stdout);
5801255 //
5801256 setcurnum (num1++);
5801257 lp = lp->next;
5801258 }
5801259 //
5801260 return true;
5801261 }
5801262
5801263 //----------------------------------------------------------
5801264 // Insert a new line with the specified text.
5801265 // The line is inserted so as to become the specified
5801266 // line, thus pushing any existing and further lines
5801267 // down one.
5801268 // The inserted line is also set to become the current
5801269 // line.
5801270 // Returns true if successful.
5801271 //----------------------------------------------------------
5801272 bool
5801273 insertline (num_t num, char *data, len_t len)
5801274 {
5801275 line_t *newlp;
5801276 line_t *lp;
5801277 //
5801278 if ((num < 1) || (num > lastnum + 1))
5801279 {
5801280 fprintf (stderr, "Inserting at bad line number\n");
5801281 return false;
5801282 }
5801283 //
5801284 newlp = (line_t *) malloc (sizeof (line_t) + len - 1);
5801285 if (newlp == NULL)
5801286 {
5801287 fprintf (stderr,
5801288 "Failed to allocate memory for line\n");
5801289 return false;
5801290 }
5801291 //
5801292 memcpy (newlp->data, data, len);
5801293 newlp->len = len;
5801294 //
5801295 if (num > lastnum)
5801296 {
5801297 lp = &lines;
5801298 }
5801299 else
5801300 {
5801301 lp = findline (num);
5801302 if (lp == NULL)
5801303 {
5801304 free ((char *) newlp);
5801305 return false;
5801306 }
5801307 }
5801308 //
5801309 newlp->next = lp;
5801310 newlp->prev = lp->prev;
5801311 lp->prev->next = newlp;
5801312 lp->prev = newlp;
5801313 //
5801314 lastnum++;
5801315 dirty = true;
5801316 //
5801317 return setcurnum (num);
5801318 }
5801319
5801320 //----------------------------------------------------------
5801321 // Delete lines from the given range.
5801322 //----------------------------------------------------------
5801323 bool
5801324 deletelines (num_t num1, num_t num2)
5801325 {
5801326 line_t *lp;
5801327 line_t *nlp;
5801328 line_t *plp;
5801329 num_t count;
5801330 //
5801331 if ((num1 < 1) || (num2 > lastnum) || (num1 > num2))
5801332 {
5801333 fprintf (stderr, "Bad line numbers for delete\n");
5801334 return false;
5801335 }
5801336 //
5801337 lp = findline (num1);
5801338 if (lp == NULL)
5801339 {
5801340 return false;
5801341 }
5801342 //
5801343 if ((curnum >= num1) && (curnum <= num2))
5801344 {
5801345 if (num2 < lastnum)
5801346 {
5801347 setcurnum (num2 + 1);
5801348 }
5801349 else if (num1 > 1)
5801350 {
5801351 setcurnum (num1 - 1);
5801352 }
5801353 else
5801354 {
5801355 curnum = 0;
5801356 }
5801357 }
5801358 //
5801359 count = num2 - num1 + 1;
5801360 //
5801361 if (curnum > num2)
5801362 {
5801363 curnum -= count;
5801364 }
5801365 //
5801366 lastnum -= count;
5801367 //
5801368 while (count-- > 0)
5801369 {
5801370 nlp = lp->next;
5801371 plp = lp->prev;
5801372 plp->next = nlp;
5801373 nlp->prev = plp;
5801374 lp->next = NULL;
5801375 lp->prev = NULL;
5801376 lp->len = 0;
5801377 free (lp);
5801378 lp = nlp;
5801379 }
5801380 //
5801381 dirty = true;
5801382 //
5801383 return true;
5801384 }
5801385
5801386 //----------------------------------------------------------
5801387 // Search for a line which contains the specified
5801388 // string.
5801389 // If the string is NULL, then the previously searched
5801390 // for string is used. The currently searched for
5801391 // string is saved for future use.
5801392 // Returns the line number which matches, or 0 if there
5801393 // was no match with an error printed.
5801394 //----------------------------------------------------------
5801395 num_t
5801396 searchlines (char *str, num_t num1, num_t num2)
5801397 {
5801398 line_t *lp;
5801399 int len;
5801400 //
5801401 if ((num1 < 1) || (num2 > lastnum) || (num1 > num2))
5801402 {
5801403 fprintf (stderr, "Bad line numbers for search\n");
5801404 return 0;
5801405 }
5801406 //
5801407 if (*str == '\0')
5801408 {
5801409 if (searchstring[0] == '\0')
5801410 {
5801411 fprintf (stderr, "No previous search string\n");
5801412 return 0;
5801413 }
5801414 str = searchstring;
5801415 }
5801416 //
5801417 if (str != searchstring)
5801418 {
5801419 strcpy (searchstring, str);
5801420 }
5801421 //
5801422 len = strlen (str);
5801423 //
5801424 lp = findline (num1);
5801425 if (lp == NULL)
5801426 {
5801427 return 0;
5801428 }
5801429 //
5801430 while (num1 <= num2)
5801431 {
5801432 if (findstring (lp, str, len, 0) >= 0)
5801433 {
5801434 return num1;
5801435 }
5801436 //
5801437 num1++;
5801438 lp = lp->next;
5801439 }
5801440 //
5801441 fprintf (stderr, "Cannot find string \"%s\"\n", str);
5801442 //
5801443 return 0;
5801444 }
5801445
5801446 //----------------------------------------------------------
5801447 // Return a pointer to the specified line number.
5801448 //----------------------------------------------------------
5801449 line_t *
5801450 findline (num_t num)
5801451 {
5801452 line_t *lp;
5801453 num_t lnum;
5801454 //
5801455 if ((num < 1) || (num > lastnum))
5801456 {
5801457 fprintf (stderr,
5801458 "Line number %d does not exist\n", num);
5801459 return NULL;
5801460 }
5801461 //
5801462 if (curnum <= 0)
5801463 {
5801464 curnum = 1;
5801465 curline = lines.next;
5801466 }
5801467 //
5801468 if (num == curnum)
5801469 {
5801470 return curline;
5801471 }
5801472 //
5801473 lp = curline;
5801474 lnum = curnum;
5801475 //
5801476 if (num < (curnum / 2))
5801477 {
5801478 lp = lines.next;
5801479 lnum = 1;
5801480 }
5801481 else if (num > ((curnum + lastnum) / 2))
5801482 {
5801483 lp = lines.prev;
5801484 lnum = lastnum;
5801485 }
5801486 //
5801487 while (lnum < num)
5801488 {
5801489 lp = lp->next;
5801490 lnum++;
5801491 }
5801492 //
5801493 while (lnum > num)
5801494 {
5801495 lp = lp->prev;
5801496 lnum--;
5801497 }
5801498 //
5801499 return lp;
5801500 }
5801501
5801502 //----------------------------------------------------------
5801503 // Set the current line number.
5801504 // Returns true if successful.
5801505 //----------------------------------------------------------
5801506 bool
5801507 setcurnum (num_t num)
5801508 {
5801509 line_t *lp;
5801510 //
5801511 lp = findline (num);
5801512 if (lp == NULL)
5801513 {
5801514 return false;
5801515 }
5801516 //
5801517 curnum = num;
5801518 curline = lp;
5801519 //
5801520 return true;
5801521 }
5801522
5801523 /* END CODE */
|