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