/*
  $NiH: stream_uuextract.c,v 1.8 2002/05/10 21:09:22 wiz Exp $

  stream_uuextract.c -- extrace uuencode 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 <stddef.h>

#include "header.h"
#include "stream.h"
#include "stream_types.h"
#include "util.h"

enum uu_state { UU_FULL, UU_SHORT, UU_EMPTY, UU_END, UU_POST, UU_PRE,
		UU_HEADER };

struct stream_uux {
    stream st;

    enum uu_state state;
    char buf[66];
};

static int uux_close(struct stream_uux *st);
static token *uux_get(struct stream_uux *st);



stream *
stream_uuextract_open(struct stream *source)
{
    struct stream_uux *this;

    this = (struct stream_uux *)stream_new(sizeof(struct stream_uux),
					   uux_get, uux_close, source);

    this->state = UU_FULL;

    return (stream *)this;
}



static int
uux_close(struct stream_uux *this)
{
    /* XXX: skip to EOF? */

    stream_free((stream *)this);

    return 0;
}



static token *
uux_get(struct stream_uux *this)
{
    token *t;
    int len, line_kept, nbytes;
    enum uu_state state, old_state;

    old_state = this->state;
    line_kept = 0;

    for (;;) {
	t = stream_get(this->st.source);

	switch (t->type) {
	case TOK_LINE:
	    len = strlen(t->line);

	    if (old_state == UU_HEADER)
		state = UU_HEADER;
	    else if (t->line[0] == 'M' && len == 61)
		state = UU_FULL;
	    else if (len == 1 && (t->line[0] == ' ' || t->line[0] == '`'))
		state = UU_EMPTY;
	    else if (strcmp(t->line, "end") == 0)
		state = UU_END;
	    else if (t->line[0] > ' ' && t->line[0] < 'M'
		     && (len-1+3)/4 == (t->line[0]-' '+2)/3)
		state = UU_SHORT;
	    else
		state = UU_POST;

	    switch (state) {
	    case UU_HEADER:
		break;

	    case UU_END:
		if (line_kept)
		    token_set(stream_enqueue((stream *)this),
			      TOK_LINE, this->buf);
		return TOKEN_EOF;

	    case UU_EMPTY:
		if (old_state == UU_EMPTY) {
		    token_set3(stream_enqueue((stream *)this), TOK_ERR,
			       TOK_ERR_WARNING,
			       "end expected after empty line");
		}
		break;

	    case UU_FULL:
		if (old_state != UU_FULL && old_state != UU_PRE) {
		    /* error: end or empty expected */
		    token_set3(stream_enqueue((stream *)this), TOK_ERR,
			       TOK_ERR_ERROR, "long line unexpected");
		}
		this->state = UU_FULL;
		return token_set(&this->st.tok, TOK_LINE, t->line+1);

	    case UU_SHORT:
		if (old_state != UU_FULL && old_state != UU_PRE) {
		    token_set3(stream_enqueue((stream *)this), TOK_ERR,
			       TOK_ERR_ERROR, "unexpected short line");
		}
		strcpy(this->buf, t->line+1);
		/* some encoders output complete quadruples; truncate them */
		nbytes = t->line[0] - ' ';
		this->buf[nbytes/3*4 + nbytes%3+1] = '\0';
		line_kept = 1;
		break;

	    case UU_POST:
		if (old_state == UU_PRE)
		    state = old_state;
		break;

	    default:
		break;
	    }
	    break;

	case TOK_EOA:
	    state = UU_HEADER;
	    break;

	case TOK_EOF:
	    if (line_kept)
		token_set(stream_enqueue((stream *)this), TOK_LINE, this->buf);
	    token_set3(stream_enqueue((stream *)this), TOK_ERR, TOK_ERR_ERROR,
		       "missing end in uu stream");
	    return TOKEN_EOF;

	case TOK_EOH:
	    state = UU_PRE;
	    break;

	default:
	    return t;
#if 0
	    token_set3(stream_enqueue((stream *)this), TOK_ERR, TOK_ERR_ERROR,
		       "unexpected token type");
	    if (old_state == UU_PRE || old_state == UU_HEADER)
		state = old_state;
	    else
		state = UU_POST;
	    break;
#endif
	}

	old_state = state;
    }
}


syntax highlighted by Code2HTML, v. 0.9.1