xref: /titanic_52/usr/src/contrib/ast/src/cmd/ksh93/sh/nvtype.c (revision 906afcb89d0412cc073b95c2d701a804a8cdb62c)
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 
30 static const char sh_opttype[] =
31 "[-1c?\n@(#)$Id: type (AT&T Labs Research) 2008-07-01 $\n]"
32 USAGE_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 
71 typedef struct Namtype Namtype_t;
72 typedef struct Namchld
73 {
74 	Namfun_t	fun;
75 	Namtype_t 	*ptype;
76 	Namtype_t 	*ttype;
77 } Namchld_t;
78 
79 struct 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
103 struct type
104 {
105 	Namtype_t	hdr;
106 	unsigned short	ndisc;
107 	unsigned short	current;
108 	unsigned short	nref;
109 };
110 #endif
111 
112 typedef 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 
134 static void put_type(Namval_t*, const char*, int, Namfun_t*);
135 static Namval_t* create_type(Namval_t*, const char*, int, Namfun_t*);
136 static Namfun_t* clone_type(Namval_t*, Namval_t*, int, Namfun_t*);
137 static Namval_t* next_type(Namval_t*, Dt_t*, Namfun_t*);
138 
139 static 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 
156 size_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 
210 static 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 
232 static 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 
259 static 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 
266 static 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 
278 static 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 
297 static 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 
364 static 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  */
497 static 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;
529 found:
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 
547 static 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 
586 static 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 
601 static 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 
616 static 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 
744 static 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);
770 found:
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 
797 void 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 
835 void 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  */
851 Namval_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 	k = roundof(sizeof(Namtype_t),sizeof(Sfdouble_t)) - sizeof(Namtype_t);
933 	pp = newof(NiL, Namtype_t, 1, nnodes*NV_MINSZ + offset + size + (nnodes+nd)*sizeof(char*) + iref*sizeof(struct Namref)+k);
934 	pp->fun.dsize = sizeof(Namtype_t)+nnodes*NV_MINSZ +offset+k;
935 	pp->fun.type = mp;
936 	pp->parent = nv_lastdict();
937 	pp->np = mp;
938 	pp->bp = bp;
939 	pp->childfun.fun.disc = &chtype_disc;
940 	pp->childfun.fun.nofree = 1;
941 	pp->childfun.ttype = pp;
942 	pp->childfun.ptype = pp;
943 	pp->fun.disc = &type_disc;
944 	pp->nodes = (char*)(pp+1);
945 	pp->numnodes = nnodes;
946 	pp->data = pp->nodes + nnodes*NV_MINSZ +k;
947 	pp->dsize = offset;
948 	nrp = (struct Namref*)(pp->data+offset);
949 	pp->names = (char**)(nrp+iref);
950 	help = &pp->names[nd];
951 	pp->strsize = size;
952 	cp = (char*)&pp->names[nd+nnodes];
953 	if(qp)
954 		mnodes = newof(NiL, Namval_t*, nd+1, 0);
955 	nd = 0;
956 	nq = nv_namptr(pp->nodes,0);
957 	nq->nvname = cp;
958 	nv_onattr(nq,NV_MINIMAL);
959 	cp = strcopy(cp,NV_DATA);
960 	*cp++ = 0;
961 	for(name=0, offset=0, k=i=1; i < numnodes; i++)
962 	{
963 		np=nodes[i];
964 		if(is_afunction(np))
965 		{
966 			sp = np->nvname+m;
967 			if(memcmp(np->nvname,NV_CLASS,sizeof(NV_CLASS)-1)==0)
968 				sp += sizeof(NV_CLASS);
969 			if(!std_disc(np, pp))
970 			{
971 				/* see if discipline already defined */
972 				for(j=0; j< nd; j++)
973 				{
974 					if(strcmp(sp,pp->names[j])==0)
975 					{
976 						mnodes[j] = nodes[i];
977 						break;
978 					}
979 				}
980 				if(j>=nd)
981 				{
982 					pp->names[nd] = cp;
983 					mnodes[nd++] = nodes[i];
984 					cp = strcopy(cp,sp);
985 					*cp++ = 0;
986 				}
987 				nv_onattr(mnodes[j],NV_NOFREE);
988 			}
989 			continue;
990 		}
991 		if(inherit)
992 		{
993 			for(j=0; j < k ; j++)
994 			{
995 				nq = nv_namptr(pp->nodes,j);
996 				if(strcmp(nq->nvname,&np->nvname[m])==0)
997 					break;
998 			}
999 			if(j < k)
1000 			{
1001 				sp = nv_getval(np);
1002 				if(nv_isvtree(np))
1003 					sfprintf(sfstderr,"initialization not implemented\n");
1004 				else if(sp)
1005 					nv_putval(nq,sp,0);
1006 				goto skip;
1007 			}
1008 		}
1009 		if(strcmp(&np->nvname[m],NV_DATA)==0 && !nv_type(np))
1010 		{
1011 			char *val=nv_getval(np);
1012 			nq = nv_namptr(pp->nodes,0);
1013 			nq->nvfun = 0;
1014 			nv_putval(nq,(val?val:0),nv_isattr(np,~(NV_IMPORT|NV_EXPORT|NV_ARRAY)));
1015 			nq->nvflag = np->nvflag|NV_NOFREE|NV_MINIMAL;
1016 			goto skip;
1017 		}
1018 		if(qp)
1019 		{
1020 			Nambfun_t	*bp;
1021 			dp = (Namtype_t*)nv_hasdisc(nv_type(np), &type_disc);
1022 			memcpy(pp->nodes,dp->nodes,dp->numnodes*NV_MINSZ);
1023 			offset = nv_size(np);
1024 			memcpy(pp->data,dp->data,offset);
1025 			for(k=0;k < dp->numnodes; k++)
1026 			{
1027 				Namval_t *nr = nv_namptr(qp->nodes,k);
1028 				nq = nv_namptr(pp->nodes,k);
1029 				if(fixnode(pp,dp,k,nrp,0))
1030 				{
1031 					nrp++;
1032 					nq = nq->nvalue.nrp->np;
1033 				}
1034 				if(!nv_isattr(nr,NV_REF) && !nv_hasdisc(nr,&type_disc))
1035 				{
1036 					if(nr->nvsize)
1037 						memcpy((char*)nq->nvalue.cp,nr->nvalue.cp,size=nv_datasize(nr,(size_t*)0));
1038 					else
1039 					{
1040 						nq->nvalue.cp = nr->nvalue.cp;
1041 						nv_onattr(nq,NV_NOFREE);
1042 					}
1043 				}
1044 			}
1045 			if(bp=(Nambfun_t*)nv_hasdisc(np,nv_discfun(NV_DCADD)))
1046 			{
1047 				for(j=0; j < bp->num; j++)
1048 				{
1049 					pp->names[nd++] = (char*)bp->bnames[j];
1050 					mnodes[j] = bp->bltins[j];
1051 				}
1052 			}
1053 			qp = 0;
1054 			inherit=1;
1055 			goto skip;
1056 		}
1057 		nq = nv_namptr(pp->nodes,k);
1058 		if(np->nvenv)
1059 		{
1060 			/* need to save the string pointer */
1061 			nv_offattr(np,NV_EXPORT);
1062 			help[k] = cp;
1063 			cp = strcopy(cp,np->nvenv);
1064 			j = *help[k];
1065 			if(islower(j))
1066 				*help[k] = toupper(j);
1067 			*cp++ = 0;
1068 			np->nvenv = 0;
1069 		}
1070 		nq->nvname = cp;
1071 		if(name && memcmp(name,&np->nvname[m],n)==0 && np->nvname[m+n]=='.')
1072 			offset -= sizeof(char*);
1073 		dsize = nv_datasize(np,&offset);
1074 		cp = strcopy(name=cp, &np->nvname[m]);
1075 		n = cp-name;
1076 		*cp++ = 0;
1077 		nq->nvsize = np->nvsize;
1078 		nq->nvflag = (np->nvflag&~(NV_IMPORT|NV_EXPORT))|NV_NOFREE|NV_MINIMAL;
1079 		if(dp = (Namtype_t*)nv_hasdisc(np, &type_disc))
1080 		{
1081 			int r,kfirst=k;
1082 			char *cname = &np->nvname[m];
1083 			/*
1084 			 * If field is a type, mark the type by setting
1085 			 * strsize<0.  This changes create_type()
1086 			 */
1087 			clone_all_disc(np,nq,NV_RDONLY);
1088 			if(nv_isarray(np))
1089 			{
1090 				nv_disc(nq, &pp->childfun.fun, NV_LAST);
1091 				k++;
1092 				goto skip;
1093 			}
1094 			if(fp=nv_hasdisc(nq,&chtype_disc))
1095 				nv_disc(nq, &pp->childfun.fun, NV_LAST);
1096 			if(tp = (Namtype_t*)nv_hasdisc(nq, &type_disc))
1097 				tp->strsize = -tp->strsize;
1098 else sfprintf(sfstderr,"tp==NULL\n");
1099 			for(r=0; r < dp->numnodes; r++)
1100 			{
1101 				Namval_t *nr = nv_namptr(dp->nodes,r);
1102 				nq = nv_namptr(pp->nodes,++k);
1103 				nq->nvname = cp;
1104 				dsize = nv_datasize(nr,&offset);
1105 				nq->nvflag = nr->nvflag;
1106 				if(nr->nvalue.cp)
1107 				{
1108 					Namchld_t *xp = (Namchld_t*)nv_hasdisc(nr,&chtype_disc);
1109 					if(xp && nr->nvalue.cp >= xp->ptype->data && nr->nvalue.cp < xp->ptype->data+xp->ptype->fun.dsize)
1110 					{
1111 						nq->nvalue.cp = pp->data+offset;
1112 						memcpy((char*)nq->nvalue.cp,nr->nvalue.cp,dsize);
1113 						nv_onattr(nq,NV_NOFREE);
1114 					}
1115 					else
1116 						nq->nvalue.cp = strdup(nr->nvalue.cp);
1117 					nv_disc(nq, &pp->childfun.fun, NV_LAST);
1118 				}
1119 				nq->nvsize = nr->nvsize;
1120 				offset += dsize;
1121 				if(*cname!='_' || cname[1])
1122 				{
1123 					cp = strcopy(cp,cname);
1124 					*cp++ = '.';
1125 				}
1126 				cp = strcopy(cp,nr->nvname);
1127 				*cp++ = 0;
1128 			}
1129 			while((i+1) < numnodes && (cname=&nodes[i+1]->nvname[m]) && memcmp(cname,&np->nvname[m],n)==0 && cname[n]=='.')
1130 			{
1131 				int j=kfirst;
1132 				nv_unset(np);
1133 				nv_delete(np,root,0);
1134 				np = nodes[++i];
1135 				while(j < k)
1136 				{
1137 					nq = nv_namptr(pp->nodes,++j);
1138 					if(strcmp(nq->nvname,cname)==0)
1139 					{
1140 						sfprintf(sfstderr,"%s found at k=%d\n",nq->nvname,k);
1141 						if(nq->nvalue.cp>=pp->data && nq->nvalue.cp< (char*)pp->names)
1142 							memcpy((char*)nq->nvalue.cp,np->nvalue.cp,nv_datasize(np,0));
1143 						break;
1144 					}
1145 				}
1146 			}
1147 		}
1148 		else
1149 		{
1150 			Namarr_t *ap;
1151 			j = nv_isattr(np,NV_NOFREE);
1152 			if(j==0 && (ap=nv_arrayptr(np)) && !ap->fun)
1153 				j = 1;
1154 			nq->nvfun = np->nvfun;
1155 			np->nvfun = 0;
1156 			nv_disc(nq, &pp->childfun.fun, NV_LAST);
1157 			if(nq->nvfun)
1158 			{
1159 				for(fp=nq->nvfun; fp; fp = fp->next)
1160 					fp->nofree |= 1;
1161 			}
1162 			nq->nvalue.cp = np->nvalue.cp;
1163 			if(dsize  && (np->nvalue.cp || !nv_isarray(np)))
1164 			{
1165 				nq->nvalue.cp = pp->data+offset;
1166 				sp = (char*)np->nvalue.cp;
1167 				if(nv_isattr(np,NV_INT16P) ==NV_INT16)
1168 				{
1169 					sp= (char*)&np->nvalue;
1170 					nv_onattr(nq,NV_INT16P);
1171 					j = 1;
1172 				}
1173 				if(sp)
1174 					memcpy((char*)nq->nvalue.cp,sp,dsize);
1175 				else if(nv_isattr(np,NV_LJUST|NV_RJUST))
1176 					memset((char*)nq->nvalue.cp,' ',dsize);
1177 				if(!j)
1178 					free((void*)np->nvalue.cp);
1179 			}
1180 			if(!nq->nvalue.cp && nq->nvfun== &pp->childfun.fun)
1181 				nq->nvalue.cp = Empty;
1182 			np->nvalue.cp = 0;
1183 #if 0
1184 			offset += dsize;
1185 #else
1186 			offset += (dsize?dsize:4);
1187 #endif
1188 		}
1189 		k++;
1190 	skip:
1191 		if(!nv_isnull(np))
1192 			_nv_unset(np,0);
1193 		nv_delete(np,root,0);
1194 	}
1195 	pp->ndisc = nd;
1196 	pp->nref = nref;
1197 	if(k>1)
1198 	{
1199 		nv_setsize(mp,offset);
1200 		mp->nvalue.cp = pp->data;
1201 		nv_onattr(mp,NV_NOFREE|NV_BINARY|NV_RAW);
1202 	}
1203 	else if(!mp->nvalue.cp)
1204 		mp->nvalue.cp = Empty;
1205 	nv_onattr(mp,NV_TAGGED);
1206 	nv_disc(mp, &pp->fun, NV_LAST);
1207 	if(nd>0)
1208 	{
1209 		pp->names[nd] = 0;
1210 		nv_adddisc(mp, (const char**)pp->names, mnodes);
1211 	}
1212 	if(mnodes!=nodes)
1213 		free((void*)mnodes);
1214 	nv_newtype(mp);
1215 	return(mp);
1216 }
1217 
1218 Namval_t *nv_mkinttype(char *name, size_t size, int sign, const char *help, Namdisc_t *ep)
1219 {
1220 	Namval_t	*mp;
1221 	Namfun_t	*fp;
1222 	Namdisc_t	*dp;
1223 	int		offset=staktell();
1224 	stakputs(NV_CLASS);
1225 	stakputc('.');
1226 	stakputs(name);
1227 	stakputc(0);
1228         mp = nv_open(stakptr(offset), sh.var_tree, NV_VARNAME);
1229 	stakseek(offset);
1230 	offset = size + sizeof(Namdisc_t);
1231 	fp = newof(NiL, Namfun_t, 1, offset);
1232 	fp->type = mp;
1233 	fp->nofree |= 1;
1234 	fp->dsize = sizeof(Namfun_t)+size;
1235 	dp = (Namdisc_t*)(fp+1);
1236 	if(ep)
1237 		*dp = *ep;
1238 	dp->clonef =  clone_inttype;
1239 	fp->disc = dp;
1240 	mp->nvalue.cp = (char*)(fp+1) + sizeof(Namdisc_t);
1241 	nv_setsize(mp,10);
1242 	mp->nvenv = (char*)help;
1243 	nv_onattr(mp,NV_NOFREE|NV_RDONLY|NV_INTEGER|NV_EXPORT);
1244 	if(size==16)
1245 		nv_onattr(mp,NV_INT16P);
1246 	else if(size==64)
1247 		nv_onattr(mp,NV_INT64);
1248 	if(!sign)
1249 		nv_onattr(mp,NV_UNSIGN);
1250 	nv_disc(mp, fp, NV_LAST);
1251 	nv_newtype(mp);
1252 	return(mp);
1253 }
1254 
1255 void nv_typename(Namval_t *tp, Sfio_t *out)
1256 {
1257 	char *v,*cp;
1258 	Namtype_t	*dp;
1259 	cp = nv_name(tp);
1260 	if(v=strrchr(cp,'.'))
1261 		cp = v+1;
1262 	if((dp = (Namtype_t*)nv_hasdisc(tp,&type_disc)) && dp->bp)
1263 	{
1264 		nv_typename(dp->bp,out);
1265 		sfprintf(out,"%s.%s",sfstruse(out),cp);
1266 	}
1267 	else
1268 		sfputr(out,cp,-1);
1269 }
1270 
1271 Namval_t *nv_type(Namval_t *np)
1272 {
1273 	Namfun_t  *fp;
1274 	if(nv_isattr(np,NV_BLTIN|BLT_DCL)==(NV_BLTIN|BLT_DCL))
1275 	{
1276 		Namdecl_t *ntp = (Namdecl_t*)nv_context(np);
1277 		return(ntp?ntp->tp:0);
1278 	}
1279 	for(fp=np->nvfun; fp; fp=fp->next)
1280 	{
1281 		if(fp->type)
1282 			return(fp->type);
1283 		if(fp->disc && fp->disc->typef && (np= (*fp->disc->typef)(np,fp)))
1284 			return(np);
1285 	}
1286 	return(0);
1287 }
1288 
1289 /*
1290  * call any and all create functions
1291  */
1292 static void type_init(Namval_t *np)
1293 {
1294 	int 		i;
1295 	Namtype_t	*dp, *pp=(Namtype_t*)nv_hasdisc(np,&type_disc);
1296 	Namval_t	*nq;
1297 	if(!pp)
1298 		return;
1299 	for(i=0; i < pp->numnodes; i++)
1300 	{
1301 		nq = nv_namptr(pp->nodes,i);
1302 		if((dp=(Namtype_t*)nv_hasdisc(nq,&type_disc)) && dp->cp)
1303 			sh_fun(dp->cp,nq, (char**)0);
1304 	}
1305 	if(pp->cp)
1306 		sh_fun(pp->cp, np, (char**)0);
1307 }
1308 
1309 /*
1310  * This function turns variable <np>  to the type <tp>
1311  */
1312 int nv_settype(Namval_t* np, Namval_t *tp, int flags)
1313 {
1314 	int		isnull = nv_isnull(np);
1315 	int		rdonly = nv_isattr(np,NV_RDONLY);
1316 	char		*val=0;
1317 	Namarr_t	*ap=0;
1318 	Shell_t		*shp = sh_getinterp();
1319 	int		nelem=0,subshell=shp->subshell;
1320 #if SHOPT_TYPEDEF
1321 	Namval_t	*tq;
1322 	if(nv_type(np)==tp)
1323 		return(0);
1324 	if(nv_isarray(np) && (tq=nv_type(np)))
1325 	{
1326 		if(tp==tq)
1327 			return(0);
1328 		errormsg(SH_DICT,ERROR_exit(1),e_redef,nv_name(np));
1329 	}
1330 	if((ap=nv_arrayptr(np)) && ap->nelem>0)
1331 	{
1332 		nv_putsub(np,NIL(char*),ARRAY_SCAN);
1333 		ap->hdr.type = tp;
1334 		do
1335 		{
1336 			nv_arraysettype(np, tp, nv_getsub(np),flags);
1337 		}
1338 		while(nv_nextsub(np));
1339 	}
1340 	else if(ap || nv_isarray(np))
1341 	{
1342 		flags &= ~NV_APPEND;
1343 		if(!ap)
1344 		{
1345 			if(subshell)
1346 			{
1347 				sh_assignok(np,1);
1348 				shp->subshell = 0;
1349 			}
1350 			nv_putsub(np,"0",ARRAY_FILL);
1351 			ap = nv_arrayptr(np);
1352 			nelem = 1;
1353 
1354 		}
1355 	}
1356 	else
1357 #endif /*SHOPT_TYPEDEF */
1358 	{
1359 		if(isnull)
1360 			flags &= ~NV_APPEND;
1361 		else if(!nv_isvtree(np))
1362 		{
1363 			val = strdup(nv_getval(np));
1364 			if(!(flags&NV_APPEND))
1365 				_nv_unset(np, NV_RDONLY);
1366 		}
1367 		if(!nv_clone(tp,np,flags|NV_NOFREE))
1368 			return(0);
1369 	}
1370 	if(ap)
1371 	{
1372 		int nofree;
1373 		nv_disc(np,&ap->hdr,NV_POP);
1374 		np->nvalue.up = 0;
1375 		nv_clone(tp,np,flags|NV_NOFREE);
1376 		if(np->nvalue.cp && np->nvalue.cp!=Empty && !nv_isattr(np,NV_NOFREE))
1377 			free((void*)np->nvalue.cp);
1378 		np->nvalue.up = 0;
1379 		nofree = ap->hdr.nofree;
1380 		ap->hdr.nofree = 0;
1381 		ap->hdr.type = tp;
1382 		nv_disc(np, &ap->hdr, NV_FIRST);
1383 		ap->hdr.nofree = nofree;
1384 		nv_onattr(np,NV_ARRAY);
1385 		if(nelem)
1386 		{
1387 			ap->nelem++;
1388 			nv_putsub(np,"0",0);
1389 			_nv_unset(np,NV_RDONLY|NV_TYPE);
1390 			ap->nelem--;
1391 			shp->subshell = subshell;
1392 		}
1393 	}
1394 	type_init(np);
1395 	if(!rdonly)
1396 		nv_offattr(np,NV_RDONLY);
1397 	if(val)
1398 	{
1399 		nv_putval(np,val,NV_RDONLY);
1400 		free((void*)val);
1401 	}
1402 	return(0);
1403 }
1404 
1405 #define S(x)	#x
1406 #define FIELD(x,y)	{ S(y##x),	S(x##_t), offsetof(struct stat,st_##y##x) }
1407 typedef struct _field_
1408 {
1409 	char	*name;
1410 	char	*type;
1411 	int	offset;
1412 } Fields_t;
1413 
1414 Fields_t foo[]=
1415 {
1416 	FIELD(dev,),
1417 	FIELD(ino,),
1418 	FIELD(nlink,),
1419 	FIELD(mode,),
1420 	FIELD(uid,),
1421 	FIELD(gid,),
1422 	FIELD(size,),
1423 	FIELD(time,a),
1424 	FIELD(time,m),
1425 	FIELD(time,c),
1426 #if 0
1427 	FIELD(blksize,),
1428 	FIELD(blocks,),
1429 #endif
1430 	0
1431 };
1432 
1433 
1434 Namval_t *nv_mkstruct(const char *name, int rsize, Fields_t *fields)
1435 {
1436 	Namval_t	*mp, *nq, *nr, *tp;
1437 	Fields_t	*fp;
1438 	Namtype_t	*dp, *pp;
1439 	char		*cp, *sp;
1440 	int		nnodes=0, offset=staktell(), n, r, i, j;
1441 	size_t		m, size=0;
1442 	stakputs(NV_CLASS);
1443 	stakputc('.');
1444 	r = staktell();
1445 	stakputs(name);
1446 	stakputc(0);
1447 	mp = nv_open(stakptr(offset), sh.var_tree, NV_VARNAME);
1448 	stakseek(r);
1449 
1450 	for(fp=fields; fp->name; fp++)
1451 	{
1452 		m = strlen(fp->name)+1;
1453 		size += m;
1454 		nnodes++;
1455 		if(memcmp(fp->type,"typeset",7))
1456 		{
1457 			stakputs(fp->type);
1458 			stakputc(0);
1459 			tp = nv_open(stakptr(offset), sh.var_tree, NV_VARNAME|NV_NOADD|NV_NOFAIL);
1460 			stakseek(r);
1461 			if(!tp)
1462 				errormsg(SH_DICT,ERROR_exit(1),e_unknowntype,strlen(fp->type),fp->type);
1463 			if(dp = (Namtype_t*)nv_hasdisc(tp,&type_disc))
1464 			{
1465 				nnodes += dp->numnodes;
1466 				if((i=dp->strsize) < 0)
1467 					i = -i;
1468 				size += i + dp->numnodes*m;
1469 			}
1470 		}
1471 	}
1472 	pp = newof(NiL,Namtype_t, 1,  nnodes*NV_MINSZ + rsize + size);
1473 	pp->fun.dsize = sizeof(Namtype_t)+nnodes*NV_MINSZ +rsize;
1474 	pp->fun.type = mp;
1475 	pp->np = mp;
1476 	pp->childfun.fun.disc = &chtype_disc;
1477 	pp->childfun.fun.nofree = 1;
1478 	pp->childfun.ttype = pp;
1479 	pp->childfun.ptype = pp;
1480 	pp->fun.disc = &type_disc;
1481 	pp->nodes = (char*)(pp+1);
1482 	pp->numnodes = nnodes;
1483 	pp->strsize = size;
1484 	pp->data = pp->nodes + nnodes*NV_MINSZ;
1485 	cp = pp->data + rsize;
1486 	for(i=0,fp=fields; fp->name; fp++)
1487 	{
1488 		nq = nv_namptr(pp->nodes,i++);
1489 		nq->nvname = cp;
1490 		nq->nvalue.cp = pp->data + fp->offset;
1491 		nv_onattr(nq,NV_MINIMAL|NV_NOFREE);
1492 		m = strlen(fp->name)+1;
1493 		memcpy(cp, fp->name, m);
1494 		cp += m;
1495 		if(memcmp(fp->type,"typeset",7))
1496 		{
1497 			stakputs(fp->type);
1498 			stakputc(0);
1499 			tp = nv_open(stakptr(offset), sh.var_tree, NV_VARNAME);
1500 			stakseek(r);
1501 			clone_all_disc(tp,nq,NV_RDONLY);
1502 			nq->nvflag = tp->nvflag|NV_MINIMAL|NV_NOFREE;
1503 			nq->nvsize = tp->nvsize;
1504 			if(dp = (Namtype_t*)nv_hasdisc(nq,&type_disc))
1505 				dp->strsize = -dp->strsize;
1506 			if(dp = (Namtype_t*)nv_hasdisc(tp,&type_disc))
1507 			{
1508 				if(nv_hasdisc(nq,&chtype_disc))
1509 					nv_disc(nq, &pp->childfun.fun, NV_LAST);
1510 				sp = (char*)nq->nvalue.cp;
1511 				memcpy(sp, dp->data, nv_size(tp));
1512 				for(j=0; j < dp->numnodes; j++)
1513 				{
1514 					nr = nv_namptr(dp->nodes,j);
1515 					nq = nv_namptr(pp->nodes,i++);
1516 					nq->nvname = cp;
1517 					memcpy(cp,fp->name,m);
1518 					cp[m-1] = '.';
1519 					cp += m;
1520 					n = strlen(nr->nvname)+1;
1521 					memcpy(cp,nr->nvname,n);
1522 					cp += n;
1523 					if(nr->nvalue.cp>=dp->data && nr->nvalue.cp < (char*)pp + pp->fun.dsize)
1524 					{
1525 						nq->nvalue.cp = sp + (nr->nvalue.cp-dp->data);
1526 					}
1527 					nq->nvflag = nr->nvflag;
1528 					nq->nvsize = nr->nvsize;
1529 				}
1530 			}
1531 		}
1532 		else if(strmatch(fp->type+7,"*-*i*")==0)
1533 		{
1534 			nv_onattr(nq,NV_NOFREE|NV_RDONLY|NV_INTEGER);
1535 			if(strmatch(fp->type+7,"*-*s*")==0)
1536 				nv_onattr(nq,NV_INT16P);
1537 			else if(strmatch(fp->type+7,"*-*l*")==0)
1538 				nv_onattr(nq,NV_INT64);
1539 			if(strmatch(fp->type+7,"*-*u*")==0)
1540 				nv_onattr(nq,NV_UNSIGN);
1541 		}
1542 
1543 	}
1544 	stakseek(offset);
1545 	nv_onattr(mp,NV_RDONLY|NV_NOFREE|NV_BINARY);
1546 	nv_setsize(mp,rsize);
1547 	nv_disc(mp, &pp->fun, NV_LAST);
1548 	mp->nvalue.cp = pp->data;
1549 	nv_newtype(mp);
1550 	return(mp);
1551 }
1552 
1553 static void put_stat(Namval_t* np, const char* val, int flag, Namfun_t* nfp)
1554 {
1555 	if(val)
1556 	{
1557 		if(stat(val,(struct stat*)np->nvalue.cp)<0)
1558 			sfprintf(sfstderr,"stat of %s failed\n",val);
1559 		return;
1560 	}
1561 	nv_putv(np,val,flag,nfp);
1562 	nv_disc(np,nfp,NV_POP);
1563 	if(!(nfp->nofree&1))
1564 		free((void*)nfp);
1565 }
1566 
1567 static const Namdisc_t stat_disc =
1568 {
1569         0,
1570         put_stat
1571 };
1572 
1573 
1574 void nv_mkstat(void)
1575 {
1576 	Namval_t *tp;
1577 	Namfun_t *fp;
1578 	tp = nv_mkstruct("stat_t", sizeof(struct stat), foo);
1579 	nv_offattr(tp,NV_RDONLY);
1580 	nv_setvtree(tp);
1581 	fp = newof(NiL,Namfun_t,1,0);
1582 	fp->type = tp;
1583 	fp->disc = &stat_disc;
1584 	nv_disc(tp,fp,NV_FIRST);
1585 	nv_putval(tp,e_devnull,0);
1586 	nv_onattr(tp,NV_RDONLY);
1587 }
1588 
1589 static void write_indent(Sfio_t *out,char *str,int n,int indent)
1590 {
1591 	register int	c, first=1;
1592 	register char	*cp = str;
1593 	while(n-- && (c = *str++))
1594 	{
1595 		if(c=='\n')
1596 		{
1597 			if(!first)
1598 				sfnputc(out,'\t',indent);
1599 			first = 0;
1600 			sfwrite(out,cp,str-cp);
1601 			cp = str;
1602 		}
1603 	}
1604 	if(cp > str)
1605 	{
1606 		sfnputc(out,'\t',indent);
1607 		sfwrite(out,cp,str-cp);
1608 	}
1609 }
1610 
1611 int	sh_outtype(Shell_t *shp,Sfio_t *out)
1612 {
1613 	Namval_t	node,*mp,*tp;
1614 	Dt_t		*dp;
1615 	char		*cp,*sp,*xp,nvtype[sizeof(NV_CLASS)];
1616 	Sfio_t		*iop=0;
1617 	int		n=0,indent = 0;
1618 	if(cp=shp->prefix)
1619 	{
1620 		indent=1;
1621 		while(*cp)
1622 		{
1623 			if(*cp++ =='.')
1624 				indent++;
1625 		}
1626 		n = cp-shp->prefix+1;
1627 	}
1628 	strcpy(nvtype,NV_CLASS);
1629 	if(!(mp = nv_open(nvtype, shp->var_base,NV_NOADD|NV_VARNAME)))
1630 		return(0);
1631 	memcpy(&node,L_ARGNOD,sizeof(node));
1632 	L_ARGNOD->nvfun = 0;
1633 	L_ARGNOD->nvalue.cp = 0;
1634 	dp  = 	nv_dict(mp);
1635 	if(indent==0)
1636 	for(tp = (Namval_t*)dtfirst(dp); tp; tp = (Namval_t*)dtnext(dp,tp))
1637 	{
1638 		if(!nv_search(tp->nvname,shp->bltin_tree,0))
1639 			continue;
1640 		sfprintf(out,"typeset -T %s\n",tp->nvname);
1641 	}
1642 	for(tp = (Namval_t*)dtfirst(dp); tp; tp = (Namval_t*)dtnext(dp,tp))
1643 	{
1644 		if(nv_isnull(tp))
1645 			continue;
1646 		if(indent && (memcmp(tp->nvname,shp->prefix,n-1) || tp->nvname[n-1]!='.' || strchr(tp->nvname+n,'.')))
1647 			continue;
1648 		nv_settype(L_ARGNOD,tp,0);
1649 		if(indent)
1650 			sfnputc(out,'\t',indent);
1651 		sfprintf(out,"typeset -T %s=",tp->nvname+n);
1652 		shp->last_table = 0;
1653 		cp = nv_getval(L_ARGNOD);
1654 		if(indent)
1655 			write_indent(out,cp,strlen(cp)-1,indent);
1656 		else
1657 			sfprintf(out,"%.*s",strlen(cp)-1,cp);
1658 		_nv_unset(L_ARGNOD,NV_RDONLY);
1659 		for(sp=0; sp=nv_setdisc(tp,(char*)0,(Namval_t*)sp,(Namfun_t*)tp);)
1660 		{
1661 			mp = (Namval_t*)nv_setdisc(tp,sp,tp,(Namfun_t*)tp);
1662 			if(!mp || mp==tp)
1663 				continue;
1664 			if(cp=strrchr(mp->nvname,'.'))
1665 				cp++;
1666 			else
1667 				cp = mp->nvname;
1668 			if(indent)
1669 				sfnputc(out,'\t',indent);
1670 			if(nv_isattr(mp,NV_FPOSIX))
1671 				sfprintf(out,"\t%s()",cp);
1672 			else
1673 				sfprintf(out,"\tfunction %s",cp);
1674 			xp = 0;
1675 			if(mp->nvalue.ip && mp->nvalue.rp->hoffset>=0)
1676 			{
1677 				if(nv_isattr(mp,NV_FTMP))
1678 					iop = shp->heredocs;
1679 				else if(xp=mp->nvalue.rp->fname)
1680 					iop = sfopen(iop,xp,"r");
1681 				else if(shp->gd->hist_ptr)
1682 					iop = (shp->gd->hist_ptr)->histfp;
1683 				if(iop && sfseek(iop,(Sfoff_t)mp->nvalue.rp->hoffset,SEEK_SET)>=0)
1684 					sfmove(iop,out, nv_size(mp), -1);
1685 				else
1686 					sfputc(iop,'\n');
1687 				if(xp)
1688 					sfclose(iop);
1689 				if(nv_isattr(mp,NV_STATICF|NV_TAGGED))
1690 				{
1691 					if(indent)
1692 						sfnputc(out,'\t',indent);
1693 					sfwrite(out,"\ttypeset -f",11);
1694 					if(nv_isattr(mp,NV_STATICF))
1695 						sfputc(out,'S');
1696 					if(nv_isattr(mp,NV_TAGGED))
1697 						sfputc(out,'t');
1698 					if(mp->nvalue.rp->help)
1699 						sfprintf(out,"h '%s'",mp->nvalue.rp->help);
1700 					sfprintf(out," %s\n",cp);
1701 				}
1702 				iop = 0;
1703 			}
1704 		}
1705 		if(indent)
1706 			sfnputc(out,'\t',indent);
1707 		sfwrite(out,")\n",2);
1708 	}
1709 	dtdelete(shp->var_base,L_ARGNOD);
1710 	memcpy(L_ARGNOD,&node,sizeof(node));
1711 	dtinsert(shp->var_base,L_ARGNOD);
1712 	return(0);
1713 }
1714