Warning fix (Andrew Stanaski).
[nedit.git] / source / server.c
blob6cd3af19ae407b42c1e5249b4ea7af222b26ce62
1 static const char CVSID[] = "$Id: server.c,v 1.28 2004/04/14 09:44:00 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"
45 #include "../util/misc.h"
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <limits.h>
51 #ifdef VMS
52 #include <lib$routines.h>
53 #include ssdef
54 #include syidef
55 #include "../util/VMSparam.h"
56 #include "../util/VMSutils.h"
57 #else
58 #include <sys/types.h>
59 #include <sys/utsname.h>
60 #ifndef __MVS__
61 #include <sys/param.h>
62 #endif
63 #include <unistd.h>
64 #include <pwd.h>
65 #endif
67 #include <Xm/Xm.h>
68 #include <Xm/XmP.h>
70 #ifdef HAVE_DEBUG_H
71 #include "../debug.h"
72 #endif
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(),
93 &ServerExistsAtom,
94 &ServerRequestAtom);
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)
111 if (!IsServer) {
112 *atom = None;
113 return;
116 if (*atom != None) {
117 XDeleteProperty(TheDisplay,
118 RootWindow(TheDisplay, DefaultScreen(TheDisplay)),
119 *atom);
120 *atom = None;
125 ** Exit handler. Removes server-exists property on root window
127 static void cleanUpServerCommunication(void)
129 WindowInfo *w;
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)
161 while (TRUE) {
162 XEvent event;
163 XtAppNextEvent(context, &event);
164 ServerDispatchEvent(&event);
168 static void processServerCommand(void)
170 Atom dummyAtom;
171 unsigned long nItems, dummyULong;
172 unsigned char *propValue;
173 int getFmt;
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)
180 return;
182 /* Invoke the command line processor on the string to process the request */
183 processServerCommandString((char *)propValue);
184 XFree(propValue);
187 Boolean ServerDispatchEvent(XEvent *event)
189 if (IsServer) {
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,
199 rootWindow,
200 ServerExistsAtom, XA_STRING,
201 8, PropModeReplace,
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];
213 Atom atom;
215 if (!IsServer) return(None);
217 strcpy(path, pathname);
218 strcat(path, filename);
219 atom = CreateServerFileOpenAtom(GetPrefServerName(), path);
220 return(atom);
223 /* Destroy the 'FileOpen' atom to inform nc that this file has
224 ** been opened.
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];
248 Atom atom;
250 if (!IsServer) return(None);
252 strcpy(path, pathname);
253 strcat(path, filename);
254 atom = CreateServerFileClosedAtom(GetPrefServerName(),
255 path,
256 True); /* don't create */
257 return(atom);
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,
265 window->path);
269 /* Delete the 'FileClosed' atom to inform nc that this file has
270 ** been closed.
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)
301 break;
302 if (window == NULL) {
303 EditNewFile(WindowList, NULL, False, NULL, NULL);
304 CheckCloseDim();
306 else {
307 RaiseDocument(window);
308 XMapRaised(TheDisplay, XtWindow(window->shell));
310 return;
314 ** Loop over all of the files in the command list
316 inPtr = string;
317 while (TRUE) {
319 if (*inPtr == '\0')
320 break;
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);
330 if (itemsRead != 9)
331 goto readError;
332 inPtr += charsRead + 1;
333 if (inPtr - string + fileLen > stringLen)
334 goto readError;
335 fullname = inPtr;
336 inPtr += fileLen;
337 *inPtr++ = '\0';
338 if (inPtr - string + doLen > stringLen)
339 goto readError;
340 doCommand = inPtr;
341 inPtr += doLen;
342 *inPtr++ = '\0';
343 if (inPtr - string + lmLen > stringLen)
344 goto readError;
345 langMode = inPtr;
346 inPtr += lmLen;
347 *inPtr++ = '\0';
348 if (inPtr - string + geomLen > stringLen)
349 goto readError;
350 geometry = inPtr;
351 inPtr += geomLen;
352 *inPtr++ = '\0';
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
358 if (fileLen <= 0) {
359 for (window=WindowList; window!=NULL; window=window->next)
360 if (!window->filenameSet && !window->fileChanged)
361 break;
363 if (*doCommand == '\0') {
364 if (window == NULL) {
365 EditNewFile(tabbed? WindowList : NULL, NULL, iconicFlag,
366 lmLen==0?NULL:langMode, NULL);
367 } else {
368 if (iconicFlag)
369 RaiseDocument(window);
370 else
371 RaiseDocumentWindow(window);
373 } else {
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) {
378 win = win->next;
381 if (!win) {
382 XBell(TheDisplay, 0);
383 } else {
384 /* Raise before -do (macro could close window). */
385 if (iconicFlag)
386 RaiseDocument(win);
387 else
388 RaiseDocumentWindow(win);
389 DoMacro(win, doCommand, "-do macro");
392 CheckCloseDim();
393 return;
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);
403 break;
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);
419 if (window) {
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);
435 if (lineNum > 0)
436 SelectNumberedLine(window, lineNum);
438 if (*doCommand != '\0') {
439 RaiseDocument(window);
441 if (!iconicFlag)
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);
448 } else {
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))
453 window = NULL;
454 if (lastFile && !IsValidWindow(lastFile))
455 lastFile = NULL;
459 /* register the last file opened for later use */
460 if (window) {
461 lastFile = window;
462 lastIconic = iconicFlag;
464 } else {
465 deleteFileOpenProperty2(filename, pathname);
466 deleteFileClosedProperty2(filename, pathname);
470 /* Raise the last file opened */
471 if (lastFile) {
472 CleanUpTabBarExposeQueue(lastFile);
473 if (lastIconic)
474 RaiseDocument(lastFile);
475 else
476 RaiseDocumentWindow(lastFile);
477 CheckCloseDim();
479 return;
481 readError:
482 fprintf(stderr, "NEdit: error processing server request\n");
483 return;