/* $Id: b64.c,v 1.4 2015/09/29 13:17:42 onoe Exp $ */

/*-
 * Copyright (c) 2005 Atsushi Onoe
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/types.h>

#include "b64.h"

int
b64_filbuf(struct b64_state *bs)
{
	int i, c, skip;
	const char *p, *ep;
	u_long code;

	p = bs->bs_ptr;
	ep = bs->bs_eptr;
	while (p < bs->bs_eptr && *p == '\n') {
		p++;
		if (p < ep && *p == '-') {
			bs->bs_eptr = p;
			return -1;
		}
	}
	if (p + 4 > ep) {
		bs->bs_ptr = (char *)ep;
		return -1;
	}
	for (i = 0, code = 0, skip = 0; i < 4; p++) {
		if (*p == '=') {
			i++;
			code <<= 6;
			skip += 6;
			continue;
		}
		c = rb64[(u_char)*p];
		if (c < 0) {
			/* skip rest of the line */
			while (p < ep) {
				if (*p++ == '\n')
					break;
			}
			bs->bs_ptr = p;
			return -1;
		}
		i++;
		code = (code << 6) | c;
	}
	bs->bs_ptr = p;
	bs->bs_code = code;
	bs->bs_hold = (24 - skip) / 8;
	return 0;
}

void
b64_set(struct b64_state *bs, const char *p, const char *ep)
{

	bs->bs_ptr = p;
	bs->bs_eptr = ep;
	bs->bs_hold = 0;
}

const char *
b64_getptr(struct b64_state *bs)
{
	const char *p;

	p = bs->bs_ptr;
	while (p < bs->bs_eptr && *p == '\n') {
		p++;
		if (p < bs->bs_eptr && *p == '-') {
			bs->bs_eptr = p;
			break;
		}
	}
	return bs->bs_ptr = p;
}

#if 0	/* implemented by macro */
int
b64_getc(struct b64_state *bs)
{
	int c;

	if (bs->bs_hold == 0) {
		if (b64_filbuf(bs))
			return -1;
	}
	c = (bs->bs_code >> 16) & 0xff;
	bs->bs_code <<= 8;
	bs->bs_hold--;
	return c;
}
#endif

int
b64_ngetc(struct b64_state *bs, char *p, int n)
{
	int i, c;

	for (i = 0; i < n; i++) {
		c = b64_getc(bs);
		if (c == -1)
			break;
		if (p)
			*p++ = c;
	}
	return i;
}
