Home | History | Annotate | Download | only in aidl
      1 #include "generate_java.h"
      2 #include "AST.h"
      3 #include "Type.h"
      4 #include <string.h>
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 
      9 // =================================================
     10 class VariableFactory
     11 {
     12 public:
     13     VariableFactory(const string& base); // base must be short
     14     Variable* Get(Type* type);
     15     Variable* Get(int index);
     16 private:
     17     vector<Variable*> m_vars;
     18     string m_base;
     19     int m_index;
     20 };
     21 
     22 VariableFactory::VariableFactory(const string& base)
     23     :m_base(base),
     24      m_index(0)
     25 {
     26 }
     27 
     28 Variable*
     29 VariableFactory::Get(Type* type)
     30 {
     31     char name[100];
     32     sprintf(name, "%s%d", m_base.c_str(), m_index);
     33     m_index++;
     34     Variable* v = new Variable(type, name);
     35     m_vars.push_back(v);
     36     return v;
     37 }
     38 
     39 Variable*
     40 VariableFactory::Get(int index)
     41 {
     42     return m_vars[index];
     43 }
     44 
     45 // =================================================
     46 class StubClass : public Class
     47 {
     48 public:
     49     StubClass(Type* type, Type* interfaceType);
     50     virtual ~StubClass();
     51 
     52     Variable* transact_code;
     53     Variable* transact_data;
     54     Variable* transact_reply;
     55     Variable* transact_flags;
     56     SwitchStatement* transact_switch;
     57 private:
     58     void make_as_interface(Type* interfaceType);
     59 };
     60 
     61 StubClass::StubClass(Type* type, Type* interfaceType)
     62     :Class()
     63 {
     64     this->comment = "/** Local-side IPC implementation stub class. */";
     65     this->modifiers = PUBLIC | ABSTRACT | STATIC;
     66     this->what = Class::CLASS;
     67     this->type = type;
     68     this->extends = BINDER_NATIVE_TYPE;
     69     this->interfaces.push_back(interfaceType);
     70 
     71     // descriptor
     72     Field* descriptor = new Field(STATIC | FINAL | PRIVATE,
     73                             new Variable(STRING_TYPE, "DESCRIPTOR"));
     74     descriptor->value = "\"" + interfaceType->QualifiedName() + "\"";
     75     this->elements.push_back(descriptor);
     76 
     77     // ctor
     78     Method* ctor = new Method;
     79         ctor->modifiers = PUBLIC;
     80         ctor->comment = "/** Construct the stub at attach it to the "
     81                         "interface. */";
     82         ctor->name = "Stub";
     83         ctor->statements = new StatementBlock;
     84     MethodCall* attach = new MethodCall(THIS_VALUE, "attachInterface",
     85                             2, THIS_VALUE, new LiteralExpression("DESCRIPTOR"));
     86     ctor->statements->Add(attach);
     87     this->elements.push_back(ctor);
     88 
     89     // asInterface
     90     make_as_interface(interfaceType);
     91 
     92     // asBinder
     93     Method* asBinder = new Method;
     94         asBinder->modifiers = PUBLIC;
     95         asBinder->returnType = IBINDER_TYPE;
     96         asBinder->name = "asBinder";
     97         asBinder->statements = new StatementBlock;
     98     asBinder->statements->Add(new ReturnStatement(THIS_VALUE));
     99     this->elements.push_back(asBinder);
    100 
    101     // onTransact
    102     this->transact_code = new Variable(INT_TYPE, "code");
    103     this->transact_data = new Variable(PARCEL_TYPE, "data");
    104     this->transact_reply = new Variable(PARCEL_TYPE, "reply");
    105     this->transact_flags = new Variable(INT_TYPE, "flags");
    106     Method* onTransact = new Method;
    107         onTransact->modifiers = PUBLIC | OVERRIDE;
    108         onTransact->returnType = BOOLEAN_TYPE;
    109         onTransact->name = "onTransact";
    110         onTransact->parameters.push_back(this->transact_code);
    111         onTransact->parameters.push_back(this->transact_data);
    112         onTransact->parameters.push_back(this->transact_reply);
    113         onTransact->parameters.push_back(this->transact_flags);
    114         onTransact->statements = new StatementBlock;
    115         onTransact->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
    116     this->elements.push_back(onTransact);
    117     this->transact_switch = new SwitchStatement(this->transact_code);
    118 
    119     onTransact->statements->Add(this->transact_switch);
    120     MethodCall* superCall = new MethodCall(SUPER_VALUE, "onTransact", 4,
    121                                     this->transact_code, this->transact_data,
    122                                     this->transact_reply, this->transact_flags);
    123     onTransact->statements->Add(new ReturnStatement(superCall));
    124 }
    125 
    126 StubClass::~StubClass()
    127 {
    128 }
    129 
    130 void
    131 StubClass::make_as_interface(Type *interfaceType)
    132 {
    133     Variable* obj = new Variable(IBINDER_TYPE, "obj");
    134 
    135     Method* m = new Method;
    136         m->comment = "/**\n * Cast an IBinder object into an ";
    137         m->comment += interfaceType->QualifiedName();
    138         m->comment += " interface,\n";
    139         m->comment += " * generating a proxy if needed.\n */";
    140         m->modifiers = PUBLIC | STATIC;
    141         m->returnType = interfaceType;
    142         m->name = "asInterface";
    143         m->parameters.push_back(obj);
    144         m->statements = new StatementBlock;
    145 
    146     IfStatement* ifstatement = new IfStatement();
    147         ifstatement->expression = new Comparison(obj, "==", NULL_VALUE);
    148         ifstatement->statements = new StatementBlock;
    149         ifstatement->statements->Add(new ReturnStatement(NULL_VALUE));
    150     m->statements->Add(ifstatement);
    151 
    152     // IInterface iin = obj.queryLocalInterface(DESCRIPTOR)
    153     MethodCall* queryLocalInterface = new MethodCall(obj, "queryLocalInterface");
    154     queryLocalInterface->arguments.push_back(new LiteralExpression("DESCRIPTOR"));
    155     IInterfaceType* iinType = new IInterfaceType();
    156     Variable *iin = new Variable(iinType, "iin");
    157     VariableDeclaration* iinVd = new VariableDeclaration(iin, queryLocalInterface, iinType);
    158     m->statements->Add(iinVd);
    159 
    160     // Ensure the instance type of the local object is as expected.
    161     // One scenario where this is needed is if another package (with a
    162     // different class loader) runs in the same process as the service.
    163 
    164     // if (iin != null && iin instanceof <interfaceType>) return (<interfaceType>) iin;
    165     Comparison* iinNotNull = new Comparison(iin, "!=", NULL_VALUE);
    166     Comparison* instOfCheck = new Comparison(iin, " instanceof ",
    167             new LiteralExpression(interfaceType->QualifiedName()));
    168     IfStatement* instOfStatement = new IfStatement();
    169         instOfStatement->expression = new Comparison(iinNotNull, "&&", instOfCheck);
    170         instOfStatement->statements = new StatementBlock;
    171         instOfStatement->statements->Add(new ReturnStatement(new Cast(interfaceType, iin)));
    172     m->statements->Add(instOfStatement);
    173 
    174     string proxyType = interfaceType->QualifiedName();
    175     proxyType += ".Stub.Proxy";
    176     NewExpression* ne = new NewExpression(NAMES.Find(proxyType));
    177     ne->arguments.push_back(obj);
    178     m->statements->Add(new ReturnStatement(ne));
    179 
    180     this->elements.push_back(m);
    181 }
    182 
    183 
    184 
    185 // =================================================
    186 class ProxyClass : public Class
    187 {
    188 public:
    189     ProxyClass(Type* type, InterfaceType* interfaceType);
    190     virtual ~ProxyClass();
    191 
    192     Variable* mRemote;
    193     bool mOneWay;
    194 };
    195 
    196 ProxyClass::ProxyClass(Type* type, InterfaceType* interfaceType)
    197     :Class()
    198 {
    199     this->modifiers = PRIVATE | STATIC;
    200     this->what = Class::CLASS;
    201     this->type = type;
    202     this->interfaces.push_back(interfaceType);
    203 
    204     mOneWay = interfaceType->OneWay();
    205 
    206     // IBinder mRemote
    207     mRemote = new Variable(IBINDER_TYPE, "mRemote");
    208     this->elements.push_back(new Field(PRIVATE, mRemote));
    209 
    210     // Proxy()
    211     Variable* remote = new Variable(IBINDER_TYPE, "remote");
    212     Method* ctor = new Method;
    213         ctor->name = "Proxy";
    214         ctor->statements = new StatementBlock;
    215         ctor->parameters.push_back(remote);
    216     ctor->statements->Add(new Assignment(mRemote, remote));
    217     this->elements.push_back(ctor);
    218 
    219     // IBinder asBinder()
    220     Method* asBinder = new Method;
    221         asBinder->modifiers = PUBLIC;
    222         asBinder->returnType = IBINDER_TYPE;
    223         asBinder->name = "asBinder";
    224         asBinder->statements = new StatementBlock;
    225     asBinder->statements->Add(new ReturnStatement(mRemote));
    226     this->elements.push_back(asBinder);
    227 }
    228 
    229 ProxyClass::~ProxyClass()
    230 {
    231 }
    232 
    233 // =================================================
    234 static string
    235 gather_comments(extra_text_type* extra)
    236 {
    237     string s;
    238     while (extra) {
    239         if (extra->which == SHORT_COMMENT) {
    240             s += extra->data;
    241         }
    242         else if (extra->which == LONG_COMMENT) {
    243             s += "/*";
    244             s += extra->data;
    245             s += "*/";
    246         }
    247         extra = extra->next;
    248     }
    249     return s;
    250 }
    251 
    252 static string
    253 append(const char* a, const char* b)
    254 {
    255     string s = a;
    256     s += b;
    257     return s;
    258 }
    259 
    260 static void
    261 generate_new_array(Type* t, StatementBlock* addTo, Variable* v,
    262                             Variable* parcel)
    263 {
    264     Variable* len = new Variable(INT_TYPE, v->name + "_length");
    265     addTo->Add(new VariableDeclaration(len, new MethodCall(parcel, "readInt")));
    266     IfStatement* lencheck = new IfStatement();
    267     lencheck->expression = new Comparison(len, "<", new LiteralExpression("0"));
    268     lencheck->statements->Add(new Assignment(v, NULL_VALUE));
    269     lencheck->elseif = new IfStatement();
    270     lencheck->elseif->statements->Add(new Assignment(v,
    271                 new NewArrayExpression(t, len)));
    272     addTo->Add(lencheck);
    273 }
    274 
    275 static void
    276 generate_write_to_parcel(Type* t, StatementBlock* addTo, Variable* v,
    277                             Variable* parcel, int flags)
    278 {
    279     if (v->dimension == 0) {
    280         t->WriteToParcel(addTo, v, parcel, flags);
    281     }
    282     if (v->dimension == 1) {
    283         t->WriteArrayToParcel(addTo, v, parcel, flags);
    284     }
    285 }
    286 
    287 static void
    288 generate_create_from_parcel(Type* t, StatementBlock* addTo, Variable* v,
    289                             Variable* parcel, Variable** cl)
    290 {
    291     if (v->dimension == 0) {
    292         t->CreateFromParcel(addTo, v, parcel, cl);
    293     }
    294     if (v->dimension == 1) {
    295         t->CreateArrayFromParcel(addTo, v, parcel, cl);
    296     }
    297 }
    298 
    299 static void
    300 generate_read_from_parcel(Type* t, StatementBlock* addTo, Variable* v,
    301                             Variable* parcel, Variable** cl)
    302 {
    303     if (v->dimension == 0) {
    304         t->ReadFromParcel(addTo, v, parcel, cl);
    305     }
    306     if (v->dimension == 1) {
    307         t->ReadArrayFromParcel(addTo, v, parcel, cl);
    308     }
    309 }
    310 
    311 
    312 static void
    313 generate_method(const method_type* method, Class* interface,
    314                     StubClass* stubClass, ProxyClass* proxyClass, int index)
    315 {
    316     arg_type* arg;
    317     int i;
    318     bool hasOutParams = false;
    319 
    320     const bool oneway = proxyClass->mOneWay || method->oneway;
    321 
    322     // == the TRANSACT_ constant =============================================
    323     string transactCodeName = "TRANSACTION_";
    324     transactCodeName += method->name.data;
    325 
    326     char transactCodeValue[50];
    327     sprintf(transactCodeValue, "(android.os.IBinder.FIRST_CALL_TRANSACTION + %d)", index);
    328 
    329     Field* transactCode = new Field(STATIC | FINAL,
    330                             new Variable(INT_TYPE, transactCodeName));
    331     transactCode->value = transactCodeValue;
    332     stubClass->elements.push_back(transactCode);
    333 
    334     // == the declaration in the interface ===================================
    335     Method* decl = new Method;
    336         decl->comment = gather_comments(method->comments_token->extra);
    337         decl->modifiers = PUBLIC;
    338         decl->returnType = NAMES.Search(method->type.type.data);
    339         decl->returnTypeDimension = method->type.dimension;
    340         decl->name = method->name.data;
    341 
    342     arg = method->args;
    343     while (arg != NULL) {
    344         decl->parameters.push_back(new Variable(
    345                             NAMES.Search(arg->type.type.data), arg->name.data,
    346                             arg->type.dimension));
    347         arg = arg->next;
    348     }
    349 
    350     decl->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
    351 
    352     interface->elements.push_back(decl);
    353 
    354     // == the stub method ====================================================
    355 
    356     Case* c = new Case(transactCodeName);
    357 
    358     MethodCall* realCall = new MethodCall(THIS_VALUE, method->name.data);
    359 
    360     // interface token validation is the very first thing we do
    361     c->statements->Add(new MethodCall(stubClass->transact_data,
    362             "enforceInterface", 1, new LiteralExpression("DESCRIPTOR")));
    363 
    364     // args
    365     Variable* cl = NULL;
    366     VariableFactory stubArgs("_arg");
    367     arg = method->args;
    368     while (arg != NULL) {
    369         Type* t = NAMES.Search(arg->type.type.data);
    370         Variable* v = stubArgs.Get(t);
    371         v->dimension = arg->type.dimension;
    372 
    373         c->statements->Add(new VariableDeclaration(v));
    374 
    375         if (convert_direction(arg->direction.data) & IN_PARAMETER) {
    376             generate_create_from_parcel(t, c->statements, v,
    377                     stubClass->transact_data, &cl);
    378         } else {
    379             if (arg->type.dimension == 0) {
    380                 c->statements->Add(new Assignment(
    381                                                 v, new NewExpression(v->type)));
    382             }
    383             else if (arg->type.dimension == 1) {
    384                 generate_new_array(v->type, c->statements, v,
    385                         stubClass->transact_data);
    386             }
    387             else {
    388                 fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__,
    389                         __LINE__);
    390             }
    391         }
    392 
    393         realCall->arguments.push_back(v);
    394 
    395         arg = arg->next;
    396     }
    397 
    398     // the real call
    399     Variable* _result = NULL;
    400     if (0 == strcmp(method->type.type.data, "void")) {
    401         c->statements->Add(realCall);
    402 
    403         if (!oneway) {
    404             // report that there were no exceptions
    405             MethodCall* ex = new MethodCall(stubClass->transact_reply,
    406                     "writeNoException", 0);
    407             c->statements->Add(ex);
    408         }
    409     } else {
    410         _result = new Variable(decl->returnType, "_result",
    411                                 decl->returnTypeDimension);
    412         c->statements->Add(new VariableDeclaration(_result, realCall));
    413 
    414         if (!oneway) {
    415             // report that there were no exceptions
    416             MethodCall* ex = new MethodCall(stubClass->transact_reply,
    417                     "writeNoException", 0);
    418             c->statements->Add(ex);
    419         }
    420 
    421         // marshall the return value
    422         generate_write_to_parcel(decl->returnType, c->statements, _result,
    423                                     stubClass->transact_reply,
    424                                     Type::PARCELABLE_WRITE_RETURN_VALUE);
    425     }
    426 
    427     // out parameters
    428     i = 0;
    429     arg = method->args;
    430     while (arg != NULL) {
    431         Type* t = NAMES.Search(arg->type.type.data);
    432         Variable* v = stubArgs.Get(i++);
    433 
    434         if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
    435             generate_write_to_parcel(t, c->statements, v,
    436                                 stubClass->transact_reply,
    437                                 Type::PARCELABLE_WRITE_RETURN_VALUE);
    438             hasOutParams = true;
    439         }
    440 
    441         arg = arg->next;
    442     }
    443 
    444     // return true
    445     c->statements->Add(new ReturnStatement(TRUE_VALUE));
    446     stubClass->transact_switch->cases.push_back(c);
    447 
    448     // == the proxy method ===================================================
    449     Method* proxy = new Method;
    450         proxy->comment = gather_comments(method->comments_token->extra);
    451         proxy->modifiers = PUBLIC;
    452         proxy->returnType = NAMES.Search(method->type.type.data);
    453         proxy->returnTypeDimension = method->type.dimension;
    454         proxy->name = method->name.data;
    455         proxy->statements = new StatementBlock;
    456         arg = method->args;
    457         while (arg != NULL) {
    458             proxy->parameters.push_back(new Variable(
    459                             NAMES.Search(arg->type.type.data), arg->name.data,
    460                             arg->type.dimension));
    461             arg = arg->next;
    462         }
    463         proxy->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
    464     proxyClass->elements.push_back(proxy);
    465 
    466     // the parcels
    467     Variable* _data = new Variable(PARCEL_TYPE, "_data");
    468     proxy->statements->Add(new VariableDeclaration(_data,
    469                                 new MethodCall(PARCEL_TYPE, "obtain")));
    470     Variable* _reply = NULL;
    471     if (!oneway) {
    472         _reply = new Variable(PARCEL_TYPE, "_reply");
    473         proxy->statements->Add(new VariableDeclaration(_reply,
    474                                     new MethodCall(PARCEL_TYPE, "obtain")));
    475     }
    476 
    477     // the return value
    478     _result = NULL;
    479     if (0 != strcmp(method->type.type.data, "void")) {
    480         _result = new Variable(proxy->returnType, "_result",
    481                 method->type.dimension);
    482         proxy->statements->Add(new VariableDeclaration(_result));
    483     }
    484 
    485     // try and finally
    486     TryStatement* tryStatement = new TryStatement();
    487     proxy->statements->Add(tryStatement);
    488     FinallyStatement* finallyStatement = new FinallyStatement();
    489     proxy->statements->Add(finallyStatement);
    490 
    491     // the interface identifier token: the DESCRIPTOR constant, marshalled as a string
    492     tryStatement->statements->Add(new MethodCall(_data, "writeInterfaceToken",
    493             1, new LiteralExpression("DESCRIPTOR")));
    494 
    495     // the parameters
    496     arg = method->args;
    497     while (arg != NULL) {
    498         Type* t = NAMES.Search(arg->type.type.data);
    499         Variable* v = new Variable(t, arg->name.data, arg->type.dimension);
    500         int dir = convert_direction(arg->direction.data);
    501         if (dir == OUT_PARAMETER && arg->type.dimension != 0) {
    502             IfStatement* checklen = new IfStatement();
    503             checklen->expression = new Comparison(v, "==", NULL_VALUE);
    504             checklen->statements->Add(new MethodCall(_data, "writeInt", 1,
    505                         new LiteralExpression("-1")));
    506             checklen->elseif = new IfStatement();
    507             checklen->elseif->statements->Add(new MethodCall(_data, "writeInt",
    508                         1, new FieldVariable(v, "length")));
    509             tryStatement->statements->Add(checklen);
    510         }
    511         else if (dir & IN_PARAMETER) {
    512             generate_write_to_parcel(t, tryStatement->statements, v, _data, 0);
    513         }
    514         arg = arg->next;
    515     }
    516 
    517     // the transact call
    518     MethodCall* call = new MethodCall(proxyClass->mRemote, "transact", 4,
    519                             new LiteralExpression("Stub." + transactCodeName),
    520                             _data, _reply ? _reply : NULL_VALUE,
    521                             new LiteralExpression(
    522                                 oneway ? "android.os.IBinder.FLAG_ONEWAY" : "0"));
    523     tryStatement->statements->Add(call);
    524 
    525     // throw back exceptions.
    526     if (_reply) {
    527         MethodCall* ex = new MethodCall(_reply, "readException", 0);
    528         tryStatement->statements->Add(ex);
    529     }
    530 
    531     // returning and cleanup
    532     if (_reply != NULL) {
    533         if (_result != NULL) {
    534             generate_create_from_parcel(proxy->returnType,
    535                     tryStatement->statements, _result, _reply, &cl);
    536         }
    537 
    538         // the out/inout parameters
    539         arg = method->args;
    540         while (arg != NULL) {
    541             Type* t = NAMES.Search(arg->type.type.data);
    542             Variable* v = new Variable(t, arg->name.data, arg->type.dimension);
    543             if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
    544                 generate_read_from_parcel(t, tryStatement->statements,
    545                                             v, _reply, &cl);
    546             }
    547             arg = arg->next;
    548         }
    549 
    550         finallyStatement->statements->Add(new MethodCall(_reply, "recycle"));
    551     }
    552     finallyStatement->statements->Add(new MethodCall(_data, "recycle"));
    553 
    554     if (_result != NULL) {
    555         proxy->statements->Add(new ReturnStatement(_result));
    556     }
    557 }
    558 
    559 static void
    560 generate_interface_descriptors(StubClass* stub, ProxyClass* proxy)
    561 {
    562     // the interface descriptor transaction handler
    563     Case* c = new Case("INTERFACE_TRANSACTION");
    564     c->statements->Add(new MethodCall(stub->transact_reply, "writeString",
    565             1, new LiteralExpression("DESCRIPTOR")));
    566     c->statements->Add(new ReturnStatement(TRUE_VALUE));
    567     stub->transact_switch->cases.push_back(c);
    568 
    569     // and the proxy-side method returning the descriptor directly
    570     Method* getDesc = new Method;
    571     getDesc->modifiers = PUBLIC;
    572     getDesc->returnType = STRING_TYPE;
    573     getDesc->returnTypeDimension = 0;
    574     getDesc->name = "getInterfaceDescriptor";
    575     getDesc->statements = new StatementBlock;
    576     getDesc->statements->Add(new ReturnStatement(new LiteralExpression("DESCRIPTOR")));
    577     proxy->elements.push_back(getDesc);
    578 }
    579 
    580 static Class*
    581 generate_interface_class(const interface_type* iface)
    582 {
    583     InterfaceType* interfaceType = static_cast<InterfaceType*>(
    584         NAMES.Find(iface->package, iface->name.data));
    585 
    586     // the interface class
    587     Class* interface = new Class;
    588         interface->comment = gather_comments(iface->comments_token->extra);
    589         interface->modifiers = PUBLIC;
    590         interface->what = Class::INTERFACE;
    591         interface->type = interfaceType;
    592         interface->interfaces.push_back(IINTERFACE_TYPE);
    593 
    594     // the stub inner class
    595     StubClass* stub = new StubClass(
    596         NAMES.Find(iface->package, append(iface->name.data, ".Stub").c_str()),
    597         interfaceType);
    598     interface->elements.push_back(stub);
    599 
    600     // the proxy inner class
    601     ProxyClass* proxy = new ProxyClass(
    602         NAMES.Find(iface->package,
    603                          append(iface->name.data, ".Stub.Proxy").c_str()),
    604         interfaceType);
    605     stub->elements.push_back(proxy);
    606 
    607     // stub and proxy support for getInterfaceDescriptor()
    608     generate_interface_descriptors(stub, proxy);
    609 
    610     // all the declared methods of the interface
    611     int index = 0;
    612     interface_item_type* item = iface->interface_items;
    613     while (item != NULL) {
    614         if (item->item_type == METHOD_TYPE) {
    615             generate_method((method_type*)item, interface, stub, proxy, index);
    616         }
    617         item = item->next;
    618         index++;
    619     }
    620 
    621     return interface;
    622 }
    623 
    624 int
    625 generate_java(const string& filename, const string& originalSrc,
    626                 interface_type* iface)
    627 {
    628     Document* document = new Document;
    629         document->comment = "";
    630         if (iface->package) document->package = iface->package;
    631         document->originalSrc = originalSrc;
    632         document->classes.push_back(generate_interface_class(iface));
    633 
    634 //    printf("outputting... filename=%s\n", filename.c_str());
    635     FILE* to;
    636     if (filename == "-") {
    637         to = stdout;
    638     } else {
    639        /* open file in binary mode to ensure that the tool produces the
    640         * same output on all platforms !!
    641         */
    642         to = fopen(filename.c_str(), "wb");
    643         if (to == NULL) {
    644             fprintf(stderr, "unable to open %s for write\n", filename.c_str());
    645             return 1;
    646         }
    647     }
    648 
    649     document->Write(to);
    650 
    651     fclose(to);
    652     return 0;
    653 }
    654 
    655