1 static const char CVSID
[] = "$Id: nc.c,v 1.32 2002/12/10 13:16:16 edg Exp $";
2 /*******************************************************************************
4 * nc.c -- Nirvana Editor client program for nedit server processes *
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"
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"
44 #include <lib$routines.h>
48 #include "../util/VMSparam.h"
49 #include "../util/VMSutils.h"
52 #include <sys/param.h>
54 #include <sys/types.h>
55 #include <sys/utsname.h>
58 #include "../util/clearcase.h"
64 #include <X11/Intrinsic.h>
65 #include <X11/Xatom.h>
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 */
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
,
93 Atom serverExistsAtom
);
94 static void startNewServer(XtAppContext context
,
97 Atom serverExistsAtom
);
98 static void waitUntilRequestProcessed(XtAppContext context
,
101 Atom serverRequestAtom
);
102 static void waitUntilFilesOpenedOrClosed(XtAppContext context
,
104 Atom serverExistsAtom
);
107 static Atom currentWaitForAtom
;
108 static Atom noAtom
= (Atom
)(-1);
110 static const char cmdLineHelp
[] =
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";
120 /* Structure to hold X Resource values */
123 char serverCmd
[2*MAXPATHLEN
]; /* holds executable name + flags */
124 char serverName
[MAXPATHLEN
];
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
;
158 struct _FileListEntry
*next
;
162 int waitForOpenCount
;
163 int waitForCloseCount
;
164 FileListEntry
* fileList
;
166 static FileListHead fileListHead
;
168 static void setPropertyValue(Atom atom
) {
169 XChangeProperty(TheDisplay
,
170 RootWindow(TheDisplay
, DefaultScreen(TheDisplay
)),
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
)
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
))
187 /* Add the atom to the head of the file list if it wasn't found. */
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()
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
,
216 setPropertyValue(item
->waitForFileClosedAtom
);
221 int main(int argc
, char **argv
)
223 XtAppContext context
;
225 CommandLine commandLine
;
226 Atom serverExistsAtom
, serverRequestAtom
;
228 Boolean serverExists
;
230 /* Initialize toolkit and get an application context */
231 XtToolkitInitialize();
232 context
= XtCreateApplicationContext();
235 /* Convert the command line to Unix style */
236 ConvertVMSCommandLine(&argc
, &argv
);
239 /* expand wildcards if necessary */
240 _wildcard(&argc
, &argv
);
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
,
257 XtWarning ("nc: Can't open display\n");
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;
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
);
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
,
298 serverExists
= findExistingServer(context
,
302 if (serverExists
== False
) {
303 startNewServer(context
, rootWindow
, commandLine
.org
, serverExistsAtom
);
306 waitUntilRequestProcessed(context
,
311 waitUntilFilesOpenedOrClosed(context
,
315 XtCloseDisplay(TheDisplay
);
316 XtFree(commandLine
.org
);
317 XtFree(commandLine
.string
);
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
,
345 Atom serverExistsAtom
)
347 Boolean serverExists
= True
;
348 unsigned char *propValue
;
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
;
360 Boolean timeOut
= False
;
361 XtIntervalId timerId
;
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
,
374 currentWaitForAtom
= serverExistsAtom
;
377 /* NOTE: XtAppNextEvent() does call the timeout routine but
378 ** doesn't return unless there are more events. */
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
) {
392 XtDispatchEvent(&event
);
395 /* Start a new server if the timeout expired. The server exists
396 ** property was not recreated. */
398 serverExists
= False
;
400 XtRemoveTimeOut(timerId
);
404 return(serverExists
);
410 static void startNewServer(XtAppContext context
,
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
);
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
,
440 currentWaitForAtom
= serverExistsAtom
;
442 /* Wait for the server to start */
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
) {
458 } else if (e
->state
== PropertyDelete
) {
459 fprintf(stderr
, "%s: The server failed to start.\n", APP_NAME
);
460 XtCloseDisplay(TheDisplay
);
464 XtDispatchEvent(&event
);
466 /* Exit if the timeout expired. */
468 fprintf(stderr
, "%s: The server failed to start (time-out).\n", APP_NAME
);
469 XtCloseDisplay(TheDisplay
);
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
;
483 int spawnFlags
= 1 /* + 1<<3 */; /* NOWAIT, NOKEYPAD */
485 struct dsc$descriptor_s
*cmdDesc
;
486 char *nulDev
= "NL:";
487 struct dsc$descriptor_s
*nulDevDesc
;
492 /* prompt user whether to start server */
493 if (!Preferences
.autoStart
) {
497 } while (c
== ' ' || c
== '\t');
498 if (c
!= 'Y' && c
!= 'y' && c
!= '\n')
502 /* start the server */
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);
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");
516 FreeStrDesc(cmdDesc
);
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");
531 sh
= getenv ("COMSPEC");
534 sh_spec
=XtNewString(sh
);
535 base
=_getname(sh_spec
);
537 if (stricmp (base
, "cmd") == 0 || stricmp (base
, "4os2") == 0) {
538 sprintf(commandLine
, "start /C /MIN %s %s",
539 Preferences
.serverCmd
, commandLineArgs
);
541 sprintf(commandLine
, "%s %s &",
542 Preferences
.serverCmd
, commandLineArgs
);
547 commandLine
= XtMalloc(strlen(Preferences
.serverCmd
) +
548 strlen(commandLineArgs
) + 3);
549 sprintf(commandLine
, "%s %s&", Preferences
.serverCmd
, commandLineArgs
);
552 sysrc
=system(commandLine
);
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
;
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
++) {
591 nextArg(argc
, argv
, &i
);
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")) {
602 for (c
=argv
[i
]; *c
!='\0'; c
++) {
615 nextArg(argc
, argv
, &i
);
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");
633 ** Converts command line into a command string suitable for passing to
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 */
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
);
664 } else if (opts
&& (!strcmp(argv
[i
], "-g") ||
665 !strcmp(argv
[i
], "-geometry"))) {
666 nextArg(argc
, argv
, &i
);
668 } else if (opts
&& !strcmp(argv
[i
], "-read")) {
670 } else if (opts
&& !strcmp(argv
[i
], "-create")) {
672 } else if (opts
&& (!strcmp(argv
[i
], "-iconic") ||
673 !strcmp(argv
[i
], "-icon"))) {
675 } else if (opts
&& !strcmp(argv
[i
], "-line")) {
676 nextArg(argc
, argv
, &i
);
677 nRead
= sscanf(argv
[i
], "%d", &lineArg
);
679 fprintf(stderr
, "nc: argument to line should be a number\n");
682 } else if (opts
&& (*argv
[i
] == '+')) {
683 nRead
= sscanf((argv
[i
]+1), "%d", &lineArg
);
685 fprintf(stderr
, "nc: argument to + should be a number\n");
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"))) {
698 } else if (opts
&& (*argv
[i
] == '-')) {
702 fprintf(stderr
, "nc: Unrecognized option %s\n%s", argv
[i
],
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 */
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
;
734 /* Create the file open atoms for the paths supplied */
737 if (nameList
!= NULL
)
740 if (ParseFilename(argv
[i
], name
, path
) != 0) {
741 /* An Error, most likely too long paths/strings given */
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
);
760 strcpy(outPtr
, toDoCommand
);
761 outPtr
+= strlen(toDoCommand
);
763 strcpy(outPtr
, langMode
);
764 outPtr
+= strlen(langMode
);
766 strcpy(outPtr
, geometry
);
767 outPtr
+= strlen(geometry
);
771 /* Create the file open atoms for the paths supplied */
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
),
784 outPtr
+= charsWritten
;
785 strcpy(outPtr
, toDoCommand
);
786 outPtr
+= strlen(toDoCommand
);
793 return commandString
;
797 static void waitUntilRequestProcessed(XtAppContext context
,
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
,
816 (XtTimerCallbackProc
)timeOutProc
,
818 currentWaitForAtom
= serverRequestAtom
;
820 /* Wait for the property to be deleted to know the request was processed */
823 const XPropertyEvent
*e
= (const XPropertyEvent
*)&event
;
825 XtAppNextEvent(context
, &event
);
826 if (e
->window
== rootWindow
&&
827 e
->atom
== serverRequestAtom
&&
828 e
->state
== PropertyDelete
)
830 XtDispatchEvent(&event
);
833 /* Exit if the timeout expired. */
835 fprintf(stderr
, "%s: The server did not respond to the request.\n", APP_NAME
);
836 XtCloseDisplay(TheDisplay
);
839 XtRemoveTimeOut(timerId
);
843 static void waitUntilFilesOpenedOrClosed(XtAppContext context
,
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
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
) {
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
) {
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) {
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
) {
907 XtDispatchEvent(&event
);
912 static void nextArg(int argc
, char **argv
, int *argIndex
)
914 if (*argIndex
+ 1 >= argc
) {
916 *argv
[*argIndex
] = '/';
918 fprintf(stderr
, "nc: %s requires an argument\n%s",
919 argv
[*argIndex
], cmdLineHelp
);
926 /* Print version of 'nc' */
927 static void printNcVersion(void ) {
928 static const char *const ncHelpText
= \
929 "nc (NEdit) Version 5.3 version\n\
931 Built on: %s, %s, %s\n\
934 fprintf(stdout
, ncHelpText
,
935 COMPILE_OS
, COMPILE_MACHINE
, COMPILE_COMPILER
,