/** 
   --  Block-oriented Subroutines
   
    Copyright (C) Tektronix, Inc. 1998 - 2001. All rights reserved.
  
    @see     GNU LGPL
    @author  Tektronix CTE            @(#) %derived_by: guidod %
    @version %version: bln_mpt1!1.16 %
      (%date_modified: Tue Jun 04 17:09:09 2002 %)
 */
/*@{*/
#if defined(__version_control__) && defined(__GNUC__)
static char* id __attribute__((unused)) = 
"@(#) $Id: %full_filespec:  block-sub.c~bln_mpt1!1.16:csrc:bln_12xx!1 % $";
#endif

#define _P4_SOURCE 1

#include <pfe/pfe-base.h>
#include <pfe/def-xtra.h>

#include <errno.h>
#include <string.h>

#include <pfe/file-sub.h>
#include <pfe/logging.h>
#include <pfe/_nonansi.h>
#include <pfe/_missing.h>

/* ********************************************************************** 
   file interface
 */
/**
 */
static p4_File *
p4_free_file_slot (void)
{
    p4_File *f;

    for (f = PFE.files; f < PFE.files_top; f++)
        if (f->f == NULL)
        
{
            memset (f, 0, sizeof *f);
            return f;
        }
return NULL; }
/**
   Return best possible access method,
   0 if no access but file exists, -1 if file doesn't exist.
 */
_export int
p4_file_access (const char *fn, int len)
{
    char* buf;

    buf = p4_pocket_filename (fn, len);
    if (access (buf, F_OK) != 0)
        return -1;
    if (access (buf, R_OK | W_OK) == 0)
        return FMODE_RW;
    if (access (buf, R_OK) == 0)
        return FMODE_RO;
    if (access (buf, W_OK) == 0)
        return FMODE_WO;
    return 0;
}
static char open_mode[][4] =	/* mode strings for fopen() */
{
    "r", "r+", "r+",		/* R/O W/O R/W */
    "rb", "r+b", "r+b",		/* after application of BIN */
}
;
/**
   open file
 */
_export p4_File *
p4_open_file (const char *name, int len, int mode)
{
    p4_File *fid;
    mode &= 7;

    fid = p4_free_file_slot ();
    if (fid == NULL)
        return NULL;
    p4_store_filename (name, len, fid->name, sizeof fid->name);
    fid->mode = mode;
    fid->last_op = 0;
    strcpy (fid->mdstr, open_mode[mode - FMODE_RO]);
    if ((fid->f = fopen (fid->name, fid->mdstr)) == NULL)
        return NULL;
    fid->size = (p4ucell) (fsize (fid->f) / BPBUF);
    fid->n = (unsigned) -1;
    return fid;
}
/**
   create file 
 */
_export p4_File *
p4_create_file (const char *name, int len, int mode)
{
#   define null_AT_fclose(X) { FILE* f = (X); if (!f) goto _null; fclose(f); }

    char* fn;
    p4_File *fid;

    fn = p4_pocket_filename (name, len);
    null_AT_fclose (fopen (fn, "wb"));
    fid = p4_open_file (name, len, mode);
    if (fid)
    
{
        return fid;
    }
else
{
        _pfe_remove (fn);
        return NULL;
    }
# undef null_AT_fclose _null: if (mode > 256)
{ P4_fail2 ("%s : %s", fn, strerror(errno)); }
/* updec! */ return NULL; }
/**
   close file
 */
_export int
p4_close_file (p4_File *fid)
{
    int res = 0;
    
    if (fid->f)
    
{
        res = fclose (fid->f);
        memset (fid, 0, sizeof *fid);
    }
return res; }
/**
   seek file
 */
_export int
p4_reposition_file (p4_File *fid, long pos)
{
    fid->last_op = 0;
    return fseek (fid->f, pos, SEEK_SET) ? errno : 0;
}
/*
   Called before trying to read from a file.
   Checks if you may, maybe fseeks() so you can.
 */
static int
p4_can_read (p4_File *fid)
{
    switch (fid->mode)		/* check permission */
    
{
     case FMODE_WO:
     case FMODE_WOB:
         return 0;
    }
if (fid->last_op < 0) /* last operation was write? */ fseek (fid->f, 0, SEEK_CUR); /* then seek to this position */ fid->last_op = 1; return 1; }
/*
   Called before trying to write to a file.
   Checks if you may, maybe fseeks() so you can.
 */
static int
p4_can_write (p4_File *fid)
{
    switch (fid->mode)		/* check permission */
    
{
     case FMODE_RO:
     case FMODE_ROB:
         return 0;
    }
if (fid->last_op > 0) /* last operation was read? */ fseek (fid->f, 0, SEEK_CUR); /* then seek to this position */ fid->last_op = -1; return 1; }
/**
   read file
 */
_export int
p4_read_file (void *p, p4ucell *n, p4_File *fid)
{
    int m;

    if (!p4_can_read (fid))
        return EPERM;
    errno = 0;
    m = fread (p, 1, *n, fid->f);
    if (m != (int) *n)
    
{
        *n = m;
        return errno;
    }
else return 0; }
/**
   write file
 */
_export int
p4_write_file (void *p, p4ucell n, p4_File *fid)
{
    if (!p4_can_write (fid))
        return EPERM;
    errno = 0;
    return (p4ucell) fwrite (p, 1, n, fid->f) != n ? errno : 0;
}
/**
   resize file
 */
_export int
p4_resize_file (p4_File *fid, long size)
{
    long pos;
    int r;

    if (fid == NULL || fid->f == NULL)
        p4_throw (P4_ON_FILE_NEX);

    pos = ftell (fid->f);
    if (pos == -1)
        return -1;
    
    fclose (fid->f);
    r = fn_resize (fid->name, size);
    fid->f = fopen (fid->name, fid->mdstr);
    
    if (pos < size)
        fseek (fid->f, pos, SEEK_SET);
    else
        fseek (fid->f, 0, SEEK_END);
    return r;
}
/**
   read line
 */
_export int
p4_read_line (char *p, p4ucell *u, p4_File *fid, p4cell *ior)
{
    int c, n;
    
    if (!p4_can_read (fid))
        return EPERM;
    fid->pos = ftell (fid->f);
    for (n = 0; (p4ucell) n < *u; n++)
    
{
        switch (c = getc (fid->f))
        
{
         default:
             *p++ = c;
             continue;
         case EOF:
             *u = n;
             if (ferror (fid->f))
                 *ior = errno;
             else
                 *ior = 0;
             return P4_FLAG (n > 0);
         case '\r':
             c = getc (fid->f);
             if (c != '\n')
                 ungetc (c, fid->f);
         case '\n':
             goto happy;
        }
}
happy: *u = n; *ior = 0; fid->n++; return P4_TRUE; }
/* -------------------------------------------------------------------- *
   block-files
 */
/**
   source input: read from block-file 
 */
_export p4_File *
p4_open_blockfile (const char *name, int len)
{
    char* fn;
    int mode;

    fn = p4_pocket_expanded_filename (name, len, 
				      P4_opt.blkpaths, P4_opt.blkext);
    mode = p4_file_access (fn, strlen (fn));
    if (mode <= 0)
        return NULL;
    return p4_open_file (fn, strlen (fn), mode + FMODE_BIN);
}
/**
   set fid as current block-file, possibly close the old one.
   (does nothing if argument is null, returns the argument)
 */
_export p4_File*
p4_set_blockfile (p4_File* fid)
{
    if (! fid) return fid;
    if (BLOCK_FILE)
    
{
	FX (p4_save_buffers);
	p4_close_file (BLOCK_FILE);
    }
return ((BLOCK_FILE = fid)); }
/**
   very traditional block read/write primitive 
 */
_export void
p4_read_write (p4_File *fid, char *p, p4ucell n, int readflag)
{
    size_t len;
    
    p4_Q_file_open (fid);
    clearerr (fid->f);
    if (n > fid->size)
        p4_throw (P4_ON_INVALID_BLOCK);
    if (readflag && n == fid->size)
    
{
        memset (p, ' ', BPBUF);
        return;
    }
if (fseek (fid->f, n * BPBUF, SEEK_SET) != 0) p4_throws (FX_IOR, fid->name, 0); if (readflag)
{
        if (!p4_can_read (fid))
	p4_throw (P4_ON_BLOCK_READ);
        len = fread (p, 1, BPBUF, fid->f);
        if (ferror (fid->f))
            p4_throws (FX_IOR, fid->name, 0);
        memset (p + len, ' ', BPBUF - len);
    }
else
{
        if (!p4_can_write (fid))
	p4_throw (P4_ON_BLOCK_WRITE);
        len = fwrite (p, 1, BPBUF, fid->f);
        if (len < BPBUF || ferror (fid->f))
            p4_throws (FX_IOR, fid->name, 0);
        if (n == fid->size)
            fid->size++;
    }
return; }
/**
   traditional BUFFER impl
 */
_export char *
p4_buffer (p4_File *fid, p4ucell n, int *reload)
{
    p4_Q_file_open (fid);
    if (fid->n != n)
    
{
        if (fid->updated)
            p4_read_write (fid, fid->buffer, fid->n, P4_FALSE);
        fid->n = n;
        *reload = 1;
    }
else
{
        *reload = 0;
    }
return fid->buffer; }
/**
   traditional BLOCK impl
 */
_export char *
p4_block (p4_File *fid, p4ucell n)
{
    char *p;
    int reload;
    
    p = p4_buffer (fid, n, &reload);
    if (reload)
        p4_read_write (fid, p, n, P4_TRUE);
    return p;
}
/**
   EMPTY-BUFFERS
 */
_export void
p4_empty_buffers (p4_File *fid)
{
    p4_Q_file_open (fid);
    ZERO (fid->buffer);
    fid->n = UINT_MAX;
    fid->updated = 0;
}
/**
   SAVE-BUFFERS
 */
_export void
p4_save_buffers (p4_File *fid)
{
    if (fid && fid->updated)
    
{
        p4_read_write (fid, fid->buffer, fid->n, P4_FALSE);
        fflush (fid->f);
        fid->updated = 0;
    }
}
/**
   UPDATE
 */
_export void
p4_update (p4_File *fid)
{
    p4_Q_file_open (fid);
    if ((int) fid->n < 0)
        p4_throw (P4_ON_INVALID_BLOCK);
    fid->updated = 1;
}
/**
   LIST
 */
_export void
p4_list (p4_File *fid, int n)
{
    int i;

    for (i = 0; i < 16; i++)
    
{
        FX (p4_cr);
        p4_outf ("%2d: ", i);
        p4_dot_line (fid, n, i);
    }
FX (p4_space); SCR = n; }
/**
   => INTERPET file
 */
_export void
p4_load (p4_File *fid, p4ucell blk)
{
    if (blk == 0)
        p4_throw (P4_ON_INVALID_BLOCK);
# ifdef P4_RP_IN_VM 
    RP = (p4xcode **) p4_save_input (RP);
# else
    /* RP = (p4xcode **) p4_save_input (RP); ** FIXME: !! */
# endif
    BLOCK_FILE = fid;
    SOURCE_ID = 0;
    BLK = blk;
    TO_IN = 0;
    FX (p4_interpret);
# ifdef P4_RP_IN_VM 
    RP = (p4xcode **) p4_restore_input (RP);
# else
    /*  RP = (p4xcode **) p4_restore_input (RP); ** FIXME: !! */
# endif
}
/**
   open and => LOAD
 */
_export void
p4_load_file (char *fn, int cnt, int blk)
{
    File *fid = p4_open_blockfile (fn, cnt);
    
    if (fid == NULL)
        p4_throws (FX_IOR, fn, cnt);
    p4_load (fid, blk);
}
/**
   => THRU
 */
_export void
p4_thru (p4_File *fid, int lo, int hi)
{
    int i;

    for (i = lo; i <= hi; i++)
        p4_load (fid, i);
}
/*@}*/