1 static const char CVSID
[] = "$Id: nc.c,v 1.48 2007/11/29 22:18:48 tringali 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 *
11 * version. In addition, you may distribute version of this program linked to *
12 * Motif or Open Motif. See README for details. *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
23 * Nirvana Text Editor *
26 * Written by Mark Edel *
28 *******************************************************************************/
31 #include "../config.h"
34 #include "server_common.h"
35 #include "../util/fileUtils.h"
36 #include "../util/utils.h"
37 #include "../util/prefFile.h"
38 #include "../util/system.h"
45 #include <lib$routines.h>
49 #include "../util/VMSparam.h"
50 #include "../util/VMSutils.h"
53 #include <sys/param.h>
55 #include <sys/types.h>
56 #include <sys/utsname.h>
59 #include "../util/clearcase.h"
65 #include <X11/Intrinsic.h>
66 #include <X11/Xatom.h>
73 #define APP_CLASS "NEditClient"
75 #define PROPERTY_CHANGE_TIMEOUT (Preferences.timeOut * 1000) /* milliseconds */
76 #define SERVER_START_TIMEOUT (Preferences.timeOut * 3000) /* milliseconds */
77 #define REQUEST_TIMEOUT (Preferences.timeOut * 1000) /* milliseconds */
78 #define FILE_OPEN_TIMEOUT (Preferences.timeOut * 3000) /* milliseconds */
86 static void timeOutProc(Boolean
*timeOutReturn
, XtIntervalId
*id
);
87 static int startServer(const char *message
, const char *commandLine
);
88 static CommandLine
processCommandLine(int argc
, char** argv
);
89 static void parseCommandLine(int argc
, char **arg
, CommandLine
*cmdLine
);
90 static void nextArg(int argc
, char **argv
, int *argIndex
);
91 static void copyCommandLineArg(CommandLine
*cmdLine
, const char *arg
);
92 static void printNcVersion(void);
93 static Boolean
findExistingServer(XtAppContext context
,
95 Atom serverExistsAtom
);
96 static void startNewServer(XtAppContext context
,
99 Atom serverExistsAtom
);
100 static void waitUntilRequestProcessed(XtAppContext context
,
103 Atom serverRequestAtom
);
104 static void waitUntilFilesOpenedOrClosed(XtAppContext context
,
108 XtAppContext AppContext
;
109 static Atom currentWaitForAtom
;
110 static Atom noAtom
= (Atom
)(-1);
112 static const char cmdLineHelp
[] =
114 "[Sorry, no on-line help available.]\n"; /* Why is that ? */
116 "Usage: nc [-read] [-create]\n"
117 " [-line n | +n] [-do command] [-lm languagemode]\n"
118 " [-svrname name] [-svrcmd command]\n"
119 " [-ask] [-noask] [-timeout seconds]\n"
120 " [-geometry geometry | -g geometry] [-icon | -iconic]\n"
121 " [-tabbed] [-untabbed] [-group] [-wait]\n"
122 " [-V | -version] [-h|-help]\n"
123 " [-xrm resourcestring] [-display [host]:server[.screen]]\n"
127 /* Structure to hold X Resource values */
130 char serverCmd
[2*MAXPATHLEN
]; /* holds executable name + flags */
131 char serverName
[MAXPATHLEN
];
136 /* Application resources */
137 static PrefDescripRec PrefDescrip
[] = {
138 {"autoStart", "AutoStart", PREF_BOOLEAN
, "True",
139 &Preferences
.autoStart
, NULL
, True
},
140 {"serverCommand", "ServerCommand", PREF_STRING
, "nedit -server",
141 Preferences
.serverCmd
, (void *)sizeof(Preferences
.serverCmd
), False
},
142 {"serverName", "serverName", PREF_STRING
, "", Preferences
.serverName
,
143 (void *)sizeof(Preferences
.serverName
), False
},
144 {"waitForClose", "WaitForClose", PREF_BOOLEAN
, "False",
145 &Preferences
.waitForClose
, NULL
, False
},
146 {"timeOut", "TimeOut", PREF_INT
, "10",
147 &Preferences
.timeOut
, NULL
, False
}
150 /* Resource related command line options */
151 static XrmOptionDescRec OpTable
[] = {
152 {"-ask", ".autoStart", XrmoptionNoArg
, (caddr_t
)"False"},
153 {"-noask", ".autoStart", XrmoptionNoArg
, (caddr_t
)"True"},
154 {"-svrname", ".serverName", XrmoptionSepArg
, (caddr_t
)NULL
},
155 {"-svrcmd", ".serverCommand", XrmoptionSepArg
, (caddr_t
)NULL
},
156 {"-wait", ".waitForClose", XrmoptionNoArg
, (caddr_t
)"True"},
157 {"-timeout", ".timeOut", XrmoptionSepArg
, (caddr_t
)NULL
}
160 /* Struct to hold info about files being opened and edited. */
161 typedef struct _FileListEntry
{
162 Atom waitForFileOpenAtom
;
163 Atom waitForFileClosedAtom
;
165 struct _FileListEntry
*next
;
169 int waitForOpenCount
;
170 int waitForCloseCount
;
171 FileListEntry
* fileList
;
173 static FileListHead fileListHead
;
175 static void setPropertyValue(Atom atom
) {
176 XChangeProperty(TheDisplay
,
177 RootWindow(TheDisplay
, DefaultScreen(TheDisplay
)),
180 (unsigned char *)"True", 4);
183 /* Add another entry to the file entry list, if it doesn't exist yet. */
184 static void addToFileList(const char *path
)
188 /* see if the file already exists in the list */
189 for (item
= fileListHead
.fileList
; item
; item
= item
->next
) {
190 if (!strcmp(item
->path
, path
))
194 /* Add the atom to the head of the file list if it wasn't found. */
196 item
= malloc(sizeof(item
[0]));
197 item
->waitForFileOpenAtom
= None
;
198 item
->waitForFileClosedAtom
= None
;
199 item
->path
= (char*)malloc(strlen(path
)+1);
200 strcpy(item
->path
, path
);
201 item
->next
= fileListHead
.fileList
;
202 fileListHead
.fileList
= item
;
206 /* Creates the properties for the various paths */
207 static void createWaitProperties(void)
211 for (item
= fileListHead
.fileList
; item
; item
= item
->next
) {
212 fileListHead
.waitForOpenCount
++;
213 item
->waitForFileOpenAtom
=
214 CreateServerFileOpenAtom(Preferences
.serverName
, item
->path
);
215 setPropertyValue(item
->waitForFileOpenAtom
);
217 if (Preferences
.waitForClose
== True
) {
218 fileListHead
.waitForCloseCount
++;
219 item
->waitForFileClosedAtom
=
220 CreateServerFileClosedAtom(Preferences
.serverName
,
223 setPropertyValue(item
->waitForFileClosedAtom
);
228 int main(int argc
, char **argv
)
230 XtAppContext context
;
232 CommandLine commandLine
;
233 Atom serverExistsAtom
, serverRequestAtom
;
235 Boolean serverExists
;
237 /* Initialize toolkit and get an application context */
238 XtToolkitInitialize();
239 AppContext
= context
= XtCreateApplicationContext();
242 /* Convert the command line to Unix style */
243 ConvertVMSCommandLine(&argc
, &argv
);
246 /* expand wildcards if necessary */
247 _wildcard(&argc
, &argv
);
250 /* Read the preferences command line into a database (note that we
251 don't support the .nc file anymore) */
252 prefDB
= CreatePreferencesDatabase(NULL
, APP_CLASS
,
253 OpTable
, XtNumber(OpTable
), (unsigned *)&argc
, argv
);
255 /* Process the command line before calling XtOpenDisplay, because the
256 latter consumes certain command line arguments that we still need
257 (-icon, -geometry ...) */
258 commandLine
= processCommandLine(argc
, argv
);
260 /* Open the display and find the root window */
261 TheDisplay
= XtOpenDisplay (context
, NULL
, APP_NAME
, APP_CLASS
, NULL
,
264 XtWarning ("nc: Can't open display\n");
267 rootWindow
= RootWindow(TheDisplay
, DefaultScreen(TheDisplay
));
269 /* Read the application resources into the Preferences data structure */
270 RestorePreferences(prefDB
, XtDatabase(TheDisplay
), APP_NAME
,
271 APP_CLASS
, PrefDescrip
, XtNumber(PrefDescrip
));
273 /* Make sure that the time out unit is at least 1 second and not too
274 large either (overflow!). */
275 if (Preferences
.timeOut
< 1) {
276 Preferences
.timeOut
= 1;
277 } else if (Preferences
.timeOut
> 1000) {
278 Preferences
.timeOut
= 1000;
282 /* For Clearcase users who have not set a server name, use the clearcase
283 view name. Clearcase views make files with the same absolute path names
284 but different contents (and therefore can't be edited in the same nedit
285 session). This should have no bad side-effects for non-clearcase users */
286 if (Preferences
.serverName
[0] == '\0') {
287 const char* viewTag
= GetClearCaseViewTag();
288 if (viewTag
!= NULL
&& strlen(viewTag
) < MAXPATHLEN
) {
289 strcpy(Preferences
.serverName
, viewTag
);
294 /* Create the wait properties for the various files. */
295 createWaitProperties();
297 /* Monitor the properties on the root window */
298 XSelectInput(TheDisplay
, rootWindow
, PropertyChangeMask
);
300 /* Create the server property atoms on the current DISPLAY. */
301 CreateServerPropertyAtoms(Preferences
.serverName
,
305 serverExists
= findExistingServer(context
,
309 if (serverExists
== False
)
310 startNewServer(context
, rootWindow
, commandLine
.shell
, serverExistsAtom
);
312 waitUntilRequestProcessed(context
,
314 commandLine
.serverRequest
,
317 waitUntilFilesOpenedOrClosed(context
, rootWindow
);
319 XtCloseDisplay(TheDisplay
);
320 XtFree(commandLine
.shell
);
321 XtFree(commandLine
.serverRequest
);
327 ** Xt timer procedure for timeouts on NEdit server requests
329 static void timeOutProc(Boolean
*timeOutReturn
, XtIntervalId
*id
)
331 /* NOTE: XtAppNextEvent() does call this routine but
332 ** doesn't return unless there are more events.
333 ** Hence, we generate this (synthetic) event to break the deadlock
335 Window rootWindow
= RootWindow(TheDisplay
, DefaultScreen(TheDisplay
));
336 if (currentWaitForAtom
!= noAtom
) {
337 XChangeProperty(TheDisplay
, rootWindow
, currentWaitForAtom
, XA_STRING
,
338 8, PropModeReplace
, (unsigned char *)"", strlen(""));
341 /* Flag that the timeout has occurred. */
342 *timeOutReturn
= True
;
347 static Boolean
findExistingServer(XtAppContext context
,
349 Atom serverExistsAtom
)
351 Boolean serverExists
= True
;
352 unsigned char *propValue
;
355 unsigned long dummyULong
, nItems
;
357 /* See if there might be a server (not a guaranty), by translating the
358 root window property NEDIT_SERVER_EXISTS_<user>_<host> */
359 if (XGetWindowProperty(TheDisplay
, rootWindow
, serverExistsAtom
, 0,
360 INT_MAX
, False
, XA_STRING
, &dummyAtom
, &getFmt
, &nItems
,
361 &dummyULong
, &propValue
) != Success
|| nItems
== 0) {
362 serverExists
= False
;
364 Boolean timeOut
= False
;
365 XtIntervalId timerId
;
369 /* Remove the server exists property to make sure the server is
370 ** running. If it is running it will get recreated.
372 XDeleteProperty(TheDisplay
, rootWindow
, serverExistsAtom
);
373 XSync(TheDisplay
, False
);
374 timerId
= XtAppAddTimeOut(context
,
375 PROPERTY_CHANGE_TIMEOUT
,
376 (XtTimerCallbackProc
)timeOutProc
,
378 currentWaitForAtom
= serverExistsAtom
;
381 /* NOTE: XtAppNextEvent() does call the timeout routine but
382 ** doesn't return unless there are more events. */
384 const XPropertyEvent
*e
= (const XPropertyEvent
*)&event
;
385 XtAppNextEvent(context
, &event
);
387 /* We will get a PropertyNewValue when the server recreates
388 ** the server exists atom. */
389 if (e
->type
== PropertyNotify
&&
390 e
->window
== rootWindow
&&
391 e
->atom
== serverExistsAtom
) {
392 if (e
->state
== PropertyNewValue
) {
396 XtDispatchEvent(&event
);
399 /* Start a new server if the timeout expired. The server exists
400 ** property was not recreated. */
402 serverExists
= False
;
404 XtRemoveTimeOut(timerId
);
408 return(serverExists
);
414 static void startNewServer(XtAppContext context
,
417 Atom serverExistsAtom
)
419 Boolean timeOut
= False
;
420 XtIntervalId timerId
;
422 /* Add back the server name resource from the command line or resource
423 database to the command line for starting the server. If -svrcmd
424 appeared on the original command line, it was removed by
425 CreatePreferencesDatabase before the command line was recorded
426 in commandLine.shell. Moreover, if no server name was specified, it
427 may have defaulted to the ClearCase view tag. */
428 if (Preferences
.serverName
[0] != '\0') {
429 strcat(commandLine
, " -svrname ");
430 strcat(commandLine
, Preferences
.serverName
);
432 switch (startServer("No servers available, start one? (y|n) [y]: ",
435 case -1: /* Start failed */
436 XtCloseDisplay(TheDisplay
);
439 case -2: /* Start canceled by user */
440 XtCloseDisplay(TheDisplay
);
445 /* Set up a timeout proc in case the server is dead. The standard
446 selection timeout is probably a good guess at how long to wait
447 for this style of inter-client communication as well */
448 timerId
= XtAppAddTimeOut(context
,
449 SERVER_START_TIMEOUT
,
450 (XtTimerCallbackProc
)timeOutProc
,
452 currentWaitForAtom
= serverExistsAtom
;
454 /* Wait for the server to start */
457 const XPropertyEvent
*e
= (const XPropertyEvent
*)&event
;
458 /* NOTE: XtAppNextEvent() does call the timeout routine but
459 ** doesn't return unless there are more events. */
460 XtAppNextEvent(context
, &event
);
462 /* We will get a PropertyNewValue when the server updates
463 ** the server exists atom. If the property is deleted the
464 ** the server must have died. */
465 if (e
->type
== PropertyNotify
&&
466 e
->window
== rootWindow
&&
467 e
->atom
== serverExistsAtom
) {
468 if (e
->state
== PropertyNewValue
) {
470 } else if (e
->state
== PropertyDelete
) {
471 fprintf(stderr
, "%s: The server failed to start.\n", APP_NAME
);
472 XtCloseDisplay(TheDisplay
);
476 XtDispatchEvent(&event
);
478 /* Exit if the timeout expired. */
480 fprintf(stderr
, "%s: The server failed to start (time-out).\n", APP_NAME
);
481 XtCloseDisplay(TheDisplay
);
484 XtRemoveTimeOut(timerId
);
489 ** Prompt the user about starting a server, with "message", then start server
491 static int startServer(const char *message
, const char *commandLineArgs
)
493 char c
, *commandLine
;
495 int spawnFlags
= 1 /* + 1<<3 */; /* NOWAIT, NOKEYPAD */
497 struct dsc$descriptor_s
*cmdDesc
;
498 char *nulDev
= "NL:";
499 struct dsc$descriptor_s
*nulDevDesc
;
504 /* prompt user whether to start server */
505 if (!Preferences
.autoStart
) {
509 } while (c
== ' ' || c
== '\t');
510 if (c
!= 'Y' && c
!= 'y' && c
!= '\n')
514 /* start the server */
516 commandLine
= XtMalloc(strlen(Preferences
.serverCmd
) +
517 strlen(commandLineArgs
) + 3);
518 sprintf(commandLine
, "%s %s", Preferences
.serverCmd
, commandLineArgs
);
519 cmdDesc
= NulStrToDesc(commandLine
); /* build command descriptor */
520 nulDevDesc
= NulStrToDesc(nulDev
); /* build "NL:" descriptor */
521 spawn_sts
= lib$
spawn(cmdDesc
, nulDevDesc
, 0, &spawnFlags
, 0,0,0,0,0,0,0,0);
523 if (spawn_sts
!= SS$_NORMAL
) {
524 fprintf(stderr
, "Error return from lib$spawn: %d\n", spawn_sts
);
525 fprintf(stderr
, "Nedit server not started.\n");
528 FreeStrDesc(cmdDesc
);
532 #if defined(__EMX__) /* OS/2 */
533 /* Unfortunately system() calls a shell determined by the environment
534 variables COMSPEC and EMXSHELL. We have to figure out which one. */
536 char *sh_spec
, *sh
, *base
;
537 char *CMD_EXE
="cmd.exe";
539 commandLine
= XtMalloc(strlen(Preferences
.serverCmd
) +
540 strlen(commandLineArgs
) + 15);
541 sh
= getenv ("EMXSHELL");
543 sh
= getenv ("COMSPEC");
546 sh_spec
=XtNewString(sh
);
547 base
=_getname(sh_spec
);
549 if (stricmp (base
, "cmd") == 0 || stricmp (base
, "4os2") == 0) {
550 sprintf(commandLine
, "start /C /MIN %s %s",
551 Preferences
.serverCmd
, commandLineArgs
);
553 sprintf(commandLine
, "%s %s &",
554 Preferences
.serverCmd
, commandLineArgs
);
559 commandLine
= XtMalloc(strlen(Preferences
.serverCmd
) +
560 strlen(commandLineArgs
) + 3);
561 sprintf(commandLine
, "%s %s&", Preferences
.serverCmd
, commandLineArgs
);
564 sysrc
=system(commandLine
);
574 /* Reconstruct the command line in string commandLine in case we have to
575 * start a server (nc command line args parallel nedit's). Include
576 * -svrname if nc wants a named server, so nedit will match. Special
577 * characters are protected from the shell by escaping EVERYTHING with \
579 static CommandLine
processCommandLine(int argc
, char** argv
)
581 CommandLine commandLine
;
585 for (i
=1; i
<argc
; i
++) {
586 length
+= 1 + strlen(argv
[i
])*4 + 2;
588 commandLine
.shell
= XtMalloc(length
+1 + 9 + MAXPATHLEN
);
589 *commandLine
.shell
= '\0';
591 /* Convert command line arguments into a command string for the server */
592 parseCommandLine(argc
, argv
, &commandLine
);
593 if (commandLine
.serverRequest
== NULL
) {
594 fprintf(stderr
, "nc: Invalid commandline argument\n");
603 ** Converts command line into a command string suitable for passing to
606 static void parseCommandLine(int argc
, char **argv
, CommandLine
*commandLine
)
608 #define MAX_RECORD_HEADER_LENGTH 38
609 char name
[MAXPATHLEN
], path
[MAXPATHLEN
];
610 const char *toDoCommand
= "", *langMode
= "", *geometry
= "";
611 char *commandString
, *outPtr
;
612 int lineNum
= 0, read
= 0, create
= 0, iconic
= 0, tabbed
= -1, length
= 0;
613 int i
, lineArg
, nRead
, charsWritten
, opts
= True
;
614 int fileCount
= 0, group
= 0, isTabbed
;
616 /* Allocate a string for output, for the maximum possible length. The
617 maximum length is calculated by assuming every argument is a file,
618 and a complete record of maximum length is created for it */
619 for (i
=1; i
<argc
; i
++) {
620 length
+= MAX_RECORD_HEADER_LENGTH
+ strlen(argv
[i
]) + MAXPATHLEN
;
622 /* In case of no arguments, must still allocate space for one record header */
623 if (length
< MAX_RECORD_HEADER_LENGTH
)
625 length
= MAX_RECORD_HEADER_LENGTH
;
627 commandString
= XtMalloc(length
+1);
629 /* Parse the arguments and write the output string */
630 outPtr
= commandString
;
631 for (i
=1; i
<argc
; i
++) {
632 if (opts
&& !strcmp(argv
[i
], "--")) {
633 opts
= False
; /* treat all remaining arguments as filenames */
635 } else if (opts
&& !strcmp(argv
[i
], "-do")) {
636 nextArg(argc
, argv
, &i
);
637 toDoCommand
= argv
[i
];
638 } else if (opts
&& !strcmp(argv
[i
], "-lm")) {
639 copyCommandLineArg(commandLine
, argv
[i
]);
640 nextArg(argc
, argv
, &i
);
642 copyCommandLineArg(commandLine
, argv
[i
]);
643 } else if (opts
&& (!strcmp(argv
[i
], "-g") ||
644 !strcmp(argv
[i
], "-geometry"))) {
645 copyCommandLineArg(commandLine
, argv
[i
]);
646 nextArg(argc
, argv
, &i
);
648 copyCommandLineArg(commandLine
, argv
[i
]);
649 } else if (opts
&& !strcmp(argv
[i
], "-read")) {
651 } else if (opts
&& !strcmp(argv
[i
], "-create")) {
653 } else if (opts
&& !strcmp(argv
[i
], "-tabbed")) {
655 group
= 0; /* override -group option */
656 } else if (opts
&& !strcmp(argv
[i
], "-untabbed")) {
658 group
= 0; /* override -group option */
659 } else if (opts
&& !strcmp(argv
[i
], "-group")) {
660 group
= 2; /* 2: start new group, 1: in group */
661 } else if (opts
&& (!strcmp(argv
[i
], "-iconic") ||
662 !strcmp(argv
[i
], "-icon"))) {
664 copyCommandLineArg(commandLine
, argv
[i
]);
665 } else if (opts
&& !strcmp(argv
[i
], "-line")) {
666 nextArg(argc
, argv
, &i
);
667 nRead
= sscanf(argv
[i
], "%d", &lineArg
);
669 fprintf(stderr
, "nc: argument to line should be a number\n");
672 } else if (opts
&& (*argv
[i
] == '+')) {
673 nRead
= sscanf((argv
[i
]+1), "%d", &lineArg
);
675 fprintf(stderr
, "nc: argument to + should be a number\n");
678 } else if (opts
&& (!strcmp(argv
[i
], "-ask") || !strcmp(argv
[i
], "-noask"))) {
679 ; /* Ignore resource-based arguments which are processed later */
680 } else if (opts
&& (!strcmp(argv
[i
], "-svrname") ||
681 !strcmp(argv
[i
], "-svrcmd"))) {
682 nextArg(argc
, argv
, &i
); /* Ignore rsrc args with data */
683 } else if (opts
&& (!strcmp(argv
[i
], "-xrm") ||
684 !strcmp(argv
[i
], "-display"))) {
685 copyCommandLineArg(commandLine
, argv
[i
]);
686 nextArg(argc
, argv
, &i
); /* Ignore rsrc args with data */
687 copyCommandLineArg(commandLine
, argv
[i
]);
688 } else if (opts
&& (!strcmp(argv
[i
], "-version") || !strcmp(argv
[i
], "-V"))) {
691 } else if (opts
&& (!strcmp(argv
[i
], "-h") ||
692 !strcmp(argv
[i
], "-help"))) {
693 fprintf(stderr
, "%s", cmdLineHelp
);
695 } else if (opts
&& (*argv
[i
] == '-')) {
699 fprintf(stderr
, "nc: Unrecognized option %s\n%s", argv
[i
],
704 int numFiles
, j
, oldLength
;
705 char **nameList
= NULL
, *newCommandString
;
706 /* Use VMS's LIB$FILESCAN for filename in argv[i] to process */
707 /* wildcards and to obtain a full VMS file specification */
708 numFiles
= VMSFileScan(argv
[i
], &nameList
, NULL
, INCLUDE_FNF
);
709 /* for each expanded file name do: */
710 for (j
= 0; j
< numFiles
; ++j
) {
711 oldLength
= outPtr
-commandString
;
712 newCommandString
= XtMalloc(oldLength
+length
+1);
713 strncpy(newCommandString
, commandString
, oldLength
);
714 XtFree(commandString
);
715 commandString
= newCommandString
;
716 outPtr
= newCommandString
+ oldLength
;
717 if (ParseFilename(nameList
[j
], name
, path
) != 0) {
718 /* An Error, most likely too long paths/strings given */
719 commandLine
->serverRequest
= NULL
;
724 /* determine if file is to be openned in new tab, by
725 factoring the options -group, -tabbed & -untabbed */
727 isTabbed
= 0; /* start a new window for new group */
728 group
= 1; /* next file will be within group */
730 else if (group
== 1) {
731 isTabbed
= 1; /* new tab for file in group */
734 isTabbed
= tabbed
; /* not in group */
737 /* See below for casts */
738 sprintf(outPtr
, "%d %d %d %d %d %ld %ld %ld %ld\n%s\n%s\n%s\n%s\n%n",
739 lineNum
, read
, create
, iconic
, tabbed
, (long) strlen(path
),
740 (long) strlen(toDoCommand
), (long) strlen(langMode
),
741 (long) strlen(geometry
),
742 path
, toDoCommand
, langMode
, geometry
, &charsWritten
);
743 outPtr
+= charsWritten
;
746 /* Create the file open atoms for the paths supplied */
750 if (nameList
!= NULL
)
753 if (ParseFilename(argv
[i
], name
, path
) != 0) {
754 /* An Error, most likely too long paths/strings given */
755 commandLine
->serverRequest
= NULL
;
760 /* determine if file is to be openned in new tab, by
761 factoring the options -group, -tabbed & -untabbed */
763 isTabbed
= 0; /* start a new window for new group */
764 group
= 1; /* next file will be within group */
766 else if (group
== 1) {
767 isTabbed
= 1; /* new tab for file in group */
770 isTabbed
= tabbed
; /* not in group */
773 /* SunOS 4 acc or acc and/or its runtime library has a bug
774 such that %n fails (segv) if it follows a string in a
775 printf or sprintf. The silly code below avoids this.
777 The "long" cast on strlen() is necessary because size_t
778 is 64 bit on Alphas, and 32-bit on most others. There is
779 no printf format specifier for "size_t", thanx, ANSI. */
780 sprintf(outPtr
, "%d %d %d %d %d %ld %ld %ld %ld\n%n", lineNum
,
781 read
, create
, iconic
, isTabbed
, (long) strlen(path
),
782 (long) strlen(toDoCommand
), (long) strlen(langMode
),
783 (long) strlen(geometry
), &charsWritten
);
784 outPtr
+= charsWritten
;
785 strcpy(outPtr
, path
);
786 outPtr
+= strlen(path
);
788 strcpy(outPtr
, toDoCommand
);
789 outPtr
+= strlen(toDoCommand
);
791 strcpy(outPtr
, langMode
);
792 outPtr
+= strlen(langMode
);
794 strcpy(outPtr
, geometry
);
795 outPtr
+= strlen(geometry
);
799 /* Create the file open atoms for the paths supplied */
809 /* If there's an un-written -do command, or we are to open a new window,
810 * or user has requested iconic state, but not provided a file name,
811 * create a server request with an empty file name and requested
812 * iconic state (and optional language mode and geometry).
814 if (toDoCommand
[0] != '\0' || fileCount
== 0) {
815 sprintf(outPtr
, "0 0 0 %d %d 0 %ld %ld %ld\n\n%n", iconic
, tabbed
,
816 (long) strlen(toDoCommand
),
817 (long) strlen(langMode
), (long) strlen(geometry
), &charsWritten
);
818 outPtr
+= charsWritten
;
819 strcpy(outPtr
, toDoCommand
);
820 outPtr
+= strlen(toDoCommand
);
822 strcpy(outPtr
, langMode
);
823 outPtr
+= strlen(langMode
);
825 strcpy(outPtr
, geometry
);
826 outPtr
+= strlen(geometry
);
831 commandLine
->serverRequest
= commandString
;
835 static void waitUntilRequestProcessed(XtAppContext context
,
838 Atom serverRequestAtom
)
840 XtIntervalId timerId
;
841 Boolean timeOut
= False
;
843 /* Set the NEDIT_SERVER_REQUEST_<user>_<host> property on the root
844 window to activate the server */
845 XChangeProperty(TheDisplay
, rootWindow
, serverRequestAtom
, XA_STRING
, 8,
846 PropModeReplace
, (unsigned char *)commandString
,
847 strlen(commandString
));
849 /* Set up a timeout proc in case the server is dead. The standard
850 selection timeout is probably a good guess at how long to wait
851 for this style of inter-client communication as well */
852 timerId
= XtAppAddTimeOut(context
,
854 (XtTimerCallbackProc
)timeOutProc
,
856 currentWaitForAtom
= serverRequestAtom
;
858 /* Wait for the property to be deleted to know the request was processed */
861 const XPropertyEvent
*e
= (const XPropertyEvent
*)&event
;
863 XtAppNextEvent(context
, &event
);
864 if (e
->window
== rootWindow
&&
865 e
->atom
== serverRequestAtom
&&
866 e
->state
== PropertyDelete
)
868 XtDispatchEvent(&event
);
871 /* Exit if the timeout expired. */
873 fprintf(stderr
, "%s: The server did not respond to the request.\n", APP_NAME
);
874 XtCloseDisplay(TheDisplay
);
877 XtRemoveTimeOut(timerId
);
881 static void waitUntilFilesOpenedOrClosed(XtAppContext context
,
884 XtIntervalId timerId
;
885 Boolean timeOut
= False
;
887 /* Set up a timeout proc so we don't wait forever if the server is dead.
888 The standard selection timeout is probably a good guess at how
889 long to wait for this style of inter-client communication as
891 timerId
= XtAppAddTimeOut(context
, FILE_OPEN_TIMEOUT
,
892 (XtTimerCallbackProc
)timeOutProc
, &timeOut
);
893 currentWaitForAtom
= noAtom
;
895 /* Wait for all of the windows to be opened by server,
896 * and closed if -wait was supplied */
897 while (fileListHead
.fileList
) {
899 const XPropertyEvent
*e
= (const XPropertyEvent
*)&event
;
901 XtAppNextEvent(context
, &event
);
903 /* Update the fileList and check if all files have been closed. */
904 if (e
->type
== PropertyNotify
&& e
->window
== rootWindow
) {
907 if (e
->state
== PropertyDelete
) {
908 for (item
= fileListHead
.fileList
; item
; item
= item
->next
) {
909 if (e
->atom
== item
->waitForFileOpenAtom
) {
910 /* The 'waitForFileOpen' property is deleted when the file is opened */
911 fileListHead
.waitForOpenCount
--;
912 item
->waitForFileOpenAtom
= None
;
914 /* Reset the timer while we wait for all files to be opened. */
915 XtRemoveTimeOut(timerId
);
916 timerId
= XtAppAddTimeOut(context
, FILE_OPEN_TIMEOUT
,
917 (XtTimerCallbackProc
)timeOutProc
, &timeOut
);
918 } else if (e
->atom
== item
->waitForFileClosedAtom
) {
919 /* When file is opened in -wait mode the property
920 * is deleted when the file is closed.
922 fileListHead
.waitForCloseCount
--;
923 item
->waitForFileClosedAtom
= None
;
927 if (fileListHead
.waitForOpenCount
== 0 && !timeOut
) {
928 XtRemoveTimeOut(timerId
);
931 if (fileListHead
.waitForOpenCount
== 0 &&
932 fileListHead
.waitForCloseCount
== 0) {
938 /* We are finished if we are only waiting for files to open and
939 ** the file open timeout has expired. */
940 if (!Preferences
.waitForClose
&& timeOut
) {
944 XtDispatchEvent(&event
);
949 static void nextArg(int argc
, char **argv
, int *argIndex
)
951 if (*argIndex
+ 1 >= argc
) {
953 *argv
[*argIndex
] = '/';
955 fprintf(stderr
, "nc: %s requires an argument\n%s",
956 argv
[*argIndex
], cmdLineHelp
);
962 /* Copies a given nc command line argument to the server startup command
963 ** line (-icon, -geometry, -xrm, ...) Special characters are protected from
964 ** the shell by escaping EVERYTHING with \
965 ** Note that the .shell string in the command line structure is large enough
966 ** to hold the escaped characters.
968 static void copyCommandLineArg(CommandLine
*commandLine
, const char *arg
)
971 char *outPtr
= commandLine
->shell
+ strlen(commandLine
->shell
);
972 #if defined(VMS) || defined(__EMX__)
973 /* Non-Unix shells don't want/need esc */
974 for (c
=arg
; *c
!='\0'; c
++) {
981 for (c
=arg
; *c
!='\0'; c
++) {
997 /* Print version of 'nc' */
998 static void printNcVersion(void ) {
999 static const char *const ncHelpText
= \
1000 "nc (NEdit) Version 5.5 (October 2004)\n\n\
1001 Built on: %s, %s, %s\n\
1002 Built at: %s, %s\n";
1004 fprintf(stdout
, ncHelpText
,
1005 COMPILE_OS
, COMPILE_MACHINE
, COMPILE_COMPILER
,
1006 __DATE__
, __TIME__
);