# include	<fcntl.h>
# include	<sccs.h>

SCCSID(@(#)timefix.c	8.2	1/18/85)

/*
** TIMEFIX -- patch binary program to correct for timezone changes.
**
**	Timefix is compiled with the ctime(III) variables:
**		daylight,
**		timezone,
**		tzname[]
**
**	Each file specified is examined to see if it contains
**	all of these variables. If it does then the current values
**	of those variables for that file are given. If the "-u" flag
**	is specified then the values are not overwritten.
**
**	The other flags can be used to override the values of the
**	variables; specifically:
**		-sxxx  --> timezone	(xxx converted from ascii to binary)
**		-dx    --> daylight	(x converted from ascii to binary)
**		-tXXXYYY -> tzname[0] = "XXX" and tzname[1] = "YYY"
*/

struct header
{
	int	magic;
	int	tsize;
	int	dsize;
	int	bss;
	int	ssize;
	int	start_ad;
	int	unused;
	int	reloc_flag;
};

struct sym
{
	char	symname[8];
	int	type;
	int	value;
};


/* these values are defined in the unix ctime() routine */
extern int	daylight;
extern int	timezone;
extern char	*tzname[];

int	Noupdate;

main(argc, argv)
int	argc;
char	*argv[];
{

	if (argc < 2)
	{
		printf("usage: timefix filename ...\n");
		exit (1);
	}

	argc--;
	argv++;

	if (checkflags(&argc, argv))
		exit (1);

	pr_values("your installation", timezone, tzname[0], tzname[1], daylight);

	while (argc--)
		newtime(*argv++);
}
/*
**  NEWTIME
*/

newtime(filename)
char	*filename;
{
	struct sym	timez, name_st, name_dy, dayflag;
	struct header	head;
	register int	i, fdes;
	int		adr, secs;
	char		std[4], dyl[4];

	if ((fdes = openheader(&head, filename)) < 0)
		return (-1);

	bmove("_timezon", timez.symname, 8);
	bmove("_tzname\0", name_st.symname, 8);
	bmove("_dayligh", dayflag.symname, 8);

	/* pick up addresses from symbol table */
	i = findsymbol(&head, fdes, &timez);
	i |= findsymbol(&head, fdes, &name_st);
	i |= findsymbol(&head, fdes, &dayflag);

	if (i)
	{
		printf("File %s does not need to be corrected\n", filename);
		close(fdes);
		return (-2);
	}

	/* form entries for pointer to "PST" and "PDT" */
	i = getvalue(&head, fdes, &name_st, &adr, 2);
	name_st.value += 2;
	i |= getvalue(&head, fdes, &name_st, &name_dy.value, 2);
	name_dy.type = name_st.type;
	name_st.value = adr;

	if (i)
	{
		printf("can't find pointers to timezone names in %s\n", filename);
		close(fdes);
		return (-3);
	}

	/* now print out current values */
	i = getvalue(&head, fdes, &timez, &secs, 2);
	i |= getvalue(&head, fdes, &name_st, std, 4);
	i |= getvalue(&head, fdes, &name_dy, dyl, 4);
	i |= getvalue(&head, fdes, &dayflag, &adr, 2);

	if (i)
	{
		printf("one or more symbols cannot be read from %s\n", filename);
		close(fdes);
		return (-4);
	}

	pr_values(filename, secs, std, dyl, adr);

	if (!Noupdate)
	{
		if (putvalue(&head, fdes, &timez, &timezone, 2)
			|| putvalue(&head, fdes, &name_st, tzname[0], 4)
			|| putvalue(&head, fdes, &name_dy, tzname[1], 4)
			|| putvalue(&head, fdes, &dayflag, &daylight, 2))
		{
			printf("cannot update %s with new values\n", filename);
			close(fdes);
			return (-2);
		}
		else
			printf("File %s updated.\n", filename);
	}


	close(fdes);

	return (0);
}
/*
**	Open the indicated file and read & verify header
*/

openheader(hd, filename)
struct header	*hd;
char		*filename;
{
	register int	fd, i;

	if ((fd = open(filename, O_RDWR)) < 0)
	{
		printf("can't open file %s\n", filename);
		return (-1);
	}

	if ((i = read(fd, hd, sizeof (*hd))) != sizeof (*hd))
	{
		printf("can't read in header\n");
		close(fd);
		return  (-2);
	}

	switch (hd->magic)
	{

	  case 0407:
	  case 0410:
	  case 0411:
		if (hd->ssize)
			break;
		printf("File %s does not have a symbol table.\n", filename);
		return (-4);

	  default:
		printf("%s not an object file\n", filename);
		return (-3);
	}

	return (fd);
}
/*
**	Seek to beginning of symbol table
*/

startsymbol(hd, fdx)
struct header	*hd;
int		fdx;
{
	register int		i, fd;
	register struct header	*h;
	long			offset;
	long			itol();

	h = hd;
	fd = fdx;

	/* seek past header */
	i = lseek(fd, 16L, 0);

	/* seek to start of symbol table */
	offset = itol(h->tsize);
	offset = offset + itol(h->dsize);
	if (h->reloc_flag == 0)
		offset += offset;

	i |= lseek(fd, offset, 1);

	if (i < 0)
	{
		printf("can't seek to symbol table\n");
		return (-1);
	}

	return (0);
}
/*
**	Locate symbol in symbol table and return sucess-failure
*/

findsymbol(hd, fd, s)
struct header	*hd;
int		fd;
struct sym	*s;
{
	register int	i, j;
	struct sym	next;

	if (startsymbol(hd, fd))
		return (-1);

	for (i = hd->ssize; i--; )
	{
		j = read(fd, &next, sizeof (next));

		if (j != sizeof (next))
		{
			if (j)
				printf("symbol table error %d,%d,%d\n", hd->ssize, i, j);
			return (-1);
		}

		if (bequal(next.symname, s->symname, sizeof (next.symname)))
		{
			s->type = next.type;
			s->value = next.value;
			return (0);
		}
	}

	return (1);
}
/*
**  GETVALUE
*/

getvalue(hd, fd, s, cp, len)
struct header	*hd;
int		fd;
struct sym	*s;
char		*cp;
int		len;
{
	register int	i;
	long		getaddr(), addr;

	addr = getaddr(hd, s);
	if (addr == -1)
		return (-1);

	if (lseek(fd, addr, 0) < 0)
		return (-1);

	if ((i = read(fd, cp, len)) != len)
		return (-1);

	return (0);
}
/*
**  PUTVALUE
*/


putvalue(hd, fd, s, loc, len)
struct header	*hd;
int		fd;
struct sym	*s;
char		*loc;
int		len;
{
	long		adr, getaddr();

	adr = getaddr(hd, s);
	if (adr == -1)
		return (-1);

	if (lseek(fd, adr, 0) < 0)
		return (-1);

	if (write(fd, loc, len) != len)
		return (-2);

	return (0);
}
/*
**  PR_VALUES
*/

pr_values(str, secs, std, dyl, flag)
char	*str;
int	secs;
char	*std;
char	*dyl;
int	flag;
{
	printf("\nCurrent values for %s are:\n\t# seconds past greenwich:  %d\n", str, secs);
	printf("\ttimezones:  %.4s and %.4s\n\tdaylight saving flag:  %d\n", std, dyl, flag);
}
/*
**  CHECKFLAGS
*/

checkflags(argc, argv)
int	*argc;
char	*argv[];
{
	register char	*cp, **nargv;
	register int	cnt;
	int		ret;

	ret = 0;
	cnt = *argc;
	nargv = argv;
	while (cnt--)
	{
		cp = *argv++;
		if (*cp == '-')
		{
			(*argc)--;
			cp++;
			switch (*cp++)
			{

			  case 's':
				timezone = atoi(cp);
				break;

			  case 't':
				bmove(cp, tzname[0], 3);
				bmove(cp+3, tzname[1], 3);
				break;

			  case 'd':
				daylight = atoi(cp);
				break;

			  case 'u':
				Noupdate++;
				break;

			  default:
				printf("bad flag %s\n", cp - 2);
				ret = -1;
			}
		}
		else
			*nargv++ = cp;
	}

	return (ret);
}


bmove(src, dst, len)
char	*src;
char	*dst;
int	len;
{
	register int	i;
	register char	*s, *d;

	s = src;
	d = dst;
	i = len;

	while (i--)
		*d++ = *s++;
}


bequal(s1, s2, len)
char	*s1;
char	*s2;
int	len;
{
	register int	i;
	register char	*s, *d;

	s = s1;
	d = s2;
	i = len;

	while (i--)
		if (*s++ != *d++)
			return (0);

	return (1);
}
/*
**  GETADDR
*/

long
getaddr(hd, s)
struct header	*hd;
struct sym	*s;
{
	long	addr;
	int	i;
	long	l;
	long	itol();

	addr = s->value + 16;	/* offset past header */

	switch (s->type)
	{

	  /* extern text segment */
	  case 042:
		return (addr);

	  /* data segment extern */
	  case 043:
		switch (hd->magic)
		{

		  case 0407:
			return (addr);

		  case 0410:
			/* subtract space between text and data */
			l = itol(hd->tsize) + 017777;
			l &= ~017777;

			return (addr - (l - itol(hd->tsize)));

		  case 0411:
			return (addr + itol(hd->tsize));

		}
	  default:
		printf("Invalid symbol type %o\n", s->type);
		return (-1);
	}
}


long itol(value)
int	value;
{
	long	ret;
	int	*ip;

	ret = value;
	ip = &ret;
	*ip = 0;

	return (ret);
}
