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 | #include <iprt/errcore.h>
|
---|
43 | #include <iprt/file.h>
|
---|
44 | #include <iprt/mem.h>
|
---|
45 |
|
---|
46 | #include "xpidl.h"
|
---|
47 |
|
---|
48 | static gboolean parsed_empty_file;
|
---|
49 |
|
---|
50 | /*
|
---|
51 | * The bulk of the generation happens here.
|
---|
52 | */
|
---|
53 | gboolean
|
---|
54 | xpidl_process_node(TreeState *state)
|
---|
55 | {
|
---|
56 | gint type;
|
---|
57 | nodeHandler *dispatch, handler;
|
---|
58 |
|
---|
59 | XPT_ASSERT(state->tree);
|
---|
60 | type = IDL_NODE_TYPE(state->tree);
|
---|
61 |
|
---|
62 | if ((dispatch = state->dispatch) && (handler = dispatch[type]))
|
---|
63 | return handler(state);
|
---|
64 | return TRUE;
|
---|
65 | }
|
---|
66 |
|
---|
67 | static int
|
---|
68 | msg_callback(int level, int num, int line, const char *file,
|
---|
69 | const char *message)
|
---|
70 | {
|
---|
71 | char *warning_message;
|
---|
72 |
|
---|
73 | /*
|
---|
74 | * Egregious hack to permit empty files.
|
---|
75 | * XXX libIDL needs an API to detect this case robustly.
|
---|
76 | */
|
---|
77 | if (0 == strcmp(message, "File empty after optimization")) {
|
---|
78 | parsed_empty_file = TRUE;
|
---|
79 | return 1;
|
---|
80 | }
|
---|
81 |
|
---|
82 | if (!file)
|
---|
83 | file = "<unknown file>";
|
---|
84 | warning_message = g_strdup_printf("%s:%d: %s\n", file, line, message);
|
---|
85 |
|
---|
86 | fputs(warning_message, stderr);
|
---|
87 | g_free(warning_message);
|
---|
88 | return 1;
|
---|
89 | }
|
---|
90 |
|
---|
91 | /*
|
---|
92 | * To keep track of the state associated with a given input file. The 'next'
|
---|
93 | * field lets us maintain a stack of input files.
|
---|
94 | */
|
---|
95 | typedef struct input_data {
|
---|
96 | char *filename; /* where did I come from? */
|
---|
97 | unsigned int lineno; /* last lineno processed */
|
---|
98 | char *buf; /* contents of file */
|
---|
99 | char *point; /* next char to feed to libIDL */
|
---|
100 | char *max; /* 1 past last char in buf */
|
---|
101 | struct input_data *next; /* file from which we were included */
|
---|
102 | } input_data;
|
---|
103 |
|
---|
104 | /*
|
---|
105 | * Passed to us by libIDL. Holds global information and the current stack of
|
---|
106 | * include files.
|
---|
107 | */
|
---|
108 | typedef struct input_callback_state {
|
---|
109 | struct input_data *input_stack; /* linked list of input_data */
|
---|
110 | GHashTable *already_included; /* to prevent redundant includes */
|
---|
111 | IncludePathEntry *include_path; /* search path for included files */
|
---|
112 | GSList *base_includes; /* to accumulate #includes from *first* file;
|
---|
113 | * for passing thru TreeState to
|
---|
114 | * xpidl_header backend. */
|
---|
115 | } input_callback_state;
|
---|
116 |
|
---|
117 | static void *
|
---|
118 | file_read_from_includes(const char *filename, IncludePathEntry *include_path, size_t *pcbFile)
|
---|
119 | {
|
---|
120 | IncludePathEntry *current_path = include_path;
|
---|
121 | char *pathname;
|
---|
122 |
|
---|
123 | if (filename[0] != '/') {
|
---|
124 | while (current_path) {
|
---|
125 | pathname = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
|
---|
126 | current_path->directory, filename);
|
---|
127 | if (!pathname)
|
---|
128 | return NULL;
|
---|
129 | void *pvFile = NULL;
|
---|
130 | int vrc = RTFileReadAll(pathname, &pvFile, pcbFile);
|
---|
131 | g_free(pathname);
|
---|
132 | if (RT_SUCCESS(vrc))
|
---|
133 | return pvFile;
|
---|
134 | current_path = current_path->next;
|
---|
135 | }
|
---|
136 | } else {
|
---|
137 | void *pvFile = NULL;
|
---|
138 | int vrc = RTFileReadAll(filename, &pvFile, pcbFile);
|
---|
139 | if (RT_SUCCESS(vrc))
|
---|
140 | return pvFile;
|
---|
141 | }
|
---|
142 | return NULL;
|
---|
143 | }
|
---|
144 |
|
---|
145 | #if defined(XP_MAC) && defined(XPIDL_PLUGIN)
|
---|
146 | extern FILE* mac_fopen(const char* filename, const char *mode);
|
---|
147 | #endif
|
---|
148 |
|
---|
149 | static input_data *
|
---|
150 | new_input_data(const char *filename, IncludePathEntry *include_path)
|
---|
151 | {
|
---|
152 | input_data *new_data;
|
---|
153 |
|
---|
154 | /*
|
---|
155 | * Rather than try to keep track of many different varieties of state
|
---|
156 | * around the boundaries of a circular buffer, we just read in the entire
|
---|
157 | * file.
|
---|
158 | */
|
---|
159 | size_t cbFile = 0;
|
---|
160 | void *pvFile = file_read_from_includes(filename, include_path, &cbFile);
|
---|
161 | if (!pvFile)
|
---|
162 | return NULL;
|
---|
163 |
|
---|
164 | /* Need to copy the data over into a new buffer in order to be able to append a zero terminator. */
|
---|
165 | char *pbBuf = (char *)RTMemDupEx(pvFile, cbFile, 1 /* for the zero terminator */);
|
---|
166 | if (!pbBuf)
|
---|
167 | {
|
---|
168 | RTFileReadAllFree(pvFile, cbFile);
|
---|
169 | return NULL;
|
---|
170 | }
|
---|
171 |
|
---|
172 | memcpy(pbBuf, pvFile, cbFile);
|
---|
173 | RTFileReadAllFree(pvFile, cbFile);
|
---|
174 |
|
---|
175 | new_data = xpidl_malloc(sizeof (struct input_data));
|
---|
176 | if (!new_data)
|
---|
177 | {
|
---|
178 | RTMemFree(pbBuf);
|
---|
179 | return NULL;
|
---|
180 | }
|
---|
181 |
|
---|
182 | new_data->point = new_data->buf = pbBuf;
|
---|
183 | new_data->max = pbBuf + cbFile;
|
---|
184 | *new_data->max = '\0';
|
---|
185 | new_data->filename = xpidl_strdup(filename);
|
---|
186 | /* libIDL expects the line number to be that of the *next* line */
|
---|
187 | new_data->lineno = 2;
|
---|
188 | new_data->next = NULL;
|
---|
189 | return new_data;
|
---|
190 | }
|
---|
191 |
|
---|
192 | /* process pending raw section */
|
---|
193 | static int
|
---|
194 | NextIsRaw(input_data *data, char **startp, int *lenp)
|
---|
195 | {
|
---|
196 | char *end, *start;
|
---|
197 |
|
---|
198 | /*
|
---|
199 | * XXXmccabe still needed: an in_raw flag to handle the case where we're in
|
---|
200 | * a raw block, but haven't managed to copy it all to xpidl. This will
|
---|
201 | * happen when we have a raw block larger than
|
---|
202 | * IDL_input_data->fill.max_size (currently 8192.)
|
---|
203 | */
|
---|
204 | if (!(data->point[0] == '%' && data->point[1] == '{'))
|
---|
205 | return 0;
|
---|
206 |
|
---|
207 | start = *startp = data->point;
|
---|
208 |
|
---|
209 | end = NULL;
|
---|
210 | while (start < data->max && (end = strstr(start, "%}"))) {
|
---|
211 | if (end[-1] == '\r' ||
|
---|
212 | end[-1] == '\n')
|
---|
213 | break;
|
---|
214 | start = end + 1;
|
---|
215 | }
|
---|
216 |
|
---|
217 | if (end && start < data->max) {
|
---|
218 | *lenp = end - data->point + 2;
|
---|
219 | return 1;
|
---|
220 | } else {
|
---|
221 | const char *filename;
|
---|
222 | int lineno;
|
---|
223 |
|
---|
224 | IDL_file_get(&filename, &lineno);
|
---|
225 | msg_callback(IDL_ERROR, 0, lineno, filename,
|
---|
226 | "unterminated %{ block");
|
---|
227 | return -1;
|
---|
228 | }
|
---|
229 | }
|
---|
230 |
|
---|
231 | /* process pending comment */
|
---|
232 | static int
|
---|
233 | NextIsComment(input_data *data, char **startp, int *lenp)
|
---|
234 | {
|
---|
235 | char *end;
|
---|
236 |
|
---|
237 | if (!(data->point[0] == '/' && data->point[1] == '*'))
|
---|
238 | return 0;
|
---|
239 |
|
---|
240 | end = strstr(data->point, "*/");
|
---|
241 | *lenp = 0;
|
---|
242 | if (end) {
|
---|
243 | int skippedLines = 0;
|
---|
244 | char *tempPoint;
|
---|
245 |
|
---|
246 | /* get current lineno */
|
---|
247 | IDL_file_get(NULL,(int *)&data->lineno);
|
---|
248 |
|
---|
249 | /* get line count */
|
---|
250 | for (tempPoint = data->point; tempPoint < end; tempPoint++) {
|
---|
251 | if (*tempPoint == '\n')
|
---|
252 | skippedLines++;
|
---|
253 | }
|
---|
254 |
|
---|
255 | data->lineno += skippedLines;
|
---|
256 | IDL_file_set(data->filename, (int)data->lineno);
|
---|
257 |
|
---|
258 | *startp = end + 2;
|
---|
259 |
|
---|
260 | /* If it's a ** comment, tell libIDL about it. */
|
---|
261 | if (data->point[2] == '*') {
|
---|
262 | /* hack termination. +2 to get past '*' '/' */
|
---|
263 | char t = *(end + 2);
|
---|
264 | *(end + 2) = '\0';
|
---|
265 | IDL_queue_new_ident_comment(data->point);
|
---|
266 | *(end + 2) = t;
|
---|
267 | }
|
---|
268 |
|
---|
269 | data->point = *startp; /* XXXmccabe move this out of function? */
|
---|
270 | return 1;
|
---|
271 | } else {
|
---|
272 | const char *filename;
|
---|
273 | int lineno;
|
---|
274 |
|
---|
275 | IDL_file_get(&filename, &lineno);
|
---|
276 | msg_callback(IDL_ERROR, 0, lineno, filename,
|
---|
277 | "unterminated comment");
|
---|
278 | return -1;
|
---|
279 | }
|
---|
280 | }
|
---|
281 |
|
---|
282 | static int
|
---|
283 | NextIsInclude(input_callback_state *callback_state, char **startp,
|
---|
284 | int *lenp)
|
---|
285 | {
|
---|
286 | input_data *data = callback_state->input_stack;
|
---|
287 | input_data *new_data;
|
---|
288 | char *filename, *end;
|
---|
289 | const char *scratch;
|
---|
290 |
|
---|
291 | /* process the #include that we're in now */
|
---|
292 | if (strncmp(data->point, "#include \"", 10)) {
|
---|
293 | return 0;
|
---|
294 | }
|
---|
295 |
|
---|
296 | filename = data->point + 10; /* skip #include " */
|
---|
297 | XPT_ASSERT(filename < data->max);
|
---|
298 | end = filename;
|
---|
299 | while (end < data->max) {
|
---|
300 | if (*end == '\"' || *end == '\n' || *end == '\r')
|
---|
301 | break;
|
---|
302 | end++;
|
---|
303 | }
|
---|
304 |
|
---|
305 | if (*end != '\"') {
|
---|
306 | /*
|
---|
307 | * Didn't find end of include file. Scan 'til next whitespace to find
|
---|
308 | * some reasonable approximation of the filename, and use it to report
|
---|
309 | * an error.
|
---|
310 | */
|
---|
311 |
|
---|
312 | end = filename;
|
---|
313 | while (end < data->max) {
|
---|
314 | if (*end == ' ' || *end == '\n' || *end == '\r' || *end == '\t')
|
---|
315 | break;
|
---|
316 | end++;
|
---|
317 | }
|
---|
318 | *end = '\0';
|
---|
319 |
|
---|
320 | /* make sure we have accurate line info */
|
---|
321 | IDL_file_get(&scratch, (int *)&data->lineno);
|
---|
322 | fprintf(stderr,
|
---|
323 | "%s:%u: didn't find end of quoted include name \"%s\n",
|
---|
324 | scratch, data->lineno, filename);
|
---|
325 | return -1;
|
---|
326 | }
|
---|
327 |
|
---|
328 | *end = '\0';
|
---|
329 | *startp = end + 1;
|
---|
330 |
|
---|
331 | if (data->next == NULL) {
|
---|
332 | /*
|
---|
333 | * If we're in the initial file, add this filename to the list
|
---|
334 | * of filenames to be turned into #include "filename.h"
|
---|
335 | * directives in xpidl_header.c. We do it here rather than in the
|
---|
336 | * block below so it still gets added to the list even if it's
|
---|
337 | * already been recursively included from some other file.
|
---|
338 | */
|
---|
339 | char *filename_cp = xpidl_strdup(filename);
|
---|
340 |
|
---|
341 | /* note that g_slist_append accepts and likes null as list-start. */
|
---|
342 | callback_state->base_includes =
|
---|
343 | g_slist_append(callback_state->base_includes, filename_cp);
|
---|
344 | }
|
---|
345 |
|
---|
346 | /* store offset for when we pop, or if we skip this one */
|
---|
347 | data->point = *startp;
|
---|
348 |
|
---|
349 | if (!g_hash_table_lookup(callback_state->already_included, filename)) {
|
---|
350 | filename = xpidl_strdup(filename);
|
---|
351 | g_hash_table_insert(callback_state->already_included,
|
---|
352 | filename, (void *)TRUE);
|
---|
353 | new_data = new_input_data(filename, callback_state->include_path);
|
---|
354 | if (!new_data) {
|
---|
355 | char *error_message;
|
---|
356 | IDL_file_get(&scratch, (int *)&data->lineno);
|
---|
357 | error_message =
|
---|
358 | g_strdup_printf("can't open included file %s for reading\n",
|
---|
359 | filename);
|
---|
360 | msg_callback(IDL_ERROR, 0,
|
---|
361 | data->lineno, scratch, error_message);
|
---|
362 | g_free(error_message);
|
---|
363 | return -1;
|
---|
364 | }
|
---|
365 |
|
---|
366 | new_data->next = data;
|
---|
367 | /* tell libIDL to exclude this IDL from the toplevel tree */
|
---|
368 | IDL_inhibit_push();
|
---|
369 | IDL_file_get(&scratch, (int *)&data->lineno);
|
---|
370 | callback_state->input_stack = new_data;
|
---|
371 | IDL_file_set(new_data->filename, (int)new_data->lineno);
|
---|
372 | }
|
---|
373 |
|
---|
374 | *lenp = 0; /* this is magic, see the comment below */
|
---|
375 | return 1;
|
---|
376 | }
|
---|
377 |
|
---|
378 | static void
|
---|
379 | FindSpecial(input_data *data, char **startp, int *lenp)
|
---|
380 | {
|
---|
381 | char *point = data->point;
|
---|
382 |
|
---|
383 | /* magic sequences are:
|
---|
384 | * "%{" raw block
|
---|
385 | * "/\*" comment
|
---|
386 | * "#include \"" include
|
---|
387 | * The first and last want a newline [\r\n] before, or the start of the
|
---|
388 | * file.
|
---|
389 | */
|
---|
390 |
|
---|
391 | #define LINE_START(data, point) (point == data->buf || \
|
---|
392 | (point > data->point && \
|
---|
393 | (point[-1] == '\r' || point[-1] == '\n')))
|
---|
394 |
|
---|
395 | while (point < data->max) {
|
---|
396 | if (point[0] == '/' && point[1] == '*')
|
---|
397 | break;
|
---|
398 | if (LINE_START(data, point)) {
|
---|
399 | if (point[0] == '%' && point[1] == '{')
|
---|
400 | break;
|
---|
401 | if (point[0] == '#' && !strncmp(point + 1, "include \"", 9))
|
---|
402 | break;
|
---|
403 | }
|
---|
404 | point++;
|
---|
405 | }
|
---|
406 |
|
---|
407 | #undef LINE_START
|
---|
408 |
|
---|
409 | *startp = data->point;
|
---|
410 | *lenp = point - data->point;
|
---|
411 | }
|
---|
412 |
|
---|
413 | #ifndef VBOX
|
---|
414 | /* set this with a debugger to see exactly what libIDL sees */
|
---|
415 | static FILE *tracefile = NULL;
|
---|
416 | #endif
|
---|
417 |
|
---|
418 | static int
|
---|
419 | input_callback(IDL_input_reason reason, union IDL_input_data *cb_data,
|
---|
420 | gpointer user_data)
|
---|
421 | {
|
---|
422 | input_callback_state *callback_state = user_data;
|
---|
423 | input_data *data = callback_state->input_stack;
|
---|
424 | input_data *new_data = NULL;
|
---|
425 | unsigned int len, copy;
|
---|
426 | int rv;
|
---|
427 | char *start;
|
---|
428 |
|
---|
429 | switch(reason) {
|
---|
430 | case IDL_INPUT_REASON_INIT:
|
---|
431 | if (data == NULL || data->next == NULL) {
|
---|
432 | /*
|
---|
433 | * This is the first file being processed. As it's the target
|
---|
434 | * file, we only look for it in the first entry in the include
|
---|
435 | * path, which we assume to be the current directory.
|
---|
436 | */
|
---|
437 |
|
---|
438 | /* XXXmccabe proper assumption? Do we handle files in other
|
---|
439 | directories? */
|
---|
440 |
|
---|
441 | IncludePathEntry first_entry;
|
---|
442 |
|
---|
443 | first_entry.directory = callback_state->include_path->directory;
|
---|
444 | first_entry.next = NULL;
|
---|
445 |
|
---|
446 | new_data = new_input_data(cb_data->init.filename,
|
---|
447 | &first_entry);
|
---|
448 | } else {
|
---|
449 | new_data = new_input_data(cb_data->init.filename,
|
---|
450 | callback_state->include_path);
|
---|
451 | }
|
---|
452 |
|
---|
453 | if (!new_data)
|
---|
454 | return -1;
|
---|
455 |
|
---|
456 | IDL_file_set(new_data->filename, (int)new_data->lineno);
|
---|
457 | callback_state->input_stack = new_data;
|
---|
458 | return 0;
|
---|
459 |
|
---|
460 | case IDL_INPUT_REASON_FILL:
|
---|
461 | start = NULL;
|
---|
462 | len = 0;
|
---|
463 |
|
---|
464 | while (data->point >= data->max) {
|
---|
465 | if (!data->next)
|
---|
466 | return 0;
|
---|
467 |
|
---|
468 | /* Current file is done; revert to including file */
|
---|
469 | callback_state->input_stack = data->next;
|
---|
470 | free(data->filename);
|
---|
471 | RTMemFree(data->buf);
|
---|
472 | free(data);
|
---|
473 | data = callback_state->input_stack;
|
---|
474 |
|
---|
475 | IDL_file_set(data->filename, (int)data->lineno);
|
---|
476 | IDL_inhibit_pop();
|
---|
477 | }
|
---|
478 |
|
---|
479 | /*
|
---|
480 | * Now we scan for sequences which require special attention:
|
---|
481 | * \n#include begins an include statement
|
---|
482 | * \n%{ begins a raw-source block
|
---|
483 | * /\* begins a comment
|
---|
484 | *
|
---|
485 | * We used to be fancier here, so make sure that we sent the most
|
---|
486 | * data possible at any given time. To that end, we skipped over
|
---|
487 | * \n%{ raw \n%} blocks and then _continued_ the search for special
|
---|
488 | * sequences like \n#include or /\* comments .
|
---|
489 | *
|
---|
490 | * It was really ugly, though -- liberal use of goto! lots of implicit
|
---|
491 | * state! what fun! -- so now we just do this:
|
---|
492 | *
|
---|
493 | * if (special at start) {
|
---|
494 | * process that special -
|
---|
495 | * - raw: send it to libIDL, and don't look inside for specials
|
---|
496 | * - comments: adjust point and start over
|
---|
497 | * - includes: push new input_data struct for included file, and
|
---|
498 | * start over
|
---|
499 | * } else {
|
---|
500 | * scan for next special
|
---|
501 | * send data up to that special to libIDL
|
---|
502 | * }
|
---|
503 | *
|
---|
504 | * If len is set to zero, it is a sentinel value indicating we a comment
|
---|
505 | * or include was found, and parsing should start over.
|
---|
506 | *
|
---|
507 | * XXX const string foo = "/\*" will just screw us horribly.
|
---|
508 | * Hm but. We could treat strings as we treat raw blocks, eh?
|
---|
509 | */
|
---|
510 |
|
---|
511 | /*
|
---|
512 | * Order is important, so that you can have /\* comments and
|
---|
513 | * #includes within raw sections, and so that you can comment out
|
---|
514 | * #includes.
|
---|
515 | */
|
---|
516 | rv = NextIsRaw(data, &start, (int *)&len);
|
---|
517 | if (rv == -1) return -1;
|
---|
518 | if (!rv) {
|
---|
519 | /*
|
---|
520 | * When NextIsComment succeeds, it returns a 0 len (requesting a
|
---|
521 | * restart) and adjusts data->point to pick up after the comment.
|
---|
522 | */
|
---|
523 | rv = NextIsComment(data, &start, (int *)&len);
|
---|
524 | if (rv == -1) return -1;
|
---|
525 | if (!rv) {
|
---|
526 | /*
|
---|
527 | * NextIsInclude might push a new input_data struct; if so, it
|
---|
528 | * will return a 0 len, letting the callback pick up the new
|
---|
529 | * file the next time around.
|
---|
530 | */
|
---|
531 | rv = NextIsInclude(callback_state, &start, (int *)&len);
|
---|
532 | if (rv == -1) return -1;
|
---|
533 | if (!rv)
|
---|
534 | FindSpecial(data, &start, (int *)&len);
|
---|
535 | }
|
---|
536 | }
|
---|
537 |
|
---|
538 | if (len == 0) {
|
---|
539 | /*
|
---|
540 | * len == 0 is a sentinel value that means we found a comment or
|
---|
541 | * include. If we found a comment, point has been adjusted to
|
---|
542 | * point past the comment. If we found an include, a new input_data
|
---|
543 | * has been pushed. In both cases, calling the input_callback again
|
---|
544 | * will pick up the new state.
|
---|
545 | */
|
---|
546 | return input_callback(reason, cb_data, user_data);
|
---|
547 | }
|
---|
548 |
|
---|
549 | copy = MIN(len, (unsigned int) cb_data->fill.max_size);
|
---|
550 | memcpy(cb_data->fill.buffer, start, copy);
|
---|
551 | data->point = start + copy;
|
---|
552 |
|
---|
553 | #ifndef VBOX
|
---|
554 | if (tracefile)
|
---|
555 | fwrite(cb_data->fill.buffer, copy, 1, tracefile);
|
---|
556 | #endif
|
---|
557 |
|
---|
558 | return copy;
|
---|
559 |
|
---|
560 | case IDL_INPUT_REASON_ABORT:
|
---|
561 | case IDL_INPUT_REASON_FINISH:
|
---|
562 | while (data != NULL) {
|
---|
563 | input_data *next;
|
---|
564 |
|
---|
565 | next = data->next;
|
---|
566 | free(data->filename);
|
---|
567 | RTMemFree(data->buf);
|
---|
568 | free(data);
|
---|
569 | data = next;
|
---|
570 | }
|
---|
571 | callback_state->input_stack = NULL;
|
---|
572 | return 0;
|
---|
573 |
|
---|
574 | default:
|
---|
575 | g_error("unknown input reason %d!", reason);
|
---|
576 | return -1;
|
---|
577 | }
|
---|
578 | }
|
---|
579 |
|
---|
580 | static void
|
---|
581 | free_ghash_key(gpointer key, gpointer value, gpointer user_data)
|
---|
582 | {
|
---|
583 | /* We're only storing TRUE in the value... */
|
---|
584 | free(key);
|
---|
585 | }
|
---|
586 |
|
---|
587 | static void
|
---|
588 | free_gslist_data(gpointer data, gpointer user_data)
|
---|
589 | {
|
---|
590 | free(data);
|
---|
591 | }
|
---|
592 |
|
---|
593 | /* Pick up unlink. */
|
---|
594 | #include <unistd.h>
|
---|
595 |
|
---|
596 | int
|
---|
597 | xpidl_process_idl(char *filename, IncludePathEntry *include_path,
|
---|
598 | char *file_basename, ModeData *mode)
|
---|
599 | {
|
---|
600 | char *tmp, *outname, *real_outname = NULL;
|
---|
601 | IDL_tree top;
|
---|
602 | TreeState state;
|
---|
603 | int rv;
|
---|
604 | input_callback_state callback_state;
|
---|
605 | gboolean ok = TRUE;
|
---|
606 | backend *emitter;
|
---|
607 |
|
---|
608 | callback_state.input_stack = NULL;
|
---|
609 | callback_state.base_includes = NULL;
|
---|
610 | callback_state.include_path = include_path;
|
---|
611 | callback_state.already_included = g_hash_table_new(g_str_hash, g_str_equal);
|
---|
612 |
|
---|
613 | if (!callback_state.already_included) {
|
---|
614 | fprintf(stderr, "failed to create hashtable. out of memory?\n");
|
---|
615 | return 0;
|
---|
616 | }
|
---|
617 |
|
---|
618 | state.basename = xpidl_strdup(filename);
|
---|
619 |
|
---|
620 | /* if basename has an .extension, truncate it. */
|
---|
621 | tmp = strrchr(state.basename, '.');
|
---|
622 | if (tmp)
|
---|
623 | *tmp = '\0';
|
---|
624 |
|
---|
625 | if (!file_basename)
|
---|
626 | outname = xpidl_strdup(state.basename);
|
---|
627 | else
|
---|
628 | outname = xpidl_strdup(file_basename);
|
---|
629 |
|
---|
630 | /* so we don't include it again! */
|
---|
631 | g_hash_table_insert(callback_state.already_included,
|
---|
632 | xpidl_strdup(filename), (void *)TRUE);
|
---|
633 |
|
---|
634 | parsed_empty_file = FALSE;
|
---|
635 |
|
---|
636 | rv = IDL_parse_filename_with_input(filename, input_callback, &callback_state,
|
---|
637 | msg_callback, &top,
|
---|
638 | &state.ns,
|
---|
639 | IDLF_IGNORE_FORWARDS |
|
---|
640 | IDLF_XPIDL,
|
---|
641 | enable_warnings ? IDL_WARNING1 :
|
---|
642 | IDL_ERROR);
|
---|
643 | if (parsed_empty_file) {
|
---|
644 | /*
|
---|
645 | * If we've detected (via hack in msg_callback) that libIDL returned
|
---|
646 | * failure because it found a file with no IDL, set the parse tree to
|
---|
647 | * null and proceed. Allowing this is useful to permit .idl files that
|
---|
648 | * collect #includes.
|
---|
649 | */
|
---|
650 | top = NULL;
|
---|
651 | state.ns = NULL;
|
---|
652 | } else if (rv != IDL_SUCCESS) {
|
---|
653 | if (rv == -1) {
|
---|
654 | g_warning("Parse of %s failed: %s", filename, g_strerror(errno));
|
---|
655 | } else {
|
---|
656 | g_warning("Parse of %s failed", filename);
|
---|
657 | }
|
---|
658 | free(outname);
|
---|
659 | return 0;
|
---|
660 | }
|
---|
661 |
|
---|
662 | state.basename = xpidl_strdup(filename);
|
---|
663 | tmp = strrchr(state.basename, '.');
|
---|
664 | if (tmp)
|
---|
665 | *tmp = '\0';
|
---|
666 |
|
---|
667 | /* so xpidl_header.c can use it to generate a list of #include directives */
|
---|
668 | state.base_includes = callback_state.base_includes;
|
---|
669 |
|
---|
670 | emitter = mode->factory();
|
---|
671 | state.dispatch = emitter->dispatch_table;
|
---|
672 |
|
---|
673 | if (strcmp(outname, "-")) {
|
---|
674 | const char *fopen_mode;
|
---|
675 | char *out_basename;
|
---|
676 |
|
---|
677 | /* explicit_output_filename can't be true without a filename */
|
---|
678 | if (explicit_output_filename) {
|
---|
679 | real_outname = g_strdup(outname);
|
---|
680 | } else {
|
---|
681 |
|
---|
682 | if (!file_basename) {
|
---|
683 | out_basename = xpidl_basename(outname);
|
---|
684 | } else {
|
---|
685 | out_basename = outname;
|
---|
686 | }
|
---|
687 |
|
---|
688 | real_outname = g_strdup_printf("%s.%s", out_basename, mode->suffix);
|
---|
689 | if (out_basename != outname)
|
---|
690 | g_free(out_basename);
|
---|
691 | }
|
---|
692 |
|
---|
693 | /* Use binary write for typelib mode */
|
---|
694 | fopen_mode = (strcmp(mode->mode, "typelib")) ? "w" : "wb";
|
---|
695 | state.file = fopen(real_outname, fopen_mode);
|
---|
696 | if (!state.file) {
|
---|
697 | perror("error opening output file");
|
---|
698 | free(outname);
|
---|
699 | return 0;
|
---|
700 | }
|
---|
701 | } else {
|
---|
702 | state.file = stdout;
|
---|
703 | }
|
---|
704 | state.tree = top;
|
---|
705 | #ifdef VBOX_XPIDL_EMULATE_GENJIFACES
|
---|
706 | state.real_outname = real_outname;
|
---|
707 | #endif
|
---|
708 |
|
---|
709 | if (emitter->emit_prolog)
|
---|
710 | emitter->emit_prolog(&state);
|
---|
711 | if (state.tree) /* Only if we have a tree to process. */
|
---|
712 | ok = xpidl_process_node(&state);
|
---|
713 | if (emitter->emit_epilog)
|
---|
714 | emitter->emit_epilog(&state);
|
---|
715 |
|
---|
716 | if (state.file != stdout)
|
---|
717 | fclose(state.file);
|
---|
718 | free(state.basename);
|
---|
719 | free(outname);
|
---|
720 | g_hash_table_foreach(callback_state.already_included, free_ghash_key, NULL);
|
---|
721 | g_hash_table_destroy(callback_state.already_included);
|
---|
722 | g_slist_foreach(callback_state.base_includes, free_gslist_data, NULL);
|
---|
723 |
|
---|
724 | if (state.ns)
|
---|
725 | IDL_ns_free(state.ns);
|
---|
726 | if (top)
|
---|
727 | IDL_tree_free(top);
|
---|
728 |
|
---|
729 | if (real_outname != NULL) {
|
---|
730 | /*
|
---|
731 | * Delete partial output file on failure. (Mac does this in the plugin
|
---|
732 | * driver code, if the compiler returns failure.)
|
---|
733 | */
|
---|
734 | if (!ok)
|
---|
735 | unlink(real_outname);
|
---|
736 |
|
---|
737 | g_free(real_outname);
|
---|
738 | }
|
---|
739 |
|
---|
740 | return ok;
|
---|
741 | }
|
---|