nam_stream.cc
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:14k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /*
  2.  * Copyright (c) 1998 University of Southern California.
  3.  * All rights reserved.                                            
  4.  *                                                                
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that the above copyright notice and this paragraph are
  7.  * duplicated in all such forms and that any documentation, advertising
  8.  * materials, and other materials related to such distribution and use
  9.  * acknowledge that the software was developed by the University of
  10.  * Southern California, Information Sciences Institute.  The name of the
  11.  * University may not be used to endorse or promote products derived from
  12.  * this software without specific prior written permission.
  13.  * 
  14.  * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
  15.  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  16.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  17.  *
  18.  */
  19. #include <stdlib.h>
  20. #include <sys/types.h>
  21. #include <limits.h>
  22. #include <assert.h>
  23. #include <stdlib.h>  // for atof
  24. #include <fcntl.h> // for O_NONBLOCK
  25. #include <sys/stat.h> // for lstat()
  26. #include <tclcl.h>
  27. #include "trace.h"
  28. #include "nam_stream.h"
  29. /**********************************************************************/
  30. class NamStreamClass : public TclClass {
  31. public:
  32. NamStreamClass() : TclClass("NamStream") {}
  33. TclObject* create(int argc, const char*const* argv) {
  34. if (argc < 5) 
  35. return 0;
  36. return NamStream::open(argv[4]);
  37. }
  38. } NamStream_class;
  39. int
  40. NamStream::command(int argc, const char *const *argv)
  41. {
  42. Tcl& tcl = Tcl::instance();
  43. if (0) {
  44. } else if (argc == 2 && strcmp(argv[1], "gets") == 0) {
  45. if (eof())
  46. return TCL_ERROR;
  47. // return a line
  48. char *buf = tcl.buffer();
  49. assert(4096 > TRACE_LINE_MAXLEN+1);  // can the buffer handle us?
  50. gets(buf, TRACE_LINE_MAXLEN);
  51. buf[TRACE_LINE_MAXLEN] = 0;  // paranoia
  52. tcl.result(buf);
  53. return TCL_OK;
  54. } else if (argc == 2 && strcmp(argv[1], "close") == 0) {
  55. close();
  56. return TCL_OK;
  57. } else if (argc == 2 && strcmp(argv[1], "eof") == 0) {
  58. tcl.resultf("%d", eof());
  59. return TCL_OK;
  60. };
  61. return (TclObject::command(argc, argv));
  62. }
  63. NamStream *
  64. NamStream::open(const char *fn)
  65. {
  66. struct stat state;
  67. if (strcmp(fn, "-") == 0 || strcmp(fn, "-.nam") == 0)
  68. return NamStreamPipe::open_pipe("-");
  69. #ifndef WIN32
  70. // Windows doesn't have the lstat and S_ISFIFO functions
  71. if (lstat(fn, &state) < 0)
  72. return NULL;
  73. if (S_ISFIFO(state.st_mode))
  74. return NamStreamPipe::open_pipe(fn);
  75. #endif
  76. if (strcmp(fn + strlen(fn) - 3, ".gz") == 0 ||
  77.     strcmp(fn + strlen(fn) - 2, ".Z") == 0) {
  78. #ifdef HAVE_ZLIB_H
  79. return new NamStreamCompressedFile(fn);
  80. #else /* ! HAVE_ZLIB */
  81. fprintf(stderr, "nam not built with zlib; cannot read compressed files.n");
  82. return NULL;
  83. #endif /* HAVE_ZLIB */
  84. };
  85. /* hope we've got it now */
  86. return new NamStreamFile(fn);
  87. }
  88. /*
  89.  * rgets (gets in reverse order)
  90.  */
  91. char *
  92. NamStream::rgets(char *buf, int len)
  93. {
  94. int ch;
  95. /*
  96.  * prior-line n current-line n next-line
  97.  *
  98.  * Initially the cursor is on the n of next-line.
  99.  * read in current-line (which is behind us)
  100.  * return it
  101.  * leave the cursor on c of current line.
  102.  */
  103. /* first make sure we back over the prior newline, if any */
  104. if (seek(-1, SEEK_CUR) < 0)
  105. return NULL;
  106. ch = get_char();
  107. if (seek(ch == 'n' ? -2 : -1, SEEK_CUR) < 0)
  108. return NULL;
  109. /* now loop backwards until we get to the newline separating
  110.  * prior and current.
  111.  */
  112. off_t pos = tell();
  113. for(;;) {
  114. ch = get_char();
  115. if (ch == 'n')
  116. break;
  117. if (pos == 0) {
  118. // beginning of file
  119. if (seek(-1, SEEK_CUR) < 0)
  120. return NULL;
  121. break;
  122. }
  123. // back up a char & try again
  124. pos--;
  125. if (seek(-2, SEEK_CUR) < 0)
  126. return NULL;
  127. };
  128. /* we're just passed the newline for prior-line, or we're at 0 */
  129. /* read current-line, then reset the pointer there */
  130. gets(buf, len);
  131. if (pos != seek(pos, SEEK_SET))
  132. return NULL;
  133. return buf;
  134. }
  135. /**********************************************************************/
  136. #if 0
  137. NamStreamFile::NamStreamFile(int fd) : NamStream(fd)
  138. {
  139. file_ = fdopen(fd, "r");
  140. is_open_ = (NULL != file_);
  141. }
  142. #endif /* 0 */
  143. NamStreamFile::NamStreamFile(const char *fn) : NamStream(fn)
  144. {
  145. #ifdef WIN32
  146. // Open in raw binary mode; we'll get rid of rn manually.
  147. // Otherwise ftell() and fseek() does not work properly. :(
  148. file_ = fopen(fn, "rb");
  149. #else
  150. file_ = fopen(fn, "r");
  151. #endif
  152. is_open_ = (NULL != file_);
  153. if (!is_open_) 
  154. perror(fn);
  155. }
  156. char *
  157. NamStreamFile::gets(char *buf, int len) {
  158.   char ch = fgetc(file_);
  159.   if (ch == 'n') {
  160.     return fgets(buf, len-1, file_);
  161.   } else {
  162.     ungetc(ch, file_);
  163.     return fgets(buf, len, file_);
  164.   }
  165. }
  166. char
  167. NamStreamFile::get_char()
  168. {
  169. return getc(file_);
  170. }
  171. off_t
  172. NamStreamFile::seek(off_t offset, int whence)
  173. {
  174. if (0 == fseek(file_, offset, whence))
  175. return ftell(file_);
  176. else return -1;
  177. }
  178. off_t
  179. NamStreamFile::tell()
  180. {
  181. return ftell(file_);
  182. }
  183. int
  184. NamStreamFile::close()
  185. {
  186. // fclose(NULL) crashes on Windows, so double check
  187. if (file_ == NULL) return 0;
  188. int e = fclose(file_);
  189. file_ = NULL;
  190. return e;
  191. }
  192. int
  193. NamStreamFile::eof()
  194. {
  195. // feof(NULL) crashes on Windows, so double check
  196. return (file_ == NULL) || feof(file_);
  197. }
  198. int
  199. NamStreamFile::read(char *buf, int size)
  200. {
  201.       return fread(buf, 1, size, file_);
  202. }
  203. /**********************************************************************/
  204. #ifdef HAVE_ZLIB_H
  205. /*
  206.  * Beware:
  207.  * nam *requires* zlib-1.1.3, as we trip over bugs in 1.1.2's gz* functions.
  208.  */
  209. NamStreamCompressedFile::NamStreamCompressedFile(const char *fn) : NamStream(fn)
  210. {
  211. #ifndef ZLIB_VERSION
  212. die("zlib version not specified.n");
  213. int a, b, c;
  214. if (3 != sscanf(ZLIB_VERSION, "%d.%d.%d", &a, &b, &c)) {
  215. die("zlib version: unknown format.n");
  216. };
  217. if (!(a > 1 ||
  218.     (a == 1 && b > 1) ||
  219.       (a == 1 && b == 1 && c >= 3)))
  220. die("zlib version is too old, nam requires 1.1.3.n");
  221. #endif
  222. file_ = gzopen(fn, "r");
  223. is_open_ = (NULL != file_);
  224. }
  225. char *
  226. NamStreamCompressedFile::gets(char *buf, int len)
  227. {
  228. char *b = gzgets(file_, buf, len);
  229. return b;
  230. }
  231. char 
  232. NamStreamCompressedFile::get_char()
  233. {
  234. return gzgetc(file_);
  235. }
  236. off_t
  237. NamStreamCompressedFile::seek(off_t offset, int whence)
  238. {
  239. if (whence == SEEK_END) {
  240. /*
  241.  * zlib doesn't support SEEK_END :-<
  242.  * Walk our way to the end-of-file.
  243.  */
  244. off_t p = gzseek(file_, 0, SEEK_SET), q;
  245. #define STEP (16*1024)
  246. char buf[STEP];
  247. int count;
  248. for (;;) {
  249. /*
  250.  * Sigh.  We actually move all the bytes.  XXX
  251.  * (we can't lseek because we'd lseek
  252.  * past eof without knowing).
  253.  */
  254. count = gzread(file_, buf, STEP);
  255. if (count <= 0)
  256. break;
  257. p += count;
  258. };
  259. q = gztell(file_);
  260. assert (p == q);
  261. return q;
  262. } else {
  263. return gzseek(file_, offset, whence);
  264. };
  265. }
  266. off_t
  267. NamStreamCompressedFile::tell()
  268. {
  269. return gztell(file_);
  270. }
  271. int
  272. NamStreamCompressedFile::close()
  273. {
  274. int e = gzclose(file_);
  275. file_ = NULL;
  276. return e;
  277. }
  278. int
  279. NamStreamCompressedFile::eof()
  280. {
  281. return gzeof(file_);
  282. }
  283. int
  284. NamStreamCompressedFile::read(char *buf, int size)
  285. {
  286. int e = gzread(file_, buf, size);
  287. return e;
  288. }
  289. #endif /* HAVE_ZLIB */
  290. /**********************************************************************
  291.  * Implementation of class NamStreamPipe
  292.  * - The read operation called by nam is always performed on the backup
  293.  *   file.
  294.  * - Pipe data are copied to backup file and never returned to nam
  295.  *   directly.
  296.  * - Pipes are checked when timer expires or nam executes read
  297.  *   operations.
  298.  *********************************************************************/
  299. // static data members
  300. NamStreamPipe* NamStreamPipe::head_ = NULL;
  301. int            NamStreamPipe::instances_ = 0;
  302. Tcl_TimerToken NamStreamPipe::timer_ = NULL;
  303. /**********************************************************************
  304.  * Timer handler that checks data availability of all pipes.
  305.  **********************************************************************/
  306. void
  307. NamStreamPipe::timer_handler(ClientData data)
  308. {
  309. if (read_pipe())
  310. timer_ = Tcl_CreateTimerHandler(10, timer_handler, NULL);
  311. else timer_ = NULL;
  312. }
  313. /**********************************************************************
  314.  * Read currently opened pipes.
  315.  *
  316.  * RETURN: the number of currently opened pipes.
  317.  **********************************************************************/
  318. #ifdef PIPE_BUF
  319. # define BUF_SIZE PIPE_BUF
  320. #else
  321. # define BUF_SIZE 8192
  322. #endif
  323. int
  324. NamStreamPipe::read_pipe()
  325. {
  326. NamStreamPipe *p;
  327. int l, n, fileopen = 0;
  328. off_t off;
  329. static char buf[BUF_SIZE];
  330. for (p = head_; p; p = p->next_) { // polling for all pipes
  331. if (p->front_ < 0)
  332. continue;
  333. l = ::read(p->front_, buf, BUF_SIZE);
  334. if (l < 0) {
  335. fileopen++;
  336. continue;
  337. }
  338. if (l == 0) { // end of file
  339. ::close(p->front_);
  340. p->front_ = -1;
  341. continue;
  342. }
  343. // there are data ready for read, copy to backup file
  344. off = ftell(p->back_);
  345. fseek(p->back_, 0, SEEK_END);
  346. n = fwrite(buf, l, 1, p->back_);
  347. if (n <= 0) { // fail to write to backup file
  348. ::close(p->front_);
  349. p->front_ = -1;
  350. // XXX notify user
  351. die("NamStreamPipe::read_pipe: tmpfile write problem.n");
  352. }
  353. else {
  354. p->back_len_ += l;
  355. fileopen++;
  356. }
  357. if (-1 == fseek(p->back_, off, SEEK_SET)) {
  358. // XXX notify user
  359. die("NamStreamPipe::read_pipe: fseek problem.n");
  360. }
  361. }
  362. return fileopen;
  363. }
  364. /**********************************************************************
  365.  * Reentrant function for opening nam pipe stream.
  366.  *
  367.  * RETURN: pointer to an NamStreamPipe instance that is associated
  368.  *         with the specified pipe.
  369.  **********************************************************************/
  370. NamStreamPipe*
  371. NamStreamPipe::open_pipe(const char *fn)
  372. {
  373. NamStreamPipe *p;
  374. for (p = head_; p; p = p->next_) {
  375. if (! strcmp(fn, p->pipename_))
  376. return p;
  377. }
  378. return new NamStreamPipe(fn);
  379. }
  380. NamStreamPipe::NamStreamPipe(const char *fn) :
  381. NamStream(fn), front_(-1),back_(NULL), back_len_(0),
  382. pipename_(NULL), prev_(NULL), next_(NULL)
  383. {
  384. #ifndef WIN32
  385. // Windows doesn't have fcntl() function
  386. // open pipe and temporary file
  387. if (! strcmp(fn, "-")) { // stdin
  388. int flag = fcntl(0, F_GETFL, 0);
  389. if (flag < 0)
  390. goto exception;
  391. if (fcntl(0, F_SETFL, flag | O_NONBLOCK) < 0)
  392. goto exception;
  393. front_ = 0;
  394. }
  395. else front_ = ::open(fn, O_RDONLY | O_NONBLOCK);
  396. back_ = tmpfile();
  397. #endif
  398. if ((front_ < 0) || (back_ == NULL))
  399. goto exception;
  400. pipename_ = strdup(fn);
  401. is_open_ = 1;
  402. next_ = head_;
  403. if (next_)
  404. next_->prev_ = this;
  405. head_ = this;
  406. instances_++;
  407. if (! timer_) { // start the timer if it's idle
  408. timer_ = Tcl_CreateTimerHandler(10, timer_handler, NULL);
  409. }
  410. return;
  411. exception:
  412. if (front_ > 0)
  413. ::close(front_);
  414. if (back_)
  415. fclose(back_);
  416. }
  417. /**********************************************************************
  418.  * Destructor:
  419.  **********************************************************************/
  420. NamStreamPipe::~NamStreamPipe()
  421. {
  422. fclose(back_);
  423. if (front_ > 0)
  424. ::close(front_);
  425. if (next_)
  426. next_->prev_ = prev_;
  427. if (prev_)
  428. prev_->next_ = next_;
  429. if (pipename_)
  430. delete pipename_;
  431. instances_--;
  432. }
  433. /**********************************************************************
  434.  * Read a line. Nam requires the following semantics:
  435.  * RETURN: NULL to indicate end-of-file
  436.  * RETURN: a COMPLETE line, i.e. a complete nam command. It can't return
  437.  *         a partial line, otherwise nam will complain because nam
  438.  *         assumes NamStream is line-buffered.
  439.  * We assume that there always is new data in the pipe. It's the
  440.  * responsibility of the pipe data producer (i.e. the application that
  441.  * generates the nam events to the pipe) to ensure we always have new
  442.  * input. If we have no new input, then we simply block.
  443.  **********************************************************************/
  444. char *
  445. NamStreamPipe::gets(char *buf, int len)
  446. {
  447. char *ret = buf;
  448. off_t off = ftell(back_);
  449. read_pipe();
  450. /*
  451.  * Case 1: The pipe is just opened to read, but the other end
  452.  *         of the pipe has not open it to write. So, read_pipe()
  453.  *         will close it.
  454.  *         => Reopen the pipe, and return a fake initial command.
  455.  * Case 2: Both ends have opened the pipe, but the writer has not
  456.  *         produced any data.
  457.  *         => Return a fake initial command.
  458.  * The fake return value needs to be an initial command. Comment
  459.  * lines or empty lines are not accepted by nam (e.g. see animator.tcl)
  460.  *
  461.  */
  462. if (back_len_ == 0) {
  463. #ifndef WIN32
  464. // Windows doesn't have O_NONBLOCK
  465. if (front_ == -1) {   // it may be closed by read_pipe()
  466. front_ = ::open(pipename_, O_RDONLY | O_NONBLOCK);
  467. }
  468. #endif
  469. strcpy(buf, "V -t * -v 1.0a9 -a 0n");
  470. return buf;
  471. }
  472. while (1) {
  473. /*
  474.  * Case 3: The backup file is over, but there is no new data in pipe.
  475.  *         => Busy waiting for new input. XXX
  476.  *            This is not a good idea. Since nam is a
  477.  *            single-thread application, busy waiting makes nam
  478.  *            unable to handle other events (e.g. Window, I/O events).
  479.  *            The problem here is that we don't know what to return
  480.  *            when there is no new input from the pipe.
  481.  *            (see netmodel.cc#NetModel::handle())
  482.  */
  483. if (! fgets(buf, len, back_)) { // EOF backup file
  484. if (front_ == -1)
  485. return NULL;
  486. else read_pipe();
  487. }
  488. /*
  489.  * Case 4: The backup file is near to over and the last line is
  490.  *         not complete.
  491.  *         => Busy waiting for new input. XXX
  492.  */
  493. else {
  494. int n = strlen(buf) - 1;
  495. if (buf[n] == 'n') // here is the normal case
  496. return buf;
  497. if (front_ == -1)
  498. return NULL;
  499. fseek(back_, off, SEEK_SET);
  500. read_pipe();
  501. }
  502. }
  503. return ret;
  504. }
  505. /**********************************************************************
  506.  * Read a block of data.
  507.  * XXX Not finished. Don't know what semantics is required by nam.
  508.  **********************************************************************/
  509. int
  510. NamStreamPipe::read(char *buf, int len)
  511. {
  512. read_pipe();
  513. return fread(buf, 1, len, back_);
  514. }
  515. /**********************************************************************
  516.  * Read a character.
  517.  * XXX Not finished. Don't know what semantics is required by nam.
  518.  **********************************************************************/
  519. char
  520. NamStreamPipe::get_char()
  521. {
  522. read_pipe();
  523. return fgetc(back_);
  524. }
  525. off_t
  526. NamStreamPipe::seek(off_t offset, int whence)
  527. {
  528. read_pipe();
  529. if (0 == fseek(back_, offset, whence))
  530. return ftell(back_);
  531. else return -1;
  532. }
  533. off_t
  534. NamStreamPipe::tell()
  535. {
  536. return ftell(back_);
  537. }
  538. int
  539. NamStreamPipe::close()
  540. {
  541. fseek(back_, 0, SEEK_SET);
  542. return 0;
  543. }
  544. int
  545. NamStreamPipe::eof()
  546. {
  547. if (front_ == -1) {
  548. return feof(back_);
  549. }
  550. else return 0;
  551. }