/* Copyright (c) Xerox Corporation 1993. All rights reserved. */
/* tread.c -- Low-level Tioga reader routines.
   David Nichols
   December, 1990 */

#include <stdio.h>		/* for NULL */
#include <ctype.h>
#include "tioga.h"
#include "tread.h"

#define TRUE	1
#define FALSE	0

extern char *malloc();
extern char *realloc();
long GetLength();
char *strsav();

unsigned char trailerID[] = { 0205, 0227 };
unsigned char commentID[] = { 0, 0 };
unsigned char controlID[] = { 0235, 0312 };

tread←Init(r, buf, len, procs, data)
    struct tread←Reader *r;
    char *buf;
    long len;
    struct tread←Procs *procs;
    char *data;
{
    unsigned char *p;
    unsigned char *ubuf = (unsigned char *)buf;
    long propLen, textLen, commentLen, totalControlLen, totalSize;

    r->procs = procs;
    r->data = data;
    r->buf = buf;
    r->totalLen = len;
    /* Find the three main parts and verify that the lengths are ok. */
    p = ubuf + len - tioga←TrailerLen;
    if (CheckID(&p, trailerID) < 0)
	return -1;
    propLen = GetLength(&p);
    textLen = GetLength(&p);
    totalSize = GetLength(&p);
    if (totalSize != len || propLen > len || textLen > len)
	return -1;
    p = ubuf + textLen;
    if (CheckID(&p, commentID) < 0)
	return -1;
    commentLen = GetLength(&p);
    p = ubuf + textLen + commentLen;
    if (CheckID(&p, controlID) < 0)
	return -1;
    totalControlLen = GetLength(&p);
    if (len != textLen + commentLen + totalControlLen)
	return -1;

    /* Find start and end of each section of the file. */
    r->text.next = ubuf;
    r->text.limit = r->text.next + textLen;
    r->com.next = ubuf + textLen + tioga←CommentHeaderLen;
    r->com.limit = ubuf + textLen + commentLen;
    r->control.next = ubuf + textLen + commentLen + tioga←ControlHeaderLen;
    r->control.limit = ubuf + len - tioga←TrailerLen;

    /* Other reader state initialization. */
    r->strLen = 0;
    r->str = NULL;
    /* First format is the null format. */
    r->nFormats = 1;
    r->formats[0] = NULL;
    /* First look is the empty look. */
    r->nLooks = 1;
    r->looks[0] = 0;
    /* First property is NIL. */
    r->nProps = 1;
    r->props[0] = NULL;
    /* Preload system atoms. */
    tread←AddProp(r, "prefix");
    tread←AddProp(r, "postfix");
    return 0;
}

static int CheckID(pp, id)
    unsigned char **pp;
    unsigned char *id;
{
    unsigned char *s = *pp;
    int ok = TRUE;
    int i;

    for (i = 0; i < tioga←IDLen; ++i) {
	if (*s++ != *id++)
	    ok = FALSE;
    }
    *pp = s;
    return ok ? 0 : -1;
}

/* Get next opcode from file. */
enum tioga←ControlOp tread←GetOp(r)
    struct tread←Reader *r;
{
    if (r->control.next < r->control.limit)
	return (enum tioga←ControlOp) *r->control.next++;
    else
	return endOfFile;
}

static int EnsureStrLen(r, len)
    struct tread←Reader *r;
    long len;
{
    if (r->strLen == 0) {
	r->strLen = len;
	r->str = (unsigned char *) malloc(len);
    }
    else if (len > r->strLen) {
	r->strLen = len;
	r->str = (unsigned char *) realloc(r->str, r->strLen);
    }
    if (r->str == NULL) {
	tioga←Error("Out of memory.\n");
	return -1;
    }
    return 0;
}

/* Get text from control stream. */
tread←GetStr(r)
    struct tread←Reader *r;
{
    long len = *r->control.next++;

    return tread←SGetRope(r, &r->control, len);
}

/* Get text from a specific stream. */
tread←SGetRope(r, s, len)
    struct tread←Reader *r;
    struct tread←Stream *s;
    long len;
{
    long i;

    if (EnsureStrLen(r, len + 1) < 0)
	return -1;
    for (i = 0; i < len; ++i) {
	if (s->next >= s->limit) {
	    tioga←Error("Rope too long.\n");
	    return -1;
	}
	r->str[i] = *s->next++;
    }
    r->str[len] = 0;
    return 0;
}

/* Add a new format, and return its index. */
int tread←AddFormat(r, format)
    struct tread←Reader *r;
    char *format;
{
    int i;

    /* Index zero reserved for null format. */
    if (format == NULL || format[0] == 0)
	return 0;
    for (i = 1; i < r->nFormats; ++i) {
	if (strcmp(r->formats[i], format) == 0)
	    return i;
    }
    if (r->nFormats < tioga←NumFormats) {
	r->formats[r->nFormats] = strsav(format);
	++r->nFormats;
	return r->nFormats - 1;
    }
    else {
	tioga←Error("Too many formats.\n");
	return 0;
    }
}

/* Add a new looks vector, and return its index. */
int tread←AddLooks(r, l)
    struct tread←Reader *r;
    long l;
{
    int i;

    /* Index zero reserved for empty looks. */
    if (l == 0)
	return 0;
    for (i = 1; i < r->nLooks; ++i) {
	if (r->looks[i] == l)
	    return i;
    }
    if (r->nLooks < tioga←NumLooks) {
	r->looks[r->nLooks] = l;
	++r->nLooks;
	return r->nLooks - 1;
    }
    else {
	tioga←Error("Too many looks.\n");
	return 0;
    }
}

/* Add a new property name, and return its index. */
int tread←AddProp(r, propName)
    struct tread←Reader *r;
    char *propName;
{
    int i;
    char *p;

    /* Index zero reserved for NIL. */
    if (propName == NULL || propName[0] == 0)
	return 0;
    for (i = 1; i < r->nProps; ++i) {
	if (strcmp(r->props[i], propName) == 0)
	    return i;
    }
    if (r->nProps < tioga←NumProps) {
	r->props[r->nProps] = strsav(propName);
	for (p = r->props[r->nProps]; *p != 0; ++p) {
	    /* Can't use tolower due to ATK problems. */
	    if ('A' <= *p && *p <= 'Z')
		*p = *p - 'A' + 'a';
	}
	++r->nProps;
	return r->nProps - 1;
    }
    else {
	tioga←Error("Too many properties..\n");
	return 0;
    }
}

/* Get a byte from the control stream. */
int tread←GetByte(r)
    struct tread←Reader *r;
{
    if (r->control.next < r->control.limit)
	return *r->control.next++;
    return 0;
}

/* Get an integer from the control stream. */
long tread←GetInt(r)
    struct tread←Reader *r;
{
    long result = 0;
    int nBits = 0;

    while (*r->control.next & 0x80) {
	result |= (*r->control.next++ & 0x7f) << nBits;
	nBits += 7;
    }
    result |= (*r->control.next++ & 0x7f) << nBits;
    return result;
}

static long GetLength(pp)
    unsigned char **pp;
{
    unsigned char *s = *pp;
    long result = 0;

    result |= *s++ << 8;
    result |= *s++;
    result |= *s++ << 24;
    result |= *s++ << 16;
    *pp = s;
    return result;
}

/* Allocate a copy of s and return it. */
char *strsav(s)
    char *s;
{
    char *p = malloc(strlen(s)+1);
    if (p == NULL)
	return NULL;
    strcpy(p, s);
    return p;
}