cut.c

Go to the documentation of this file.
00001 /* cut.c */
00002 
00003 /* Author:
00004  *      Steve Kirkendall
00005  *      14407 SW Teal Blvd. #C
00006  *      Beaverton, OR 97005
00007  *      kirkenda@cs.pdx.edu
00008  */
00009 
00010 
00011 /* This file contains function which manipulate the cut buffers. */
00012 
00013 #include "config.h"
00014 #include "vi.h"
00015 #if TURBOC
00016 #include <process.h>            /* needed for getpid */
00017 #endif
00018 #if TOS
00019 #include <osbind.h>
00020 #define rename(a,b)     Frename(0,a,b)
00021 #endif
00022 
00023 # define NANONS 9       /* number of anonymous buffers */
00024 
00025 static struct cutbuf
00026 {
00027         short   *phys;  /* pointer to an array of #s of BLKs containing text */
00028         int     nblks;  /* number of blocks in phys[] array */
00029         int     start;  /* offset into first block of start of cut */
00030         int     end;    /* offset into last block of end of cut */
00031         int     tmpnum; /* ID number of the temp file */
00032         char    lnmode; /* boolean: line-mode cut? (as opposed to char-mode) */
00033 }
00034         named[27],      /* cut buffers "a through "z and ". */
00035         anon[NANONS];   /* anonymous cut buffers */
00036 
00037 static char     cbname; /* name chosen for next cut/paste operation */
00038 static char     dotcb;  /* cut buffer to use if "doingdot" is set */
00039 
00040 
00041 #ifndef NO_RECYCLE
00042 /* This function builds a list of all blocks needed in the current tmp file
00043  * for the contents of cut buffers.
00044  * !!! WARNING: if you have more than ~450000 bytes of text in all of the
00045  * cut buffers, then this will fail disastrously, because buffer overflow
00046  * is *not* allowed for.
00047  */
00048 int cutneeds(need)
00049         BLK             *need;  /* this is where we deposit the list */
00050 {
00051         struct cutbuf   *cb;    /* used to count through cut buffers */
00052         int             i;      /* used to count through blocks of a cut buffer */
00053         int             n;      /* total number of blocks in list */
00054 
00055         n = 0;
00056 
00057         /* first the named buffers... */
00058         for (cb = named; cb < &named[27]; cb++)
00059         {
00060                 if (cb->tmpnum != tmpnum)
00061                         continue;
00062 
00063                 for (i = cb->nblks; i-- > 0; )
00064                 {
00065                         need->n[n++] = cb->phys[i];
00066                 }
00067         }
00068 
00069         /* then the anonymous buffers */
00070         for (cb = anon; cb < &anon[NANONS]; cb++)
00071         {
00072                 if (cb->tmpnum != tmpnum)
00073                         continue;
00074 
00075                 for (i = cb->nblks; i-- > 0; )
00076                 {
00077                         need->n[n++] = cb->phys[i];
00078                 }
00079         }
00080 
00081         /* return the length of the list */
00082         return n;
00083 }
00084 #endif
00085 
00086 static void maybezap(num)
00087         int     num;    /* the tmpnum of the temporary file to [maybe] delete */
00088 {
00089         char    cutfname[80];
00090         int     i;
00091 
00092         /* if this is the current tmp file, then we'd better keep it! */
00093         if (tmpfd >= 0 && num == tmpnum)
00094         {
00095                 return;
00096         }
00097 
00098         /* see if anybody else needs this tmp file */
00099         for (i = 27; --i >= 0; )
00100         {
00101                 if (named[i].nblks > 0 && named[i].tmpnum == num)
00102                 {
00103                         break;
00104                 }
00105         }
00106         if (i < 0)
00107         {
00108                 for (i = NANONS; --i >= 0 ; )
00109                 {
00110                         if (anon[i].nblks > 0 && anon[i].tmpnum == num)
00111                         {
00112                                 break;
00113                         }
00114                 }
00115         }
00116 
00117         /* if nobody else needs it, then discard the tmp file */
00118         if (i < 0)
00119         {
00120 #if MSDOS || TOS
00121                 strcpy(cutfname, o_directory);
00122                 if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i - 1]))
00123                         cutfname[i++] = SLASH;
00124                 sprintf(cutfname + i, TMPNAME + 3, getpid(), num);
00125 #else
00126                 sprintf(cutfname, TMPNAME, o_directory, getpid(), num);
00127 #endif
00128                 unlink(cutfname);
00129         }
00130 }
00131 
00132 /* This function frees a cut buffer.  If it was the last cut buffer that
00133  * refered to an old temp file, then it will delete the temp file. */
00134 static void cutfree(buf)
00135         struct cutbuf   *buf;
00136 {
00137         int     num;
00138 
00139         /* return immediately if the buffer is already empty */
00140         if (buf->nblks <= 0)
00141         {
00142                 return;
00143         }
00144 
00145         /* else free up stuff */
00146         num = buf->tmpnum;
00147         buf->nblks = 0;
00148 #ifdef DEBUG
00149         if (!buf->phys)
00150                 msg("cutfree() tried to free a NULL buf->phys pointer.");
00151         else
00152 #endif
00153         free((char *)buf->phys);
00154 
00155         /* maybe delete the temp file */
00156         maybezap(num);
00157 }
00158 
00159 /* This function is called when we are about to abort a tmp file.
00160  *
00161  * To minimize the number of extra files lying around, only named cut buffers
00162  * are preserved in a file switch; the anonymous buffers just go away.
00163  */
00164 void cutswitch()
00165 {
00166         int     i;
00167 
00168         /* mark the current temp file as being "obsolete", and close it.  */
00169         storename((char *)0);
00170         close(tmpfd);
00171         tmpfd = -1;
00172 
00173         /* discard all anonymous cut buffers */
00174         for (i = 0; i < NANONS; i++)
00175         {
00176                 cutfree(&anon[i]);
00177         }
00178 
00179         /* delete the temp file, if we don't really need it */
00180         maybezap(tmpnum);
00181 }
00182 
00183 /* This function should be called just before termination of vi */
00184 void cutend()
00185 {
00186         int     i;
00187 
00188         /* free the anonymous buffers, if they aren't already free */
00189         cutswitch();
00190 
00191         /* free all named cut buffers, since they might be forcing an older
00192          * tmp file to be retained.
00193          */
00194         for (i = 0; i < 27; i++)
00195         {
00196                 cutfree(&named[i]);
00197         }
00198 
00199         /* delete the temp file */
00200         maybezap(tmpnum);
00201 }
00202 
00203 
00204 /* This function is used to select the cut buffer to be used next */
00205 void cutname(name)
00206         int     name;   /* a single character */
00207 {
00208         cbname = name;
00209 }
00210 
00211 
00212 
00213 
00214 /* This function copies a selected segment of text to a cut buffer */
00215 void cut(from, to)
00216         MARK    from;           /* start of text to cut */
00217         MARK    to;             /* end of text to cut */
00218 {
00219         int             first;  /* logical number of first block in cut */
00220         int             last;   /* logical number of last block used in cut */
00221         long            line;   /* a line number */
00222         int             lnmode; /* boolean: will this be a line-mode cut? */
00223         MARK            delthru;/* end of text temporarily inserted for apnd */
00224         REG struct cutbuf *cb;
00225         REG long        l;
00226         REG int         i;
00227         REG char        *scan;
00228         char            *blkc;
00229 
00230         /* detect whether this must be a line-mode cut or char-mode cut */
00231         if (markidx(from) == 0 && markidx(to) == 0)
00232                 lnmode = TRUE;
00233         else
00234                 lnmode = FALSE;
00235 
00236         /* by default, we don't "delthru" anything */
00237         delthru = MARK_UNSET;
00238 
00239         /* handle the "doingdot" quirks */
00240         if (doingdot)
00241         {
00242                 if (!cbname)
00243                 {
00244                         cbname = dotcb;
00245                 }
00246         }
00247         else if (cbname != '.')
00248         {
00249                 dotcb = cbname;
00250         }
00251 
00252         /* decide which cut buffer to use */
00253         if (!cbname)
00254         {
00255                 /* free up the last anonymous cut buffer */
00256                 cutfree(&anon[NANONS - 1]);
00257 
00258                 /* shift the anonymous cut buffers */
00259                 for (i = NANONS - 1; i > 0; i--)
00260                 {
00261                         anon[i] = anon[i - 1];
00262                 }
00263 
00264                 /* use the first anonymous cut buffer */
00265                 cb = anon;
00266                 cb->nblks = 0;
00267         }
00268         else if (cbname >= 'a' && cbname <= 'z')
00269         {
00270                 cb = &named[cbname - 'a'];
00271                 cutfree(cb);
00272         }
00273 #ifndef CRUNCH
00274         else if (cbname >= 'A' && cbname <= 'Z')
00275         {
00276                 cb = &named[cbname - 'A'];
00277                 if (cb->nblks > 0)
00278                 {
00279                         /* resolve linemode/charmode differences */
00280                         if (!lnmode && cb->lnmode)
00281                         {
00282                                 from &= ~(BLKSIZE - 1);
00283                                 if (markidx(to) != 0 || to == from)
00284                                 {
00285                                         to = to + BLKSIZE - markidx(to);
00286                                 }
00287                                 lnmode = TRUE;
00288                         }
00289 
00290                         /* insert the old cut-buffer before the new text */
00291                         mark[28] = to;
00292                         delthru = paste(from, FALSE, TRUE);
00293                         if (delthru == MARK_UNSET)
00294                         {
00295                                 return;
00296                         }
00297                         delthru++;
00298                         to = mark[28];
00299                 }
00300                 cutfree(cb);
00301         }
00302 #endif /* not CRUNCH */
00303         else if (cbname == '.')
00304         {
00305                 cb = &named[26];
00306                 cutfree(cb);
00307         }
00308         else
00309         {
00310                 msg("Invalid cut buffer name: \"%c", cbname);
00311                 dotcb = cbname = '\0';
00312                 return;
00313         }
00314         cbname = '\0';
00315         cb->tmpnum = tmpnum;
00316 
00317         /* detect whether we're doing a line mode cut */
00318         cb->lnmode = lnmode;
00319 
00320         /* ---------- */
00321 
00322         /* Reporting... */      
00323         if (markidx(from) == 0 && markidx(to) == 0)
00324         {
00325                 rptlines = markline(to) - markline(from);
00326                 rptlabel = "yanked";
00327         }
00328 
00329         /* ---------- */
00330 
00331         /* make sure each block has a physical disk address */
00332         blksync();
00333 
00334         /* find the first block in the cut */
00335         line = markline(from);
00336         for (first = 1; line > lnum[first]; first++)
00337         {
00338         }
00339 
00340         /* fetch text of the block containing that line */
00341         blkc = scan = blkget(first)->c;
00342 
00343         /* find the mark in the block */
00344         for (l = lnum[first - 1]; ++l < line; )
00345         {
00346                 while (*scan++ != '\n')
00347                 {
00348                 }
00349         }
00350         scan += markidx(from);
00351 
00352         /* remember the offset of the start */
00353         cb->start = scan - blkc;
00354 
00355         /* ---------- */
00356 
00357         /* find the last block in the cut */
00358         line = markline(to);
00359         for (last = first; line > lnum[last]; last++)
00360         {
00361         }
00362 
00363         /* fetch text of the block containing that line */
00364         if (last != first)
00365         {
00366                 blkc = scan = blkget(last)->c;
00367         }
00368         else
00369         {
00370                 scan = blkc;
00371         }
00372 
00373         /* find the mark in the block */
00374         for (l = lnum[last - 1]; ++l < line; )
00375         {
00376                 while (*scan++ != '\n')
00377                 {
00378                 }
00379         }
00380         if (markline(to) <= nlines)
00381         {
00382                 scan += markidx(to);
00383         }
00384 
00385         /* remember the offset of the end */
00386         cb->end = scan - blkc;
00387 
00388         /* ------- */
00389 
00390         /* remember the physical block numbers of all included blocks */
00391         cb->nblks = last - first;
00392         if (cb->end > 0)
00393         {
00394                 cb->nblks++;
00395         }
00396 #ifdef lint
00397         cb->phys = (short *)0;
00398 #else
00399         cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short)));
00400 #endif
00401         for (i = 0; i < cb->nblks; i++)
00402         {
00403                 cb->phys[i] = hdr.n[first++];
00404         }
00405 
00406 #ifndef CRUNCH
00407         /* if we temporarily inserted text for appending, then delete that
00408          * text now -- before the user sees it.
00409          */
00410         if (delthru)
00411         {
00412                 line = rptlines;
00413                 delete(from, delthru);
00414                 rptlines = line;
00415                 rptlabel = "yanked";
00416         }
00417 #endif /* not CRUNCH */
00418 }
00419 
00420 
00421 static void readcutblk(cb, blkno)
00422         struct cutbuf   *cb;
00423         int             blkno;
00424 {
00425         char            cutfname[50];/* name of an old temp file */
00426         int             fd;     /* either tmpfd or the result of open() */
00427 #if MSDOS || TOS
00428         int             i;
00429 #endif
00430 
00431         /* decide which fd to use */
00432         if (cb->tmpnum == tmpnum)
00433         {
00434                 fd = tmpfd;
00435         }
00436         else
00437         {
00438 #if MSDOS || TOS
00439                 strcpy(cutfname, o_directory);
00440                 if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i-1]))
00441                         cutfname[i++]=SLASH;
00442                 sprintf(cutfname+i, TMPNAME+3, getpid(), cb->tmpnum);
00443 #else
00444                 sprintf(cutfname, TMPNAME, o_directory, getpid(), cb->tmpnum);
00445 #endif
00446                 fd = open(cutfname, O_RDONLY);
00447         }
00448 
00449         /* get the block */
00450         lseek(fd, (long)cb->phys[blkno] * (long)BLKSIZE, 0);
00451         if (read(fd, tmpblk.c, (unsigned)BLKSIZE) != BLKSIZE)
00452         {
00453                 msg("Error reading back from tmp file for pasting!");
00454         }
00455 
00456         /* close the fd, if it isn't tmpfd */
00457         if (fd != tmpfd)
00458         {
00459                 close(fd);
00460         }
00461 }
00462 
00463 
00464 /* This function inserts text from a cut buffer, and returns the MARK where
00465  * insertion ended.  Return MARK_UNSET on errors.
00466  */
00467 MARK paste(at, after, retend)
00468         MARK    at;     /* where to insert the text */
00469         int     after;  /* boolean: insert after mark? (rather than before) */
00470         int     retend; /* boolean: return end of text? (rather than start) */
00471 {
00472         REG struct cutbuf       *cb;
00473         REG int                 i;
00474 
00475         /* handle the "doingdot" quirks */
00476         if (doingdot)
00477         {
00478                 if (!cbname)
00479                 {
00480                         if (dotcb >= '1' && dotcb < '1' + NANONS - 1)
00481                         {
00482                                 dotcb++;
00483                         }
00484                         cbname = dotcb;
00485                 }
00486         }
00487         else if (cbname != '.')
00488         {
00489                 dotcb = cbname;
00490         }
00491 
00492         /* decide which cut buffer to use */
00493         if (cbname >= 'A' && cbname <= 'Z')
00494         {
00495                 cb = &named[cbname - 'A'];
00496         }
00497         else if (cbname >= 'a' && cbname <= 'z')
00498         {
00499                 cb = &named[cbname - 'a'];
00500         }
00501         else if (cbname >= '1' && cbname <= '9')
00502         {
00503                 cb = &anon[cbname - '1'];
00504         }
00505         else if (cbname == '.')
00506         {
00507                 cb = &named[26];
00508         }
00509         else if (!cbname)
00510         {
00511                 cb = anon;
00512         }
00513         else
00514         {
00515                 msg("Invalid cut buffer name: \"%c", cbname);
00516                 cbname = '\0';
00517                 return MARK_UNSET;
00518         }
00519 
00520         /* make sure it isn't empty */
00521         if (cb->nblks == 0)
00522         {
00523                 if (cbname)
00524                         msg("Cut buffer \"%c is empty", cbname);
00525                 else
00526                         msg("Cut buffer is empty");
00527                 cbname = '\0';
00528                 return MARK_UNSET;
00529         }
00530         cbname = '\0';
00531 
00532         /* adjust the insertion MARK for "after" and line-mode cuts */
00533         if (cb->lnmode)
00534         {
00535                 at &= ~(BLKSIZE - 1);
00536                 if (after)
00537                 {
00538                         at += BLKSIZE;
00539                 }
00540         }
00541         else if (after)
00542         {
00543                 /* careful! if markidx(at) == 0 we might be pasting into an
00544                  * empty line -- so we can't blindly increment "at".
00545                  */
00546                 if (markidx(at) == 0)
00547                 {
00548                         pfetch(markline(at));
00549                         if (plen != 0)
00550                         {
00551                                 at++;
00552                         }
00553                 }
00554                 else
00555                 {
00556                         at++;
00557                 }
00558         }
00559 
00560         /* put a copy of the "at" mark in the mark[] array, so it stays in
00561          * sync with changes made via add().
00562          */
00563         mark[27] = at;
00564 
00565         /* simple one-block paste? */
00566         if (cb->nblks == 1)
00567         {
00568                 /* get the block */
00569                 readcutblk(cb, 0);
00570 
00571                 /* isolate the text we need within it */
00572                 if (cb->end)
00573                 {
00574                         tmpblk.c[cb->end] = '\0';
00575                 }
00576 
00577                 /* insert it */
00578                 ChangeText
00579                 {
00580                         add(at, &tmpblk.c[cb->start]);
00581                 }
00582         }
00583         else
00584         {
00585                 /* multi-block paste */
00586 
00587                 ChangeText
00588                 {
00589                         i = cb->nblks - 1;
00590 
00591                         /* add text from the last block first */
00592                         if (cb->end > 0)
00593                         {
00594                                 readcutblk(cb, i);
00595                                 tmpblk.c[cb->end] = '\0';
00596                                 add(at, tmpblk.c);
00597                                 i--;
00598                         }
00599 
00600                         /* add intervening blocks */
00601                         while (i > 0)
00602                         {
00603                                 readcutblk(cb, i);
00604                                 add(at, tmpblk.c);
00605                                 i--;
00606                         }
00607 
00608                         /* add text from the first cut block */
00609                         readcutblk(cb, 0);
00610                         add(at, &tmpblk.c[cb->start]);
00611                 }
00612         }
00613 
00614         /* Reporting... */
00615         rptlines = markline(mark[27]) - markline(at);
00616         rptlabel = "pasted";
00617 
00618         /* return the mark at the beginning/end of inserted text */
00619         if (retend)
00620         {
00621                 return mark[27] - 1L;
00622         }
00623         return at;
00624 }
00625 
00626 
00627 
00628 
00629 #ifndef NO_AT
00630 
00631 /* This function copies characters from a cut buffer into a string.
00632  * It returns the number of characters in the cut buffer.  If the cut
00633  * buffer is too large to fit in the string (i.e. if cb2str() returns
00634  * a number >= size) then the characters will not have been copied.
00635  * It returns 0 if the cut buffer is empty, and -1 for invalid cut buffers.
00636  */
00637 int cb2str(name, buf, size)
00638         int     name;   /* the name of a cut-buffer to get: a-z only! */
00639         char    *buf;   /* where to put the string */
00640         unsigned size;  /* size of buf */
00641 {
00642         REG struct cutbuf       *cb;
00643         REG char                *src;
00644         REG char                *dest;
00645 
00646         /* decide which cut buffer to use */
00647         if (name >= 'a' && name <= 'z')
00648         {
00649                 cb = &named[name - 'a'];
00650         }
00651         else
00652         {
00653                 return -1;
00654         }
00655 
00656         /* if the buffer is empty, return 0 */
00657         if (cb->nblks == 0)
00658         {
00659                 return 0;
00660         }
00661 
00662         /* !!! if not a single-block cut, then fail */
00663         if (cb->nblks != 1)
00664         {
00665                 return size;
00666         }
00667 
00668         /* if too big, return the size now, without doing anything */
00669         if (cb->end - cb->start >= size)
00670         {
00671                 return cb->end - cb->start;
00672         }
00673 
00674         /* get the block */
00675         readcutblk(cb, 0);
00676 
00677         /* isolate the string within that blk */
00678         if (cb->start == 0)
00679         {
00680                 tmpblk.c[cb->end] = '\0';
00681         }
00682         else
00683         {
00684                 for (dest = tmpblk.c, src = dest + cb->start; src < tmpblk.c + cb->end; )
00685                 {
00686                         *dest++ = *src++;
00687                 }
00688                 *dest = '\0';
00689         }
00690 
00691         /* copy the string into the buffer */
00692         if (buf != tmpblk.c)
00693         {
00694                 strcpy(buf, tmpblk.c);
00695         }
00696 
00697         /* return the length */
00698         return cb->end - cb->start;
00699 }
00700 #endif

Generated on Fri Apr 14 22:56:52 2006 for minix by  doxygen 1.4.6