1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
---|
2 | /* ***** BEGIN LICENSE BLOCK *****
|
---|
3 | * Version: NPL 1.1/GPL 2.0/LGPL 2.1
|
---|
4 | *
|
---|
5 | * The contents of this file are subject to the Netscape Public License
|
---|
6 | * Version 1.1 (the "License"); you may not use this file except in
|
---|
7 | * compliance with the License. You may obtain a copy of the License at
|
---|
8 | * http://www.mozilla.org/NPL/
|
---|
9 | *
|
---|
10 | * Software distributed under the License is distributed on an "AS IS" basis,
|
---|
11 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
---|
12 | * for the specific language governing rights and limitations under the
|
---|
13 | * License.
|
---|
14 | *
|
---|
15 | * The Original Code is mozilla.org code.
|
---|
16 | *
|
---|
17 | * The Initial Developer of the Original Code is
|
---|
18 | * Netscape Communications Corporation.
|
---|
19 | * Portions created by the Initial Developer are Copyright (C) 1998
|
---|
20 | * the Initial Developer. All Rights Reserved.
|
---|
21 | *
|
---|
22 | * Contributor(s):
|
---|
23 | * Michael Ang <mang@subcarrier.org>
|
---|
24 | *
|
---|
25 | * Alternatively, the contents of this file may be used under the terms of
|
---|
26 | * either the GNU General Public License Version 2 or later (the "GPL"), or
|
---|
27 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
---|
28 | * in which case the provisions of the GPL or the LGPL are applicable instead
|
---|
29 | * of those above. If you wish to allow use of your version of this file only
|
---|
30 | * under the terms of either the GPL or the LGPL, and not to allow others to
|
---|
31 | * use your version of this file under the terms of the NPL, indicate your
|
---|
32 | * decision by deleting the provisions above and replace them with the notice
|
---|
33 | * and other provisions required by the GPL or the LGPL. If you do not delete
|
---|
34 | * the provisions above, a recipient may use your version of this file under
|
---|
35 | * the terms of any one of the NPL, the GPL or the LGPL.
|
---|
36 | *
|
---|
37 | * ***** END LICENSE BLOCK ***** */
|
---|
38 |
|
---|
39 | /*
|
---|
40 | * Common IDL-processing code.
|
---|
41 | */
|
---|
42 |
|
---|
43 | #include "xpidl.h"
|
---|
44 |
|
---|
45 | #ifdef XP_MAC
|
---|
46 | #include <stat.h>
|
---|
47 | #endif
|
---|
48 |
|
---|
49 | static gboolean parsed_empty_file;
|
---|
50 |
|
---|
51 | /*
|
---|
52 | * The bulk of the generation happens here.
|
---|
53 | */
|
---|
54 | gboolean
|
---|
55 | xpidl_process_node(TreeState *state)
|
---|
56 | {
|
---|
57 | gint type;
|
---|
58 | nodeHandler *dispatch, handler;
|
---|
59 |
|
---|
60 | XPT_ASSERT(state->tree);
|
---|
61 | type = IDL_NODE_TYPE(state->tree);
|
---|
62 |
|
---|
63 | if ((dispatch = state->dispatch) && (handler = dispatch[type]))
|
---|
64 | return handler(state);
|
---|
65 | return TRUE;
|
---|
66 | }
|
---|
67 |
|
---|
68 | #if defined(XP_MAC) && defined(XPIDL_PLUGIN)
|
---|
69 | extern void mac_warning(const char* warning_message);
|
---|
70 | #endif
|
---|
71 |
|
---|
72 | static int
|
---|
73 | msg_callback(int level, int num, int line, const char *file,
|
---|
74 | const char *message)
|
---|
75 | {
|
---|
76 | char *warning_message;
|
---|
77 |
|
---|
78 | /*
|
---|
79 | * Egregious hack to permit empty files.
|
---|
80 | * XXX libIDL needs an API to detect this case robustly.
|
---|
81 | */
|
---|
82 | if (0 == strcmp(message, "File empty after optimization")) {
|
---|
83 | parsed_empty_file = TRUE;
|
---|
84 | return 1;
|
---|
85 | }
|
---|
86 |
|
---|
87 | if (!file)
|
---|
88 | file = "<unknown file>";
|
---|
89 | warning_message = g_strdup_printf("%s:%d: %s\n", file, line, message);
|
---|
90 |
|
---|
91 | #if defined(XP_MAC) && defined(XPIDL_PLUGIN)
|
---|
92 | mac_warning(warning_message);
|
---|
93 | #else
|
---|
94 | fputs(warning_message, stderr);
|
---|
95 | #endif
|
---|
96 |
|
---|
97 | g_free(warning_message);
|
---|
98 | return 1;
|
---|
99 | }
|
---|
100 |
|
---|
101 | /*
|
---|
102 | * To keep track of the state associated with a given input file. The 'next'
|
---|
103 | * field lets us maintain a stack of input files.
|
---|
104 | */
|
---|
105 | typedef struct input_data {
|
---|
106 | char *filename; /* where did I come from? */
|
---|
107 | unsigned int lineno; /* last lineno processed */
|
---|
108 | char *buf; /* contents of file */
|
---|
109 | char *point; /* next char to feed to libIDL */
|
---|
110 | char *max; /* 1 past last char in buf */
|
---|
111 | struct input_data *next; /* file from which we were included */
|
---|
112 | } input_data;
|
---|
113 |
|
---|
114 | /*
|
---|
115 | * Passed to us by libIDL. Holds global information and the current stack of
|
---|
116 | * include files.
|
---|
117 | */
|
---|
118 | typedef struct input_callback_state {
|
---|
119 | struct input_data *input_stack; /* linked list of input_data */
|
---|
120 | GHashTable *already_included; /* to prevent redundant includes */
|
---|
121 | IncludePathEntry *include_path; /* search path for included files */
|
---|
122 | GSList *base_includes; /* to accumulate #includes from *first* file;
|
---|
123 | * for passing thru TreeState to
|
---|
124 | * xpidl_header backend. */
|
---|
125 | } input_callback_state;
|
---|
126 |
|
---|
127 | static FILE *
|
---|
128 | fopen_from_includes(const char *filename, const char *mode,
|
---|
129 | IncludePathEntry *include_path)
|
---|
130 | {
|
---|
131 | IncludePathEntry *current_path = include_path;
|
---|
132 | char *pathname;
|
---|
133 | FILE *inputfile;
|
---|
134 | if (!strcmp(filename, "-"))
|
---|
135 | return stdin;
|
---|
136 |
|
---|
137 | if (filename[0] != '/') {
|
---|
138 | while (current_path) {
|
---|
139 | pathname = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
|
---|
140 | current_path->directory, filename);
|
---|
141 | if (!pathname)
|
---|
142 | return NULL;
|
---|
143 | inputfile = fopen(pathname, mode);
|
---|
144 | g_free(pathname);
|
---|
145 | if (inputfile)
|
---|
146 | return inputfile;
|
---|
147 | current_path = current_path->next;
|
---|
148 | }
|
---|
149 | } else {
|
---|
150 | inputfile = fopen(filename, mode);
|
---|
151 | if (inputfile)
|
---|
152 | return inputfile;
|
---|
153 | }
|
---|
154 | return NULL;
|
---|
155 | }
|
---|
156 |
|
---|
157 | #if defined(XP_MAC) && defined(XPIDL_PLUGIN)
|
---|
158 | extern FILE* mac_fopen(const char* filename, const char *mode);
|
---|
159 | #endif
|
---|
160 |
|
---|
161 | static input_data *
|
---|
162 | new_input_data(const char *filename, IncludePathEntry *include_path)
|
---|
163 | {
|
---|
164 | input_data *new_data;
|
---|
165 | FILE *inputfile;
|
---|
166 | char *buffer = NULL;
|
---|
167 | size_t offset = 0;
|
---|
168 | size_t buffer_size;
|
---|
169 | #ifdef XP_MAC
|
---|
170 | size_t i;
|
---|
171 | #endif
|
---|
172 |
|
---|
173 | #if defined(XP_MAC) && defined(XPIDL_PLUGIN)
|
---|
174 | /* on Mac, fopen knows how to find files. */
|
---|
175 | inputfile = fopen(filename, "r");
|
---|
176 | #elif defined(XP_OS2) || defined(XP_WIN32)
|
---|
177 | /*
|
---|
178 | * if filename is fully qualified (starts with driver letter), then
|
---|
179 | * just call fopen(); else, go with fopen_from_includes()
|
---|
180 | */
|
---|
181 | if( filename[1] == ':' )
|
---|
182 | inputfile = fopen(filename, "r");
|
---|
183 | else
|
---|
184 | inputfile = fopen_from_includes(filename, "r", include_path);
|
---|
185 | #else
|
---|
186 | inputfile = fopen_from_includes(filename, "r", include_path);
|
---|
187 | #endif
|
---|
188 |
|
---|
189 | if (!inputfile)
|
---|
190 | return NULL;
|
---|
191 |
|
---|
192 | #ifdef XP_MAC
|
---|
193 | {
|
---|
194 | struct stat input_stat;
|
---|
195 | if (fstat(fileno(inputfile), &input_stat))
|
---|
196 | return NULL;
|
---|
197 | buffer = malloc(input_stat.st_size + 1);
|
---|
198 | if (!buffer)
|
---|
199 | return NULL;
|
---|
200 | offset = fread(buffer, 1, input_stat.st_size, inputfile);
|
---|
201 | if (ferror(inputfile))
|
---|
202 | return NULL;
|
---|
203 | }
|
---|
204 | #else
|
---|
205 | /*
|
---|
206 | * Rather than try to keep track of many different varieties of state
|
---|
207 | * around the boundaries of a circular buffer, we just read in the entire
|
---|
208 | * file.
|
---|
209 | *
|
---|
210 | * We iteratively grow the buffer here; an alternative would be to use
|
---|
211 | * stat to find the exact buffer size we need, as xpt_dump does.
|
---|
212 | */
|
---|
213 | for (buffer_size = 8191; ; buffer_size *= 2) {
|
---|
214 | size_t just_read;
|
---|
215 | buffer = realloc(buffer, buffer_size + 1); /* +1 for trailing nul */
|
---|
216 | just_read = fread(buffer + offset, 1, buffer_size - offset, inputfile);
|
---|
217 | if (ferror(inputfile))
|
---|
218 | return NULL;
|
---|
219 |
|
---|
220 | if (just_read < buffer_size - offset || just_read == 0) {
|
---|
221 | /* Done reading. */
|
---|
222 | offset += just_read;
|
---|
223 | break;
|
---|
224 | }
|
---|
225 | offset += just_read;
|
---|
226 | }
|
---|
227 | #endif
|
---|
228 |
|
---|
229 | fclose(inputfile);
|
---|
230 |
|
---|
231 | #ifdef XP_MAC
|
---|
232 | /*
|
---|
233 | * libIDL doesn't speak '\r' properly - always make sure lines end with
|
---|
234 | * '\n'.
|
---|
235 | */
|
---|
236 | for (i = 0; i < offset; i++) {
|
---|
237 | if (buffer[i] == '\r')
|
---|
238 | buffer[i] = '\n';
|
---|
239 | }
|
---|
240 | #endif
|
---|
241 |
|
---|
242 | new_data = xpidl_malloc(sizeof (struct input_data));
|
---|
243 | new_data->point = new_data->buf = buffer;
|
---|
244 | new_data->max = buffer + offset;
|
---|
245 | *new_data->max = '\0';
|
---|
246 | new_data->filename = xpidl_strdup(filename);
|
---|
247 | /* libIDL expects the line number to be that of the *next* line */
|
---|
248 | new_data->lineno = 2;
|
---|
249 | new_data->next = NULL;
|
---|
250 |
|
---|
251 | return new_data;
|
---|
252 | }
|
---|
253 |
|
---|
254 | /* process pending raw section */
|
---|
255 | static int
|
---|
256 | NextIsRaw(input_data *data, char **startp, int *lenp)
|
---|
257 | {
|
---|
258 | char *end, *start;
|
---|
259 |
|
---|
260 | /*
|
---|
261 | * XXXmccabe still needed: an in_raw flag to handle the case where we're in
|
---|
262 | * a raw block, but haven't managed to copy it all to xpidl. This will
|
---|
263 | * happen when we have a raw block larger than
|
---|
264 | * IDL_input_data->fill.max_size (currently 8192.)
|
---|
265 | */
|
---|
266 | if (!(data->point[0] == '%' && data->point[1] == '{'))
|
---|
267 | return 0;
|
---|
268 |
|
---|
269 | start = *startp = data->point;
|
---|
270 |
|
---|
271 | end = NULL;
|
---|
272 | while (start < data->max && (end = strstr(start, "%}"))) {
|
---|
273 | if (end[-1] == '\r' ||
|
---|
274 | end[-1] == '\n')
|
---|
275 | break;
|
---|
276 | start = end + 1;
|
---|
277 | }
|
---|
278 |
|
---|
279 | if (end && start < data->max) {
|
---|
280 | *lenp = end - data->point + 2;
|
---|
281 | return 1;
|
---|
282 | } else {
|
---|
283 | const char *filename;
|
---|
284 | int lineno;
|
---|
285 |
|
---|
286 | IDL_file_get(&filename, &lineno);
|
---|
287 | msg_callback(IDL_ERROR, 0, lineno, filename,
|
---|
288 | "unterminated %{ block");
|
---|
289 | return -1;
|
---|
290 | }
|
---|
291 | }
|
---|
292 |
|
---|
293 | /* process pending comment */
|
---|
294 | static int
|
---|
295 | NextIsComment(input_data *data, char **startp, int *lenp)
|
---|
296 | {
|
---|
297 | char *end;
|
---|
298 |
|
---|
299 | if (!(data->point[0] == '/' && data->point[1] == '*'))
|
---|
300 | return 0;
|
---|
301 |
|
---|
302 | end = strstr(data->point, "*/");
|
---|
303 | *lenp = 0;
|
---|
304 | if (end) {
|
---|
305 | int skippedLines = 0;
|
---|
306 | char *tempPoint;
|
---|
307 |
|
---|
308 | /* get current lineno */
|
---|
309 | IDL_file_get(NULL,(int *)&data->lineno);
|
---|
310 |
|
---|
311 | /* get line count */
|
---|
312 | for (tempPoint = data->point; tempPoint < end; tempPoint++) {
|
---|
313 | if (*tempPoint == '\n')
|
---|
314 | skippedLines++;
|
---|
315 | }
|
---|
316 |
|
---|
317 | data->lineno += skippedLines;
|
---|
318 | IDL_file_set(data->filename, (int)data->lineno);
|
---|
319 |
|
---|
320 | *startp = end + 2;
|
---|
321 |
|
---|
322 | /* If it's a ** comment, tell libIDL about it. */
|
---|
323 | if (data->point[2] == '*') {
|
---|
324 | /* hack termination. +2 to get past '*' '/' */
|
---|
325 | char t = *(end + 2);
|
---|
326 | *(end + 2) = '\0';
|
---|
327 | IDL_queue_new_ident_comment(data->point);
|
---|
328 | *(end + 2) = t;
|
---|
329 | }
|
---|
330 |
|
---|
331 | data->point = *startp; /* XXXmccabe move this out of function? */
|
---|
332 | return 1;
|
---|
333 | } else {
|
---|
334 | const char *filename;
|
---|
335 | int lineno;
|
---|
336 |
|
---|
337 | IDL_file_get(&filename, &lineno);
|
---|
338 | msg_callback(IDL_ERROR, 0, lineno, filename,
|
---|
339 | "unterminated comment");
|
---|
340 | return -1;
|
---|
341 | }
|
---|
342 | }
|
---|
343 |
|
---|
344 | static int
|
---|
345 | NextIsInclude(input_callback_state *callback_state, char **startp,
|
---|
346 | int *lenp)
|
---|
347 | {
|
---|
348 | input_data *data = callback_state->input_stack;
|
---|
349 | input_data *new_data;
|
---|
350 | char *filename, *end;
|
---|
351 | const char *scratch;
|
---|
352 |
|
---|
353 | /* process the #include that we're in now */
|
---|
354 | if (strncmp(data->point, "#include \"", 10)) {
|
---|
355 | return 0;
|
---|
356 | }
|
---|
357 |
|
---|
358 | filename = data->point + 10; /* skip #include " */
|
---|
359 | XPT_ASSERT(filename < data->max);
|
---|
360 | end = filename;
|
---|
361 | while (end < data->max) {
|
---|
362 | if (*end == '\"' || *end == '\n' || *end == '\r')
|
---|
363 | break;
|
---|
364 | end++;
|
---|
365 | }
|
---|
366 |
|
---|
367 | if (*end != '\"') {
|
---|
368 | /*
|
---|
369 | * Didn't find end of include file. Scan 'til next whitespace to find
|
---|
370 | * some reasonable approximation of the filename, and use it to report
|
---|
371 | * an error.
|
---|
372 | */
|
---|
373 |
|
---|
374 | end = filename;
|
---|
375 | while (end < data->max) {
|
---|
376 | if (*end == ' ' || *end == '\n' || *end == '\r' || *end == '\t')
|
---|
377 | break;
|
---|
378 | end++;
|
---|
379 | }
|
---|
380 | *end = '\0';
|
---|
381 |
|
---|
382 | /* make sure we have accurate line info */
|
---|
383 | IDL_file_get(&scratch, (int *)&data->lineno);
|
---|
384 | fprintf(stderr,
|
---|
385 | "%s:%d: didn't find end of quoted include name \"%s\n",
|
---|
386 | scratch, data->lineno, filename);
|
---|
387 | return -1;
|
---|
388 | }
|
---|
389 |
|
---|
390 | *end = '\0';
|
---|
391 | *startp = end + 1;
|
---|
392 |
|
---|
393 | if (data->next == NULL) {
|
---|
394 | /*
|
---|
395 | * If we're in the initial file, add this filename to the list
|
---|
396 | * of filenames to be turned into #include "filename.h"
|
---|
397 | * directives in xpidl_header.c. We do it here rather than in the
|
---|
398 | * block below so it still gets added to the list even if it's
|
---|
399 | * already been recursively included from some other file.
|
---|
400 | */
|
---|
401 | char *filename_cp = xpidl_strdup(filename);
|
---|
402 |
|
---|
403 | /* note that g_slist_append accepts and likes null as list-start. */
|
---|
404 | callback_state->base_includes =
|
---|
405 | g_slist_append(callback_state->base_includes, filename_cp);
|
---|
406 | }
|
---|
407 |
|
---|
408 | /* store offset for when we pop, or if we skip this one */
|
---|
409 | data->point = *startp;
|
---|
410 |
|
---|
411 | if (!g_hash_table_lookup(callback_state->already_included, filename)) {
|
---|
412 | filename = xpidl_strdup(filename);
|
---|
413 | g_hash_table_insert(callback_state->already_included,
|
---|
414 | filename, (void *)TRUE);
|
---|
415 | new_data = new_input_data(filename, callback_state->include_path);
|
---|
416 | if (!new_data) {
|
---|
417 | char *error_message;
|
---|
418 | IDL_file_get(&scratch, (int *)&data->lineno);
|
---|
419 | error_message =
|
---|
420 | g_strdup_printf("can't open included file %s for reading\n",
|
---|
421 | filename);
|
---|
422 | msg_callback(IDL_ERROR, 0,
|
---|
423 | data->lineno, scratch, error_message);
|
---|
424 | g_free(error_message);
|
---|
425 | return -1;
|
---|
426 | }
|
---|
427 |
|
---|
428 | new_data->next = data;
|
---|
429 | /* tell libIDL to exclude this IDL from the toplevel tree */
|
---|
430 | IDL_inhibit_push();
|
---|
431 | IDL_file_get(&scratch, (int *)&data->lineno);
|
---|
432 | callback_state->input_stack = new_data;
|
---|
433 | IDL_file_set(new_data->filename, (int)new_data->lineno);
|
---|
434 | }
|
---|
435 |
|
---|
436 | *lenp = 0; /* this is magic, see the comment below */
|
---|
437 | return 1;
|
---|
438 | }
|
---|
439 |
|
---|
440 | static void
|
---|
441 | FindSpecial(input_data *data, char **startp, int *lenp)
|
---|
442 | {
|
---|
443 | char *point = data->point;
|
---|
444 |
|
---|
445 | /* magic sequences are:
|
---|
446 | * "%{" raw block
|
---|
447 | * "/\*" comment
|
---|
448 | * "#include \"" include
|
---|
449 | * The first and last want a newline [\r\n] before, or the start of the
|
---|
450 | * file.
|
---|
451 | */
|
---|
452 |
|
---|
453 | #define LINE_START(data, point) (point == data->buf || \
|
---|
454 | (point > data->point && \
|
---|
455 | (point[-1] == '\r' || point[-1] == '\n')))
|
---|
456 |
|
---|
457 | while (point < data->max) {
|
---|
458 | if (point[0] == '/' && point[1] == '*')
|
---|
459 | break;
|
---|
460 | if (LINE_START(data, point)) {
|
---|
461 | if (point[0] == '%' && point[1] == '{')
|
---|
462 | break;
|
---|
463 | if (point[0] == '#' && !strncmp(point + 1, "include \"", 9))
|
---|
464 | break;
|
---|
465 | }
|
---|
466 | point++;
|
---|
467 | }
|
---|
468 |
|
---|
469 | #undef LINE_START
|
---|
470 |
|
---|
471 | *startp = data->point;
|
---|
472 | *lenp = point - data->point;
|
---|
473 | }
|
---|
474 |
|
---|
475 | /* set this with a debugger to see exactly what libIDL sees */
|
---|
476 | static FILE *tracefile;
|
---|
477 |
|
---|
478 | static int
|
---|
479 | input_callback(IDL_input_reason reason, union IDL_input_data *cb_data,
|
---|
480 | gpointer user_data)
|
---|
481 | {
|
---|
482 | input_callback_state *callback_state = user_data;
|
---|
483 | input_data *data = callback_state->input_stack;
|
---|
484 | input_data *new_data = NULL;
|
---|
485 | unsigned int len, copy;
|
---|
486 | int rv;
|
---|
487 | char *start;
|
---|
488 |
|
---|
489 | switch(reason) {
|
---|
490 | case IDL_INPUT_REASON_INIT:
|
---|
491 | if (data == NULL || data->next == NULL) {
|
---|
492 | /*
|
---|
493 | * This is the first file being processed. As it's the target
|
---|
494 | * file, we only look for it in the first entry in the include
|
---|
495 | * path, which we assume to be the current directory.
|
---|
496 | */
|
---|
497 |
|
---|
498 | /* XXXmccabe proper assumption? Do we handle files in other
|
---|
499 | directories? */
|
---|
500 |
|
---|
501 | IncludePathEntry first_entry;
|
---|
502 |
|
---|
503 | first_entry.directory = callback_state->include_path->directory;
|
---|
504 | first_entry.next = NULL;
|
---|
505 |
|
---|
506 | new_data = new_input_data(cb_data->init.filename,
|
---|
507 | &first_entry);
|
---|
508 | } else {
|
---|
509 | new_data = new_input_data(cb_data->init.filename,
|
---|
510 | callback_state->include_path);
|
---|
511 | }
|
---|
512 |
|
---|
513 | if (!new_data)
|
---|
514 | return -1;
|
---|
515 |
|
---|
516 | IDL_file_set(new_data->filename, (int)new_data->lineno);
|
---|
517 | callback_state->input_stack = new_data;
|
---|
518 | return 0;
|
---|
519 |
|
---|
520 | case IDL_INPUT_REASON_FILL:
|
---|
521 | start = NULL;
|
---|
522 | len = 0;
|
---|
523 |
|
---|
524 | while (data->point >= data->max) {
|
---|
525 | if (!data->next)
|
---|
526 | return 0;
|
---|
527 |
|
---|
528 | /* Current file is done; revert to including file */
|
---|
529 | callback_state->input_stack = data->next;
|
---|
530 | free(data->filename);
|
---|
531 | free(data->buf);
|
---|
532 | free(data);
|
---|
533 | data = callback_state->input_stack;
|
---|
534 |
|
---|
535 | IDL_file_set(data->filename, (int)data->lineno);
|
---|
536 | IDL_inhibit_pop();
|
---|
537 | }
|
---|
538 |
|
---|
539 | /*
|
---|
540 | * Now we scan for sequences which require special attention:
|
---|
541 | * \n#include begins an include statement
|
---|
542 | * \n%{ begins a raw-source block
|
---|
543 | * /\* begins a comment
|
---|
544 | *
|
---|
545 | * We used to be fancier here, so make sure that we sent the most
|
---|
546 | * data possible at any given time. To that end, we skipped over
|
---|
547 | * \n%{ raw \n%} blocks and then _continued_ the search for special
|
---|
548 | * sequences like \n#include or /\* comments .
|
---|
549 | *
|
---|
550 | * It was really ugly, though -- liberal use of goto! lots of implicit
|
---|
551 | * state! what fun! -- so now we just do this:
|
---|
552 | *
|
---|
553 | * if (special at start) {
|
---|
554 | * process that special -
|
---|
555 | * - raw: send it to libIDL, and don't look inside for specials
|
---|
556 | * - comments: adjust point and start over
|
---|
557 | * - includes: push new input_data struct for included file, and
|
---|
558 | * start over
|
---|
559 | * } else {
|
---|
560 | * scan for next special
|
---|
561 | * send data up to that special to libIDL
|
---|
562 | * }
|
---|
563 | *
|
---|
564 | * If len is set to zero, it is a sentinel value indicating we a comment
|
---|
565 | * or include was found, and parsing should start over.
|
---|
566 | *
|
---|
567 | * XXX const string foo = "/\*" will just screw us horribly.
|
---|
568 | * Hm but. We could treat strings as we treat raw blocks, eh?
|
---|
569 | */
|
---|
570 |
|
---|
571 | /*
|
---|
572 | * Order is important, so that you can have /\* comments and
|
---|
573 | * #includes within raw sections, and so that you can comment out
|
---|
574 | * #includes.
|
---|
575 | */
|
---|
576 | rv = NextIsRaw(data, &start, (int *)&len);
|
---|
577 | if (rv == -1) return -1;
|
---|
578 | if (!rv) {
|
---|
579 | /*
|
---|
580 | * When NextIsComment succeeds, it returns a 0 len (requesting a
|
---|
581 | * restart) and adjusts data->point to pick up after the comment.
|
---|
582 | */
|
---|
583 | rv = NextIsComment(data, &start, (int *)&len);
|
---|
584 | if (rv == -1) return -1;
|
---|
585 | if (!rv) {
|
---|
586 | /*
|
---|
587 | * NextIsInclude might push a new input_data struct; if so, it
|
---|
588 | * will return a 0 len, letting the callback pick up the new
|
---|
589 | * file the next time around.
|
---|
590 | */
|
---|
591 | rv = NextIsInclude(callback_state, &start, (int *)&len);
|
---|
592 | if (rv == -1) return -1;
|
---|
593 | if (!rv)
|
---|
594 | FindSpecial(data, &start, (int *)&len);
|
---|
595 | }
|
---|
596 | }
|
---|
597 |
|
---|
598 | if (len == 0) {
|
---|
599 | /*
|
---|
600 | * len == 0 is a sentinel value that means we found a comment or
|
---|
601 | * include. If we found a comment, point has been adjusted to
|
---|
602 | * point past the comment. If we found an include, a new input_data
|
---|
603 | * has been pushed. In both cases, calling the input_callback again
|
---|
604 | * will pick up the new state.
|
---|
605 | */
|
---|
606 | return input_callback(reason, cb_data, user_data);
|
---|
607 | }
|
---|
608 |
|
---|
609 | copy = MIN(len, (unsigned int) cb_data->fill.max_size);
|
---|
610 | memcpy(cb_data->fill.buffer, start, copy);
|
---|
611 | data->point = start + copy;
|
---|
612 |
|
---|
613 | if (tracefile)
|
---|
614 | fwrite(cb_data->fill.buffer, copy, 1, tracefile);
|
---|
615 |
|
---|
616 | return copy;
|
---|
617 |
|
---|
618 | case IDL_INPUT_REASON_ABORT:
|
---|
619 | case IDL_INPUT_REASON_FINISH:
|
---|
620 | while (data != NULL) {
|
---|
621 | input_data *next;
|
---|
622 |
|
---|
623 | next = data->next;
|
---|
624 | free(data->filename);
|
---|
625 | free(data->buf);
|
---|
626 | free(data);
|
---|
627 | data = next;
|
---|
628 | }
|
---|
629 | return 0;
|
---|
630 |
|
---|
631 | default:
|
---|
632 | g_error("unknown input reason %d!", reason);
|
---|
633 | return -1;
|
---|
634 | }
|
---|
635 | }
|
---|
636 |
|
---|
637 | static void
|
---|
638 | free_ghash_key(gpointer key, gpointer value, gpointer user_data)
|
---|
639 | {
|
---|
640 | /* We're only storing TRUE in the value... */
|
---|
641 | free(key);
|
---|
642 | }
|
---|
643 |
|
---|
644 | static void
|
---|
645 | free_gslist_data(gpointer data, gpointer user_data)
|
---|
646 | {
|
---|
647 | free(data);
|
---|
648 | }
|
---|
649 |
|
---|
650 | /* Pick up unlink. */
|
---|
651 | #ifdef XP_UNIX
|
---|
652 | #include <unistd.h>
|
---|
653 | #elif XP_WIN
|
---|
654 | /* We get it from stdio.h. */
|
---|
655 | #endif
|
---|
656 |
|
---|
657 | int
|
---|
658 | xpidl_process_idl(char *filename, IncludePathEntry *include_path,
|
---|
659 | char *file_basename, ModeData *mode)
|
---|
660 | {
|
---|
661 | char *tmp, *outname, *real_outname = NULL;
|
---|
662 | IDL_tree top;
|
---|
663 | TreeState state;
|
---|
664 | int rv;
|
---|
665 | input_callback_state callback_state;
|
---|
666 | gboolean ok = TRUE;
|
---|
667 | backend *emitter;
|
---|
668 |
|
---|
669 | callback_state.input_stack = NULL;
|
---|
670 | callback_state.base_includes = NULL;
|
---|
671 | callback_state.include_path = include_path;
|
---|
672 | callback_state.already_included = g_hash_table_new(g_str_hash, g_str_equal);
|
---|
673 |
|
---|
674 | if (!callback_state.already_included) {
|
---|
675 | fprintf(stderr, "failed to create hashtable. out of memory?\n");
|
---|
676 | return 0;
|
---|
677 | }
|
---|
678 |
|
---|
679 | state.basename = xpidl_strdup(filename);
|
---|
680 |
|
---|
681 | /* if basename has an .extension, truncate it. */
|
---|
682 | tmp = strrchr(state.basename, '.');
|
---|
683 | if (tmp)
|
---|
684 | *tmp = '\0';
|
---|
685 |
|
---|
686 | if (!file_basename)
|
---|
687 | outname = xpidl_strdup(state.basename);
|
---|
688 | else
|
---|
689 | outname = xpidl_strdup(file_basename);
|
---|
690 |
|
---|
691 | /* so we don't include it again! */
|
---|
692 | g_hash_table_insert(callback_state.already_included,
|
---|
693 | xpidl_strdup(filename), (void *)TRUE);
|
---|
694 |
|
---|
695 | parsed_empty_file = FALSE;
|
---|
696 |
|
---|
697 | rv = IDL_parse_filename_with_input(filename, input_callback, &callback_state,
|
---|
698 | msg_callback, &top,
|
---|
699 | &state.ns,
|
---|
700 | IDLF_IGNORE_FORWARDS |
|
---|
701 | IDLF_XPIDL,
|
---|
702 | enable_warnings ? IDL_WARNING1 :
|
---|
703 | IDL_ERROR);
|
---|
704 | if (parsed_empty_file) {
|
---|
705 | /*
|
---|
706 | * If we've detected (via hack in msg_callback) that libIDL returned
|
---|
707 | * failure because it found a file with no IDL, set the parse tree to
|
---|
708 | * null and proceed. Allowing this is useful to permit .idl files that
|
---|
709 | * collect #includes.
|
---|
710 | */
|
---|
711 | top = NULL;
|
---|
712 | state.ns = NULL;
|
---|
713 | } else if (rv != IDL_SUCCESS) {
|
---|
714 | if (rv == -1) {
|
---|
715 | g_warning("Parse of %s failed: %s", filename, g_strerror(errno));
|
---|
716 | } else {
|
---|
717 | g_warning("Parse of %s failed", filename);
|
---|
718 | }
|
---|
719 | return 0;
|
---|
720 | }
|
---|
721 |
|
---|
722 | state.basename = xpidl_strdup(filename);
|
---|
723 | tmp = strrchr(state.basename, '.');
|
---|
724 | if (tmp)
|
---|
725 | *tmp = '\0';
|
---|
726 |
|
---|
727 | /* so xpidl_header.c can use it to generate a list of #include directives */
|
---|
728 | state.base_includes = callback_state.base_includes;
|
---|
729 |
|
---|
730 | emitter = mode->factory();
|
---|
731 | state.dispatch = emitter->dispatch_table;
|
---|
732 |
|
---|
733 | if (strcmp(outname, "-")) {
|
---|
734 | const char *fopen_mode;
|
---|
735 | const char *out_basename;
|
---|
736 |
|
---|
737 | /* explicit_output_filename can't be true without a filename */
|
---|
738 | if (explicit_output_filename) {
|
---|
739 | real_outname = g_strdup(outname);
|
---|
740 | } else {
|
---|
741 | /*
|
---|
742 | *This combination seems a little strange, what about OS/2?
|
---|
743 | * Assume it's some build issue
|
---|
744 | */
|
---|
745 | #if defined(XP_UNIX) || defined(XP_WIN)
|
---|
746 | if (!file_basename) {
|
---|
747 | out_basename = xpidl_basename(outname);
|
---|
748 | } else {
|
---|
749 | out_basename = outname;
|
---|
750 | }
|
---|
751 | #else
|
---|
752 | out_basename = outname;
|
---|
753 | #endif
|
---|
754 | real_outname = g_strdup_printf("%s.%s", out_basename, mode->suffix);
|
---|
755 | }
|
---|
756 |
|
---|
757 | /* Use binary write for typelib mode */
|
---|
758 | fopen_mode = (strcmp(mode->mode, "typelib")) ? "w" : "wb";
|
---|
759 | state.file = fopen(real_outname, fopen_mode);
|
---|
760 | if (!state.file) {
|
---|
761 | perror("error opening output file");
|
---|
762 | return 0;
|
---|
763 | }
|
---|
764 | } else {
|
---|
765 | state.file = stdout;
|
---|
766 | }
|
---|
767 | state.tree = top;
|
---|
768 |
|
---|
769 | if (emitter->emit_prolog)
|
---|
770 | emitter->emit_prolog(&state);
|
---|
771 | if (state.tree) /* Only if we have a tree to process. */
|
---|
772 | ok = xpidl_process_node(&state);
|
---|
773 | if (emitter->emit_epilog)
|
---|
774 | emitter->emit_epilog(&state);
|
---|
775 |
|
---|
776 | if (state.file != stdout)
|
---|
777 | fclose(state.file);
|
---|
778 | free(state.basename);
|
---|
779 | free(outname);
|
---|
780 | g_hash_table_foreach(callback_state.already_included, free_ghash_key, NULL);
|
---|
781 | g_hash_table_destroy(callback_state.already_included);
|
---|
782 | g_slist_foreach(callback_state.base_includes, free_gslist_data, NULL);
|
---|
783 |
|
---|
784 | if (state.ns)
|
---|
785 | IDL_ns_free(state.ns);
|
---|
786 | if (top)
|
---|
787 | IDL_tree_free(top);
|
---|
788 |
|
---|
789 | if (real_outname != NULL) {
|
---|
790 | /*
|
---|
791 | * Delete partial output file on failure. (Mac does this in the plugin
|
---|
792 | * driver code, if the compiler returns failure.)
|
---|
793 | */
|
---|
794 | #if defined(XP_UNIX) || defined(XP_WIN)
|
---|
795 | if (!ok)
|
---|
796 | unlink(real_outname);
|
---|
797 | #endif
|
---|
798 | g_free(real_outname);
|
---|
799 | }
|
---|
800 |
|
---|
801 | return ok;
|
---|
802 | }
|
---|
803 |
|
---|
804 | /*
|
---|
805 | * Our own version of IDL_tree_warning, which we use when IDL_tree_warning
|
---|
806 | * would crash on us.
|
---|
807 | */
|
---|
808 | void
|
---|
809 | xpidl_tree_warning(IDL_tree p, int level, const char *fmt, ...)
|
---|
810 | {
|
---|
811 | va_list ap;
|
---|
812 | char *msg, *file;
|
---|
813 | int lineno;
|
---|
814 |
|
---|
815 | /* XXX need to check against __IDL_max_msg_level, no accessor */
|
---|
816 | va_start(ap, fmt);
|
---|
817 | msg = g_strdup_vprintf(fmt, ap);
|
---|
818 |
|
---|
819 | if (p) {
|
---|
820 | file = p->_file;
|
---|
821 | lineno = p->_line;
|
---|
822 | } else {
|
---|
823 | file = NULL;
|
---|
824 | lineno = 0;
|
---|
825 | }
|
---|
826 |
|
---|
827 | /* call our message callback, like IDL_tree_warning would */
|
---|
828 | msg_callback(level, 0, lineno, file, msg);
|
---|
829 | g_free(msg);
|
---|
830 | va_end(ap);
|
---|
831 | }
|
---|