CoEdFile.C 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. /*
  2. * CDE - Common Desktop Environment
  3. *
  4. * Copyright (c) 1993-2012, The Open Group. All rights reserved.
  5. *
  6. * These libraries and programs are free software; you can
  7. * redistribute them and/or modify them under the terms of the GNU
  8. * Lesser General Public License as published by the Free Software
  9. * Foundation; either version 2 of the License, or (at your option)
  10. * any later version.
  11. *
  12. * These libraries and programs are distributed in the hope that
  13. * they will be useful, but WITHOUT ANY WARRANTY; without even the
  14. * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  15. * PURPOSE. See the GNU Lesser General Public License for more
  16. * details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with these libraries and programs; if not, write
  20. * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
  21. * Floor, Boston, MA 02110-1301 USA
  22. */
  23. //%% (c) Copyright 1993, 1994 Hewlett-Packard Company
  24. //%% (c) Copyright 1993, 1994 International Business Machines Corp.
  25. //%% (c) Copyright 1993, 1994 Sun Microsystems, Inc.
  26. //%% (c) Copyright 1993, 1994 Novell, Inc.
  27. //%% $XConsortium: CoEdFile.C /main/3 1995/10/20 17:06:45 rswiston $
  28. /*
  29. * CoEdFile.cc
  30. *
  31. * Copyright (c) 1991 by Sun Microsystems. All Rights Reserved.
  32. *
  33. * Permission to use, copy, modify, distribute, and sell this software
  34. * and its documentation for any purpose is hereby granted without
  35. * fee, provided that the above copyright notice appear in all copies
  36. * and that both that copyright notice and this permission notice
  37. * appear in supporting documentation, and that the names of Sun
  38. * Microsystems and its subsidiaries not be used in advertising or
  39. * publicity pertaining to distribution of the software without
  40. * specific, written prior permission. Sun Microsystems and its
  41. * subsidiaries make no representations about the suitability of this
  42. * software for any purpose. It is provided "as is" without express
  43. * or implied warranty.
  44. *
  45. * Sun Microsystems and its subsidiaries disclaim all warranties with
  46. * regard to this software, including all implied warranties of
  47. * merchantability and fitness. In no event shall Sun Microsystems or
  48. * its subsidiaries be liable for any special, indirect or
  49. * consequential damages or any damages whatsoever resulting from loss
  50. * of use, data or profits, whether in an action of contract,
  51. * negligence or other tortious action, arising out of or in
  52. * connection with the use or performance of this software.
  53. */
  54. #include <stdio.h>
  55. #include <string.h>
  56. #include <sys/param.h>
  57. #include <stdlib.h>
  58. #include <poll.h>
  59. #include <sys/time.h>
  60. #include <sys/resource.h>
  61. #include "CoEdFile.h"
  62. #include "CoEdGlobals.h"
  63. #include "CoEdChangeHistory.h"
  64. #include "CoEdChangeQueue.h"
  65. #include "CoEdTextVersion.h"
  66. #undef DEBUG
  67. CoEdFile::
  68. CoEdFile( const char *path, CoEdTextBuffer *textBuf, CoEdStatus &status,
  69. int timeOutSec )
  70. {
  71. Tt_status err;
  72. _next = 0;
  73. _numLocalChanges = 0;
  74. _joining = 1;
  75. if (path == 0) {
  76. fprintf( stderr, "libCoEd: can't join null file\n" );
  77. status = CoEdErrFile;
  78. return;
  79. }
  80. _appliedChanges = new CoEdChangeHistory;
  81. if (_appliedChanges == 0) {
  82. status = CoEdErrNoMem;
  83. return;
  84. }
  85. _textBuf = textBuf;
  86. if (_textBuf == 0) {
  87. status = CoEdErrBadPointer;
  88. return;
  89. }
  90. _unAppliedChanges = new CoEdChangeQueue;
  91. if (_unAppliedChanges == 0) {
  92. status = CoEdErrNoMem;
  93. return;
  94. }
  95. _version = new CoEdTextVersion;
  96. if (_version == 0) {
  97. status = CoEdErrNoMem;
  98. return;
  99. }
  100. _versionInQ = new CoEdTextVersion;
  101. if (_versionInQ == 0) {
  102. status = CoEdErrNoMem;
  103. return;
  104. }
  105. _coEditors = new CoEdSiteIDList;
  106. if (_coEditors == 0) {
  107. status = CoEdErrNoMem;
  108. return;
  109. }
  110. //
  111. // Join the file.
  112. //
  113. err = tt_file_join( path );
  114. if (err != TT_OK) {
  115. fprintf( stderr, "libCoEd: %s: %s\n", path,
  116. tt_status_message( err ));
  117. status = (CoEdStatus)err;
  118. return;
  119. }
  120. //
  121. // Trick ToolTalk into translating the path into the canonical
  122. // path that it will use to label messages about this file.
  123. //
  124. char *oldDefaultFile = tt_default_file();
  125. err = tt_ptr_error( oldDefaultFile );
  126. if (err != TT_OK) {
  127. fprintf( stderr, "libCoEd: tt_default_file(): %s\n",
  128. tt_status_message( err ));
  129. status = (CoEdStatus)err;
  130. return;
  131. }
  132. err = tt_default_file_set( path );
  133. if (err != TT_OK) {
  134. fprintf( stderr, "libCoEd: tt_default_file_set(\"%s\"): %s\n",
  135. path, tt_status_message( err ));
  136. status = (CoEdStatus)err;
  137. return;
  138. }
  139. char *temp = tt_default_file();
  140. err = tt_ptr_error( temp );
  141. if (err != TT_OK) {
  142. fprintf( stderr, "libCoEd: tt_default_file(): %s\n",
  143. tt_status_message( err ));
  144. status = (CoEdStatus)err;
  145. return;
  146. }
  147. _path = strdup( temp );
  148. if (_path == 0) {
  149. status = CoEdErrNoMem;
  150. return;
  151. }
  152. tt_free( temp );
  153. if (oldDefaultFile != 0) {
  154. //
  155. // Reset the default file to what it was.
  156. //
  157. Tt_status err = tt_default_file_set( oldDefaultFile );
  158. if (err != TT_OK) {
  159. fprintf( stderr, "libCoEd: tt_default_file_set(\"%s\")"
  160. ": %s\n", oldDefaultFile,
  161. tt_status_message( err ));
  162. status = (CoEdStatus)err;
  163. return;
  164. }
  165. }
  166. tt_free( oldDefaultFile );
  167. //
  168. // Ask to join the file.
  169. //
  170. Tt_message msg = tt_prequest_create( TT_FILE, "Text_File_Join" );
  171. Tt_status ttErr = tt_ptr_error( msg );
  172. if (ttErr != TT_OK) {
  173. fprintf( stderr, "libCoEd: tt_prequest_create(): %s\n",
  174. tt_status_message( ttErr ));
  175. status = (CoEdStatus)ttErr;
  176. return;
  177. }
  178. //
  179. // Set the file of the message.
  180. //
  181. ttErr = tt_message_file_set( msg, _path );
  182. if (ttErr != TT_OK) {
  183. fprintf( stderr, "libCoEd: tt_message_file_set(): %s\n",
  184. tt_status_message( ttErr ));
  185. status = (CoEdStatus)ttErr;
  186. return;
  187. }
  188. //
  189. // Send the message.
  190. //
  191. ttErr = tt_message_send( msg );
  192. if (ttErr != TT_OK) {
  193. fprintf( stderr, "libCoEd: tt_message_send(): %s\n",
  194. tt_status_message( ttErr ));
  195. status = (CoEdStatus)ttErr;
  196. return;
  197. }
  198. //
  199. // Add ourselves to the list of files joined.
  200. //
  201. coEdFiles->append( this );
  202. //
  203. // Wait for the reply.
  204. //
  205. status = CoEdOK;
  206. time_t start = time(0);
  207. struct rlimit nofile;
  208. getrlimit( RLIMIT_NOFILE, &nofile );
  209. struct pollfd fds[ 1 ];
  210. fds[ 0 ].fd = coEdTtFd;
  211. fds[ 0 ].events = POLLIN;
  212. while (_joining && (status == CoEdOK)) {
  213. if ((timeOutSec > 0) && (time(0) - start > timeOutSec)) {
  214. status = CoEdWarnTimeout;
  215. break;
  216. }
  217. int activeFDs = poll( fds, 1, (1000*timeOutSec) );
  218. if (activeFDs > 0) {
  219. if (fds[ 0 ].revents & POLLIN) {
  220. status = coEdHandleActiveFD( coEdTtFd );
  221. }
  222. } else if (activeFDs == 0) {
  223. status = CoEdWarnTimeout;
  224. } else {
  225. perror( "libCoEd" );
  226. status = CoEdErrFailure;
  227. }
  228. }
  229. }
  230. CoEdFile::
  231. ~CoEdFile()
  232. {
  233. // XXX unjoin from the file, remove from coEdFiles
  234. if (_appliedChanges != 0) {
  235. delete _appliedChanges;
  236. }
  237. if (_unAppliedChanges != 0) {
  238. delete _unAppliedChanges;
  239. }
  240. if (_version != 0) {
  241. delete _version;
  242. }
  243. if (_versionInQ != 0) {
  244. delete _versionInQ;
  245. }
  246. if (_path != 0) {
  247. free( _path );
  248. }
  249. }
  250. CoEdStatus CoEdFile::
  251. insertText( long start, long end, const char *text )
  252. {
  253. CoEdTextChange *change;
  254. _numLocalChanges++;
  255. change = new CoEdTextChange( start, end, text, _version, coEdSiteID,
  256. _numLocalChanges );
  257. _version ->update( *coEdSiteID, _numLocalChanges );
  258. _versionInQ->update( *coEdSiteID, _numLocalChanges );
  259. if (change == 0) {
  260. return CoEdErrNoMem;
  261. }
  262. _appliedChanges->insert( change );
  263. return change->broadcast( _path );
  264. }
  265. CoEdStatus CoEdFile::
  266. _handleMsg( Tt_message msg )
  267. {
  268. Tt_class theClass = tt_message_class( msg );
  269. Tt_status err = tt_int_error( theClass );
  270. if (err != TT_OK) {
  271. fprintf( stderr, "libCoEd: tt_message_class(): %s\n",
  272. tt_status_message( err ));
  273. return (CoEdStatus)err;
  274. }
  275. switch (theClass) {
  276. case TT_REQUEST:
  277. return _handleRequest( msg );
  278. case TT_NOTICE:
  279. return _handleNotice( msg );
  280. default:
  281. fprintf( stderr, "libCoEd: bad Tt_class!\n" );
  282. return CoEdOK;
  283. }
  284. }
  285. CoEdStatus CoEdFile::
  286. _handleRequest( Tt_message msg )
  287. {
  288. CoEdStatus val2Return;
  289. char *op = tt_message_op( msg );
  290. Tt_status err = tt_ptr_error( op );
  291. if (err != TT_OK) {
  292. fprintf( stderr, "libCoEd: tt_message_op(): %s\n",
  293. tt_status_message( err ));
  294. val2Return = (CoEdStatus)err;
  295. }
  296. if (op == 0) {
  297. fprintf( stderr, "libCoEd: msg has null op!\n" );
  298. val2Return = CoEdErrBadMsg;
  299. }
  300. if (! strcmp( op, "Text_File_Join" )) {
  301. val2Return = _handleJoin( msg );
  302. } else if (! strcmp( op, "Text_File_Version_Vote" )) {
  303. //val2Return = _handleVersionVote( msg );
  304. } else {
  305. fprintf( stderr, "libCoEd: unknown msg op \"%s\"\n", op );
  306. val2Return = CoEdErrBadMsg;
  307. }
  308. return val2Return;
  309. }
  310. CoEdStatus CoEdFile::
  311. _handleJoin( Tt_message msg )
  312. {
  313. Tt_status ttErr;
  314. Tt_state state = tt_message_state( msg );
  315. ttErr = tt_int_error( state );
  316. if (ttErr != TT_OK) {
  317. fprintf( stderr, "libCoEd: tt_message_state(): %s\n",
  318. tt_status_message( ttErr ));
  319. return (CoEdStatus)ttErr;
  320. }
  321. switch (state) {
  322. char *sender;
  323. Tt_status ttErr;
  324. case TT_FAILED:
  325. //
  326. // Nobody handled our request, so we must be the
  327. // first process to have joined the file.
  328. //
  329. _joining = 0;
  330. return CoEdOK;
  331. case TT_SENT:
  332. //
  333. // If the Text_File_Join request was sent by us, we
  334. // don't care about it.
  335. //
  336. sender = tt_message_sender( msg );
  337. ttErr = tt_ptr_error( sender );
  338. if (ttErr != TT_OK) {
  339. fprintf( stderr, "libCoEd: tt_message_sender(): %s\n",
  340. tt_status_message( ttErr ));
  341. return (CoEdStatus)ttErr;
  342. }
  343. if (sender == 0) {
  344. return CoEdErrBadMsg;
  345. }
  346. if (! strcmp( sender, coEdProcID )) {
  347. tt_free( sender );
  348. //
  349. // The request was made by us, so we reject it, in
  350. // order to give someone in the know a chance to
  351. // handle it.
  352. //
  353. ttErr = tt_message_reject( msg );
  354. if (ttErr != TT_OK) {
  355. fprintf( stderr,
  356. "libCoEd: tt_message_reject(): %s\n",
  357. tt_status_message( ttErr ));
  358. return (CoEdStatus)ttErr;
  359. }
  360. return CoEdOK;
  361. }
  362. tt_free( sender );
  363. // XXX Quiesce the file, and ship 'em a copy.
  364. tt_message_fail( msg );
  365. return CoEdOK;
  366. default:
  367. fprintf( stderr, "msg state: %d!\n", (int)state );
  368. tt_message_reject( msg );
  369. break;
  370. }
  371. return CoEdOK;
  372. } /* CoEdFile::_handleJoin() */
  373. CoEdStatus CoEdFile::
  374. _handleNotice( Tt_message msg )
  375. {
  376. Tt_state state = tt_message_state( msg );
  377. Tt_status err = tt_int_error( state );
  378. if (err != TT_OK) {
  379. fprintf( stderr, "libCoEd: tt_message_state(): %s\n",
  380. tt_status_message( err ));
  381. return (CoEdStatus)err;
  382. }
  383. if (state != TT_SENT) {
  384. return CoEdOK;
  385. }
  386. CoEdStatus val2Return;
  387. char *op = tt_message_op( msg );
  388. err = tt_ptr_error( op );
  389. if (err != TT_OK) {
  390. fprintf( stderr, "libCoEd: tt_message_op(): %s\n",
  391. tt_status_message( err ));
  392. val2Return = (CoEdStatus)err;
  393. }
  394. if (op == 0) {
  395. fprintf( stderr, "libCoEd: msg has null op!\n" );
  396. val2Return = CoEdErrBadMsg;
  397. }
  398. if (! strcmp( op, "Text_File_Changed" )) {
  399. val2Return = _handleChanged( msg );
  400. } else if (! strcmp( op, "Text_File_Poll_Version" )) {
  401. val2Return = _handlePollVersion( msg );
  402. } else {
  403. fprintf( stderr, "libCoEd: unknown msg op \"%s\"\n", op );
  404. val2Return = CoEdErrBadMsg;
  405. }
  406. tt_message_destroy( msg );
  407. return val2Return;
  408. }
  409. CoEdStatus CoEdFile::
  410. _handleChanged( Tt_message msg )
  411. {
  412. if (_joining) {
  413. fprintf( stderr, "libCoEd: warning: got a change while "
  414. "joining \"%s\"\n", _path );
  415. return CoEdOK;
  416. }
  417. //
  418. // If the Text_File_Changed notice was sent by us, we don't care
  419. // about it.
  420. //
  421. char *sender = tt_message_sender( msg );
  422. Tt_status ttErr = tt_ptr_error( sender );
  423. if (ttErr != TT_OK) {
  424. fprintf( stderr, "libCoEd: tt_message_sender(): %s\n",
  425. tt_status_message( ttErr ));
  426. return (CoEdStatus)ttErr;
  427. }
  428. if (sender == 0) {
  429. return CoEdErrBadMsg;
  430. }
  431. if (! strcmp( sender, coEdProcID )) {
  432. tt_free( sender );
  433. return CoEdOK;
  434. }
  435. tt_free( sender );
  436. //
  437. // It was not sent by us. Process it.
  438. //
  439. CoEdStatus err;
  440. CoEdTextChange *change = new CoEdTextChange( msg, err );
  441. if (err != CoEdOK) {
  442. return CoEdOK;
  443. }
  444. return _handleChange( change );
  445. }
  446. CoEdStatus CoEdFile::
  447. _handlePollVersion( Tt_message msg )
  448. {
  449. //
  450. // If the Text_File_Changed notice was sent by us, we don't care
  451. // about it.
  452. //
  453. char *sender = tt_message_sender( msg );
  454. Tt_status ttErr = tt_ptr_error( sender );
  455. if (ttErr != TT_OK) {
  456. fprintf( stderr, "libCoEd: tt_message_sender(): %s\n",
  457. tt_status_message( ttErr ));
  458. return (CoEdStatus)ttErr;
  459. }
  460. if (sender == 0) {
  461. return CoEdErrBadMsg;
  462. }
  463. if (! strcmp( sender, coEdProcID )) {
  464. tt_free( sender );
  465. return CoEdOK;
  466. }
  467. tt_free( sender );
  468. //
  469. // It was not sent by us. Respond.
  470. //
  471. Tt_message response = tt_prequest_create( TT_FILE,
  472. "Text_File_Version_Vote" );
  473. ttErr = tt_ptr_error( response );
  474. if (ttErr != TT_OK) {
  475. fprintf( stderr, "libCoEd: tt_prequest_create(): %s\n",
  476. tt_status_message( ttErr ));
  477. return (CoEdStatus)ttErr;
  478. }
  479. //
  480. // Set the file of the response.
  481. //
  482. ttErr = tt_message_file_set( response, _path );
  483. if (ttErr != TT_OK) {
  484. fprintf( stderr, "libCoEd: tt_message_file_set(): %s\n",
  485. tt_status_message( ttErr ));
  486. return (CoEdStatus)ttErr;
  487. }
  488. //
  489. // Aim the response back at the sender
  490. //
  491. ttErr = tt_message_handler_set( response, sender );
  492. if (ttErr != TT_OK) {
  493. fprintf( stderr, "libCoEd: tt_message_handler_set(): %s\n",
  494. tt_status_message( ttErr ));
  495. return (CoEdStatus)ttErr;
  496. }
  497. //
  498. // Send the message.
  499. //
  500. ttErr = tt_message_send( response );
  501. if (ttErr != TT_OK) {
  502. fprintf( stderr, "libCoEd: tt_message_send(): %s\n",
  503. tt_status_message( ttErr ));
  504. return (CoEdStatus)ttErr;
  505. }
  506. //
  507. // We don't expect or care about a reply, so destroy the
  508. // message now.
  509. //
  510. return CoEdOK;
  511. }
  512. CoEdStatus CoEdFile::
  513. _handleChange( CoEdTextChange *change, int changeIsFromQueue )
  514. {
  515. if (change->knowsOfNewerChangesThan( *_version )) {
  516. if (changeIsFromQueue) {
  517. fprintf( stderr, "Re-queuing change!\n" );
  518. abort();
  519. }
  520. _unAppliedChanges->insert( change );
  521. _versionInQ->update( change->causer(), change->changeNum() );
  522. } else {
  523. _appliedChanges->insert( change );
  524. CoEdTextChange *translatedChange =
  525. _appliedChanges->translate( *change );
  526. if (translatedChange != 0) {
  527. CoEdStatus err;
  528. err = _textBuf->insertText( translatedChange->start(),
  529. translatedChange->end(),
  530. translatedChange->text() );
  531. if (err != CoEdOK) {
  532. fprintf( stderr, "libCoEd: CoEdTextBuffer::"
  533. "insertText(): %d! Failed change: ");
  534. translatedChange->print( stderr );
  535. }
  536. delete translatedChange;
  537. }
  538. _version->update( change->causer(), change->changeNum() );
  539. if (! changeIsFromQueue) {
  540. _versionInQ->update( change->causer(),
  541. change->changeNum() );
  542. }
  543. }
  544. CoEdTextChange *newlyEligibleChange =
  545. _unAppliedChanges->deQEligibleChng( *_version );
  546. if (newlyEligibleChange != 0) {
  547. return _handleChange( newlyEligibleChange, 1 );
  548. } else {
  549. return CoEdOK;
  550. }
  551. }
  552. CoEdFileList::
  553. CoEdFileList()
  554. {
  555. _head = 0;
  556. _tail = 0;
  557. _count = 0;
  558. }
  559. CoEdFileList::
  560. ~CoEdFileList()
  561. {
  562. CoEdFile *curr = _head;
  563. CoEdFile *prev;
  564. while (curr != 0) {
  565. prev = curr;
  566. curr = curr->_next;
  567. delete prev;
  568. }
  569. }
  570. void CoEdFileList::
  571. push( CoEdFile *file )
  572. {
  573. file->_next = _head;
  574. file->_prev = 0;
  575. if (_tail == 0) {
  576. _tail = file;
  577. } else {
  578. _head->_prev = file;
  579. }
  580. _head = file;
  581. _count++;
  582. }
  583. void CoEdFileList::
  584. append( CoEdFile *file )
  585. {
  586. file->_next = 0;
  587. file->_prev = _tail;
  588. if (_head == 0) {
  589. _head = file;
  590. } else {
  591. _tail->_next = file;
  592. }
  593. _tail = file;
  594. _count++;
  595. }
  596. CoEdStatus CoEdFileList::
  597. handleMsg( const char *path, Tt_message msg )
  598. {
  599. if (path == 0) {
  600. fprintf( stderr, "libCoEd: got msg for null file!\n" );
  601. return CoEdErrFile;
  602. }
  603. CoEdFile *curr = _head;
  604. while (curr != 0) {
  605. if ((curr->_path != 0) && (! strcmp( path, curr->_path))) {
  606. return curr->_handleMsg( msg );
  607. }
  608. curr = curr->_next;
  609. }
  610. fprintf( stderr, "libCoEd: \"%s\" is not a file being CoEdited.\n",
  611. path );
  612. return CoEdErrFile;
  613. }