1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
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
15 * The Original Code is mozilla.org code.
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.
23 * Michael Ang <mang@subcarrier.org>
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 MPL, 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 MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
40 * Common IDL-processing code.
46 static gboolean parsed_empty_file
;
49 * The bulk of the generation happens here.
52 xpidl_process_node(TreeState
*state
)
55 nodeHandler
*dispatch
, handler
;
57 XPT_ASSERT(state
->tree
);
58 type
= IDL_NODE_TYPE(state
->tree
);
60 if ((dispatch
= state
->dispatch
) && (handler
= dispatch
[type
]))
61 return handler(state
);
66 msg_callback(int level
, int num
, int line
, const char *file
,
69 char *warning_message
;
72 * Egregious hack to permit empty files.
73 * XXX libIDL needs an API to detect this case robustly.
75 if (0 == strcmp(message
, "File empty after optimization")) {
76 parsed_empty_file
= TRUE
;
81 file
= "<unknown file>";
82 warning_message
= g_strdup_printf("%s:%d: %s\n", file
, line
, message
);
84 fputs(warning_message
, stderr
);
86 g_free(warning_message
);
91 * To keep track of the state associated with a given input file. The 'next'
92 * field lets us maintain a stack of input files.
94 typedef struct input_data
{
95 char *filename
; /* where did I come from? */
96 unsigned int lineno
; /* last lineno processed */
97 char *buf
; /* contents of file */
98 char *point
; /* next char to feed to libIDL */
99 char *max
; /* 1 past last char in buf */
100 struct input_data
*next
; /* file from which we were included */
104 * Passed to us by libIDL. Holds global information and the current stack of
107 typedef struct input_callback_state
{
108 struct input_data
*input_stack
; /* linked list of input_data */
109 GHashTable
*already_included
; /* to prevent redundant includes */
110 IncludePathEntry
*include_path
; /* search path for included files */
111 GSList
*base_includes
; /* to accumulate #includes from *first* file;
112 * for passing thru TreeState to
113 * xpidl_header backend. */
114 } input_callback_state
;
117 fopen_from_includes(char **filename
, const char *mode
,
118 IncludePathEntry
*include_path
)
120 IncludePathEntry
*current_path
= include_path
;
123 if (!strcmp(*filename
, "-"))
126 if ((*filename
)[0] != '/') {
127 while (current_path
) {
128 pathname
= g_strdup_printf("%s" G_DIR_SEPARATOR_S
"%s",
129 current_path
->directory
, *filename
);
132 inputfile
= fopen(pathname
, mode
);
135 *filename
= xpidl_strdup(pathname
);
140 current_path
= current_path
->next
;
143 inputfile
= fopen(*filename
, mode
);
151 new_input_data(char **filename
, IncludePathEntry
*include_path
)
153 input_data
*new_data
;
159 #if defined(XP_OS2) || defined(XP_WIN32)
161 * if filename is fully qualified (starts with driver letter), then
162 * just call fopen(); else, go with fopen_from_includes()
164 if( (*filename
)[1] == ':' )
165 inputfile
= fopen(*filename
, "r");
167 inputfile
= fopen_from_includes(filename
, "r", include_path
);
169 inputfile
= fopen_from_includes(filename
, "r", include_path
);
175 * Rather than try to keep track of many different varieties of state
176 * around the boundaries of a circular buffer, we just read in the entire
179 * We iteratively grow the buffer here; an alternative would be to use
180 * stat to find the exact buffer size we need, as xpt_dump does.
182 for (buffer_size
= 8191; ; buffer_size
*= 2) {
184 buffer
= realloc(buffer
, buffer_size
+ 1); /* +1 for trailing nul */
185 just_read
= fread(buffer
+ offset
, 1, buffer_size
- offset
, inputfile
);
186 if (ferror(inputfile
))
189 if (just_read
< buffer_size
- offset
|| just_read
== 0) {
199 new_data
= xpidl_malloc(sizeof (struct input_data
));
200 new_data
->point
= new_data
->buf
= buffer
;
201 new_data
->max
= buffer
+ offset
;
202 *new_data
->max
= '\0';
203 new_data
->filename
= *filename
;
204 /* libIDL expects the line number to be that of the *next* line */
205 new_data
->lineno
= 2;
206 new_data
->next
= NULL
;
209 fprintf(deps
, " \\\n\t%s", *filename
);
214 /* process pending raw section */
216 NextIsRaw(input_data
*data
, char **startp
, int *lenp
)
221 * XXXmccabe still needed: an in_raw flag to handle the case where we're in
222 * a raw block, but haven't managed to copy it all to xpidl. This will
223 * happen when we have a raw block larger than
224 * IDL_input_data->fill.max_size (currently 8192.)
226 if (!(data
->point
[0] == '%' && data
->point
[1] == '{'))
229 start
= *startp
= data
->point
;
232 while (start
< data
->max
&& (end
= strstr(start
, "%}"))) {
233 if (end
[-1] == '\r' ||
239 if (end
&& start
< data
->max
) {
240 *lenp
= end
- data
->point
+ 2;
243 const char *filename
;
246 IDL_file_get(&filename
, &lineno
);
247 msg_callback(IDL_ERROR
, 0, lineno
, filename
,
248 "unterminated %{ block");
253 /* process pending comment */
255 NextIsComment(input_data
*data
, char **startp
, int *lenp
)
259 if (!(data
->point
[0] == '/' && data
->point
[1] == '*'))
262 end
= strstr(data
->point
, "*/");
265 int skippedLines
= 0;
268 /* get current lineno */
269 IDL_file_get(NULL
,(int *)&data
->lineno
);
272 for (tempPoint
= data
->point
; tempPoint
< end
; tempPoint
++) {
273 if (*tempPoint
== '\n')
277 data
->lineno
+= skippedLines
;
278 IDL_file_set(data
->filename
, (int)data
->lineno
);
282 /* If it's a ** comment, tell libIDL about it. */
283 if (data
->point
[2] == '*') {
284 /* hack termination. +2 to get past '*' '/' */
287 IDL_queue_new_ident_comment(data
->point
);
291 data
->point
= *startp
; /* XXXmccabe move this out of function? */
294 const char *filename
;
297 IDL_file_get(&filename
, &lineno
);
298 msg_callback(IDL_ERROR
, 0, lineno
, filename
,
299 "unterminated comment");
305 NextIsInclude(input_callback_state
*callback_state
, char **startp
,
308 input_data
*data
= callback_state
->input_stack
;
309 input_data
*new_data
;
310 char *filename
, *end
;
313 /* process the #include that we're in now */
314 if (strncmp(data
->point
, "#include \"", 10)) {
318 filename
= data
->point
+ 10; /* skip #include " */
319 XPT_ASSERT(filename
< data
->max
);
321 while (end
< data
->max
) {
322 if (*end
== '\"' || *end
== '\n' || *end
== '\r')
329 * Didn't find end of include file. Scan 'til next whitespace to find
330 * some reasonable approximation of the filename, and use it to report
335 while (end
< data
->max
) {
336 if (*end
== ' ' || *end
== '\n' || *end
== '\r' || *end
== '\t')
342 /* make sure we have accurate line info */
343 IDL_file_get(&scratch
, (int *)&data
->lineno
);
345 "%s:%d: didn't find end of quoted include name \"%s\n",
346 scratch
, data
->lineno
, filename
);
353 if (data
->next
== NULL
) {
355 * If we're in the initial file, add this filename to the list
356 * of filenames to be turned into #include "filename.h"
357 * directives in xpidl_header.c. We do it here rather than in the
358 * block below so it still gets added to the list even if it's
359 * already been recursively included from some other file.
361 char *filename_cp
= xpidl_strdup(filename
);
363 /* note that g_slist_append accepts and likes null as list-start. */
364 callback_state
->base_includes
=
365 g_slist_append(callback_state
->base_includes
, filename_cp
);
368 /* store offset for when we pop, or if we skip this one */
369 data
->point
= *startp
;
371 if (!g_hash_table_lookup(callback_state
->already_included
, filename
)) {
372 filename
= xpidl_strdup(filename
);
373 g_hash_table_insert(callback_state
->already_included
,
374 filename
, (void *)TRUE
);
375 filename
= xpidl_strdup(filename
);
376 new_data
= new_input_data(&filename
, callback_state
->include_path
);
379 IDL_file_get(&scratch
, (int *)&data
->lineno
);
381 g_strdup_printf("can't open included file %s for reading\n",
383 msg_callback(IDL_ERROR
, 0,
384 data
->lineno
, scratch
, error_message
);
385 g_free(error_message
);
390 new_data
->next
= data
;
391 /* tell libIDL to exclude this IDL from the toplevel tree */
393 IDL_file_get(&scratch
, (int *)&data
->lineno
);
394 callback_state
->input_stack
= new_data
;
395 IDL_file_set(new_data
->filename
, (int)new_data
->lineno
);
398 *lenp
= 0; /* this is magic, see the comment below */
403 FindSpecial(input_data
*data
, char **startp
, int *lenp
)
405 char *point
= data
->point
;
407 /* magic sequences are:
410 * "#include \"" include
411 * The first and last want a newline [\r\n] before, or the start of the
415 #define LINE_START(data, point) (point == data->buf || \
416 (point > data->point && \
417 (point[-1] == '\r' || point[-1] == '\n')))
419 while (point
< data
->max
) {
420 if (point
[0] == '/' && point
[1] == '*')
422 if (LINE_START(data
, point
)) {
423 if (point
[0] == '%' && point
[1] == '{')
425 if (point
[0] == '#' && !strncmp(point
+ 1, "include \"", 9))
433 *startp
= data
->point
;
434 *lenp
= point
- data
->point
;
437 /* set this with a debugger to see exactly what libIDL sees */
438 static FILE *tracefile
;
441 input_callback(IDL_input_reason reason
, union IDL_input_data
*cb_data
,
444 input_callback_state
*callback_state
= user_data
;
445 input_data
*data
= callback_state
->input_stack
;
446 input_data
*new_data
= NULL
;
447 unsigned int len
, copy
;
449 char *start
, *filename
;
452 case IDL_INPUT_REASON_INIT
:
453 filename
= xpidl_strdup(cb_data
->init
.filename
);
454 if (data
== NULL
|| data
->next
== NULL
) {
456 * This is the first file being processed. As it's the target
457 * file, we only look for it in the first entry in the include
458 * path, which we assume to be the current directory.
461 /* XXXmccabe proper assumption? Do we handle files in other
464 IncludePathEntry first_entry
;
466 first_entry
.directory
= callback_state
->include_path
->directory
;
467 first_entry
.next
= NULL
;
469 new_data
= new_input_data(&filename
, &first_entry
);
471 new_data
= new_input_data(&filename
, callback_state
->include_path
);
479 IDL_file_set(new_data
->filename
, (int)new_data
->lineno
);
480 callback_state
->input_stack
= new_data
;
483 case IDL_INPUT_REASON_FILL
:
487 while (data
->point
>= data
->max
) {
491 /* Current file is done; revert to including file */
492 callback_state
->input_stack
= data
->next
;
493 free(data
->filename
);
496 data
= callback_state
->input_stack
;
498 IDL_file_set(data
->filename
, (int)data
->lineno
);
503 * Now we scan for sequences which require special attention:
504 * \n#include begins an include statement
505 * \n%{ begins a raw-source block
506 * /\* begins a comment
508 * We used to be fancier here, so make sure that we sent the most
509 * data possible at any given time. To that end, we skipped over
510 * \n%{ raw \n%} blocks and then _continued_ the search for special
511 * sequences like \n#include or /\* comments .
513 * It was really ugly, though -- liberal use of goto! lots of implicit
514 * state! what fun! -- so now we just do this:
516 * if (special at start) {
517 * process that special -
518 * - raw: send it to libIDL, and don't look inside for specials
519 * - comments: adjust point and start over
520 * - includes: push new input_data struct for included file, and
523 * scan for next special
524 * send data up to that special to libIDL
527 * If len is set to zero, it is a sentinel value indicating we a comment
528 * or include was found, and parsing should start over.
530 * XXX const string foo = "/\*" will just screw us horribly.
531 * Hm but. We could treat strings as we treat raw blocks, eh?
535 * Order is important, so that you can have /\* comments and
536 * #includes within raw sections, and so that you can comment out
539 rv
= NextIsRaw(data
, &start
, (int *)&len
);
540 if (rv
== -1) return -1;
543 * When NextIsComment succeeds, it returns a 0 len (requesting a
544 * restart) and adjusts data->point to pick up after the comment.
546 rv
= NextIsComment(data
, &start
, (int *)&len
);
547 if (rv
== -1) return -1;
550 * NextIsInclude might push a new input_data struct; if so, it
551 * will return a 0 len, letting the callback pick up the new
552 * file the next time around.
554 rv
= NextIsInclude(callback_state
, &start
, (int *)&len
);
555 if (rv
== -1) return -1;
557 FindSpecial(data
, &start
, (int *)&len
);
563 * len == 0 is a sentinel value that means we found a comment or
564 * include. If we found a comment, point has been adjusted to
565 * point past the comment. If we found an include, a new input_data
566 * has been pushed. In both cases, calling the input_callback again
567 * will pick up the new state.
569 return input_callback(reason
, cb_data
, user_data
);
572 copy
= MIN(len
, (unsigned int) cb_data
->fill
.max_size
);
573 memcpy(cb_data
->fill
.buffer
, start
, copy
);
574 data
->point
= start
+ copy
;
577 fwrite(cb_data
->fill
.buffer
, copy
, 1, tracefile
);
581 case IDL_INPUT_REASON_ABORT
:
582 case IDL_INPUT_REASON_FINISH
:
583 while (data
!= NULL
) {
587 free(data
->filename
);
595 g_error("unknown input reason %d!", reason
);
601 free_ghash_key(gpointer key
, gpointer value
, gpointer user_data
)
603 /* We're only storing TRUE in the value... */
608 free_gslist_data(gpointer data
, gpointer user_data
)
613 /* Pick up unlink. */
617 /* We get it from stdio.h. */
618 #define unlink _unlink
622 xpidl_process_idl(char *filename
, IncludePathEntry
*include_path
,
623 char *file_basename
, ModeData
*mode
)
625 char *tmp
, *outname
, *real_outname
= NULL
;
629 input_callback_state callback_state
;
633 callback_state
.input_stack
= NULL
;
634 callback_state
.base_includes
= NULL
;
635 callback_state
.include_path
= include_path
;
636 callback_state
.already_included
= g_hash_table_new(g_str_hash
, g_str_equal
);
638 if (!callback_state
.already_included
) {
639 fprintf(stderr
, "failed to create hashtable. out of memory?\n");
643 state
.basename
= xpidl_strdup(filename
);
645 /* if basename has an .extension, truncate it. */
646 tmp
= strrchr(state
.basename
, '.');
651 outname
= xpidl_strdup(state
.basename
);
653 outname
= xpidl_strdup(file_basename
);
655 /* so we don't include it again! */
656 g_hash_table_insert(callback_state
.already_included
,
657 xpidl_strdup(filename
), (void *)TRUE
);
659 parsed_empty_file
= FALSE
;
661 rv
= IDL_parse_filename_with_input(filename
, input_callback
, &callback_state
,
664 IDLF_IGNORE_FORWARDS
|
666 enable_warnings
? IDL_WARNING1
:
668 if (parsed_empty_file
) {
670 * If we've detected (via hack in msg_callback) that libIDL returned
671 * failure because it found a file with no IDL, set the parse tree to
672 * null and proceed. Allowing this is useful to permit .idl files that
677 } else if (rv
!= IDL_SUCCESS
) {
679 g_warning("Parse of %s failed: %s", filename
, g_strerror(errno
));
681 g_warning("Parse of %s failed", filename
);
686 state
.basename
= xpidl_strdup(filename
);
687 tmp
= strrchr(state
.basename
, '.');
691 /* so xpidl_header.c can use it to generate a list of #include directives */
692 state
.base_includes
= callback_state
.base_includes
;
694 emitter
= mode
->factory();
695 state
.dispatch
= emitter
->dispatch_table
;
697 if (strcmp(outname
, "-")) {
698 const char *fopen_mode
;
699 const char *out_basename
;
701 /* explicit_output_filename can't be true without a filename */
702 if (explicit_output_filename
) {
703 real_outname
= g_strdup(outname
);
706 *This combination seems a little strange, what about OS/2?
707 * Assume it's some build issue
709 #if defined(XP_UNIX) || defined(XP_WIN)
710 if (!file_basename
) {
711 out_basename
= xpidl_basename(outname
);
713 out_basename
= outname
;
716 out_basename
= outname
;
718 real_outname
= g_strdup_printf("%s.%s", out_basename
, mode
->suffix
);
721 /* don't create/open file here for Java */
722 if (strcmp(mode
->mode
, "java") == 0) {
723 state
.filename
= real_outname
;
725 /* Use binary write for typelib mode */
726 fopen_mode
= (strcmp(mode
->mode
, "typelib")) ? "w" : "wb";
727 state
.file
= fopen(real_outname
, fopen_mode
);
729 perror("error opening output file");
738 if (emitter
->emit_prolog
)
739 emitter
->emit_prolog(&state
);
740 if (state
.tree
) /* Only if we have a tree to process. */
741 ok
= xpidl_process_node(&state
);
742 if (emitter
->emit_epilog
)
743 emitter
->emit_epilog(&state
);
745 if (strcmp(mode
->mode
, "java") != 0) {
746 if (state
.file
!= stdout
)
750 free(state
.basename
);
752 g_hash_table_foreach(callback_state
.already_included
, free_ghash_key
, NULL
);
753 g_hash_table_destroy(callback_state
.already_included
);
754 g_slist_foreach(callback_state
.base_includes
, free_gslist_data
, NULL
);
757 IDL_ns_free(state
.ns
);
761 if (strcmp(mode
->mode
, "java") != 0) {
762 if (real_outname
!= NULL
) {
764 * Delete partial output file on failure. (Mac does this in the
765 * plugin driver code, if the compiler returns failure.)
767 #if defined(XP_UNIX) || defined(XP_WIN)
769 unlink(real_outname
);
771 g_free(real_outname
);
779 * Our own version of IDL_tree_warning, which we use when IDL_tree_warning
783 xpidl_tree_warning(IDL_tree p
, int level
, const char *fmt
, ...)
789 /* XXX need to check against __IDL_max_msg_level, no accessor */
791 msg
= g_strdup_vprintf(fmt
, ap
);
801 /* call our message callback, like IDL_tree_warning would */
802 msg_callback(level
, 0, lineno
, file
, msg
);