/*
$NiH: stream_yenc.c,v 1.8 2002/05/10 21:09:22 wiz Exp $
stream_yenc.c -- extract and decode yenc data
Copyright (C) 2002 Dieter Baron and Thomas Klausner
This file is part of cg, a program to assemble and decode binary Usenet
postings. The authors can be contacted at <nih@giga.or.at>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <ctype.h>
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include "crc.h"
#include "header.h"
#include "stream.h"
#include "stream_types.h"
#include "util.h"
#define YENC_BEGIN (_yenc_sym[0])
#define YENC_CRC32 (_yenc_sym[1])
#define YENC_END (_yenc_sym[2])
#define YENC_LINE (_yenc_sym[3])
#define YENC_NAME (_yenc_sym[4])
#define YENC_PART (_yenc_sym[5])
#define YENC_PCRC32 (_yenc_sym[6])
#define YENC_SIZE (_yenc_sym[7])
#define YENC_MAX 8
static const char *_yenc_str[YENC_MAX] = {
"begin",
"crc32",
"end",
"line",
"name",
"part",
"pcrc32",
"size",
};
static symbol _yenc_sym[YENC_MAX];
static int _yenc_inited = 0;
enum yenc_state {
Y_HEADER, /* reading header */
Y_PRE, /* body lines before =ybegin */
Y_BEGIN, /* just after =ybegin line */
Y_DATA, /* in data part */
Y_POST /* after =yend line */
};
struct stream_yenc {
stream st;
enum yenc_state state; /* state stream is in */
int part; /* current next part */
long size; /* file size, from header */
long pbegin, pend; /* beginning/ending position of this part */
long pos; /* position in file (amount decoded) */
unsigned long crc, pcrc; /* computed crc and part crc */
char *name; /* file name */
};
static int yenc_close(struct stream_yenc *st);
static token *yenc_get(struct stream_yenc *st);
static unsigned long _yenc_get_hex(char *s);
static int _yenc_get_int(char *s);
static unsigned long _yenc_get_long(char *s);
static void _yenc_handle_begin(struct stream_yenc *this, char *ybegin,
int firstp);
static void _yenc_handle_end(struct stream_yenc *this, char *line);
static void _yenc_handle_part(struct stream_yenc *this, char *line);
static void _yenc_init(void);
static symbol _yenc_parse(char **linep);
stream *
stream_yenc_open(struct stream *source, char *ybegin)
{
struct stream_yenc *this;
this = (struct stream_yenc *)stream_new(sizeof(struct stream_yenc),
yenc_get, yenc_close, source);
this->state = Y_BEGIN;
if (!_yenc_inited)
_yenc_init();
_yenc_handle_begin(this, ybegin, 1);
return (stream *)this;
}
static int
yenc_close(struct stream_yenc *this)
{
/* XXX: skip to EOF? */
stream_free((stream *)this);
return 0;
}
static token *
yenc_get(struct stream_yenc *this)
{
token *t;
enum yenc_state state, old_state;
int i;
char *p;
old_state = this->state;
for (;;) {
t = stream_get(this->st.source);
switch (t->type) {
case TOK_LINE:
if (old_state == Y_HEADER)
break;
else if (strncmp(t->line, "=ybegin ", 8) == 0) {
if (old_state != Y_PRE) {
token_set3(stream_enqueue((stream *)this), TOK_ERR,
TOK_ERR_ERROR, "unexpected =ybegin line");
break;
}
_yenc_handle_begin(this, t->line, 0);
state = Y_BEGIN;
}
else if (strncmp(t->line, "=ypart ", 7) == 0) {
if (old_state != Y_BEGIN) {
token_set3(stream_enqueue((stream *)this), TOK_ERR,
TOK_ERR_ERROR, "unexpected =ypart line");
break;
}
_yenc_handle_part(this, t->line);
state = Y_DATA;
}
else if (strncmp(t->line, "=yend ", 6) == 0) {
if (old_state != Y_DATA && old_state != Y_BEGIN) {
token_set3(stream_enqueue((stream *)this), TOK_ERR,
TOK_ERR_ERROR, "unexpected =yend line");
break;
}
_yenc_handle_end(this, t->line);
state = Y_POST;
if (this->pos == this->size)
return TOKEN_EOF;
}
else {
switch (old_state) {
case Y_BEGIN:
case Y_DATA:
i=0;
p = t->line;
while (*p) {
if (*p == '=') {
if (*p++ == 0) {
token_set3(stream_enqueue((stream *)this),
TOK_ERR, TOK_ERR_ERROR,
"escape char = "
"at end of yenc line");
break;
}
*p -= 64;
}
t->line[i++] = (*p-42)&0xff;
p++;
}
this->state = Y_DATA;
this->pos += i;
this->crc = crc_update(this->crc,
(unsigned char *)t->line, i);
this->pcrc = crc_update(this->pcrc,
(unsigned char *)t->line, i);
return token_set3(&this->st.tok, TOK_DATA, i, t->line);
default:
break;
}
break;
}
break;
case TOK_EOA:
if (old_state != Y_POST)
token_set3(stream_enqueue((stream *)this), TOK_ERR,
TOK_ERR_ERROR, "missing =yend in yenc stream");
state = Y_HEADER;
break;
case TOK_EOF:
token_set3(stream_enqueue((stream *)this), TOK_ERR,
TOK_ERR_ERROR, "missing =yend in yenc stream");
return TOKEN_EOF;
case TOK_EOH:
state = Y_PRE;
break;
default:
return t;
}
old_state = state;
}
}
static unsigned long
_yenc_get_hex(char *s)
{
unsigned long l;
char *p;
errno = 0;
l = strtoul(s, &p, 16);
if (errno == ERANGE || (*p & !isspace(*p))) {
/* XXX: error handling */
}
return l;
}
static int
_yenc_get_int(char *s)
{
int i;
char *p;
errno = 0;
i = strtol(s, &p, 10);
if (errno == ERANGE || (*p & !isspace(*p))) {
/* XXX: error handling */
}
return i;
}
static unsigned long
_yenc_get_long(char *s)
{
unsigned long l;
char *p;
errno = 0;
l = strtol(s, &p, 10);
if (errno == ERANGE || (*p & !isspace(*p))) {
/* XXX: error handling */
}
return l;
}
static void
_yenc_handle_begin(struct stream_yenc *this, char *ybegin, int firstp)
{
symbol s;
char *p;
unsigned long size;
int i;
if (firstp) {
this->part = 0;
this->crc = crc_update(0, NULL, 0);
this->size = -1;
this->pos = 0;
this->name = NULL;
}
this->part++;
this->pbegin = this->pos;
this->pend = -1;
this->pcrc = crc_update(0, NULL, 0);
do {
ybegin = strchr(ybegin, ' ');
s = _yenc_parse(&ybegin);
if (s == YENC_PART) {
if ((i=_yenc_get_int(ybegin)) != this->part) {
token_printf3(stream_enqueue((stream *)this), TOK_ERR,
TOK_ERR_ERROR, "unexpected yEnc part, "
"expected %d, got %d", this->part, i);
}
}
else if (s == YENC_SIZE) {
size = _yenc_get_long(ybegin);
if (firstp)
this->size = size;
else if (this->size != size) {
token_printf3(stream_enqueue((stream *)this), TOK_ERR,
TOK_ERR_ERROR, "yEnc: size mismatch between "
"parts, expected %ld, got %ld", this->size,
size);
}
}
else if (s == YENC_NAME) {
ybegin+=strspn(ybegin, "\" \t");
p = ybegin+strlen(ybegin)-1;
while (strchr("\" \t", *p))
--p;
p[1] = '\0';
if (firstp) {
this->name = xstrdup(ybegin);
token_set(stream_enqueue((stream *)this), TOK_FNAME, ybegin);
}
else {
if (strcmp(this->name, ybegin) != 0) {
token_set3(stream_enqueue((stream *)this), TOK_ERR,
TOK_ERR_ERROR,
"yEnc: file name mismatch between parts");
}
}
break;
}
} while (s);
}
static void
_yenc_handle_end(struct stream_yenc *this, char *line)
{
symbol s;
unsigned long size, partcrc;
int i;
do {
line = strchr(line, ' ');
s = _yenc_parse(&line);
if (s == YENC_PART) {
if ((i=_yenc_get_int(line)) != this->part) {
token_printf3(stream_enqueue((stream *)this), TOK_ERR,
TOK_ERR_ERROR, "unexpected yEnc part, "
"expected %d, got %d", this->part, i);
}
}
else if (s == YENC_SIZE) {
size = _yenc_get_long(line);
if (this->pbegin+size != this->pos) {
token_printf3(stream_enqueue((stream *)this), TOK_ERR,
TOK_ERR_ERROR, "yEnc: part size wrong, "
"expected %ld, got %ld", this->pos-this->pbegin,
size);
}
}
else if (s == YENC_PCRC32) {
partcrc = _yenc_get_hex(line);
if (this->pcrc != partcrc) {
token_printf3(stream_enqueue((stream *)this), TOK_ERR,
TOK_ERR_ERROR, "yEnc: part crc wrong, "
"expected %08lx, got %08lx", this->pcrc,
partcrc);
}
}
else if (s == YENC_CRC32) {
partcrc=_yenc_get_hex(line);
if (this->crc != partcrc) {
token_printf3(stream_enqueue((stream *)this), TOK_ERR,
TOK_ERR_ERROR, "yEnc: file crc wrong, "
"expected %08lx, got %08lx", this->crc,
partcrc);
}
}
} while (s);
if (this->pend != -1 && this->pos != this->pend) {
token_printf3(stream_enqueue((stream *)this), TOK_ERR, TOK_ERR_ERROR,
"yEnc: part end wrong, expected %ld, got %ld",
this->pend, this->pos);
}
}
static void
_yenc_handle_part(struct stream_yenc *this, char *line)
{
symbol s;
long size;
do {
line = strchr(line, ' ');
s = _yenc_parse(&line);
if (s == YENC_BEGIN) {
size = _yenc_get_int(line)-1;
if (this->pbegin != size) {
token_printf3(stream_enqueue((stream *)this), TOK_ERR,
TOK_ERR_ERROR, "yEnc part begin wrong, "
"expected %ld, got %ld", this->pbegin, size);
}
}
else if (s == YENC_END)
this->pend = _yenc_get_long(line);
} while (s);
}
static void
_yenc_init(void)
{
int i;
for (i=0; i<YENC_MAX; i++)
_yenc_sym[i] = intern((char *)_yenc_str[i]);
_yenc_inited = 1;
}
static symbol
_yenc_parse(char **linep)
{
char *p, *q;
q = *linep;
if (q == NULL)
return NULL;
q += strspn(q, " \t");
if ((p=strchr(q, '=')) == NULL)
return NULL;
*p = '\0';
*linep = p+1;
return intern(q);
}
syntax highlighted by Code2HTML, v. 0.9.1