| 4250001 //----------------------------------------------------------------------
4250002 // 2009.08.18
4250003 // Modified by Daniele Giacomini for `os16', to harmonize with it,
4250004 // even, when possible, on coding style.
4250005 //
4250006 // The original was taken form ELKS sources: `elkscmd/misc_utils/ed.c'.
4250007 //----------------------------------------------------------------------
4250008 //
4250009 // Copyright (c) 1993 by David I. Bell
4250010 // Permission is granted to use, distribute, or modify this source,
4250011 // provided that this copyright notice remains intact.
4250012 //
4250013 // The "ed" built-in command (much simplified)
4250014 //
4250015 //----------------------------------------------------------------------
4250016 
4250017 #include <stdio.h>
4250018 #include <ctype.h>
4250019 #include <unistd.h>
4250020 #include <stdbool.h>
4250021 #include <string.h>
4250022 #include <stdlib.h>
4250023 #include <fcntl.h>
4250024 //----------------------------------------------------------------------
4250025 #define isoctal(ch)  (((ch) >= '0') && ((ch) <= '7'))
4250026 #define USERSIZE     1024       /* max line length typed in by user */
4250027 #define INITBUFSIZE  1024       /* initial buffer size */
4250028 //----------------------------------------------------------------------
4250029 typedef int num_t;
4250030 typedef int len_t;
4250031 //
4250032 // The following is the type definition of structure `line_t', but the
4250033 // structure contains pointers to the same kind of type. With the
4250034 // compiler Bcc, it is the only way to declare it.
4250035 //
4250036 typedef struct line line_t;
4250037 //
4250038 struct line {
4250039     line_t *next;
4250040     line_t *prev;
4250041     len_t   len;
4250042     char    data[1];
4250043 };
4250044 //
4250045 static  line_t  lines;
4250046 static  line_t *curline;
4250047 static  num_t   curnum;
4250048 static  num_t   lastnum;
4250049 static  num_t   marks[26];
4250050 static  bool    dirty;
4250051 static  char    *filename;
4250052 static  char    searchstring[USERSIZE];
4250053 //
4250054 static  char   *bufbase;
4250055 static  char   *bufptr;
4250056 static  len_t   bufused;
4250057 static  len_t   bufsize;
4250058 //----------------------------------------------------------------------
4250059 static  void    docommands  (void);
4250060 static  void    subcommand  (char *cp, num_t num1, num_t num2);
4250061 static  bool    getnum      (char **retcp, bool *rethavenum,
4250062                              num_t *retnum);
4250063 static  bool    setcurnum   (num_t num);
4250064 static  bool    initedit    (void);
4250065 static  void    termedit    (void);
4250066 static  void    addlines    (num_t num);
4250067 static  bool    insertline  (num_t num, char *data, len_t len);
4250068 static  bool    deletelines (num_t num1, num_t num2);
4250069 static  bool    printlines  (num_t num1, num_t num2, bool expandflag);
4250070 static  bool    writelines  (char *file, num_t num1, num_t num2);
4250071 static  bool    readlines   (char *file, num_t num);
4250072 static  num_t   searchlines (char *str, num_t num1, num_t num2);
4250073 static  len_t   findstring  (line_t *lp, char *str, len_t len,
4250074                              len_t offset);
4250075 static  line_t *findline    (num_t num);
4250076 //----------------------------------------------------------------------
4250077 // Main.
4250078 //----------------------------------------------------------------------
4250079 int
4250080 main (int argc, char *argv[], char *envp[])
4250081 {
4250082     if (!initedit ()) return (2);
4250083     //
4250084     if (argc > 1)
4250085       {
4250086         filename = strdup (argv[1]);
4250087         if (filename == NULL)
4250088           {
4250089             fprintf (stderr, "No memory\n");
4250090             termedit ();
4250091             return (1);
4250092           }
4250093         //
4250094         if (!readlines (filename, 1))
4250095           {
4250096             termedit ();
4250097             return (0);
4250098           }
4250099         //
4250100         if (lastnum) setcurnum(1);
4250101         //
4250102         dirty = false;
4250103       }
4250104     //
4250105     docommands ();
4250106     //
4250107     termedit ();
4250108     return (0);
4250109 }
4250110 //----------------------------------------------------------------------
4250111 // Read commands until we are told to stop.
4250112 //----------------------------------------------------------------------
4250113 void
4250114 docommands (void)
4250115 {
4250116     char   *cp;
4250117     int     len;
4250118     num_t   num1;
4250119     num_t   num2;
4250120     bool    have1;
4250121     bool    have2;
4250122     char    buf[USERSIZE];
4250123     //
4250124     while (true)
4250125       {
4250126         printf(": ");
4250127         fflush (stdout);
4250128         //
4250129         if (fgets (buf, sizeof(buf), stdin) == NULL)
4250130           {
4250131             return;
4250132           }
4250133         //
4250134         len = strlen (buf);
4250135         if (len == 0)
4250136           {
4250137             return;
4250138           }
4250139         //
4250140         cp = &buf[len - 1];
4250141         if (*cp != '\n')
4250142           {
4250143             fprintf(stderr, "Command line too long\n");
4250144             do
4250145               {
4250146                 len = fgetc(stdin);
4250147               }
4250148             while ((len != EOF) && (len != '\n'));
4250149             //
4250150             continue;
4250151           }
4250152         //
4250153         while ((cp > buf) && isblank (cp[-1]))
4250154           {
4250155             cp--;
4250156           }
4250157         //
4250158         *cp = '\0';
4250159         //
4250160         cp = buf;
4250161         //
4250162         while (isblank (*cp))
4250163           {
4250164             //*cp++;
4250165             cp++;
4250166           }
4250167         //
4250168         have1 = false;
4250169         have2 = false;
4250170         //
4250171         if ((curnum == 0) && (lastnum > 0))
4250172           {
4250173             curnum = 1;
4250174             curline = lines.next;
4250175           }
4250176         //
4250177         if (!getnum (&cp, &have1, &num1))
4250178           {
4250179             continue;
4250180           }
4250181         //
4250182         while (isblank (*cp))
4250183           {
4250184             cp++;
4250185           }
4250186         //
4250187         if (*cp == ',')
4250188           {
4250189             cp++;
4250190             if (!getnum (&cp, &have2, &num2))
4250191               {
4250192                 continue;
4250193               }
4250194             //
4250195             if (!have1)
4250196               {
4250197                 num1 = 1;
4250198               }
4250199             if (!have2)
4250200               {
4250201                 num2 = lastnum;
4250202               }
4250203             have1 = true;
4250204             have2 = true;
4250205           }
4250206         //
4250207         if (!have1)
4250208           {
4250209             num1 = curnum;
4250210           }
4250211         if (!have2)
4250212           {
4250213             num2 = num1;
4250214           }
4250215         //
4250216         // Command interpretation switch.
4250217         //
4250218         switch (*cp++)
4250219           {
4250220             case 'a':
4250221                 addlines (num1 + 1);
4250222                 break;
4250223                 //
4250224             case 'c':
4250225                 deletelines (num1, num2);
4250226                 addlines (num1);
4250227                 break;
4250228                 //
4250229             case 'd':
4250230                 deletelines (num1, num2);
4250231                 break;
4250232                 //
4250233             case 'f':
4250234                 if (*cp && !isblank (*cp))
4250235                   {
4250236                     fprintf (stderr, "Bad file command\n");
4250237                     break;
4250238                   }
4250239                 //
4250240                 while (isblank (*cp))
4250241                   {
4250242                     cp++;
4250243                   }
4250244                 if (*cp == '\0')
4250245                   {
4250246                     if (filename)
4250247                       {
4250248                         printf ("\"%s\"\n", filename);
4250249                       }
4250250                     else
4250251                       {
4250252                         printf ("No filename\n");
4250253                       }
4250254                     break;
4250255                   }
4250256                 //
4250257                 cp = strdup (cp);
4250258                 //
4250259                 if (cp == NULL)
4250260                   {
4250261                     fprintf (stderr, "No memory for filename\n");
4250262                     break;
4250263                   }
4250264                 //
4250265                 if (filename)
4250266                   {
4250267                     free(filename);
4250268                   }
4250269                 //
4250270                 filename = cp;
4250271                 break;
4250272                 //
4250273             case 'i':
4250274                 addlines (num1);
4250275                 break;
4250276                 //
4250277             case 'k':
4250278                 while (isblank(*cp))
4250279                   {
4250280                     cp++;
4250281                   }
4250282                 //
4250283                 if ((*cp < 'a') || (*cp > 'a') || cp[1])
4250284                   {
4250285                     fprintf (stderr, "Bad mark name\n");
4250286                     break;
4250287                   }
4250288                 //
4250289                 marks[*cp - 'a'] = num2;
4250290                 break;
4250291                 //
4250292             case 'l':
4250293                 printlines (num1, num2, true);
4250294                 break;
4250295                 //
4250296             case 'p':
4250297                 printlines (num1, num2, false);
4250298                 break;
4250299                 //
4250300             case 'q':
4250301                 while (isblank(*cp))
4250302                   {
4250303                     cp++;
4250304                   }
4250305                 //
4250306                 if (have1 || *cp)
4250307                   {
4250308                     fprintf (stderr, "Bad quit command\n");
4250309                     break;
4250310                   }
4250311                 //
4250312                 if (!dirty)
4250313                   {
4250314                     return;
4250315                   }
4250316                 //
4250317                 printf ("Really quit? ");
4250318                 fflush (stdout);
4250319                 //
4250320                 buf[0] = '\0';
4250321                 fgets (buf, sizeof(buf), stdin);
4250322                 cp = buf;
4250323                 //
4250324                 while (isblank (*cp))
4250325                   {
4250326                     cp++;
4250327                   }
4250328                 //
4250329                 if ((*cp == 'y') || (*cp == 'Y'))
4250330                   {
4250331                     return;
4250332                   }
4250333                 //
4250334                 break;
4250335                 //
4250336             case 'r':
4250337                 if (*cp && !isblank(*cp))
4250338                   {
4250339                     fprintf (stderr, "Bad read command\n");
4250340                     break;
4250341                   }
4250342                 //
4250343                 while (isblank(*cp))
4250344                   {
4250345                     cp++;
4250346                   }
4250347                 //
4250348                 if (*cp == '\0')
4250349                   {
4250350                     fprintf (stderr, "No filename\n");
4250351                     break;
4250352                   }
4250353                 //
4250354                 if (!have1)
4250355                   {
4250356                     num1 = lastnum;
4250357                   }
4250358                 //
4250359                 // Open the file and add to the buffer
4250360                 // at the next line.
4250361                 //
4250362                 if (readlines (cp, num1 + 1))
4250363                   {
4250364                     //
4250365                     // If the file open fails, just
4250366                     // break the command.
4250367                     //
4250368                     break;
4250369                   }
4250370                 //
4250371                 // Set the default file name, if no
4250372                 // previous name is available.
4250373                 //
4250374                 if (filename == NULL)
4250375                   {
4250376                     filename = strdup (cp);
4250377                   }
4250378                 //
4250379                 break;
4250380 
4250381             case 's':
4250382                 subcommand (cp, num1, num2);
4250383                 break;
4250384                 //
4250385             case 'w':
4250386                 if (*cp && !isblank(*cp))
4250387                   {
4250388                     fprintf(stderr, "Bad write command\n");
4250389                     break;
4250390                   }
4250391                 //
4250392                 while (isblank(*cp))
4250393                   {
4250394                     cp++;
4250395                   }
4250396                 //
4250397                 if (!have1)
4250398                   {
4250399                     num1 = 1;
4250400                     num2 = lastnum;
4250401                   }
4250402                 //
4250403                 // If the file name is not specified, use the
4250404                 // default one.
4250405                 //
4250406                 if (*cp == '\0')
4250407                   {
4250408                     cp = filename;
4250409                   }
4250410                 //
4250411                 // If even the default file name is not specified,
4250412                 // tell it.
4250413                 //
4250414                 if (cp == NULL)
4250415                   {
4250416                     fprintf (stderr, "No file name specified\n");
4250417                     break;
4250418                   }
4250419                 //
4250420                 // Write the file.
4250421                 //
4250422                 writelines (cp, num1, num2);
4250423                 //
4250424                 break;
4250425                 //
4250426             case 'z':
4250427                 switch (*cp)
4250428                   {
4250429                     case '-':
4250430                         printlines (curnum-21, curnum, false);
4250431                         break;
4250432                     case '.':
4250433                         printlines (curnum-11, curnum+10, false);
4250434                         break;
4250435                     default:
4250436                         printlines (curnum, curnum+21, false);
4250437                         break;
4250438                   }
4250439                 break;
4250440                 //
4250441             case '.':
4250442                 if (have1)
4250443                   {
4250444                     fprintf (stderr, "No arguments allowed\n");
4250445                     break;
4250446                   }
4250447                 printlines (curnum, curnum, false);
4250448                 break;
4250449                 //
4250450             case '-':
4250451                 if (setcurnum (curnum - 1))
4250452                   {
4250453                     printlines (curnum, curnum, false);
4250454                   }
4250455                 break;
4250456                 //
4250457             case '=':
4250458                 printf ("%d\n", num1);
4250459                 break;
4250460                 //
4250461             case '\0':
4250462                 if (have1)
4250463                   {
4250464                     printlines (num2, num2, false);
4250465                     break;
4250466                   }
4250467                 //
4250468                 if (setcurnum (curnum + 1))
4250469                   {
4250470                     printlines (curnum, curnum, false);
4250471                   }
4250472                 break;
4250473                 //
4250474             default:
4250475                 fprintf (stderr, "Unimplemented command\n");
4250476                 break;
4250477           }
4250478       }
4250479 }
4250480 //----------------------------------------------------------------------
4250481 // Do the substitute command.
4250482 // The current line is set to the last substitution done.
4250483 //----------------------------------------------------------------------
4250484 void
4250485 subcommand (char *cp, num_t num1, num_t num2)
4250486 {
4250487     int     delim;
4250488     char   *oldstr;
4250489     char   *newstr;
4250490     len_t   oldlen;
4250491     len_t   newlen;
4250492     len_t   deltalen;
4250493     len_t   offset;
4250494     line_t *lp;
4250495     line_t *nlp;
4250496     bool    globalflag;
4250497     bool    printflag;
4250498     bool    didsub;
4250499     bool    needprint;
4250500 
4250501     if ((num1 < 1) || (num2 > lastnum) || (num1 > num2))
4250502       {
4250503         fprintf (stderr, "Bad line range for substitute\n");
4250504         return;
4250505       }
4250506     //
4250507     globalflag = false;
4250508     printflag = false;
4250509     didsub = false;
4250510     needprint = false;
4250511     //
4250512     if (isblank (*cp) || (*cp == '\0'))
4250513       {
4250514         fprintf (stderr, "Bad delimiter for substitute\n");
4250515         return;
4250516       }
4250517     //
4250518     delim = *cp++;
4250519     oldstr = cp;
4250520     //
4250521     cp = strchr (cp, delim);
4250522     //
4250523     if (cp == NULL)
4250524       {
4250525         fprintf (stderr, "Missing 2nd delimiter for substitute\n");
4250526         return;
4250527       }
4250528     //
4250529     *cp++ = '\0';
4250530     //
4250531     newstr = cp;
4250532     cp = strchr (cp, delim);
4250533     //
4250534     if (cp)
4250535       {
4250536         *cp++ = '\0';
4250537       }
4250538     else
4250539       {
4250540         cp = "";
4250541       }
4250542     while (*cp)
4250543       {
4250544         switch (*cp++)
4250545           {
4250546             case 'g':
4250547                 globalflag = true;
4250548                 break;
4250549                 //
4250550             case 'p':
4250551                 printflag = true;
4250552                 break;
4250553                 //
4250554             default:
4250555                 fprintf (stderr, "Unknown option for substitute\n");
4250556                 return;
4250557           }
4250558       }
4250559     //
4250560     if (*oldstr == '\0')
4250561       {
4250562         if (searchstring[0] == '\0')
4250563           {
4250564             fprintf (stderr, "No previous search string\n");
4250565             return;
4250566           }
4250567         oldstr = searchstring;
4250568       }
4250569     //
4250570     if (oldstr != searchstring)
4250571       {
4250572         strcpy (searchstring, oldstr);
4250573       }
4250574     //
4250575     lp = findline (num1);
4250576     if (lp == NULL)
4250577       {
4250578         return;
4250579       }
4250580     //
4250581     oldlen = strlen(oldstr);
4250582     newlen = strlen(newstr);
4250583     deltalen = newlen - oldlen;
4250584     offset = 0;
4250585     //
4250586     while (num1 <= num2)
4250587       {
4250588         offset = findstring (lp, oldstr, oldlen, offset);
4250589         if (offset < 0)
4250590           {
4250591             if (needprint)
4250592               {
4250593                 printlines (num1, num1, false);
4250594                 needprint = false;
4250595               }
4250596             //
4250597             offset = 0;
4250598             lp = lp->next;
4250599             num1++;
4250600             continue;
4250601           }
4250602         //
4250603         needprint = printflag;
4250604         didsub = true;
4250605         dirty = true;
4250606 
4250607         //--------------------------------------------------------------
4250608         // If the replacement string is the same size or shorter
4250609         // than the old string, then the substitution is easy.
4250610         //--------------------------------------------------------------
4250611 
4250612         if (deltalen <= 0)
4250613           {
4250614             memcpy (&lp->data[offset], newstr, newlen);
4250615             //
4250616             if (deltalen)
4250617               {
4250618                 memcpy (&lp->data[offset + newlen],
4250619                         &lp->data[offset + oldlen],
4250620                         lp->len - offset - oldlen);
4250621                 //
4250622                 lp->len += deltalen;
4250623               }
4250624             //
4250625             offset += newlen;
4250626             //
4250627             if (globalflag)
4250628               {
4250629                 continue;
4250630               }
4250631             //
4250632             if (needprint)
4250633               {
4250634                 printlines(num1, num1, false);
4250635                 needprint = false;
4250636               }
4250637             //
4250638             lp = nlp->next;
4250639             num1++;
4250640             continue;
4250641           }
4250642 
4250643         //--------------------------------------------------------------
4250644         // The new string is larger, so allocate a new line
4250645         // structure and use that.  Link it in in place of
4250646         // the old line structure.
4250647         //--------------------------------------------------------------
4250648 
4250649         nlp = (line_t *) malloc (sizeof (line_t) + lp->len + deltalen);
4250650         //
4250651         if (nlp == NULL)
4250652           {
4250653             fprintf (stderr, "Cannot get memory for line\n");
4250654             return;
4250655           }
4250656         //
4250657         nlp->len = lp->len + deltalen;
4250658         //
4250659         memcpy (nlp->data, lp->data, offset);
4250660         //
4250661         memcpy (&nlp->data[offset], newstr, newlen);
4250662         //
4250663         memcpy (&nlp->data[offset + newlen],
4250664                 &lp->data[offset + oldlen],
4250665                 lp->len - offset - oldlen);
4250666         //
4250667         nlp->next = lp->next;
4250668         nlp->prev = lp->prev;
4250669         nlp->prev->next = nlp;
4250670         nlp->next->prev = nlp;
4250671         //
4250672         if (curline == lp)
4250673           {
4250674             curline = nlp;
4250675           }
4250676         //
4250677         free(lp);
4250678         lp = nlp;
4250679         //
4250680         offset += newlen;
4250681         //
4250682         if (globalflag)
4250683           {
4250684             continue;
4250685           }
4250686         //
4250687         if (needprint)
4250688           {
4250689             printlines (num1, num1, false);
4250690             needprint = false;
4250691           }
4250692         //
4250693         lp = lp->next;
4250694         num1++;
4250695       }
4250696     //
4250697     if (!didsub)
4250698       {
4250699         fprintf (stderr, "No substitutions found for \"%s\"\n", oldstr);
4250700       }
4250701 }
4250702 //----------------------------------------------------------------------
4250703 // Search a line for the specified string starting at the specified
4250704 // offset in the line.  Returns the offset of the found string, or -1.
4250705 //----------------------------------------------------------------------
4250706 len_t
4250707 findstring (line_t *lp, char *str, len_t len, len_t offset)
4250708 {
4250709     len_t    left;
4250710     char    *cp;
4250711     char    *ncp;
4250712     //
4250713     cp = &lp->data[offset];
4250714     left = lp->len - offset;
4250715     //
4250716     while (left >= len)
4250717       {
4250718         ncp = memchr(cp, *str, left);
4250719         if (ncp == NULL)
4250720           {
4250721             return (len_t) -1;
4250722           }
4250723         //
4250724         left -= (ncp - cp);
4250725         if (left < len)
4250726           {
4250727             return (len_t) -1;
4250728           }
4250729         //
4250730         cp = ncp;
4250731         if (memcmp(cp, str, len) == 0)
4250732           {
4250733             return (len_t) (cp - lp->data);
4250734           }
4250735         //
4250736         cp++;
4250737         left--;
4250738       }
4250739     //
4250740     return (len_t) -1;
4250741 }
4250742 //----------------------------------------------------------------------
4250743 // Add lines which are typed in by the user.
4250744 // The lines are inserted just before the specified line number.
4250745 // The lines are terminated by a line containing a single dot (ugly!),
4250746 // or by an end of file.
4250747 //----------------------------------------------------------------------
4250748 void
4250749 addlines (num_t num)
4250750 {
4250751     int     len;
4250752     char    buf[USERSIZE + 1];
4250753     //
4250754     while (fgets (buf, sizeof (buf), stdin))
4250755       {
4250756         if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0'))
4250757           {
4250758             return;
4250759           }
4250760         //
4250761         len = strlen (buf);
4250762         //
4250763         if (len == 0)
4250764           {
4250765             return;
4250766           }
4250767         //
4250768         if (buf[len - 1] != '\n')
4250769          {
4250770             fprintf (stderr, "Line too long\n");
4250771             //
4250772             do
4250773               {
4250774                 len = fgetc(stdin);
4250775               }
4250776             while ((len != EOF) && (len != '\n'));
4250777             //
4250778             return;
4250779           }
4250780         //
4250781         if (!insertline (num++, buf, len))
4250782           {
4250783             return;
4250784           }
4250785       }
4250786 }
4250787 //----------------------------------------------------------------------
4250788 // Parse a line number argument if it is present.  This is a sum
4250789 // or difference of numbers, '.', '$', 'x, or a search string.
4250790 // Returns true if successful (whether or not there was a number). 
4250791 // Returns false if there was a parsing error, with a message output.
4250792 // Whether there was a number is returned indirectly, as is the number.
4250793 // The character pointer which stopped the scan is also returned.
4250794 //----------------------------------------------------------------------
4250795 static bool
4250796 getnum (char **retcp, bool *rethavenum, num_t *retnum)
4250797 {
4250798     char   *cp;
4250799     char   *str;
4250800     bool    havenum;
4250801     num_t   value;
4250802     num_t   num;
4250803     num_t   sign;
4250804     //
4250805     cp = *retcp;
4250806     havenum = false;
4250807     value = 0;
4250808     sign = 1;
4250809     //
4250810     while (true)
4250811       {
4250812         while (isblank(*cp))
4250813           {
4250814             cp++;
4250815           }
4250816         //
4250817         switch (*cp)
4250818           {
4250819             case '.':
4250820                 havenum = true;
4250821                 num = curnum;
4250822                 cp++;
4250823                 break;
4250824                 //
4250825             case '$':
4250826                 havenum = true;
4250827                 num = lastnum;
4250828                 cp++;
4250829                 break;
4250830                 //
4250831             case '\'':
4250832                 cp++;
4250833                 if ((*cp < 'a') || (*cp > 'z'))
4250834                   {
4250835                     fprintf (stderr, "Bad mark name\n");
4250836                     return false;
4250837                   }
4250838                 //
4250839                 havenum = true;
4250840                 num = marks[*cp++ - 'a'];
4250841                 break;
4250842                 //
4250843             case '/':
4250844                 str = ++cp;
4250845                 cp = strchr (str, '/');
4250846                 if (cp)
4250847                   {
4250848                     *cp++ = '\0';
4250849                   }
4250850                 else
4250851                   {
4250852                     cp = "";
4250853                   }
4250854                 num = searchlines (str, curnum, lastnum);
4250855                 if (num == 0)
4250856                   {
4250857                     return false;
4250858                   }
4250859                 //
4250860                 havenum = true;
4250861                 break;
4250862                 //
4250863             default:
4250864                 if (!isdigit (*cp))
4250865                  {
4250866                     *retcp = cp;
4250867                     *rethavenum = havenum;
4250868                     *retnum = value;
4250869                     return true;
4250870                   }
4250871                 //
4250872                 num = 0;
4250873                 while (isdigit(*cp))
4250874                   {
4250875                     num = num * 10 + *cp++ - '0';
4250876                   }
4250877                 havenum = true;
4250878                 break;
4250879           }
4250880         //
4250881         value += num * sign;
4250882         //
4250883         while (isblank (*cp))
4250884           {
4250885             cp++;
4250886           }
4250887         //
4250888         switch (*cp)
4250889           {
4250890             case '-':
4250891                 sign = -1;
4250892                 cp++;
4250893                 break;
4250894                 //
4250895             case '+':
4250896                 sign = 1;
4250897                 cp++;
4250898                 break;
4250899                 //
4250900             default:
4250901                 *retcp = cp;
4250902                 *rethavenum = havenum;
4250903                 *retnum = value;
4250904                 return true;
4250905           }
4250906     }
4250907 }
4250908 //----------------------------------------------------------------------
4250909 // Initialize everything for editing.
4250910 //----------------------------------------------------------------------
4250911 bool
4250912 initedit (void)
4250913 {
4250914     int i;
4250915     //
4250916     bufsize = INITBUFSIZE;
4250917     bufbase = malloc (bufsize);
4250918     //
4250919     if (bufbase == NULL)
4250920       {
4250921         fprintf (stderr, "No memory for buffer\n");
4250922         return false;
4250923       }
4250924     //
4250925     bufptr = bufbase;
4250926     bufused = 0;
4250927     //
4250928     lines.next = &lines;
4250929     lines.prev = &lines;
4250930     //
4250931     curline = NULL;
4250932     curnum = 0;
4250933     lastnum = 0;
4250934     dirty = false;
4250935     filename = NULL;
4250936     searchstring[0] = '\0';
4250937     //
4250938     for (i = 0; i < 26; i++)
4250939       {
4250940         marks[i] = 0;
4250941       }
4250942     //
4250943     return true;
4250944 }
4250945 //----------------------------------------------------------------------
4250946 // Finish editing.
4250947 //----------------------------------------------------------------------
4250948 void
4250949 termedit (void)
4250950 {
4250951     if (bufbase) free(bufbase);
4250952     bufbase = NULL;
4250953     //
4250954     bufptr = NULL;
4250955     bufsize = 0;
4250956     bufused = 0;
4250957     //
4250958     if (filename) free(filename);
4250959     filename = NULL;
4250960     //
4250961     searchstring[0] = '\0';
4250962     //
4250963     if (lastnum) deletelines (1, lastnum);
4250964     //
4250965     lastnum = 0;
4250966     curnum = 0;
4250967     curline = NULL;
4250968 }
4250969 //----------------------------------------------------------------------
4250970 // Read lines from a file at the specified line number.
4250971 // Returns true if the file was successfully read.
4250972 //----------------------------------------------------------------------
4250973 bool
4250974 readlines (char *file, num_t num)
4250975 {
4250976     int     fd;
4250977     int     cc;
4250978     len_t   len;
4250979     len_t   linecount;
4250980     len_t   charcount;
4250981     char   *cp;
4250982     //
4250983     if ((num < 1) || (num > lastnum + 1))
4250984       {
4250985         fprintf (stderr, "Bad line for read\n");
4250986         return false;
4250987       }
4250988     //
4250989     fd = open (file, O_RDONLY);
4250990     if (fd < 0)
4250991       {
4250992         perror (file);
4250993         return false;
4250994       }
4250995     //
4250996     bufptr = bufbase;
4250997     bufused = 0;
4250998     linecount = 0;
4250999     charcount = 0;
4251000     //
4251001     printf ("\"%s\", ", file);
4251002     fflush(stdout);
4251003     //
4251004     do
4251005       {
4251006         cp = memchr(bufptr, '\n', bufused);
4251007         if (cp)
4251008           {
4251009             len = (cp - bufptr) + 1;
4251010             //
4251011             if (!insertline (num, bufptr, len))
4251012               {
4251013                 close (fd);
4251014                 return false;
4251015               }
4251016             //
4251017             bufptr += len;
4251018             bufused -= len;
4251019             charcount += len;
4251020             linecount++;
4251021             num++;
4251022             continue;
4251023           }
4251024         //
4251025         if (bufptr != bufbase)
4251026           {
4251027             memcpy (bufbase, bufptr, bufused);
4251028             bufptr = bufbase + bufused;
4251029           }
4251030         //
4251031         if (bufused >= bufsize)
4251032           {
4251033             len = (bufsize * 3) / 2;
4251034             cp = realloc (bufbase, len);
4251035             if (cp == NULL)
4251036               {
4251037                 fprintf (stderr, "No memory for buffer\n");
4251038                 close (fd);
4251039                 return false;
4251040               }
4251041             //
4251042             bufbase = cp;
4251043             bufptr = bufbase + bufused;
4251044             bufsize = len;
4251045           }
4251046         //
4251047         cc = read (fd, bufptr, bufsize - bufused);
4251048         bufused += cc;
4251049         bufptr = bufbase;
4251050       }
4251051     while (cc > 0);
4251052     //
4251053     if (cc < 0)
4251054       {
4251055         perror (file);
4251056         close (fd);
4251057         return false;
4251058       }
4251059     //
4251060     if (bufused)
4251061       {
4251062         if (!insertline (num, bufptr, bufused))
4251063           {
4251064             close (fd);
4251065             return -1;
4251066           }
4251067         linecount++;
4251068         charcount += bufused;
4251069       }
4251070     //
4251071     close (fd);
4251072     //
4251073     printf ("%d lines%s, %d chars\n",
4251074             linecount,
4251075             (bufused ? " (incomplete)" : ""),
4251076             charcount);
4251077     //
4251078     return true;
4251079 }
4251080 //----------------------------------------------------------------------
4251081 // Write the specified lines out to the specified file.
4251082 // Returns true if successful, or false on an error with a message
4251083 // output.
4251084 //----------------------------------------------------------------------
4251085 bool
4251086 writelines (char *file, num_t num1, num_t num2)
4251087 {
4251088     int     fd;
4251089     line_t *lp;
4251090     len_t   linecount;
4251091     len_t   charcount;
4251092     //
4251093     if ((num1 < 1) || (num2 > lastnum) || (num1 > num2))
4251094       {
4251095         fprintf (stderr, "Bad line range for write\n");
4251096         return false;
4251097       }
4251098     //
4251099     linecount = 0;
4251100     charcount = 0;
4251101     //
4251102     fd = creat (file, 0666);
4251103     if (fd < 0)
4251104       {
4251105         perror (file);
4251106         return false;
4251107       }
4251108     //
4251109     printf("\"%s\", ", file);
4251110     fflush (stdout);
4251111     //
4251112     lp = findline (num1);
4251113     if (lp == NULL)
4251114       {
4251115         close (fd);
4251116         return false;
4251117       }
4251118     //
4251119     while (num1++ <= num2)
4251120       {
4251121         if (write(fd, lp->data, lp->len) != lp->len)
4251122           {
4251123             perror(file);
4251124             close(fd);
4251125             return false;
4251126           }
4251127         //
4251128         charcount += lp->len;
4251129         linecount++;
4251130         lp = lp->next;
4251131       }
4251132     //
4251133     if (close(fd) < 0)
4251134       {
4251135         perror(file);
4251136         return false;
4251137       }
4251138     //
4251139     printf ("%d lines, %d chars\n", linecount, charcount);
4251140     //
4251141     return true;
4251142 }
4251143 //----------------------------------------------------------------------
4251144 // Print lines in a specified range.
4251145 // The last line printed becomes the current line.
4251146 // If expandflag is true, then the line is printed specially to
4251147 // show magic characters.
4251148 //----------------------------------------------------------------------
4251149 bool
4251150 printlines (num_t num1, num_t num2, bool expandflag)
4251151 {
4251152     line_t        *lp;
4251153     unsigned char *cp;
4251154     int            ch;
4251155     len_t          count;
4251156     //
4251157     if ((num1 < 1) || (num2 > lastnum) || (num1 > num2))
4251158       {
4251159         fprintf (stderr, "Bad line range for print\n");
4251160         return false;
4251161       }
4251162     //
4251163     lp = findline (num1);
4251164     if (lp == NULL)
4251165       {
4251166         return false;
4251167       }
4251168     //
4251169     while (num1 <= num2)
4251170       {
4251171         if (!expandflag)
4251172           {
4251173             write (STDOUT_FILENO, lp->data, lp->len);
4251174             setcurnum (num1++);
4251175             lp = lp->next;
4251176             continue;
4251177           }
4251178 
4251179         //--------------------------------------------------------------
4251180         // Show control characters and characters with the
4251181         // high bit set specially.
4251182         //--------------------------------------------------------------
4251183 
4251184         cp = (unsigned char *) lp->data;
4251185         count = lp->len;
4251186         //
4251187         if ((count > 0) && (cp[count - 1] == '\n'))
4251188           {
4251189             count--;
4251190           }
4251191         //
4251192         while (count-- > 0)
4251193           {
4251194             ch = *cp++;
4251195             if (ch & 0x80)
4251196               {
4251197                 fputs ("M-", stdout);
4251198                 ch &= 0x7f;
4251199               }
4251200             if (ch < ' ')
4251201               {
4251202                 fputc ('^', stdout);
4251203                 ch += '@';
4251204               }
4251205             if (ch == 0x7f)
4251206               {
4251207                 fputc ('^', stdout);
4251208                 ch = '?';
4251209               }
4251210             fputc (ch, stdout);
4251211           }
4251212         //
4251213         fputs ("$\n", stdout);
4251214         //
4251215         setcurnum (num1++);
4251216         lp = lp->next;
4251217       }
4251218     //
4251219     return true;
4251220 }
4251221 //----------------------------------------------------------------------
4251222 // Insert a new line with the specified text.
4251223 // The line is inserted so as to become the specified line,
4251224 // thus pushing any existing and further lines down one.
4251225 // The inserted line is also set to become the current line.
4251226 // Returns true if successful.
4251227 //----------------------------------------------------------------------
4251228 bool
4251229 insertline (num_t num, char *data, len_t len)
4251230 {
4251231     line_t    *newlp;
4251232     line_t    *lp;
4251233     //
4251234     if ((num < 1) || (num > lastnum + 1))
4251235       {
4251236         fprintf (stderr, "Inserting at bad line number\n");
4251237         return false;
4251238       }
4251239     //
4251240     newlp = (line_t *) malloc (sizeof (line_t) + len - 1);
4251241     if (newlp == NULL)
4251242       {
4251243         fprintf (stderr, "Failed to allocate memory for line\n");
4251244         return false;
4251245       }
4251246     //
4251247     memcpy (newlp->data, data, len);
4251248     newlp->len = len;
4251249     //
4251250     if (num > lastnum)
4251251       {
4251252         lp = &lines;
4251253       }
4251254     else
4251255       {
4251256         lp = findline (num);
4251257         if (lp == NULL)
4251258           {
4251259             free ((char *) newlp);
4251260             return false;
4251261           }
4251262       }
4251263     //
4251264     newlp->next = lp;
4251265     newlp->prev = lp->prev;
4251266     lp->prev->next = newlp;
4251267     lp->prev = newlp;
4251268     //
4251269     lastnum++;
4251270     dirty = true;
4251271     //
4251272     return setcurnum (num);
4251273 }
4251274 //----------------------------------------------------------------------
4251275 // Delete lines from the given range.
4251276 //----------------------------------------------------------------------
4251277 bool
4251278 deletelines (num_t num1, num_t num2)
4251279 {
4251280     line_t   *lp;
4251281     line_t   *nlp;
4251282     line_t   *plp;
4251283     num_t     count;
4251284     //
4251285     if ((num1 < 1) || (num2 > lastnum) || (num1 > num2))
4251286       {
4251287         fprintf (stderr, "Bad line numbers for delete\n");
4251288         return false;
4251289       }
4251290     //
4251291     lp = findline (num1);
4251292     if (lp == NULL)
4251293       {
4251294         return false;
4251295       }
4251296     //
4251297     if ((curnum >= num1) && (curnum <= num2))
4251298       {
4251299         if (num2 < lastnum)
4251300           {
4251301             setcurnum (num2 + 1);
4251302           }
4251303         else if (num1 > 1)
4251304           {
4251305             setcurnum (num1 - 1);
4251306           }
4251307         else
4251308           {
4251309             curnum = 0;
4251310           }
4251311       }
4251312     //
4251313     count = num2 - num1 + 1;
4251314     //
4251315     if (curnum > num2)
4251316       {
4251317         curnum -= count;
4251318       }
4251319     //
4251320     lastnum -= count;
4251321     //
4251322     while (count-- > 0)
4251323       {
4251324         nlp = lp->next;
4251325         plp = lp->prev;
4251326         plp->next = nlp;
4251327         nlp->prev = plp;
4251328         lp->next = NULL;
4251329         lp->prev = NULL;
4251330         lp->len = 0;
4251331         free(lp);
4251332         lp = nlp;
4251333       }
4251334     //
4251335     dirty = true;
4251336     //
4251337     return true;
4251338 }
4251339 //----------------------------------------------------------------------
4251340 // Search for a line which contains the specified string.
4251341 // If the string is NULL, then the previously searched for string
4251342 // is used.  The currently searched for string is saved for future use.
4251343 // Returns the line number which matches, or 0 if there was no match
4251344 // with an error printed.
4251345 //----------------------------------------------------------------------
4251346 num_t
4251347 searchlines (char *str, num_t num1, num_t num2)
4251348 {
4251349     line_t *lp;
4251350     int     len;
4251351     //
4251352     if ((num1 < 1) || (num2 > lastnum) || (num1 > num2))
4251353       {
4251354         fprintf (stderr, "Bad line numbers for search\n");
4251355         return 0;
4251356       }
4251357     //
4251358     if (*str == '\0')
4251359       {
4251360         if (searchstring[0] == '\0')
4251361           {
4251362             fprintf(stderr, "No previous search string\n");
4251363             return 0;
4251364           }
4251365         str = searchstring;
4251366       }
4251367     //
4251368     if (str != searchstring)
4251369       {
4251370         strcpy(searchstring, str);
4251371       }
4251372     //
4251373     len = strlen(str);
4251374     //
4251375     lp = findline (num1);
4251376     if (lp == NULL)
4251377       {
4251378         return 0;
4251379       }
4251380     //
4251381     while (num1 <= num2)
4251382       {
4251383         if (findstring(lp, str, len, 0) >= 0)
4251384           {
4251385             return num1;
4251386           }
4251387         //
4251388         num1++;
4251389         lp = lp->next;
4251390       }
4251391     //
4251392     fprintf (stderr, "Cannot find string \"%s\"\n", str);
4251393     //
4251394     return 0;
4251395 }
4251396 //----------------------------------------------------------------------
4251397 // Return a pointer to the specified line number.
4251398 //----------------------------------------------------------------------
4251399 line_t *
4251400 findline (num_t num)
4251401 {
4251402     line_t   *lp;
4251403     num_t     lnum;
4251404     //
4251405     if ((num < 1) || (num > lastnum))
4251406       {
4251407         fprintf (stderr, "Line number %d does not exist\n", num);
4251408         return NULL;
4251409       }
4251410     //
4251411     if (curnum <= 0)
4251412       {
4251413         curnum = 1;
4251414         curline = lines.next;
4251415       }
4251416     //
4251417     if (num == curnum)
4251418       {
4251419         return curline;
4251420       }
4251421     //
4251422     lp = curline;
4251423     lnum = curnum;
4251424     //
4251425     if (num < (curnum / 2))
4251426       {
4251427         lp = lines.next;
4251428         lnum = 1;
4251429       }
4251430     else if (num > ((curnum + lastnum) / 2))
4251431       {
4251432         lp = lines.prev;
4251433         lnum = lastnum;
4251434       }
4251435     //
4251436     while (lnum < num)
4251437       {
4251438         lp = lp->next;
4251439         lnum++;
4251440       }
4251441     //
4251442     while (lnum > num)
4251443       {
4251444         lp = lp->prev;
4251445         lnum--;
4251446       }
4251447     //
4251448     return lp;
4251449 }
4251450 //----------------------------------------------------------------------
4251451 // Set the current line number.
4251452 // Returns true if successful.
4251453 //----------------------------------------------------------------------
4251454 bool
4251455 setcurnum (num_t num)
4251456 {
4251457     line_t    *lp;
4251458     //
4251459     lp = findline (num);
4251460     if (lp == NULL)
4251461       {
4251462         return false;
4251463       }
4251464     //
4251465     curnum = num;
4251466     curline = lp;
4251467     //
4251468     return true;
4251469 }
4251470 
4251471 /* END CODE */
 |