Project

General

Profile

Statistics
| Branch: | Revision:

root / src / nedxml / nedtool.cc @ 79bb12dc

History | View | Annotate | Download (20 KB)

1
//==========================================================================
2
//  NEDTOOL.CC - part of
3
//
4
//                     OMNeT++/OMNEST
5
//            Discrete System Simulation in C++
6
//
7
// Contains: main() for nedtool
8
//
9
//==========================================================================
10

    
11
/*--------------------------------------------------------------*
12
  Copyright (C) 2002-2008 Andras Varga
13
  Copyright (C) 2006-2008 OpenSim Ltd.
14

15
  This file is distributed WITHOUT ANY WARRANTY. See the file
16
  `license' for details on this and other legal matters.
17
*--------------------------------------------------------------*/
18

    
19

    
20
#include <assert.h>
21
#include <stdio.h>
22
#include <stdlib.h>
23
#include <string.h>
24
#include <fstream>
25
#include <errno.h>
26

    
27
#include "nedparser.h"
28
#include "nederror.h"
29
#include "nedexception.h"
30
#include "nedxmlparser.h"
31
#include "neddtdvalidator.h"
32
#include "nedsyntaxvalidator.h"
33
#include "nedcrossvalidator.h"
34
#include "ned2generator.h"
35
#include "ned1generator.h"
36
#include "xmlgenerator.h"
37
#include "nedtools.h"
38
#include "fileglobber.h"
39
#include "fileutil.h"
40
#include "stringutil.h"
41
#include "platmisc.h"   // getcwd, chdir
42
#include "../common/ver.h"
43

    
44
USING_NAMESPACE
45

    
46
using std::ofstream;
47
using std::ifstream;
48
using std::ios;
49

    
50
// file types
51
enum {XML_FILE, NED_FILE, MSG_FILE, CPP_FILE, UNKNOWN_FILE};
52

    
53
// option variables
54
bool opt_genxml = false;           // -x
55
bool opt_gensrc = false;           // -n
56
bool opt_validateonly = false;     // -v
57
int opt_nextfiletype = UNKNOWN_FILE; // -X
58
bool opt_oldsyntax = false;        // -Q
59
const char *opt_suffix = NULL;     // -s
60
const char *opt_hdrsuffix = NULL;  // -S
61
bool opt_inplace = false;          // -k
62
bool opt_unparsedexpr = false;     // -e
63
bool opt_storesrc = false;         // -t
64
bool opt_novalidation = false;     // -y
65
bool opt_noimports = false;        // -z
66
bool opt_srcloc = false;           // -p
67
bool opt_mergeoutput = false;      // -m
68
bool opt_verbose = false;          // -V
69
const char *opt_outputfile = NULL; // -o
70
bool opt_here = false;             // -h
71
bool opt_splitnedfiles = false;    // -u
72

    
73
FilesElement *outputtree;
74

    
75

    
76
void printUsage()
77
{
78
    fprintf(stderr,
79
       "nedtool -- part of " OMNETPP_PRODUCT ", (C) 2006-2008 Andras Varga, OpenSim Ltd.\n"
80
       "Version: " OMNETPP_VERSION_STR ", build: " OMNETPP_BUILDID ", edition: " OMNETPP_EDITION "\n"
81
       "\n"
82
       "Usage: nedtool [options] <file1> <file2> ...\n"
83
       "Files may be given in a listfile as well, with the @listfile or @@listfile\n"
84
       "syntax (check the difference below.) By default, if neither -n nor -x is\n"
85
       "specified, nedtool generates C++ source.\n"
86
       "  -x: generate XML (you may need -y, -e and -p as well)\n"
87
       "  -n: generate source (NED or MSG; you may need -y and -e as well)\n"
88
       "  -P: pretty-print and/or convert 3.x NED files to the current syntax;\n"
89
       "      this is a shortcut for -n -k -y\n"
90
       "  -v: no output (only validate input)\n"
91
       "  -m: output is a single file (out_n.* by default, see also -o)\n"
92
       "  -o <filename>: output file name (don't use when processing multiple files)\n"
93
       "  -h  place output file into current directory\n"
94
       "  -I <dir>: add directory to NED include path\n"
95
       "  -X xml/ned/msg/off: following files are XML, NED or MSG up to '-X off'\n"
96
       "  -Q: with -n: use old (3.x) NED syntax\n"
97
       "  -s <suffix>: suffix for generated files\n"
98
       "  -S <suffix>: when generating C++, suffix for generated header files\n"
99
       "  -k: with -n: replace original file and create backup (.bak). If input is a\n"
100
       "      single XML file created by `nedtool -m -x': replace original NED files\n"
101
       "  -u: with -m or -k: split NED files to one NED component per file\n" //XXX refine help text
102
       "  -e: do not parse expressions in NED input; expect unparsed expressions in XML\n"
103
       "  -y: skip semantic validation (implies -z, skip processing imports)\n"
104
       "  -z: skip processing imports\n"
105
       "  -t: with NED parsing: include source code of components in XML\n"
106
       "  -p: with -x: add source location info (src-loc attributes) to XML output\n"
107
       "  -V: verbose\n"
108
       "  @listfile: listfile should contain one file per line (@ or @@ listfiles\n"
109
       "      also accepted). Files are interpreted as relative to the listfile.\n"
110
       "      @ listfiles can be invoked from anywhere, with the same effect.\n"
111
       "  @@listfile: like @listfile, but contents is interpreted as relative to\n"
112
       "      the current working directory. @@ listfiles can be put anywhere,\n"
113
       "      including /tmp -- effect only depends on the working directory.\n"
114
       "Message (.msg) files should be processed with opp_msgc.\n"
115
    );
116
}
117

    
118
void createFileNameWithSuffix(char *outfname, const char *infname, const char *suffix)
119
{
120
    if (opt_here)
121
    {
122
        // remove directory part
123
        const char *s = infname+strlen(infname);
124
        while (s>infname && *s!='/' && *s!='\\') s--;
125
        if (*s=='/' || *s=='\\') s++;
126
        strcpy(outfname, s);
127
    }
128
    else
129
    {
130
        strcpy(outfname, infname);
131
    }
132

    
133
    // replace extension with suffix.
134
    char *s = outfname+strlen(outfname);
135
    while (s>outfname && *s!='/' && *s!='\\' && *s!='.') s--;
136
    if (*s!='.') s=outfname+strlen(outfname);
137
    strcpy(s,suffix);
138
}
139

    
140
bool renameFileToBAK(const char *fname)
141
{
142
    // returns false on failure, true if successfully renamed or no such file
143
    char bakfname[1024];
144
    createFileNameWithSuffix(bakfname, fname, ".bak");
145

    
146
    if (unlink(bakfname)!=0 && errno!=ENOENT)
147
    {
148
        fprintf(stderr,"nedtool: cannot remove old backup file %s, leaving file %s unchanged\n", bakfname, fname);
149
        return false;
150
    }
151
    if (rename(fname, bakfname)!=0 && errno!=ENOENT)
152
    {
153
        fprintf(stderr,"nedtool: cannot rename original %s to %s, leaving file unchanged\n", fname, bakfname);
154
        return false;
155
    }
156
    return true;
157
}
158

    
159
void generateNED(std::ostream& out, NEDElement *node, NEDErrorStore *e, bool oldsyntax)
160
{
161
    if (oldsyntax)
162
        generateNED1(out, node, e);
163
    else
164
        generateNED2(out, node, e);
165
}
166

    
167
bool processFile(const char *fname, NEDErrorStore *errors)
168
{
169
    if (opt_verbose) fprintf(stdout,"processing '%s'...\n",fname);
170

    
171
    // determine file type
172
    int ftype = opt_nextfiletype;
173
    if (ftype==UNKNOWN_FILE)
174
    {
175
        if (opp_stringendswith(fname, ".ned"))
176
            ftype=NED_FILE;
177
        else if (opp_stringendswith(fname, ".msg"))
178
            ftype=MSG_FILE;
179
        else if (opp_stringendswith(fname, ".xml"))
180
            ftype=XML_FILE;
181
        else
182
            ftype=NED_FILE;
183
    }
184

    
185
    // process input tree
186
    NEDElement *tree = 0;
187
    errors->clear();
188
    if (ftype==XML_FILE)
189
    {
190
        tree = parseXML(fname, errors);
191
    }
192
    else if (ftype==NED_FILE || ftype==MSG_FILE)
193
    {
194
        NEDParser parser(errors);
195
        parser.setParseExpressions(!opt_unparsedexpr);
196
        parser.setStoreSource(opt_storesrc);
197
        tree = (ftype==NED_FILE) ? parser.parseNEDFile(fname) : parser.parseMSGFile(fname);
198
    }
199
    if (errors->containsError())
200
    {
201
        delete tree;
202
        return false;
203
    }
204

    
205
    // DTD validation and additional syntax validation
206
    NEDDTDValidator dtdvalidator(errors);
207
    try {
208
        dtdvalidator.validate(tree);
209
    }
210
    catch(NEDException& e) {
211
        fprintf(stderr,"nedtool: NEDException: %s\n", e.what());
212
        delete tree;
213
        return false;
214
    }
215
    if (errors->containsError())
216
    {
217
        delete tree;
218
        return false;
219
    }
220

    
221
    NEDSyntaxValidator syntaxvalidator(!opt_unparsedexpr, errors);
222
    syntaxvalidator.validate(tree);
223
    if (errors->containsError())
224
    {
225
        delete tree;
226
        return false;
227
    }
228

    
229
    if (opt_mergeoutput)
230
    {
231
        outputtree->appendChild(tree);
232
    }
233
    else if (!opt_validateonly)
234
    {
235
        char outfname[1024];
236
        char outhdrfname[1024];
237

    
238
        if (opt_inplace)
239
        {
240
            // won't be used if we're to split a single XML to several NED files
241
            strcpy(outfname, fname);
242
            strcpy(outhdrfname, "");  // unused
243
        }
244
        else if (opt_outputfile && (opt_genxml || opt_gensrc))
245
        {
246
            strcpy(outfname, opt_outputfile);
247
            strcpy(outhdrfname, "");  // unused
248
        }
249
        else
250
        {
251
            // generate output file name
252
            const char *suffix = opt_suffix;
253
            const char *hdrsuffix = opt_hdrsuffix;
254
            if (!suffix)
255
            {
256
                if (opt_genxml)
257
                    suffix = (ftype==MSG_FILE) ? "_m.xml" : "_n.xml";
258
                else if (opt_gensrc)
259
                    suffix = (ftype==MSG_FILE) ? "_m.msg" : "_n.ned";
260
                else
261
                    suffix = (ftype==MSG_FILE) ? "_m.cc" : "_n.cc";
262
            }
263
            if (!hdrsuffix)
264
            {
265
                hdrsuffix = "_m.h";
266
            }
267
            createFileNameWithSuffix(outfname, fname, suffix);
268
            createFileNameWithSuffix(outhdrfname, fname, hdrsuffix);
269
        }
270

    
271
        // TBD check output file for write errors!
272
        if (opt_genxml)
273
        {
274
            if (opt_inplace && !renameFileToBAK(outfname))
275
                return false;
276
            ofstream out(outfname);
277
            generateXML(out, tree, opt_srcloc);
278
            out.close();
279
        }
280
        else if (opt_inplace && opt_gensrc && (tree->getTagCode()==NED_FILES ||
281
                 tree->getTagCode()==NED_NED_FILE || tree->getTagCode()==NED_MSG_FILE))
282
        {
283
             if (tree->getTagCode()==NED_NED_FILE || tree->getTagCode()==NED_MSG_FILE)
284
             {
285
                 // wrap the tree into a FilesElement
286
                 NEDElement *file = tree;
287
                 tree = new FilesElement();
288
                 tree->appendChild(file);
289
             }
290

    
291
             if (opt_splitnedfiles)
292
                NEDTools::splitToFiles((FilesElement *)tree);
293

    
294
            for (NEDElement *child=tree->getFirstChild(); child; child=child->getNextSibling())
295
            {
296
                // extract file name
297
                if (child->getTagCode()==NED_NED_FILE)
298
                    strcpy(outfname, ((NedFileElement *)child)->getFilename());
299
                else if (child->getTagCode()==NED_MSG_FILE)
300
                    strcpy(outfname, ((MsgFileElement *)child)->getFilename());
301
                else
302
                    continue; // if there's anything else, ignore it
303

    
304
                // generate the file
305
                if (opt_inplace && !renameFileToBAK(outfname))
306
                    return false;
307
                ofstream out(outfname);
308
                generateNED(out, child, errors, opt_oldsyntax);
309
                out.close();
310
            }
311
        }
312
        else if (opt_gensrc)
313
        {
314
            if (opt_inplace && !renameFileToBAK(outfname))
315
                return false;
316
            ofstream out(outfname);
317
            generateNED(out, tree, errors, opt_oldsyntax);
318
            out.close();
319
        }
320

    
321
        delete tree;
322

    
323
        if (errors->containsError())
324
            return false;
325
    }
326
    return true;
327
}
328

    
329

    
330
bool processListFile(const char *listfilename, bool istemplistfile, NEDErrorStore *errors)
331
{
332
    const int maxline=1024;
333
    char line[maxline];
334
    char olddir[1024] = "";
335

    
336
    if (opt_verbose) fprintf(stdout,"processing list file '%s'...\n",listfilename);
337

    
338
    ifstream in(listfilename, ios::in);
339
    if (in.fail())
340
    {
341
        fprintf(stderr,"nedtool: cannot open list file '%s'\n",listfilename);
342
        return false;
343
    }
344

    
345
    if (!istemplistfile)
346
    {
347
        // with @listfile, files should be relative to list file, so try cd into list file's directory
348
        // (with @@listfile, files are relative to the wd, so we don't cd)
349
        std::string dir, fnameonly;
350
        splitFileName(listfilename, dir, fnameonly);
351
        if (!getcwd(olddir,1024))
352
        {
353
            fprintf(stderr,"nedtool: cannot get the name of current directory\n");
354
            return false;
355
        }
356
        if (opt_verbose) fprintf(stdout,"changing into '%s'...\n",dir.c_str());
357
        if (chdir(dir.c_str()))
358
        {
359
            fprintf(stderr,"nedtool: cannot temporarily change to directory `%s' (does it exist?)\n", dir.c_str());
360
            return false;
361
        }
362
    }
363

    
364
    while (in.getline(line, maxline))
365
    {
366
        int len = in.gcount();
367
        if (line[len-1]=='\n')
368
            line[len-1] = '\0';
369
        const char *fname = line;
370
        if (fname[0]=='@')
371
        {
372
            bool istmp = (fname[1]=='@');
373
            if (!processListFile(fname+(istmp?2:1), istmp, errors))
374
            {
375
                in.close();
376
                return false;
377
            }
378
        }
379
        else if (fname[0] && fname[0]!='#')
380
        {
381
            if (!processFile(fname, errors))
382
            {
383
                in.close();
384
                return false;
385
            }
386
        }
387
    }
388

    
389
    if (in.bad())
390
    {
391
        fprintf(stderr,"nedtool: error reading list file '%s'\n",listfilename);
392
        return false;
393
    }
394
    in.close();
395

    
396
    if (olddir[0])
397
    {
398
        if (opt_verbose) fprintf(stdout,"changing back to '%s'...\n",olddir);
399
        if (chdir(olddir))
400
        {
401
            fprintf(stderr,"nedtool: cannot change back to directory `%s'\n", olddir);
402
            return false;
403
        }
404
    }
405
    return true;
406
}
407

    
408

    
409
int main(int argc, char **argv)
410
{
411
    // print usage
412
    if (argc<2)
413
    {
414
        printUsage();
415
        return 0;
416
    }
417

    
418
    NEDErrorStore errorstore;
419
    NEDErrorStore *errors = &errorstore;
420
    errors->setPrintToStderr(true);
421

    
422
    // process options
423
    for (int i=1; i<argc; i++)
424
    {
425
        if (!strcmp(argv[i],"-x"))
426
        {
427
            opt_genxml = true;
428
        }
429
        else if (!strcmp(argv[i],"-n"))
430
        {
431
            opt_gensrc = true;
432
        }
433
        else if (!strcmp(argv[i],"-P"))
434
        {
435
            opt_gensrc = true;
436
            opt_inplace = true;
437
            opt_novalidation = true;
438
        }
439
        else if (!strcmp(argv[i],"-v"))
440
        {
441
            opt_validateonly = true;
442
        }
443
        else if (!strcmp(argv[i],"-I"))
444
        {
445
            i++;
446
            if (i==argc) {
447
                fprintf(stderr,"nedtool: unexpected end of arguments after -I\n");
448
                return 1;
449
            }
450
            // -I option is currently ignored
451
        }
452
        else if (argv[i][0]=='-' && argv[i][1]=='I')
453
        {
454
            // -I option is currently ignored
455
        }
456
        else if (!strcmp(argv[i],"-X"))
457
        {
458
            i++;
459
            if (i==argc) {
460
                fprintf(stderr,"nedtool: unexpected end of arguments after -X\n");
461
                return 1;
462
            }
463
            if (!strcmp(argv[i],"ned"))
464
                opt_nextfiletype = NED_FILE;
465
            else if (!strcmp(argv[i],"msg"))
466
                opt_nextfiletype = MSG_FILE;
467
            else if (!strcmp(argv[i],"xml"))
468
                opt_nextfiletype = XML_FILE;
469
            else if (!strcmp(argv[i],"off"))
470
                opt_nextfiletype = UNKNOWN_FILE;
471
            else {
472
                fprintf(stderr,"nedtool: unknown file type %s after -X\n",argv[i]);
473
                return 1;
474
            }
475
        }
476
        else if (!strcmp(argv[i],"-Q"))
477
        {
478
            opt_oldsyntax = true;
479
        }
480
        else if (!strcmp(argv[i],"-s"))
481
        {
482
            i++;
483
            if (i==argc) {
484
                fprintf(stderr,"nedtool: unexpected end of arguments after -s\n");
485
                return 1;
486
            }
487
            opt_suffix = argv[i];
488
        }
489
        else if (!strcmp(argv[i],"-S"))
490
        {
491
            i++;
492
            if (i==argc) {
493
                fprintf(stderr,"nedtool: unexpected end of arguments after -S\n");
494
                return 1;
495
            }
496
            opt_hdrsuffix = argv[i];
497
        }
498
        else if (!strcmp(argv[i],"-k"))
499
        {
500
            opt_inplace = true;
501
        }
502
        else if (!strcmp(argv[i],"-u"))
503
        {
504
            opt_splitnedfiles = true;
505
        }
506
        else if (!strcmp(argv[i],"-e"))
507
        {
508
            opt_unparsedexpr = true;
509
        }
510
        else if (!strcmp(argv[i],"-t"))
511
        {
512
            opt_storesrc = true;
513
        }
514
        else if (!strcmp(argv[i],"-y"))
515
        {
516
            opt_novalidation = true;
517
        }
518
        else if (!strcmp(argv[i],"-z"))
519
        {
520
            opt_noimports = true;
521
        }
522
        else if (!strcmp(argv[i],"-p"))
523
        {
524
            opt_srcloc = true;
525
        }
526
        else if (!strcmp(argv[i],"-m"))
527
        {
528
            opt_mergeoutput = true;
529
            outputtree = new FilesElement;
530
        }
531
        else if (!strcmp(argv[i],"-o"))
532
        {
533
            i++;
534
            if (i==argc) {
535
                fprintf(stderr,"nedtool: unexpected end of arguments after -o\n");
536
                return 1;
537
            }
538
            opt_outputfile = argv[i];
539
        }
540
        else if (!strcmp(argv[i],"-V"))
541
        {
542
            opt_verbose = true;
543
        }
544
        else if (!strcmp(argv[i], "-h"))
545
        {
546
            opt_here = true;
547
        }
548
        else if (argv[i][0]=='-')
549
        {
550
            fprintf(stderr,"nedtool: unknown option %s\n",argv[i]);
551
            return 1;
552
        }
553
        else if (argv[i][0]=='@')
554
        {
555
            // treat @listfile and @@listfile differently
556
            bool istmp = (argv[i][1]=='@');
557
            if (!processListFile(argv[i]+(istmp?2:1), istmp, errors))
558
                return 1;
559
        }
560
        else
561
        {
562
            // process individual files on the command line
563
            //FIXME these checks get bypassed with list files
564
            if (!opt_genxml && !opt_gensrc)
565
            {
566
                fprintf(stderr,"nedtool: generating C++ source not currently supported\n"); //XXX
567
                return 1;
568
            }
569
            if (opt_genxml && opt_gensrc)
570
            {
571
                fprintf(stderr,"nedtool: conflicting options -n (generate source) and -x (generate XML)\n");
572
                return 1;
573
            }
574
            if (opt_mergeoutput && opt_inplace)
575
            {
576
                fprintf(stderr,"nedtool: conflicting options -m (merge files) and -k (replace original file)\n");
577
                return 1;
578
            }
579
            if (opt_inplace && !opt_genxml && !opt_gensrc)
580
            {
581
                fprintf(stderr,"nedtool: conflicting options: -k (replace original file) needs -n (generate source) or -x (generate XML)\n");
582
                return 1;
583
            }
584
            if (opt_mergeoutput && !opt_genxml && !opt_gensrc)
585
            {
586
                fprintf(stderr,"nedtool: option -m not supported with C++ output\n");
587
                return 1;
588
            }
589
            if (opt_splitnedfiles && !opt_mergeoutput && !opt_inplace)
590
            {
591
                fprintf(stderr,"nedtool: option -u ignored because -k or -m is not specified\n"); //XXX not too logical
592
            }
593

    
594
#if SHELL_EXPANDS_WILDCARDS
595
            if (!processFile(argv[i], errors))
596
                return 1;
597
#else
598
            // we have to expand wildcards ourselves
599
            std::vector<std::string> filelist = FileGlobber(argv[i]).getFilenames();
600
            if (filelist.empty())
601
            {
602
                fprintf(stderr,"nedtool: not found: %s\n", argv[i]);
603
                return 1;
604
            }
605
            for (int i=0; i<filelist.size(); i++)
606
                if (!processFile(filelist[i].c_str(), errors))
607
                    return 1;
608
#endif
609
        }
610
    }
611

    
612
    if (opt_mergeoutput)
613
    {
614
        if (errors->containsError())
615
        {
616
            delete outputtree;
617
            return 1;
618
        }
619

    
620
        if (opt_splitnedfiles)
621
            NEDTools::splitToFiles(outputtree);
622

    
623
        const char *outfname;
624

    
625
        if (opt_outputfile)
626
            outfname = opt_outputfile;
627
        else if (opt_genxml)
628
            outfname = "out_n.xml";
629
        else if (opt_gensrc)
630
            outfname = "out_n.ned";
631
        else
632
            outfname = "out_n.cc";
633

    
634
        ofstream out(outfname);
635

    
636
        if (opt_genxml)
637
            generateXML(out, outputtree, opt_srcloc);
638
        else if (opt_gensrc)
639
            generateNED(out, outputtree, errors, opt_oldsyntax);
640
        else
641
            return 1; // mergeoutput with C++ output not supported
642
            // generateCpp(out, cout, outputtree);
643
        out.close();
644

    
645
        delete outputtree;
646

    
647
        if (errors->containsError())
648
            return 1;
649
    }
650

    
651
    return 0;
652
}
653

    
654