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