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 #include <shell.h>
22
23 static const char enum_usage[] =
24 "[-?@(#)$Id: enum (AT&T Research) 2008-01-08 $\n]"
25 USAGE_LICENSE
26 "[+NAME?enum - create an enumeration type]"
27 "[+DESCRIPTION?\benum\b is a declaration command that creates an enumeration "
28 "type \atypename\a that can only store any one of the values in the indexed "
29 "array variable \atypename\a.]"
30 "[+?If the list of \avalue\as is omitted, then \atypename\a must name an "
31 "indexed array variable with at least two elements.]"
32 "[i:ignorecase?The values are case insensitive.]"
33 "\n"
34 "\n\atypename\a[\b=(\b \avalue\a ... \b)\b]\n"
35 "\n"
36 "[+EXIT STATUS]"
37 "{"
38 "[+0?Successful completion.]"
39 "[+>0?An error occurred.]"
40 "}"
41 "[+SEE ALSO?\bksh\b(1), \btypeset\b(1).]"
42 ;
43
44 static const char enum_type[] =
45 "[-1c?\n@(#)$Id: type (AT&T Labs Research) 2008-01-08 $\n]"
46 USAGE_LICENSE
47 "[+NAME?\f?\f - create an instance of type \b\f?\f\b]"
48 "[+DESCRIPTION?\b\f?\f\b creates a variable for each \aname\a with "
49 "enumeration type \b\f?\f\b where \b\f?\f\b is a type that has been "
50 "created with the \benum\b(1) command.]"
51 "[+?The variable can have one of the following values\fvalues\f. "
52 "The the values are \fcase\fcase sensitive.]"
53 "[+?If \b=\b\avalue\a is omitted, the default is \fdefault\f.]"
54 "[+?If no \aname\as are specified then the names and values of all "
55 "variables of this type are written to standard output.]"
56 "[+?\b\f?\f\b is built-in to the shell as a declaration command so that "
57 "field splitting and pathname expansion are not performed on "
58 "the arguments. Tilde expansion occurs on \avalue\a.]"
59 "[r?Enables readonly. Once enabled, the value cannot be changed or unset.]"
60 "[a?index array. Each \aname\a will converted to an index "
61 "array of type \b\f?\f\b. If a variable already exists, the current "
62 "value will become index \b0\b.]"
63 "[A?Associative array. Each \aname\a will converted to an associate "
64 "array of type \b\f?\f\b. If a variable already exists, the current "
65 "value will become subscript \b0\b.]"
66 "[h]:[string?Used within a type definition to provide a help string "
67 "for variable \aname\a. Otherwise, it is ignored.]"
68 "[S?Used with a type definition to indicate that the variable is shared by "
69 "each instance of the type. When used inside a function defined "
70 "with the \bfunction\b reserved word, the specified variables "
71 "will have function static scope. Otherwise, the variable is "
72 "unset prior to processing the assignment list.]"
73 #if 0
74 "[p?Causes the output to be in a form of \b\f?\f\b commands that can be "
75 "used as input to the shell to recreate the current type of "
76 "these variables.]"
77 #endif
78 "\n"
79 "\n[name[=value]...]\n"
80 "\n"
81 "[+EXIT STATUS?]{"
82 "[+0?Successful completion.]"
83 "[+>0?An error occurred.]"
84 "}"
85
86 "[+SEE ALSO?\benum\b(1), \btypeset\b(1)]"
87 ;
88
89 struct Enum
90 {
91 Namfun_t hdr;
92 short nelem;
93 short iflag;
94 const char *values[1];
95 };
96
enuminfo(Opt_t * op,Sfio_t * out,const char * str,Optdisc_t * fp)97 static int enuminfo(Opt_t* op, Sfio_t *out, const char *str, Optdisc_t *fp)
98 {
99 Namval_t *np;
100 struct Enum *ep;
101 int n=0;
102 const char *v;
103 np = *(Namval_t**)(fp+1);
104 ep = (struct Enum*)np->nvfun;
105 if(strcmp(str,"default")==0)
106 #if 0
107 sfprintf(out,"\b%s\b%c",ep->values[0],0);
108 #else
109 sfprintf(out,"\b%s\b",ep->values[0]);
110 #endif
111 else if(strcmp(str,"case")==0)
112 {
113 if(ep->iflag)
114 sfprintf(out,"not ");
115 }
116 else while(v=ep->values[n++])
117 {
118 sfprintf(out,", \b%s\b",v);
119 }
120 return(0);
121 }
122
clone_enum(Namval_t * np,Namval_t * mp,int flags,Namfun_t * fp)123 static Namfun_t *clone_enum(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
124 {
125 struct Enum *ep, *pp=(struct Enum*)fp;
126 ep = newof(0,struct Enum,1,pp->nelem*sizeof(char*));
127 memcpy((void*)ep,(void*)pp,sizeof(struct Enum)+pp->nelem*sizeof(char*));
128 return(&ep->hdr);
129 }
130
put_enum(Namval_t * np,const char * val,int flags,Namfun_t * fp)131 static void put_enum(Namval_t* np,const char *val,int flags,Namfun_t *fp)
132 {
133 struct Enum *ep = (struct Enum*)fp;
134 register const char *v;
135 unsigned short i=0, n;
136 if(!val)
137 {
138 nv_putv(np, val, flags,fp);
139 nv_disc(np,&ep->hdr,NV_POP);
140 if(!ep->hdr.nofree)
141 free((void*)ep);
142 return;
143 }
144 if(flags&NV_INTEGER)
145 {
146 nv_putv(np,val,flags,fp);
147 return;
148 }
149 while(v=ep->values[i])
150 {
151 if(ep->iflag)
152 n = strcasecmp(v,val);
153 else
154 n = strcmp(v,val);
155 if(n==0)
156 {
157 nv_putv(np, (char*)&i, NV_UINT16, fp);
158 return;
159 }
160 i++;
161 }
162 if(nv_isattr(np,NV_NOFREE))
163 error(ERROR_exit(1), "%s: invalid value %s",nv_name(np),val);
164 }
165
get_enum(register Namval_t * np,Namfun_t * fp)166 static char* get_enum(register Namval_t* np, Namfun_t *fp)
167 {
168 static char buff[6];
169 struct Enum *ep = (struct Enum*)fp;
170 long n = nv_getn(np,fp);
171 if(n < ep->nelem)
172 return((char*)ep->values[n]);
173 sfsprintf(buff,sizeof(buff),"%u%c",n,0);
174 return(buff);
175 }
176
get_nenum(register Namval_t * np,Namfun_t * fp)177 static Sfdouble_t get_nenum(register Namval_t* np, Namfun_t *fp)
178 {
179 return(nv_getn(np,fp));
180 }
181
182 const Namdisc_t ENUM_disc = { 0, put_enum, get_enum, get_nenum, 0,0,clone_enum };
183
184 #ifdef STANDALONE
enum_create(int argc,char ** argv,Shbltin_t * context)185 static int enum_create(int argc, char** argv, Shbltin_t *context)
186 #else
187 int b_enum(int argc, char** argv, Shbltin_t *context)
188 #endif
189 {
190 int sz,i,n,iflag = 0;
191 Namval_t *np, *tp;
192 Namarr_t *ap;
193 char *cp,*sp;
194 struct Enum *ep;
195 Shell_t *shp = context->shp;
196 struct {
197 Optdisc_t opt;
198 Namval_t *np;
199 } optdisc;
200
201 cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
202 for (;;)
203 {
204 switch (optget(argv, enum_usage))
205 {
206 case 'i':
207 iflag = 'i';
208 continue;
209 case '?':
210 error(ERROR_USAGE|4, "%s", opt_info.arg);
211 break;
212 case ':':
213 error(2, "%s", opt_info.arg);
214 break;
215 }
216 break;
217 }
218 argv += opt_info.index;
219 if (error_info.errors || !*argv || *(argv + 1))
220 {
221 error(ERROR_USAGE|2, "%s", optusage(NiL));
222 return 1;
223 }
224 while(cp = *argv++)
225 {
226 if(!(np = nv_open(cp, (void*)0, NV_VARNAME|NV_NOADD)) || !(ap=nv_arrayptr(np)) || ap->fun || (sz=ap->nelem&(((1L<<ARRAY_BITS)-1))) < 2)
227 error(ERROR_exit(1), "%s must name an array containing at least two elements",cp);
228 n = staktell();
229 sfprintf(stkstd,"%s.%s%c",NV_CLASS,np->nvname,0);
230 tp = nv_open(stakptr(n), shp->var_tree, NV_VARNAME);
231 stakseek(n);
232 n = sz;
233 i = 0;
234 nv_onattr(tp, NV_UINT16);
235 nv_putval(tp, (char*)&i, NV_INTEGER);
236 nv_putsub(np, (char*)0, ARRAY_SCAN);
237 do
238 {
239 sz += strlen(nv_getval(np));
240 }
241 while(nv_nextsub(np));
242 sz += n*sizeof(char*);
243 if(!(ep = newof(0,struct Enum,1,sz)))
244 error(ERROR_system(1), "out of space");
245 ep->iflag = iflag;
246 ep->nelem = n;
247 cp = (char*)&ep->values[n+1];
248 nv_putsub(np, (char*)0, ARRAY_SCAN);
249 ep->values[n] = 0;
250 i = 0;
251 do
252 {
253 ep->values[i++] = cp;
254 sp = nv_getval(np);
255 n = strlen(sp);
256 memcpy(cp,sp,n+1);
257 cp += n+1;
258 }
259 while(nv_nextsub(np));
260 ep->hdr.dsize = sizeof(struct Enum)+sz;
261 ep->hdr.disc = &ENUM_disc;
262 ep->hdr.type = tp;
263 nv_onattr(tp, NV_RDONLY);
264 nv_disc(tp, &ep->hdr,NV_FIRST);
265 memset(&optdisc,0,sizeof(optdisc));
266 optdisc.opt.infof = enuminfo;
267 optdisc.np = tp;
268 nv_addtype(tp, enum_type, &optdisc.opt, sizeof(optdisc));
269 }
270 return error_info.errors != 0;
271 }
272
273 #ifdef STANDALONE
lib_init(int flag,void * context)274 void lib_init(int flag, void* context)
275 {
276 Shell_t *shp = ((Shbltin_t*)context)->shp;
277 Namval_t *mp,*bp;
278 if(flag)
279 return;
280 bp = sh_addbuiltin("Enum", enum_create, (void*)0);
281 mp = nv_search("typeset",shp->bltin_tree,0);
282 nv_onattr(bp,nv_isattr(mp,NV_PUBLIC));
283 }
284 #endif
285