- Change help version to 5.4DEV
[nedit.git] / source / server.c
blob570e89b7754204831a63dde8dd062727f8727fb0
1 static const char CVSID[] = "$Id: server.c,v 1.20 2002/10/29 15:49:21 edg Exp $";
2 /*******************************************************************************
3 * *
4 * server.c -- Nirvana Editor edit-server component *
5 * *
6 * Copyright (C) 1999 Mark Edel *
7 * *
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 *
11 * version. *
12 * *
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 *
16 * for more details. *
17 * *
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 *
21 * *
22 * Nirvana Text Editor *
23 * November, 1995 *
24 * *
25 * Written by Mark Edel *
26 * *
27 *******************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
33 #include "server.h"
34 #include "textBuf.h"
35 #include "nedit.h"
36 #include "window.h"
37 #include "file.h"
38 #include "selection.h"
39 #include "macro.h"
40 #include "menu.h"
41 #include "preferences.h"
42 #include "server_common.h"
43 #include "../util/fileUtils.h"
44 #include "../util/utils.h"
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <limits.h>
50 #ifdef VMS
51 #include <lib$routines.h>
52 #include ssdef
53 #include syidef
54 #include "../util/VMSparam.h"
55 #include "../util/VMSutils.h"
56 #else
57 #include <sys/types.h>
58 #include <sys/utsname.h>
59 #ifndef __MVS__
60 #include <sys/param.h>
61 #endif
62 #include <unistd.h>
63 #include <pwd.h>
64 #endif
66 #include <Xm/Xm.h>
68 #ifdef HAVE_DEBUG_H
69 #include "../debug.h"
70 #endif
73 static void processServerCommand(void);
74 static void cleanUpServerCommunication(void);
75 static void processServerCommandString(char *string);
76 static void getFileClosedProperty(WindowInfo *window);
78 static Atom ServerRequestAtom = 0;
79 static Atom ServerExistsAtom = 0;
82 ** Set up inter-client communication for NEdit server end, expected to be
83 ** called only once at startup time
85 void InitServerCommunication(void)
87 Window rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
89 /* Create the server property atoms on the current DISPLAY. */
90 CreateServerPropertyAtoms(GetPrefServerName(),
91 &ServerExistsAtom,
92 &ServerRequestAtom);
94 /* Create the server-exists property on the root window to tell clients
95 whether to try a request (otherwise clients would always have to
96 try and wait for their timeouts to expire) */
97 XChangeProperty(TheDisplay, rootWindow, ServerExistsAtom, XA_STRING, 8,
98 PropModeReplace, (unsigned char *)"True", 4);
100 /* Set up exit handler for cleaning up server-exists property */
101 atexit(cleanUpServerCommunication);
103 /* Pay attention to PropertyChangeNotify events on the root window */
104 XSelectInput(TheDisplay, rootWindow, PropertyChangeMask);
107 static void deleteProperty(Atom* atom)
109 if (!IsServer) {
110 *atom = None;
111 return;
114 if (*atom != None) {
115 XDeleteProperty(TheDisplay,
116 RootWindow(TheDisplay, DefaultScreen(TheDisplay)),
117 *atom);
118 *atom = None;
123 ** Exit handler. Removes server-exists property on root window
125 static void cleanUpServerCommunication(void)
127 WindowInfo *w;
129 /* Delete any per-file properties that still exist
130 * (and that server knows about)
132 for (w = WindowList; w; w = w->next) {
133 DeleteFileClosedProperty(w);
136 /* Delete any per-file properties that still exist
137 * (but that that server doesn't know about)
139 DeleteServerFileAtoms(GetPrefServerName(),
140 RootWindow(TheDisplay, DefaultScreen(TheDisplay)));
142 /* Delete the server-exists property from the root window (if it was
143 assigned) and don't let the process exit until the X server has
144 processed the delete request (otherwise it won't be done) */
145 deleteProperty(&ServerExistsAtom);
146 XSync(TheDisplay, False);
150 ** Special event loop for NEdit servers. Processes PropertyNotify events on
151 ** the root window (this would not be necessary if it were possible to
152 ** register an Xt event-handler for a window, rather than only a widget).
153 ** Invokes server routines when a server-request property appears,
154 ** re-establishes server-exists property when another server exits and
155 ** this server is still alive to take over.
157 void ServerMainLoop(XtAppContext context)
159 while (TRUE) {
160 XEvent event;
161 XtAppNextEvent(context, &event);
162 ServerDispatchEvent(&event);
166 static void processServerCommand(void)
168 Atom dummyAtom;
169 unsigned long nItems, dummyULong;
170 unsigned char *propValue;
171 int getFmt;
173 /* Get the value of the property, and delete it from the root window */
174 if (XGetWindowProperty(TheDisplay, RootWindow(TheDisplay,
175 DefaultScreen(TheDisplay)), ServerRequestAtom, 0, INT_MAX, True,
176 XA_STRING, &dummyAtom, &getFmt, &nItems, &dummyULong, &propValue)
177 != Success || getFmt != 8)
178 return;
180 /* Invoke the command line processor on the string to process the request */
181 processServerCommandString((char *)propValue);
182 XFree(propValue);
185 Boolean ServerDispatchEvent(XEvent *event)
187 if (IsServer) {
188 Window rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
189 if (event->xany.window == rootWindow && event->xany.type == PropertyNotify) {
190 const XPropertyEvent* e = &event->xproperty;
192 if (e->type == PropertyNotify && e->window == rootWindow) {
193 if (e->atom == ServerRequestAtom && e->state == PropertyNewValue)
194 processServerCommand();
195 else if (e->atom == ServerExistsAtom && e->state == PropertyDelete)
196 XChangeProperty(TheDisplay,
197 rootWindow,
198 ServerExistsAtom, XA_STRING,
199 8, PropModeReplace,
200 (unsigned char *)"True", 4);
204 return XtDispatchEvent(event);
207 /* Try to find existing 'FileOpen' property atom for path. */
208 static Atom findFileOpenProperty(const char* filename,
209 const char* pathname) {
210 char path[MAXPATHLEN];
211 Atom atom;
213 if (!IsServer) return(None);
215 strcpy(path, pathname);
216 strcat(path, filename);
217 atom = CreateServerFileOpenAtom(GetPrefServerName(), path);
218 return(atom);
221 /* Destroy the 'FileOpen' atom to inform nc that this file has
222 ** been opened.
224 static void deleteFileOpenProperty(WindowInfo *window)
226 if (window->filenameSet) {
227 Atom atom = findFileOpenProperty(window->filename, window->path);
228 deleteProperty(&atom);
232 static void deleteFileOpenProperty2(const char* filename,
233 const char* pathname)
235 Atom atom = findFileOpenProperty(filename, pathname);
236 deleteProperty(&atom);
241 /* Try to find existing 'FileClosed' property atom for path. */
242 static Atom findFileClosedProperty(const char* filename,
243 const char* pathname)
245 char path[MAXPATHLEN];
246 Atom atom;
248 if (!IsServer) return(None);
250 strcpy(path, pathname);
251 strcat(path, filename);
252 atom = CreateServerFileClosedAtom(GetPrefServerName(),
253 path,
254 True); /* don't create */
255 return(atom);
258 /* Get hold of the property to use when closing the file. */
259 static void getFileClosedProperty(WindowInfo *window)
261 if (window->filenameSet) {
262 window->fileClosedAtom = findFileClosedProperty(window->filename,
263 window->path);
267 /* Delete the 'FileClosed' atom to inform nc that this file has
268 ** been closed.
270 void DeleteFileClosedProperty(WindowInfo *window)
272 if (window->filenameSet) {
273 deleteProperty(&window->fileClosedAtom);
277 static void deleteFileClosedProperty2(const char* filename,
278 const char* pathname)
280 Atom atom = findFileClosedProperty(filename, pathname);
281 deleteProperty(&atom);
285 static void processServerCommandString(char *string)
287 char *fullname, filename[MAXPATHLEN], pathname[MAXPATHLEN];
288 char *doCommand, *geometry, *langMode, *inPtr;
289 int editFlags, stringLen = strlen(string);
290 int lineNum, createFlag, readFlag, iconicFlag;
291 int fileLen, doLen, lmLen, geomLen, charsRead, itemsRead;
292 WindowInfo *window;
294 /* If the command string is empty, put up an empty, Untitled window
295 (or just pop one up if it already exists) */
296 if (string[0] == '\0') {
297 for (window=WindowList; window!=NULL; window=window->next)
298 if (!window->filenameSet && !window->fileChanged)
299 break;
300 if (window == NULL) {
301 EditNewFile(NULL, False, NULL, NULL);
302 CheckCloseDim();
303 } else
304 XMapRaised(TheDisplay, XtWindow(window->shell));
305 return;
309 ** Loop over all of the files in the command list
311 inPtr = string;
312 while (TRUE) {
314 if (*inPtr == '\0')
315 break;
317 /* Read a server command from the input string. Header contains:
318 linenum createFlag fileLen doLen\n, followed by a filename and -do
319 command both followed by newlines. This bit of code reads the
320 header, and converts the newlines following the filename and do
321 command to nulls to terminate the filename and doCommand strings */
322 itemsRead = sscanf(inPtr, "%d %d %d %d %d %d %d %d%n", &lineNum,
323 &readFlag, &createFlag, &iconicFlag, &fileLen, &doLen,
324 &lmLen, &geomLen, &charsRead);
325 if (itemsRead != 8)
326 goto readError;
327 inPtr += charsRead + 1;
328 if (inPtr - string + fileLen > stringLen)
329 goto readError;
330 fullname = inPtr;
331 inPtr += fileLen;
332 *inPtr++ = '\0';
333 if (inPtr - string + doLen > stringLen)
334 goto readError;
335 doCommand = inPtr;
336 inPtr += doLen;
337 *inPtr++ = '\0';
338 if (inPtr - string + lmLen > stringLen)
339 goto readError;
340 langMode = inPtr;
341 inPtr += lmLen;
342 *inPtr++ = '\0';
343 if (inPtr - string + geomLen > stringLen)
344 goto readError;
345 geometry = inPtr;
346 inPtr += geomLen;
347 *inPtr++ = '\0';
349 /* An empty file name means: choose a random window for
350 executing the -do macro upon */
351 if (fileLen <= 0) {
352 if (*doCommand != '\0') {
353 WindowInfo *win = WindowList;
354 /* Starting a new command while another one is still running
355 in the same window is not possible (crashes). */
356 while (win != NULL && win->macroCmdData != NULL) {
357 win = win->next;
359 if (!win) {
360 XBell(TheDisplay, 0);
361 } else {
362 DoMacro(win, doCommand, "-do macro");
365 CheckCloseDim();
366 return;
369 /* Process the filename by looking for the files in an
370 existing window, or opening if they don't exist */
371 editFlags = (readFlag ? PREF_READ_ONLY : 0) | CREATE |
372 (createFlag ? SUPPRESS_CREATE_WARN : 0);
373 if (ParseFilename(fullname, filename, pathname) != 0) {
374 fprintf(stderr, "NEdit: invalid file name\n");
375 deleteFileClosedProperty2(filename, pathname);
376 return;
378 window = FindWindowWithFile(filename, pathname);
379 if (window == NULL)
380 window = EditExistingFile(WindowList, filename, pathname,
381 editFlags, geometry, iconicFlag, lmLen==0?NULL:langMode);
383 /* Do the actions requested (note DoMacro is last, since the do
384 command can do anything, including closing the window!) */
385 if (window != NULL) {
386 deleteFileOpenProperty(window);
387 getFileClosedProperty(window);
388 if (!iconicFlag)
389 XMapRaised(TheDisplay, XtWindow(window->shell));
390 if (lineNum > 0)
391 SelectNumberedLine(window, lineNum);
392 if (*doCommand != '\0') {
393 /* Starting a new command while another one is still running
394 in the same window is not possible (crashes). */
395 if (window->macroCmdData != NULL) {
396 XBell(TheDisplay, 0);
397 } else {
398 DoMacro(window, doCommand, "-do macro");
401 } else {
402 deleteFileOpenProperty2(filename, pathname);
403 deleteFileClosedProperty2(filename, pathname);
407 CheckCloseDim();
408 return;
410 readError:
411 fprintf(stderr, "NEdit: error processing server request\n");
412 return;