/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 2002-2006 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                  David Korn <dgk@research.att.com>                   *
*                                                                      *
***********************************************************************/
#pragma prototyped
/*
 * pdp11 archive format method
 */

#include <ardirlib.h>
#include <swap.h>
#include <tm.h>

#define MAGIC		0177545
#define MAGIC_SIZE	2

typedef struct Header_s
{
	char		ar_name[14];
	unsigned int_4	ar_date;		/* native representation*/
	char		ar_uid;			/* 	"		*/
	char		ar_gid;			/* 	"		*/
	int_2		ar_mode;		/* 	"		*/
	unsigned int_4	ar_size;		/* 	"		*/
} Header_t;

typedef struct State_s			/* method state			*/
{
	off_t		current;	/* current dirent offset	*/
	off_t		offset;		/* next dirent offset		*/
	int		swap;		/* swapget() op if necessary	*/
	Header_t	header;		/* current header		*/
	char		name[15];	/* ar_name with trailing '\0'	*/
} State_t;

/*
 * closef
 */

static int
pdpclose(Ardir_t* ar)
{
	State_t*	state;

	if (ar && (state = (State_t*)ar->data))
		free(state);
	return 0;
}

/*
 * openf
 */

static int
pdpopen(Ardir_t* ar, char* buf, size_t n)
{
	int		swap;
	State_t*	state;

	if (n <= MAGIC_SIZE)
		return -1;
	if (swapget(0, buf, MAGIC_SIZE) == MAGIC)
		swap = 0;
	else if (swapget(1, buf, MAGIC_SIZE) == MAGIC)
		swap = 3;
	else
		return -1;
	if (!(state = newof(0, State_t, 1, 0)))
		return -1;
	ar->data = (void*)state;
	state->swap = swap;
	state->offset = MAGIC_SIZE;
	ar->truncate = 14;
	return 0;
 nope:
	pdpclose(ar);
	return -1;
}

/*
 * nextf
 */

static Ardirent_t*
pdpnext(Ardir_t* ar)
{
	State_t*	state = (State_t*)ar->data;
	ssize_t		z;

	state->current = state->offset;
	if (lseek(ar->fd, state->offset, SEEK_SET) != state->offset)
	{
		ar->error = errno;
		return 0;
	}
	if (read(ar->fd, (char*)&state->header, sizeof(state->header)) != sizeof(state->header))
	{
		if ((z = read(ar->fd, (char*)&state->header, 1)) < 0)
			ar->error = errno;
		else if (z > 0)
			ar->error = EINVAL;
		return 0;
		
	}
	strncpy(ar->dirent.name = state->name, state->header.ar_name, sizeof(state->header.ar_name));
	ar->dirent.mtime = swapget(state->swap, (char*)&state->header.ar_date, sizeof(state->header.ar_date));
	ar->dirent.uid = swapget(state->swap, (char*)&state->header.ar_uid, sizeof(state->header.ar_uid));
	ar->dirent.gid = swapget(state->swap, (char*)&state->header.ar_gid, sizeof(state->header.ar_gid));
	ar->dirent.mode = swapget(state->swap, (char*)&state->header.ar_mode, sizeof(state->header.ar_mode));
	ar->dirent.offset = state->offset += sizeof(state->header);
	ar->dirent.size = swapget(state->swap, (char*)&state->header.ar_size, sizeof(state->header.ar_size));
	state->offset += ar->dirent.size + (ar->dirent.size & 01);
	return &ar->dirent;
}

/*
 * changef
 */

static int
pdpchange(Ardir_t* ar, Ardirent_t* ent)
{
	State_t*	state = (State_t*)ar->data;
	off_t		o;

	o = state->current + offsetof(Header_t, ar_date);
	if (lseek(ar->fd, o, SEEK_SET) != o)
	{
		ar->error = errno;
		return -1;
	}
	swapput(state->swap, (char*)&state->header.ar_date, sizeof(state->header.ar_date), (int_max)ent->mtime);
	if (write(ar->fd, &state->header.ar_date, sizeof(state->header.ar_date)) != sizeof(state->header.ar_date))
	{
		ar->error = errno;
		return -1;
	}
	return 0;
}

Ardirmeth_t ar_pdp11 =
{
	"pdp11",
	"pdp11 archive",
	pdpopen,
	pdpnext,
	pdpchange,
	0,
	0,
	pdpclose,
	ar_pdp11_next
};
