1 | //
|
---|
2 | // ipv2_read.cc
|
---|
3 | //
|
---|
4 | // Copyright (C) 1996 Limit Point Systems, Inc.
|
---|
5 | //
|
---|
6 | // Author: Curtis Janssen <cljanss@limitpt.com>
|
---|
7 | // Maintainer: LPS
|
---|
8 | //
|
---|
9 | // This file is part of the SC Toolkit.
|
---|
10 | //
|
---|
11 | // The SC Toolkit is free software; you can redistribute it and/or modify
|
---|
12 | // it under the terms of the GNU Library General Public License as published by
|
---|
13 | // the Free Software Foundation; either version 2, or (at your option)
|
---|
14 | // any later version.
|
---|
15 | //
|
---|
16 | // The SC Toolkit is distributed in the hope that it will be useful,
|
---|
17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
19 | // GNU Library General Public License for more details.
|
---|
20 | //
|
---|
21 | // You should have received a copy of the GNU Library General Public License
|
---|
22 | // along with the SC Toolkit; see the file COPYING.LIB. If not, write to
|
---|
23 | // the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
---|
24 | //
|
---|
25 | // The U.S. Government is granted a limited license as per AL 91-7.
|
---|
26 | //
|
---|
27 |
|
---|
28 | /* This file provides the routines to do the initial parse of the input
|
---|
29 | * file. */
|
---|
30 |
|
---|
31 | #include <util/misc/string.h>
|
---|
32 | #ifdef DEC
|
---|
33 | #include <math.h>
|
---|
34 | #else
|
---|
35 | #include <stdlib.h>
|
---|
36 | #endif
|
---|
37 | #include <util/keyval/ipv2.h>
|
---|
38 |
|
---|
39 | #include <stdio.h>
|
---|
40 | #include <iostream>
|
---|
41 | #include <fstream>
|
---|
42 |
|
---|
43 | using namespace std;
|
---|
44 | using namespace sc;
|
---|
45 |
|
---|
46 | /* Initialize the ip routines. This involves parsing the entire file and
|
---|
47 | * converting it into an internal representation. */
|
---|
48 | /* in = the input file. */
|
---|
49 | /* out = the output file. */
|
---|
50 | void
|
---|
51 | IPV2::ip_initialize(istream&in,ostream&out)
|
---|
52 | {
|
---|
53 | ip_initialized = 1;
|
---|
54 |
|
---|
55 | ip_in = ∈
|
---|
56 | ip_out = &out;
|
---|
57 |
|
---|
58 | /* Just in case a scanner has already been running. */
|
---|
59 | lexer->switch_streams(ip_in, ip_out);
|
---|
60 |
|
---|
61 | /* If ip_tree is not NULL, then ip_initialize has been called twice,
|
---|
62 | * with a done inbetween. Call done now. */
|
---|
63 | if (ip_tree) {
|
---|
64 | warn("ip_initialize has been called twice without an IPV2::done");
|
---|
65 | done();
|
---|
66 | }
|
---|
67 |
|
---|
68 | sub_tree = ip_tree;
|
---|
69 |
|
---|
70 | yparse();
|
---|
71 |
|
---|
72 | /* The initial cwk list is nothing. */
|
---|
73 | cwk_clear();
|
---|
74 |
|
---|
75 | ip_internal_values();
|
---|
76 | }
|
---|
77 |
|
---|
78 | /* Continue adding to the ip_tree with, presumably, another input file.
|
---|
79 | * This should be called after ip_initialize has been called with different
|
---|
80 | * input file. Multiple calls to ip_append, with different input files,
|
---|
81 | * are allowed. */
|
---|
82 | /* in = the input file. */
|
---|
83 | /* out = the output file. */
|
---|
84 | void
|
---|
85 | IPV2::ip_append(istream&in,ostream&out)
|
---|
86 | {
|
---|
87 |
|
---|
88 | ip_in = ∈
|
---|
89 | ip_out = &out;
|
---|
90 |
|
---|
91 | lexer->switch_streams(ip_in, ip_out);
|
---|
92 |
|
---|
93 | if (sub_tree != NULL) {
|
---|
94 | error("ip_append: sub_tree != NULL - impossible");
|
---|
95 | }
|
---|
96 |
|
---|
97 | yparse();
|
---|
98 |
|
---|
99 | ip_internal_values();
|
---|
100 | }
|
---|
101 |
|
---|
102 | /* This routine can be called by the user after the ip routines have been
|
---|
103 | * initialized. It look for a prefix"dir" in the cwk list and a prefix"files"
|
---|
104 | * array. If prefix"dir" is found this concatenated with each of the
|
---|
105 | * prefix"files" that does not begin with a '/'. Each of these files is
|
---|
106 | * ip_append'ed to the set of inputs. */
|
---|
107 | void
|
---|
108 | IPV2::append_from_input(const char*prefix,ostream&outfile)
|
---|
109 | {
|
---|
110 | char keyword[KEYWORD_LENGTH];
|
---|
111 | const char *dir;
|
---|
112 | const char *file;
|
---|
113 | char dirfile[512];
|
---|
114 | int i,nfile;
|
---|
115 |
|
---|
116 | /* Get the prefix. */
|
---|
117 | strcpy(keyword,prefix);
|
---|
118 | strcat(keyword,"dir");
|
---|
119 | if (value(keyword,&dir,0) != OK) dir = NULL;
|
---|
120 |
|
---|
121 | strcpy(keyword,prefix);
|
---|
122 | strcat(keyword,"files");
|
---|
123 | if (count(keyword,&nfile,0)!=OK) return;
|
---|
124 | for (i=0; i<nfile; i++) {
|
---|
125 | if (value_v(keyword,&file,1,&i) == OK) {
|
---|
126 | if (dir && (file[0] != '/')) strcpy(dirfile,dir);
|
---|
127 | else dirfile[0] = '\0';
|
---|
128 | strcat(dirfile,file);
|
---|
129 | ifstream infile(dirfile, ios::in);
|
---|
130 | if (infile.bad()) {
|
---|
131 | ExEnv::errn() << "WARNING: IPV2::append_from_input: "
|
---|
132 | << "couldn't open the file "
|
---|
133 | << dirfile
|
---|
134 | << endl;
|
---|
135 | }
|
---|
136 | else {
|
---|
137 | outfile << "appending " << dirfile << " to input" << endl;
|
---|
138 | ip_append(infile,outfile);
|
---|
139 | }
|
---|
140 | }
|
---|
141 | }
|
---|
142 | }
|
---|
143 |
|
---|
144 |
|
---|
145 | /* Set up internal ip variables based on the input that has been read in. */
|
---|
146 | void
|
---|
147 | IPV2::ip_internal_values()
|
---|
148 | {
|
---|
149 | int errcod;
|
---|
150 |
|
---|
151 | errcod = boolean(":ip:keyword",&ip_keyword,0);
|
---|
152 | if (errcod) ip_keyword = 0;
|
---|
153 |
|
---|
154 | }
|
---|
155 |
|
---|
156 | /* Free all of the data. */
|
---|
157 | void
|
---|
158 | IPV2::done()
|
---|
159 | {
|
---|
160 | ip_free_keyword_tree(ip_tree);
|
---|
161 | ip_tree = NULL;
|
---|
162 | sub_tree = NULL;
|
---|
163 | ip_in = NULL;
|
---|
164 | ip_out = NULL;
|
---|
165 | }
|
---|
166 |
|
---|
167 | void
|
---|
168 | IPV2::ip_push_keyword(char*keyword)
|
---|
169 | {
|
---|
170 | ip_push_keyclass(keyword,0,0);
|
---|
171 | }
|
---|
172 |
|
---|
173 | void
|
---|
174 | IPV2::ip_push_keyclass(char*keyword,char*classname,ip_string_list_t*parentlist)
|
---|
175 | {
|
---|
176 | ip_keyword_tree_t *I, *new_keyword;
|
---|
177 |
|
---|
178 | // if there is no keyword, then set the classname of the current
|
---|
179 | // sub_tree
|
---|
180 | if (!keyword) {
|
---|
181 | if (classname && sub_tree && sub_tree->keyword && !sub_tree->classname) {
|
---|
182 | sub_tree->classname = classname;
|
---|
183 | return;
|
---|
184 | }
|
---|
185 | else if (!sub_tree) {
|
---|
186 | keyword = strdup("TOP");
|
---|
187 | }
|
---|
188 | else {
|
---|
189 | if (classname) error("got a classname only in invalid context: %k");
|
---|
190 | else error("no classname, no keyword");
|
---|
191 | }
|
---|
192 | }
|
---|
193 |
|
---|
194 | /* Make the parentlist a part of the keyword. */
|
---|
195 | if (parentlist) {
|
---|
196 | int newkeysize = strlen(keyword) + 4 + 1;
|
---|
197 | ip_string_list_t *pl;
|
---|
198 | char* newkey;
|
---|
199 |
|
---|
200 | for (pl=parentlist; pl != NULL; pl=pl->p) {
|
---|
201 | newkeysize += strlen(pl->string);
|
---|
202 | if (pl->p) newkeysize++;
|
---|
203 | }
|
---|
204 |
|
---|
205 | newkey = (char*)malloc(newkeysize);
|
---|
206 | strcpy(newkey,keyword);
|
---|
207 | strcat(newkey,"<<");
|
---|
208 |
|
---|
209 | for (pl=parentlist; pl != NULL; pl=pl->p) {
|
---|
210 | strcat(newkey,pl->string);
|
---|
211 | if (pl->p) strcat(newkey,",");
|
---|
212 | }
|
---|
213 | strcat(newkey,">>");
|
---|
214 |
|
---|
215 | free(keyword);
|
---|
216 | keyword = newkey;
|
---|
217 | }
|
---|
218 |
|
---|
219 | /* If this is the first keyword, then create the tree. */
|
---|
220 | if (!ip_tree) {
|
---|
221 | sub_tree = ip_tree = ip_alloc_keyword_tree();
|
---|
222 | sub_tree->across = sub_tree;
|
---|
223 | sub_tree->keyword = keyword;
|
---|
224 | sub_tree->classname = classname;
|
---|
225 | return;
|
---|
226 | }
|
---|
227 |
|
---|
228 | /* This is not the first keyword, so descend the tree. */
|
---|
229 |
|
---|
230 | /* If sub_tree is at the top (NULL), then move to ip_tree. */
|
---|
231 | if (!sub_tree) {
|
---|
232 | sub_tree = ip_tree;
|
---|
233 | }
|
---|
234 | /* If there is not already a sub_tree->down, then create it. */
|
---|
235 | else if (!sub_tree->down) {
|
---|
236 | sub_tree->down = ip_alloc_keyword_tree();
|
---|
237 | sub_tree->down->across = sub_tree->down;
|
---|
238 | sub_tree->down->up = sub_tree;
|
---|
239 |
|
---|
240 | sub_tree = sub_tree->down;
|
---|
241 | sub_tree->keyword = keyword;
|
---|
242 | sub_tree->classname = classname;
|
---|
243 | return;
|
---|
244 | }
|
---|
245 | /* Descend the tree, but keep track of where we were. */
|
---|
246 | else {
|
---|
247 | sub_tree = sub_tree->down;
|
---|
248 | }
|
---|
249 |
|
---|
250 | /* Does the keyword exist in the current sub tree? */
|
---|
251 | I=sub_tree;
|
---|
252 | do {
|
---|
253 |
|
---|
254 | if (!strcmp(I->keyword,keyword)) {
|
---|
255 | /* We found it. */
|
---|
256 | sub_tree = I;
|
---|
257 | if (classname && I->classname) {
|
---|
258 | if (strcmp(classname,I->classname)) {
|
---|
259 | error("Class specifications differ for keyword %k\n");
|
---|
260 | }
|
---|
261 | free(classname);
|
---|
262 | }
|
---|
263 | else if (classname) I->classname = classname;
|
---|
264 | free(keyword);
|
---|
265 | return;
|
---|
266 | }
|
---|
267 |
|
---|
268 | } while ((I = I->across) != sub_tree);
|
---|
269 |
|
---|
270 | /* We could not find it -- create a new entry. */
|
---|
271 |
|
---|
272 | new_keyword = ip_alloc_keyword_tree();
|
---|
273 | new_keyword->across = sub_tree->across;
|
---|
274 | new_keyword->keyword = keyword;
|
---|
275 | new_keyword->classname = classname;
|
---|
276 | sub_tree->across = new_keyword;
|
---|
277 |
|
---|
278 | new_keyword->up = sub_tree->up;
|
---|
279 |
|
---|
280 | /* Move us down to the new keyword. */
|
---|
281 | sub_tree = new_keyword;
|
---|
282 | }
|
---|
283 |
|
---|
284 | void
|
---|
285 | IPV2::ip_pop_keyword()
|
---|
286 | {
|
---|
287 | /* Make sure we aren\'t already on top. */
|
---|
288 | if (!sub_tree) {
|
---|
289 | error("ip_pop_keyword: tried to pop above top");
|
---|
290 | }
|
---|
291 | sub_tree = sub_tree->up;
|
---|
292 | }
|
---|
293 |
|
---|
294 | void
|
---|
295 | IPV2::ip_begin_table(ip_string_list_t*keywords)
|
---|
296 | {
|
---|
297 | current_table_keyword = table_keywords = keywords;
|
---|
298 | table_sub_tree = sub_tree;
|
---|
299 | table_row_number = 0;
|
---|
300 | }
|
---|
301 |
|
---|
302 | /* Given a string containing keywords separated by ':', push the
|
---|
303 | * keywords. */
|
---|
304 | void
|
---|
305 | IPV2::ip_push_table_col(char*keys)
|
---|
306 | {
|
---|
307 | char cindex[10];
|
---|
308 | char * tmp = dup_string(keys);
|
---|
309 | char * keyword = strtok(tmp,":");
|
---|
310 | int n = 0;
|
---|
311 | do {
|
---|
312 | ip_push_keyword(dup_string(keyword));
|
---|
313 | n++;
|
---|
314 | } while((keyword = strtok(NULL,":")) != NULL);
|
---|
315 | free(tmp);
|
---|
316 | sprintf(cindex,"%d",table_row_number);
|
---|
317 | ip_push_keyword(dup_string(cindex));
|
---|
318 | }
|
---|
319 |
|
---|
320 | void
|
---|
321 | IPV2::ip_next_table_entry()
|
---|
322 | {
|
---|
323 | if (table_array_depth>0) return;
|
---|
324 |
|
---|
325 | sub_tree = table_sub_tree;
|
---|
326 | ip_push_table_col(current_table_keyword->string);
|
---|
327 |
|
---|
328 | /* Advance the current_table_keyword pointer */
|
---|
329 | if (current_table_keyword->p == NULL) {
|
---|
330 | current_table_keyword = table_keywords;
|
---|
331 | table_row_number++;
|
---|
332 | }
|
---|
333 | else {
|
---|
334 | current_table_keyword = current_table_keyword->p;
|
---|
335 | }
|
---|
336 | }
|
---|
337 |
|
---|
338 | void
|
---|
339 | IPV2::ip_done_table()
|
---|
340 | {
|
---|
341 | ip_string_list_t *I,*J;
|
---|
342 |
|
---|
343 | /* Free the keywords strings and string list */
|
---|
344 | for (I=table_keywords; I!=NULL; ) {
|
---|
345 | free(I->string);
|
---|
346 | J = I->p;
|
---|
347 | free(I);
|
---|
348 | I = J;
|
---|
349 | }
|
---|
350 | table_keywords = NULL;
|
---|
351 | current_table_keyword = NULL;
|
---|
352 | sub_tree = table_sub_tree;
|
---|
353 | table_sub_tree = NULL;
|
---|
354 | }
|
---|
355 |
|
---|
356 | /* This adds the string, s, to the string list linked list, sl. */
|
---|
357 | ip_string_list_t *
|
---|
358 | IPV2::ip_add_string_list(ip_string_list_t*sl,char*s)
|
---|
359 | {
|
---|
360 | ip_string_list_t *I;
|
---|
361 |
|
---|
362 | if (!sl) return ip_string_to_string_list(s);
|
---|
363 |
|
---|
364 | for (I=sl; I->p!=NULL; I=I->p);
|
---|
365 | I->p = ip_string_to_string_list(s);
|
---|
366 | return sl;
|
---|
367 | }
|
---|
368 |
|
---|
369 | ip_string_list_t *
|
---|
370 | IPV2::ip_string_to_string_list(char*s)
|
---|
371 | {
|
---|
372 | ip_string_list_t *r;
|
---|
373 | r = (ip_string_list_t *) malloc(sizeof(ip_string_list_t));
|
---|
374 | r->string = s;
|
---|
375 | r->p = NULL;
|
---|
376 | return r;
|
---|
377 | }
|
---|
378 |
|
---|
379 | char *
|
---|
380 | IPV2::dup_string(const char*s)
|
---|
381 | {
|
---|
382 | char *r;
|
---|
383 | r = (char *) malloc(strlen(s)+1);
|
---|
384 | strcpy(r,s);
|
---|
385 | return r;
|
---|
386 | }
|
---|
387 |
|
---|
388 | ip_keyword_tree_t *
|
---|
389 | IPV2::ip_get_variable_kt(char* variable)
|
---|
390 | {
|
---|
391 | char* passed_variable = variable;
|
---|
392 | ip_keyword_tree_t *kt;
|
---|
393 | ip_keyword_tree_t *top;
|
---|
394 |
|
---|
395 | top = sub_tree;
|
---|
396 |
|
---|
397 | /* One or more occurrences of "..:" at the beginning of the keyword
|
---|
398 | * move us up the keyword tree */
|
---|
399 | while(top && !strncmp(variable,"..:",3)) {
|
---|
400 | variable = &variable[3];
|
---|
401 | top = top->up;
|
---|
402 | }
|
---|
403 |
|
---|
404 | /* If top is still then we have a problem. */
|
---|
405 | if (!top) {
|
---|
406 | error("tried to get a variable above the top level - impossible");
|
---|
407 | }
|
---|
408 |
|
---|
409 | /* Descend the keyword tree, creating nodes if needed. */
|
---|
410 | if (variable[0] == ':') {
|
---|
411 | kt = ip_descend_tree(ip_tree,variable);
|
---|
412 | }
|
---|
413 | else {
|
---|
414 | kt = ip_descend_tree(top,variable);
|
---|
415 | }
|
---|
416 |
|
---|
417 | /* This should never be the case since variable keyword trees are
|
---|
418 | * created as needed. */
|
---|
419 | if (!kt) {
|
---|
420 | ExEnv::errn() << "WARNING: couldn't find the variable "
|
---|
421 | << variable
|
---|
422 | << endl;
|
---|
423 | return NULL;
|
---|
424 | }
|
---|
425 |
|
---|
426 | /* Release storage for the variable. */
|
---|
427 | free(passed_variable);
|
---|
428 |
|
---|
429 | return(kt);
|
---|
430 | }
|
---|
431 |
|
---|
432 | void
|
---|
433 | IPV2::ip_assign_variable(char* variable)
|
---|
434 | {
|
---|
435 | if (table_keywords) ip_next_table_entry();
|
---|
436 |
|
---|
437 | /* Note that the subtree is really a reference to another subtree. */
|
---|
438 | sub_tree->variable = variable;
|
---|
439 | }
|
---|
440 |
|
---|
441 | char *
|
---|
442 | IPV2::ip_get_variable_value(char*variable)
|
---|
443 | {
|
---|
444 | ip_keyword_tree_t *kt;
|
---|
445 |
|
---|
446 | /* Get the keyword tree associated with the variable. */
|
---|
447 | kt = ip_get_variable_kt(variable);
|
---|
448 |
|
---|
449 | /* Return the value associated with the keyword. */
|
---|
450 | if (kt) return(kt->value);
|
---|
451 | else return NULL;
|
---|
452 | }
|
---|
453 |
|
---|
454 | double
|
---|
455 | IPV2::ip_get_variable_double(char*variable)
|
---|
456 | {
|
---|
457 | char *value;
|
---|
458 |
|
---|
459 | value = ip_get_variable_value(variable);
|
---|
460 |
|
---|
461 | if (value == NULL) return 0.0;
|
---|
462 | else return atof(value);
|
---|
463 | }
|
---|
464 |
|
---|
465 | char *
|
---|
466 | IPV2::ip_double_to_string(double val)
|
---|
467 | {
|
---|
468 | char *result;
|
---|
469 |
|
---|
470 | result = (char *) malloc(64);
|
---|
471 |
|
---|
472 | sprintf(result,"%22.15e",val);
|
---|
473 |
|
---|
474 | return result;
|
---|
475 | }
|
---|
476 |
|
---|
477 | void
|
---|
478 | IPV2::ip_assign_value(char*value)
|
---|
479 | {
|
---|
480 |
|
---|
481 | if (table_keywords) ip_next_table_entry();
|
---|
482 |
|
---|
483 | /* If sub_tree is still NULL then we have a problem. */
|
---|
484 | if (!sub_tree) {
|
---|
485 | error("tried to put a keyword at the top level - impossible");
|
---|
486 | }
|
---|
487 |
|
---|
488 | /* Check for duplicate definitions. */
|
---|
489 | if (sub_tree->value) {
|
---|
490 | # ifdef DUP_WARN
|
---|
491 | /* Warn the user about duplicate definitions. */
|
---|
492 | warn("duplicate definition of the following keyword:");
|
---|
493 | ip_print_keyword(ip_out,sub_tree);
|
---|
494 | fprintf(ip_out,"\n");
|
---|
495 | warn("the new value will be ignored");
|
---|
496 | # endif /* DUP_WARN */
|
---|
497 | free(value);
|
---|
498 | }
|
---|
499 | else sub_tree->value = value;
|
---|
500 | }
|
---|
501 |
|
---|
502 | void
|
---|
503 | IPV2::ip_start_karray()
|
---|
504 | {
|
---|
505 | if (table_keywords && table_array_depth == 0) ip_next_table_entry();
|
---|
506 | if (table_keywords) table_array_depth++;
|
---|
507 | if (!karray_indices) {
|
---|
508 | karray_indices = (intlist_t *) malloc(sizeof(intlist_t));
|
---|
509 | karray_indices->p = NULL;
|
---|
510 | }
|
---|
511 | else {
|
---|
512 | intlist_t *tmp;
|
---|
513 | tmp = (intlist_t *) malloc(sizeof(intlist_t));
|
---|
514 | tmp->p = karray_indices;
|
---|
515 | karray_indices = tmp;
|
---|
516 | }
|
---|
517 | }
|
---|
518 |
|
---|
519 | void
|
---|
520 | IPV2::ip_init_karray()
|
---|
521 | {
|
---|
522 | init_karray = 1;
|
---|
523 | karray_indices->i = 0;
|
---|
524 | }
|
---|
525 |
|
---|
526 | void
|
---|
527 | IPV2::ip_incr_karray()
|
---|
528 | {
|
---|
529 | char *key;
|
---|
530 |
|
---|
531 | if (init_karray) {
|
---|
532 | init_karray = 0;
|
---|
533 | }
|
---|
534 | else {
|
---|
535 | ip_pop_keyword();
|
---|
536 | }
|
---|
537 |
|
---|
538 | /* Construct a keyword to push. */
|
---|
539 | /* A cheap, yet inaccurate estimate of the string size needed. */
|
---|
540 | key = (char *) malloc(karray_indices->i/1000 + 4);
|
---|
541 | sprintf(key,"%d",karray_indices->i);
|
---|
542 |
|
---|
543 | ip_push_keyword(key);
|
---|
544 |
|
---|
545 | /* Increment the current karray index. */
|
---|
546 | karray_indices->i++;
|
---|
547 | }
|
---|
548 |
|
---|
549 | void
|
---|
550 | IPV2::ip_pop_karray()
|
---|
551 | {
|
---|
552 | intlist_t *tmp;
|
---|
553 | if (table_keywords) table_array_depth--;
|
---|
554 | if (!init_karray) ip_pop_keyword();
|
---|
555 | tmp = karray_indices;
|
---|
556 | karray_indices = karray_indices->p;
|
---|
557 | if (tmp) free(tmp);
|
---|
558 | }
|
---|
559 |
|
---|
560 | char *
|
---|
561 | IPV2::ip_append_keystrings(char*s1,char*s2)
|
---|
562 | {
|
---|
563 | char *r;
|
---|
564 |
|
---|
565 | if (s1) r = (char *) malloc(strlen(s1)+strlen(s2)+2);
|
---|
566 | else r = (char *) malloc(strlen(s2)+2);
|
---|
567 | r[0] = '\0';
|
---|
568 | if (s1) strcat(r,s1);
|
---|
569 | strcat(r,":");
|
---|
570 | strcat(r,s2);
|
---|
571 | if (s1) free(s1);
|
---|
572 | free(s2);
|
---|
573 | return r;
|
---|
574 | }
|
---|
575 |
|
---|
576 | /////////////////////////////////////////////////////////////////////////////
|
---|
577 |
|
---|
578 | // Local Variables:
|
---|
579 | // mode: c++
|
---|
580 | // c-file-style: "CLJ"
|
---|
581 | // End:
|
---|