/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 1989-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>                   *
*                   Eduardo Krell <ekrell@adexus.cl>                   *
*                                                                      *
***********************************************************************/
#pragma prototyped

/*
 * 3d mount map support
 */

#include "3d.h"

/*
 * visit a mapping and compute its size or copy mapping
 */

int
mapget(Map_t* map, char* buf, int flags)
{
	register char*	p;
	register int	z;
	register char*	v;
	register int	n;
#if FS
	Mount_t*	mp = 0;
#endif

	NoP(flags);
	if (!(n = map->keysize))
		n = strlen(map->key);
	switch (z = map->valsize & T_SIZE)
	{
#if FS
	case T_MOUNT:
		mp = (Mount_t*)map->val;
		if (state.visit.fs && state.visit.fs != mp->fs)
			return 0;
		v = mp->fs->special;
		z = mp->fs->specialsize;
		break;
#endif
	case 0:
		z = strlen(map->val);
		/*FALLTHROUGH*/
	default:
		v = map->val;
		break;
	}
	if (p = buf)
	{
		strncpy(p, map->key, n);
		p += n;
		*p++ = ' ';
		if (
#if FS
			mp ||
#endif
			state.visit.prefix)
		{
			*p++ = '/';
			*p++ = '#';
#if FS
			if (!mp)
#endif
			{
				p = strcopy(p, state.visit.prefix);
				*p++ = '/';
				if (*v == '/')
					*p++ = '#';
			}
		}
		strncpy(p, v, z);
		p += z;
#if FS
		if (mp)
		{
			if (mp->channel)
				p += sfsprintf(p, 0, "/channel=%d", MSG_CHANNEL_SYS(mp->channel));
			p += getattr(mp->attr, p);
			if (mp->physical)
				p += sfsprintf(p, 0, "%-*s", mp->physicalsize ? mp->physicalsize : strlen(mp->physical), mp->physical);
		}
#endif
		*p++ = ' ';
		n = p - buf;
	}
	else
		n += z + state.visit.prelen
#if FS
		+ (mp ? ((mp->channel ? 16 : 0) + (mp->physical ? (mp->physicalsize ? mp->physicalsize : strlen(mp->physical)) : 0) + getattr(mp->attr, NiL)) : 0)
#endif
		+ 6;
	return n;
}

/*
 * dump table mappings into buf
 * the mapping size is returned
 * if tab==0 then all 3d mappings are processed
 * if buf==0 then the size is just computed
 */

int
mapdump(Table_t* tab, char* buf, int flags)
{
	register Fs_t*		fs;
	register int		n;
	register int		sum = 0;
	register char*		p = buf;

	if (flags & (MAP_EXEC|MAP_INIT))
	{
		if (state.kernel)
		{
			if (p)
				*p = 0;
			return 1;
		}
		if (p)
		{
			n = sfsprintf(p, 0, "%s", TABLE_PREFIX);
			p += n;
		}
		else
			n = sizeof(TABLE_PREFIX) - 1;
		sum += n;
	}
	if (!tab)
	{
		for (fs = state.fs; fs < state.fs + elementsof(state.fs); fs++)
			if (fs->get)
			{
				n = (*fs->get)(fs, p, NiL, flags);
				if (p)
					p += n;
				sum += n;
			}
	}
	else
	{
		n = iterate(tab, mapget, p, flags);
		if (p)
			p += n;
		sum += n;
	}
	if (p)
	{
		if (p > buf)
		{
			if (!(flags & MAP_INIT))
				p--;
			else if ((p - buf) == (sizeof(TABLE_PREFIX) - 1))
				p = buf;
			else
				*(p - 1) = '\n';
		}
		*p = 0;
		sum = p - buf;
	}
	return sum;
}

#if LICENSED

/*
 * check fs license
 */

static int
licensed(register Fs_t* fs)
{
	if ((fs->flags & (FS_INTERNAL|FS_LICENSED)) == FS_INTERNAL)
	{
		if (!(fs->flags & FS_VALIDATED))
		{
			fs->flags |= FS_VALIDATED;
			if (!*state.license)
			{
				Pathcheck_t	pc;

				if (pathcheck(PATHCHECK, error_info.id, &pc))
					*state.license = LICENSE_NONE;
				else if (!pc.feature)
					*state.license = LICENSE_ALL;
				else
					sfsprintf(state.license, sizeof(state.license), "%c%s%c", LICENSE_SEP, pc.feature, LICENSE_SEP);
				message((-2, "license: feature=%s", state.license));
			}
			if (*state.license == LICENSE_ALL)
				fs->flags |= FS_LICENSED;
			else
			{
				char	buf[sizeof(fs->special) + 4];

				sfsprintf(buf, sizeof(buf), "*%c%s%c*", LICENSE_SEP, fs->special, LICENSE_SEP);
				message((-1, "mount: license=%s pattern=%s", state.license, buf));
				if (strmatch(state.license, buf))
					fs->flags |= FS_LICENSED;
			}
			if (!(fs->flags & FS_LICENSED))
				error(2, "%s service not licensed", fs->special);
		}
		if (!(fs->flags & FS_LICENSED))
			return 0;
	}
	return 1;
}

#else

#define licensed(p)	(1)

#endif

#if FS

/*
 * copy attributes into table buf
 */

int
getattr(const char* attr, char* buf)
{
	register char*	a = (char*)attr;
	register char*	b = buf;
	register int	c;

	if (b)
	{
		while (c = *b++ = *a++)
			if (c == ' ')
				*(b - 1) = '/';
		return b - buf - 1;
	}
	return strlen(a);
}

/*
 * add/delete attributes
 * attributes stored as
 *
 *	(<space>name=value)*
 */

const char*
setattr(char* attr, const char* op, register const char* oe)
{
	register const char*	s;
	register const char*	v;
	register char*		a;
	register char*		x;
	register int		n;
	int			k;
	int			m;

	if (s = op)
	{
		for (;;)
		{
			if (s >= oe || *s == '/')
				return op;
			if (*s++ == '=')
			{
				v = s - 1;
				n = v - op;
				while (s < oe && *s != '/')
					s++;
				k = s - v - 1;
				m = s - op;
				a = attr;
				while (*a)
				{
					a++;
					if (strneq(op, a, n) && a[n] == '=')
					{
						if (!k || strncmp(op, a, m) || a[m] && a[m] != ' ')
						{
							if (!(x = strchr(a, ' ')))
							{
								if (a > attr)
									a--;
								*a = 0;
							}
							else
								while (*a++ = *++x);
						}
						else
							k = 0;
						break;
					}
					if (!(a = strchr(a, ' ')))
						break;
				}
				if (k && ((k = strlen(attr)) + m) < (ATTR_MAX - 1))
				{
					a = attr + k;
					*a++ = ' ';
					strncpy(a, op, m);
					a[m] = 0;
				}
				if (s++ >= oe)
					return 0;
				op = s;
			}
		}
	}
	return op;
}

#endif

/*
 * handle `arg /#<op>'
 */

static int
special(register const char* op, int opsize, const char* arg, int argsize)
{
	register const char*	oe;
	register Fs_t*		fs;
	const char*		ov;
	const char*		org;
	Map_t*			map;
	int			arglen;
	unsigned long		x;
#if FS
	register Mount_t*	mp;
#endif

	if (!(arglen = argsize))
		arglen = strlen(arg);
	oe = op + (opsize ? opsize : strlen(op));
	if (!(x = getkey(op, oe, 0)))
	{
		if (arglen)
		{
			errno = ENODEV;
			return -1;
		}
		for (fs = state.fs; fs < state.fs + elementsof(state.fs); fs++)
			if (fs->set)
				(*fs->set)(fs, state.null, 0, state.null, 0);
		return 0;
	}
	if (state.key.invert)
	{
		errno = ENODEV;
		return -1;
	}
	org = arg;
	if (!argsize && *arg)
	{
		if (!pathreal(arg, P_PATHONLY|P_ABSOLUTE|P_NOSLASH, NiL))
			return -1;
		arg = (const char*)state.path.name;
	}
	ov = (char*)state.key.next;
	for (fs = state.fs; fs < state.fs + elementsof(state.fs); fs++)
		if (!x || x == fs->key)
	{
		if (!licensed(fs))
			return 0;
#if FS
		if (fs->flags & FS_FS)
		{
			if (map = search(&state.vmount, arg, arglen, NiL, op < oe ? 0 : T_DELETE))
			{
				mp = (Mount_t*)map->val;
				if (mp->fs != fs)
				{
					errno = EEXIST;
					return -1;
				}
				message((-2, "mount: old fs=%s map=%-*s arg=%-*s op=%-*s", fs->special, map->keysize, map->key, arglen, arg, ov ? (oe - ov) : 6, ov));
				if (setattr(mp->attr, ov, oe))
				{
					errno = EEXIST;
					return -1;
				}
			}
			else if (op < oe)
			{
				if (arglen)
				{
					for (mp = state.mount;; mp++)
					{
						if (mp >= state.mount + elementsof(state.mount))
							return -1;
						if (!mp->fs)
							break;
					}
					if (map = search(&state.vmount, arg, arglen, (char*)mp, argsize ? T_MOUNT : (T_MOUNT|T_ALLOCATE)))
					{
						message((-2, "mount: new fs=%s map=%-*s arg=%-*s op=%-*s", fs->special, map->keysize, map->key, arglen, arg, ov ? (oe - ov) : 6, ov));
						fs->flags |= FS_REFERENCED;
						if (!(fs->flags & FS_NAME) && !state.cache)
							state.cache = 1;
						mp->fs = fs;
						mp->logical = map->key;
						mp->logicalsize = map->keysize;
						if (ov)
						{
							if ((oe - ov) > 8 && strneq(ov, "channel=", 8))
							{
								mp->channel = strtol(ov + 8, (char**)&ov, 0);
								if (++ov >= oe)
									ov = 0;
							}
							if (ov = setattr(mp->attr, ov, oe))
							{
								if (opsize)
								{
									mp->physical = (char*)ov - 1;
									mp->physicalsize = oe - mp->physical;
								}
								else
								{
									mp->physical = strcpy(newof(0, char, strlen(ov), 2), ov - 1);
									mp->physicalsize = 0;
								}
							}
						}
					}
				}
				else if (setattr(fs->attr, ov, oe))
				{
					errno = EEXIST;
					return -1;
				}
			}
		}
		else
#endif
		if (fs->set)
		{
			if (ov)
			{
				if ((*fs->set)(fs, (fs->flags & FS_RAW) ? org : arg, argsize, ov, oe - ov))
					return -1;
			}
			else if (arglen)
			{
				if ((*fs->set)(fs, (fs->flags & FS_RAW) ? org : arg, argsize, state.null, 0))
					return -1;
			}
			else if ((*fs->set)(fs, state.null, 0, state.null, 0))
				return -1;
		}
		if (op < oe)
			break;
	}
	if (op < oe && fs >= state.fs + elementsof(state.fs))
	{
		errno = ENODEV;
		return -1;
	}
	return 0;
}

/*
 * set a single 3d table mapping
 * size==0 for volatile args
 * from="-" maps to from=""
 * to="-" maps to to=""
 */

int
mapset(Table_t* tab, const char* from, int fromsize, register const char* to, int tosize)
{
	register Map_t*	old;
	register int	n;
	int		x;
	char		buf[PATH_MAX + 1];

	if (state.safe && !(state.test & 0100))
	{
		errno = EPERM;
		return -1;
	}
	if (!fromsize && (!from || !from[0] || from[0] == '-' && !from[1]) || fromsize == 1 && from[0] == '-')
	{
		from = state.null;
		fromsize = 0;
	}
	if (!tosize && (!to || !to[0] || to[0] == '-' && !to[1]) || tosize == 1 && to[0] == '-')
	{
		to = state.null;
		tosize = 0;
	}
	message((-2, "mount: %-*s %-*s", from == state.null ? 1 : fromsize ? fromsize : strlen(from), from == state.null ? "-" : from, to == state.null ? 1 : tosize ? tosize : strlen(to), to == state.null ? "-" : to));
	if ((!tosize || tosize >= 2) && to[0] == '/' && to[1] == '#')
	{
		to += 2;
		if (tosize)
			tosize -= 2;
		return special(to, tosize, from, fromsize);
	}
	if (!*from)
	{
		if (!*to)
			while (tab->table->key)
				search(tab, tab->table->key, tab->table->keysize, NiL, T_DELETE);
		return 0;
	}
	if (fromsize)
	{
		if (tosize || !to[0])
		{
			/*
			 * no malloc during initialization
			 * so we have to believe from and to here
			 */

			return search(tab, from, fromsize, to, tosize) ? 0 : -1;
		}
		n = *(from + fromsize);
		*((char*)from + fromsize) = 0;
	}
	if (!licensed(&state.fs[tab == &state.vpath ? FS_view : tab == &state.vmap ? FS_map : FS_safe]))
		return 0;
	if (x = tab != &state.vintercept)
	{
		if (!pathreal(from, P_PATHONLY|P_ABSOLUTE|P_NOSLASH, NiL))
			return -1;
		if (fromsize)
		{
			*((char*)from + fromsize) = n;
			fromsize = 0;
		}
		from = (const char*)state.path.name;
	}
	if (!fromsize)
		fromsize = strlen(from);
	old = search(tab, from, fromsize, NiL, *to ? 0 : T_DELETE);
	if (!*to)
	{
		search(&state.vmount, from, fromsize, NiL, T_DELETE);
		return 0;
	}
	if (tab == &state.vmap)
	{
		if (old)
		{
			if (!(old->valsize & T_SIZE))
				free(old->val);
			if ((old->valsize = tosize) & T_SIZE)
				old->val = (char*)to;
			else
				old->val = strcpy(newof(0, char, strlen(to), 1), to);
		}
		else
			search(tab, from, fromsize, to, tosize|T_ALLOCATE);
	}
	else if (old)
	{
		/*
		 * ok if previous mapping matched
		 */

		if (x && !(to = pathreal(to, P_PATHONLY|P_ABSOLUTE|P_NOSLASH, NiL)))
			return -1;
		if ((n = T_VALSIZE(old)) == (tosize ? tosize : strlen(to)) && strneq(old->val, to, n))
			return 0;

		/*
		 * already have different mapping
		 */

		errno = EEXIST;
		return -1;
	}
	else
	{
		strncpy(buf, from, PATH_MAX);
		buf[PATH_MAX] = 0;
		if (x)
		{
			if (!pathreal(to, P_PATHONLY|P_ABSOLUTE|P_NOSLASH, NiL))
				return -1;
			to = (const char*)state.path.name;
		}
		if (tab == &state.vsafe || !streq(buf, to))
			search(tab, buf, fromsize, to, T_ALLOCATE);
	}
	return 0;
}

/*
 * initialize mappings from space separated pairs in buf
 * if readonly!=0 then buf must not be changed after this call
 */

int
mapinit(const char* buf, int readonly)
{
	register char*	bp = (char*)buf;
	register char*	from;
	register char*	to;
	int		fromsize;
	int		tosize;

#if DEBUG_TABLE
	if (!error_info.trace)
		error_info.trace = -3;
	message((-1, "TABLE `%s'", bp));
#endif
	if (strneq(bp, TABLE_PREFIX, sizeof(TABLE_PREFIX) - 1))
		bp += sizeof(TABLE_PREFIX) - 1;
#if DEBUG_TABLE
	if (bp != buf)
		message((-1, "TABLE `%s'", bp));
#endif
	if (!readonly)
		fromsize = tosize = 0;
	while (*bp)
	{
		for (from = bp; *bp && *bp != ' '; bp++);
		if (readonly)
		{
			fromsize = bp - from;
			if (*bp)
				bp++;
		}
		else if (*bp)
			*bp++ = 0;
		for (to = bp; *bp && *bp != ' ' && *bp != '\n'; bp++);
		if (readonly)
			tosize = bp - to;
		else if (*bp)
			*bp++ = 0;
#if DEBUG_TABLE
		if (mapset(&state.vpath, from, fromsize, to, tosize))
		{
			if (readonly)
				message((-1, "TABLE %-*s -> %-*s FAILED", fromsize, from, tosize, to));
			else
				message((-1, "TABLE %s -> %s FAILED", from, to));
			return -1;
		}
		if (state.table.version != TABLE_VERSION)
		{
			message((-1, "TABLE state.table.version=%d != TABLE_VERSION=%d", state.table.version, TABLE_VERSION));
			return -1;
		}
#else
		if (mapset(&state.vpath, from, fromsize, to, tosize) || state.table.version != TABLE_VERSION)
			return -1;
#endif
		if (*bp)
			bp++;
	}
	return 0;
}
