/********************************************************************
* lindner
* 3.12
* 1995/02/17 18:29:36
* /home/arcwelder/GopherSrc/CVS/gopher+/object/BLblock.c,v
* Exp
*
* Paul Lindner, University of Minnesota CIS.
*
* Copyright 1991, 1992, 1993 by the Regents of the University of Minnesota
* see the file "Copyright" in the distribution for conditions of use.
*********************************************************************
* MODULE: BLblock.c
* Implementation of Block handling routines.
*********************************************************************
* Revision History:
* BLblock.c,v
* Revision 3.12  1995/02/17  18:29:36  lindner
* Abstract display support
*
* Revision 3.11  1994/08/08  20:26:35  lindner
* fix bug for Abstract= lines.
*
* Revision 3.10  1994/06/29  05:45:24  lindner
* Mods for tickets
*
* Revision 3.9  1994/02/20  21:49:09  lindner
* deal with malformed views lines better
*
* Revision 3.8  1993/11/02  06:13:36  lindner
* Fix for HTML mods
*
* Revision 3.7  1993/07/29  19:59:23  lindner
* Removed extraneous variables
*
* Revision 3.6  1993/07/27  05:30:17  lindner
* Mondo Debug overhaul from Mitra
*
* Revision 3.5  1993/06/15  06:10:34  lindner
* Moved string.h include
*
* Revision 3.4  1993/04/27  20:54:32  lindner
* really fixed BLtoNet()
*
* Revision 3.3  1993/04/23  20:14:48  lindner
* Fix for BltoNet(), using wrong datatype
*
* Revision 3.2  1993/03/26  19:49:34  lindner
* More comments, memory leak fix, etc
*
* Revision 3.1.1.1  1993/02/11  18:03:06  lindner
* Gopher+1.2beta release
*
* Revision 2.1  1993/02/09  22:45:54  lindner
* Fixes for retrieving from the net.  New BLAsearch
*
* Revision 1.1  1993/01/31  00:22:51  lindner
* Initial revision
*
*
*********************************************************************/

#include "String.h"
#include "BLblock.h"
#include "Malloc.h"
#include <stdio.h>

/*
* Make a new Block Object
*/

Blockobj *
BLnew()
{
    Blockobj *temp;

    temp = (Blockobj *) malloc(sizeof(Blockobj));

    temp->btype = BLOCK_UNKNOWN;
    temp->Blockname = STRnew();
    temp->datatype  = BDATA_NONE;

    return(temp);
}


/*
* Initialize the block structure
*/

void
BLinit(bl)
 Blockobj *bl;
{
    STRinit(bl->Blockname);
    switch (BLgetDatatype(bl)) {
    case BDATA_FILE:
         STRinit(bl->data.filename);
         break;
    case BDATA_GREF:
         GSinit(bl->data.gs);
         break;
    case BDATA_TEXT:
         STAinit(bl->data.text);
         break;
    }
}

/*
* Calls the right destory fcn depending on the object in the union.
*
* wouldn't c++ be nice right here? :-)
*/

static void
BLdatadestroy(bl)
 Blockobj *bl;
{
    switch (BLgetDatatype(bl)) {
    case BDATA_FILE:
         STRdestroy(bl->data.filename);
         break;
    case BDATA_TEXT:
         STAdestroy(bl->data.text);
         break;
    case BDATA_GREF:
         GSdestroy(bl->data.gs);
         break;
    }
}

/*
* Free memory of BlockObj
*/

void
BLdestroy(bl)
 Blockobj *bl;
{
    STRdestroy(bl->Blockname);
    BLdatadestroy(bl);
    free(bl);
}


void
BLcpy(dest, orig)
 Blockobj *dest,*orig;
{
    BLsetName(dest,BLgetName(orig));
    dest->btype = orig->btype;

    switch (BLgetDatatype(orig)) {
    case BDATA_FILE:
         BLsetFile(dest, STRget(orig->data.filename));
         break;
    case BDATA_GREF:
         BLsetGref(dest, orig->data.gs);
         break;
    case BDATA_TEXT:
         BLsetText(dest, orig->data.text);
         break;
    }
}


/*
* BLgetNumLines() returns the number of lines for the Text record
* It returns -1 if this isn't the type of record.
*/

int
BLgetNumLines(bl)
 Blockobj *bl;
{
    switch (BLgetDatatype(bl)) {
    case BDATA_TEXT:
         return(STAgetTop(bl->data.text));
    default:
         return(-1);
    }
    ;
}


/*
* BLgetLine() returns a character pointer to the line for the Text data type
*/

char*
BLgetLine(bl, lineno)
 Blockobj *bl;
 int lineno;
{
    switch (BLgetDatatype(bl)) {
    case BDATA_TEXT:
         return(STRget(STAgetEntry(bl->data.text, lineno)));
    }
    return(NULL);
}


/*
* BLsetFile() sets up a block to contain a file reference.
* Really handy for the server.
*/

void
BLsetFile(bl, filename)
 Blockobj *bl;
 char *filename;
{
    /** Reset data field and put in new values **/

    BLdatadestroy(bl);

    bl->datatype      = BDATA_FILE;
    bl->data.filename = STRnew();

    STRset(bl->data.filename, filename);
}


/*
* BLsetText sets everything up for storing text, if the optional parameter
* sta is set, it copies it in.
*/

void
BLsetText(bl, sta)
 Blockobj *bl;
 StrArray *sta;
{
    /** Reset data field and put in new values **/
    if (BLgetDatatype(bl) != BDATA_TEXT) {
         BLdatadestroy(bl);

         bl->datatype = BDATA_TEXT;
         bl->data.text = STAnew(10);
    }
    if (sta != NULL) {
         STAcpy(bl->data.text, sta);
    }
}


/*
* Keep data in memory, text is a line to be added to the text
*/

void
BLaddText(bl, text)
 Blockobj *bl;
 char *text;
{
    String   *tempstr;

    BLsetText(bl, NULL);

    tempstr = STRnew();

    STRset(tempstr, text);
    STApush(bl->data.text, tempstr);

    STRdestroy(tempstr);
}

/*
* Make the block a gopher-reference
*/

void
BLsetGref(bl, gs)
 Blockobj *bl;
 GopherObj *gs;
{
    /** Reset data field and put in new values **/
    BLdatadestroy(bl);

    bl->datatype = BDATA_GREF;

    bl->data.gs = GSnew();

    GScpy(bl->data.gs, gs);

    GSdestroy(gs);
}


/*
* BLtoNet() transmits the block as per gopher+ protocol
* if the data field is a file, it opens it and sends it out
*/

void
BLtoNet(bl, fd, showheader)
 Blockobj *bl;
 int fd;
 boolean showheader;
{
    FILE *infile;
    int i;
    char outputline[512];

    /** Switch on data type **/

    if (showheader) {
         sprintf(outputline, "+%s:", BLgetName(bl));
         writestring(fd, outputline);
    }

    switch (BLgetDatatype(bl)) {

    case BDATA_GREF:
         writestring(fd, " ");
         GStoNet(bl->data.gs,fd, GSFORM_G0, NULL);
         break;

    case BDATA_TEXT:
         writestring(fd, "\r\n");
         for (i=0; i<STAgetTop(bl->data.text); i++) {
              writestring(fd, " ");
              writestring(fd, STRget(STAgetEntry(bl->data.text, i)));
              writestring(fd, "\r\n");
         }
         break;

    case BDATA_FILE:
         writestring(fd, "\r\n");

         if ((infile = fopen(STRget(bl->data.filename), "r"))==NULL)
              break;

         while (fgets(outputline, sizeof(outputline), infile)!=NULL)  {
              ZapCRLF(outputline);
              writestring(fd, " ");
              writestring(fd, outputline);
              writestring(fd, "\r\n");
         }
         fclose(infile);
         break;
    }
}


/*
* BLfromNet() assumes that the initial '+' in the data stream has been read,
* along with the blockname, up to the ':', but not anything after..
*
* It then executes most of the state diagram, it returns when it has
* encountered EOF, or encounters the next '+', or '.'
*
* Returns the following:
*  0 for EOF encountered, block retrieved successfully
*  1 for successful retrieve, and another block coming
*  neg value for error conditions from read routines and botched block vals
*/

int
BLfromNet(bl, fd, blockname)
 Blockobj *bl;
 int fd;
 char *blockname;
{
    char inputline[512];
    int err;


    /*** State: _GotBlockName_ ***/

    BLsetName(bl, blockname);

    /** Find out if there's a gopher reference **/
    err = readrecvbuf(fd, inputline, 1);

    if (*inputline == ' ') {
         /** GREF coming up, State: _GotBnameSpace_ **/
         GopherObj *gs;

         gs = GSnew();
         GSfromNet(gs, fd);
         BLsetGref(bl, gs);
         /** read up to the next \r\n+ **/
         while (1) {
              if ((err= readrecvbuf(fd, inputline, 1)) <= 0)
                   return(err);
              if (*inputline == '+')
                   return(1);
              else {
                   err = readline(fd, inputline, sizeof(inputline));
                   if (err <= 0)
                        return(err); /*** Error ***/
              }
         }
    }

    /** Okay, let's just stick the text in verbatim **/

    /** get rid of remaining newline **/
    readline(fd, inputline, sizeof(inputline));

    /** State: _FirstChar_ **/

    while (1) {
         /** Check for plus **/
         if ((err= readrecvbuf(fd, inputline, 1)) <=0)
              return(err);

         if (*inputline == '+')
              return(1);
              /*** Return to state _NewBlock_ ***/

         if (*inputline == '.') {
              readline(fd, inputline, sizeof(inputline));
              return(0);
         }

         if (*inputline == ' ') {
              /** add a line otherwise   State: _Addline_ **/
              readline(fd, inputline, sizeof(inputline));
              ZapCRLF(inputline);
              BLaddText(bl, inputline);
         } else {
              /** malformed block line received
                proceed as if it was OK, but don't add the line **/
              readline(fd, inputline, sizeof(inputline));
        }
    }
}


int
BLAsearch(bla, bname)
 BlockArray *bla;
 char *bname;
{
    int i;

    if (bla == NULL)
         return(-1);

    for (i=0; i<BLAgetTop(bla); i++) {
         if (strcmp(BLgetName(BLAgetEntry(bla,i)), bname)==0)
              return(i);
    }

    return(-1);
}