PageRenderTime 77ms CodeModel.GetById 33ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 0ms

/omnetpp-4.1/src/nedxml/nedfilebuffer.cc

https://bitbucket.org/indigopony/omnetproject
C++ | 460 lines | 316 code | 72 blank | 72 comment | 90 complexity | 01da1da66ad4c37badece3c0373d11c4 MD5 | raw file
  1//==========================================================================
  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
 27USING_NAMESPACE
 28
 29//-----------------------------------------------------------
 30
 31NEDFileBuffer::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
 44NEDFileBuffer::~NEDFileBuffer()
 45{
 46    delete [] wholeFile;
 47    delete [] lineBeg;
 48    delete [] commentBuf;
 49}
 50
 51bool 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
 75bool 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]
 87bool 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
126int NEDFileBuffer::getLineType(int lineNumber)
127{
128    return getLineType(getPosition(lineNumber,0));
129}
130
131int 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
139bool 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
149int NEDFileBuffer::getLineIndent(int lineNumber)
150{
151    return getLineIndent(getPosition(lineNumber,0));
152}
153
154int 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
165char *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
190const 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
210const 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
216YYLTYPE 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//
246int 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
255const char *NEDFileBuffer::getBannerComment(YYLTYPE pos)
256{
257    return stripComment(get(getBannerCommentPos(pos)));
258}
259
260YYLTYPE 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//
284const char *NEDFileBuffer::getTrailingComment(YYLTYPE pos)
285{
286    return stripComment(get(getTrailingCommentPos(pos)));
287}
288
289YYLTYPE 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
326static 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
335const 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
375YYLTYPE 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
385const 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//
394char *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
421void 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}
460