Apply the "frame around text area" patch and associated geometry fixes. Looks
[nedit.git] / source / nc.c
blobac95be58a93ead47fc5652bf0067d9c21b7bf0d2
1 static const char CVSID[] = "$Id: nc.c,v 1.32 2002/12/10 13:16:16 edg Exp $";
2 /*******************************************************************************
3 * *
4 * nc.c -- Nirvana Editor client program for nedit server processes *
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_common.h"
34 #include "../util/fileUtils.h"
35 #include "../util/utils.h"
36 #include "../util/prefFile.h"
37 #include "../util/system.h"
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <limits.h>
42 #include <string.h>
43 #ifdef VMS
44 #include <lib$routines.h>
45 #include descrip
46 #include ssdef
47 #include syidef
48 #include "../util/VMSparam.h"
49 #include "../util/VMSutils.h"
50 #else
51 #ifndef __MVS__
52 #include <sys/param.h>
53 #endif
54 #include <sys/types.h>
55 #include <sys/utsname.h>
56 #include <unistd.h>
57 #include <pwd.h>
58 #include "../util/clearcase.h"
59 #endif /* VMS */
60 #ifdef __EMX__
61 #include <process.h>
62 #endif
64 #include <X11/Intrinsic.h>
65 #include <X11/Xatom.h>
67 #ifdef HAVE_DEBUG_H
68 #include "../debug.h"
69 #endif
71 #define APP_NAME "nc"
72 #define APP_CLASS "NEditClient"
74 #define PROPERTY_CHANGE_TIMEOUT (Preferences.timeOut * 1000) /* milliseconds */
75 #define SERVER_START_TIMEOUT (Preferences.timeOut * 3000) /* milliseconds */
76 #define REQUEST_TIMEOUT (Preferences.timeOut * 1000) /* milliseconds */
77 #define FILE_OPEN_TIMEOUT (Preferences.timeOut * 3000) /* milliseconds */
79 typedef struct
81 char* org;
82 char* string;
83 } CommandLine;
85 static void timeOutProc(Boolean *timeOutReturn, XtIntervalId *id);
86 static int startServer(const char *message, const char *commandLine);
87 static CommandLine processCommandLine(int argc, char** argv);
88 static char *parseCommandLine(int argc, char **argv);
89 static void nextArg(int argc, char **argv, int *argIndex);
90 static void printNcVersion(void);
91 static Boolean findExistingServer(XtAppContext context,
92 Window rootWindow,
93 Atom serverExistsAtom);
94 static void startNewServer(XtAppContext context,
95 Window rootWindow,
96 char* commandLine,
97 Atom serverExistsAtom);
98 static void waitUntilRequestProcessed(XtAppContext context,
99 Window rootWindow,
100 char* commandString,
101 Atom serverRequestAtom);
102 static void waitUntilFilesOpenedOrClosed(XtAppContext context,
103 Window rootWindow,
104 Atom serverExistsAtom);
106 Display *TheDisplay;
107 static Atom currentWaitForAtom;
108 static Atom noAtom = (Atom)(-1);
110 static const char cmdLineHelp[] =
111 #ifdef VMS
113 #else
114 "Usage: nc [-read] [-create] [-line n | +n] [-do command] [-ask] [-noask]\n\
115 [-svrname name] [-svrcmd command] [-lm languagemode]\n\
116 [-geometry geometry] [-iconic] [-timeout seconds] [-wait]\n\
117 [-V|-version] [--] [file...]\n";
118 #endif /*VMS*/
120 /* Structure to hold X Resource values */
121 static struct {
122 int autoStart;
123 char serverCmd[2*MAXPATHLEN]; /* holds executable name + flags */
124 char serverName[MAXPATHLEN];
125 int waitForClose;
126 int timeOut;
127 } Preferences;
129 /* Application resources */
130 static PrefDescripRec PrefDescrip[] = {
131 {"autoStart", "AutoStart", PREF_BOOLEAN, "True",
132 &Preferences.autoStart, NULL, True},
133 {"serverCommand", "ServerCommand", PREF_STRING, "nedit -server",
134 Preferences.serverCmd, (void *)sizeof(Preferences.serverCmd), False},
135 {"serverName", "serverName", PREF_STRING, "", Preferences.serverName,
136 (void *)sizeof(Preferences.serverName), False},
137 {"waitForClose", "WaitForClose", PREF_BOOLEAN, "False",
138 &Preferences.waitForClose, NULL, False},
139 {"timeOut", "TimeOut", PREF_INT, "10",
140 &Preferences.timeOut, NULL, False}
143 /* Resource related command line options */
144 static XrmOptionDescRec OpTable[] = {
145 {"-ask", ".autoStart", XrmoptionNoArg, (caddr_t)"False"},
146 {"-noask", ".autoStart", XrmoptionNoArg, (caddr_t)"True"},
147 {"-svrname", ".serverName", XrmoptionSepArg, (caddr_t)NULL},
148 {"-svrcmd", ".serverCommand", XrmoptionSepArg, (caddr_t)NULL},
149 {"-wait", ".waitForClose", XrmoptionNoArg, (caddr_t)"True"},
150 {"-timeout", ".timeOut", XrmoptionSepArg, (caddr_t)NULL}
153 /* Struct to hold info about files being opened and edited. */
154 typedef struct _FileListEntry {
155 Atom waitForFileOpenAtom;
156 Atom waitForFileClosedAtom;
157 char* path;
158 struct _FileListEntry *next;
159 } FileListEntry;
161 typedef struct {
162 int waitForOpenCount;
163 int waitForCloseCount;
164 FileListEntry* fileList;
165 } FileListHead;
166 static FileListHead fileListHead;
168 static void setPropertyValue(Atom atom) {
169 XChangeProperty(TheDisplay,
170 RootWindow(TheDisplay, DefaultScreen(TheDisplay)),
171 atom, XA_STRING,
172 8, PropModeReplace,
173 (unsigned char *)"True", 4);
176 /* Add another entry to the file entry list, if it doesn't exist yet. */
177 static void addToFileList(const char *path)
179 FileListEntry *item;
181 /* see if the file already exists in the list */
182 for (item = fileListHead.fileList; item; item = item->next) {
183 if (!strcmp(item->path, path))
184 break;
187 /* Add the atom to the head of the file list if it wasn't found. */
188 if (item == 0) {
189 item = malloc(sizeof(item[0]));
190 item->waitForFileOpenAtom = None;
191 item->waitForFileClosedAtom = None;
192 item->path = (char*)malloc(strlen(path)+1);
193 strcpy(item->path, path);
194 item->next = fileListHead.fileList;
195 fileListHead.fileList = item;
199 /* Creates the properties for the various paths */
200 static void createWaitProperties()
202 FileListEntry *item;
204 for (item = fileListHead.fileList; item; item = item->next) {
205 fileListHead.waitForOpenCount++;
206 item->waitForFileOpenAtom =
207 CreateServerFileOpenAtom(Preferences.serverName, item->path);
208 setPropertyValue(item->waitForFileOpenAtom);
210 if (Preferences.waitForClose == True) {
211 fileListHead.waitForCloseCount++;
212 item->waitForFileClosedAtom =
213 CreateServerFileClosedAtom(Preferences.serverName,
214 item->path,
215 False);
216 setPropertyValue(item->waitForFileClosedAtom);
221 int main(int argc, char **argv)
223 XtAppContext context;
224 Window rootWindow;
225 CommandLine commandLine;
226 Atom serverExistsAtom, serverRequestAtom;
227 XrmDatabase prefDB;
228 Boolean serverExists;
230 /* Initialize toolkit and get an application context */
231 XtToolkitInitialize();
232 context = XtCreateApplicationContext();
234 #ifdef VMS
235 /* Convert the command line to Unix style */
236 ConvertVMSCommandLine(&argc, &argv);
237 #endif /*VMS*/
238 #ifdef __EMX__
239 /* expand wildcards if necessary */
240 _wildcard(&argc, &argv);
241 #endif
243 /* Read the preferences command line into a database (note that we
244 don't support the .nc file anymore) */
245 prefDB = CreatePreferencesDatabase(NULL, APP_CLASS,
246 OpTable, XtNumber(OpTable), (unsigned *)&argc, argv);
248 /* Process the command line before calling XtOpenDisplay, because the
249 latter consumes certain command line arguments that we still need
250 (-icon, -geometry ...) */
251 commandLine = processCommandLine(argc, argv);
253 /* Open the display and find the root window */
254 TheDisplay = XtOpenDisplay (context, NULL, APP_NAME, APP_CLASS, NULL,
255 0, &argc, argv);
256 if (!TheDisplay) {
257 XtWarning ("nc: Can't open display\n");
258 exit(EXIT_FAILURE);
260 rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
262 /* Read the application resources into the Preferences data structure */
263 RestorePreferences(prefDB, XtDatabase(TheDisplay), APP_NAME,
264 APP_CLASS, PrefDescrip, XtNumber(PrefDescrip));
266 /* Make sure that the time out unit is at least 1 second and not too
267 large either (overflow!). */
268 if (Preferences.timeOut < 1) {
269 Preferences.timeOut = 1;
270 } else if (Preferences.timeOut > 1000) {
271 Preferences.timeOut = 1000;
274 #ifndef VMS
275 /* For Clearcase users who have not set a server name, use the clearcase
276 view name. Clearcase views make files with the same absolute path names
277 but different contents (and therefore can't be edited in the same nedit
278 session). This should have no bad side-effects for non-clearcase users */
279 if (Preferences.serverName[0] == '\0') {
280 const char* viewTag = GetClearCaseViewTag();
281 if (viewTag != NULL && strlen(viewTag) < MAXPATHLEN) {
282 strcpy(Preferences.serverName, viewTag);
285 #endif /* VMS */
287 /* Create the wait properties for the various files. */
288 createWaitProperties();
290 /* Monitor the properties on the root window */
291 XSelectInput(TheDisplay, rootWindow, PropertyChangeMask);
293 /* Create the server property atoms on the current DISPLAY. */
294 CreateServerPropertyAtoms(Preferences.serverName,
295 &serverExistsAtom,
296 &serverRequestAtom);
298 serverExists = findExistingServer(context,
299 rootWindow,
300 serverExistsAtom);
302 if (serverExists == False) {
303 startNewServer(context, rootWindow, commandLine.org, serverExistsAtom);
306 waitUntilRequestProcessed(context,
307 rootWindow,
308 commandLine.string,
309 serverRequestAtom);
311 waitUntilFilesOpenedOrClosed(context,
312 rootWindow,
313 serverExistsAtom);
315 XtCloseDisplay(TheDisplay);
316 XtFree(commandLine.org);
317 XtFree(commandLine.string);
318 return 0;
323 ** Xt timer procedure for timeouts on NEdit server requests
325 static void timeOutProc(Boolean *timeOutReturn, XtIntervalId *id)
327 /* NOTE: XtAppNextEvent() does call this routine but
328 ** doesn't return unless there are more events.
329 ** Hence, we generate this (synthetic) event to break the deadlock
331 Window rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
332 if (currentWaitForAtom != noAtom) {
333 XChangeProperty(TheDisplay, rootWindow, currentWaitForAtom, XA_STRING,
334 8, PropModeReplace, (unsigned char *)"", strlen(""));
337 /* Flag that the timeout has occurred. */
338 *timeOutReturn = True;
343 static Boolean findExistingServer(XtAppContext context,
344 Window rootWindow,
345 Atom serverExistsAtom)
347 Boolean serverExists = True;
348 unsigned char *propValue;
349 int getFmt;
350 Atom dummyAtom;
351 unsigned long dummyULong, nItems;
353 /* See if there might be a server (not a guaranty), by translating the
354 root window property NEDIT_SERVER_EXISTS_<user>_<host> */
355 if (XGetWindowProperty(TheDisplay, rootWindow, serverExistsAtom, 0,
356 INT_MAX, False, XA_STRING, &dummyAtom, &getFmt, &nItems,
357 &dummyULong, &propValue) != Success || nItems == 0) {
358 serverExists = False;
359 } else {
360 Boolean timeOut = False;
361 XtIntervalId timerId;
363 XFree(propValue);
365 /* Remove the server exists property to make sure the server is
366 ** running. If it is running it will get recreated.
368 XDeleteProperty(TheDisplay, rootWindow, serverExistsAtom);
369 XSync(TheDisplay, False);
370 timerId = XtAppAddTimeOut(context,
371 PROPERTY_CHANGE_TIMEOUT,
372 (XtTimerCallbackProc)timeOutProc,
373 &timeOut);
374 currentWaitForAtom = serverExistsAtom;
376 while (!timeOut) {
377 /* NOTE: XtAppNextEvent() does call the timeout routine but
378 ** doesn't return unless there are more events. */
379 XEvent event;
380 const XPropertyEvent *e = (const XPropertyEvent *)&event;
381 XtAppNextEvent(context, &event);
383 /* We will get a PropertyNewValue when the server recreates
384 ** the server exists atom. */
385 if (e->type == PropertyNotify &&
386 e->window == rootWindow &&
387 e->atom == serverExistsAtom) {
388 if (e->state == PropertyNewValue) {
389 break;
392 XtDispatchEvent(&event);
395 /* Start a new server if the timeout expired. The server exists
396 ** property was not recreated. */
397 if (timeOut) {
398 serverExists = False;
399 } else {
400 XtRemoveTimeOut(timerId);
404 return(serverExists);
410 static void startNewServer(XtAppContext context,
411 Window rootWindow,
412 char* commandLine,
413 Atom serverExistsAtom)
415 Boolean timeOut = False;
416 XtIntervalId timerId;
418 /* Add back the server name resource from the command line or resource
419 database to the command line for starting the server. If -svrcmd
420 appeared on the original command line, it was removed by
421 CreatePreferencesDatabase before the command line was recorded
422 in commandLine.org. Moreover, if no server name was specified, it
423 may have defaulted to the ClearCase view tag. */
424 if (Preferences.serverName[0] != '\0') {
425 strcat(commandLine, " -svrname ");
426 strcat(commandLine, Preferences.serverName);
428 if (startServer("No servers available, start one? (y|n)[y]: ", commandLine) != 0) {
429 XtCloseDisplay(TheDisplay);
430 exit(EXIT_FAILURE);
433 /* Set up a timeout proc in case the server is dead. The standard
434 selection timeout is probably a good guess at how long to wait
435 for this style of inter-client communication as well */
436 timerId = XtAppAddTimeOut(context,
437 SERVER_START_TIMEOUT,
438 (XtTimerCallbackProc)timeOutProc,
439 &timeOut);
440 currentWaitForAtom = serverExistsAtom;
442 /* Wait for the server to start */
443 while (!timeOut) {
444 XEvent event;
445 const XPropertyEvent *e = (const XPropertyEvent *)&event;
446 /* NOTE: XtAppNextEvent() does call the timeout routine but
447 ** doesn't return unless there are more events. */
448 XtAppNextEvent(context, &event);
450 /* We will get a PropertyNewValue when the server updates
451 ** the server exists atom. If the property is deleted the
452 ** the server must have died. */
453 if (e->type == PropertyNotify &&
454 e->window == rootWindow &&
455 e->atom == serverExistsAtom) {
456 if (e->state == PropertyNewValue) {
457 break;
458 } else if (e->state == PropertyDelete) {
459 fprintf(stderr, "%s: The server failed to start.\n", APP_NAME);
460 XtCloseDisplay(TheDisplay);
461 exit(EXIT_FAILURE);
464 XtDispatchEvent(&event);
466 /* Exit if the timeout expired. */
467 if (timeOut) {
468 fprintf(stderr, "%s: The server failed to start (time-out).\n", APP_NAME);
469 XtCloseDisplay(TheDisplay);
470 exit(EXIT_FAILURE);
471 } else {
472 XtRemoveTimeOut(timerId);
477 ** Prompt the user about starting a server, with "message", then start server
479 static int startServer(const char *message, const char *commandLineArgs)
481 char c, *commandLine;
482 #ifdef VMS
483 int spawnFlags = 1 /* + 1<<3 */; /* NOWAIT, NOKEYPAD */
484 int spawn_sts;
485 struct dsc$descriptor_s *cmdDesc;
486 char *nulDev = "NL:";
487 struct dsc$descriptor_s *nulDevDesc;
488 #else
489 int sysrc;
490 #endif /* !VMS */
492 /* prompt user whether to start server */
493 if (!Preferences.autoStart) {
494 puts(message);
495 do {
496 c = getc(stdin);
497 } while (c == ' ' || c == '\t');
498 if (c != 'Y' && c != 'y' && c != '\n')
499 return 0;
502 /* start the server */
503 #ifdef VMS
504 commandLine = XtMalloc(strlen(Preferences.serverCmd) +
505 strlen(commandLineArgs) + 3);
506 sprintf(commandLine, "%s %s", Preferences.serverCmd, commandLineArgs);
507 cmdDesc = NulStrToDesc(commandLine); /* build command descriptor */
508 nulDevDesc = NulStrToDesc(nulDev); /* build "NL:" descriptor */
509 spawn_sts = lib$spawn(cmdDesc, nulDevDesc, 0, &spawnFlags, 0,0,0,0,0,0,0,0);
510 XtFree(commandLine);
511 if (spawn_sts != SS$_NORMAL) {
512 fprintf(stderr, "Error return from lib$spawn: %d\n", spawn_sts);
513 fprintf(stderr, "Nedit server not started.\n");
514 return (-1);
515 } else {
516 FreeStrDesc(cmdDesc);
517 return 0;
519 #else
520 #if defined(__EMX__) /* OS/2 */
521 /* Unfortunately system() calls a shell determined by the environment
522 variables COMSPEC and EMXSHELL. We have to figure out which one. */
524 char *sh_spec, *sh, *base;
525 char *CMD_EXE="cmd.exe";
527 commandLine = XtMalloc(strlen(Preferences.serverCmd) +
528 strlen(commandLineArgs) + 15);
529 sh = getenv ("EMXSHELL");
530 if (sh == NULL)
531 sh = getenv ("COMSPEC");
532 if (sh == NULL)
533 sh = CMD_EXE;
534 sh_spec=XtNewString(sh);
535 base=_getname(sh_spec);
536 _remext(base);
537 if (stricmp (base, "cmd") == 0 || stricmp (base, "4os2") == 0) {
538 sprintf(commandLine, "start /C /MIN %s %s",
539 Preferences.serverCmd, commandLineArgs);
540 } else {
541 sprintf(commandLine, "%s %s &",
542 Preferences.serverCmd, commandLineArgs);
544 XtFree(sh_spec);
546 #else /* Unix */
547 commandLine = XtMalloc(strlen(Preferences.serverCmd) +
548 strlen(commandLineArgs) + 3);
549 sprintf(commandLine, "%s %s&", Preferences.serverCmd, commandLineArgs);
550 #endif
552 sysrc=system(commandLine);
553 XtFree(commandLine);
555 if (sysrc==0)
556 return 0;
557 else
558 return (-1);
559 #endif /* !VMS */
562 /* Reconstruct the command line in string commandLine in case we have to
563 * start a server (nc command line args parallel nedit's). Include
564 * -svrname if nc wants a named server, so nedit will match. Special
565 * characters are protected from the shell by escaping EVERYTHING with \
567 static CommandLine processCommandLine(int argc, char** argv)
569 CommandLine commandLine;
570 char *c, *outPtr;
571 int i;
572 int length = 0;
574 for (i=1; i<argc; i++) {
575 length += 1 + strlen(argv[i])*4 + 2;
577 commandLine.org = XtMalloc(length+1 + 9 + MAXPATHLEN);
578 outPtr = commandLine.org;
579 #if defined(VMS) || defined(__EMX__)
580 /* Non-Unix shells don't want/need esc */
581 for (i=1; i<argc; i++) {
582 /* Don't pass macro commands at the command line. They are
583 communicated via properties and we don't want the commands
584 to be executed twice. */
585 if (strcmp(argv[i], "-do")) {
586 for (c=argv[i]; *c!='\0'; c++) {
587 *outPtr++ = *c;
589 *outPtr++ = ' ';
590 } else {
591 nextArg(argc, argv, &i);
594 *outPtr = '\0';
595 #else
596 for (i=1; i<argc; i++) {
597 /* Don't pass macro commands at the command line. They are
598 communicated via properties and we don't want the commands
599 to be executed twice. */
600 if (strcmp(argv[i], "-do")) {
601 *outPtr++ = '\'';
602 for (c=argv[i]; *c!='\0'; c++) {
603 if (*c == '\'') {
604 *outPtr++ = '\'';
605 *outPtr++ = '\\';
607 *outPtr++ = *c;
608 if (*c == '\'') {
609 *outPtr++ = '\'';
612 *outPtr++ = '\'';
613 *outPtr++ = ' ';
614 } else {
615 nextArg(argc, argv, &i);
618 *outPtr = '\0';
619 #endif /* VMS */
621 /* Convert command line arguments into a command string for the server */
622 commandLine.string = parseCommandLine(argc, argv);
623 if (commandLine.string == NULL) {
624 fprintf(stderr, "nc: Invalid commandline argument\n");
625 exit(EXIT_FAILURE);
628 return(commandLine);
633 ** Converts command line into a command string suitable for passing to
634 ** the server
636 static char *parseCommandLine(int argc, char **argv)
638 char name[MAXPATHLEN], path[MAXPATHLEN];
639 char *toDoCommand = "", *langMode = "", *geometry = "";
640 char *commandString, *outPtr;
641 int lineNum = 0, read = 0, create = 0, iconic = 0, length = 0;
642 int i, lineArg, nRead, charsWritten, opts = True;
644 /* Allocate a string for output, for the maximum possible length. The
645 maximum length is calculated by assuming every argument is a file,
646 and a complete record of maximum length is created for it */
647 for (i=1; i<argc; i++) {
648 length += 38 + strlen(argv[i]) + MAXPATHLEN;
650 commandString = XtMalloc(length+1);
652 /* Parse the arguments and write the output string */
653 outPtr = commandString;
654 for (i=1; i<argc; i++) {
655 if (opts && !strcmp(argv[i], "--")) {
656 opts = False; /* treat all remaining arguments as filenames */
657 continue;
658 } else if (opts && !strcmp(argv[i], "-do")) {
659 nextArg(argc, argv, &i);
660 toDoCommand = argv[i];
661 } else if (opts && !strcmp(argv[i], "-lm")) {
662 nextArg(argc, argv, &i);
663 langMode = argv[i];
664 } else if (opts && (!strcmp(argv[i], "-g") ||
665 !strcmp(argv[i], "-geometry"))) {
666 nextArg(argc, argv, &i);
667 geometry = argv[i];
668 } else if (opts && !strcmp(argv[i], "-read")) {
669 read = 1;
670 } else if (opts && !strcmp(argv[i], "-create")) {
671 create = 1;
672 } else if (opts && (!strcmp(argv[i], "-iconic") ||
673 !strcmp(argv[i], "-icon"))) {
674 iconic = 1;
675 } else if (opts && !strcmp(argv[i], "-line")) {
676 nextArg(argc, argv, &i);
677 nRead = sscanf(argv[i], "%d", &lineArg);
678 if (nRead != 1)
679 fprintf(stderr, "nc: argument to line should be a number\n");
680 else
681 lineNum = lineArg;
682 } else if (opts && (*argv[i] == '+')) {
683 nRead = sscanf((argv[i]+1), "%d", &lineArg);
684 if (nRead != 1)
685 fprintf(stderr, "nc: argument to + should be a number\n");
686 else
687 lineNum = lineArg;
688 } else if (opts && (!strcmp(argv[i], "-ask") || !strcmp(argv[i], "-noAsk"))) {
689 ; /* Ignore resource-based arguments which are processed later */
690 } else if (opts && (!strcmp(argv[i], "-svrname") ||
691 !strcmp(argv[i], "-xrm") ||
692 !strcmp(argv[i], "-svrcmd") ||
693 !strcmp(argv[i], "-display"))) {
694 nextArg(argc, argv, &i); /* Ignore rsrc args with data */
695 } else if (opts && (!strcmp(argv[i], "-version") || !strcmp(argv[i], "-V"))) {
696 printNcVersion();
697 exit(EXIT_SUCCESS);
698 } else if (opts && (*argv[i] == '-')) {
699 #ifdef VMS
700 *argv[i] = '/';
701 #endif /*VMS*/
702 fprintf(stderr, "nc: Unrecognized option %s\n%s", argv[i],
703 cmdLineHelp);
704 exit(EXIT_FAILURE);
705 } else {
706 #ifdef VMS
707 int numFiles, j, oldLength;
708 char **nameList = NULL, *newCommandString;
709 /* Use VMS's LIB$FILESCAN for filename in argv[i] to process */
710 /* wildcards and to obtain a full VMS file specification */
711 numFiles = VMSFileScan(argv[i], &nameList, NULL, INCLUDE_FNF);
712 /* for each expanded file name do: */
713 for (j = 0; j < numFiles; ++j) {
714 oldLength = outPtr-commandString;
715 newCommandString = XtMalloc(oldLength+length+1);
716 strncpy(newCommandString, commandString, oldLength);
717 XtFree(commandString);
718 commandString = newCommandString;
719 outPtr = newCommandString + oldLength;
720 if (ParseFilename(nameList[j], name, path) != 0) {
721 /* An Error, most likely too long paths/strings given */
722 return NULL;
724 strcat(path, name);
725 /* See below for casts */
726 sprintf(outPtr, "%d %d %d %d %ld %ld %ld %ld\n%s\n%s\n%s\n%s\n%n",
727 lineNum, read, create, iconic, (long) strlen(path),
728 (long) strlen(toDoCommand), (long) strlen(langMode),
729 (long) strlen(geometry),
730 path, toDoCommand, langMode, geometry, &charsWritten);
731 outPtr += charsWritten;
732 free(nameList[j]);
734 /* Create the file open atoms for the paths supplied */
735 addToFileList(path);
737 if (nameList != NULL)
738 free(nameList);
739 #else
740 if (ParseFilename(argv[i], name, path) != 0) {
741 /* An Error, most likely too long paths/strings given */
742 return NULL;
744 strcat(path, name);
745 /* SunOS 4 acc or acc and/or its runtime library has a bug
746 such that %n fails (segv) if it follows a string in a
747 printf or sprintf. The silly code below avoids this.
749 The "long" cast on strlen() is necessary because size_t
750 is 64 bit on Alphas, and 32-bit on most others. There is
751 no printf format specifier for "size_t", thanx, ANSI. */
753 sprintf(outPtr, "%d %d %d %d %ld %ld %ld %ld\n%n", lineNum, read,
754 create, iconic, (long) strlen(path), (long) strlen(toDoCommand),
755 (long) strlen(langMode), (long) strlen(geometry), &charsWritten);
756 outPtr += charsWritten;
757 strcpy(outPtr, path);
758 outPtr += strlen(path);
759 *outPtr++ = '\n';
760 strcpy(outPtr, toDoCommand);
761 outPtr += strlen(toDoCommand);
762 *outPtr++ = '\n';
763 strcpy(outPtr, langMode);
764 outPtr += strlen(langMode);
765 *outPtr++ = '\n';
766 strcpy(outPtr, geometry);
767 outPtr += strlen(geometry);
768 *outPtr++ = '\n';
769 toDoCommand = "";
771 /* Create the file open atoms for the paths supplied */
772 addToFileList(path);
773 #endif /* VMS */
776 #ifdef VMS
777 VMSFileScanDone();
778 #endif /*VMS*/
780 /* If there's an un-written -do command, write it with an empty file name */
781 if (toDoCommand[0] != '\0') {
782 sprintf(outPtr, "0 0 0 0 0 %d 0 0\n\n%n", (int) strlen(toDoCommand),
783 &charsWritten);
784 outPtr += charsWritten;
785 strcpy(outPtr, toDoCommand);
786 outPtr += strlen(toDoCommand);
787 *outPtr++ = '\n';
788 *outPtr++ = '\n';
789 *outPtr++ = '\n';
792 *outPtr = '\0';
793 return commandString;
797 static void waitUntilRequestProcessed(XtAppContext context,
798 Window rootWindow,
799 char* commandString,
800 Atom serverRequestAtom)
802 XtIntervalId timerId;
803 Boolean timeOut = False;
805 /* Set the NEDIT_SERVER_REQUEST_<user>_<host> property on the root
806 window to activate the server */
807 XChangeProperty(TheDisplay, rootWindow, serverRequestAtom, XA_STRING, 8,
808 PropModeReplace, (unsigned char *)commandString,
809 strlen(commandString));
811 /* Set up a timeout proc in case the server is dead. The standard
812 selection timeout is probably a good guess at how long to wait
813 for this style of inter-client communication as well */
814 timerId = XtAppAddTimeOut(context,
815 REQUEST_TIMEOUT,
816 (XtTimerCallbackProc)timeOutProc,
817 &timeOut);
818 currentWaitForAtom = serverRequestAtom;
820 /* Wait for the property to be deleted to know the request was processed */
821 while (!timeOut) {
822 XEvent event;
823 const XPropertyEvent *e = (const XPropertyEvent *)&event;
825 XtAppNextEvent(context, &event);
826 if (e->window == rootWindow &&
827 e->atom == serverRequestAtom &&
828 e->state == PropertyDelete)
829 break;
830 XtDispatchEvent(&event);
833 /* Exit if the timeout expired. */
834 if (timeOut) {
835 fprintf(stderr, "%s: The server did not respond to the request.\n", APP_NAME);
836 XtCloseDisplay(TheDisplay);
837 exit(EXIT_FAILURE);
838 } else {
839 XtRemoveTimeOut(timerId);
843 static void waitUntilFilesOpenedOrClosed(XtAppContext context,
844 Window rootWindow,
845 Atom serverExistsAtom)
847 XtIntervalId timerId;
848 Boolean timeOut = False;
850 /* Set up a timeout proc so we don't wait forever if the server is dead.
851 The standard selection timeout is probably a good guess at how
852 long to wait for this style of inter-client communication as
853 well */
854 timerId = XtAppAddTimeOut(context, FILE_OPEN_TIMEOUT,
855 (XtTimerCallbackProc)timeOutProc, &timeOut);
856 currentWaitForAtom = noAtom;
858 /* Wait for all of the windows to be opened by server,
859 * and closed if -wait was supplied */
860 while (fileListHead.fileList) {
861 XEvent event;
862 const XPropertyEvent *e = (const XPropertyEvent *)&event;
864 XtAppNextEvent(context, &event);
866 /* Update the fileList and check if all files have been closed. */
867 if (e->type == PropertyNotify && e->window == rootWindow) {
868 FileListEntry *item;
870 if (e->state == PropertyDelete) {
871 for (item = fileListHead.fileList; item; item = item->next) {
872 if (e->atom == item->waitForFileOpenAtom) {
873 /* The 'waitForFileOpen' property is deleted when the file is opened */
874 fileListHead.waitForOpenCount--;
875 item->waitForFileOpenAtom = None;
877 /* Reset the timer while we wait for all files to be opened. */
878 XtRemoveTimeOut(timerId);
879 timerId = XtAppAddTimeOut(context, FILE_OPEN_TIMEOUT,
880 (XtTimerCallbackProc)timeOutProc, &timeOut);
881 } else if (e->atom == item->waitForFileClosedAtom) {
882 /* When file is opened in -wait mode the property
883 * is deleted when the file is closed.
885 fileListHead.waitForCloseCount--;
886 item->waitForFileClosedAtom = None;
890 if (fileListHead.waitForOpenCount == 0 && !timeOut) {
891 XtRemoveTimeOut(timerId);
894 if (fileListHead.waitForOpenCount == 0 &&
895 fileListHead.waitForCloseCount == 0) {
896 break;
901 /* We are finished if we are only waiting for files to open and
902 ** the file open timeout has expired. */
903 if (!Preferences.waitForClose && timeOut) {
904 break;
907 XtDispatchEvent(&event);
912 static void nextArg(int argc, char **argv, int *argIndex)
914 if (*argIndex + 1 >= argc) {
915 #ifdef VMS
916 *argv[*argIndex] = '/';
917 #endif /*VMS*/
918 fprintf(stderr, "nc: %s requires an argument\n%s",
919 argv[*argIndex], cmdLineHelp);
920 exit(EXIT_FAILURE);
922 (*argIndex)++;
926 /* Print version of 'nc' */
927 static void printNcVersion(void ) {
928 static const char *const ncHelpText = \
929 "nc (NEdit) Version 5.3 version\n\
930 (July 2002)\n\n\
931 Built on: %s, %s, %s\n\
932 Built at: %s, %s\n";
934 fprintf(stdout, ncHelpText,
935 COMPILE_OS, COMPILE_MACHINE, COMPILE_COMPILER,
936 __DATE__, __TIME__);