1 static const char CVSID
[] = "$Id: server.c,v 1.28 2004/04/14 09:44:00 edg Exp $";
2 /*******************************************************************************
4 * server.c -- Nirvana Editor edit-server component *
6 * Copyright (C) 1999 Mark Edel *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
20 * Place, Suite 330, Boston, MA 02111-1307 USA *
22 * Nirvana Text Editor *
25 * Written by Mark Edel *
27 *******************************************************************************/
30 #include "../config.h"
38 #include "selection.h"
41 #include "preferences.h"
42 #include "server_common.h"
43 #include "../util/fileUtils.h"
44 #include "../util/utils.h"
45 #include "../util/misc.h"
52 #include <lib$routines.h>
55 #include "../util/VMSparam.h"
56 #include "../util/VMSutils.h"
58 #include <sys/types.h>
59 #include <sys/utsname.h>
61 #include <sys/param.h>
75 static void processServerCommand(void);
76 static void cleanUpServerCommunication(void);
77 static void processServerCommandString(char *string
);
78 static void getFileClosedProperty(WindowInfo
*window
);
80 static Atom ServerRequestAtom
= 0;
81 static Atom ServerExistsAtom
= 0;
84 ** Set up inter-client communication for NEdit server end, expected to be
85 ** called only once at startup time
87 void InitServerCommunication(void)
89 Window rootWindow
= RootWindow(TheDisplay
, DefaultScreen(TheDisplay
));
91 /* Create the server property atoms on the current DISPLAY. */
92 CreateServerPropertyAtoms(GetPrefServerName(),
96 /* Create the server-exists property on the root window to tell clients
97 whether to try a request (otherwise clients would always have to
98 try and wait for their timeouts to expire) */
99 XChangeProperty(TheDisplay
, rootWindow
, ServerExistsAtom
, XA_STRING
, 8,
100 PropModeReplace
, (unsigned char *)"True", 4);
102 /* Set up exit handler for cleaning up server-exists property */
103 atexit(cleanUpServerCommunication
);
105 /* Pay attention to PropertyChangeNotify events on the root window */
106 XSelectInput(TheDisplay
, rootWindow
, PropertyChangeMask
);
109 static void deleteProperty(Atom
* atom
)
117 XDeleteProperty(TheDisplay
,
118 RootWindow(TheDisplay
, DefaultScreen(TheDisplay
)),
125 ** Exit handler. Removes server-exists property on root window
127 static void cleanUpServerCommunication(void)
131 /* Delete any per-file properties that still exist
132 * (and that server knows about)
134 for (w
= WindowList
; w
; w
= w
->next
) {
135 DeleteFileClosedProperty(w
);
138 /* Delete any per-file properties that still exist
139 * (but that that server doesn't know about)
141 DeleteServerFileAtoms(GetPrefServerName(),
142 RootWindow(TheDisplay
, DefaultScreen(TheDisplay
)));
144 /* Delete the server-exists property from the root window (if it was
145 assigned) and don't let the process exit until the X server has
146 processed the delete request (otherwise it won't be done) */
147 deleteProperty(&ServerExistsAtom
);
148 XSync(TheDisplay
, False
);
152 ** Special event loop for NEdit servers. Processes PropertyNotify events on
153 ** the root window (this would not be necessary if it were possible to
154 ** register an Xt event-handler for a window, rather than only a widget).
155 ** Invokes server routines when a server-request property appears,
156 ** re-establishes server-exists property when another server exits and
157 ** this server is still alive to take over.
159 void ServerMainLoop(XtAppContext context
)
163 XtAppNextEvent(context
, &event
);
164 ServerDispatchEvent(&event
);
168 static void processServerCommand(void)
171 unsigned long nItems
, dummyULong
;
172 unsigned char *propValue
;
175 /* Get the value of the property, and delete it from the root window */
176 if (XGetWindowProperty(TheDisplay
, RootWindow(TheDisplay
,
177 DefaultScreen(TheDisplay
)), ServerRequestAtom
, 0, INT_MAX
, True
,
178 XA_STRING
, &dummyAtom
, &getFmt
, &nItems
, &dummyULong
, &propValue
)
179 != Success
|| getFmt
!= 8)
182 /* Invoke the command line processor on the string to process the request */
183 processServerCommandString((char *)propValue
);
187 Boolean
ServerDispatchEvent(XEvent
*event
)
190 Window rootWindow
= RootWindow(TheDisplay
, DefaultScreen(TheDisplay
));
191 if (event
->xany
.window
== rootWindow
&& event
->xany
.type
== PropertyNotify
) {
192 const XPropertyEvent
* e
= &event
->xproperty
;
194 if (e
->type
== PropertyNotify
&& e
->window
== rootWindow
) {
195 if (e
->atom
== ServerRequestAtom
&& e
->state
== PropertyNewValue
)
196 processServerCommand();
197 else if (e
->atom
== ServerExistsAtom
&& e
->state
== PropertyDelete
)
198 XChangeProperty(TheDisplay
,
200 ServerExistsAtom
, XA_STRING
,
202 (unsigned char *)"True", 4);
206 return XtDispatchEvent(event
);
209 /* Try to find existing 'FileOpen' property atom for path. */
210 static Atom
findFileOpenProperty(const char* filename
,
211 const char* pathname
) {
212 char path
[MAXPATHLEN
];
215 if (!IsServer
) return(None
);
217 strcpy(path
, pathname
);
218 strcat(path
, filename
);
219 atom
= CreateServerFileOpenAtom(GetPrefServerName(), path
);
223 /* Destroy the 'FileOpen' atom to inform nc that this file has
226 static void deleteFileOpenProperty(WindowInfo
*window
)
228 if (window
->filenameSet
) {
229 Atom atom
= findFileOpenProperty(window
->filename
, window
->path
);
230 deleteProperty(&atom
);
234 static void deleteFileOpenProperty2(const char* filename
,
235 const char* pathname
)
237 Atom atom
= findFileOpenProperty(filename
, pathname
);
238 deleteProperty(&atom
);
243 /* Try to find existing 'FileClosed' property atom for path. */
244 static Atom
findFileClosedProperty(const char* filename
,
245 const char* pathname
)
247 char path
[MAXPATHLEN
];
250 if (!IsServer
) return(None
);
252 strcpy(path
, pathname
);
253 strcat(path
, filename
);
254 atom
= CreateServerFileClosedAtom(GetPrefServerName(),
256 True
); /* don't create */
260 /* Get hold of the property to use when closing the file. */
261 static void getFileClosedProperty(WindowInfo
*window
)
263 if (window
->filenameSet
) {
264 window
->fileClosedAtom
= findFileClosedProperty(window
->filename
,
269 /* Delete the 'FileClosed' atom to inform nc that this file has
272 void DeleteFileClosedProperty(WindowInfo
*window
)
274 if (window
->filenameSet
) {
275 deleteProperty(&window
->fileClosedAtom
);
279 static void deleteFileClosedProperty2(const char* filename
,
280 const char* pathname
)
282 Atom atom
= findFileClosedProperty(filename
, pathname
);
283 deleteProperty(&atom
);
287 static void processServerCommandString(char *string
)
289 char *fullname
, filename
[MAXPATHLEN
], pathname
[MAXPATHLEN
];
290 char *doCommand
, *geometry
, *langMode
, *inPtr
;
291 int editFlags
, stringLen
= strlen(string
);
292 int lineNum
, createFlag
, readFlag
, iconicFlag
, lastIconic
= 0, tabbed
;
293 int fileLen
, doLen
, lmLen
, geomLen
, charsRead
, itemsRead
;
294 WindowInfo
*window
, *lastFile
= NULL
;
296 /* If the command string is empty, put up an empty, Untitled window
297 (or just pop one up if it already exists) */
298 if (string
[0] == '\0') {
299 for (window
=WindowList
; window
!=NULL
; window
=window
->next
)
300 if (!window
->filenameSet
&& !window
->fileChanged
)
302 if (window
== NULL
) {
303 EditNewFile(WindowList
, NULL
, False
, NULL
, NULL
);
307 RaiseDocument(window
);
308 XMapRaised(TheDisplay
, XtWindow(window
->shell
));
314 ** Loop over all of the files in the command list
322 /* Read a server command from the input string. Header contains:
323 linenum createFlag fileLen doLen\n, followed by a filename and -do
324 command both followed by newlines. This bit of code reads the
325 header, and converts the newlines following the filename and do
326 command to nulls to terminate the filename and doCommand strings */
327 itemsRead
= sscanf(inPtr
, "%d %d %d %d %d %d %d %d %d%n", &lineNum
,
328 &readFlag
, &createFlag
, &iconicFlag
, &tabbed
, &fileLen
,
329 &doLen
, &lmLen
, &geomLen
, &charsRead
);
332 inPtr
+= charsRead
+ 1;
333 if (inPtr
- string
+ fileLen
> stringLen
)
338 if (inPtr
- string
+ doLen
> stringLen
)
343 if (inPtr
- string
+ lmLen
> stringLen
)
348 if (inPtr
- string
+ geomLen
> stringLen
)
354 /* An empty file name means:
355 * put up an empty, Untitled window, or use an existing one
356 * choose a random window for executing the -do macro upon
359 for (window
=WindowList
; window
!=NULL
; window
=window
->next
)
360 if (!window
->filenameSet
&& !window
->fileChanged
)
363 if (*doCommand
== '\0') {
364 if (window
== NULL
) {
365 EditNewFile(tabbed
? WindowList
: NULL
, NULL
, iconicFlag
,
366 lmLen
==0?NULL
:langMode
, NULL
);
369 RaiseDocument(window
);
371 RaiseDocumentWindow(window
);
374 WindowInfo
*win
= WindowList
;
375 /* Starting a new command while another one is still running
376 in the same window is not possible (crashes). */
377 while (win
!= NULL
&& win
->macroCmdData
!= NULL
) {
382 XBell(TheDisplay
, 0);
384 /* Raise before -do (macro could close window). */
388 RaiseDocumentWindow(win
);
389 DoMacro(win
, doCommand
, "-do macro");
396 /* Process the filename by looking for the files in an
397 existing window, or opening if they don't exist */
398 editFlags
= (readFlag
? PREF_READ_ONLY
: 0) | CREATE
|
399 (createFlag
? SUPPRESS_CREATE_WARN
: 0);
400 if (ParseFilename(fullname
, filename
, pathname
) != 0) {
401 fprintf(stderr
, "NEdit: invalid file name\n");
402 deleteFileClosedProperty2(filename
, pathname
);
406 window
= FindWindowWithFile(filename
, pathname
);
407 if (window
== NULL
) {
408 /* Files are opened in background to improve opening speed
409 by defering certain time consuiming task such as syntax
410 highlighting. At the end of the file-opening loop, the
411 last file opened will be raised to restore those deferred
412 items. The current file may also be raised if there're
413 macros to execute on. */
414 window
= EditExistingFile(WindowList
,
415 filename
, pathname
, editFlags
, geometry
, iconicFlag
,
416 lmLen
== 0 ? NULL
: langMode
,
417 tabbed
== -1? GetPrefOpenInTab() : tabbed
, True
);
420 CleanUpTabBarExposeQueue(window
);
421 if (lastFile
&& window
->shell
!= lastFile
->shell
) {
422 CleanUpTabBarExposeQueue(lastFile
);
423 RaiseDocument(lastFile
);
429 /* Do the actions requested (note DoMacro is last, since the do
430 command can do anything, including closing the window!) */
431 if (window
!= NULL
) {
432 deleteFileOpenProperty(window
);
433 getFileClosedProperty(window
);
436 SelectNumberedLine(window
, lineNum
);
438 if (*doCommand
!= '\0') {
439 RaiseDocument(window
);
442 XMapRaised(TheDisplay
, XtWindow(window
->shell
));
444 /* Starting a new command while another one is still running
445 in the same window is not possible (crashes). */
446 if (window
->macroCmdData
!= NULL
) {
447 XBell(TheDisplay
, 0);
449 DoMacro(window
, doCommand
, "-do macro");
450 /* in case window is closed by macro functions
451 such as close() or detach_document() */
452 if (!IsValidWindow(window
))
454 if (lastFile
&& !IsValidWindow(lastFile
))
459 /* register the last file opened for later use */
462 lastIconic
= iconicFlag
;
465 deleteFileOpenProperty2(filename
, pathname
);
466 deleteFileClosedProperty2(filename
, pathname
);
470 /* Raise the last file opened */
472 CleanUpTabBarExposeQueue(lastFile
);
474 RaiseDocument(lastFile
);
476 RaiseDocumentWindow(lastFile
);
482 fprintf(stderr
, "NEdit: error processing server request\n");