|
|
1.1 root 1: %{
2: /*
3: * Set return status based on
4: * various specified conditions,
5: * mostly related to files.
6: * Used mostly in shell files.
7: */
8:
9: #include <stdio.h>
10: #include <sys/stat.h>
11: #include <access.h>
12: #include "testnode.h"
13:
14: #define NPRIM (sizeof(prims)/sizeof(prims[0]))
15: #define NFNAME 500 /* size of filename buffer */
16: %}
17: %start command
18:
19: %union {
20: NODE *nodeptr;
21: char *fname;
22: }
23:
24: %left OR
25: %left AND
26: %left '!'
27: %token _R _W _F _D _S _T _Z _N
28: %token SEQ SNEQ
29: %token _EQ _NE _GT _GE _LT _LE
30: %type <nodeptr> exp
31: %token <fname> STR
32: %%
33:
34: command:
35: exp '\n' { code = $1; return; }
36: ;
37:
38: exp:
39: '(' exp ')' { $$ = $2; }
40: | '!' exp { $$ = bnode('!', $2, NULL); }
41: | exp OR exp { $$ = bnode(OR, $1, $3); }
42: | exp AND exp { $$ = bnode(AND, $1, $3); }
43: | _R STR { $$ = lnode(xr, $2, NULL); }
44: | _W STR { $$ = lnode(xw, $2, NULL); }
45: | _F STR { $$ = lnode(xf, $2, NULL); }
46: | _D STR { $$ = lnode(xd, $2, NULL); }
47: | _S STR { $$ = lnode(xs, $2, NULL); }
48: | _T STR { $$ = lnode(xt, $2, NULL); }
49: | _T { $$ = lnode(xt, "1", NULL); }
50: | _Z STR { $$ = lnode(xz, $2, NULL); }
51: | _N STR { $$ = lnode(xn, $2, NULL); }
52: | STR SEQ STR { $$ = lnode(xseq, $1, $3); }
53: | STR SNEQ STR { $$ = lnode(xsneq, $1, $3); }
54: | STR _EQ STR { $$ = lnode(xeq, $1, $3); }
55: | STR _NE STR { $$ = lnode(xne, $1, $3); }
56: | STR _GT STR { $$ = lnode(xgt, $1, $3); }
57: | STR _GE STR { $$ = lnode(xge, $1, $3); }
58: | STR _LT STR { $$ = lnode(xlt, $1, $3); }
59: | STR _LE STR { $$ = lnode(xle, $1, $3); }
60: | STR { $$ = lnode(xn, $1, NULL); }
61: ;
62:
63: %%
64: struct prim {
65: char *p_name;
66: int p_lval;
67: int p_bin;
68: } prims[] = {
69: "-r", _R, 0,
70: "-w", _W, 0,
71: "-f", _F, 0,
72: "-d", _D, 0,
73: "-s", _S, 0,
74: "-t", _T, 0,
75: "-z", _Z, 0,
76: "-n", _N, 0,
77: "-eq", _EQ, 1,
78: "-ne", _NE, 1,
79: "-gt", _GT, 1,
80: "-ge", _GE, 1,
81: "-lt", _LT, 1,
82: "-le", _LE, 1,
83: "-o", OR, 1,
84: "-a", AND, 1
85: };
86:
87:
88: char **gav;
89: int gac;
90:
91: struct stat sb;
92:
93: NODE *code;
94:
95: char *next();
96: NODE *bnode();
97: NODE *lnode();
98: long atol();
99: int xr();
100: int xw();
101: int xf();
102: int xd();
103: int xs();
104: int xt();
105: int xz();
106: int xn();
107: int xseq();
108: int xsneq();
109: int xeq();
110: int xne();
111: int xgt();
112: int xge();
113: int xlt();
114:
115: main(argc, argv)
116: char *argv[];
117: {
118: gav = argv+1;
119: gac = argc-1;
120: if (argv[0][0]=='[' && argv[0][1]=='\0') {
121: if (strcmp(argv[gac], "]") != 0)
122: tsterr("unbalanced [..]");
123: gac--;
124: }
125: if (gac == 0)
126: exit(1);
127: yyparse();
128: exit(!execute(code));
129: }
130:
131: /*
132: * Lexical analyser
133: */
134: yylex()
135: {
136: static char laststr = 0; /* 1 if last token was string */
137: register char *ap;
138: register struct prim *pp;
139:
140: if ((yylval.fname = ap = next()) == NULL)
141: return ('\n');
142:
143: if (*ap == '-') {
144: for (pp = prims; pp < &prims[NPRIM]; pp++) {
145: if (strcmp(pp->p_name, ap) == 0) {
146: if (!laststr && pp->p_bin)
147: break;
148: laststr = 0;
149: return (pp->p_lval);
150: }
151: }
152: }
153: else {
154: laststr = 0;
155: if (strcmp("!=", ap) == 0)
156: return (SNEQ);
157:
158: if (ap[1] == '\0') {
159: if (ap[0]==')') {
160: laststr = 1;
161: return(')');
162: }
163: if (ap[0]=='(' || ap[0]=='!')
164: return (ap[0]);
165: if (ap[0]=='=')
166: return (SEQ);
167: }
168: }
169: laststr = 1;
170: return (STR);
171: }
172:
173: yyerror()
174: {
175: fprintf(stderr, "Test expression syntax error\n");
176: usage();
177: }
178:
179: /*
180: * Return the next argument from the arg list.
181: */
182: char *
183: next()
184: {
185: if (gac < 1)
186: return (NULL);
187: gac--;
188: return (*gav++);
189: }
190:
191: /*
192: * Build an expression tree node (non-leaf)
193: */
194: NODE *
195: bnode(op, left, right)
196: int op;
197: NODE *left, *right;
198: {
199: register NODE *np;
200: char *malloc();
201:
202: if ((np = (NODE *)malloc(sizeof (NODE))) == NULL)
203: tsterr("Out of space");
204: np->n_un.n_op = op;
205: np->n_left = left;
206: np->n_right = right;
207: return (np);
208: }
209:
210: /*
211: * Build a leaf node in expression tree.
212: */
213: NODE *
214: lnode(fn, str1, str2)
215: int (*fn)();
216: char *str1, *str2;
217: {
218: register NODE *np;
219: char *malloc();
220:
221: if ((np = (NODE *)malloc(sizeof (NODE))) == NULL)
222: tsterr("Out of space");
223: np->n_left = np->n_right = NULL;
224: np->n_un.n_fun = fn;
225: np->n_s1 = str1;
226: np->n_s2 = str2;
227: return (np);
228: }
229:
230: /*
231: * Execute compiled code.
232: */
233: execute(np)
234: register NODE *np;
235: {
236: if (np->n_left != NULL)
237: switch (np->n_un.n_op) {
238: case AND:
239: if (execute(np->n_left) && execute(np->n_right))
240: return (1);
241: return (0);
242:
243: case OR:
244: if (execute(np->n_left) || execute(np->n_right))
245: return (1);
246: return (0);
247:
248: case '!':
249: return (!execute(np->n_left));
250:
251: default:
252: tsterr("Panic: bad tree (op %d)", np->n_un.n_op);
253: }
254: else
255: return ((*np->n_un.n_fun)(np));
256: /* NOTREACHED */
257: }
258:
259: /*
260: * Check to see if the file exists
261: * and if readable.
262: */
263: xr(np)
264: NODE *np;
265: {
266: return (access(np->n_s1, AREAD) >= 0);
267: }
268:
269: /*
270: * Check if the file exists and is
271: * writeable.
272: */
273: xw(np)
274: NODE *np;
275: {
276: return (access(np->n_s1, AWRITE) >= 0);
277: }
278:
279: /*
280: * Check if the file exists and is not
281: * a directory.
282: */
283: xf(np)
284: NODE *np;
285: {
286: return (stat(np->n_s1, &sb)>=0 && (sb.st_mode&S_IFMT)!=S_IFDIR);
287: }
288:
289: /*
290: * Check to see if the file exists
291: * and is a directory.
292: */
293: xd(np)
294: NODE *np;
295: {
296: return (stat(np->n_s1, &sb)>=0 && (sb.st_mode&S_IFMT)==S_IFDIR);
297: }
298:
299: /*
300: * Check to see if the file exists
301: * and has a non-zero size.
302: */
303: xs(np)
304: NODE *np;
305: {
306: return (stat(np->n_s1, &sb)>=0 && sb.st_size>0);
307: }
308:
309: /*
310: * Check to see if the file
311: * descriptor is associated
312: * with a terminal.
313: */
314: xt(np)
315: NODE *np;
316: {
317: return (isatty(atoi(np->n_s1)));
318: }
319:
320: /*
321: * True if the length of the given
322: * string is zero.
323: */
324: xz(np)
325: NODE *np;
326: {
327: return (np->n_s1[0] == '\0');
328: }
329:
330: /*
331: * True if the length of the given
332: * string is non-zero.
333: */
334: xn(np)
335: NODE *np;
336: {
337: return (np->n_s1[0] != '\0');
338: }
339:
340: /*
341: * True if the two strings are
342: * lexicographically equal.
343: */
344: xseq(np)
345: register NODE *np;
346: {
347: return (strcmp(np->n_s1, np->n_s2) == 0);
348: }
349:
350: /*
351: * True if the two strings are
352: * lexicographically unequal.
353: */
354: xsneq(np)
355: register NODE *np;
356: {
357: return (strcmp(np->n_s1, np->n_s2) != 0);
358: }
359:
360: /*
361: * True if the two numbers are
362: * equal.
363: */
364: xeq(np)
365: register NODE *np;
366: {
367: return (atol(np->n_s1) == atol(np->n_s2));
368: }
369:
370: /*
371: * True if the two numbers are
372: * not equal.
373: */
374: xne(np)
375: register NODE *np;
376: {
377: return (atol(np->n_s1) != atol(np->n_s2));
378: }
379:
380: /*
381: * True if the first number is
382: * greater than the second.
383: */
384: xgt(np)
385: register NODE *np;
386: {
387: return (atol(np->n_s1) > atol(np->n_s2));
388: }
389:
390: /*
391: * True if the first number is
392: * greater than or equal to the second.
393: */
394: xge(np)
395: register NODE *np;
396: {
397: return (atol(np->n_s1) >= atol(np->n_s2));
398: }
399:
400: /*
401: * True if the first number is
402: * less than the second.
403: */
404: xlt(np)
405: register NODE *np;
406: {
407: return (atol(np->n_s1) < atol(np->n_s2));
408: }
409:
410: /*
411: * True if the first number is
412: * less than or equal to the second.
413: */
414: xle(np)
415: register NODE *np;
416: {
417: return (atol(np->n_s1) <= atol(np->n_s2));
418: }
419:
420: /*
421: * Error messages.
422: */
423: /* VARARGS */
424: tsterr(x)
425: {
426: fprintf(stderr, "test: %r\n", &x);
427: exit(1);
428: }
429:
430: usage()
431: {
432: fprintf(stderr, "Usage: test expression\n");
433: exit(1);
434: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.