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