Project

General

Profile

Statistics
| Branch: | Revision:

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

History | View | Annotate | Download (12.2 KB)

1 01873262 Georg Kunz
//==========================================================================
2
//  NEDFILEBUFFER.CC - part of
3
//
4
//                     OMNeT++/OMNEST
5
//            Discrete System Simulation in C++
6
//
7
//==========================================================================
8
9
/*--------------------------------------------------------------*
10
  Copyright (C) 2002-2008 Andras Varga
11
  Copyright (C) 2006-2008 OpenSim Ltd.
12

13
  This file is distributed WITHOUT ANY WARRANTY. See the file
14
  `license' for details on this and other legal matters.
15
*--------------------------------------------------------------*/
16
17
18
#include <string.h>
19
#include <stdio.h>
20
#include <time.h>
21
#include <sys/stat.h>
22
#include <assert.h>
23
#include "opp_ctype.h"
24
#include "nedfilebuffer.h"
25
#include "nedyylib.h"
26
27
USING_NAMESPACE
28
29
//-----------------------------------------------------------
30
31
NEDFileBuffer::NEDFileBuffer()
32
{
33
    wholeFile = NULL;
34
35
    numLines = 0;
36
    lineBeg = NULL;
37
38
    end = 0;
39
40
    commentBufLen = 1024;
41
    commentBuf = new char[commentBufLen];
42
}
43
44
NEDFileBuffer::~NEDFileBuffer()
45
{
46
    delete [] wholeFile;
47
    delete [] lineBeg;
48
    delete [] commentBuf;
49
}
50
51
bool NEDFileBuffer::readFile(const char *filename)
52
{
53
    // load file into memory
54
    if (wholeFile) return false; // reinit not supported
55
56
    // Note: must use binary mode on the file, otherwise due to CR/LF conversion
57
    // the number of characters actually stored will be less than "size"
58
    // (which is the same as fread()'s return value), and we'll get garbage
59
    // at the end of the buffer.
60
    FILE *intmp = fopen(filename,"rb");
61
    if (!intmp) return false;
62
63
    struct stat statbuf;
64
    fstat(fileno(intmp), &statbuf);
65
    int size = statbuf.st_size;
66
    wholeFile = new char [size+2];  // +1 because last line may need an extra '\n'
67
68
    fread(wholeFile,size,1,intmp);
69
    fclose(intmp);
70
    wholeFile[size]='\0';
71
72
    return indexLines();
73
}
74
75
bool NEDFileBuffer::setData(const char *data)
76
{
77
    if (wholeFile) return false;  // reinit not supported
78
79
    wholeFile = new char [strlen(data)+2]; // +1 because last line may need an extra '\n'
80
    strcpy(wholeFile,data);
81
    return indexLines();
82
}
83
84
// indexLines()
85
//   Sets up the lineBeg[] array. Line numbering goes from 1, ie. the first line
86
//   is lineBeg[1]
87
bool NEDFileBuffer::indexLines()
88
{
89
    // convert all CR and CR+LF into LF to avoid trouble
90
    char *s, *d;
91
    for (s=d=wholeFile; d==wholeFile || *(d-1); )
92
    {
93
        if (*s=='\r' && *(s+1)=='\n')  s++;
94
        else if (*s=='\r') {s++; *d++ = '\n';}
95
        else *d++ = *s++;
96
    }
97
98
    // terminate last line if necessary
99
    d--;  // now d points to terminating zero
100
    if (*(d-1)!='\n') {*d++ = '\n'; *d = '\0';}
101
102
    // count lines
103
    numLines = 0;
104
    for (s = wholeFile; *s; s++)
105
        if (*s=='\n')
106
            numLines++;
107
108
    // allocate array
109
    lineBeg = new char * [numLines+2];
110
111
    // fill in array
112
    lineBeg[0] = NULL;
113
    lineBeg[1] = wholeFile;
114
    int line = 2;
115
    for (s = wholeFile; *s; s++)
116
        if (*s=='\n')
117
            lineBeg[line++] = s+1;
118
119
    // last line plus one points to end of file (terminating zero)
120
    assert(line==numLines+2);
121
    assert(lineBeg[numLines+1]==s);
122
123
    return true;
124
}
125
126
int NEDFileBuffer::getLineType(int lineNumber)
127
{
128
    return getLineType(getPosition(lineNumber,0));
129
}
130
131
int NEDFileBuffer::getLineType(const char *s)
132
{
133
    while (*s==' ' || *s=='\t') s++;
134
    if (*s=='/' && *(s+1)=='/') return COMMENT_LINE;
135
    if (!*s || *s=='\n') return BLANK_LINE; // if there's only punctuation, it'll count as BLANK too
136
    return CODE_LINE;
137
}
138
139
bool NEDFileBuffer::lineContainsCode(const char *s)
140
{
141
    // tolerant version: punctuation does not count as code
142
    while (*s==' ' || *s=='\t' || *s==':' || *s==',' || *s==';') s++;
143
    if (*s=='/' && *(s+1)=='/') return false;
144
    if (!*s || *s=='\n') return false;
145
    return true;
146
}
147
148
149
int NEDFileBuffer::getLineIndent(int lineNumber)
150
{
151
    return getLineIndent(getPosition(lineNumber,0));
152
}
153
154
int NEDFileBuffer::getLineIndent(const char *s)
155
{
156
    int co = 0;
157
    while (*s==' ' || *s=='\t')
158
    {
159
        co += (*s=='\t') ? 8-(co%8) : 1;
160
        s++;
161
    }
162
    return co;
163
}
164
165
char *NEDFileBuffer::getPosition(int line, int column)
166
{
167
    // tolerant version: if line is out of range, return beginning or end of file
168
    if (line<1)
169
        return lineBeg[1];
170
    if (line>numLines)
171
        return lineBeg[numLines]+strlen(lineBeg[numLines]);
172
173
    char *s = lineBeg[line];
174
175
    int co = 0;
176
    while (co<column)
177
    {
178
        if (!*s) return s;
179
        if (*s=='\n')
180
            {column-=co; co=0;}
181
        else if (*s=='\t')
182
            co += 8-(co%8);
183
        else
184
            co++;
185
        s++;
186
    }
187
    return s;
188
}
189
190
const char *NEDFileBuffer::get(YYLTYPE pos)
191
{
192
    if (end) {*end = savedChar; end=NULL;}
193
194
    // return NULL
195
    if (pos.first_line==0 && pos.last_line==0)
196
        return NULL;
197
198
    if (isEmpty(pos))
199
        return "";
200
201
    // the meat of the whole stuff:
202
    end = getPosition(pos.last_line,  pos.last_column);
203
    savedChar = *end;
204
    *end = '\0';
205
206
    char *beg = getPosition(pos.first_line, pos.first_column);
207
    return beg;
208
}
209
210
const char *NEDFileBuffer::getFileComment()
211
{
212
    return stripComment(get(getFileCommentPos()));
213
}
214
215
// all subsequent comment and blank lines will be included, up to the _last blank_ line
216
YYLTYPE NEDFileBuffer::getFileCommentPos()
217
{
218
    if (end) {*end = savedChar; end=NULL;}
219
220
    // seek end of comment block (that is, last blank line before a code line or eof)
221
    int lastBlank = 0;
222
    int lineType;
223
    int line;
224
    for (line=1; line<=numLines && (lineType=getLineType(line))!=CODE_LINE; line++)
225
        if (lineType==BLANK_LINE)
226
            lastBlank = line;
227
228
    // if file doesn't contain code line, take the whole file
229
    if (line > numLines)
230
        lastBlank = numLines;
231
232
    // return comment block
233
    YYLTYPE commentPos;
234
    commentPos.first_line = 1;
235
    commentPos.first_column = 0;
236
    commentPos.last_line = lastBlank+1;
237
    commentPos.last_column = 0;
238
    return commentPos;
239
}
240
241
// topLineOfBannerComment()
242
//   li: code line below the comment
243
//   result: ==li     there was no comment
244
//           ==li-1   single-line comment on line li-1
245
//
246
int NEDFileBuffer::topLineOfBannerComment(int li)
247
{
248
    // seek beginning of comment block
249
    int codeLineIndent = getLineIndent(li);
250
    while (li>=2 && getLineType(li-1)==COMMENT_LINE && getLineIndent(li-1) <= codeLineIndent)
251
        li--;
252
    return li;
253
}
254
255
const char *NEDFileBuffer::getBannerComment(YYLTYPE pos)
256
{
257
    return stripComment(get(getBannerCommentPos(pos)));
258
}
259
260
YYLTYPE NEDFileBuffer::getBannerCommentPos(YYLTYPE pos)
261
{
262
    trimSpaceAndComments(pos);
263
    if (end) {*end = savedChar; end=NULL;}
264
265
    // there must be nothing before it on the same line
266
    char *beg = getPosition(pos.first_line, pos.first_column);
267
    for (char *s=getPosition(pos.first_line, 0); s<beg; s++)
268
        if (*s!=' ' && *s!='\t')
269
            return makeYYLTYPE(1,0,1,0); // empty pos, will be returned as ""
270
271
    // return comment block
272
    YYLTYPE commentPos;
273
    commentPos.first_line = topLineOfBannerComment(pos.first_line);
274
    commentPos.first_column = 0;
275
    commentPos.last_line = pos.first_line;
276
    commentPos.last_column = 0;
277
    return commentPos;
278
}
279
280
//
281
//  also serves as "getRightComment"
282
//  NOTE: only handles really trailing comments, ie. those after last_line.last_column
283
//
284
const char *NEDFileBuffer::getTrailingComment(YYLTYPE pos)
285
{
286
    return stripComment(get(getTrailingCommentPos(pos)));
287
}
288
289
YYLTYPE NEDFileBuffer::getTrailingCommentPos(YYLTYPE pos)
290
{
291
    trimSpaceAndComments(pos);
292
    if (end) {*end = savedChar; end=NULL;}
293
294
    // there must be no code after it on the same line
295
    char *endp = getPosition(pos.last_line, pos.last_column);
296
    if (lineContainsCode(endp))
297
        return makeYYLTYPE(1,0,1,0); // empty pos, will be returned as ""
298
299
    // seek 1st line after comment (lineAfter)
300
    int lineAfter;
301
302
    if (pos.last_line>=numLines) // 'pos' ends on last line of file
303
    {
304
        lineAfter = numLines+1;
305
    }
306
    else
307
    {
308
        // seek fwd to next code line (or end of file)
309
        lineAfter = pos.last_line+1;
310
        while (lineAfter<=numLines && getLineType(lineAfter)!=CODE_LINE)
311
            lineAfter++;
312
313
        // now seek back to beginning of comment block
314
        lineAfter = topLineOfBannerComment(lineAfter);
315
    }
316
317
    // return comment block
318
    YYLTYPE commentPos;
319
    commentPos.first_line = pos.last_line;
320
    commentPos.first_column = pos.last_column;
321
    commentPos.last_line = lineAfter;
322
    commentPos.last_column = 0;
323
    return commentPos;
324
}
325
326
static const char *findCommentOnLine(const char *s)
327
{
328
    // find comment on this line
329
    while (*s!='\n' && (*s!='/' || *(s+1)!='/')) s++;
330
    if (*s!='/' || *(s+1)!='/')
331
        return NULL;
332
    return s;
333
}
334
335
const char *NEDFileBuffer::getNextInnerComment(YYLTYPE& pos)
336
{
337
    // FIXME unfortunately, this will collect comments even from
338
    // inside single-line or multi-line string literals
339
    // (like "Hello //World")
340
    while (!isEmpty(pos))
341
    {
342
        const char *s = getPosition(pos.first_line, pos.first_column);
343
        const char *comment = findCommentOnLine(s);
344
        if (comment)
345
        {
346
            int commentColumn = pos.first_column + comment - s;
347
            if (pos.first_line==pos.last_line && commentColumn >= pos.last_column)
348
                return NULL; // comment is past the end of "pos"
349
350
            // seek fwd to next code line (or end of block)
351
            int lineAfter = pos.first_line+1;
352
            while (lineAfter<pos.last_line && getLineType(lineAfter)!=CODE_LINE)
353
                lineAfter++;
354
355
            YYLTYPE commentPos;
356
            commentPos.first_line = pos.first_line;
357
            commentPos.first_column = commentColumn;
358
            commentPos.last_line = lineAfter;
359
            commentPos.last_column = 0;
360
361
            // skip comment
362
            pos.first_line = commentPos.last_line;
363
            pos.first_column = commentPos.last_column;
364
365
            return stripComment(get(commentPos));
366
        }
367
368
        // go to beginning of next line
369
        ++pos.first_line;
370
        pos.first_column = 0;
371
    }
372
    return NULL;
373
}
374
375
YYLTYPE NEDFileBuffer::getFullTextPos()
376
{
377
    YYLTYPE pos;
378
    pos.first_line = 1;
379
    pos.first_column = 0;
380
    pos.last_line = numLines+1;
381
    pos.last_column = 0;
382
    return pos;
383
}
384
385
const char *NEDFileBuffer::getFullText()
386
{
387
    return get(getFullTextPos());
388
}
389
390
// stripComment()
391
//  return a "stripped" version of a multi-line comment --
392
//  all non-comment elements (comma, etc) are deleted
393
//
394
char *NEDFileBuffer::stripComment(const char *comment)
395
{
396
    // expand buffer if necessary
397
    if (commentBufLen < (int)strlen(comment)+1)
398
    {
399
        commentBufLen = strlen(comment)+1;
400
        delete [] commentBuf;
401
        commentBuf = new char[commentBufLen];
402
    }
403
404
    const char *s = comment;
405
    char *d = commentBuf;
406
    bool incomment = false;
407
    while(*s)
408
    {
409
        if ((*s=='/' && *(s+1)=='/')) incomment = true;
410
        if (*s=='\n') incomment = false;
411
412
        if (incomment || opp_isspace(*s))
413
            *d++ = *s++;
414
        else
415
            s++;
416
    }
417
    *d = '\0';
418
    return commentBuf;
419
}
420
421
void NEDFileBuffer::trimSpaceAndComments(YYLTYPE& pos)
422
{
423
    if (end) {*end = savedChar; end=NULL;}
424
425
    // skip space and comments with the beginning of the region
426
    const char *s = getPosition(pos.first_line, pos.first_column);
427
    while (opp_isspace(*s) || (*s=='/' && *(s+1)=='/'))
428
    {
429
        if (*s=='\n' || *s=='/')
430
        {
431
            // newline or comment: skip to next line
432
            pos.first_line++;
433
            pos.first_column = 0;
434
            if (pos.first_line > numLines)
435
                break;
436
            s = getPosition(pos.first_line, pos.first_column);
437
        }
438
        else if (*s=='\t')
439
        {
440
            pos.first_column += 8 - (pos.first_column % 8);
441
            s++;
442
        }
443
        else // treat the rest as space
444
        {
445
            pos.first_column++;
446
            s++;
447
        }
448
    }
449
450
    // just make sure "start" doesn't overtake "end"
451
    if (pos.first_line>pos.last_line)
452
        pos.first_line = pos.last_line;
453
    if (pos.first_line==pos.last_line && pos.first_column>pos.last_column)
454
        pos.first_column = pos.last_column;
455
456
    // TBD decrement last_line/last_column while they point into space/comment;
457
    // this is currently not needed though, as bison grammar doesn't produce
458
    // YYLTYPEs with trailing spaces/comments.
459
}