1 /* res.cxx
2      $Id: res.cxx,v 1.16 1998/10/21 21:13:57 elf Exp $
3 
4    written by Marc Singer
5    14 May 1997
6 
7    This file is part of the project XO.  See the file README for
8    more information.
9 
10    Copyright (C) 1997 Marc Singer
11 
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License as
14    published by the Free Software Foundation; either version 2 of the
15    License, or (at your option) any later version.
16 
17    This program is distributed in the hope that it will be useful, but
18    WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20    General Public License for more details.
21 
22    You should have received a copy of the GNU General Public License
23    in a file called COPYING along with this program; if not, write to
24    the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
25    02139, USA.
26 
27    -----------
28    DESCRIPTION
29    -----------
30 
31 */
32 
33 //#define USE_TEST
34 
35 #include "standard.h"
36 #include "ctype.h"
37 #if defined (HAVE_FCNTL_H)
38 # include "fcntl.h"
39 #endif
40 #if defined (HAVE_MEMORY_H)
41 # include "memory.h"
42 #endif
43 
44 #define IS_PARSER
45 #include "lres.h"
46 #include "res_y.hxx"
47 
48 #define LONG_PRIME_MAX		(LONG_MAX/10)
49 #define LONG_PRIME_MAX_REM	(LONG_MAX - LONG_PRIME_MAX*10)
50 //#define TEST_LEXER
51 
52 #define CB_NODES		(0x10000)
53 
54 #define DPRINTF(a)	printf a
55 
56 void* LResNode::g_pvBase;
57 void* LResNode::g_pvAlloc;
58 int*  LResNode::g_piResource;
59 
alloc(int cb,void * pv)60 void* LResNode::alloc (int cb, void* pv)
61 {
62   if (!g_pvBase) {
63     g_pvBase = g_pvAlloc = malloc (CB_NODES);
64     if (!g_pvBase)
65       return NULL;
66     g_piResource = (int*) alloc (sizeof (int), NULL);
67   }
68 
69   void* pvNew = g_pvAlloc;
70   if (pv)
71     memcpy (pvNew, pv, cb);
72   else
73     memset (pvNew, 0, cb);
74   cb = ((cb + 3) & ~3);
75 
76   g_pvAlloc = (unsigned8*) g_pvAlloc + cb;
77   return pvNew;
78 }
79 
80 
81 /* safe_atol
82 
83    converts a string to a long in a safe manner, checking for errors.
84 
85 */
86 
safe_atol(char * sz,signed long * pl)87 int safe_atol (char* sz, signed long* pl)
88 {
89   if (sz[0] == '0' && tolower (sz[1]) == 'x') {
90     unsigned long l = 0;
91     if (strlen (sz) > 10)
92       return 1;			// Overflow on unsigned
93     for (sz += 2; *sz; ++sz) {
94       switch (tolower (*sz)) {
95       case '0':
96       case '1':
97       case '2':
98       case '3':
99       case '4':
100       case '5':
101       case '6':
102       case '7':
103       case '8':
104       case '9':
105 	l = (l << 4) + (*sz - '0');
106 	break;
107       case 'a':
108       case 'b':
109       case 'c':
110       case 'd':
111       case 'e':
112       case 'f':
113 	l = (l << 4) + (tolower (*sz) - 'a' + 10);
114 	break;
115       default:
116 //	assert (false);
117 	break;
118       }  /* switch */
119     }  /* for */
120     *pl = (long) l;
121   }  /* if */
122   else {
123     register long l = 0;
124     int fSigned = 0;
125 
126     for (; *sz; ++sz) {
127       switch (*sz) {
128       case '0':
129       case '1':
130       case '2':
131       case '3':
132       case '4':
133       case '5':
134       case '6':
135       case '7':
136       case '8':
137       case '9':
138 	/*      DPRINTF (("%d %d %d %d\n", l, *sz - '0', LONG_PRIME_MAX,
139 		LONG_PRIME_MAX_REM)); */
140 	if (       l         <  LONG_PRIME_MAX
141 	    || (   l         == LONG_PRIME_MAX
142 		&& *sz - '0' <  LONG_PRIME_MAX_REM))
143 	  l = l*10 + *sz - '0';
144 	else
145 	  return 1;		/* Overflow on unsigned number */
146 	break;
147 
148       case '-':
149 	fSigned = 1;
150       case '+':
151 	break;
152       }  /* switch */
153     }  /* while */
154     if (fSigned && l > LONG_MAX - 1)
155       return 2;			/* Overflow on signed number */
156     *pl = fSigned ? -l : l;
157   }  /* else */
158   return 0;
159 }  /* safe_atol */
160 
161 
safe_atof(char * sz,float * pr)162 int safe_atof (char* sz, float* pr)
163 {
164   *pr = atof (sz);
165   return 0;
166 }
167 
168 
is_invocation(const char * szKeyword,int id)169 bool LResNode::is_invocation (const char* szKeyword, int id)
170 {
171   return (   m_type						== INVOCATION
172 	  && m_v.pNode
173 	  && m_v.pNode->m_type					== KEYWORD
174 	  && m_v.pNode->m_v.sz
175 	  && !strcasecmp (m_v.pNode->m_v.sz, szKeyword)
176 	  && m_v.pNode->m_pNode
177 	  && m_v.pNode->m_pNode->m_type				== ARGLIST
178 	  && m_v.pNode->m_pNode->m_v.pNode
179 	  && m_v.pNode->m_pNode->m_v.pNode->m_type		== INTEGER
180 	  && m_v.pNode->m_pNode->m_v.pNode->m_v.l		== id
181 	  );
182 }
183 
184 
integer(int * pi)185 bool LResNode::integer (int* pi)
186 {
187   if (m_type != INTEGER)
188     return false;
189   *pi = m_v.l;
190   return true;
191 }
192 
real(float * pr)193 bool LResNode::real (float* pr)
194 {
195   if (m_type != REAL)
196     return false;
197   *pr = m_v.r;
198   return true;
199 }
200 
string(char ** psz)201 bool LResNode::string (char** psz)
202 {
203   if (m_type != STRING)
204     return false;
205   *psz = m_v.sz;
206   return true;
207 }
208 
209 
210 /* find_resources
211 
212    returns a pointer to the first invocation contained by this
213    invocation.  This function recurses into the current invocation.
214 
215 */
216 
find_resources(void)217 LResNode* LResNode::find_resources (void)
218 {
219   if (m_type != INVOCATION)
220     return NULL;
221   for (LResNode* pNode = m_v.pNode; pNode; pNode = pNode->m_pNode)
222     if (pNode->m_type == RESOURCE)
223       return pNode->m_v.pNode;
224   return NULL;
225 }
226 
227 /* LResNode::keyword
228 
229    returns a pointer to the keyword string for the invocation.  The
230    return value is NULL if the node is not an invocation.
231 
232 */
233 
keyword(void)234 char* LResNode::keyword (void)
235 {
236   if (    m_type != INVOCATION
237       || !m_v.pNode
238       ||  m_v.pNode->m_type != KEYWORD)
239     return NULL;
240   return m_v.pNode->m_v.sz;
241 }
242 
243 
244 /* LResNode::locate
245 
246    locates a typed resource with the given ID.  It is required that
247    all resources have the ID as the first argument if they are found
248    with this primitive.
249 
250 */
251 
locate(const char * szInvocation,int id)252 LResNode* LResNode::locate (const char* szInvocation, int id)
253 {
254   LResNode* pNode = (LResNode*) ((unsigned8*) g_pvBase + *g_piResource);
255   if (pNode->m_v.pNode)
256     pNode = pNode->m_v.pNode;
257 
258   for (; pNode; pNode = pNode->m_pNode) {
259     if (pNode->is_invocation (szInvocation, id))
260       return pNode;
261   }
262 
263   return NULL;
264 }
265 
266 
267 /* LResNode::enum_args
268 
269    enumerates the argument vector for an invocation.  On the first
270    call, pass the pointer to the invocation.  For successive calls,
271    pass the pointer returned by the function.
272 
273 */
274 
275 
enum_args(void)276 LResNode* LResNode::enum_args (void)
277 {
278   if (!this)
279     return NULL;
280 				// Initial case
281   if (m_type == INVOCATION) {
282     for (LResNode* pNode = m_v.pNode; pNode; pNode = pNode->m_pNode)
283       if (pNode->m_type == ARGLIST)
284 	return pNode->m_v.pNode;
285     return NULL;
286   }
287 
288 				// Iterative case
289   return m_pNode;
290 }
291 
292 
emit(int index)293 void LResNode::emit (int index)
294 {
295 /*   DPRINTF (("[emit 0x%x]", (char*) pNode - (char*) g_pvNodeAlloc)); */
296 
297   switch (m_type) {
298   case ARGLIST:
299     DPRINTF ((" ("));
300     for (LResNode* pNode = m_v.pNode; pNode; pNode = pNode->m_pNode)
301       pNode->emit (index + 1);
302     DPRINTF ((" )"));
303     break;
304   case INVOCATION:
305     DPRINTF (("%*.*s", index, index, ""));
306     for (LResNode* pNode = m_v.pNode; pNode; pNode = pNode->m_pNode)
307       pNode->emit (index + 1);
308     DPRINTF (("\n"));
309     break;
310   case KEYWORD:
311     DPRINTF (("%s", m_v.sz));
312     break;
313   case INTEGER:
314     DPRINTF ((" %ld", m_v.l));
315     break;
316   case REAL:
317     DPRINTF ((" %g", m_v.r));
318     break;
319   case STRING:
320     DPRINTF ((" \"%s\"", m_v.sz));
321     break;
322   case RESOURCE:
323     if (index > 0)
324       DPRINTF ((" {\n"));
325     for (LResNode* pNode = m_v.pNode; pNode; pNode = pNode->m_pNode) {
326       pNode->emit (index + 1);
327 /*      DPRINTF (("[ in resource 0x%x=>0x%x]",
328 	      (char*) pNode - (char*) g_pvNodeAlloc,
329 	      (char*) pNode->m_pNode - (char*) g_pvNodeAlloc));
330 */
331     }
332     if (index > 0)
333       DPRINTF (("%*.*s}", index - 1, index - 1, ""));
334     break;
335   }
336 }
337 
generate(LResNode * pNode)338 void LResNode::generate (LResNode* pNode)
339 {
340   *g_piResource = (unsigned8*) pNode - (unsigned8*) g_pvBase;
341 
342 				// Emit all of the resources
343   //  for (; pNode; pNode = pNode->m_pNode)
344   //    emit (pNode, -1);
345 
346   DPRINTF (("resource templates require %d bytes\n",
347 	    int ((unsigned8*) g_pvAlloc - (unsigned8*) g_pvBase)));
348 
349   {
350     int fh = open ("resources", O_WRONLY | O_CREAT | O_TRUNC, 0660);
351     write (fh, g_pvBase, (unsigned8*) g_pvAlloc - (unsigned8*) g_pvBase);
352     close (fh);
353   }
354 
355   //  pNode = locate ("menu", 1);
356   //  pNode->emit (0);
357 }
358 
359 
360 /* _fixup
361 
362    reverses the order of the argument and resource lists.  These lists
363    are collected in reverse order because of the way that the parser
364    reduces the grammer.  Yes, we could fix it, but the overhead of
365    reordering is low.
366 
367    Note that the invocation itself is not reordered, but it must be
368    enumerated in order to reorder the argument list, the possible
369    resource list.
370 
371 */
372 
_fixup(void)373 void LResNode::_fixup (void)
374 {
375   switch (m_type) {
376   default:
377     break;
378   case ARGLIST:
379   case RESOURCE:
380     {
381       LResNode* pNodeNext = NULL;
382       LResNode* pNodeLink;
383       for (LResNode* pNodeStep = m_v.pNode; pNodeStep;
384 	   pNodeStep = pNodeLink) {
385 	pNodeLink = pNodeStep->m_pNode;
386 	pNodeStep->m_pNode = pNodeNext;
387 	pNodeNext = pNodeStep;
388       }
389       m_v.pNode = pNodeNext;
390     }
391   case INVOCATION:
392     for (LResNode* pNode = m_v.pNode; pNode; pNode = pNode->m_pNode)
393       pNode->_fixup ();
394     break;
395   }
396 }
397 
398 
fixup(LResNode * pNode)399 void LResNode::fixup (LResNode* pNode)
400 {
401   for (; pNode; pNode = pNode->m_pNode)
402     pNode->_fixup ();
403 }
404 
405 /* as_bool
406 
407    returns a boolean value interpretation for a X Resource Manager
408    value.  This is a convenience feature to make the specification of
409    booleans simple.  The default is returned if the value is not
410    valid.
411 
412 */
413 
as_bool(const XrmValue & value,bool fDefault)414 bool as_bool (const XrmValue& value, bool fDefault)
415 {
416   if (!value.addr || !value.size)
417     return fDefault;
418 
419   if (   *value.addr == 't' || *value.addr == 'T'
420       || *value.addr == 'y' || *value.addr == 'Y'
421       || *value.addr == '1')
422     return true;
423   if (   *value.addr == 'f' || *value.addr == 'F'
424       || *value.addr == 'n' || *value.addr == 'N'
425       || *value.addr == '0')
426     return false;
427   return fDefault;
428 }
429 
430 
yywrap(void)431 extern "C" int yywrap (void)
432 {
433   return 1;
434 }  /* yywrap */
435 
436 
437 #if defined (USE_TEST)
438 
main(int,char **)439 int main (int /* argc */, char** /* argv */)
440 {
441   yyin = stdin;
442 
443 #if defined (TEST_LEXER)
444   int result;
445   while (result = yylex ()) {
446     switch (result) {
447     case KEYWORD:
448       DPRINTF ((" <%s>", yylval.sz));
449       break;
450     case PO:
451       DPRINTF ((" PO"));
452       break;
453     case PC:
454       DPRINTF ((" PC\n"));
455       break;
456     case BO:
457       DPRINTF ((" BO\n"));
458       break;
459     case BC:
460       DPRINTF ((" BC\n"));
461       break;
462     case QUOTE:
463       DPRINTF ((" QUOTE"));
464       break;
465     case INTEGER:
466       DPRINTF ((" INTEGER %d", yylval.l));
467       break;
468     case REAL:
469       DPRINTF ((" REAL"));
470       break;
471     case STRING:
472       DPRINTF ((" STRING '%s'", yylval.sz));
473       break;
474     case ERROR:
475       DPRINTF ((" ERROR\n"));
476       break;
477     default:
478       if (isprint (result))
479 	DPRINTF ((" '%c'", result));
480       else
481 	DPRINTF ((" [%d]", result));
482       break;
483     }
484   }
485 #else
486 
487   yyparse ();
488 #endif
489 
490 #if 0
491   LResNode* pNodeMenu = LResNode::locate ("dialog", 1);
492   for (LResNode* pNode = pNodeMenu->find_resources (); pNode;
493        pNode = pNode->next ()) {
494     pNode->emit (1);
495     for (LResNode* pNodeArg = pNode; pNodeArg = pNodeArg->enum_args (); ) {
496       pNodeArg->emit (3);
497     }
498   }
499 #endif
500 
501 }
502 
503 #endif
504