Fix for SF bug #564782: Show Path in Windows Menu
[nedit.git] / source / nc.c
blobe21b67ad3f513ddaeeb285d55028d9487b36b73c
1 static const char CVSID[] = "$Id: nc.c,v 1.33 2003/02/20 17:30:02 arnef 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* shell;
82 char* serverRequest;
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 void parseCommandLine(int argc, char **arg, CommandLine *cmdLine);
89 static void nextArg(int argc, char **argv, int *argIndex);
90 static void copyCommandLineArg(CommandLine *cmdLine, const char *arg);
91 static void printNcVersion(void);
92 static Boolean findExistingServer(XtAppContext context,
93 Window rootWindow,
94 Atom serverExistsAtom);
95 static void startNewServer(XtAppContext context,
96 Window rootWindow,
97 char* commandLine,
98 Atom serverExistsAtom);
99 static void waitUntilRequestProcessed(XtAppContext context,
100 Window rootWindow,
101 char* commandString,
102 Atom serverRequestAtom);
103 static void waitUntilFilesOpenedOrClosed(XtAppContext context,
104 Window rootWindow,
105 Atom serverExistsAtom);
107 Display *TheDisplay;
108 static Atom currentWaitForAtom;
109 static Atom noAtom = (Atom)(-1);
111 static const char cmdLineHelp[] =
112 #ifdef VMS
114 #else
115 "Usage: nc [-read] [-create] [-line n | +n] [-do command] [-ask] [-noask]\n\
116 [-svrname name] [-svrcmd command] [-lm languagemode]\n\
117 [-geometry geometry] [-iconic] [-timeout seconds] [-wait]\n\
118 [-V|-version] [--] [file...]\n";
119 #endif /*VMS*/
121 /* Structure to hold X Resource values */
122 static struct {
123 int autoStart;
124 char serverCmd[2*MAXPATHLEN]; /* holds executable name + flags */
125 char serverName[MAXPATHLEN];
126 int waitForClose;
127 int timeOut;
128 } Preferences;
130 /* Application resources */
131 static PrefDescripRec PrefDescrip[] = {
132 {"autoStart", "AutoStart", PREF_BOOLEAN, "True",
133 &Preferences.autoStart, NULL, True},
134 {"serverCommand", "ServerCommand", PREF_STRING, "nedit -server",
135 Preferences.serverCmd, (void *)sizeof(Preferences.serverCmd), False},
136 {"serverName", "serverName", PREF_STRING, "", Preferences.serverName,
137 (void *)sizeof(Preferences.serverName), False},
138 {"waitForClose", "WaitForClose", PREF_BOOLEAN, "False",
139 &Preferences.waitForClose, NULL, False},
140 {"timeOut", "TimeOut", PREF_INT, "10",
141 &Preferences.timeOut, NULL, False}
144 /* Resource related command line options */
145 static XrmOptionDescRec OpTable[] = {
146 {"-ask", ".autoStart", XrmoptionNoArg, (caddr_t)"False"},
147 {"-noask", ".autoStart", XrmoptionNoArg, (caddr_t)"True"},
148 {"-svrname", ".serverName", XrmoptionSepArg, (caddr_t)NULL},
149 {"-svrcmd", ".serverCommand", XrmoptionSepArg, (caddr_t)NULL},
150 {"-wait", ".waitForClose", XrmoptionNoArg, (caddr_t)"True"},
151 {"-timeout", ".timeOut", XrmoptionSepArg, (caddr_t)NULL}
154 /* Struct to hold info about files being opened and edited. */
155 typedef struct _FileListEntry {
156 Atom waitForFileOpenAtom;
157 Atom waitForFileClosedAtom;
158 char* path;
159 struct _FileListEntry *next;
160 } FileListEntry;
162 typedef struct {
163 int waitForOpenCount;
164 int waitForCloseCount;
165 FileListEntry* fileList;
166 } FileListHead;
167 static FileListHead fileListHead;
169 static void setPropertyValue(Atom atom) {
170 XChangeProperty(TheDisplay,
171 RootWindow(TheDisplay, DefaultScreen(TheDisplay)),
172 atom, XA_STRING,
173 8, PropModeReplace,
174 (unsigned char *)"True", 4);
177 /* Add another entry to the file entry list, if it doesn't exist yet. */
178 static void addToFileList(const char *path)
180 FileListEntry *item;
182 /* see if the file already exists in the list */
183 for (item = fileListHead.fileList; item; item = item->next) {
184 if (!strcmp(item->path, path))
185 break;
188 /* Add the atom to the head of the file list if it wasn't found. */
189 if (item == 0) {
190 item = malloc(sizeof(item[0]));
191 item->waitForFileOpenAtom = None;
192 item->waitForFileClosedAtom = None;
193 item->path = (char*)malloc(strlen(path)+1);
194 strcpy(item->path, path);
195 item->next = fileListHead.fileList;
196 fileListHead.fileList = item;
200 /* Creates the properties for the various paths */
201 static void createWaitProperties()
203 FileListEntry *item;
205 for (item = fileListHead.fileList; item; item = item->next) {
206 fileListHead.waitForOpenCount++;
207 item->waitForFileOpenAtom =
208 CreateServerFileOpenAtom(Preferences.serverName, item->path);
209 setPropertyValue(item->waitForFileOpenAtom);
211 if (Preferences.waitForClose == True) {
212 fileListHead.waitForCloseCount++;
213 item->waitForFileClosedAtom =
214 CreateServerFileClosedAtom(Preferences.serverName,
215 item->path,
216 False);
217 setPropertyValue(item->waitForFileClosedAtom);
222 int main(int argc, char **argv)
224 XtAppContext context;
225 Window rootWindow;
226 CommandLine commandLine;
227 Atom serverExistsAtom, serverRequestAtom;
228 XrmDatabase prefDB;
229 Boolean serverExists;
231 /* Initialize toolkit and get an application context */
232 XtToolkitInitialize();
233 context = XtCreateApplicationContext();
235 #ifdef VMS
236 /* Convert the command line to Unix style */
237 ConvertVMSCommandLine(&argc, &argv);
238 #endif /*VMS*/
239 #ifdef __EMX__
240 /* expand wildcards if necessary */
241 _wildcard(&argc, &argv);
242 #endif
244 /* Read the preferences command line into a database (note that we
245 don't support the .nc file anymore) */
246 prefDB = CreatePreferencesDatabase(NULL, APP_CLASS,
247 OpTable, XtNumber(OpTable), (unsigned *)&argc, argv);
249 /* Process the command line before calling XtOpenDisplay, because the
250 latter consumes certain command line arguments that we still need
251 (-icon, -geometry ...) */
252 commandLine = processCommandLine(argc, argv);
254 /* Open the display and find the root window */
255 TheDisplay = XtOpenDisplay (context, NULL, APP_NAME, APP_CLASS, NULL,
256 0, &argc, argv);
257 if (!TheDisplay) {
258 XtWarning ("nc: Can't open display\n");
259 exit(EXIT_FAILURE);
261 rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
263 /* Read the application resources into the Preferences data structure */
264 RestorePreferences(prefDB, XtDatabase(TheDisplay), APP_NAME,
265 APP_CLASS, PrefDescrip, XtNumber(PrefDescrip));
267 /* Make sure that the time out unit is at least 1 second and not too
268 large either (overflow!). */
269 if (Preferences.timeOut < 1) {
270 Preferences.timeOut = 1;
271 } else if (Preferences.timeOut > 1000) {
272 Preferences.timeOut = 1000;
275 #ifndef VMS
276 /* For Clearcase users who have not set a server name, use the clearcase
277 view name. Clearcase views make files with the same absolute path names
278 but different contents (and therefore can't be edited in the same nedit
279 session). This should have no bad side-effects for non-clearcase users */
280 if (Preferences.serverName[0] == '\0') {
281 const char* viewTag = GetClearCaseViewTag();
282 if (viewTag != NULL && strlen(viewTag) < MAXPATHLEN) {
283 strcpy(Preferences.serverName, viewTag);
286 #endif /* VMS */
288 /* Create the wait properties for the various files. */
289 createWaitProperties();
291 /* Monitor the properties on the root window */
292 XSelectInput(TheDisplay, rootWindow, PropertyChangeMask);
294 /* Create the server property atoms on the current DISPLAY. */
295 CreateServerPropertyAtoms(Preferences.serverName,
296 &serverExistsAtom,
297 &serverRequestAtom);
299 serverExists = findExistingServer(context,
300 rootWindow,
301 serverExistsAtom);
303 if (serverExists == False) {
304 startNewServer(context, rootWindow, commandLine.shell, serverExistsAtom);
307 waitUntilRequestProcessed(context,
308 rootWindow,
309 commandLine.serverRequest,
310 serverRequestAtom);
312 waitUntilFilesOpenedOrClosed(context,
313 rootWindow,
314 serverExistsAtom);
316 XtCloseDisplay(TheDisplay);
317 XtFree(commandLine.shell);
318 XtFree(commandLine.serverRequest);
319 return 0;
324 ** Xt timer procedure for timeouts on NEdit server requests
326 static void timeOutProc(Boolean *timeOutReturn, XtIntervalId *id)
328 /* NOTE: XtAppNextEvent() does call this routine but
329 ** doesn't return unless there are more events.
330 ** Hence, we generate this (synthetic) event to break the deadlock
332 Window rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
333 if (currentWaitForAtom != noAtom) {
334 XChangeProperty(TheDisplay, rootWindow, currentWaitForAtom, XA_STRING,
335 8, PropModeReplace, (unsigned char *)"", strlen(""));
338 /* Flag that the timeout has occurred. */
339 *timeOutReturn = True;
344 static Boolean findExistingServer(XtAppContext context,
345 Window rootWindow,
346 Atom serverExistsAtom)
348 Boolean serverExists = True;
349 unsigned char *propValue;
350 int getFmt;
351 Atom dummyAtom;
352 unsigned long dummyULong, nItems;
354 /* See if there might be a server (not a guaranty), by translating the
355 root window property NEDIT_SERVER_EXISTS_<user>_<host> */
356 if (XGetWindowProperty(TheDisplay, rootWindow, serverExistsAtom, 0,
357 INT_MAX, False, XA_STRING, &dummyAtom, &getFmt, &nItems,
358 &dummyULong, &propValue) != Success || nItems == 0) {
359 serverExists = False;
360 } else {
361 Boolean timeOut = False;
362 XtIntervalId timerId;
364 XFree(propValue);
366 /* Remove the server exists property to make sure the server is
367 ** running. If it is running it will get recreated.
369 XDeleteProperty(TheDisplay, rootWindow, serverExistsAtom);
370 XSync(TheDisplay, False);
371 timerId = XtAppAddTimeOut(context,
372 PROPERTY_CHANGE_TIMEOUT,
373 (XtTimerCallbackProc)timeOutProc,
374 &timeOut);
375 currentWaitForAtom = serverExistsAtom;
377 while (!timeOut) {
378 /* NOTE: XtAppNextEvent() does call the timeout routine but
379 ** doesn't return unless there are more events. */
380 XEvent event;
381 const XPropertyEvent *e = (const XPropertyEvent *)&event;
382 XtAppNextEvent(context, &event);
384 /* We will get a PropertyNewValue when the server recreates
385 ** the server exists atom. */
386 if (e->type == PropertyNotify &&
387 e->window == rootWindow &&
388 e->atom == serverExistsAtom) {
389 if (e->state == PropertyNewValue) {
390 break;
393 XtDispatchEvent(&event);
396 /* Start a new server if the timeout expired. The server exists
397 ** property was not recreated. */
398 if (timeOut) {
399 serverExists = False;
400 } else {
401 XtRemoveTimeOut(timerId);
405 return(serverExists);
411 static void startNewServer(XtAppContext context,
412 Window rootWindow,
413 char* commandLine,
414 Atom serverExistsAtom)
416 Boolean timeOut = False;
417 XtIntervalId timerId;
419 /* Add back the server name resource from the command line or resource
420 database to the command line for starting the server. If -svrcmd
421 appeared on the original command line, it was removed by
422 CreatePreferencesDatabase before the command line was recorded
423 in commandLine.shell. Moreover, if no server name was specified, it
424 may have defaulted to the ClearCase view tag. */
425 if (Preferences.serverName[0] != '\0') {
426 strcat(commandLine, " -svrname ");
427 strcat(commandLine, Preferences.serverName);
429 if (startServer("No servers available, start one? (y|n)[y]: ", commandLine) != 0) {
430 XtCloseDisplay(TheDisplay);
431 exit(EXIT_FAILURE);
434 /* Set up a timeout proc in case the server is dead. The standard
435 selection timeout is probably a good guess at how long to wait
436 for this style of inter-client communication as well */
437 timerId = XtAppAddTimeOut(context,
438 SERVER_START_TIMEOUT,
439 (XtTimerCallbackProc)timeOutProc,
440 &timeOut);
441 currentWaitForAtom = serverExistsAtom;
443 /* Wait for the server to start */
444 while (!timeOut) {
445 XEvent event;
446 const XPropertyEvent *e = (const XPropertyEvent *)&event;
447 /* NOTE: XtAppNextEvent() does call the timeout routine but
448 ** doesn't return unless there are more events. */
449 XtAppNextEvent(context, &event);
451 /* We will get a PropertyNewValue when the server updates
452 ** the server exists atom. If the property is deleted the
453 ** the server must have died. */
454 if (e->type == PropertyNotify &&
455 e->window == rootWindow &&
456 e->atom == serverExistsAtom) {
457 if (e->state == PropertyNewValue) {
458 break;
459 } else if (e->state == PropertyDelete) {
460 fprintf(stderr, "%s: The server failed to start.\n", APP_NAME);
461 XtCloseDisplay(TheDisplay);
462 exit(EXIT_FAILURE);
465 XtDispatchEvent(&event);
467 /* Exit if the timeout expired. */
468 if (timeOut) {
469 fprintf(stderr, "%s: The server failed to start (time-out).\n", APP_NAME);
470 XtCloseDisplay(TheDisplay);
471 exit(EXIT_FAILURE);
472 } else {
473 XtRemoveTimeOut(timerId);
478 ** Prompt the user about starting a server, with "message", then start server
480 static int startServer(const char *message, const char *commandLineArgs)
482 char c, *commandLine;
483 #ifdef VMS
484 int spawnFlags = 1 /* + 1<<3 */; /* NOWAIT, NOKEYPAD */
485 int spawn_sts;
486 struct dsc$descriptor_s *cmdDesc;
487 char *nulDev = "NL:";
488 struct dsc$descriptor_s *nulDevDesc;
489 #else
490 int sysrc;
491 #endif /* !VMS */
493 /* prompt user whether to start server */
494 if (!Preferences.autoStart) {
495 puts(message);
496 do {
497 c = getc(stdin);
498 } while (c == ' ' || c == '\t');
499 if (c != 'Y' && c != 'y' && c != '\n')
500 return 0;
503 /* start the server */
504 #ifdef VMS
505 commandLine = XtMalloc(strlen(Preferences.serverCmd) +
506 strlen(commandLineArgs) + 3);
507 sprintf(commandLine, "%s %s", Preferences.serverCmd, commandLineArgs);
508 cmdDesc = NulStrToDesc(commandLine); /* build command descriptor */
509 nulDevDesc = NulStrToDesc(nulDev); /* build "NL:" descriptor */
510 spawn_sts = lib$spawn(cmdDesc, nulDevDesc, 0, &spawnFlags, 0,0,0,0,0,0,0,0);
511 XtFree(commandLine);
512 if (spawn_sts != SS$_NORMAL) {
513 fprintf(stderr, "Error return from lib$spawn: %d\n", spawn_sts);
514 fprintf(stderr, "Nedit server not started.\n");
515 return (-1);
516 } else {
517 FreeStrDesc(cmdDesc);
518 return 0;
520 #else
521 #if defined(__EMX__) /* OS/2 */
522 /* Unfortunately system() calls a shell determined by the environment
523 variables COMSPEC and EMXSHELL. We have to figure out which one. */
525 char *sh_spec, *sh, *base;
526 char *CMD_EXE="cmd.exe";
528 commandLine = XtMalloc(strlen(Preferences.serverCmd) +
529 strlen(commandLineArgs) + 15);
530 sh = getenv ("EMXSHELL");
531 if (sh == NULL)
532 sh = getenv ("COMSPEC");
533 if (sh == NULL)
534 sh = CMD_EXE;
535 sh_spec=XtNewString(sh);
536 base=_getname(sh_spec);
537 _remext(base);
538 if (stricmp (base, "cmd") == 0 || stricmp (base, "4os2") == 0) {
539 sprintf(commandLine, "start /C /MIN %s %s",
540 Preferences.serverCmd, commandLineArgs);
541 } else {
542 sprintf(commandLine, "%s %s &",
543 Preferences.serverCmd, commandLineArgs);
545 XtFree(sh_spec);
547 #else /* Unix */
548 commandLine = XtMalloc(strlen(Preferences.serverCmd) +
549 strlen(commandLineArgs) + 3);
550 sprintf(commandLine, "%s %s&", Preferences.serverCmd, commandLineArgs);
551 #endif
553 sysrc=system(commandLine);
554 XtFree(commandLine);
556 if (sysrc==0)
557 return 0;
558 else
559 return (-1);
560 #endif /* !VMS */
563 /* Reconstruct the command line in string commandLine in case we have to
564 * start a server (nc command line args parallel nedit's). Include
565 * -svrname if nc wants a named server, so nedit will match. Special
566 * characters are protected from the shell by escaping EVERYTHING with \
568 static CommandLine processCommandLine(int argc, char** argv)
570 CommandLine commandLine;
571 int i;
572 int length = 0;
574 for (i=1; i<argc; i++) {
575 length += 1 + strlen(argv[i])*4 + 2;
577 commandLine.shell = XtMalloc(length+1 + 9 + MAXPATHLEN);
578 *commandLine.shell = '\0';
580 /* Convert command line arguments into a command string for the server */
581 parseCommandLine(argc, argv, &commandLine);
582 if (commandLine.serverRequest == NULL) {
583 fprintf(stderr, "nc: Invalid commandline argument\n");
584 exit(EXIT_FAILURE);
587 return(commandLine);
592 ** Converts command line into a command string suitable for passing to
593 ** the server
595 static void parseCommandLine(int argc, char **argv, CommandLine *commandLine)
597 #define MAX_RECORD_HEADER_LENGTH 38
598 char name[MAXPATHLEN], path[MAXPATHLEN];
599 const char *toDoCommand = "", *langMode = "", *geometry = "";
600 char *commandString, *outPtr;
601 int lineNum = 0, read = 0, create = 0, iconic = 0, length = 0;
602 int i, lineArg, nRead, charsWritten, opts = True;
603 int fileCount = 0;
605 /* Allocate a string for output, for the maximum possible length. The
606 maximum length is calculated by assuming every argument is a file,
607 and a complete record of maximum length is created for it */
608 for (i=1; i<argc; i++) {
609 length += MAX_RECORD_HEADER_LENGTH + strlen(argv[i]) + MAXPATHLEN;
611 /* In case of no arguments, must still allocate space for one record header */
612 if (length < MAX_RECORD_HEADER_LENGTH)
614 length = MAX_RECORD_HEADER_LENGTH;
616 commandString = XtMalloc(length+1);
618 /* Parse the arguments and write the output string */
619 outPtr = commandString;
620 for (i=1; i<argc; i++) {
621 if (opts && !strcmp(argv[i], "--")) {
622 opts = False; /* treat all remaining arguments as filenames */
623 continue;
624 } else if (opts && !strcmp(argv[i], "-do")) {
625 nextArg(argc, argv, &i);
626 toDoCommand = argv[i];
627 } else if (opts && !strcmp(argv[i], "-lm")) {
628 nextArg(argc, argv, &i);
629 langMode = argv[i];
630 } else if (opts && (!strcmp(argv[i], "-g") ||
631 !strcmp(argv[i], "-geometry"))) {
632 copyCommandLineArg(commandLine, argv[i]);
633 nextArg(argc, argv, &i);
634 geometry = argv[i];
635 copyCommandLineArg(commandLine, argv[i]);
636 } else if (opts && !strcmp(argv[i], "-read")) {
637 read = 1;
638 } else if (opts && !strcmp(argv[i], "-create")) {
639 create = 1;
640 } else if (opts && (!strcmp(argv[i], "-iconic") ||
641 !strcmp(argv[i], "-icon"))) {
642 iconic = 1;
643 copyCommandLineArg(commandLine, argv[i]);
644 } else if (opts && !strcmp(argv[i], "-line")) {
645 nextArg(argc, argv, &i);
646 nRead = sscanf(argv[i], "%d", &lineArg);
647 if (nRead != 1)
648 fprintf(stderr, "nc: argument to line should be a number\n");
649 else
650 lineNum = lineArg;
651 } else if (opts && (*argv[i] == '+')) {
652 nRead = sscanf((argv[i]+1), "%d", &lineArg);
653 if (nRead != 1)
654 fprintf(stderr, "nc: argument to + should be a number\n");
655 else
656 lineNum = lineArg;
657 } else if (opts && (!strcmp(argv[i], "-ask") || !strcmp(argv[i], "-noAsk"))) {
658 ; /* Ignore resource-based arguments which are processed later */
659 } else if (opts && (!strcmp(argv[i], "-svrname") ||
660 !strcmp(argv[i], "-svrcmd"))) {
661 nextArg(argc, argv, &i); /* Ignore rsrc args with data */
662 } else if (opts && (!strcmp(argv[i], "-xrm") ||
663 !strcmp(argv[i], "-display"))) {
664 copyCommandLineArg(commandLine, argv[i]);
665 nextArg(argc, argv, &i); /* Ignore rsrc args with data */
666 copyCommandLineArg(commandLine, argv[i]);
667 } else if (opts && (!strcmp(argv[i], "-version") || !strcmp(argv[i], "-V"))) {
668 printNcVersion();
669 exit(EXIT_SUCCESS);
670 } else if (opts && (*argv[i] == '-')) {
671 #ifdef VMS
672 *argv[i] = '/';
673 #endif /*VMS*/
674 fprintf(stderr, "nc: Unrecognized option %s\n%s", argv[i],
675 cmdLineHelp);
676 exit(EXIT_FAILURE);
677 } else {
678 #ifdef VMS
679 int numFiles, j, oldLength;
680 char **nameList = NULL, *newCommandString;
681 /* Use VMS's LIB$FILESCAN for filename in argv[i] to process */
682 /* wildcards and to obtain a full VMS file specification */
683 numFiles = VMSFileScan(argv[i], &nameList, NULL, INCLUDE_FNF);
684 /* for each expanded file name do: */
685 for (j = 0; j < numFiles; ++j) {
686 oldLength = outPtr-commandString;
687 newCommandString = XtMalloc(oldLength+length+1);
688 strncpy(newCommandString, commandString, oldLength);
689 XtFree(commandString);
690 commandString = newCommandString;
691 outPtr = newCommandString + oldLength;
692 if (ParseFilename(nameList[j], name, path) != 0) {
693 /* An Error, most likely too long paths/strings given */
694 commandLine->serverRequest = NULL;
695 return;
697 strcat(path, name);
698 /* See below for casts */
699 sprintf(outPtr, "%d %d %d %d %ld %ld %ld %ld\n%s\n%s\n%s\n%s\n%n",
700 lineNum, read, create, iconic, (long) strlen(path),
701 (long) strlen(toDoCommand), (long) strlen(langMode),
702 (long) strlen(geometry),
703 path, toDoCommand, langMode, geometry, &charsWritten);
704 outPtr += charsWritten;
705 free(nameList[j]);
707 /* Create the file open atoms for the paths supplied */
708 addToFileList(path);
709 fileCount++;
711 if (nameList != NULL)
712 free(nameList);
713 #else
714 if (ParseFilename(argv[i], name, path) != 0) {
715 /* An Error, most likely too long paths/strings given */
716 commandLine->serverRequest = NULL;
717 return;
719 strcat(path, name);
720 /* SunOS 4 acc or acc and/or its runtime library has a bug
721 such that %n fails (segv) if it follows a string in a
722 printf or sprintf. The silly code below avoids this.
724 The "long" cast on strlen() is necessary because size_t
725 is 64 bit on Alphas, and 32-bit on most others. There is
726 no printf format specifier for "size_t", thanx, ANSI. */
728 sprintf(outPtr, "%d %d %d %d %ld %ld %ld %ld\n%n", lineNum, read,
729 create, iconic, (long) strlen(path), (long) strlen(toDoCommand),
730 (long) strlen(langMode), (long) strlen(geometry), &charsWritten);
731 outPtr += charsWritten;
732 strcpy(outPtr, path);
733 outPtr += strlen(path);
734 *outPtr++ = '\n';
735 strcpy(outPtr, toDoCommand);
736 outPtr += strlen(toDoCommand);
737 *outPtr++ = '\n';
738 strcpy(outPtr, langMode);
739 outPtr += strlen(langMode);
740 *outPtr++ = '\n';
741 strcpy(outPtr, geometry);
742 outPtr += strlen(geometry);
743 *outPtr++ = '\n';
744 toDoCommand = "";
746 /* Create the file open atoms for the paths supplied */
747 addToFileList(path);
748 fileCount++;
749 #endif /* VMS */
752 #ifdef VMS
753 VMSFileScanDone();
754 #endif /*VMS*/
756 /* If there's an un-written -do command,
757 * or user has requested iconic state, but not provided a file name,
758 * create a server request with an empty file name and requested
759 * iconic state.
761 if (toDoCommand[0] != '\0' || fileCount == 0) {
762 sprintf(outPtr, "0 0 0 %d 0 %d 0 0\n\n%n", iconic, (int) strlen(toDoCommand),
763 &charsWritten);
764 outPtr += charsWritten;
765 strcpy(outPtr, toDoCommand);
766 outPtr += strlen(toDoCommand);
767 *outPtr++ = '\n';
768 *outPtr++ = '\n';
769 *outPtr++ = '\n';
772 *outPtr = '\0';
773 commandLine->serverRequest = commandString;
777 static void waitUntilRequestProcessed(XtAppContext context,
778 Window rootWindow,
779 char* commandString,
780 Atom serverRequestAtom)
782 XtIntervalId timerId;
783 Boolean timeOut = False;
785 /* Set the NEDIT_SERVER_REQUEST_<user>_<host> property on the root
786 window to activate the server */
787 XChangeProperty(TheDisplay, rootWindow, serverRequestAtom, XA_STRING, 8,
788 PropModeReplace, (unsigned char *)commandString,
789 strlen(commandString));
791 /* Set up a timeout proc in case the server is dead. The standard
792 selection timeout is probably a good guess at how long to wait
793 for this style of inter-client communication as well */
794 timerId = XtAppAddTimeOut(context,
795 REQUEST_TIMEOUT,
796 (XtTimerCallbackProc)timeOutProc,
797 &timeOut);
798 currentWaitForAtom = serverRequestAtom;
800 /* Wait for the property to be deleted to know the request was processed */
801 while (!timeOut) {
802 XEvent event;
803 const XPropertyEvent *e = (const XPropertyEvent *)&event;
805 XtAppNextEvent(context, &event);
806 if (e->window == rootWindow &&
807 e->atom == serverRequestAtom &&
808 e->state == PropertyDelete)
809 break;
810 XtDispatchEvent(&event);
813 /* Exit if the timeout expired. */
814 if (timeOut) {
815 fprintf(stderr, "%s: The server did not respond to the request.\n", APP_NAME);
816 XtCloseDisplay(TheDisplay);
817 exit(EXIT_FAILURE);
818 } else {
819 XtRemoveTimeOut(timerId);
823 static void waitUntilFilesOpenedOrClosed(XtAppContext context,
824 Window rootWindow,
825 Atom serverExistsAtom)
827 XtIntervalId timerId;
828 Boolean timeOut = False;
830 /* Set up a timeout proc so we don't wait forever if the server is dead.
831 The standard selection timeout is probably a good guess at how
832 long to wait for this style of inter-client communication as
833 well */
834 timerId = XtAppAddTimeOut(context, FILE_OPEN_TIMEOUT,
835 (XtTimerCallbackProc)timeOutProc, &timeOut);
836 currentWaitForAtom = noAtom;
838 /* Wait for all of the windows to be opened by server,
839 * and closed if -wait was supplied */
840 while (fileListHead.fileList) {
841 XEvent event;
842 const XPropertyEvent *e = (const XPropertyEvent *)&event;
844 XtAppNextEvent(context, &event);
846 /* Update the fileList and check if all files have been closed. */
847 if (e->type == PropertyNotify && e->window == rootWindow) {
848 FileListEntry *item;
850 if (e->state == PropertyDelete) {
851 for (item = fileListHead.fileList; item; item = item->next) {
852 if (e->atom == item->waitForFileOpenAtom) {
853 /* The 'waitForFileOpen' property is deleted when the file is opened */
854 fileListHead.waitForOpenCount--;
855 item->waitForFileOpenAtom = None;
857 /* Reset the timer while we wait for all files to be opened. */
858 XtRemoveTimeOut(timerId);
859 timerId = XtAppAddTimeOut(context, FILE_OPEN_TIMEOUT,
860 (XtTimerCallbackProc)timeOutProc, &timeOut);
861 } else if (e->atom == item->waitForFileClosedAtom) {
862 /* When file is opened in -wait mode the property
863 * is deleted when the file is closed.
865 fileListHead.waitForCloseCount--;
866 item->waitForFileClosedAtom = None;
870 if (fileListHead.waitForOpenCount == 0 && !timeOut) {
871 XtRemoveTimeOut(timerId);
874 if (fileListHead.waitForOpenCount == 0 &&
875 fileListHead.waitForCloseCount == 0) {
876 break;
881 /* We are finished if we are only waiting for files to open and
882 ** the file open timeout has expired. */
883 if (!Preferences.waitForClose && timeOut) {
884 break;
887 XtDispatchEvent(&event);
892 static void nextArg(int argc, char **argv, int *argIndex)
894 if (*argIndex + 1 >= argc) {
895 #ifdef VMS
896 *argv[*argIndex] = '/';
897 #endif /*VMS*/
898 fprintf(stderr, "nc: %s requires an argument\n%s",
899 argv[*argIndex], cmdLineHelp);
900 exit(EXIT_FAILURE);
902 (*argIndex)++;
905 /* Copies a given nc command line argument to the server startup command
906 ** line (-icon, -geometry, -xrm, ...) Special characters are protected from
907 ** the shell by escaping EVERYTHING with \
908 ** Note that the .shell string in the command line structure is large enough
909 ** to hold the escaped characters.
911 static void copyCommandLineArg(CommandLine *commandLine, const char *arg)
913 const char *c;
914 char *outPtr = commandLine->shell + strlen(commandLine->shell);
915 #if defined(VMS) || defined(__EMX__)
916 /* Non-Unix shells don't want/need esc */
917 for (c=arg; *c!='\0'; c++) {
918 *outPtr++ = *c;
920 *outPtr++ = ' ';
921 *outPtr = '\0';
922 #else
923 *outPtr++ = '\'';
924 for (c=arg; *c!='\0'; c++) {
925 if (*c == '\'') {
926 *outPtr++ = '\'';
927 *outPtr++ = '\\';
929 *outPtr++ = *c;
930 if (*c == '\'') {
931 *outPtr++ = '\'';
934 *outPtr++ = '\'';
935 *outPtr++ = ' ';
936 *outPtr = '\0';
937 #endif /* VMS */
940 /* Print version of 'nc' */
941 static void printNcVersion(void ) {
942 static const char *const ncHelpText = \
943 "nc (NEdit) Version 5.3 version\n\
944 (July 2002)\n\n\
945 Built on: %s, %s, %s\n\
946 Built at: %s, %s\n";
948 fprintf(stdout, ncHelpText,
949 COMPILE_OS, COMPILE_MACHINE, COMPILE_COMPILER,
950 __DATE__, __TIME__);