getline.c

Go to the documentation of this file.
00001 /* Copyright (c) 1985 Ceriel J.H. Jacobs */
00002 
00003 # ifndef lint
00004 static char rcsid[] = "$Header: /opt/proj/minix/cvsroot/src/commands/yap/getline.c,v 1.1.1.1 2005/04/21 14:55:39 beng Exp $";
00005 # endif
00006 
00007 # define _GETLINE_
00008 
00009 # include <errno.h>
00010 # include "in_all.h"
00011 # include "getline.h"
00012 # include "options.h"
00013 # include "process.h"
00014 # include "term.h"
00015 # include "main.h"
00016 # include "display.h"
00017 # include "output.h"
00018 # include "assert.h"
00019 
00020 extern int errno;
00021 
00022 # define BLOCKSIZE 2048         /* size of blocks */
00023 # define CHUNK 50               /* # of blockheaders allocated at a time */
00024 
00025 /*
00026  * The blockheaders of the blocks that are in core are kept in a linked list.
00027  * The last added block is indicated by b_head,
00028  * the tail of the list is indicated by b_tail.
00029  * The links go from b_tail to b_head.
00030  * The blockheaders are all in an array, in the order of the line numbers.
00031  * Also, the blockheaders must always be in core, so they have to be rather
00032  * small. On systems with a small address space, yap can run out of core,
00033  * and panic. However, this should only happen with very large files (>> 1M).
00034  */
00035 
00036 struct block {
00037         int             b_flags;        /* Contains the following flags: */
00038 # define DUMPED         01              /* block dumped on temporary file */
00039 # define PARTLY         02              /* block not filled completely (eof) */
00040         int             b_next;         /* ptr in linked list */
00041         long            b_end;          /* line number of last line in block */
00042         char     *      b_info;         /* the block */
00043         int      *      b_offs;         /* line offsets within the block */
00044         long            b_foff;         /* offset of block in file */
00045 };
00046 
00047 static struct block *   blocklist,      /* beginning of the list of blocks */
00048                     *   maxblocklist,   /* first free entry in the list */
00049                     *   topblocklist;   /* end of allocated core for the list */
00050 static int      b_head,
00051                 b_tail;
00052 static int      tfdes, ifdes;           /* File descriptors for temporary's */
00053 static long     lastreadline;           /* lineno of last line read */
00054 static int      ENDseen;
00055 
00056 STATIC VOID readblock();
00057 STATIC VOID nextblock();
00058 STATIC char *re_alloc();
00059 
00060 STATIC struct block *
00061 new_block()
00062 {
00063         register struct block *pblock = maxblocklist - 1;
00064 
00065         if (!maxblocklist || !(pblock->b_flags & PARTLY)) {
00066                 /*
00067                  * There is no last block, or it was filled completely,
00068                  * so allocate a new blockheader.
00069                  */
00070                 register int siz;
00071 
00072                 pblock = blocklist;
00073                 if (maxblocklist == topblocklist) {
00074                         /*
00075                          * No blockheaders left. Allocate new ones
00076                          */
00077                         siz = topblocklist - pblock;
00078                         blocklist = pblock = (struct block *)
00079                         re_alloc((char *) pblock,
00080                         (unsigned) (siz * sizeof(*pblock)),
00081                         (unsigned) ((siz + CHUNK) * sizeof(*pblock)));
00082                         pblock += siz;
00083                         topblocklist = pblock + CHUNK;
00084                         maxblocklist = pblock;
00085                         for (; pblock < topblocklist; pblock++) {
00086                                 pblock->b_end = 0;
00087                                 pblock->b_info = 0;
00088                                 pblock->b_flags = 0;
00089                         }
00090                         if (!siz) {
00091                                 /*
00092                                  * Create dummy header cell.
00093                                  */
00094                                 maxblocklist++;
00095                         }
00096                 }
00097                 pblock = maxblocklist++;
00098         }
00099         nextblock(pblock);
00100         return pblock;
00101 }
00102 
00103 /*
00104  * Return the block in which line 'n' of the current file can be found.
00105  * If "disable_interrupt" = 0, the call may be interrupted, in which
00106  * case it returns 0.
00107  */
00108 
00109 STATIC struct block *
00110 getblock(n, disable_interrupt) register long n; {
00111         register struct block * pblock;
00112 
00113         if (stdf < 0) {
00114                 /*
00115                  * Not file descriptor, so return end of file
00116                  */
00117                 return 0;
00118         }
00119         pblock = maxblocklist - 1;
00120         if (n < lastreadline ||
00121             (n == lastreadline && !(pblock->b_flags & PARTLY))) {
00122                 /*
00123                  * The line asked for has been read already.
00124                  * Perform binary search in the blocklist to find the block
00125                  * where it's in.
00126                  */
00127                 register struct block *min, *mid;
00128 
00129                 min = blocklist + 1;
00130                 do {
00131                         mid = min + (pblock - min) / 2;
00132                         if (n > mid->b_end) {
00133                                 min = mid + 1;
00134                         }
00135                         else pblock = mid;
00136                 } while (min < pblock);
00137                 /* Found, pblock is now a reference to the block wanted */
00138                 if (!pblock->b_info) readblock(pblock);
00139                 return pblock;
00140         }
00141 
00142         /*
00143          * The line was'nt read yet, so read blocks until found
00144          */
00145         for (;;) {
00146                 if (interrupt && !disable_interrupt) return 0;
00147                 pblock = new_block();
00148                 if (pblock->b_end >= n) {
00149                         return pblock;
00150                 }
00151                 if (pblock->b_flags & PARTLY) {
00152                         /*
00153                          * We did not find it, and the last block could not be
00154                          * read completely, so return 0;
00155                          */
00156                         return  0;
00157                 }
00158         }
00159         /* NOTREACHED */
00160 }
00161 
00162 char *
00163 getline(n, disable_interrupt) long n; {
00164         register struct block *pblock;
00165 
00166         if (!(pblock = getblock(n, disable_interrupt))) {
00167                 return (char *) 0;
00168         }
00169         return pblock->b_info + pblock->b_offs[n - ((pblock-1)->b_end + 1)];
00170 }
00171 
00172 /*
00173  * Find the last line of the input, and return its number
00174  */
00175 
00176 long
00177 to_lastline() {
00178 
00179         for (;;) {
00180                 if (!getline(lastreadline + 1, 0)) {
00181                         /*
00182                          * "lastreadline" always contains the linenumber of
00183                          * the last line read. So, if the call to getline
00184                          * succeeds, "lastreadline" is affected
00185                          */
00186                         if (interrupt) return -1L;
00187                         return lastreadline;
00188                 }
00189         }
00190         /* NOTREACHED */
00191 }
00192 
00193 #if MAXNBLOCKS
00194 int nblocks;            /* Count number of large blocks */
00195 #endif
00196 
00197 /*
00198  * Allocate some memory. If unavailable, free some and try again.
00199  * If all fails, panic.
00200  */
00201 
00202 char *
00203 alloc(size, isblock) unsigned size; {
00204 
00205         register char *pmem;
00206         register struct block *pblock, *bllist;
00207         char *malloc();
00208         long lseek();
00209         register long i;
00210 
00211         bllist = blocklist;
00212         while (
00213 #if MAXNBLOCKS
00214            (isblock && nblocks >= MAXNBLOCKS) ||
00215 #endif
00216            !(pmem = malloc(size))   /* No space */
00217         ) {
00218                 if (b_tail == 0) {
00219                         /*
00220                          * Also, no blocks in core. Pity
00221                          */
00222                         panic("No core");
00223                 }
00224 #if MAXNBLOCKS
00225                 nblocks--;
00226 #endif
00227                 pblock = bllist + b_tail;
00228                 b_tail = pblock->b_next;
00229                 if (!nopipe && !(pblock->b_flags & DUMPED)) {
00230                         /*
00231                          * Dump the block on a temporary file
00232                          */
00233                         if (!tfdes) {
00234                                 /*
00235                                  * create and open temporary files
00236                                  */
00237                                 tfdes = opentemp(0);
00238                                 ifdes = opentemp(1);
00239                         }
00240                         pblock->b_flags |= DUMPED;
00241                         /*
00242                          * Find out where to dump the block, and dump it
00243                          */
00244                         i = (pblock-1)->b_end * sizeof(int);
00245                         (VOID) lseek(tfdes,
00246                                 ((long) BLOCKSIZE * (pblock - bllist)), 0);
00247                         if (write(tfdes, pblock->b_info, BLOCKSIZE)
00248                             != BLOCKSIZE) {
00249                                 panic("write failed");
00250                         }
00251                         /*
00252                          * Also dump the offsets of the lines in the block
00253                          */
00254                         (VOID) lseek(ifdes, i, 0);
00255                         i = pblock->b_end * sizeof(int) - i;
00256                         if (write(ifdes, (char *) pblock->b_offs, (int) i)
00257                             != (int) i) {
00258                                 panic("Write failed");
00259                         }
00260                 }
00261                 /*
00262                  * Now that the block is dumped, the space taken by it can
00263                  * be freed
00264                  */
00265                 free((char *) pblock->b_offs);
00266                 free(pblock->b_info);
00267                 pblock->b_info = (char *) 0;
00268         }
00269 #if MAXNBLOCKS
00270         if (isblock) nblocks++;
00271 #endif
00272         return pmem;
00273 }
00274 
00275 /*
00276  * Re-allocate the memorychunk pointed to by ptr, to let it
00277  * grow or shrink.
00278  * realloc of the standard C library is useless, as it is destructive
00279  * if the malloc fails.
00280  */
00281 
00282 STATIC char *
00283 re_alloc(ptr,oldsize, newsize)
00284 char *ptr; unsigned oldsize; unsigned newsize; {
00285         register char *pmem;
00286         register char *c1, *c2;
00287 
00288         /*
00289          * We could be smarter here, by checking if newsize < oldsize, and in
00290          * that case using realloc, but this depends on realloc using the
00291          * same block if the block shrinks. The question is, wether all
00292          * reallocs in the world do this.
00293          */
00294         pmem = alloc(newsize, 0);
00295         if (oldsize) {
00296                 /*
00297                  * This test makes re_alloc also work if there was no old block
00298                  */
00299                 c1 = pmem;
00300                 c2 = ptr;
00301                 if (newsize > oldsize) {
00302                         newsize = oldsize;
00303                 }
00304                 while (newsize--) {
00305                         *c1++ = *c2++;
00306                 }
00307                 free(ptr);
00308         }
00309         return pmem;
00310 }
00311 
00312 /*
00313  * Append a block to the linked list of blockheaders of blocks that are
00314  * in core.
00315  */
00316 
00317 STATIC VOID
00318 addtolist(pblock) register struct block *pblock; {
00319         register struct block *bllist = blocklist;
00320 
00321         pblock->b_next = 0;
00322         (bllist + b_head)->b_next = pblock - bllist;
00323         b_head = pblock - bllist;
00324         if (!b_tail) {
00325                 /*
00326                  * The list was empty, initialize
00327                  */
00328                 b_tail = b_head;
00329         }
00330 }
00331 
00332 static char *saved;
00333 static long filldegree;
00334 
00335 /*
00336  * Try to read the block indicated by pblock
00337  */
00338 
00339 STATIC VOID
00340 nextblock(pblock) register struct block *pblock; {
00341         register char *c,       /* Run through pblock->b_info */
00342                       *c1;      /* indicate end of pblock->b_info */
00343         register int *poff;     /* pointer in line-offset list */
00344         register int cnt;       /* # of characters read */
00345         register unsigned siz;  /* Size of allocated line-offset list */
00346         static unsigned savedsiz;       /* saved "siz" */
00347         static int *savedpoff;          /* saved "poff" */
00348         static char *savedc1;           /* saved "c1" */
00349 
00350         if (pblock->b_flags & PARTLY) {
00351                 /*
00352                  * The block was already partly filled. Initialize locals
00353                  * accordingly
00354                  */
00355                 poff = savedpoff;
00356                 siz = savedsiz;
00357                 pblock->b_flags = 0;
00358                 c1 = savedc1;
00359                 if (c1 == pblock->b_info || *(c1 - 1)) {
00360                         /*
00361                          * We had incremented "lastreadline" temporarily,
00362                          * because the last line could not be completely read
00363                          * last time we tried. Undo this increment
00364                          */
00365                         poff--;
00366                         --lastreadline;
00367                 }
00368         }
00369         else {
00370                 if (nopipe) pblock->b_foff = lseek(stdf, 0L, 1);
00371                 if (saved) {
00372                         /*
00373                          * There were leftovers from the previous block
00374                          */
00375                         pblock->b_info = saved;
00376                         if (nopipe) pblock->b_foff -= savedc1 - saved;
00377                         c1 = savedc1;
00378                         saved = 0;
00379                 }
00380                 else {  /* Allocate new block */
00381                         pblock->b_info = c1 = alloc(BLOCKSIZE + 1, 1);
00382                 }
00383                 /*
00384                  * Allocate some space for line-offsets
00385                  */
00386                 pblock->b_offs = poff = (int *)
00387                         alloc((unsigned) (100 * sizeof(int)), 0);
00388                 siz = 99;
00389                 *poff++ = 0;
00390         }
00391         c = c1;
00392         for (;;) {
00393                 /*
00394                  * Read loop
00395                  */
00396                 cnt = read(stdf, c1, BLOCKSIZE - (c1 - pblock->b_info));
00397                 if (cnt < 0) {
00398                         /*
00399                          * Interrupted read
00400                          */
00401                         if (errno == EINTR) continue;
00402                         error("Could not read input file");
00403                         cnt = 0;
00404                 }
00405                 c1 += cnt;
00406                 if (c1 != pblock->b_info + BLOCKSIZE) {
00407                         ENDseen = 1;
00408                         pblock->b_flags |= PARTLY;
00409                 }
00410                 break;
00411         }
00412         assert(c <= c1);
00413         while (c < c1) {
00414                 /*
00415                  * Now process the block
00416                  */
00417                 *c &= 0177;     /* Most significant bit ignored */
00418                 if (*c == '\n') {
00419                         /*
00420                          * Newlines are replaced by '\0', so that "getline"
00421                          * can deliver one line at a time
00422                          */
00423                         *c = 0;
00424                         lastreadline++;
00425                         /*
00426                          * Remember the line-offset
00427                          */
00428                         if (poff == pblock->b_offs + siz) {
00429                                 /*
00430                                  * No space for it, allocate some more
00431                                  */
00432                                 pblock->b_offs = (int *)
00433                                         re_alloc((char *) pblock->b_offs,
00434                                                  (siz+1) * sizeof(int),
00435                                                  (siz + 51) * sizeof(int));
00436                                 poff = pblock->b_offs + siz;
00437                                 siz += 50;
00438                         }
00439                         *poff++ = c - pblock->b_info + 1;
00440                 }
00441                 else if (*c == '\0') {
00442                         /*
00443                          * 0-bytes are replaced by 0200, because newlines are
00444                          * replaced by 0, and 0200 & 0177 gives again 0 ...
00445                          */
00446                         *c = 0200;
00447                 }
00448                 c++;
00449         }
00450         assert(c==c1);
00451         *c = 0;
00452         if (c != pblock->b_info && *(c-1) != 0) {
00453                 /*
00454                  * The last line read does not end with a newline, so add one
00455                  */
00456                 lastreadline++;
00457                 *poff++ = c - pblock->b_info + 1;
00458                 if (!(pblock->b_flags & PARTLY) && *(poff - 2) != 0) {
00459                         /*
00460                          * Save the started line; it will be in the next block.
00461                          * Remove the newline we added just now.
00462                          */
00463                         saved = c1 = alloc(BLOCKSIZE + 1, 1);
00464                         c = pblock->b_info + *(--poff - 1);
00465                         while (*c) *c1++ = *c++;
00466                         c = pblock->b_info + *(poff - 1);
00467                         savedc1 = c1;
00468                         --lastreadline;
00469                 }
00470         }
00471         pblock->b_end = lastreadline;
00472         if (pblock->b_flags & PARTLY) {
00473                 /*
00474                  * Take care, that we can call "nextblock" again, to fill in
00475                  * the rest of this block
00476                  */
00477                 savedsiz = siz;
00478                 savedpoff = poff;
00479                 savedc1 = c;
00480                 if (c == pblock->b_info) {
00481                         lastreadline++;
00482                         pblock->b_end = 0;
00483                 }
00484         }
00485         else {
00486                 /*
00487                  * Not completely read blocks are not in the linked list,
00488                  * so can never be "swapped out".
00489                  */
00490                 addtolist(pblock);
00491                 cnt = pblock - blocklist;
00492                 filldegree = ((c-pblock->b_info) + (cnt-1) * filldegree) / cnt;
00493         }
00494         assert(pblock->b_end - (pblock-1)->b_end <= poff - pblock->b_offs);
00495 }
00496 
00497 /*
00498  * Allocate core for the block, and read it back from
00499  * the temporary file.
00500  */
00501 
00502 STATIC VOID
00503 readblock(pblock) register struct block *pblock; {
00504 
00505         register int size;
00506         register long i;
00507 
00508         /*
00509          * Find out where the block is, and read it
00510          */
00511         pblock->b_info = alloc(BLOCKSIZE + 1, 1);
00512         i = (pblock - 1)->b_end * sizeof(int);
00513         size = (int) (pblock->b_end * sizeof(int) - i);
00514         pblock->b_offs  = (int *) alloc((unsigned) size, 0);
00515         if (nopipe) {
00516                 register char *c;
00517                 register int line_index;
00518                 int cnt;
00519                 long l = lseek(stdf, 0L, 1);
00520 
00521                 (VOID) lseek(stdf, pblock->b_foff, 0);
00522                 cnt = read(stdf, pblock->b_info, BLOCKSIZE);
00523                 (VOID) lseek(stdf, l, 0);
00524                 c = pblock->b_info;
00525                 pblock->b_offs[0] = 0;
00526                 line_index = 1;
00527                 size /= sizeof(int);
00528                 while (c < pblock->b_info + cnt) {
00529                         *c &= 0177;
00530                         if (*c == '\n') {
00531                                 *c = '\0';
00532                                 if (line_index < size)
00533                                         pblock->b_offs[line_index++] =
00534                                                 (c - pblock->b_info) + 1;
00535                         }
00536                         else if (*c == '\0') *c = 0200;
00537                         c++;
00538                 }
00539                 *c = '\0';
00540         }
00541         else {
00542                 (VOID) lseek(tfdes, (long) ((long) BLOCKSIZE * (pblock - blocklist)),0);
00543                 if (read(tfdes, pblock->b_info,BLOCKSIZE) != BLOCKSIZE) {
00544                         panic("read error");
00545                 }
00546                 /*
00547                  * Find out where the line-offset list is, and read it
00548                  */
00549                 (VOID) lseek(ifdes, i, 0);
00550                 if (read(ifdes, (char *) pblock->b_offs, size) != size) {
00551                         panic("read error");
00552                 }
00553                 pblock->b_info[BLOCKSIZE] = '\0';
00554         }
00555         /*
00556          * Add this block to the list of incore blocks
00557          */
00558         addtolist(pblock);
00559 }
00560 
00561 /*
00562  * Called after processing a file.
00563  * Free all core.
00564  */
00565 
00566 VOID
00567 do_clean() {
00568 
00569         register struct block *pblock;
00570         register char *p;
00571 
00572         for (pblock = blocklist; pblock < maxblocklist; pblock++) {
00573                 if (p = pblock->b_info) {
00574                         free(p);
00575                         free((char *) pblock->b_offs);
00576                 }
00577         }
00578         if (p = (char *) blocklist) {
00579                 free(p);
00580         }
00581         blocklist = 0;
00582         maxblocklist = 0;
00583         topblocklist = 0;
00584         lastreadline = 0;
00585         filldegree = 0;
00586         ENDseen = 0;
00587         if (p = saved) free(p);
00588         saved = 0;
00589         b_head = 0;
00590         b_tail = 0;
00591 # if MAXNBLOCKS
00592         nblocks = 0;
00593 # endif
00594 }
00595 
00596 /*
00597  * Close a file with file-descriptor "file", if it indeed is one
00598  */
00599 
00600 STATIC VOID
00601 cls(file) {
00602         if (file) (VOID) close(file);
00603 }
00604 
00605 /*
00606  * Close all files
00607  */
00608 
00609 VOID
00610 cls_files() {
00611 
00612         cls(tfdes);
00613         cls(ifdes);
00614         cls(stdf);
00615 }
00616 
00617 /*
00618  * Get a character. If possible, do some workahead.
00619  */
00620 
00621 int
00622 getch() {
00623 # if USG_OPEN
00624 # include <fcntl.h>
00625 # include <sys/stat.h>
00626 
00627         register int i,j;
00628         struct stat buf;
00629 # else
00630 # ifdef FIONREAD
00631 # include <sys/stat.h>
00632 
00633         struct stat buf;
00634         long i;
00635 # endif
00636 # endif
00637 
00638         char c;
00639         int retval;
00640 
00641         flush();
00642         if (startcomm) {
00643                 /*
00644                  * Command line option command
00645                  */
00646                 if (*startcomm) return *startcomm++;
00647                 return '\n';
00648         }
00649 # if USG_OPEN
00650         if (stdf >= 0) {
00651                 /*
00652                  * Make reads from the terminal non-blocking, so that
00653                  * we can see if the user typed something
00654                  */
00655                 i = fcntl(0,F_GETFL,0);
00656                 if (i != -1 && fcntl(0, F_SETFL, i|O_NDELAY) != -1) {
00657                         j = 0;
00658                         while (! ENDseen && 
00659                                ((j = read(0,&c,1)) == 0
00660 #ifdef EWOULDBLOCK
00661                                 || (j < 0 && errno == EWOULDBLOCK)
00662 #endif
00663                                )
00664                                &&
00665                                (nopipe || 
00666                                 (fstat(stdf,&buf) >= 0 && buf.st_size > 0))) {
00667                                 /*
00668                                  * Do some read ahead, after making sure there
00669                                  * is input and the user did not type a command
00670                                  */
00671                                 new_block();
00672                         }
00673                         (VOID) fcntl(0,F_SETFL,i);
00674                         if (j < 0) {
00675                                 /*
00676                                  * Could this have happened?
00677                                  * I'm not sure, because the read is
00678                                  * nonblocking. Can it be interrupted then?
00679                                  */
00680                                 return -1;
00681                         }
00682                         if (j > 0) return c;
00683                 }
00684         }
00685 # else
00686 # ifdef FIONREAD
00687         if (stdf >= 0) {
00688                 /*
00689                  * See if there are any characters waiting in the terminal input
00690                  * queue. If there are not, read ahead.
00691                  */
00692                 while (! ENDseen &&
00693                        ( ioctl(0, FIONREAD, (char *) &i) >= 0 && i == 0) &&
00694                        ( nopipe || fstat(stdf,&buf) >= 0 && buf.st_size > 0)) {
00695                         /*
00696                          * While the user does'nt type anything, and there is
00697                          * input to be processed, work ahead
00698                          */
00699                         if (interrupt) return -1;
00700                         new_block();
00701                 }
00702         }
00703 # endif
00704 # endif
00705         if (read(0,&c,1) <= 0) retval = -1; else retval = c & 0177;
00706         return retval;
00707 }
00708 
00709 /*
00710  * Get the position of line "ln" in the file.
00711  */
00712 
00713 long
00714 getpos(ln) long ln; {
00715         register struct block *pblock;
00716         register long i;
00717 
00718         pblock = getblock(ln,1);
00719         assert(pblock != 0);
00720         i = filldegree * (pblock - blocklist);
00721         return i - (filldegree - pblock->b_offs[ln - (pblock-1)->b_end]);
00722 }

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