1 // Handle high level buffer stuff
2 // Copyright (C) 2000 Core Technologies.
3 
4 // This file is part of e93.
5 //
6 // e93 is free software; you can redistribute it and/or modify
7 // it under the terms of the e93 LICENSE AGREEMENT.
8 //
9 // e93 is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // e93 LICENSE AGREEMENT for more details.
13 //
14 // You should have received a copy of the e93 LICENSE AGREEMENT
15 // along with e93; see the file "LICENSE.TXT".
16 
17 #include	"includes.h"
18 
19 // Some handy notes about buffers:
20 // shellBusy
21 //   This is used exclusively by the the shell. It indicates to the
22 //   shell that it is already doing something with the buffer, and that it is
23 //   not allowed to affect it in any way (useful when doing search and replace,
24 //   where replace is a shell script... keeps script from being able to modify
25 //   the buffer that is in the middle of being modified by replace command)
26 //   To that end, the buffer should be checked for "busy" before the shell attempts
27 //   to change the text it contains, or change its selection.
28 //   It should be set to busy whenever the shell is about to do something to the
29 //   buffer that could run a Tcl script.
30 
31 static EDITOR_BUFFER
32 	*startBuffer;								// pointer to start of editor buffer list
33 
34 enum
35 {
36 	NOT_FROM_FILE,
37 	DUPLICATE_NAME
38 };
39 
40 static const char *errorDescriptions[]=
41 {
42 	"Buffer is not linked to a file",
43 	"Duplicate buffer names are not allowed"
44 };
45 
DisposeBuffer(EDITOR_BUFFER * buffer)46 static void DisposeBuffer(EDITOR_BUFFER *buffer)
47 // dispose of a buffer
48 {
49 	EDITOR_BUFFER
50 		*previousBuffer,
51 		*testBuffer;
52 	EDITOR_BUFFER_HANDLE
53 		*handle;
54 
55 	handle=buffer->bufferHandles;
56 	while(handle)											// for each handle to this buffer, unlink and clear it
57 	{
58 		handle->buffer=NULL;								// this buffer is gone...
59 		handle=handle->nextHandle;
60 	}
61 
62 	UnInitVariableBindingTable(&(buffer->variableTable));	// remove variables hanging off this buffer
63 	if(buffer->window)
64 	{
65 		EditorCloseDocumentWindow(buffer->window);			// close down the window if one exists
66 	}
67 	if(buffer->task)
68 	{
69 		DisconnectBufferTask(buffer);						// close down the connection to any task that may be attached to this buffer
70 	}
71 	if(buffer->syntaxInstance)								// if syntax instance has been assigned, remove it
72 	{
73 		CloseSyntaxInstance(buffer->syntaxInstance);
74 	}
75 	CloseStyleUniverse(buffer->styleUniverse);
76 	while(buffer->marks)
77 	{
78 		CloseEditorMark(buffer,buffer->marks);				// get rid of all marks on this buffer
79 	}
80 	CloseSelectionUniverse(buffer->auxSelectionUniverse);
81 	CloseSelectionUniverse(buffer->xorSelectionUniverse);
82 	CloseSelectionUniverse(buffer->selectionUniverse);
83 	CloseUndoUniverse(buffer->undoUniverse);
84 	CloseTextUniverse(buffer->textUniverse);
85 	MDisposePtr(buffer->contentName);
86 
87 	previousBuffer=NULL;
88 	testBuffer=startBuffer;
89 	while(testBuffer&&testBuffer!=buffer)
90 	{
91 		previousBuffer=testBuffer;
92 		testBuffer=testBuffer->nextBuffer;
93 	}
94 	if(testBuffer==buffer)									// make sure we found it before trying to unlink
95 	{
96 		if(previousBuffer)
97 		{
98 			previousBuffer->nextBuffer=buffer->nextBuffer;
99 		}
100 		else
101 		{
102 			startBuffer=buffer->nextBuffer;
103 		}
104 	}
105 
106 	MDisposePtr(buffer);
107 }
108 
CreateBuffer(const char * bufferName)109 static EDITOR_BUFFER *CreateBuffer(const char *bufferName)
110 // create a buffer (buffers contain both text and selection
111 // universes (which are created here))
112 // if there is a problem, set the error, and return NULL
113 {
114 	EDITOR_BUFFER
115 		*buffer;
116 
117 	if((buffer=(EDITOR_BUFFER *)MNewPtr(sizeof(EDITOR_BUFFER))))
118 	{
119 		buffer->bufferHandles=NULL;									// not being held
120 		buffer->shellBusy=false;									// set this to a known value
121 		buffer->fromFile=false;
122 
123 		if((buffer->contentName=(char *)MNewPtr(strlen(bufferName)+1)))
124 		{
125 			strcpy(buffer->contentName,bufferName);
126 			if((buffer->textUniverse=OpenTextUniverse()))			// create the text universe
127 			{
128 				if((buffer->undoUniverse=OpenUndoUniverse()))		// create the undo universe
129 				{
130 					if((buffer->selectionUniverse=OpenSelectionUniverse()))			// create the selection universe
131 					{
132 						if((buffer->xorSelectionUniverse=OpenSelectionUniverse()))		// create the XOR selection universe
133 						{
134 							if((buffer->auxSelectionUniverse=OpenSelectionUniverse()))	// create the aux selection universe (used for tasks, so they can write data into a place other than at the cursor)
135 							{
136 								buffer->marks=NULL;						// no marks on this buffer yet
137 								if((buffer->styleUniverse=OpenStyleUniverse()))			// create the style universe
138 								{
139 									buffer->syntaxInstance=NULL;		// no syntax highlight on this buffer yet
140 									buffer->task=NULL;
141 									buffer->taskBytes=0;
142 									buffer->window=NULL;
143 									buffer->firstView=NULL;				// no views onto this buffer yet
144 									InitVariableBindingTable(&(buffer->variableTable));	// initialize the variable table
145 
146 									buffer->replaceHaveRange=false;
147 									buffer->replaceStartOffset=0;
148 									buffer->replaceEndOffset=0;
149 									buffer->replaceNumBytes=0;
150 
151 									buffer->anchorStartPosition=0;		// clear selection handling variables
152 									buffer->anchorEndPosition=0;
153 									buffer->anchorLine=0;
154 									buffer->anchorX=0;
155 									buffer->columnarTopLine=0;
156 									buffer->columnarBottomLine=0;
157 									buffer->columnarLeftX=0;
158 									buffer->columnarRightX=0;
159 									buffer->haveStartX=false;
160 									buffer->haveEndX=false;
161 									buffer->desiredStartX=0;
162 									buffer->desiredEndX=0;
163 									buffer->haveCurrentEnd=false;
164 									buffer->currentIsStart=false;
165 
166 									buffer->nextBuffer=startBuffer;
167 									startBuffer=buffer;
168 									return(buffer);
169 								}
170 								CloseSelectionUniverse(buffer->auxSelectionUniverse);
171 							}
172 							CloseSelectionUniverse(buffer->xorSelectionUniverse);
173 						}
174 						CloseSelectionUniverse(buffer->selectionUniverse);
175 					}
176 					CloseUndoUniverse(buffer->undoUniverse);
177 				}
178 				CloseTextUniverse(buffer->textUniverse);
179 			}
180 			MDisposePtr(buffer->contentName);
181 		}
182 		MDisposePtr(buffer);
183 	}
184 	return(NULL);
185 }
186 
SetBufferBusy(EDITOR_BUFFER * buffer)187 void SetBufferBusy(EDITOR_BUFFER *buffer)
188 // set the buffer's busy state
189 {
190 	buffer->shellBusy=true;
191 }
192 
ClearBufferBusy(EDITOR_BUFFER * buffer)193 void ClearBufferBusy(EDITOR_BUFFER *buffer)
194 // clear the buffer's busy state
195 {
196 	buffer->shellBusy=false;
197 }
198 
BufferBusy(EDITOR_BUFFER * buffer)199 bool BufferBusy(EDITOR_BUFFER *buffer)
200 // check the given buffer's busy state
201 // if busy, return true, else false
202 {
203 	return(buffer->shellBusy);
204 }
205 
EditorGetFirstBuffer()206 EDITOR_BUFFER *EditorGetFirstBuffer()
207 // return the first buffer, or NULL if none
208 {
209 	return(startBuffer);
210 }
211 
EditorGetNextBuffer(EDITOR_BUFFER * buffer)212 EDITOR_BUFFER *EditorGetNextBuffer(EDITOR_BUFFER *buffer)
213 // return the next buffer, or NULL if none
214 {
215 	return(buffer->nextBuffer);
216 }
217 
LocateBuffer(const char * bufferName)218 EDITOR_BUFFER *LocateBuffer(const char *bufferName)
219 // search through the list of buffers, and attempt to locate
220 // one with the given name
221 // if one is found, return it, else return NULL
222 {
223 	bool
224 		found;
225 	EDITOR_BUFFER
226 		*testBuffer;
227 
228 	testBuffer=startBuffer;
229 	found=false;
230 	while(testBuffer&&!found)
231 	{
232 		if(strcmp(testBuffer->contentName,bufferName)==0)
233 		{
234 			found=true;
235 		}
236 		else
237 		{
238 			testBuffer=testBuffer->nextBuffer;
239 		}
240 	}
241 	return(testBuffer);
242 }
243 
EditorCloseBuffer(EDITOR_BUFFER * buffer)244 void EditorCloseBuffer(EDITOR_BUFFER *buffer)
245 // close the given edit buffer
246 // if the buffer has a task, it will be disconnected
247 // if the buffer has a window, it will be closed too
248 // this will close the window without saving, even if the text has been modified
249 {
250 	DisposeBuffer(buffer);
251 }
252 
EditorReleaseBufferHandle(EDITOR_BUFFER_HANDLE * handle)253 void EditorReleaseBufferHandle(EDITOR_BUFFER_HANDLE *handle)
254 // Remove handle from the buffer it was linked to
255 // NOTE: if handle is already released, this does nothing
256 {
257 	EDITOR_BUFFER_HANDLE
258 		*previousHandle,
259 		*currentHandle;
260 
261 	if(handle->buffer)			// make sure this handle is held
262 	{
263 		previousHandle=NULL;
264 		currentHandle=handle->buffer->bufferHandles;
265 		while(currentHandle&&currentHandle!=handle)
266 		{
267 			previousHandle=currentHandle;
268 			currentHandle=currentHandle->nextHandle;
269 		}
270 		if(currentHandle)
271 		{
272 			if(previousHandle)
273 			{
274 				previousHandle->nextHandle=currentHandle->nextHandle;
275 			}
276 			else
277 			{
278 				currentHandle->buffer->bufferHandles=currentHandle->nextHandle;
279 			}
280 		}
281 		handle->buffer=NULL;
282 	}
283 }
284 
EditorGrabBufferHandle(EDITOR_BUFFER_HANDLE * handle,EDITOR_BUFFER * buffer)285 void EditorGrabBufferHandle(EDITOR_BUFFER_HANDLE *handle,EDITOR_BUFFER *buffer)
286 // grab a handle to buffer
287 {
288 	handle->buffer=buffer;
289 	handle->nextHandle=buffer->bufferHandles;
290 	buffer->bufferHandles=handle;
291 }
292 
EditorGetBufferFromHandle(EDITOR_BUFFER_HANDLE * handle)293 EDITOR_BUFFER *EditorGetBufferFromHandle(EDITOR_BUFFER_HANDLE *handle)
294 // return the buffer that handle points to
295 {
296 	return(handle->buffer);
297 }
298 
EditorInitBufferHandle(EDITOR_BUFFER_HANDLE * handle)299 void EditorInitBufferHandle(EDITOR_BUFFER_HANDLE *handle)
300 // initialize a buffer handle so that it is in the released state
301 {
302 	handle->buffer=NULL;
303 }
304 
EditorRevertBuffer(EDITOR_BUFFER * buffer)305 bool EditorRevertBuffer(EDITOR_BUFFER *buffer)
306 // revert buffer, no questions asked
307 // if there is a problem, SetError, and return false
308 // NOTE: the window could be left with only part of the reverted text
309 // if there was a problem
310 {
311 	if(buffer->fromFile)
312 	{
313 		if(LoadBuffer(buffer,buffer->contentName))
314 		{
315 			return(true);
316 		}
317 	}
318 	else
319 	{
320 		if(ClearBuffer(buffer))
321 		{
322 			return(true);
323 		}
324 	}
325 	return(false);
326 }
327 
EditorSaveBufferTo(EDITOR_BUFFER * buffer,const char * path)328 bool EditorSaveBufferTo(EDITOR_BUFFER *buffer,const char *path)
329 // save the text of buffer into path
330 // NOTE: the text will be saved even if it is not dirty
331 // if there is a problem, SetError, and return false
332 // NOTE: the buffer "clean" point is not moved by this call
333 // NOTE: certain problems may cause the text to be only partially saved
334 // thus corrupting the file
335 {
336 	return(SaveBuffer(buffer,path,false));
337 }
338 
EditorSaveBufferAs(EDITOR_BUFFER * buffer,const char * path)339 bool EditorSaveBufferAs(EDITOR_BUFFER *buffer,const char *path)
340 // save the text of buffer into path, change buffer's name
341 // to the absolute version of path, and update its window if it has one
342 // NOTE: the text will be saved even if it is not dirty
343 // if there is a problem, SetError, and return false
344 // NOTE: certain problems may cause the text to be only partially saved
345 // thus corrupting the file
346 // NOTE: if after a save, the buffer's name becomes the same as that
347 // of another already existing buffer, the name change will fail, even though
348 // the contents of the buffer have been written into the file
349 // this is a little ugly, but all editors have to cope with this problem somehow.
350 {
351 	EDITOR_BUFFER
352 		*otherBuffer;
353 	char
354 		*absolutePath;
355 
356 	if(SaveBuffer(buffer,path,true))							// save the file using the regular path, mark it as clean
357 	{
358 		if((absolutePath=CreateAbsolutePath(path)))				// attempt to make an absolute path out of the one given (this could fail if someone deleted the file we just saved before we get here!)
359 		{
360 			if(!(otherBuffer=LocateBuffer(absolutePath))||(otherBuffer==buffer))	// see if there is another buffer down this path
361 			{
362 				MDisposePtr(buffer->contentName);				// wipe out the old content name
363 				buffer->contentName=absolutePath;				// point it at the new path
364 				buffer->fromFile=true;							// linked to file now
365 				EditorReassignDocumentWindowTitle(buffer);		// make new title to reflect new name
366 				return(true);
367 			}
368 			else
369 			{
370 				SetError(errorDescriptions[DUPLICATE_NAME]);
371 			}
372 			MDisposePtr(absolutePath);
373 		}
374 	}
375 	return(false);
376 }
377 
EditorSaveBuffer(EDITOR_BUFFER * buffer)378 bool EditorSaveBuffer(EDITOR_BUFFER *buffer)
379 // save the text of buffer into its file if it has one
380 // if it does not have a file, this will fail
381 {
382 	if(buffer->fromFile)
383 	{
384 		return(SaveBuffer(buffer,buffer->contentName,true));	// save buffer, mark it as clean
385 	}
386 	SetError(errorDescriptions[NOT_FROM_FILE]);
387 	return(false);
388 }
389 
EditorNewBuffer(const char * bufferName)390 EDITOR_BUFFER *EditorNewBuffer(const char *bufferName)
391 // create a new buffer with the given name
392 // NOTE: if there is already a buffer with the given name, SetError
393 // if there is a problem, SetError, return NULL
394 {
395 	if(!LocateBuffer(bufferName))
396 	{
397 		return(CreateBuffer(bufferName));
398 	}
399 	else
400 	{
401 		SetError(errorDescriptions[DUPLICATE_NAME]);
402 	}
403 	return(NULL);
404 }
405 
EditorOpenBuffer(const char * path)406 EDITOR_BUFFER *EditorOpenBuffer(const char *path)
407 // open an editor buffer to the file at path
408 // if possible, open it, and return the buffer
409 // if there is a problem, set the error, and return NULL
410 // NOTE: if path points to a buffer that is already open,
411 // then that buffer is simply returned
412 {
413 	EDITOR_BUFFER
414 		*buffer;
415 	char
416 		*absolutePath;
417 
418 	if((absolutePath=CreateAbsolutePath(path)))				// attempt to make an absolute path out of the one given
419 	{
420 		if((buffer=LocateBuffer(absolutePath)))
421 		{
422 			MDisposePtr(absolutePath);
423 			return(buffer);
424 		}
425 		else
426 		{
427 			if((buffer=CreateBuffer(absolutePath)))
428 			{
429 				if(LoadBuffer(buffer,absolutePath))
430 				{
431 					buffer->fromFile=true;
432 					MDisposePtr(absolutePath);
433 					return(buffer);
434 				}
435 				DisposeBuffer(buffer);
436 			}
437 		}
438 		MDisposePtr(absolutePath);
439 	}
440 	return(NULL);
441 }
442 
UnInitBuffers()443 void UnInitBuffers()
444 // undo whatever InitBuffers did
445 {
446 	while(startBuffer)
447 	{
448 		if(startBuffer->bufferHandles)
449 		{
450 			fprintf(stderr,"Buffer dying with handles\n");	// this is a bug because no one should have a handle to a buffer at this moment
451 		}
452 		EditorCloseBuffer(startBuffer);						// get rid of any buffers left lying about
453 	}
454 }
455 
InitBuffers()456 bool InitBuffers()
457 // initialize buffer handling
458 // if there is a problem, SetError, and return false
459 {
460 	startBuffer=NULL;										// no start buffer as of yet
461 	return(true);
462 }
463