aidl.cpp revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
1
2#include "aidl_language.h"
3#include "options.h"
4#include "search_path.h"
5#include "Type.h"
6#include "generate_java.h"
7#include <unistd.h>
8#include <fcntl.h>
9#include <sys/param.h>
10#include <sys/stat.h>
11
12#include <stdio.h>
13#include <map>
14
15#ifdef HAVE_MS_C_RUNTIME
16#include <io.h>
17#include <sys/stat.h>
18#endif
19
20#ifndef O_BINARY
21#  define O_BINARY  0
22#endif
23
24using namespace std;
25
26static void
27test_document(document_item_type* d)
28{
29    while (d) {
30        if (d->item_type == INTERFACE_TYPE) {
31            interface_type* c = (interface_type*)d;
32            printf("interface %s %s {\n", c->package, c->name.data);
33            interface_item_type *q = (interface_item_type*)c->interface_items;
34            while (q) {
35                if (q->item_type == METHOD_TYPE) {
36                    method_type *m = (method_type*)q;
37                    printf("  %s %s(", m->type.type.data, m->name.data);
38                    arg_type *p = m->args;
39                    while (p) {
40                        printf("%s %s",p->type.type.data,p->name.data);
41                        if (p->next) printf(", ");
42                        p=p->next;
43                    }
44                    printf(")");
45                    printf(";\n");
46                }
47                q=q->next;
48            }
49            printf("}\n");
50        }
51        else if (d->item_type == PARCELABLE_TYPE) {
52            parcelable_type* b = (parcelable_type*)d;
53            printf("parcelable %s %s;\n", b->package, b->name.data);
54        }
55        else {
56            printf("UNKNOWN d=0x%08x d->item_type=%ld\n", (long)d, d->item_type);
57        }
58        d = d->next;
59    }
60}
61
62// ==========================================================
63int
64convert_direction(const char* direction)
65{
66    if (direction == NULL) {
67        return IN_PARAMETER;
68    }
69    if (0 == strcmp(direction, "in")) {
70        return IN_PARAMETER;
71    }
72    if (0 == strcmp(direction, "out")) {
73        return OUT_PARAMETER;
74    }
75    return INOUT_PARAMETER;
76}
77
78// ==========================================================
79struct import_info {
80    const char* from;
81    const char* filename;
82    buffer_type statement;
83    const char* neededClass;
84    document_item_type* doc;
85    struct import_info* next;
86};
87
88document_item_type* g_document = NULL;
89import_info* g_imports = NULL;
90
91static void
92main_document_parsed(document_item_type* d)
93{
94    g_document = d;
95}
96
97static void
98main_import_parsed(buffer_type* statement)
99{
100    import_info* import = (import_info*)malloc(sizeof(import_info));
101    memset(import, 0, sizeof(import_info));
102    import->from = strdup(g_currentFilename);
103    import->statement.lineno = statement->lineno;
104    import->statement.data = strdup(statement->data);
105    import->statement.extra = NULL;
106    import->next = g_imports;
107    import->neededClass = parse_import_statement(statement->data);
108    g_imports = import;
109}
110
111static ParserCallbacks g_mainCallbacks = {
112    &main_document_parsed,
113    &main_import_parsed
114};
115
116char*
117parse_import_statement(const char* text)
118{
119    const char* end;
120    int len;
121
122    while (isspace(*text)) {
123        text++;
124    }
125    while (!isspace(*text)) {
126        text++;
127    }
128    while (isspace(*text)) {
129        text++;
130    }
131    end = text;
132    while (!isspace(*end) && *end != ';') {
133        end++;
134    }
135    len = end-text;
136
137    char* rv = (char*)malloc(len+1);
138    memcpy(rv, text, len);
139    rv[len] = '\0';
140
141    return rv;
142}
143
144// ==========================================================
145static void
146import_import_parsed(buffer_type* statement)
147{
148}
149
150static ParserCallbacks g_importCallbacks = {
151    &main_document_parsed,
152    &import_import_parsed
153};
154
155// ==========================================================
156static int
157check_filename(const char* filename, const char* package, buffer_type* name)
158{
159    const char* p;
160    string expected;
161    string fn;
162    size_t len;
163    char cwd[MAXPATHLEN];
164    bool valid = false;
165
166#ifdef HAVE_WINDOWS_PATHS
167    if (isalpha(filename[0]) && filename[1] == ':'
168        && filename[2] == OS_PATH_SEPARATOR) {
169#else
170    if (filename[0] == OS_PATH_SEPARATOR) {
171#endif
172        fn = filename;
173    } else {
174        fn = getcwd(cwd, sizeof(cwd));
175        len = fn.length();
176        if (fn[len-1] != OS_PATH_SEPARATOR) {
177            fn += OS_PATH_SEPARATOR;
178        }
179        fn += filename;
180    }
181
182    if (package) {
183        expected = package;
184        expected += '.';
185    }
186
187    len = expected.length();
188    for (size_t i=0; i<len; i++) {
189        if (expected[i] == '.') {
190            expected[i] = OS_PATH_SEPARATOR;
191        }
192    }
193
194    p = strchr(name->data, '.');
195    len = p ? p-name->data : strlen(name->data);
196    expected.append(name->data, len);
197
198    expected += ".aidl";
199
200    len = fn.length();
201    valid = (len >= expected.length());
202
203    if (valid) {
204        p = fn.c_str() + (len - expected.length());
205
206#ifdef HAVE_WINDOWS_PATHS
207        if (OS_PATH_SEPARATOR != '/') {
208            // Input filename under cygwin most likely has / separators
209            // whereas the expected string uses \\ separators. Adjust
210            // them accordingly.
211          for (char *c = const_cast<char *>(p); *c; ++c) {
212                if (*c == '/') *c = OS_PATH_SEPARATOR;
213            }
214        }
215#endif
216
217#ifdef OS_CASE_SENSITIVE
218        valid = (expected == p);
219#else
220        valid = !strcasecmp(expected.c_str(), p);
221#endif
222    }
223
224    if (!valid) {
225        fprintf(stderr, "%s:%d interface %s should be declared in a file"
226                " called %s.\n",
227                filename, name->lineno, name->data, expected.c_str());
228        return 1;
229    }
230
231    return 0;
232}
233
234static int
235check_filenames(const char* filename, document_item_type* items)
236{
237    int err = 0;
238    while (items) {
239        if (items->item_type == PARCELABLE_TYPE) {
240            parcelable_type* p = (parcelable_type*)items;
241            err |= check_filename(filename, p->package, &p->name);
242        }
243        else if (items->item_type == INTERFACE_TYPE) {
244            interface_type* c = (interface_type*)items;
245            err |= check_filename(filename, c->package, &c->name);
246        }
247        else {
248            fprintf(stderr, "aidl: internal error unkown document type %d.\n",
249                        items->item_type);
250            return 1;
251        }
252        items = items->next;
253    }
254    return err;
255}
256
257// ==========================================================
258static const char*
259kind_to_string(int kind)
260{
261    switch (kind)
262    {
263        case Type::INTERFACE:
264            return "an interface";
265        case Type::PARCELABLE:
266            return "a parcelable";
267        default:
268            return "ERROR";
269    }
270}
271
272static char*
273rfind(char* str, char c)
274{
275    char* p = str + strlen(str) - 1;
276    while (p >= str) {
277        if (*p == c) {
278            return p;
279        }
280        p--;
281    }
282    return NULL;
283}
284
285static int
286gather_types(const char* filename, document_item_type* items)
287{
288    int err = 0;
289    while (items) {
290        Type* type;
291        if (items->item_type == PARCELABLE_TYPE) {
292            parcelable_type* p = (parcelable_type*)items;
293            type = new ParcelableType(p->package ? p->package : "",
294                            p->name.data, false, filename, p->name.lineno);
295        }
296        else if (items->item_type == INTERFACE_TYPE) {
297            interface_type* c = (interface_type*)items;
298            type = new InterfaceType(c->package ? c->package : "",
299                            c->name.data, false, c->oneway,
300                            filename, c->name.lineno);
301        }
302        else {
303            fprintf(stderr, "aidl: internal error %s:%d\n", __FILE__, __LINE__);
304            return 1;
305        }
306
307        Type* old = NAMES.Find(type->QualifiedName());
308        if (old == NULL) {
309            NAMES.Add(type);
310
311            if (items->item_type == INTERFACE_TYPE) {
312                // for interfaces, also add the stub and proxy types, we don't
313                // bother checking these for duplicates, because the parser
314                // won't let us do it.
315                interface_type* c = (interface_type*)items;
316
317                string name = c->name.data;
318                name += ".Stub";
319                Type* stub = new Type(c->package ? c->package : "",
320                                        name, Type::GENERATED, false, false,
321                                        filename, c->name.lineno);
322                NAMES.Add(stub);
323
324                name = c->name.data;
325                name += ".Stub.Proxy";
326                Type* proxy = new Type(c->package ? c->package : "",
327                                        name, Type::GENERATED, false, false,
328                                        filename, c->name.lineno);
329                NAMES.Add(proxy);
330            }
331        } else {
332            if (old->Kind() == Type::BUILT_IN) {
333                fprintf(stderr, "%s:%d attempt to redefine built in class %s\n",
334                            filename, type->DeclLine(),
335                            type->QualifiedName().c_str());
336                err = 1;
337            }
338            else if (type->Kind() != old->Kind()) {
339                const char* oldKind = kind_to_string(old->Kind());
340                const char* newKind = kind_to_string(type->Kind());
341
342                fprintf(stderr, "%s:%d attempt to redefine %s as %s,\n",
343                            filename, type->DeclLine(),
344                            type->QualifiedName().c_str(), newKind);
345                fprintf(stderr, "%s:%d    previously defined here as %s.\n",
346                            old->DeclFile().c_str(), old->DeclLine(), oldKind);
347                err = 1;
348            }
349        }
350
351        items = items->next;
352    }
353    return err;
354}
355
356// ==========================================================
357static bool
358matches_keyword(const char* str)
359{
360    static const char* KEYWORDS[] = { "abstract", "assert", "boolean", "break",
361        "byte", "case", "catch", "char", "class", "const", "continue",
362        "default", "do", "double", "else", "enum", "extends", "final",
363        "finally", "float", "for", "goto", "if", "implements", "import",
364        "instanceof", "int", "interface", "long", "native", "new", "package",
365        "private", "protected", "public", "return", "short", "static",
366        "strictfp", "super", "switch", "synchronized", "this", "throw",
367        "throws", "transient", "try", "void", "volatile", "while",
368        "true", "false", "null",
369        NULL
370    };
371    const char** k = KEYWORDS;
372    while (*k) {
373        if (0 == strcmp(str, *k)) {
374            return true;
375        }
376        k++;
377    }
378    return false;
379}
380
381static int
382check_method(const char* filename, method_type* m)
383{
384    int err = 0;
385
386    // return type
387    Type* returnType = NAMES.Search(m->type.type.data);
388    if (returnType == NULL) {
389        fprintf(stderr, "%s:%d unknown return type %s\n", filename,
390                    m->type.type.lineno, m->type.type.data);
391        err = 1;
392        return err;
393    }
394
395    if (!returnType->CanBeMarshalled()) {
396        fprintf(stderr, "%s:%d return type %s can't be marshalled.\n", filename,
397                    m->type.type.lineno, m->type.type.data);
398        err = 1;
399    }
400
401    if (m->type.dimension > 0 && !returnType->CanBeArray()) {
402        fprintf(stderr, "%s:%d return type %s%s can't be an array.\n", filename,
403                m->type.array_token.lineno, m->type.type.data,
404                m->type.array_token.data);
405        err = 1;
406    }
407
408    if (m->type.dimension > 1) {
409        fprintf(stderr, "%s:%d return type %s%s only one"
410                " dimensional arrays are supported\n", filename,
411                m->type.array_token.lineno, m->type.type.data,
412                m->type.array_token.data);
413        err = 1;
414    }
415
416    int index = 1;
417
418    arg_type* arg = m->args;
419    while (arg) {
420        Type* t = NAMES.Search(arg->type.type.data);
421
422        // check the arg type
423        if (t == NULL) {
424            fprintf(stderr, "%s:%d parameter %s (%d) unknown type %s\n",
425                    filename, m->type.type.lineno, arg->name.data, index,
426                    arg->type.type.data);
427            err = 1;
428            goto next;
429        }
430
431        if (!t->CanBeMarshalled()) {
432            fprintf(stderr, "%s:%d parameter %d: '%s %s' can't be marshalled.\n",
433                        filename, m->type.type.lineno, index,
434                        arg->type.type.data, arg->name.data);
435            err = 1;
436        }
437
438        if (arg->direction.data == NULL
439                && (arg->type.dimension != 0 || t->CanBeOutParameter())) {
440            fprintf(stderr, "%s:%d parameter %d: '%s %s' can be an out"
441                                " parameter, so you must declare it as in,"
442                                " out or inout.\n",
443                        filename, m->type.type.lineno, index,
444                        arg->type.type.data, arg->name.data);
445            err = 1;
446        }
447
448        if (convert_direction(arg->direction.data) != IN_PARAMETER
449                && !t->CanBeOutParameter()
450                && arg->type.dimension == 0) {
451            fprintf(stderr, "%s:%d parameter %d: '%s %s %s' can only be an in"
452                            " parameter.\n",
453                        filename, m->type.type.lineno, index,
454                        arg->direction.data, arg->type.type.data,
455                        arg->name.data);
456            err = 1;
457        }
458
459        if (arg->type.dimension > 0 && !t->CanBeArray()) {
460            fprintf(stderr, "%s:%d parameter %d: '%s %s%s %s' can't be an"
461                    " array.\n", filename,
462                    m->type.array_token.lineno, index, arg->direction.data,
463                    arg->type.type.data, arg->type.array_token.data,
464                    arg->name.data);
465            err = 1;
466        }
467
468        if (arg->type.dimension > 1) {
469            fprintf(stderr, "%s:%d parameter %d: '%s %s%s %s' only one"
470                    " dimensional arrays are supported\n", filename,
471                    m->type.array_token.lineno, index, arg->direction.data,
472                    arg->type.type.data, arg->type.array_token.data,
473                    arg->name.data);
474            err = 1;
475        }
476
477        // check that the name doesn't match a keyword
478        if (matches_keyword(arg->name.data)) {
479            fprintf(stderr, "%s:%d parameter %d %s is named the same as a"
480                    " Java keyword\n",
481                    filename, m->name.lineno, index, arg->name.data);
482            err = 1;
483        }
484
485next:
486        index++;
487        arg = arg->next;
488    }
489
490    return err;
491}
492
493static int
494check_types(const char* filename, document_item_type* items)
495{
496    int err = 0;
497    while (items) {
498        // (nothing to check for PARCELABLE_TYPE)
499        if (items->item_type == INTERFACE_TYPE) {
500            map<string,method_type*> methodNames;
501            interface_type* c = (interface_type*)items;
502
503            interface_item_type* member = c->interface_items;
504            while (member) {
505                if (member->item_type == METHOD_TYPE) {
506                    method_type* m = (method_type*)member;
507
508                    err |= check_method(filename, m);
509
510                    // prevent duplicate methods
511                    if (methodNames.find(m->name.data) == methodNames.end()) {
512                        methodNames[m->name.data] = m;
513                    } else {
514                        fprintf(stderr,"%s:%d attempt to redefine method %s,\n",
515                                filename, m->name.lineno, m->name.data);
516                        method_type* old = methodNames[m->name.data];
517                        fprintf(stderr, "%s:%d    previously defined here.\n",
518                                filename, old->name.lineno);
519                        err = 1;
520                    }
521                }
522                member = member->next;
523            }
524        }
525
526        items = items->next;
527    }
528    return err;
529}
530
531// ==========================================================
532static int
533exactly_one_interface(const char* filename, const document_item_type* items, const Options& options,
534                      bool* onlyParcelable)
535{
536    if (items == NULL) {
537        fprintf(stderr, "%s: file does not contain any interfaces\n",
538                            filename);
539        return 1;
540    }
541
542    const document_item_type* next = items->next;
543    if (items->next != NULL) {
544        int lineno = -1;
545        if (next->item_type == INTERFACE_TYPE) {
546            lineno = ((interface_type*)next)->interface_token.lineno;
547        }
548        else if (next->item_type == PARCELABLE_TYPE) {
549            lineno = ((parcelable_type*)next)->parcelable_token.lineno;
550        }
551        fprintf(stderr, "%s:%d aidl can only handle one interface per file\n",
552                            filename, lineno);
553        return 1;
554    }
555
556    if (items->item_type == PARCELABLE_TYPE) {
557        *onlyParcelable = true;
558        if (options.failOnParcelable) {
559            fprintf(stderr, "%s:%d aidl can only generate code for interfaces, not"
560                            " parcelables,\n", filename,
561                            ((parcelable_type*)items)->parcelable_token.lineno);
562            fprintf(stderr, "%s:%d .aidl files that only declare parcelables "
563                            "don't need to go in the Makefile.\n", filename,
564                            ((parcelable_type*)items)->parcelable_token.lineno);
565            return 1;
566        }
567    } else {
568        *onlyParcelable = false;
569    }
570
571    return 0;
572}
573
574// ==========================================================
575void
576generate_dep_file(const Options& options)
577{
578   /* we open the file in binary mode to ensure that the same output is
579    * generated on all platforms !!
580    */
581    FILE* to = fopen(options.depFileName.c_str(), "wb");
582    if (to == NULL) {
583        return;
584    }
585
586    const char* slash = "\\";
587    import_info* import = g_imports;
588    if (import == NULL) {
589        slash = "";
590    }
591
592    fprintf(to, "%s: \\\n", options.outputFileName.c_str());
593    fprintf(to, "  %s %s\n", options.inputFileName.c_str(), slash);
594
595    while (import) {
596        if (import->next == NULL) {
597            slash = "";
598        }
599        if (import->filename) {
600            fprintf(to, "  %s %s\n", import->filename, slash);
601        }
602        import = import->next;
603    }
604
605    fprintf(to, "\n");
606
607    fclose(to);
608}
609
610// ==========================================================
611static int
612parse_preprocessed_file(const string& filename)
613{
614    int err;
615
616    FILE* f = fopen(filename.c_str(), "rb");
617    if (f == NULL) {
618        fprintf(stderr, "aidl: can't open preprocessd file: %s\n",
619                filename.c_str());
620        return 1;
621    }
622
623    int lineno = 1;
624    char line[1024];
625    char type[1024];
626    char fullname[1024];
627    while (fgets(line, sizeof(line), f)) {
628        // skip comments and empty lines
629        if (!line[0] || strncmp(line, "//", 2) == 0) {
630          continue;
631        }
632
633        sscanf(line, "%s %[^; \r\n\t];", type, fullname);
634
635        char* packagename;
636        char* classname = rfind(fullname, '.');
637        if (classname != NULL) {
638            *classname = '\0';
639            classname++;
640            packagename = fullname;
641        } else {
642            classname = fullname;
643            packagename = NULL;
644        }
645
646        //printf("%s:%d:...%s...%s...%s...\n", filename.c_str(), lineno,
647        //        type, packagename, classname);
648        document_item_type* doc;
649
650        if (0 == strcmp("parcelable", type)) {
651            parcelable_type* parcl = (parcelable_type*)malloc(
652                    sizeof(parcelable_type));
653            memset(parcl, 0, sizeof(parcelable_type));
654            parcl->document_item.item_type = PARCELABLE_TYPE;
655            parcl->parcelable_token.lineno = lineno;
656            parcl->parcelable_token.data = strdup(type);
657            parcl->package = packagename ? strdup(packagename) : NULL;
658            parcl->name.lineno = lineno;
659            parcl->name.data = strdup(classname);
660            parcl->semicolon_token.lineno = lineno;
661            parcl->semicolon_token.data = strdup(";");
662            doc = (document_item_type*)parcl;
663        }
664        else if (0 == strcmp("interface", type)) {
665            interface_type* iface = (interface_type*)malloc(
666                    sizeof(interface_type));
667            memset(iface, 0, sizeof(interface_type));
668            iface->document_item.item_type = INTERFACE_TYPE;
669            iface->interface_token.lineno = lineno;
670            iface->interface_token.data = strdup(type);
671            iface->package = packagename ? strdup(packagename) : NULL;
672            iface->name.lineno = lineno;
673            iface->name.data = strdup(classname);
674            iface->open_brace_token.lineno = lineno;
675            iface->open_brace_token.data = strdup("{");
676            iface->close_brace_token.lineno = lineno;
677            iface->close_brace_token.data = strdup("}");
678            doc = (document_item_type*)iface;
679        }
680        else {
681            fprintf(stderr, "%s:%d: bad type in line: %s\n",
682                    filename.c_str(), lineno, line);
683            return 1;
684        }
685        err = gather_types(filename.c_str(), doc);
686        lineno++;
687    }
688
689    if (!feof(f)) {
690        fprintf(stderr, "%s:%d: error reading file, line to long.\n",
691                filename.c_str(), lineno);
692        return 1;
693    }
694
695    fclose(f);
696    return 0;
697}
698
699// ==========================================================
700static int
701compile_aidl(const Options& options)
702{
703    int err = 0, N;
704
705    set_import_paths(options.importPaths);
706
707    register_base_types();
708
709    // import the preprocessed file
710    N = options.preprocessedFiles.size();
711    for (int i=0; i<N; i++) {
712        const string& s = options.preprocessedFiles[i];
713        err |= parse_preprocessed_file(s);
714    }
715    if (err != 0) {
716        return err;
717    }
718
719    // parse the main file
720    g_callbacks = &g_mainCallbacks;
721    err = parse_aidl(options.inputFileName.c_str());
722    document_item_type* mainDoc = g_document;
723    g_document = NULL;
724
725    // parse the imports
726    g_callbacks = &g_mainCallbacks;
727    import_info* import = g_imports;
728    while (import) {
729        if (NAMES.Find(import->neededClass) == NULL) {
730            import->filename = find_import_file(import->neededClass);
731            if (!import->filename) {
732                fprintf(stderr, "%s:%d: couldn't find import for class %s\n",
733                        import->from, import->statement.lineno,
734                        import->neededClass);
735                err |= 1;
736            } else {
737                err |= parse_aidl(import->filename);
738                import->doc = g_document;
739                if (import->doc == NULL) {
740                    err |= 1;
741                }
742            }
743        }
744        import = import->next;
745    }
746    // bail out now if parsing wasn't successful
747    if (err != 0 || mainDoc == NULL) {
748        //fprintf(stderr, "aidl: parsing failed, stopping.\n");
749        return 1;
750    }
751
752    // complain about ones that aren't in the right files
753    err |= check_filenames(options.inputFileName.c_str(), mainDoc);
754    import = g_imports;
755    while (import) {
756        err |= check_filenames(import->filename, import->doc);
757        import = import->next;
758    }
759
760    // gather the types that have been declared
761    err |= gather_types(options.inputFileName.c_str(), mainDoc);
762    import = g_imports;
763    while (import) {
764        err |= gather_types(import->filename, import->doc);
765        import = import->next;
766    }
767
768#if 0
769    printf("---- main doc ----\n");
770    test_document(mainDoc);
771
772    import = g_imports;
773    while (import) {
774        printf("---- import doc ----\n");
775        test_document(import->doc);
776        import = import->next;
777    }
778    NAMES.Dump();
779#endif
780
781    // check the referenced types in mainDoc to make sure we've imported them
782    err |= check_types(options.inputFileName.c_str(), mainDoc);
783
784    // finally, there really only needs to be one thing in mainDoc, and it
785    // needs to be an interface.
786    bool onlyParcelable = false;
787    err |= exactly_one_interface(options.inputFileName.c_str(), mainDoc, options, &onlyParcelable);
788
789    // after this, there shouldn't be any more errors because of the
790    // input.
791    if (err != 0 || mainDoc == NULL) {
792        return 1;
793    }
794
795    // they didn't ask to fail on parcelables, so just exit quietly.
796    if (onlyParcelable && !options.failOnParcelable) {
797        return 0;
798    }
799
800    // if we were asked to, generate a make dependency file
801    if (options.depFileName != "") {
802        generate_dep_file(options);
803    }
804
805    err = generate_java(options.outputFileName, options.inputFileName.c_str(),
806                        (interface_type*)mainDoc);
807
808    return err;
809}
810
811static int
812preprocess_aidl(const Options& options)
813{
814    vector<string> lines;
815    int err;
816
817    // read files
818    int N = options.filesToPreprocess.size();
819    for (int i=0; i<N; i++) {
820        g_callbacks = &g_mainCallbacks;
821        err = parse_aidl(options.filesToPreprocess[i].c_str());
822        if (err != 0) {
823            return err;
824        }
825        document_item_type* doc = g_document;
826        string line;
827        if (doc->item_type == PARCELABLE_TYPE) {
828            line = "parcelable ";
829            parcelable_type* parcelable = (parcelable_type*)doc;
830            if (parcelable->package) {
831                line += parcelable->package;
832                line += '.';
833            }
834            line += parcelable->name.data;
835        } else {
836            line = "interface ";
837            interface_type* iface = (interface_type*)doc;
838            if (iface->package) {
839                line += iface->package;
840                line += '.';
841            }
842            line += iface->name.data;
843        }
844        line += ";\n";
845        lines.push_back(line);
846    }
847
848    // write preprocessed file
849    int fd = open( options.outputFileName.c_str(),
850                   O_RDWR|O_CREAT|O_TRUNC|O_BINARY,
851#ifdef HAVE_MS_C_RUNTIME
852                   _S_IREAD|_S_IWRITE);
853#else
854                   S_IRUSR|S_IWUSR|S_IRGRP);
855#endif
856    if (fd == -1) {
857        fprintf(stderr, "aidl: could not open file for write: %s\n",
858                options.outputFileName.c_str());
859        return 1;
860    }
861
862    N = lines.size();
863    for (int i=0; i<N; i++) {
864        const string& s = lines[i];
865        int len = s.length();
866        if (len != write(fd, s.c_str(), len)) {
867            fprintf(stderr, "aidl: error writing to file %s\n",
868                options.outputFileName.c_str());
869            close(fd);
870            unlink(options.outputFileName.c_str());
871            return 1;
872        }
873    }
874
875    close(fd);
876    return 0;
877}
878
879// ==========================================================
880int
881main(int argc, const char **argv)
882{
883    int err = 0;
884
885    Options options;
886    int result = parse_options(argc, argv, &options);
887    if (result) {
888        return result;
889    }
890
891    switch (options.task)
892    {
893        case COMPILE_AIDL:
894            return compile_aidl(options);
895        case PREPROCESS_AIDL:
896            return preprocess_aidl(options);
897    }
898    fprintf(stderr, "aidl: internal error\n");
899    return 1;
900}
901
902
903