VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/typelib/xpidl/xpidl_idl.c

Last change on this file was 103504, checked in by vboxsync, 3 months ago

libs/xpcom/xpidl: Fix possible but harmless dangling pointer, and fix missing but also harmless conversion from free() to RTMemFree() after previous changes, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.0 KB
Line 
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
48static gboolean parsed_empty_file;
49
50/*
51 * The bulk of the generation happens here.
52 */
53gboolean
54xpidl_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
67static int
68msg_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 */
95typedef 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 */
108typedef 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
117static void *
118file_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)
146extern FILE* mac_fopen(const char* filename, const char *mode);
147#endif
148
149static input_data *
150new_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 */
193static int
194NextIsRaw(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 */
232static int
233NextIsComment(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
282static int
283NextIsInclude(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
378static void
379FindSpecial(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 */
415static FILE *tracefile = NULL;
416#endif
417
418static int
419input_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
580static void
581free_ghash_key(gpointer key, gpointer value, gpointer user_data)
582{
583 /* We're only storing TRUE in the value... */
584 free(key);
585}
586
587static void
588free_gslist_data(gpointer data, gpointer user_data)
589{
590 free(data);
591}
592
593/* Pick up unlink. */
594#include <unistd.h>
595
596int
597xpidl_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}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use