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