1 // Misc low level 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 // routines callable by the editor
20 
GetEditorLocalVersion()21 const char *GetEditorLocalVersion()
22 // return a pointer to a constant string, that tells something about this local
23 // version of the editor (used by the "version" command)
24 // it is ok to return NULL
25 {
26 	return("X Windows");
27 }
28 
PointInRECT(INT32 x,INT32 y,EDITOR_RECT * rect)29 bool PointInRECT(INT32 x,INT32 y,EDITOR_RECT *rect)
30 // return true if x, and y fall within rect
31 {
32 	if((x>=rect->x)&&(y>=rect->y))
33 	{
34 		if((x<(rect->x+(int)rect->w))&&(y<(rect->y+(int)rect->h)))
35 		{
36 			return(true);
37 		}
38 	}
39 	return(false);
40 }
41 
GlobalOffset(Window xWindow,INT32 * outX,INT32 * outY)42 static void GlobalOffset(Window xWindow,INT32 *outX,INT32 *outY)
43 // return the global offset of xWindow
44 // NOTE: although this should always work, it is nasty
45 // X should provide a better way to do this
46 {
47 	XWindowAttributes
48 		attributes;
49 	Window
50 		current,
51 		root,
52 		parent,
53 		*children;
54 	unsigned int
55 		numChildren;
56 
57 	(*outX)=0;
58 	(*outY)=0;
59 	current=xWindow;
60 	root=RootWindow(xDisplay,xScreenNum);									// get the root
61 	while(current!=root)
62 	{
63 		XGetWindowAttributes(xDisplay,current,&attributes);
64 		(*outX)+=attributes.x;												// offset by parent
65 		(*outY)+=attributes.y;
66 		XQueryTree(xDisplay,current,&root,&parent,&children,&numChildren);	// this is an annoying way to get the parent, but it is all X allows
67 		if(children)
68 		{
69 			XFree((char *)children);
70 		}
71 		current=parent;														// step back to the parent
72 	}
73 }
74 
GlobalToLocal(Window xWindow,INT32 x,INT32 y,INT32 * outX,INT32 * outY)75 void GlobalToLocal(Window xWindow,INT32 x,INT32 y,INT32 *outX,INT32 *outY)
76 // passed an x and y at the root level of the screen that xWindow is on,
77 // convert the coordinates to those local to xWindow, and return them
78 {
79 	GlobalOffset(xWindow,outX,outY);
80 	(*outX)=x-(*outX);
81 	(*outY)=y-(*outY);
82 }
83 
LocalToGlobal(Window xWindow,INT32 x,INT32 y,INT32 * outX,INT32 * outY)84 void LocalToGlobal(Window xWindow,INT32 x,INT32 y,INT32 *outX,INT32 *outY)
85 // passed an x and y within xWindow, return the root equivalent
86 {
87 	GlobalOffset(xWindow,outX,outY);
88 	(*outX)+=x;
89 	(*outY)+=y;
90 }
91 
EditorBeep()92 void EditorBeep()
93 // make a noise so we can tell what is going on
94 {
95 	XBell(xDisplay,0);
96 }
97 
ReportMessage(const char * format,...)98 void ReportMessage(const char *format,...)
99 // report messages to the console
100 {
101 	va_list
102 		args;
103 	char
104 		tempString[256];
105 
106 	va_start(args,format);
107 	vsnprintf(tempString,sizeof(tempString),format,args);	// make the string
108 	va_end(args);
109 
110 	OkDialog(tempString);
111 }
112 
ShowBusy()113 void ShowBusy()
114 // the editor calls this when it is doing something that may take some
115 // time, it should show the user (by changing the pointer or something)
116 // this call is nestable
117 {
118 	if(!showingBusy)
119 	{
120 //		XRecolorCursor(xDisplay,caretCursor,&gray2,&gray2);
121 	}
122 	showingBusy++;
123 }
124 
ShowNotBusy()125 void ShowNotBusy()
126 // when the editor is again ready to accept user input, it will call this
127 // it should undo anything ShowBusy did
128 // every ShowBusy will be exactly balanced by a call to ShowNotBusy
129 {
130 	if(showingBusy)
131 	{
132 		showingBusy--;
133 	}
134 	if(!showingBusy)
135 	{
136 //		XRecolorCursor(xDisplay,caretCursor,&gray0,&gray3);
137 	}
138 }
139 
CreateWindowTitleFromPath(const char * absolutePath)140 char *CreateWindowTitleFromPath(const char *absolutePath)
141 // use absolute path to generate a string (which is returned) that
142 // should be placed in the title of a window
143 // the string should be disposed of using MDisposePtr
144 // if there is a problem, SetError, and return NULL
145 {
146 	int
147 		pathLength;
148 	int
149 		nameLength;
150 	char
151 		*returnString;
152 
153 	pathLength=strlen(absolutePath);
154 	if((returnString=(char *)MNewPtr(pathLength+5)))
155 	{
156 		nameLength=0;
157 		while(pathLength&&absolutePath[pathLength-1]!=PATH_SEP)	// move backwards until the first part of the path is encountered
158 		{
159 			nameLength++;
160 			pathLength--;
161 		}
162 		sprintf(returnString,"%.*s -- %.*s",nameLength,&(absolutePath[pathLength]),pathLength,&(absolutePath[0]));
163 		return(returnString);
164 	}
165 	return(NULL);
166 }
167 
realpath2(const char * path,char * outputPath)168 char *realpath2(const char *path,char *outputPath)
169 // This routine replaces the system call "realpath" since there
170 // still exist machines that do not implement it
171 // This function directly replaces "realpath"
172 // NOTE: if path is passed as a string of length 0, this
173 // will return the current directory (which is what the real
174 // realpath does)
175 {
176 
177 #define	MAX_LINK_DEPTH	256						// maximum number of links we will follow before giving up
178 
179 	int
180 		currentLinkPath;						// tells which linkBuffer to read into next time we readlink
181 	char
182 		linkBuffer[2][MAXPATHLEN];				// two buffers to read links into
183 	char
184 		*linkPath;								// pointer to current link buffer
185 	int
186 		linkDepth,								// number of times links have been traversed
187 		outputLength,							// current length of output buffer
188 		linkLength;								// length of link returned by readlink
189 	bool
190 		hadRelativeComponent;					// boolean to tell if ., or .. was seen
191 
192 	currentLinkPath=0;							// use buffer 0 next time
193 	linkDepth=0;								// so far, no links encountered
194 	outputLength=0;								// current length of output path
195 
196 	if(*path!=PATH_SEP)							// if path does not start with PATH_SEP it is relative to current directory (even if path[0]=='\0')
197 	{
198 		if(getcwd(outputPath,MAXPATHLEN))		// get absolute path up to this point
199 		{
200 			outputLength=strlen(outputPath);	// get the length of the path returned
201 			if((!outputLength)||outputPath[outputLength-1]!=PATH_SEP)	// make sure the end of the path has a PATH_SEP on it
202 			{
203 				outputPath[outputLength++]=PATH_SEP;	// drop it in
204 			}
205 		}
206 		else
207 		{
208 			errno=ENOENT;						// something went amiss with getcwd, so make up an error code, and leave
209 			return(NULL);
210 		}
211 	}
212 	else
213 	{
214 		outputPath[outputLength++]=PATH_SEP;	// path is absolute, so start it
215 		path++;									// eat the PATH_SEP in the input
216 	}
217 
218 	while(*path)								// get each component of the path, and expand
219 	{
220 		if(*path!=PATH_SEP)						// gobble up separators
221 		{
222 			hadRelativeComponent=false;			// assume no ., or ..
223 			if(path[0]=='.')					// see if possible relative path component
224 			{
225 				if(path[1]=='.')
226 				{
227 					if(!path[2]||path[2]==PATH_SEP)	// just got '..', so move back over it, and previous path component, if any
228 					{
229 						path+=2;				// skip over '..' of input
230 						if(outputLength>1)		// if at root, do not backup
231 						{
232 							outputLength--;		// step back over trailing PATH_SEP
233 							while(outputLength&&outputPath[outputLength-1]!=PATH_SEP)
234 							{
235 								outputLength--;
236 							}
237 						}
238 						hadRelativeComponent=true;	// tell rest of loop this was handled
239 					}
240 				}
241 				else
242 				{
243 					if(!path[1]||path[1]==PATH_SEP)	// just saw '.', which is redundant, so remove it
244 					{
245 						path++;						// skip over '.'
246 						hadRelativeComponent=true;	// tell rest of loop this was handled
247 					}
248 				}
249 			}
250 
251 			if(!hadRelativeComponent)				// if the component was not relative, copy it to the output, and check for links
252 			{
253 				while(*path&&*path!=PATH_SEP)		// copy the next pathname component
254 				{
255 					if(outputLength<MAXPATHLEN-1)	// check for overflow, leave room for 0 termination
256 					{
257 						outputPath[outputLength++]=*path++;	// copy the character
258 					}
259 					else
260 					{
261 						errno=ENAMETOOLONG;			// ran out of space during the copy, complain
262 						return(NULL);
263 					}
264 				}
265 
266 				outputPath[outputLength]='\0';		// terminate the path that is being created
267 				linkPath=linkBuffer[currentLinkPath];	// point to the spare link buffer
268 				if((linkLength=readlink(outputPath,linkPath,MAXPATHLEN-1))>=0)	// attempt to see what this is linked to, MAXPATHLEN-1 is to leave room for 0 terminator
269 				{
270 					if(linkDepth++<=MAX_LINK_DEPTH)	// make sure we have not exhausted max number of links
271 					{
272 						linkPath[linkLength]='\0';	// terminate the path returned by readlink
273 						if(linkPath[0]==PATH_SEP)	// see if the link is absolute or relative
274 						{
275 							outputLength=0;			// if the link is absolute, then the output is completely re-written, and scan continues from this point
276 						}
277 						else
278 						{
279 							while(outputLength&&outputPath[outputLength-1]!=PATH_SEP)	// the link is relative, so remove the link component from the output
280 							{
281 								outputLength--;
282 							}
283 							if(outputLength)		// step back over the path separator too
284 							{
285 								outputLength--;
286 							}
287 						}
288 						if(linkLength+strlen(path)<MAXPATHLEN)	// make sure the remainder of the path will fit into the buffer
289 						{
290 							strcat(linkPath,path);	// append what's left of the path to the output of readlink
291 							path=linkPath;			// call this the new path
292 							currentLinkPath^=1;		// swap link buffers
293 						}
294 						else
295 						{
296 							errno=ENAMETOOLONG;
297 							return(NULL);
298 						}
299 					}
300 					else
301 					{
302 						errno=ELOOP;				// too many links traversed, complain
303 						return(NULL);
304 					}
305 				}
306 				else
307 				{
308 					if(errno!=EINVAL)				// anything but EINVAL is fatal, EINVAL is ok, the file is just not a symbolic link
309 					{
310 						return(NULL);
311 					}
312 				}
313 				outputPath[outputLength++]=PATH_SEP;	// add separator to the output
314 			}
315 		}
316 		else
317 		{
318 			path++;									// skip extraneous path separators
319 		}
320 	}
321 
322 	if((outputLength>1)&&outputPath[outputLength-1]==PATH_SEP)	// realpath does not return the last PATH_SEP unless we're at the root
323 	{
324 		outputLength--;								// strip off final PATH_SEP
325 	}
326 	outputPath[outputLength]='\0';					// terminate the result
327 	return(outputPath);								// return it
328 }
329 
CreateAbsolutePath(const char * relativePath)330 char *CreateAbsolutePath(const char *relativePath)
331 // given a relative path, attempt to make it an absolute path
332 // NOTE: if the relativePath does not point to anything valid, or the absolute path buffer
333 // could not be allocated, SetError, and return NULL
334 // the returned buffer will be 0 terminated, and must be freed by calling
335 // MDisposePtr
336 // NOTE: the result of calling this for the same file MUST always be
337 // EXACTLY the same string (case sensitive). The editor may perform
338 // strcmp style operations on these.
339 {
340 	char
341 		resolvedPath[MAXPATHLEN];
342 	char
343 		*returnPath;
344 
345 	if(realpath2(relativePath,&(resolvedPath[0])))				// get real path if possible
346 	{
347 		if((returnPath=(char *)MNewPtr(strlen(&(resolvedPath[0]))+1)))	// create buffer to pass it back in
348 		{
349 			strcpy(returnPath,&(resolvedPath[0]));				// copy it into the buffer
350 			return(returnPath);
351 		}
352 	}
353 	else
354 	{
355 		SetStdCLibError();
356 	}
357 	return(NULL);
358 }
359 
360 #define	STARTUP_NAME		"e93lib/e93rc.tcl"					// name of the startup file
361 
ScriptExists(const char * scriptPath)362 static bool ScriptExists(const char *scriptPath)
363 // See if the script at script path exists, and is readable
364 // If so, return true, if not, return false
365 {
366 	int
367 		fileID;
368 
369 	if((fileID=open(scriptPath,O_RDONLY))>=0)
370 	{
371 		close(fileID);
372 		return(true);
373 	}
374 	return(false);
375 }
376 
LocateStartupScript(char * scriptPath)377 bool LocateStartupScript(char *scriptPath)
378 // Search for the editor's startup script, return a path
379 // to the script which is located.
380 // NOTE: scriptPath must point to at least MAXPATHLEN bytes
381 // If no script could be located, SetError and return false
382 {
383 	sprintf(scriptPath,"%s/lib/%s",PREFIX,STARTUP_NAME);
384 	if(ScriptExists(scriptPath))
385 	{
386 		return(true);
387 	}
388 	SetError("Failed to locate '%s'",scriptPath);
389 	return(false);
390 }
391 
OpenEditorReadFile(const char * path)392 EDITOR_FILE *OpenEditorReadFile(const char *path)
393 // open a document file for reading into the editor
394 // if there is an error, SetError will be called, and NULL returned
395 {
396 	EDITOR_FILE
397 		*editorFile;
398 
399 	if((editorFile=(EDITOR_FILE *)MNewPtr(sizeof(EDITOR_FILE))))
400 	{
401 		if((editorFile->fileHandle=open(path,O_RDONLY,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH))!=-1)
402 		{
403 			return(editorFile);
404 		}
405 		else
406 		{
407 			SetStdCLibError();						// set high level error information
408 		}
409 		MDisposePtr(editorFile);
410 	}
411 	return(NULL);
412 }
413 
OpenEditorWriteFile(const char * path)414 EDITOR_FILE *OpenEditorWriteFile(const char *path)
415 // open a document file for writing into by the editor
416 // if there is an error, SetError will be called, and NULL returned
417 {
418 	EDITOR_FILE
419 		*editorFile;
420 
421 	if((editorFile=(EDITOR_FILE *)MNewPtr(sizeof(EDITOR_FILE))))
422 	{
423 		if((editorFile->fileHandle=open(path,O_CREAT|O_TRUNC|O_WRONLY,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH))!=-1)
424 		{
425 			return(editorFile);
426 		}
427 		else
428 		{
429 			SetStdCLibError();						// set high level error information
430 		}
431 		MDisposePtr(editorFile);
432 	}
433 	return(NULL);
434 }
435 
ReadEditorFile(EDITOR_FILE * file,UINT8 * buffer,UINT32 bytesToRead,UINT32 * bytesRead)436 bool ReadEditorFile(EDITOR_FILE *file,UINT8 *buffer,UINT32 bytesToRead,UINT32 *bytesRead)
437 // read at most bytesToRead from file into buffer
438 // return the actual amount read in bytesRead
439 // if bytesRead is less than bytesToRead, then EOF is assumed
440 // if there is an error, SetError will be called, and false returned
441 {
442 	int
443 		bytesJustRead;
444 
445 	if((bytesJustRead=read(file->fileHandle,(char *)buffer,(int)bytesToRead))!=(int)bytesToRead)
446 	{
447 		if(bytesJustRead==-1)			// if there was an error, set it it
448 		{
449 			SetStdCLibError();			// set high level error information
450 			*bytesRead=0;
451 			return(false);
452 		}
453 	}
454 	*bytesRead=bytesJustRead;
455 	return(true);
456 }
457 
WriteEditorFile(EDITOR_FILE * file,UINT8 * buffer,UINT32 bytesToWrite,UINT32 * bytesWritten)458 bool WriteEditorFile(EDITOR_FILE *file,UINT8 *buffer,UINT32 bytesToWrite,UINT32 *bytesWritten)
459 // write bytesToWrite from buffer into file
460 // return the actual amount written in bytesWritten
461 // bytesWritten should be the same as bytesToWrite, unless there was an error
462 // if there is an error, SetError will be called, and false returned
463 {
464 	int
465 		bytesJustWritten;
466 
467 	if((bytesJustWritten=write(file->fileHandle,(char *)buffer,(int)bytesToWrite))!=(int)bytesToWrite)
468 	{
469 		if(bytesJustWritten==-1)			// if there was an error, set it it
470 		{
471 			SetStdCLibError();				// set high level error information
472 			*bytesWritten=0;
473 			return(false);
474 		}
475 	}
476 	*bytesWritten=bytesJustWritten;
477 	return(true);
478 }
479 
CloseEditorFile(EDITOR_FILE * file)480 void CloseEditorFile(EDITOR_FILE *file)
481 // when the editor is finished with a file, it will call this
482 {
483 	close(file->fileHandle);					// get rid of the file
484 	MDisposePtr(file);
485 }
486 
OutlineShadowRectangle(Window xWindow,EDITOR_RECT * rect,EDITOR_COLOR upperLeftColor,EDITOR_COLOR lowerRightColor,UINT32 thickness)487 void OutlineShadowRectangle(Window xWindow,EDITOR_RECT *rect,EDITOR_COLOR upperLeftColor,EDITOR_COLOR lowerRightColor,UINT32 thickness)
488 // outline rect with a shadow effect in thickness
489 {
490 	UINT32
491 		thicknessCount;
492 
493 	for(thicknessCount=0;thicknessCount<thickness&&thicknessCount<(rect->h/2);thicknessCount++)
494 	{
495 		XSetForeground(xDisplay,xGraphicsContext,EditorColorToXPixel(upperLeftColor));
496 		XDrawLine(xDisplay,xWindow,xGraphicsContext,rect->x,rect->y+thicknessCount,rect->x+rect->w-thicknessCount-1,rect->y+thicknessCount);	// top
497 		XDrawLine(xDisplay,xWindow,xGraphicsContext,rect->x+thicknessCount,rect->y,rect->x+thicknessCount,rect->y+rect->h-thicknessCount-1);	// left
498 
499 		XSetForeground(xDisplay,xGraphicsContext,EditorColorToXPixel(lowerRightColor));
500 		XDrawLine(xDisplay,xWindow,xGraphicsContext,rect->x+thicknessCount,rect->y+rect->h-thicknessCount-1,rect->x+rect->w-thicknessCount-1,rect->y+rect->h-thicknessCount-1); // bottom
501 		XDrawLine(xDisplay,xWindow,xGraphicsContext,rect->x+rect->w-thicknessCount-1,rect->y+thicknessCount,rect->x+rect->w-thicknessCount-1,rect->y+rect->h-thicknessCount-1); // right
502 	}
503 }
504