634 lines
16 KiB
C
634 lines
16 KiB
C
/*
|
|
* CDE - Common Desktop Environment
|
|
*
|
|
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
|
|
*
|
|
* These libraries and programs are free software; you can
|
|
* redistribute them and/or modify them under the terms of the GNU
|
|
* Lesser General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* These libraries and programs are distributed in the hope that
|
|
* they will be useful, but WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU Lesser General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with these libraries and programs; if not, write
|
|
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
* Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
//%% (c) Copyright 1993, 1994 Hewlett-Packard Company
|
|
//%% (c) Copyright 1993, 1994 International Business Machines Corp.
|
|
//%% (c) Copyright 1993, 1994 Sun Microsystems, Inc.
|
|
//%% (c) Copyright 1993, 1994 Novell, Inc.
|
|
//%% $XConsortium: CoEdFile.C /main/3 1995/10/20 17:06:45 rswiston $
|
|
/*
|
|
* CoEdFile.cc
|
|
*
|
|
* Copyright (c) 1991 by Sun Microsystems. All Rights Reserved.
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software
|
|
* and its documentation for any purpose is hereby granted without
|
|
* fee, provided that the above copyright notice appear in all copies
|
|
* and that both that copyright notice and this permission notice
|
|
* appear in supporting documentation, and that the names of Sun
|
|
* Microsystems and its subsidiaries not be used in advertising or
|
|
* publicity pertaining to distribution of the software without
|
|
* specific, written prior permission. Sun Microsystems and its
|
|
* subsidiaries make no representations about the suitability of this
|
|
* software for any purpose. It is provided "as is" without express
|
|
* or implied warranty.
|
|
*
|
|
* Sun Microsystems and its subsidiaries disclaim all warranties with
|
|
* regard to this software, including all implied warranties of
|
|
* merchantability and fitness. In no event shall Sun Microsystems or
|
|
* its subsidiaries be liable for any special, indirect or
|
|
* consequential damages or any damages whatsoever resulting from loss
|
|
* of use, data or profits, whether in an action of contract,
|
|
* negligence or other tortious action, arising out of or in
|
|
* connection with the use or performance of this software.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/param.h>
|
|
#include <stdlib.h>
|
|
#include <poll.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
#include "CoEdFile.h"
|
|
#include "CoEdGlobals.h"
|
|
#include "CoEdChangeHistory.h"
|
|
#include "CoEdChangeQueue.h"
|
|
#include "CoEdTextVersion.h"
|
|
|
|
#undef DEBUG
|
|
|
|
CoEdFile::
|
|
CoEdFile( const char *path, CoEdTextBuffer *textBuf, CoEdStatus &status,
|
|
int timeOutSec )
|
|
{
|
|
Tt_status err;
|
|
|
|
_next = 0;
|
|
_numLocalChanges = 0;
|
|
_joining = 1;
|
|
if (path == 0) {
|
|
fprintf( stderr, "libCoEd: can't join null file\n" );
|
|
status = CoEdErrFile;
|
|
return;
|
|
}
|
|
_appliedChanges = new CoEdChangeHistory;
|
|
if (_appliedChanges == 0) {
|
|
status = CoEdErrNoMem;
|
|
return;
|
|
}
|
|
_textBuf = textBuf;
|
|
if (_textBuf == 0) {
|
|
status = CoEdErrBadPointer;
|
|
return;
|
|
}
|
|
_unAppliedChanges = new CoEdChangeQueue;
|
|
if (_unAppliedChanges == 0) {
|
|
status = CoEdErrNoMem;
|
|
return;
|
|
}
|
|
_version = new CoEdTextVersion;
|
|
if (_version == 0) {
|
|
status = CoEdErrNoMem;
|
|
return;
|
|
}
|
|
_versionInQ = new CoEdTextVersion;
|
|
if (_versionInQ == 0) {
|
|
status = CoEdErrNoMem;
|
|
return;
|
|
}
|
|
_coEditors = new CoEdSiteIDList;
|
|
if (_coEditors == 0) {
|
|
status = CoEdErrNoMem;
|
|
return;
|
|
}
|
|
//
|
|
// Join the file.
|
|
//
|
|
err = tt_file_join( path );
|
|
if (err != TT_OK) {
|
|
fprintf( stderr, "libCoEd: %s: %s\n", path,
|
|
tt_status_message( err ));
|
|
status = (CoEdStatus)err;
|
|
return;
|
|
}
|
|
//
|
|
// Trick ToolTalk into translating the path into the canonical
|
|
// path that it will use to label messages about this file.
|
|
//
|
|
char *oldDefaultFile = tt_default_file();
|
|
err = tt_ptr_error( oldDefaultFile );
|
|
if (err != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_default_file(): %s\n",
|
|
tt_status_message( err ));
|
|
status = (CoEdStatus)err;
|
|
return;
|
|
}
|
|
err = tt_default_file_set( path );
|
|
if (err != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_default_file_set(\"%s\"): %s\n",
|
|
path, tt_status_message( err ));
|
|
status = (CoEdStatus)err;
|
|
return;
|
|
}
|
|
char *temp = tt_default_file();
|
|
err = tt_ptr_error( temp );
|
|
if (err != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_default_file(): %s\n",
|
|
tt_status_message( err ));
|
|
status = (CoEdStatus)err;
|
|
return;
|
|
}
|
|
_path = strdup( temp );
|
|
if (_path == 0) {
|
|
status = CoEdErrNoMem;
|
|
return;
|
|
}
|
|
tt_free( temp );
|
|
if (oldDefaultFile != 0) {
|
|
//
|
|
// Reset the default file to what it was.
|
|
//
|
|
Tt_status err = tt_default_file_set( oldDefaultFile );
|
|
if (err != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_default_file_set(\"%s\")"
|
|
": %s\n", oldDefaultFile,
|
|
tt_status_message( err ));
|
|
status = (CoEdStatus)err;
|
|
return;
|
|
}
|
|
}
|
|
tt_free( oldDefaultFile );
|
|
//
|
|
// Ask to join the file.
|
|
//
|
|
Tt_message msg = tt_prequest_create( TT_FILE, "Text_File_Join" );
|
|
Tt_status ttErr = tt_ptr_error( msg );
|
|
if (ttErr != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_prequest_create(): %s\n",
|
|
tt_status_message( ttErr ));
|
|
status = (CoEdStatus)ttErr;
|
|
return;
|
|
}
|
|
//
|
|
// Set the file of the message.
|
|
//
|
|
ttErr = tt_message_file_set( msg, _path );
|
|
if (ttErr != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_message_file_set(): %s\n",
|
|
tt_status_message( ttErr ));
|
|
status = (CoEdStatus)ttErr;
|
|
return;
|
|
}
|
|
//
|
|
// Send the message.
|
|
//
|
|
ttErr = tt_message_send( msg );
|
|
if (ttErr != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_message_send(): %s\n",
|
|
tt_status_message( ttErr ));
|
|
status = (CoEdStatus)ttErr;
|
|
return;
|
|
}
|
|
//
|
|
// Add ourselves to the list of files joined.
|
|
//
|
|
coEdFiles->append( this );
|
|
//
|
|
// Wait for the reply.
|
|
//
|
|
status = CoEdOK;
|
|
time_t start = time(0);
|
|
struct rlimit nofile;
|
|
getrlimit( RLIMIT_NOFILE, &nofile );
|
|
struct pollfd fds[ 1 ];
|
|
fds[ 0 ].fd = coEdTtFd;
|
|
fds[ 0 ].events = POLLIN;
|
|
while (_joining && (status == CoEdOK)) {
|
|
if ((timeOutSec > 0) && (time(0) - start > timeOutSec)) {
|
|
status = CoEdWarnTimeout;
|
|
break;
|
|
}
|
|
int activeFDs = poll( fds, 1, (1000*timeOutSec) );
|
|
if (activeFDs > 0) {
|
|
if (fds[ 0 ].revents & POLLIN) {
|
|
status = coEdHandleActiveFD( coEdTtFd );
|
|
}
|
|
} else if (activeFDs == 0) {
|
|
status = CoEdWarnTimeout;
|
|
} else {
|
|
perror( "libCoEd" );
|
|
status = CoEdErrFailure;
|
|
}
|
|
}
|
|
}
|
|
|
|
CoEdFile::
|
|
~CoEdFile()
|
|
{
|
|
// XXX unjoin from the file, remove from coEdFiles
|
|
if (_appliedChanges != 0) {
|
|
delete _appliedChanges;
|
|
}
|
|
if (_unAppliedChanges != 0) {
|
|
delete _unAppliedChanges;
|
|
}
|
|
if (_version != 0) {
|
|
delete _version;
|
|
}
|
|
if (_versionInQ != 0) {
|
|
delete _versionInQ;
|
|
}
|
|
if (_path != 0) {
|
|
free( _path );
|
|
}
|
|
}
|
|
|
|
CoEdStatus CoEdFile::
|
|
insertText( long start, long end, const char *text )
|
|
{
|
|
CoEdTextChange *change;
|
|
_numLocalChanges++;
|
|
change = new CoEdTextChange( start, end, text, _version, coEdSiteID,
|
|
_numLocalChanges );
|
|
_version ->update( *coEdSiteID, _numLocalChanges );
|
|
_versionInQ->update( *coEdSiteID, _numLocalChanges );
|
|
if (change == 0) {
|
|
return CoEdErrNoMem;
|
|
}
|
|
_appliedChanges->insert( change );
|
|
return change->broadcast( _path );
|
|
}
|
|
|
|
CoEdStatus CoEdFile::
|
|
_handleMsg( Tt_message msg )
|
|
{
|
|
Tt_class theClass = tt_message_class( msg );
|
|
Tt_status err = tt_int_error( theClass );
|
|
if (err != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_message_class(): %s\n",
|
|
tt_status_message( err ));
|
|
return (CoEdStatus)err;
|
|
}
|
|
switch (theClass) {
|
|
case TT_REQUEST:
|
|
return _handleRequest( msg );
|
|
case TT_NOTICE:
|
|
return _handleNotice( msg );
|
|
default:
|
|
fprintf( stderr, "libCoEd: bad Tt_class!\n" );
|
|
return CoEdOK;
|
|
}
|
|
}
|
|
|
|
CoEdStatus CoEdFile::
|
|
_handleRequest( Tt_message msg )
|
|
{
|
|
CoEdStatus val2Return;
|
|
char *op = tt_message_op( msg );
|
|
Tt_status err = tt_ptr_error( op );
|
|
if (err != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_message_op(): %s\n",
|
|
tt_status_message( err ));
|
|
val2Return = (CoEdStatus)err;
|
|
}
|
|
if (op == 0) {
|
|
fprintf( stderr, "libCoEd: msg has null op!\n" );
|
|
val2Return = CoEdErrBadMsg;
|
|
}
|
|
if (! strcmp( op, "Text_File_Join" )) {
|
|
val2Return = _handleJoin( msg );
|
|
} else if (! strcmp( op, "Text_File_Version_Vote" )) {
|
|
//val2Return = _handleVersionVote( msg );
|
|
} else {
|
|
fprintf( stderr, "libCoEd: unknown msg op \"%s\"\n", op );
|
|
val2Return = CoEdErrBadMsg;
|
|
}
|
|
return val2Return;
|
|
}
|
|
|
|
CoEdStatus CoEdFile::
|
|
_handleJoin( Tt_message msg )
|
|
{
|
|
Tt_status ttErr;
|
|
|
|
Tt_state state = tt_message_state( msg );
|
|
ttErr = tt_int_error( state );
|
|
if (ttErr != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_message_state(): %s\n",
|
|
tt_status_message( ttErr ));
|
|
return (CoEdStatus)ttErr;
|
|
}
|
|
switch (state) {
|
|
char *sender;
|
|
Tt_status ttErr;
|
|
case TT_FAILED:
|
|
//
|
|
// Nobody handled our request, so we must be the
|
|
// first process to have joined the file.
|
|
//
|
|
_joining = 0;
|
|
return CoEdOK;
|
|
case TT_SENT:
|
|
//
|
|
// If the Text_File_Join request was sent by us, we
|
|
// don't care about it.
|
|
//
|
|
sender = tt_message_sender( msg );
|
|
ttErr = tt_ptr_error( sender );
|
|
if (ttErr != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_message_sender(): %s\n",
|
|
tt_status_message( ttErr ));
|
|
return (CoEdStatus)ttErr;
|
|
}
|
|
if (sender == 0) {
|
|
return CoEdErrBadMsg;
|
|
}
|
|
if (! strcmp( sender, coEdProcID )) {
|
|
tt_free( sender );
|
|
//
|
|
// The request was made by us, so we reject it, in
|
|
// order to give someone in the know a chance to
|
|
// handle it.
|
|
//
|
|
ttErr = tt_message_reject( msg );
|
|
if (ttErr != TT_OK) {
|
|
fprintf( stderr,
|
|
"libCoEd: tt_message_reject(): %s\n",
|
|
tt_status_message( ttErr ));
|
|
return (CoEdStatus)ttErr;
|
|
}
|
|
return CoEdOK;
|
|
}
|
|
tt_free( sender );
|
|
// XXX Quiesce the file, and ship 'em a copy.
|
|
tt_message_fail( msg );
|
|
return CoEdOK;
|
|
default:
|
|
fprintf( stderr, "msg state: %d!\n", (int)state );
|
|
tt_message_reject( msg );
|
|
break;
|
|
}
|
|
return CoEdOK;
|
|
|
|
} /* CoEdFile::_handleJoin() */
|
|
|
|
CoEdStatus CoEdFile::
|
|
_handleNotice( Tt_message msg )
|
|
{
|
|
Tt_state state = tt_message_state( msg );
|
|
Tt_status err = tt_int_error( state );
|
|
if (err != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_message_state(): %s\n",
|
|
tt_status_message( err ));
|
|
return (CoEdStatus)err;
|
|
}
|
|
if (state != TT_SENT) {
|
|
return CoEdOK;
|
|
}
|
|
CoEdStatus val2Return;
|
|
char *op = tt_message_op( msg );
|
|
err = tt_ptr_error( op );
|
|
if (err != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_message_op(): %s\n",
|
|
tt_status_message( err ));
|
|
val2Return = (CoEdStatus)err;
|
|
}
|
|
if (op == 0) {
|
|
fprintf( stderr, "libCoEd: msg has null op!\n" );
|
|
val2Return = CoEdErrBadMsg;
|
|
}
|
|
if (! strcmp( op, "Text_File_Changed" )) {
|
|
val2Return = _handleChanged( msg );
|
|
} else if (! strcmp( op, "Text_File_Poll_Version" )) {
|
|
val2Return = _handlePollVersion( msg );
|
|
} else {
|
|
fprintf( stderr, "libCoEd: unknown msg op \"%s\"\n", op );
|
|
val2Return = CoEdErrBadMsg;
|
|
}
|
|
tt_message_destroy( msg );
|
|
return val2Return;
|
|
}
|
|
|
|
CoEdStatus CoEdFile::
|
|
_handleChanged( Tt_message msg )
|
|
{
|
|
if (_joining) {
|
|
fprintf( stderr, "libCoEd: warning: got a change while "
|
|
"joining \"%s\"\n", _path );
|
|
return CoEdOK;
|
|
}
|
|
//
|
|
// If the Text_File_Changed notice was sent by us, we don't care
|
|
// about it.
|
|
//
|
|
char *sender = tt_message_sender( msg );
|
|
Tt_status ttErr = tt_ptr_error( sender );
|
|
if (ttErr != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_message_sender(): %s\n",
|
|
tt_status_message( ttErr ));
|
|
return (CoEdStatus)ttErr;
|
|
}
|
|
if (sender == 0) {
|
|
return CoEdErrBadMsg;
|
|
}
|
|
if (! strcmp( sender, coEdProcID )) {
|
|
tt_free( sender );
|
|
return CoEdOK;
|
|
}
|
|
tt_free( sender );
|
|
//
|
|
// It was not sent by us. Process it.
|
|
//
|
|
CoEdStatus err;
|
|
CoEdTextChange *change = new CoEdTextChange( msg, err );
|
|
if (err != CoEdOK) {
|
|
return CoEdOK;
|
|
}
|
|
return _handleChange( change );
|
|
}
|
|
|
|
CoEdStatus CoEdFile::
|
|
_handlePollVersion( Tt_message msg )
|
|
{
|
|
//
|
|
// If the Text_File_Changed notice was sent by us, we don't care
|
|
// about it.
|
|
//
|
|
char *sender = tt_message_sender( msg );
|
|
Tt_status ttErr = tt_ptr_error( sender );
|
|
if (ttErr != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_message_sender(): %s\n",
|
|
tt_status_message( ttErr ));
|
|
return (CoEdStatus)ttErr;
|
|
}
|
|
if (sender == 0) {
|
|
return CoEdErrBadMsg;
|
|
}
|
|
if (! strcmp( sender, coEdProcID )) {
|
|
tt_free( sender );
|
|
return CoEdOK;
|
|
}
|
|
tt_free( sender );
|
|
//
|
|
// It was not sent by us. Respond.
|
|
//
|
|
Tt_message response = tt_prequest_create( TT_FILE,
|
|
"Text_File_Version_Vote" );
|
|
ttErr = tt_ptr_error( response );
|
|
if (ttErr != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_prequest_create(): %s\n",
|
|
tt_status_message( ttErr ));
|
|
return (CoEdStatus)ttErr;
|
|
}
|
|
//
|
|
// Set the file of the response.
|
|
//
|
|
ttErr = tt_message_file_set( response, _path );
|
|
if (ttErr != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_message_file_set(): %s\n",
|
|
tt_status_message( ttErr ));
|
|
return (CoEdStatus)ttErr;
|
|
}
|
|
//
|
|
// Aim the response back at the sender
|
|
//
|
|
ttErr = tt_message_handler_set( response, sender );
|
|
if (ttErr != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_message_handler_set(): %s\n",
|
|
tt_status_message( ttErr ));
|
|
return (CoEdStatus)ttErr;
|
|
}
|
|
//
|
|
// Send the message.
|
|
//
|
|
ttErr = tt_message_send( response );
|
|
if (ttErr != TT_OK) {
|
|
fprintf( stderr, "libCoEd: tt_message_send(): %s\n",
|
|
tt_status_message( ttErr ));
|
|
return (CoEdStatus)ttErr;
|
|
}
|
|
//
|
|
// We don't expect or care about a reply, so destroy the
|
|
// message now.
|
|
//
|
|
return CoEdOK;
|
|
}
|
|
|
|
CoEdStatus CoEdFile::
|
|
_handleChange( CoEdTextChange *change, int changeIsFromQueue )
|
|
{
|
|
if (change->knowsOfNewerChangesThan( *_version )) {
|
|
if (changeIsFromQueue) {
|
|
fprintf( stderr, "Re-queuing change!\n" );
|
|
abort();
|
|
}
|
|
_unAppliedChanges->insert( change );
|
|
_versionInQ->update( change->causer(), change->changeNum() );
|
|
} else {
|
|
_appliedChanges->insert( change );
|
|
CoEdTextChange *translatedChange =
|
|
_appliedChanges->translate( *change );
|
|
if (translatedChange != 0) {
|
|
CoEdStatus err;
|
|
err = _textBuf->insertText( translatedChange->start(),
|
|
translatedChange->end(),
|
|
translatedChange->text() );
|
|
if (err != CoEdOK) {
|
|
fprintf( stderr, "libCoEd: CoEdTextBuffer::"
|
|
"insertText(): %d! Failed change: ");
|
|
translatedChange->print( stderr );
|
|
}
|
|
delete translatedChange;
|
|
}
|
|
_version->update( change->causer(), change->changeNum() );
|
|
if (! changeIsFromQueue) {
|
|
_versionInQ->update( change->causer(),
|
|
change->changeNum() );
|
|
}
|
|
}
|
|
CoEdTextChange *newlyEligibleChange =
|
|
_unAppliedChanges->deQEligibleChng( *_version );
|
|
if (newlyEligibleChange != 0) {
|
|
return _handleChange( newlyEligibleChange, 1 );
|
|
} else {
|
|
return CoEdOK;
|
|
}
|
|
}
|
|
|
|
CoEdFileList::
|
|
CoEdFileList()
|
|
{
|
|
_head = 0;
|
|
_tail = 0;
|
|
_count = 0;
|
|
}
|
|
|
|
CoEdFileList::
|
|
~CoEdFileList()
|
|
{
|
|
CoEdFile *curr = _head;
|
|
CoEdFile *prev;
|
|
while (curr != 0) {
|
|
prev = curr;
|
|
curr = curr->_next;
|
|
delete prev;
|
|
}
|
|
}
|
|
|
|
void CoEdFileList::
|
|
push( CoEdFile *file )
|
|
{
|
|
file->_next = _head;
|
|
file->_prev = 0;
|
|
if (_tail == 0) {
|
|
_tail = file;
|
|
} else {
|
|
_head->_prev = file;
|
|
}
|
|
_head = file;
|
|
_count++;
|
|
}
|
|
|
|
void CoEdFileList::
|
|
append( CoEdFile *file )
|
|
{
|
|
file->_next = 0;
|
|
file->_prev = _tail;
|
|
if (_head == 0) {
|
|
_head = file;
|
|
} else {
|
|
_tail->_next = file;
|
|
}
|
|
_tail = file;
|
|
_count++;
|
|
}
|
|
|
|
CoEdStatus CoEdFileList::
|
|
handleMsg( const char *path, Tt_message msg )
|
|
{
|
|
if (path == 0) {
|
|
fprintf( stderr, "libCoEd: got msg for null file!\n" );
|
|
return CoEdErrFile;
|
|
}
|
|
CoEdFile *curr = _head;
|
|
while (curr != 0) {
|
|
if ((curr->_path != 0) && (! strcmp( path, curr->_path))) {
|
|
return curr->_handleMsg( msg );
|
|
}
|
|
curr = curr->_next;
|
|
}
|
|
fprintf( stderr, "libCoEd: \"%s\" is not a file being CoEdited.\n",
|
|
path );
|
|
return CoEdErrFile;
|
|
}
|