1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1982-2010 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                  David Korn <dgk@research.att.com>                   *
18*                                                                      *
19***********************************************************************/
20#pragma prototyped
21/*
22 * David Korn
23 * AT&T Labs
24 *
25 */
26
27#include        "defs.h"
28
29static const char sh_opttype[] =
30"[-1c?\n@(#)$Id: type (AT&T Labs Research) 2008-07-01 $\n]"
31USAGE_LICENSE
32"[+NAME?\f?\f - set the type of variables to \b\f?\f\b]"
33"[+DESCRIPTION?\b\f?\f\b sets the type on each of the variables specified "
34	"by \aname\a to \b\f?\f\b. If \b=\b\avalue\a is specified, "
35	"the variable \aname\a is set to \avalue\a before the variable "
36	"is converted to \b\f?\f\b.]"
37"[+?If no \aname\as are specified then the names and values of all "
38	"variables of this type are written to standard output.]"
39"[+?\b\f?\f\b is built-in to the shell as a declaration command so that "
40	"field splitting and pathname expansion are not performed on "
41	"the arguments.  Tilde expansion occurs on \avalue\a.]"
42"[r?Enables readonly.  Once enabled, the value cannot be changed or unset.]"
43"[a]:?[type?Indexed array. Each \aname\a will converted to an index "
44	"array of type \b\f?\f\b.  If a variable already exists, the current "
45	"value will become index \b0\b.  If \b[\b\atype\a\b]]\b is "
46	"specified, each subscript is interpreted as a value of enumeration "
47	"type \atype\a.]"
48"[A?Associative array.  Each \aname\a will converted to an associate "
49        "array of type \b\f?\f\b.  If a variable already exists, the current "
50	"value will become subscript \b0\b.]"
51"[h]:[string?Used within a type definition to provide a help string  "
52	"for variable \aname\a.  Otherwise, it is ignored.]"
53"[S?Used with a type definition to indicate that the variable is shared by "
54	"each instance of the type.  When used inside a function defined "
55	"with the \bfunction\b reserved word, the specified variables "
56	"will have function static scope.  Otherwise, the variable is "
57	"unset prior to processing the assignment list.]"
58"[+DETAILS]\ftypes\f"
59"\n"
60"\n[name[=value]...]\n"
61"\n"
62"[+EXIT STATUS?]{"
63        "[+0?Successful completion.]"
64        "[+>0?An error occurred.]"
65"}"
66
67"[+SEE ALSO?\fother\f \breadonly\b(1), \btypeset\b(1)]"
68;
69
70typedef struct Namtype Namtype_t;
71typedef struct Namchld
72{
73	Namfun_t	fun;
74	Namtype_t 	*ptype;
75	Namtype_t 	*ttype;
76} Namchld_t;
77
78struct Namtype
79{
80	Namfun_t	fun;
81	Shell_t		*sh;
82	Namval_t	*np;
83	Namval_t	*parent;
84	Namval_t	*bp;
85	Namval_t	*cp;
86	char		*nodes;
87	char		*data;
88	Namchld_t	childfun;
89	int		numnodes;
90	char		**names;
91	size_t		dsize;
92	short		strsize;
93	unsigned short	ndisc;
94	unsigned short	current;
95	unsigned short	nref;
96};
97
98#if 0
99struct type
100{
101	Namtype_t	hdr;
102	unsigned short	ndisc;
103	unsigned short	current;
104	unsigned short	nref;
105};
106#endif
107
108typedef struct
109{
110	char		_cSfdouble_t;
111	Sfdouble_t	_dSfdouble_t;
112	char		_cdouble;
113	double		_ddouble;
114	char		_cfloat;
115	float		_dfloat;
116	char		_cSflong_t;
117	Sflong_t	_dSflong_t;
118	char		_clong;
119	long		_dlong;
120	char		_cshort;
121	short		_dshort;
122	char		_cpointer;
123	char		*_dpointer;
124} _Align_;
125
126#define alignof(t)	((char*)&((_Align_*)0)->_d##t-(char*)&((_Align_*)0)->_c##t)
127
128static void put_type(Namval_t*, const char*, int, Namfun_t*);
129static Namval_t* create_type(Namval_t*, const char*, int, Namfun_t*);
130static Namfun_t* clone_type(Namval_t*, Namval_t*, int, Namfun_t*);
131static Namval_t* next_type(Namval_t*, Dt_t*, Namfun_t*);
132
133static const Namdisc_t type_disc =
134{
135	sizeof(Namtype_t),
136	put_type,
137	0,
138	0,
139	0,
140	create_type,
141	clone_type,
142	0,
143	next_type,
144	0,
145#if 0
146	read_type
147#endif
148};
149
150static size_t datasize(Namval_t *np, size_t *offset)
151{
152	size_t s=0, a=0;
153	Namarr_t *ap;
154	if(nv_isattr(np,NV_INTEGER))
155	{
156		if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE)
157		{
158			if(nv_isattr(np, NV_LONG))
159			{
160				a = alignof(Sfdouble_t);
161				s = sizeof(Sfdouble_t);
162			}
163			else if(nv_isattr(np, NV_SHORT))
164			{
165				a = alignof(float);
166				s = sizeof(float);
167			}
168			else
169			{
170				a = alignof(double);
171				s = sizeof(double);
172			}
173		}
174		else
175		{
176			if(nv_isattr(np, NV_LONG))
177			{
178				a = alignof(Sflong_t);
179				s = sizeof(Sflong_t);
180			}
181			else if(nv_isattr(np, NV_SHORT))
182			{
183				a = alignof(short);
184				s = sizeof(short);
185			}
186			else
187			{
188				a = alignof(long);
189				s = sizeof(long);
190			}
191		}
192	}
193	else if(nv_isattr(np, NV_BINARY) || nv_isattr(np,NV_LJUST|NV_RJUST|NV_ZFILL))
194		s = nv_size(np);
195	else
196	{
197		a = alignof(pointer);
198		s = nv_size(np);
199	}
200	if(a>1 && offset)
201		*offset = a*((*offset +a-1)/a);
202	if(nv_isarray(np) && (ap = nv_arrayptr(np)))
203		s *= array_elem(ap);
204	return(s);
205}
206
207static char *name_chtype(Namval_t *np, Namfun_t *fp)
208{
209	Namchld_t	*pp = (Namchld_t*)fp;
210	char		*cp, *sub;
211	Namval_t	*tp = sh.last_table;
212	Namval_t	*nq = pp->ptype->np;
213	Namarr_t	*ap;
214	if(nv_isattr(np,NV_REF|NV_TAGGED)==(NV_REF|NV_TAGGED))
215		sh.last_table = 0;
216	cp = nv_name(nq);
217	if((ap = nv_arrayptr(nq)) && !(ap->nelem&ARRAY_UNDEF) && (sub= nv_getsub(nq)))
218		sfprintf(sh.strbuf,"%s[%s].%s",cp,sub,np->nvname);
219	else
220		sfprintf(sh.strbuf,"%s.%s",cp,np->nvname);
221	sh.last_table = tp;
222	return(sfstruse(sh.strbuf));
223}
224
225static void put_chtype(Namval_t* np, const char* val, int flag, Namfun_t* fp)
226{
227	if(!val && nv_isattr(np,NV_REF))
228		return;
229	nv_putv(np,val,flag,fp);
230	if(!val)
231	{
232		Namchld_t	*pp = (Namchld_t*)fp;
233		size_t		dsize=0,offset = (char*)np-(char*)pp->ptype;
234		Namval_t	*mp = (Namval_t*)((char*)pp->ttype+offset);
235		dsize = datasize(mp,&dsize);
236		if(mp->nvalue.cp >=  pp->ttype->data && mp->nvalue.cp < (char*)pp+pp->ttype->fun.dsize)
237		{
238			np->nvalue.cp = pp->ptype->data + (mp->nvalue.cp-pp->ptype->data);
239			memcpy((char*)np->nvalue.cp,mp->nvalue.cp,dsize);
240		}
241		else if(!nv_isarray(mp) && mp->nvalue.cp)
242		{
243			np->nvalue.cp = mp->nvalue.cp;
244			nv_onattr(np,NV_NOFREE);
245		}
246		np->nvsize = mp->nvsize;
247		np->nvflag = mp->nvflag&~NV_RDONLY;
248	}
249}
250
251static Namfun_t *clone_chtype(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
252{
253	if(flags&NV_NODISC)
254		return(0);
255	return(nv_clone_disc(fp,flags));
256}
257
258static const Namdisc_t chtype_disc =
259{
260	sizeof(Namchld_t),
261	put_chtype,
262	0,
263	0,
264	0,
265	0,
266	clone_chtype,
267	name_chtype
268};
269
270static Namval_t *findref(void *nodes, int n)
271{
272	Namval_t	*tp,*np = nv_namptr(nodes,n);
273	char		*name = np->nvname;
274	int		i=n, len= strrchr(name,'.')-name;
275	Namtype_t	*pp;
276	while(--i>0)
277	{
278		np = nv_namptr(nodes,i);
279		if(np->nvname[len]==0)
280		{
281			tp = nv_type(np);
282			pp = (Namtype_t*)nv_hasdisc(tp,&type_disc);
283			return(nv_namptr(pp->nodes,n-i-1));
284		}
285	}
286	return(0);
287}
288
289static int fixnode(Namtype_t *dp, Namtype_t *pp, int i, struct Namref *nrp,int flag)
290{
291	Namval_t	*nq = nv_namptr(dp->nodes,i);
292	Namfun_t	*fp;
293	if(fp=nv_hasdisc(nq,&chtype_disc))
294		nv_disc(nq, fp, NV_POP);
295	if(nv_isattr(nq,NV_REF))
296	{
297		nq->nvalue.nrp = nrp++;
298		nv_setsize(nq,0);
299		if(strchr(nq->nvname,'.'))
300			nq->nvalue.nrp->np = findref(dp->nodes,i);
301		else
302			nq->nvalue.nrp->np = nv_namptr(pp->childfun.ttype->nodes,i);
303		nq->nvalue.nrp->root = sh.last_root;
304		nq->nvalue.nrp->table = pp->np;
305		nq ->nvflag = NV_REF|NV_NOFREE|NV_MINIMAL;
306		return(1);
307	}
308	if(nq->nvalue.cp || nq->nvfun)
309	{
310		const char *data = nq->nvalue.cp;
311		if(nq->nvfun)
312		{
313			Namval_t *np = nv_namptr(pp->nodes,i);
314			if(nv_isarray(nq))
315				nq->nvalue.cp = 0;
316			nq->nvfun = 0;
317			if(nv_isarray(nq) && nv_type(np))
318				clone_all_disc(np,nq,flag&~NV_TYPE);
319			else
320				clone_all_disc(np,nq,flag);
321			if(fp)
322				nv_disc(np, fp, NV_LAST);
323		}
324#if 0
325		if(nq->nvalue.cp >=  pp->data && nq->nvalue.cp < (char*)pp +pp->fun.dsize)
326			nq->nvalue.cp = dp->data + (nq->nvalue.cp-pp->data);
327#else
328		if(data >=  pp->data && data < (char*)pp +pp->fun.dsize)
329			nq->nvalue.cp = dp->data + (data-pp->data);
330#endif
331		else if(!nq->nvfun && pp->childfun.ttype!=pp->childfun.ptype)
332		{
333			Namval_t *nr = nv_namptr( pp->childfun.ttype->nodes,i);
334			if(nr->nvalue.cp!=nq->nvalue.cp)
335			{
336				if(i=nv_size(nq))
337				{
338					const char *cp = nq->nvalue.cp;
339					nq->nvalue.cp = (char*)malloc(i);
340					memcpy((char*)nq->nvalue.cp,cp,i);
341				}
342				else
343					nq->nvalue.cp = strdup(nq->nvalue.cp);
344				nv_offattr(nq,NV_NOFREE);
345			}
346		}
347
348	}
349	if(fp)
350		nv_disc(nq, &dp->childfun.fun, NV_LAST);
351	return(0);
352}
353
354static Namfun_t *clone_type(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
355{
356	Namtype_t		*dp, *pp=(Namtype_t*)fp;
357	register int		i;
358	register Namval_t	*nq, *nr;
359	size_t			size = fp->dsize;
360	int			save, offset=staktell();
361	char			*cp;
362	Dt_t			*root = sh.last_root;
363	Namval_t		*last_table = sh.last_table;
364	struct Namref		*nrp = 0;
365	Namarr_t		*ap;
366	if(flags&NV_MOVE)
367	{
368		pp->np = mp;
369		pp->childfun.ptype = pp;
370		return(fp);
371	}
372	if(flags&NV_TYPE)
373		return(nv_clone_disc(fp,flags));
374	if(size==0 && (!fp->disc || (size=fp->disc->dsize)==0))
375		size = sizeof(Namfun_t);
376	dp = (Namtype_t*)malloc(size+pp->nref*sizeof(struct Namref));
377	if(pp->nref)
378	{
379		nrp = (struct Namref*)((char*)dp + size);
380		memset((void*)nrp,0,pp->nref*sizeof(struct Namref));
381	}
382	memcpy((void*)dp,(void*)pp,size);
383#if 0
384	dp->parent = nv_lastdict();
385#else
386	dp->parent = mp;
387#endif
388	dp->fun.nofree = (flags&NV_RDONLY?1:0);
389	dp->np = mp;
390	dp->childfun.ptype = dp;
391#if 0
392	dp->childfun.ttype = (Namtype_t*)nv_hasdisc(dp->fun.type,&type_disc);
393#endif
394	dp->nodes = (char*)(dp+1);
395	dp->data = (char*)dp + (pp->data - (char*)pp);
396	for(i=dp->numnodes; --i >= 0; )
397	{
398		nq = nv_namptr(dp->nodes,i);
399		if(fixnode(dp,pp,i,nrp,NV_TYPE))
400		{
401			nrp++;
402			nq = nq->nvalue.nrp->np;
403		}
404		if(nq->nvalue.cp || !nv_isvtree(nq) || nv_isattr(nq,NV_RDONLY))
405		{
406			/* see if default value has been overwritten */
407			if(!mp->nvname)
408				continue;
409			sh.last_table = last_table;
410			if(pp->strsize<0)
411				cp = nv_name(np);
412			else
413				cp = nv_name(mp);
414			stakputs(cp);
415			stakputc('.');
416			stakputs(nq->nvname);
417			stakputc(0);
418			root = nv_dict(mp);
419			save = fp->nofree;
420			fp->nofree = 1;
421			nr = nv_create(stakptr(offset),root,NV_VARNAME|NV_NOADD,fp);
422			fp->nofree = save;
423			stakseek(offset);
424			if(nr)
425			{
426				if(nv_isattr(nq,NV_RDONLY) && (nq->nvalue.cp || nv_isattr(nq,NV_INTEGER)))
427					errormsg(SH_DICT,ERROR_exit(1),e_readonly, nq->nvname);
428				if(nv_isref(nq))
429					nq = nv_refnode(nq);
430				if((size = datasize(nr,(size_t*)0)) && size==datasize(nq,(size_t*)0))
431					memcpy((char*)nq->nvalue.cp,nr->nvalue.cp,size);
432				else if(ap=nv_arrayptr(nr))
433				{
434					nv_putsub(nr,NIL(char*),ARRAY_SCAN|ARRAY_NOSCOPE);
435					do
436					{
437						if(array_assoc(ap))
438							cp = (char*)((*ap->fun)(nr,NIL(char*),NV_ANAME));
439						else
440							cp = nv_getsub(nr);
441						nv_putsub(nq,cp,ARRAY_ADD|ARRAY_NOSCOPE);
442						if(array_assoc(ap))
443						{
444							Namval_t *mp = (Namval_t*)((*ap->fun)(nr,NIL(char*),NV_ACURRENT));
445							Namval_t *mq = (Namval_t*)((*ap->fun)(nq,NIL(char*),NV_ACURRENT));
446							nv_clone(mp,mq,NV_MOVE);
447							ap->nelem--;
448							nv_delete(mp,ap->table,0);
449						}
450						else
451						{
452							cp = nv_getval(nr);
453							nv_putval(nq,cp,0);
454						}
455					}
456					while(nv_nextsub(nr));
457				}
458				else
459					nv_putval(nq,nv_getval(nr),NV_RDONLY);
460#if SHOPT_TYPEDEF
461				if(sh.mktype)
462					nv_addnode(nr,1);
463#endif /* SHOPT_TYPEDEF */
464				if(pp->strsize<0)
465					continue;
466				_nv_unset(nr,0);
467				if(!nv_isattr(nr,NV_MINIMAL))
468					nv_delete(nr,sh.last_root,0);
469			}
470			else if(nv_isattr(nq,NV_RDONLY) && !nq->nvalue.cp && !nv_isattr(nq,NV_INTEGER))
471				errormsg(SH_DICT,ERROR_exit(1),e_required,nq->nvname,nv_name(mp));
472		}
473	}
474	if(nv_isattr(mp,NV_BINARY))
475		mp->nvalue.cp = dp->data;
476	if(pp->strsize<0)
477		dp->strsize = -pp->strsize;
478	return(&dp->fun);
479}
480
481
482/*
483 * return Namval_t* corresponding to child <name> in <np>
484 */
485static Namval_t *create_type(Namval_t *np,const char *name,int flag,Namfun_t *fp)
486{
487	Namtype_t		*dp = (Namtype_t*)fp;
488	register const char	*cp=name;
489	register int		i=0,n;
490	Namval_t		*nq=0;
491	if(!name)
492		return(dp->parent);
493	while((n=*cp++) && n != '=' && n != '+' && n!='[');
494	n = (cp-1) -name;
495	if(dp->numnodes && dp->strsize<0)
496	{
497		char *base =  (char*)np-sizeof(Dtlink_t);
498		int m=strlen(np->nvname);
499		while((nq=nv_namptr(base,++i)) && memcmp(nq->nvname,np->nvname,m)==0)
500		{
501			if(nq->nvname[m]=='.' && memcmp(name,&nq->nvname[m+1],n)==0 && nq->nvname[m+n+1]==0)
502				goto found;
503		}
504		nq = 0;
505	}
506	else for(i=0; i < dp->numnodes; i++)
507	{
508		nq = nv_namptr(dp->nodes,i);
509		if((n==0||memcmp(name,nq->nvname,n)==0) && nq->nvname[n]==0)
510		{
511			while(nv_isref(nq))
512				nq = nq->nvalue.nrp->np;
513			goto found;
514		}
515	}
516	nq = 0;
517found:
518	if(nq)
519	{
520		fp->last = (char*)&name[n];
521		sh.last_table = dp->parent;
522	}
523	else
524	{
525		if(name[n]!='=') for(i=0; i < dp->ndisc; i++)
526		{
527			if((memcmp(name,dp->names[i],n)==0) && dp->names[i][n]==0)
528				return(nq);
529		}
530		errormsg(SH_DICT,ERROR_exit(1),e_notelem,n,name,nv_name(np));
531	}
532	return(nq);
533}
534
535static void put_type(Namval_t* np, const char* val, int flag, Namfun_t* fp)
536{
537	Namval_t	*nq;
538	if(val && (nq=nv_open(val,sh.var_tree,NV_VARNAME|NV_ARRAY|NV_NOADD|NV_NOFAIL)))
539	{
540		Namfun_t  *pp;
541		if((pp=nv_hasdisc(nq,fp->disc)) && pp->type==fp->type)
542
543		{
544			_nv_unset(np, flag);
545			nv_clone(nq,np,0);
546			return;
547		}
548	}
549	nv_putv(np,val,flag,fp);
550	if(!val)
551	{
552		Namtype_t	*dp = (Namtype_t*)fp;
553		Namval_t	*nq;
554		Namarr_t	*ap;
555		int		i;
556		if(nv_isarray(np) && (ap=nv_arrayptr(np)) && ap->nelem>0)
557			return;
558		for(i=0; i < dp->numnodes; i++)
559		{
560			nq = nv_namptr(dp->nodes,i);
561			if(ap=nv_arrayptr(nq))
562				ap->nelem |= ARRAY_UNDEF;
563			if(!nv_hasdisc(nq,&type_disc))
564				_nv_unset(nq,flag|NV_TYPE|nv_isattr(nq,NV_RDONLY));
565		}
566		nv_disc(np,fp,NV_POP);
567		if(!(fp->nofree&1))
568			free((void*)fp);
569	}
570}
571
572static Namval_t *next_type(register Namval_t* np, Dt_t *root,Namfun_t *fp)
573{
574	Namtype_t	*dp = (Namtype_t*)fp;
575	if(!root)
576	{
577		Namarr_t	*ap = nv_arrayptr(np);
578		if(ap && (ap->nelem&ARRAY_UNDEF))
579			nv_putsub(np,(char*)0,ARRAY_SCAN);
580		dp->current = 0;
581	}
582	else if(++dp->current>=dp->numnodes)
583		return(0);
584	return(nv_namptr(dp->nodes,dp->current));
585}
586
587static Namfun_t *clone_inttype(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
588{
589	Namfun_t	*pp=  (Namfun_t*)malloc(fp->dsize);
590	memcpy((void*)pp, (void*)fp, fp->dsize);
591	fp->nofree &= ~1;
592	if(nv_isattr(mp,NV_NOFREE) && mp->nvalue.cp)
593		memcpy((void*)mp->nvalue.cp,np->nvalue.cp, fp->dsize-sizeof(*fp));
594	else
595		mp->nvalue.cp = (char*)(fp+1);
596	if(!nv_isattr(mp,NV_MINIMAL))
597		mp->nvenv = 0;
598	nv_offattr(mp,NV_RDONLY);
599	return(pp);
600}
601
602static int typeinfo(Opt_t* op, Sfio_t *out, const char *str, Optdisc_t *fp)
603{
604	char		*cp,**help,buffer[256];
605	Namtype_t	*dp;
606	Namval_t	*np,*nq,*tp;
607	int		n, i, offset=staktell();
608	Sfio_t		*sp;
609
610	np = *(Namval_t**)(fp+1);
611	stakputs(NV_CLASS);
612	stakputc('.');
613	stakputs(np->nvname);
614	stakputc(0);
615	np = nv_open(stakptr(offset), sh.var_tree, NV_NOADD|NV_VARNAME);
616	stakseek(offset);
617	if(!np)
618	{
619		sfprintf(sfstderr,"%s: no such variable\n",np->nvname);
620		return(-1);
621	}
622	if(!(dp=(Namtype_t*)nv_hasdisc(np,&type_disc)))
623	{
624		Namfun_t *fp;
625		for(fp=np->nvfun;fp;fp=fp->next)
626		{
627			if(fp->disc && fp->disc->clonef==clone_inttype)
628				break;
629		}
630		if(!fp)
631		{
632			sfprintf(sfstderr,"%s: not a type\n",np->nvname);
633			return(-1);
634		}
635		if(strcmp(str,"other")==0)
636			return(0);
637		tp = fp->type;
638		nv_offattr(np,NV_RDONLY);
639		fp->type = 0;
640		if(np->nvenv)
641			sfprintf(out,"[+?\b%s\b is a %s.]\n", tp->nvname, np->nvenv);
642		cp = (char*)out->_next;
643		sfprintf(out,"[+?\b%s\b is a %n ", tp->nvname, &i);
644		nv_attribute(np,out,(char*)0, 1);
645		if(cp[i+1]=='i')
646			cp[i-1]='n';
647		fp->type = tp;
648		nv_onattr(np,NV_RDONLY);
649		sfprintf(out," with default value \b%s\b.]",nv_getval(np));
650		return(0);
651	}
652	if(strcmp(str,"other")==0)
653	{
654		Nambfun_t	*bp;
655		if(bp=(Nambfun_t*)nv_hasdisc(np,nv_discfun(NV_DCADD)))
656		{
657			for(i=0; i < bp->num; i++)
658			{
659				if(nv_isattr(bp->bltins[i],NV_OPTGET))
660					sfprintf(out,"\b%s.%s\b(3), ",np->nvname,bp->bnames[i]);
661                        }
662		}
663		return(0);
664	}
665	help = &dp->names[dp->ndisc];
666	sp = sfnew((Sfio_t*)0,buffer,sizeof(buffer),-1,SF_STRING|SF_WRITE);
667	sfprintf(out,"[+?\b%s\b defines the following fields:]{\n",np->nvname);
668	for(i=0; i < dp->numnodes; i++)
669	{
670		nq = nv_namptr(dp->nodes,i);
671		if(tp=nv_type(nq))
672		{
673			Namfun_t *pp = nv_hasdisc(nq,&type_disc);
674			sfprintf(out,"\t[+%s?%s.\n",nq->nvname,tp->nvname);
675			n = strlen(nq->nvname);
676			while((cp=nv_namptr(dp->nodes,i+1)->nvname) && memcmp(cp,nq->nvname,n)==0 && cp[n]=='.')
677				i++;
678		}
679		else
680		{
681			sfseek(sp,(Sfoff_t)0, SEEK_SET);
682			nv_attribute(nq,sp,(char*)0,1);
683			cp = 0;
684			if(!nv_isattr(nq,NV_REF))
685				cp = sh_fmtq(nv_getval(nq));
686			sfputc(sp,0);
687			for(n=strlen(buffer); n>0 && buffer[n-1]==' '; n--);
688			buffer[n] = 0;
689			if(cp)
690				sfprintf(out,"\t[+%s?%s, default value is %s.\n",nq->nvname,*buffer?buffer:"string",cp);
691			else
692				sfprintf(out,"\t[+%s?%s.\n",nq->nvname,*buffer?buffer:"string");
693		}
694		if(help[i])
695			sfprintf(out,"  %s.",help[i]);
696		sfputc(out,']');
697	}
698	sfprintf(out,"}\n");
699	if(dp->ndisc>0)
700	{
701		stakseek(offset);
702		stakputs(NV_CLASS);
703		stakputc('.');
704		stakputs(np->nvname);
705		stakputc('.');
706		n = staktell();
707		sfprintf(out,"[+?\b%s\b defines the following discipline functions:]{\n",np->nvname);
708		for(i=0; i < dp->ndisc; i++)
709		{
710			stakputs(dp->names[i]);
711			stakputc(0);
712			cp = 0;
713			if((nq = nv_search(stakptr(offset),sh.fun_tree,0)) && nq->nvalue.cp)
714				cp = nq->nvalue.rp->help;
715			sfprintf(out,"\t[+%s?%s]\n",dp->names[i],cp?cp:Empty);
716			if(cp)
717				sfputc(out,'.');
718			stakseek(n);
719		}
720		sfprintf(out,"}\n");
721	}
722	stakseek(offset);
723	sfclose(sp);
724	return(0);
725}
726
727static int std_disc(Namval_t *mp, Namtype_t *pp)
728{
729	register const char	*sp, *cp = strrchr(mp->nvname,'.');
730	register const char	**argv;
731	register int			i;
732	Namval_t		*np=0,*nq;
733	if(cp)
734		cp++;
735	else
736		cp = mp->nvname;
737	if(strcmp(cp,"create")==0)
738	{
739		if(pp)
740			pp->cp = mp;
741		return(0);
742	}
743	for(argv=nv_discnames; sp=*argv; argv++)
744	{
745		if(strcmp(cp,sp)==0)
746		{
747			if(!pp)
748				return(1);
749			goto found;
750		}
751	}
752	return(0);
753found:
754	if(memcmp(sp=mp->nvname,NV_CLASS,sizeof(NV_CLASS)-1)==0)
755		sp += sizeof(NV_CLASS);
756	sp += strlen(pp->fun.type->nvname)+1;
757	if(sp == cp)
758		np = pp->fun.type;
759	else for(i=1; i < pp->numnodes; i++)
760	{
761		nq = nv_namptr(pp->nodes,i);
762		if(memcmp(nq->nvname, sp, cp-sp-1)==0)
763		{
764			np = nq;
765			break;
766		}
767	}
768	if(np)
769	{
770		nv_onattr(mp,NV_NOFREE);
771		if(!nv_setdisc(np,cp, mp, (Namfun_t*)np))
772			sfprintf(sfstderr," nvsetdisc failed name=%s sp=%s cp=%s\n",np->nvname,sp,cp);
773	}
774	else
775		sfprintf(sfstderr,"can't set discipline %s cp=%s \n",sp,cp);
776	return(1);
777}
778
779
780void nv_addtype(Namval_t *np, const char *optstr, Optdisc_t *op, size_t optsz)
781{
782	Namdecl_t	*cp = newof((Namdecl_t*)0,Namdecl_t,1,optsz);
783	Optdisc_t	*dp = (Optdisc_t*)(cp+1);
784	Shell_t		*shp = sh_getinterp();
785	Namval_t	*mp,*bp;
786	char		*name;
787	if(optstr)
788		cp->optstring = optstr;
789	else
790		cp->optstring = sh_opttype;
791	memcpy((void*)dp,(void*)op, optsz);
792	cp->optinfof = (void*)dp;
793	cp->tp = np;
794	mp = nv_search("typeset",shp->bltin_tree,0);
795	if(name=strrchr(np->nvname,'.'))
796		name++;
797	else
798		name = np->nvname;
799	if((bp=nv_search(name,shp->fun_tree,NV_NOSCOPE)) && !bp->nvalue.ip)
800		nv_delete(bp,shp->fun_tree,0);
801	bp = sh_addbuiltin(name, mp->nvalue.bfp, (void*)cp);
802	nv_onattr(bp,nv_isattr(mp,NV_PUBLIC));
803	nv_onattr(np, NV_RDONLY);
804}
805
806void nv_newtype(Namval_t *mp)
807{
808	struct	{
809		    Optdisc_t	opt;
810		    Namval_t	*np;
811		}	optdisc;
812	memset(&optdisc,0,sizeof(optdisc));
813	optdisc.opt.infof = typeinfo;
814	optdisc.np = mp;
815	nv_addtype(mp,sh_opttype, &optdisc.opt, sizeof(optdisc));
816}
817
818/*
819 * This function creates a type out of the <numnodes> nodes in the
820 * array <nodes>.  The first node is the name for the type
821 */
822Namval_t *nv_mktype(Namval_t **nodes, int numnodes)
823{
824	Namval_t	*mp=nodes[0], *bp=0, *np, *nq, **mnodes=nodes;
825	int		i,j,k,m,n,nd=0,nref=0,iref=0,inherit=0;
826	int		size=sizeof(NV_DATA), dsize=0, nnodes;
827	size_t		offset=0;
828	char		*name=0, *cp, *sp, **help;
829	Namtype_t	*pp,*qp=0,*dp,*tp;
830	Dt_t		*root = nv_dict(mp);
831	struct Namref	*nrp = 0;
832	Namfun_t	*fp;
833	m = strlen(mp->nvname)+1;
834	for(nnodes=1,i=1; i <numnodes; i++)
835	{
836		np=nodes[i];
837		if(is_afunction(np))
838		{
839			if(!std_disc(np, (Namtype_t*)0))
840			{
841				size += strlen(np->nvname+m)+1;
842				if(memcmp(np->nvname,NV_CLASS,sizeof(NV_CLASS)-1)==0)
843					size -= sizeof(NV_CLASS);
844				nd++;
845			}
846			continue;
847		}
848		if(nv_isattr(np,NV_REF))
849			iref++;
850		if(np->nvenv)
851			size += strlen((char*)np->nvenv)+1;
852		if(strcmp(&np->nvname[m],NV_DATA)==0 && !nv_type(np))
853			continue;
854		if(qp)
855		{	/* delete duplicates */
856			for(j=0; j < qp->numnodes;j++)
857			{
858				nq = nv_namptr(qp->nodes,j);
859				if(strcmp(nq->nvname,&np->nvname[m])==0)
860					break;
861			}
862			if(j < qp->numnodes)
863				continue;
864		}
865		nnodes++;
866		if(name && memcmp(&name[m],&np->nvname[m],n)==0 && np->nvname[m+n]=='.')
867			offset -= sizeof(char*);
868		dsize = datasize(np,&offset);
869		if(!nv_isarray(np) && (dp=(Namtype_t*)nv_hasdisc(np, &type_disc)))
870		{
871			nnodes += dp->numnodes;
872			if((n=dp->strsize)<0)
873				n = -n;
874			iref = nref += dp->nref;
875			if(np->nvname[m]=='_' && np->nvname[m+1]==0 && (bp=nv_type(np)))
876			{
877				qp = dp;
878				nd = dp->ndisc;
879				nnodes = dp->numnodes;
880				offset = 0;
881				dsize = nv_size(np);
882				size += n;
883			}
884			else
885				size += n + dp->numnodes*(strlen(&np->nvname[m])+1);
886			n = strlen(np->nvname);
887			while((i+1) < numnodes && (cp=nodes[i+1]->nvname) && memcmp(cp,np->nvname,n)==0 && cp[n]=='.')
888				i++;
889		}
890		else if(nv_isattr(np,NV_REF))
891			nref++;
892		offset += (dsize?dsize:4);
893		size += (n=strlen(name=np->nvname)-m+1);
894	}
895	offset = roundof(offset,sizeof(char*));
896	nv_setsize(mp,offset);
897	if(nd)
898		nd++;
899	k = roundof(sizeof(Namtype_t),sizeof(Sfdouble_t)) - sizeof(Namtype_t);
900	pp = newof(NiL, Namtype_t, 1, nnodes*NV_MINSZ + offset + size + (nnodes+nd)*sizeof(char*) + iref*sizeof(struct Namref)+k);
901	pp->fun.dsize = sizeof(Namtype_t)+nnodes*NV_MINSZ +offset+k;
902	pp->fun.type = mp;
903	pp->parent = nv_lastdict();
904	pp->np = mp;
905	pp->bp = bp;
906	pp->childfun.fun.disc = &chtype_disc;
907	pp->childfun.fun.nofree = 1;
908	pp->childfun.ttype = pp;
909	pp->childfun.ptype = pp;
910	pp->fun.disc = &type_disc;
911	pp->nodes = (char*)(pp+1);
912	pp->numnodes = nnodes;
913	pp->data = pp->nodes + nnodes*NV_MINSZ +k;
914	pp->dsize = offset;
915	nrp = (struct Namref*)(pp->data+offset);
916	pp->names = (char**)(nrp+iref);
917	help = &pp->names[nd];
918	pp->strsize = size;
919	cp = (char*)&pp->names[nd+nnodes];
920	if(qp)
921		mnodes = newof(NiL, Namval_t*, nd+1, 0);
922	nd = 0;
923	nq = nv_namptr(pp->nodes,0);
924	nq->nvname = cp;
925	nv_onattr(nq,NV_MINIMAL);
926	cp = strcopy(cp,NV_DATA);
927	*cp++ = 0;
928	for(name=0, offset=0, k=i=1; i < numnodes; i++)
929	{
930		np=nodes[i];
931		if(is_afunction(np))
932		{
933			sp = np->nvname+m;
934			if(memcmp(np->nvname,NV_CLASS,sizeof(NV_CLASS)-1)==0)
935				sp += sizeof(NV_CLASS);
936			if(!std_disc(np, pp))
937			{
938				/* see if discipline already defined */
939				for(j=0; j< nd; j++)
940				{
941					if(strcmp(sp,pp->names[j])==0)
942					{
943						mnodes[j] = nodes[i];
944						break;
945					}
946				}
947				if(j>=nd)
948				{
949					pp->names[nd] = cp;
950					mnodes[nd++] = nodes[i];
951					cp = strcopy(cp,sp);
952					*cp++ = 0;
953				}
954				nv_onattr(mnodes[j],NV_NOFREE);
955			}
956			continue;
957		}
958		if(inherit)
959		{
960			for(j=0; j < k ; j++)
961			{
962				nq = nv_namptr(pp->nodes,j);
963				if(strcmp(nq->nvname,&np->nvname[m])==0)
964					break;
965			}
966			if(j < k)
967			{
968				sp = nv_getval(np);
969				if(nv_isvtree(np))
970					sfprintf(sfstderr,"initialization not implemented\n");
971				else if(sp)
972					nv_putval(nq,sp,0);
973				goto skip;
974			}
975		}
976		if(strcmp(&np->nvname[m],NV_DATA)==0 && !nv_type(np))
977		{
978			char *val=nv_getval(np);
979			nq = nv_namptr(pp->nodes,0);
980			nq->nvfun = 0;
981			nv_putval(nq,(val?val:0),nv_isattr(np,~(NV_IMPORT|NV_EXPORT|NV_ARRAY)));
982			nq->nvflag = np->nvflag|NV_NOFREE|NV_MINIMAL;
983			goto skip;
984		}
985		if(qp)
986		{
987			Nambfun_t	*bp;
988			dp = (Namtype_t*)nv_hasdisc(nv_type(np), &type_disc);
989			memcpy(pp->nodes,dp->nodes,dp->numnodes*NV_MINSZ);
990			offset = nv_size(np);
991			memcpy(pp->data,dp->data,offset);
992			for(k=0;k < dp->numnodes; k++)
993			{
994				Namval_t *nr = nv_namptr(qp->nodes,k);
995				nq = nv_namptr(pp->nodes,k);
996				if(fixnode(pp,dp,k,nrp,0))
997				{
998					nrp++;
999					nq = nq->nvalue.nrp->np;
1000				}
1001				if(!nv_isattr(nr,NV_REF) && !nv_hasdisc(nr,&type_disc))
1002				{
1003					if(nr->nvsize)
1004						memcpy((char*)nq->nvalue.cp,nr->nvalue.cp,size=datasize(nr,(size_t*)0));
1005					else
1006					{
1007						nq->nvalue.cp = nr->nvalue.cp;
1008						nv_onattr(nq,NV_NOFREE);
1009					}
1010				}
1011			}
1012			if(bp=(Nambfun_t*)nv_hasdisc(np,nv_discfun(NV_DCADD)))
1013			{
1014				for(j=0; j < bp->num; j++)
1015				{
1016					pp->names[nd++] = (char*)bp->bnames[j];
1017					mnodes[j] = bp->bltins[j];
1018				}
1019			}
1020			qp = 0;
1021			inherit=1;
1022			goto skip;
1023		}
1024		nq = nv_namptr(pp->nodes,k);
1025		if(np->nvenv)
1026		{
1027			/* need to save the string pointer */
1028			nv_offattr(np,NV_EXPORT);
1029			help[k] = cp;
1030			cp = strcopy(cp,np->nvenv);
1031			j = *help[k];
1032			if(islower(j))
1033				*help[k] = toupper(j);
1034			*cp++ = 0;
1035			np->nvenv = 0;
1036		}
1037		nq->nvname = cp;
1038		if(name && memcmp(name,&np->nvname[m],n)==0 && np->nvname[m+n]=='.')
1039			offset -= sizeof(char*);
1040		dsize = datasize(np,&offset);
1041		cp = strcopy(name=cp, &np->nvname[m]);
1042		n = cp-name;
1043		*cp++ = 0;
1044		nq->nvsize = np->nvsize;
1045		nq->nvflag = (np->nvflag&~(NV_IMPORT|NV_EXPORT))|NV_NOFREE|NV_MINIMAL;
1046		if(dp = (Namtype_t*)nv_hasdisc(np, &type_disc))
1047		{
1048			int r,kfirst=k;
1049			char *cname = &np->nvname[m];
1050			/*
1051			 * If field is a type, mark the type by setting
1052			 * strsize<0.  This changes create_type()
1053			 */
1054			clone_all_disc(np,nq,NV_RDONLY);
1055			if(nv_isarray(np))
1056			{
1057				nv_disc(nq, &pp->childfun.fun, NV_LAST);
1058				k++;
1059				goto skip;
1060			}
1061			if(fp=nv_hasdisc(nq,&chtype_disc))
1062				nv_disc(nq, &pp->childfun.fun, NV_LAST);
1063			if(tp = (Namtype_t*)nv_hasdisc(nq, &type_disc))
1064				tp->strsize = -tp->strsize;
1065else sfprintf(sfstderr,"tp==NULL\n");
1066			for(r=0; r < dp->numnodes; r++)
1067			{
1068				Namval_t *nr = nv_namptr(dp->nodes,r);
1069				nq = nv_namptr(pp->nodes,++k);
1070				nq->nvname = cp;
1071				dsize = datasize(nr,&offset);
1072				nq->nvflag = nr->nvflag;
1073				if(nr->nvalue.cp)
1074				{
1075					Namchld_t *xp = (Namchld_t*)nv_hasdisc(nr,&chtype_disc);
1076					if(xp && nr->nvalue.cp >= xp->ptype->data && nr->nvalue.cp < xp->ptype->data+xp->ptype->fun.dsize)
1077					{
1078						nq->nvalue.cp = pp->data+offset;
1079						memcpy((char*)nq->nvalue.cp,nr->nvalue.cp,dsize);
1080						nv_onattr(nq,NV_NOFREE);
1081					}
1082					else
1083						nq->nvalue.cp = strdup(nr->nvalue.cp);
1084					nv_disc(nq, &pp->childfun.fun, NV_LAST);
1085				}
1086				nq->nvsize = nr->nvsize;
1087				offset += dsize;
1088				if(*cname!='_' || cname[1])
1089				{
1090					cp = strcopy(cp,cname);
1091					*cp++ = '.';
1092				}
1093				cp = strcopy(cp,nr->nvname);
1094				*cp++ = 0;
1095			}
1096			while((i+1) < numnodes && (cname=&nodes[i+1]->nvname[m]) && memcmp(cname,&np->nvname[m],n)==0 && cname[n]=='.')
1097			{
1098				int j=kfirst;
1099				nv_unset(np);
1100				nv_delete(np,root,0);
1101				np = nodes[++i];
1102				while(j < k)
1103				{
1104					nq = nv_namptr(pp->nodes,++j);
1105					if(strcmp(nq->nvname,cname)==0)
1106					{
1107						sfprintf(sfstderr,"%s found at k=%d\n",nq->nvname,k);
1108						if(nq->nvalue.cp>=pp->data && nq->nvalue.cp< (char*)pp->names)
1109							memcpy((char*)nq->nvalue.cp,np->nvalue.cp,datasize(np,0));
1110						break;
1111					}
1112				}
1113			}
1114		}
1115		else
1116		{
1117			j = nv_isattr(np,NV_NOFREE);
1118			nq->nvfun = np->nvfun;
1119			np->nvfun = 0;
1120			if(nv_isarray(nq) && !nq->nvfun)
1121			{
1122				nv_putsub(nq, (char*)0, ARRAY_FILL);
1123				if(nv_isattr(nq,NV_INTEGER))
1124					nv_putval(nq, "0",0);
1125				else
1126					((Namarr_t*)nq->nvfun)->nelem--;
1127			}
1128			nv_disc(nq, &pp->childfun.fun, NV_LAST);
1129			if(nq->nvfun)
1130			{
1131				for(fp=nq->nvfun; fp; fp = fp->next)
1132					fp->nofree |= 1;
1133			}
1134			nq->nvalue.cp = np->nvalue.cp;
1135			if(dsize)
1136			{
1137				nq->nvalue.cp = pp->data+offset;
1138				sp = (char*)np->nvalue.cp;
1139				if(nv_isattr(np,NV_INT16P) ==NV_INT16)
1140				{
1141					sp= (char*)&np->nvalue;
1142					nv_onattr(nq,NV_INT16P);
1143					j = 1;
1144				}
1145				if(sp)
1146					memcpy((char*)nq->nvalue.cp,sp,dsize);
1147				else if(nv_isattr(np,NV_LJUST|NV_RJUST))
1148					memset((char*)nq->nvalue.cp,' ',dsize);
1149				if(!j)
1150					free((void*)np->nvalue.cp);
1151			}
1152			if(!nq->nvalue.cp && nq->nvfun== &pp->childfun.fun)
1153				nq->nvalue.cp = Empty;
1154			np->nvalue.cp = 0;
1155#if 0
1156			offset += dsize;
1157#else
1158			offset += (dsize?dsize:4);
1159#endif
1160		}
1161		k++;
1162	skip:
1163		if(!nv_isnull(np))
1164			_nv_unset(np,0);
1165		nv_delete(np,root,0);
1166	}
1167	pp->ndisc = nd;
1168	pp->nref = nref;
1169	if(k>1)
1170	{
1171		nv_setsize(mp,offset);
1172		mp->nvalue.cp = pp->data;
1173		nv_onattr(mp,NV_NOFREE|NV_BINARY|NV_RAW);
1174	}
1175	else if(!mp->nvalue.cp)
1176		mp->nvalue.cp = Empty;
1177	nv_disc(mp, &pp->fun, NV_LAST);
1178	if(nd>0)
1179	{
1180		pp->names[nd] = 0;
1181		nv_adddisc(mp, (const char**)pp->names, mnodes);
1182	}
1183	if(mnodes!=nodes)
1184		free((void*)mnodes);
1185	nv_newtype(mp);
1186	return(mp);
1187}
1188
1189Namval_t *nv_mkinttype(char *name, size_t size, int sign, const char *help, Namdisc_t *ep)
1190{
1191	Namval_t	*mp;
1192	Namfun_t	*fp;
1193	Namdisc_t	*dp;
1194	int		offset=staktell();
1195	stakputs(NV_CLASS);
1196	stakputc('.');
1197	stakputs(name);
1198	stakputc(0);
1199        mp = nv_open(stakptr(offset), sh.var_tree, NV_VARNAME);
1200	stakseek(offset);
1201	offset = size + sizeof(Namdisc_t);
1202	fp = newof(NiL, Namfun_t, 1, offset);
1203	fp->type = mp;
1204	fp->nofree |= 1;
1205	fp->dsize = sizeof(Namfun_t)+size;
1206	dp = (Namdisc_t*)(fp+1);
1207	if(ep)
1208		*dp = *ep;
1209	dp->clonef =  clone_inttype;
1210	fp->disc = dp;
1211	mp->nvalue.cp = (char*)(fp+1) + sizeof(Namdisc_t);
1212	nv_setsize(mp,10);
1213	mp->nvenv = (char*)help;
1214	nv_onattr(mp,NV_NOFREE|NV_RDONLY|NV_INTEGER|NV_EXPORT);
1215	if(size==16)
1216		nv_onattr(mp,NV_INT16P);
1217	else if(size==64)
1218		nv_onattr(mp,NV_INT64);
1219	if(!sign)
1220		nv_onattr(mp,NV_UNSIGN);
1221	nv_disc(mp, fp, NV_LAST);
1222	nv_newtype(mp);
1223	return(mp);
1224}
1225
1226void nv_typename(Namval_t *tp, Sfio_t *out)
1227{
1228	char *v,*cp;
1229	Namtype_t	*dp;
1230	cp = nv_name(tp);
1231	if(v=strrchr(cp,'.'))
1232		cp = v+1;
1233	if((dp = (Namtype_t*)nv_hasdisc(tp,&type_disc)) && dp->bp)
1234	{
1235		nv_typename(dp->bp,out);
1236		sfprintf(out,"%s.%s",sfstruse(out),cp);
1237	}
1238	else
1239		sfputr(out,cp,-1);
1240}
1241
1242Namval_t *nv_type(Namval_t *np)
1243{
1244	Namfun_t  *fp;
1245	if(nv_isattr(np,NV_BLTIN|BLT_DCL)==(NV_BLTIN|BLT_DCL))
1246	{
1247		Namdecl_t *ntp = (Namdecl_t*)nv_context(np);
1248		return(ntp?ntp->tp:0);
1249	}
1250	for(fp=np->nvfun; fp; fp=fp->next)
1251	{
1252		if(fp->type)
1253			return(fp->type);
1254		if(fp->disc && fp->disc->typef && (np= (*fp->disc->typef)(np,fp)))
1255			return(np);
1256	}
1257	return(0);
1258}
1259
1260/*
1261 * call any and all create functions
1262 */
1263static void type_init(Namval_t *np)
1264{
1265	int 		i;
1266	Namtype_t	*dp, *pp=(Namtype_t*)nv_hasdisc(np,&type_disc);
1267	Namval_t	*nq;
1268	if(!pp)
1269		return;
1270	for(i=0; i < pp->numnodes; i++)
1271	{
1272		nq = nv_namptr(pp->nodes,i);
1273		if((dp=(Namtype_t*)nv_hasdisc(nq,&type_disc)) && dp->cp)
1274			sh_fun(dp->cp,nq, (char**)0);
1275	}
1276	if(pp->cp)
1277		sh_fun(pp->cp, np, (char**)0);
1278}
1279
1280/*
1281 * This function turns variable <np>  to the type <tp>
1282 */
1283int nv_settype(Namval_t* np, Namval_t *tp, int flags)
1284{
1285	int		isnull = nv_isnull(np);
1286	int		rdonly = nv_isattr(np,NV_RDONLY);
1287	char		*val=0;
1288	Namarr_t	*ap=0;
1289	int		nelem=0;
1290#if SHOPT_TYPEDEF
1291	Namval_t	*tq;
1292	if(nv_type(np)==tp)
1293		return(0);
1294	if(nv_isarray(np) && (tq=nv_type(np)))
1295	{
1296		if(tp==tq)
1297			return(0);
1298		errormsg(SH_DICT,ERROR_exit(1),e_redef,nv_name(np));
1299	}
1300	if((ap=nv_arrayptr(np)) && ap->nelem>0)
1301	{
1302		nv_putsub(np,NIL(char*),ARRAY_SCAN);
1303		ap->hdr.type = tp;
1304		do
1305		{
1306			nv_arraysettype(np, tp, nv_getsub(np),flags);
1307		}
1308		while(nv_nextsub(np));
1309	}
1310	else if(ap || nv_isarray(np))
1311	{
1312		flags &= ~NV_APPEND;
1313		if(!ap)
1314		{
1315			nv_putsub(np,"0",ARRAY_FILL);
1316			ap = nv_arrayptr(np);
1317			nelem = 1;
1318
1319		}
1320	}
1321	else
1322#endif /*SHOPT_TYPEDEF */
1323	{
1324		if(isnull)
1325			flags &= ~NV_APPEND;
1326		else if(!nv_isvtree(np))
1327		{
1328			val = strdup(nv_getval(np));
1329			if(!(flags&NV_APPEND))
1330				_nv_unset(np, NV_RDONLY);
1331		}
1332		if(!nv_clone(tp,np,flags|NV_NOFREE))
1333			return(0);
1334	}
1335	if(ap)
1336	{
1337		int nofree;
1338		nv_disc(np,&ap->hdr,NV_POP);
1339		np->nvalue.up = 0;
1340		nv_clone(tp,np,flags|NV_NOFREE);
1341		if(np->nvalue.cp && !nv_isattr(np,NV_NOFREE))
1342			free((void*)np->nvalue.cp);
1343		np->nvalue.up = 0;
1344		nofree = ap->hdr.nofree;
1345		ap->hdr.nofree = 0;
1346		ap->hdr.type = tp;
1347		nv_disc(np, &ap->hdr, NV_FIRST);
1348		ap->hdr.nofree = nofree;
1349		nv_onattr(np,NV_ARRAY);
1350		if(nelem)
1351		{
1352			ap->nelem++;
1353			nv_putsub(np,"0",0);
1354			_nv_unset(np,NV_RDONLY);
1355			ap->nelem--;
1356		}
1357	}
1358	type_init(np);
1359	if(!rdonly)
1360		nv_offattr(np,NV_RDONLY);
1361	if(val)
1362	{
1363		nv_putval(np,val,NV_RDONLY);
1364		free((void*)val);
1365	}
1366	return(0);
1367}
1368
1369#define S(x)	#x
1370#define FIELD(x,y)	{ S(y##x),	S(x##_t), offsetof(struct stat,st_##y##x) }
1371typedef struct _field_
1372{
1373	char	*name;
1374	char	*type;
1375	int	offset;
1376} Fields_t;
1377
1378Fields_t foo[]=
1379{
1380	FIELD(dev,),
1381	FIELD(ino,),
1382	FIELD(nlink,),
1383	FIELD(mode,),
1384	FIELD(uid,),
1385	FIELD(gid,),
1386	FIELD(size,),
1387	FIELD(time,a),
1388	FIELD(time,m),
1389	FIELD(time,c),
1390#if 0
1391	FIELD(blksize,),
1392	FIELD(blocks,),
1393#endif
1394	0
1395};
1396
1397
1398Namval_t *nv_mkstruct(const char *name, int rsize, Fields_t *fields)
1399{
1400	Namval_t	*mp, *nq, *nr, *tp;
1401	Fields_t	*fp;
1402	Namtype_t	*dp, *pp;
1403	char		*cp, *sp;
1404	int		nnodes=0, offset=staktell(), n, r, i, j;
1405	size_t		m, size=0;
1406	stakputs(NV_CLASS);
1407	stakputc('.');
1408	r = staktell();
1409	stakputs(name);
1410	stakputc(0);
1411	mp = nv_open(stakptr(offset), sh.var_tree, NV_VARNAME);
1412	stakseek(r);
1413
1414	for(fp=fields; fp->name; fp++)
1415	{
1416		m = strlen(fp->name)+1;
1417		size += m;
1418		nnodes++;
1419		if(memcmp(fp->type,"typeset",7))
1420		{
1421			stakputs(fp->type);
1422			stakputc(0);
1423			tp = nv_open(stakptr(offset), sh.var_tree, NV_VARNAME|NV_NOADD|NV_NOFAIL);
1424			stakseek(r);
1425			if(!tp)
1426				errormsg(SH_DICT,ERROR_exit(1),e_unknowntype,strlen(fp->type),fp->type);
1427			if(dp = (Namtype_t*)nv_hasdisc(tp,&type_disc))
1428			{
1429				nnodes += dp->numnodes;
1430				if((i=dp->strsize) < 0)
1431					i = -i;
1432				size += i + dp->numnodes*m;
1433			}
1434		}
1435	}
1436	pp = newof(NiL,Namtype_t, 1,  nnodes*NV_MINSZ + rsize + size);
1437	pp->fun.dsize = sizeof(Namtype_t)+nnodes*NV_MINSZ +rsize;
1438	pp->fun.type = mp;
1439	pp->np = mp;
1440	pp->childfun.fun.disc = &chtype_disc;
1441	pp->childfun.fun.nofree = 1;
1442	pp->childfun.ttype = pp;
1443	pp->childfun.ptype = pp;
1444	pp->fun.disc = &type_disc;
1445	pp->nodes = (char*)(pp+1);
1446	pp->numnodes = nnodes;
1447	pp->strsize = size;
1448	pp->data = pp->nodes + nnodes*NV_MINSZ;
1449	cp = pp->data + rsize;
1450	for(i=0,fp=fields; fp->name; fp++)
1451	{
1452		nq = nv_namptr(pp->nodes,i++);
1453		nq->nvname = cp;
1454		nq->nvalue.cp = pp->data + fp->offset;
1455		nv_onattr(nq,NV_MINIMAL|NV_NOFREE);
1456		m = strlen(fp->name)+1;
1457		memcpy(cp, fp->name, m);
1458		cp += m;
1459		if(memcmp(fp->type,"typeset",7))
1460		{
1461			stakputs(fp->type);
1462			stakputc(0);
1463			tp = nv_open(stakptr(offset), sh.var_tree, NV_VARNAME);
1464			stakseek(r);
1465			clone_all_disc(tp,nq,NV_RDONLY);
1466			nq->nvflag = tp->nvflag|NV_MINIMAL|NV_NOFREE;
1467			nq->nvsize = tp->nvsize;
1468			if(dp = (Namtype_t*)nv_hasdisc(nq,&type_disc))
1469				dp->strsize = -dp->strsize;
1470			if(dp = (Namtype_t*)nv_hasdisc(tp,&type_disc))
1471			{
1472				if(nv_hasdisc(nq,&chtype_disc))
1473					nv_disc(nq, &pp->childfun.fun, NV_LAST);
1474				sp = (char*)nq->nvalue.cp;
1475				memcpy(sp, dp->data, nv_size(tp));
1476				for(j=0; j < dp->numnodes; j++)
1477				{
1478					nr = nv_namptr(dp->nodes,j);
1479					nq = nv_namptr(pp->nodes,i++);
1480					nq->nvname = cp;
1481					memcpy(cp,fp->name,m);
1482					cp[m-1] = '.';
1483					cp += m;
1484					n = strlen(nr->nvname)+1;
1485					memcpy(cp,nr->nvname,n);
1486					cp += n;
1487					if(nr->nvalue.cp>=dp->data && nr->nvalue.cp < (char*)pp + pp->fun.dsize)
1488					{
1489						nq->nvalue.cp = sp + (nr->nvalue.cp-dp->data);
1490					}
1491					nq->nvflag = nr->nvflag;
1492					nq->nvsize = nr->nvsize;
1493				}
1494			}
1495		}
1496		else if(strmatch(fp->type+7,"*-*i*")==0)
1497		{
1498			nv_onattr(nq,NV_NOFREE|NV_RDONLY|NV_INTEGER);
1499			if(strmatch(fp->type+7,"*-*s*")==0)
1500				nv_onattr(nq,NV_INT16P);
1501			else if(strmatch(fp->type+7,"*-*l*")==0)
1502				nv_onattr(nq,NV_INT64);
1503			if(strmatch(fp->type+7,"*-*u*")==0)
1504				nv_onattr(nq,NV_UNSIGN);
1505		}
1506
1507	}
1508	stakseek(offset);
1509	nv_onattr(mp,NV_RDONLY|NV_NOFREE|NV_BINARY);
1510	nv_setsize(mp,rsize);
1511	nv_disc(mp, &pp->fun, NV_LAST);
1512	mp->nvalue.cp = pp->data;
1513	nv_newtype(mp);
1514	return(mp);
1515}
1516
1517static void put_stat(Namval_t* np, const char* val, int flag, Namfun_t* nfp)
1518{
1519	if(val)
1520	{
1521		if(stat(val,(struct stat*)np->nvalue.cp)<0)
1522			sfprintf(sfstderr,"stat of %s failed\n",val);
1523		return;
1524	}
1525	nv_putv(np,val,flag,nfp);
1526	nv_disc(np,nfp,NV_POP);
1527	if(!(nfp->nofree&1))
1528		free((void*)nfp);
1529}
1530
1531static const Namdisc_t stat_disc =
1532{
1533        0,
1534        put_stat
1535};
1536
1537
1538void nv_mkstat(void)
1539{
1540	Namval_t *tp;
1541	Namfun_t *fp;
1542	tp = nv_mkstruct("stat_t", sizeof(struct stat), foo);
1543	nv_offattr(tp,NV_RDONLY);
1544	nv_setvtree(tp);
1545	fp = newof(NiL,Namfun_t,1,0);
1546	fp->type = tp;
1547	fp->disc = &stat_disc;
1548	nv_disc(tp,fp,NV_FIRST);
1549	nv_putval(tp,"/dev/null",0);
1550	nv_onattr(tp,NV_RDONLY);
1551}
1552