ch.c revision 60786
1118611Snjl/* 2118611Snjl * Copyright (C) 1984-2000 Mark Nudelman 3118611Snjl * 4118611Snjl * You may distribute under the terms of either the GNU General Public 5118611Snjl * License or the Less License, as specified in the README file. 6118611Snjl * 7217365Sjkim * For more information about less, or for information on how to 8217365Sjkim * contact the author, see the README file. 9118611Snjl */ 10118611Snjl 11217365Sjkim 12217365Sjkim/* 13217365Sjkim * Low level character input from the input file. 14217365Sjkim * We use these special purpose routines which optimize moving 15217365Sjkim * both forward and backward from the current read pointer. 16217365Sjkim */ 17217365Sjkim 18217365Sjkim#include "less.h" 19217365Sjkim#if MSDOS_COMPILER==WIN32C 20217365Sjkim#include <errno.h> 21217365Sjkim#include <windows.h> 22217365Sjkim#endif 23217365Sjkim 24217365Sjkimpublic int ignore_eoi; 25118611Snjl 26217365Sjkim/* 27217365Sjkim * Pool of buffers holding the most recently used blocks of the input file. 28217365Sjkim * The buffer pool is kept as a doubly-linked circular list, 29118611Snjl * in order from most- to least-recently used. 30217365Sjkim * The circular list is anchored by the file state "thisfile". 31217365Sjkim */ 32217365Sjkim#define LBUFSIZE 1024 33217365Sjkimstruct buf { 34217365Sjkim struct buf *next, *prev; /* Must be first to match struct filestate */ 35217365Sjkim long block; 36217365Sjkim unsigned int datasize; 37217365Sjkim unsigned char data[LBUFSIZE]; 38217365Sjkim}; 39217365Sjkim 40217365Sjkim/* 41217365Sjkim * The file state is maintained in a filestate structure. 42217365Sjkim * A pointer to the filestate is kept in the ifile structure. 43118611Snjl */ 44118611Snjlstruct filestate { 45151937Sjkim /* -- Following members must match struct buf */ 46118611Snjl struct buf *buf_next, *buf_prev; 47118611Snjl long buf_block; 48193529Sjkim /* -- End of struct buf copy */ 49193529Sjkim int file; 50193529Sjkim int flags; 51193529Sjkim POSITION fpos; 52118611Snjl int nbufs; 53118611Snjl long block; 54118611Snjl unsigned int offset; 55118611Snjl POSITION fsize; 56118611Snjl}; 57151937Sjkim 58118611Snjl 59151937Sjkim#define END_OF_CHAIN ((struct buf *)thisfile) 60151937Sjkim#define ch_bufhead thisfile->buf_next 61151937Sjkim#define ch_buftail thisfile->buf_prev 62151937Sjkim#define ch_nbufs thisfile->nbufs 63151937Sjkim#define ch_block thisfile->block 64151937Sjkim#define ch_offset thisfile->offset 65151937Sjkim#define ch_fpos thisfile->fpos 66151937Sjkim#define ch_fsize thisfile->fsize 67151937Sjkim#define ch_flags thisfile->flags 68151937Sjkim#define ch_file thisfile->file 69151937Sjkim 70151937Sjkimstatic struct filestate *thisfile; 71151937Sjkimstatic int ch_ungotchar = -1; 72151937Sjkim 73151937Sjkimextern int autobuf; 74151937Sjkimextern int sigs; 75151937Sjkimextern int cbufs; 76151937Sjkimextern int secure; 77151937Sjkimextern constant char helpdata[]; 78151937Sjkimextern constant int size_helpdata; 79151937Sjkimextern IFILE curr_ifile; 80151937Sjkim#if LOGFILE 81151937Sjkimextern int logfile; 82151937Sjkimextern char *namelogfile; 83151937Sjkim#endif 84151937Sjkim 85151937Sjkimstatic int ch_addbuf(); 86151937Sjkim 87151937Sjkim 88151937Sjkim/* 89151937Sjkim * Get the character pointed to by the read pointer. 90151937Sjkim * ch_get() is a macro which is more efficient to call 91151937Sjkim * than fch_get (the function), in the usual case 92151937Sjkim * that the block desired is at the head of the chain. 93151937Sjkim */ 94151937Sjkim#define ch_get() ((ch_block == ch_bufhead->block && \ 95151937Sjkim ch_offset < ch_bufhead->datasize) ? \ 96151937Sjkim ch_bufhead->data[ch_offset] : fch_get()) 97167802Sjkim int 98167802Sjkimfch_get() 99167802Sjkim{ 100167802Sjkim register struct buf *bp; 101167802Sjkim register int n; 102167802Sjkim register int slept; 103151937Sjkim POSITION pos; 104193529Sjkim POSITION len; 105193529Sjkim 106193529Sjkim slept = FALSE; 107193529Sjkim 108193529Sjkim /* 109193529Sjkim * Look for a buffer holding the desired block. 110167802Sjkim */ 111212761Sjkim for (bp = ch_bufhead; bp != END_OF_CHAIN; bp = bp->next) 112193529Sjkim if (bp->block == ch_block) 113193529Sjkim { 114193529Sjkim if (ch_offset >= bp->datasize) 115193529Sjkim /* 116118611Snjl * Need more data in this buffer. 117118611Snjl */ 118118611Snjl goto read_more; 119118611Snjl goto found; 120118611Snjl } 121118611Snjl /* 122118611Snjl * Block is not in a buffer. 123118611Snjl * Take the least recently used buffer 124118611Snjl * and read the desired block into it. 125118611Snjl * If the LRU buffer has data in it, 126118611Snjl * then maybe allocate a new buffer. 127118611Snjl */ 128118611Snjl if (ch_buftail == END_OF_CHAIN || ch_buftail->block != (long)(-1)) 129151937Sjkim { 130118611Snjl /* 131118611Snjl * There is no empty buffer to use. 132118611Snjl * Allocate a new buffer if: 133118611Snjl * 1. We can't seek on this file and -b is not in effect; or 134118611Snjl * 2. We haven't allocated the max buffers for this file yet. 135118611Snjl */ 136118611Snjl if ((autobuf && !(ch_flags & CH_CANSEEK)) || 137151937Sjkim (cbufs == -1 || ch_nbufs < cbufs)) 138118611Snjl if (ch_addbuf()) 139118611Snjl /* 140118611Snjl * Allocation failed: turn off autobuf. 141118611Snjl */ 142118611Snjl autobuf = OPT_OFF; 143209746Sjkim } 144167802Sjkim bp = ch_buftail; 145167802Sjkim bp->block = ch_block; 146167802Sjkim bp->datasize = 0; 147118611Snjl 148151937Sjkim read_more: 149151937Sjkim pos = (ch_block * LBUFSIZE) + bp->datasize; 150118611Snjl if ((len = ch_length()) != NULL_POSITION && pos >= len) 151151937Sjkim /* 152118611Snjl * At end of file. 153151937Sjkim */ 154151937Sjkim return (EOI); 155151937Sjkim 156151937Sjkim if (pos != ch_fpos) 157151937Sjkim { 158151937Sjkim /* 159167802Sjkim * Not at the correct position: must seek. 160151937Sjkim * If input is a pipe, we're in trouble (can't seek on a pipe). 161151937Sjkim * Some data has been lost: just return "?". 162118611Snjl */ 163151937Sjkim if (!(ch_flags & CH_CANSEEK)) 164151937Sjkim return ('?'); 165151937Sjkim if (lseek(ch_file, (off_t)pos, 0) == BAD_LSEEK) 166151937Sjkim { 167151937Sjkim error("seek error", NULL_PARG); 168151937Sjkim clear_eol(); 169151937Sjkim return (EOI); 170151937Sjkim } 171151937Sjkim ch_fpos = pos; 172151937Sjkim } 173151937Sjkim 174151937Sjkim /* 175151937Sjkim * Read the block. 176151937Sjkim * If we read less than a full block, that's ok. 177151937Sjkim * We use partial block and pick up the rest next time. 178151937Sjkim */ 179151937Sjkim if (ch_ungotchar != -1) 180151937Sjkim { 181118611Snjl bp->data[bp->datasize] = ch_ungotchar; 182118611Snjl n = 1; 183151937Sjkim ch_ungotchar = -1; 184151937Sjkim } else if (ch_flags & CH_HELPFILE) 185151937Sjkim { 186118611Snjl bp->data[bp->datasize] = helpdata[ch_fpos]; 187118611Snjl n = 1; 188118611Snjl } else 189118611Snjl { 190151937Sjkim n = iread(ch_file, &bp->data[bp->datasize], 191151937Sjkim (unsigned int)(LBUFSIZE - bp->datasize)); 192151937Sjkim } 193151937Sjkim 194118611Snjl if (n == READ_INTR) 195118611Snjl return (EOI); 196118611Snjl if (n < 0) 197118611Snjl { 198118611Snjl#if MSDOS_COMPILER==WIN32C 199151937Sjkim if (errno != EPIPE) 200151937Sjkim#endif 201151937Sjkim { 202151937Sjkim error("read error", NULL_PARG); 203118611Snjl clear_eol(); 204151937Sjkim } 205151937Sjkim n = 0; 206151937Sjkim } 207151937Sjkim 208118611Snjl#if LOGFILE 209151937Sjkim /* 210118611Snjl * If we have a log file, write the new data to it. 211151937Sjkim */ 212151937Sjkim if (!secure && logfile >= 0 && n > 0) 213118611Snjl write(logfile, (char *) &bp->data[bp->datasize], n); 214151937Sjkim#endif 215118611Snjl 216151937Sjkim ch_fpos += n; 217151937Sjkim bp->datasize += n; 218151937Sjkim 219118611Snjl /* 220118611Snjl * If we have read to end of file, set ch_fsize to indicate 221118611Snjl * the position of the end of file. 222151937Sjkim */ 223118611Snjl if (n == 0) 224118611Snjl { 225118611Snjl ch_fsize = pos; 226118611Snjl if (ignore_eoi) 227151937Sjkim { 228118611Snjl /* 229151937Sjkim * We are ignoring EOF. 230151937Sjkim * Wait a while, then try again. 231151937Sjkim */ 232151937Sjkim if (!slept) 233118611Snjl ierror("Waiting for data", NULL_PARG); 234151937Sjkim#if !MSDOS_COMPILER 235151937Sjkim sleep(1); 236151937Sjkim#else 237151937Sjkim#if MSDOS_COMPILER==WIN32C 238151937Sjkim Sleep(1000); 239151937Sjkim#endif 240151937Sjkim#endif 241151937Sjkim slept = TRUE; 242151937Sjkim } 243151937Sjkim if (sigs) 244151937Sjkim return (EOI); 245151937Sjkim } 246151937Sjkim 247151937Sjkim found: 248151937Sjkim if (ch_bufhead != bp) 249151937Sjkim { 250151937Sjkim /* 251151937Sjkim * Move the buffer to the head of the buffer chain. 252151937Sjkim * This orders the buffer chain, most- to least-recently used. 253151937Sjkim */ 254151937Sjkim bp->next->prev = bp->prev; 255151937Sjkim bp->prev->next = bp->next; 256151937Sjkim 257151937Sjkim bp->next = ch_bufhead; 258151937Sjkim bp->prev = END_OF_CHAIN; 259151937Sjkim ch_bufhead->prev = bp; 260151937Sjkim ch_bufhead = bp; 261151937Sjkim } 262151937Sjkim 263151937Sjkim if (ch_offset >= bp->datasize) 264151937Sjkim /* 265151937Sjkim * After all that, we still don't have enough data. 266151937Sjkim * Go back and try again. 267118611Snjl */ 268118611Snjl goto read_more; 269118611Snjl 270151937Sjkim return (bp->data[ch_offset]); 271118611Snjl} 272151937Sjkim 273151937Sjkim/* 274151937Sjkim * ch_ungetchar is a rather kludgy and limited way to push 275151937Sjkim * a single char onto an input file descriptor. 276118611Snjl */ 277118611Snjl public void 278118611Snjlch_ungetchar(c) 279151937Sjkim int c; 280151937Sjkim{ 281151937Sjkim if (c != -1 && ch_ungotchar != -1) 282151937Sjkim error("ch_ungetchar overrun", NULL_PARG); 283151937Sjkim ch_ungotchar = c; 284151937Sjkim} 285151937Sjkim 286151937Sjkim#if LOGFILE 287151937Sjkim/* 288151937Sjkim * Close the logfile. 289151937Sjkim * If we haven't read all of standard input into it, do that now. 290151937Sjkim */ 291151937Sjkim public void 292151937Sjkimend_logfile() 293151937Sjkim{ 294151937Sjkim static int tried = FALSE; 295151937Sjkim 296151937Sjkim if (logfile < 0) 297118611Snjl return; 298118611Snjl if (!tried && ch_fsize == NULL_POSITION) 299151937Sjkim { 300151937Sjkim tried = TRUE; 301151937Sjkim ierror("Finishing logfile", NULL_PARG); 302151937Sjkim while (ch_forw_get() != EOI) 303151937Sjkim if (ABORT_SIGS()) 304151937Sjkim break; 305151937Sjkim } 306167802Sjkim close(logfile); 307151937Sjkim logfile = -1; 308151937Sjkim namelogfile = NULL; 309151937Sjkim} 310151937Sjkim 311151937Sjkim/* 312118611Snjl * Start a log file AFTER less has already been running. 313118611Snjl * Invoked from the - command; see toggle_option(). 314118611Snjl * Write all the existing buffered data to the log file. 315151937Sjkim */ 316151937Sjkim public void 317151937Sjkimsync_logfile() 318151937Sjkim{ 319151937Sjkim register struct buf *bp; 320151937Sjkim int warned = FALSE; 321151937Sjkim long block; 322151937Sjkim long nblocks; 323167802Sjkim 324167802Sjkim nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE; 325167802Sjkim for (block = 0; block < nblocks; block++) 326167802Sjkim { 327167802Sjkim for (bp = ch_bufhead; ; bp = bp->next) 328167802Sjkim { 329167802Sjkim if (bp == END_OF_CHAIN) 330167802Sjkim { 331167802Sjkim if (!warned) 332167802Sjkim { 333167802Sjkim error("Warning: log file is incomplete", 334167802Sjkim NULL_PARG); 335167802Sjkim warned = TRUE; 336167802Sjkim } 337167802Sjkim break; 338167802Sjkim } 339167802Sjkim if (bp->block == block) 340167802Sjkim { 341167802Sjkim write(logfile, (char *) bp->data, bp->datasize); 342167802Sjkim break; 343167802Sjkim } 344167802Sjkim } 345167802Sjkim } 346118611Snjl} 347118611Snjl 348118611Snjl#endif 349118611Snjl 350118611Snjl/* 351118611Snjl * Determine if a specific block is currently in one of the buffers. 352118611Snjl */ 353118611Snjl static int 354118611Snjlbuffered(block) 355118611Snjl long block; 356118611Snjl{ 357193529Sjkim register struct buf *bp; 358193529Sjkim 359193529Sjkim for (bp = ch_bufhead; bp != END_OF_CHAIN; bp = bp->next) 360193529Sjkim if (bp->block == block) 361193529Sjkim return (TRUE); 362193529Sjkim return (FALSE); 363193529Sjkim} 364193529Sjkim 365193529Sjkim/* 366193529Sjkim * Seek to a specified position in the file. 367193529Sjkim * Return 0 if successful, non-zero if can't seek there. 368193529Sjkim */ 369167802Sjkim public int 370193529Sjkimch_seek(pos) 371193529Sjkim register POSITION pos; 372167802Sjkim{ 373167802Sjkim long new_block; 374167802Sjkim POSITION len; 375167802Sjkim 376167802Sjkim len = ch_length(); 377167802Sjkim if (pos < ch_zero() || (len != NULL_POSITION && pos > len)) 378167802Sjkim return (1); 379118611Snjl 380118611Snjl new_block = pos / LBUFSIZE; 381193529Sjkim if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block)) 382193529Sjkim { 383193529Sjkim if (ch_fpos > pos) 384193529Sjkim return (1); 385193529Sjkim while (ch_fpos < pos) 386193529Sjkim { 387193529Sjkim if (ch_forw_get() == EOI) 388193529Sjkim return (1); 389193529Sjkim if (ABORT_SIGS()) 390193529Sjkim return (1); 391193529Sjkim } 392193529Sjkim return (0); 393193529Sjkim } 394193529Sjkim /* 395193529Sjkim * Set read pointer. 396193529Sjkim */ 397193529Sjkim ch_block = new_block; 398193529Sjkim ch_offset = pos % LBUFSIZE; 399193529Sjkim return (0); 400193529Sjkim} 401193529Sjkim 402193529Sjkim/* 403193529Sjkim * Seek to the end of the file. 404193529Sjkim */ 405193529Sjkim public int 406193529Sjkimch_end_seek() 407193529Sjkim{ 408193529Sjkim POSITION len; 409193529Sjkim 410193529Sjkim if (ch_flags & CH_CANSEEK) 411193529Sjkim ch_fsize = filesize(ch_file); 412193529Sjkim 413193529Sjkim len = ch_length(); 414193529Sjkim if (len != NULL_POSITION) 415193529Sjkim return (ch_seek(len)); 416193529Sjkim 417193529Sjkim /* 418193529Sjkim * Do it the slow way: read till end of data. 419118611Snjl */ 420118611Snjl while (ch_forw_get() != EOI) 421118611Snjl if (ABORT_SIGS()) 422118611Snjl return (1); 423151937Sjkim return (0); 424118611Snjl} 425118611Snjl 426118611Snjl/* 427118611Snjl * Seek to the beginning of the file, or as close to it as we can get. 428118611Snjl * We may not be able to seek there if input is a pipe and the 429118611Snjl * beginning of the pipe is no longer buffered. 430118611Snjl */ 431118611Snjl public int 432118611Snjlch_beg_seek() 433118611Snjl{ 434118611Snjl register struct buf *bp, *firstbp; 435118611Snjl 436118611Snjl /* 437118611Snjl * Try a plain ch_seek first. 438118611Snjl */ 439118611Snjl if (ch_seek(ch_zero()) == 0) 440118611Snjl return (0); 441118611Snjl 442118611Snjl /* 443167802Sjkim * Can't get to position 0. 444167802Sjkim * Look thru the buffers for the one closest to position 0. 445118611Snjl */ 446118611Snjl firstbp = bp = ch_bufhead; 447118611Snjl if (bp == END_OF_CHAIN) 448118611Snjl return (1); 449118611Snjl while ((bp = bp->next) != END_OF_CHAIN) 450118611Snjl if (bp->block < firstbp->block) 451118611Snjl firstbp = bp; 452118611Snjl ch_block = firstbp->block; 453199337Sjkim ch_offset = 0; 454151937Sjkim return (0); 455193529Sjkim} 456193529Sjkim 457193529Sjkim/* 458193529Sjkim * Return the length of the file, if known. 459193529Sjkim */ 460193529Sjkim public POSITION 461199337Sjkimch_length() 462193529Sjkim{ 463193529Sjkim if (ignore_eoi) 464118611Snjl return (NULL_POSITION); 465118611Snjl if (ch_flags & CH_HELPFILE) 466118611Snjl return (size_helpdata); 467118611Snjl return (ch_fsize); 468118611Snjl} 469118611Snjl 470118611Snjl/* 471118611Snjl * Return the current position in the file. 472118611Snjl */ 473118611Snjl#define tellpos(blk,off) ((POSITION)((((long)(blk)) * LBUFSIZE) + (off))) 474118611Snjl 475118611Snjl public POSITION 476118611Snjlch_tell() 477118611Snjl{ 478118611Snjl return (tellpos(ch_block, ch_offset)); 479118611Snjl} 480151937Sjkim 481118611Snjl/* 482118611Snjl * Get the current char and post-increment the read pointer. 483118611Snjl */ 484118611Snjl public int 485118611Snjlch_forw_get() 486118611Snjl{ 487118611Snjl register int c; 488118611Snjl 489118611Snjl c = ch_get(); 490118611Snjl if (c == EOI) 491118611Snjl return (EOI); 492118611Snjl if (ch_offset < LBUFSIZE-1) 493118611Snjl ch_offset++; 494118611Snjl else 495118611Snjl { 496118611Snjl ch_block ++; 497118611Snjl ch_offset = 0; 498118611Snjl } 499118611Snjl return (c); 500118611Snjl} 501118611Snjl 502118611Snjl/* 503118611Snjl * Pre-decrement the read pointer and get the new current char. 504118611Snjl */ 505118611Snjl public int 506118611Snjlch_back_get() 507118611Snjl{ 508118611Snjl if (ch_offset > 0) 509118611Snjl ch_offset --; 510118611Snjl else 511118611Snjl { 512118611Snjl if (ch_block <= 0) 513118611Snjl return (EOI); 514118611Snjl if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1)) 515151937Sjkim return (EOI); 516118611Snjl ch_block--; 517118611Snjl ch_offset = LBUFSIZE-1; 518118611Snjl } 519118611Snjl return (ch_get()); 520118611Snjl} 521118611Snjl 522118611Snjl/* 523118611Snjl * Allocate buffers. 524118611Snjl * Caller wants us to have a total of at least want_nbufs buffers. 525199337Sjkim */ 526151937Sjkim public int 527118611Snjlch_nbuf(want_nbufs) 528118611Snjl int want_nbufs; 529118611Snjl{ 530118611Snjl PARG parg; 531118611Snjl 532118611Snjl while (ch_nbufs < want_nbufs) 533118611Snjl { 534118611Snjl if (ch_addbuf()) 535118611Snjl { 536118611Snjl /* 537118611Snjl * Cannot allocate enough buffers. 538118611Snjl * If we don't have ANY, then quit. 539118611Snjl * Otherwise, just report the error and return. 540167802Sjkim */ 541167802Sjkim parg.p_int = want_nbufs - ch_nbufs; 542167802Sjkim error("Cannot allocate %d buffers", &parg); 543167802Sjkim if (ch_nbufs == 0) 544167802Sjkim quit(QUIT_ERROR); 545167802Sjkim break; 546167802Sjkim } 547167802Sjkim } 548167802Sjkim return (ch_nbufs); 549167802Sjkim} 550212761Sjkim 551167802Sjkim/* 552167802Sjkim * Flush (discard) any saved file state, including buffer contents. 553167802Sjkim */ 554167802Sjkim public void 555167802Sjkimch_flush() 556167802Sjkim{ 557167802Sjkim register struct buf *bp; 558167802Sjkim 559167802Sjkim if (!(ch_flags & CH_CANSEEK)) 560167802Sjkim { 561167802Sjkim /* 562167802Sjkim * If input is a pipe, we don't flush buffer contents, 563167802Sjkim * since the contents can't be recovered. 564167802Sjkim */ 565167802Sjkim ch_fsize = NULL_POSITION; 566167802Sjkim return; 567167802Sjkim } 568167802Sjkim 569167802Sjkim /* 570167802Sjkim * Initialize all the buffers. 571167802Sjkim */ 572167802Sjkim for (bp = ch_bufhead; bp != END_OF_CHAIN; bp = bp->next) 573167802Sjkim bp->block = (long)(-1); 574167802Sjkim 575167802Sjkim /* 576167802Sjkim * Figure out the size of the file, if we can. 577167802Sjkim */ 578167802Sjkim ch_fsize = filesize(ch_file); 579167802Sjkim 580167802Sjkim /* 581167802Sjkim * Seek to a known position: the beginning of the file. 582167802Sjkim */ 583167802Sjkim ch_fpos = 0; 584167802Sjkim ch_block = 0; /* ch_fpos / LBUFSIZE; */ 585167802Sjkim ch_offset = 0; /* ch_fpos % LBUFSIZE; */ 586167802Sjkim 587167802Sjkim#if 1 588167802Sjkim /* 589167802Sjkim * This is a kludge to workaround a Linux kernel bug: files in 590167802Sjkim * /proc have a size of 0 according to fstat() but have readable 591167802Sjkim * data. They are sometimes, but not always, seekable. 592167802Sjkim * Force them to be non-seekable here. 593167802Sjkim */ 594167802Sjkim if (ch_fsize == 0) 595167802Sjkim { 596167802Sjkim ch_fsize = NULL_POSITION; 597167802Sjkim ch_flags &= ~CH_CANSEEK; 598167802Sjkim } 599167802Sjkim#endif 600167802Sjkim 601167802Sjkim if (lseek(ch_file, (off_t)0, 0) == BAD_LSEEK) 602167802Sjkim { 603167802Sjkim /* 604167802Sjkim * Warning only; even if the seek fails for some reason, 605167802Sjkim * there's a good chance we're at the beginning anyway. 606167802Sjkim * {{ I think this is bogus reasoning. }} 607167802Sjkim */ 608167802Sjkim error("seek error to 0", NULL_PARG); 609167802Sjkim } 610167802Sjkim} 611167802Sjkim 612167802Sjkim/* 613167802Sjkim * Allocate a new buffer. 614167802Sjkim * The buffer is added to the tail of the buffer chain. 615167802Sjkim */ 616167802Sjkim static int 617167802Sjkimch_addbuf() 618167802Sjkim{ 619167802Sjkim register struct buf *bp; 620167802Sjkim 621167802Sjkim /* 622167802Sjkim * Allocate and initialize a new buffer and link it 623167802Sjkim * onto the tail of the buffer list. 624167802Sjkim */ 625167802Sjkim bp = (struct buf *) calloc(1, sizeof(struct buf)); 626167802Sjkim if (bp == NULL) 627167802Sjkim return (1); 628167802Sjkim ch_nbufs++; 629167802Sjkim bp->block = (long)(-1); 630167802Sjkim bp->next = END_OF_CHAIN; 631167802Sjkim bp->prev = ch_buftail; 632167802Sjkim ch_buftail->next = bp; 633167802Sjkim ch_buftail = bp; 634167802Sjkim return (0); 635167802Sjkim} 636167802Sjkim 637167802Sjkim/* 638167802Sjkim * Delete all buffers for this file. 639167802Sjkim */ 640167802Sjkim static void 641167802Sjkimch_delbufs() 642167802Sjkim{ 643167802Sjkim register struct buf *bp; 644167802Sjkim 645167802Sjkim while (ch_bufhead != END_OF_CHAIN) 646167802Sjkim { 647167802Sjkim bp = ch_bufhead; 648167802Sjkim bp->next->prev = bp->prev;; 649167802Sjkim bp->prev->next = bp->next; 650167802Sjkim free(bp); 651167802Sjkim } 652167802Sjkim ch_nbufs = 0; 653167802Sjkim} 654167802Sjkim 655167802Sjkim/* 656167802Sjkim * Is it possible to seek on a file descriptor? 657167802Sjkim */ 658167802Sjkim public int 659167802Sjkimseekable(f) 660167802Sjkim int f; 661167802Sjkim{ 662167802Sjkim#if MSDOS_COMPILER 663167802Sjkim extern int fd0; 664167802Sjkim if (f == fd0 && !isatty(fd0)) 665167802Sjkim { 666167802Sjkim /* 667167802Sjkim * In MS-DOS, pipes are seekable. Check for 668167802Sjkim * standard input, and pretend it is not seekable. 669167802Sjkim */ 670167802Sjkim return (0); 671167802Sjkim } 672167802Sjkim#endif 673167802Sjkim return (lseek(f, (off_t)1, 0) != BAD_LSEEK); 674167802Sjkim} 675167802Sjkim 676167802Sjkim/* 677167802Sjkim * Initialize file state for a new file. 678167802Sjkim */ 679167802Sjkim public void 680199337Sjkimch_init(f, flags) 681167802Sjkim int f; 682167802Sjkim int flags; 683167802Sjkim{ 684167802Sjkim /* 685167802Sjkim * See if we already have a filestate for this file. 686167802Sjkim */ 687118611Snjl thisfile = (struct filestate *) get_filestate(curr_ifile); 688118611Snjl if (thisfile == NULL) 689118611Snjl { 690118611Snjl /* 691118611Snjl * Allocate and initialize a new filestate. 692118611Snjl */ 693118611Snjl thisfile = (struct filestate *) 694118611Snjl calloc(1, sizeof(struct filestate)); 695118611Snjl thisfile->buf_next = thisfile->buf_prev = END_OF_CHAIN; 696118611Snjl thisfile->buf_block = (long)(-1); 697118611Snjl thisfile->nbufs = 0; 698118611Snjl thisfile->flags = 0; 699118611Snjl thisfile->fpos = 0; 700118611Snjl thisfile->block = 0; 701118611Snjl thisfile->offset = 0; 702118611Snjl thisfile->file = -1; 703118611Snjl thisfile->fsize = NULL_POSITION; 704118611Snjl ch_flags = flags; 705118611Snjl /* 706118611Snjl * Try to seek; set CH_CANSEEK if it works. 707118611Snjl */ 708118611Snjl if ((flags & CH_CANSEEK) && !seekable(f)) 709118611Snjl ch_flags &= ~CH_CANSEEK; 710118611Snjl set_filestate(curr_ifile, (void *) thisfile); 711118611Snjl } 712118611Snjl if (thisfile->file == -1) 713118611Snjl thisfile->file = f; 714118611Snjl ch_flush(); 715118611Snjl} 716118611Snjl 717118611Snjl/* 718118611Snjl * Close a filestate. 719118611Snjl */ 720118611Snjl public void 721118611Snjlch_close() 722118611Snjl{ 723118611Snjl int keepstate = FALSE; 724118611Snjl 725118611Snjl if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) 726118611Snjl { 727118611Snjl /* 728118611Snjl * We can seek or re-open, so we don't need to keep buffers. 729118611Snjl */ 730118611Snjl ch_delbufs(); 731118611Snjl } else 732118611Snjl keepstate = TRUE; 733118611Snjl if (!(ch_flags & CH_KEEPOPEN)) 734118611Snjl { 735118611Snjl /* 736118611Snjl * We don't need to keep the file descriptor open 737118611Snjl * (because we can re-open it.) 738118611Snjl * But don't really close it if it was opened via popen(), 739118611Snjl * because pclose() wants to close it. 740118611Snjl */ 741118611Snjl if (!(ch_flags & (CH_POPENED|CH_HELPFILE))) 742118611Snjl close(ch_file); 743118611Snjl ch_file = -1; 744118611Snjl } else 745118611Snjl keepstate = TRUE; 746118611Snjl if (!keepstate) 747118611Snjl { 748118611Snjl /* 749118611Snjl * We don't even need to keep the filestate structure. 750151937Sjkim */ 751118611Snjl free(thisfile); 752118611Snjl thisfile = NULL; 753118611Snjl set_filestate(curr_ifile, (void *) NULL); 754118611Snjl } 755118611Snjl} 756118611Snjl 757118611Snjl/* 758118611Snjl * Return ch_flags for the current file. 759118611Snjl */ 760151937Sjkim public int 761118611Snjlch_getflags() 762118611Snjl{ 763118611Snjl return (ch_flags); 764118611Snjl} 765118611Snjl 766118611Snjl#if 0 767118611Snjl public void 768118611Snjlch_dump(struct filestate *fs) 769118611Snjl{ 770118611Snjl struct buf *bp; 771118611Snjl unsigned char *s; 772118611Snjl 773118611Snjl if (fs == NULL) 774118611Snjl { 775118611Snjl printf(" --no filestate\n"); 776118611Snjl return; 777118611Snjl } 778118611Snjl printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n", 779118611Snjl fs->file, fs->flags, fs->fpos, 780118611Snjl fs->fsize, fs->block, fs->offset); 781118611Snjl printf(" %d bufs:\n", fs->nbufs); 782118611Snjl for (bp = fs->buf_next; bp != (struct buf *)fs; bp = bp->next) 783118611Snjl { 784118611Snjl printf("%x: blk %x, size %x \"", 785118611Snjl bp, bp->block, bp->datasize); 786118611Snjl for (s = bp->data; s < bp->data + 30; s++) 787118611Snjl if (*s >= ' ' && *s < 0x7F) 788118611Snjl printf("%c", *s); 789118611Snjl else 790118611Snjl printf("."); 791118611Snjl printf("\"\n"); 792118611Snjl } 793118611Snjl} 794118611Snjl#endif 795118611Snjl