3 * Copyright (c) 1992-2001 by Mike Gleason.
6 * Note: It should still be simple to backport the old GNU Readline
7 * support in here. Feel free to do that if you hate NcFTP's built-in
23 const char *tcap_normal
= "";
24 const char *tcap_boldface
= "";
25 const char *tcap_underline
= "";
26 const char *tcap_reverse
= "";
29 int gXtermTitle
; /* Idea by forsberg@lysator.liu.se */
30 char gCurXtermTitleStr
[256];
32 #if (defined(WIN32) || defined(_WINDOWS)) && defined(_CONSOLE)
33 char gSavedConsoleTitle
[64];
36 extern int gEventNumber
;
37 extern int gMaySetXtermTitle
;
38 extern LsCacheItem gLsCache
[kLsCacheSize
];
39 extern FTPConnectionInfo gConn
;
40 extern char gRemoteCWD
[512];
41 extern char gOurDirectoryPath
[];
42 extern char gVersion
[];
43 extern int gNumBookmarks
;
44 extern BookmarkPtr gBookmarkTable
;
45 extern PrefOpt gPrefOpts
[];
46 extern int gNumPrefOpts
;
47 extern int gScreenColumns
;
55 GetScreenColumns(void)
57 #if defined(WIN32) || defined(_WINDOWS)
58 CONSOLE_SCREEN_BUFFER_INFO csbi
;
60 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE
), &csbi
)) {
61 gScreenColumns
= (int) csbi
.dwSize
.X
;
62 if (gScreenColumns
< 80)
67 char ncftpbookmarks
[256];
74 if ((cp
= (char *) getenv("COLUMNS")) == NULL
) {
77 gScreenColumns
= atoi(cp
);
85 memset(&felix
, 0, sizeof(felix
));
86 if (ioctl(0, TIOCGWINSZ
, &felix
) == 0) {
87 columns
= felix
.ws_col
;
88 if ((columns
> 0) && (columns
< GL_BUF_SIZE
))
89 gScreenColumns
= columns
;
98 /* Don't run things as root unless really necessary. */
102 /* This is a brutal hack where we've hacked a
103 * special command line option into ncftp_bookmarks
104 * (which is linked with curses) so that it computes
105 * the screen size and prints it to stdout.
107 * This function runs ncftp_bookmarks and gets
108 * that information. The reason we do this is that
109 * we may or may not have a sane installation of
110 * curses/termcap, and we don't want to increase
111 * NcFTP's complexity by the curses junk just to
112 * get the screen size. Instead, we delegate this
113 * to ncftp_bookmarks which already deals with the
114 * ugliness of curses.
117 STRNCPY(ncftpbookmarks
, BINDIR
);
118 STRNCAT(ncftpbookmarks
, "/");
119 STRNCAT(ncftpbookmarks
, "ncftpbookmarks");
121 if (access(ncftpbookmarks
, X_OK
) < 0)
124 STRNCAT(ncftpbookmarks
, " --dimensions-terse");
126 osigpipe
= NcSignal(SIGPIPE
, SIG_IGN
);
127 infp
= popen(ncftpbookmarks
, "r");
130 (void) fscanf(infp
, "%d", &columns
);
131 while (getc(infp
) != EOF
) {}
134 if ((columns
> 0) && (columns
< GL_BUF_SIZE
))
135 gScreenColumns
= columns
;
137 (void) NcSignal(SIGPIPE
, (sigproc_t
) osigpipe
);
140 } /* GetScreenColumns */
144 /* For a few selected terminal types, we'll print in boldface, etc.
145 * This isn't too important, though.
150 #if (defined(WIN32) || defined(_WINDOWS)) && defined(_CONSOLE)
151 gXterm
= gXtermTitle
= 0;
152 gCurXtermTitleStr
[0] = '\0';
159 gTerm
= "MS-DOS Prompt";
160 ZeroMemory(gSavedConsoleTitle
, (DWORD
) sizeof(gSavedConsoleTitle
));
161 GetConsoleTitle(gSavedConsoleTitle
, (DWORD
) sizeof(gSavedConsoleTitle
) - 1);
162 SetConsoleTitle("NcFTP");
163 gXterm
= gXtermTitle
= 1;
167 gXterm
= gXtermTitle
= 0;
168 gCurXtermTitleStr
[0] = '\0';
170 if ((gTerm
= getenv("TERM")) == NULL
) {
179 if ( (strstr(term
, "xterm") != NULL
) ||
180 (strstr(term
, "rxvt") != NULL
) ||
181 (strstr(term
, "dtterm") != NULL
) ||
182 (ISTRCMP(term
, "scoterm") == 0)
184 gXterm
= gXtermTitle
= 1;
187 if ( (gXterm
!= 0) ||
188 (strcmp(term
, "vt100") == 0) ||
189 (strcmp(term
, "linux") == 0) ||
190 (strcmp(term
, "vt220") == 0) ||
191 (strcmp(term
, "vt102") == 0)
193 tcap_normal
= "\033[0m"; /* Default ANSI escapes */
194 tcap_boldface
= "\033[1m";
195 tcap_underline
= "\033[4m";
196 tcap_reverse
= "\033[7m";
210 FindStartOfCurrentCommand(void)
216 for (scp
= gl_buf
;;) {
221 if (!isspace((int) *scp
))
230 } else if ((*scp
== '"') || (*scp
== '\'')) {
236 } else if (*scp
== '\\') {
241 } else if (*scp
== qc
) {
248 } else if (*scp
== '\\') {
253 } else if ((*scp
== ';') || (*scp
== '\n')) {
266 } /* FindStartOfCurrentCommand */
270 static FileInfoListPtr
271 GetLsCacheFileList(const char *const item
)
276 FileInfoListPtr filp
;
278 ci
= LsCacheLookup(item
);
280 /* This dir was not in the
281 * cache -- go get it.
283 Ls(item
, 'l', "", NULL
);
284 ci
= LsCacheLookup(item
);
289 sortBy
= 'n'; /* Sort by filename. */
290 sortOrder
= 'a'; /* Sort in ascending order. */
291 filp
= &gLsCache
[ci
].fil
;
292 SortFileInfoList(filp
, sortBy
, sortOrder
);
294 } /* GetLsCacheFileList */
300 RemoteCompletionFunction(const char *text
, int state
, int fTypeFilter
)
305 const char *textbasename
;
307 FileInfoPtr diritemp
;
308 FileInfoListPtr filp
;
312 static FileInfoVec diritemv
;
315 textbasename
= strrchr(text
, '/');
316 if (textbasename
== NULL
) {
320 textdirlen
= (int) (textbasename
- text
);
323 tbnlen
= strlen(textbasename
);
326 if (text
[0] == '\0') {
327 /* Special case when they do "get <TAB><TAB> " */
328 STRNCPY(rpath
, gRemoteCWD
);
330 PathCat(rpath
, sizeof(rpath
), gRemoteCWD
, text
);
331 if (text
[strlen(text
) - 1] == '/') {
332 /* Special case when they do "get /dir1/dir2/<TAB><TAB>" */
335 cp2
= strrchr(rpath
, '/');
338 } else if (cp2
== rpath
) {
339 /* Item in root directory. */
345 filp
= GetLsCacheFileList(rpath
);
349 diritemv
= filp
->vec
;
350 if (diritemv
== NULL
)
357 diritemp
= diritemv
[i
];
358 if (diritemp
== NULL
)
362 fType
= (int) diritemp
->type
;
363 if ((fTypeFilter
== 0) || (fType
== fTypeFilter
) || (fType
== /* symlink */ 'l')) {
364 if (strncmp(textbasename
, diritemp
->relname
, tbnlen
) == 0) {
365 flen
= strlen(diritemp
->relname
);
366 if (textdirlen
< 0) {
368 cp
= (char *) malloc(mlen
);
371 (void) memcpy(cp
, diritemp
->relname
, mlen
);
373 mlen
= textdirlen
+ 1 + flen
+ 2;
374 cp
= (char *) malloc(mlen
);
377 (void) memcpy(cp
, text
, (size_t) textdirlen
);
378 cp
[textdirlen
] = '/';
379 (void) strcpy(cp
+ textdirlen
+ 1, diritemp
->relname
);
382 gl_completion_exact_match_extra_char
= '/';
384 gl_completion_exact_match_extra_char
= ' ';
391 } /* RemoteCompletionFunction */
397 RemoteFileCompletionFunction(const char *text
, int state
)
401 cp
= RemoteCompletionFunction(text
, state
, 0);
403 } /* RemoteFileCompletionFunction */
409 RemoteDirCompletionFunction(const char *text
, int state
)
413 cp
= RemoteCompletionFunction(text
, state
, 'd');
415 } /* RemoteDirCompletionFunction */
421 BookmarkCompletionFunction(const char *text
, int state
)
427 if ((gBookmarkTable
== NULL
) || (state
>= gNumBookmarks
))
430 textlen
= strlen(text
);
432 cp
= StrDup(gBookmarkTable
[state
].bookmarkName
);
435 for (i
=0, matches
=0; i
<gNumBookmarks
; i
++) {
436 if (ISTRNCMP(gBookmarkTable
[i
].bookmarkName
, text
, textlen
) == 0) {
437 if (matches
>= state
) {
438 cp
= StrDup(gBookmarkTable
[i
].bookmarkName
);
446 } /* BookmarkCompletionFunction */
452 CommandCompletionFunction(const char *text
, int state
)
459 textlen
= strlen(text
);
464 for (i
=0, matches
=0; ; i
++) {
465 cmdp
= GetCommandByIndex(i
);
466 if (cmdp
== kNoCommand
)
468 if (ISTRNCMP(cmdp
->name
, text
, textlen
) == 0) {
469 if (matches
>= state
) {
470 cp
= StrDup(cmdp
->name
);
478 } /* CommandCompletionFunction */
484 PrefOptCompletionFunction(const char *text
, int state
)
490 if (state
>= gNumPrefOpts
)
493 textlen
= strlen(text
);
495 cp
= StrDup(gPrefOpts
[state
].varname
);
498 for (i
=0, matches
=0; i
<gNumPrefOpts
; i
++) {
499 if (ISTRNCMP(gPrefOpts
[i
].varname
, text
, textlen
) == 0) {
500 if (matches
>= state
) {
501 cp
= StrDup(gPrefOpts
[i
].varname
);
509 } /* PrefOptCompletionFunction */
515 ReCacheBookmarks(void)
517 (void) LoadBookmarkTable();
523 HaveCommandNameOnly(char *cmdstart
)
526 for (cp
= cmdstart
; *cp
!= '\0'; cp
++) {
527 if (isspace((int) *cp
))
528 return (0); /* At least one argument in progress. */
531 } /* HaveCommandNameOnly */
537 CompletionFunction(const char *text
, int state
)
548 cmdstart
= FindStartOfCurrentCommand();
549 if (cmdstart
== NULL
)
551 if (HaveCommandNameOnly(cmdstart
)) {
552 flags
= -2; /* special case */
553 cp
= CommandCompletionFunction(text
, state
);
557 (void) memset(&ai
, 0, sizeof(ai
));
558 bUsed
= MakeArgv(cmdstart
, &ai
.cargc
, ai
.cargv
,
559 (int) (sizeof(ai
.cargv
) / sizeof(char *)),
560 ai
.argbuf
, sizeof(ai
.argbuf
),
567 cmdp
= GetCommandByName(ai
.cargv
[0], 0);
568 if (cmdp
== kAmbiguousCommand
) {
570 } else if (cmdp
== kNoCommand
) {
576 cp
= CommandCompletionFunction(text
, state
);
581 if ((flags
& (kCompleteLocalFile
|kCompleteLocalDir
)) != 0) {
582 cp
= gl_local_filename_completion_proc(text
, state
);
584 } else if ((flags
& kCompleteRemoteFile
) != 0) {
585 gl_filename_quoting_desired
= 1;
586 cp
= RemoteFileCompletionFunction(text
, state
);
588 } else if ((flags
& kCompleteRemoteDir
) != 0) {
589 gl_filename_quoting_desired
= 1;
590 cp
= RemoteDirCompletionFunction(text
, state
);
592 } else if ((flags
& kCompleteBookmark
) != 0) {
593 cp
= BookmarkCompletionFunction(text
, state
);
595 } else if ((flags
& kCompletePrefOpt
) != 0) {
596 cp
= PrefOptCompletionFunction(text
, state
);
600 } /* CompletionFunction */
610 if (gOurDirectoryPath
[0] == '\0')
612 (void) OurDirectoryPath(pathName
, sizeof(pathName
), kHistoryFileName
);
614 gl_histloadfile(pathName
);
620 Vt100VisibleStrlen(const char *src
)
625 for (esc
= 0, cp
= src
; *cp
!= '\0'; cp
++) {
630 /* The VT100 escape codes we use are all in the form "\033[7m"
631 * These aren't visible, so subtract them from the count.
633 return ((size_t) (cp
- src
) - (esc
* 4));
634 } /* Vt100VisibleStrlen */
638 /* Save the commands they typed in a history file, then they can use
639 * readline to go through them again next time.
646 if (gOurDirectoryPath
[0] == '\0')
647 return; /* Don't create in root directory. */
648 (void) OurDirectoryPath(pathName
, sizeof(pathName
), kHistoryFileName
);
650 gl_strlen
= Vt100VisibleStrlen
;
651 gl_histsavefile(pathName
);
652 (void) chmod(pathName
, 00600);
661 gl_completion_proc
= CompletionFunction
;
662 gl_setwidth(gScreenColumns
);
671 Readline(char *prompt
)
673 char *linecopy
, *line
, *cp
;
677 line
= getline(prompt
);
679 line
= fgets(lbuf
, sizeof(lbuf
) - 1, stdin
);
681 cp
= line
+ strlen(line
) - 1;
689 return NULL
; /* EOF */
690 linecopy
= StrDup(line
);
699 AddHistory(char *line
)
707 DisposeReadline(void)
710 } /* DisposeReadline */
718 SetXtermTitle(const char *const fmt
, ...)
723 if ((gXtermTitle
!= 0) && (gMaySetXtermTitle
!= 0)) {
724 if ((fmt
== NULL
) || (ISTRCMP(fmt
, "RESTORE") == 0)) {
725 #if (defined(WIN32) || defined(_WINDOWS)) && defined(_CONSOLE)
726 STRNCPY(buf
, gSavedConsoleTitle
);
730 } else if (ISTRCMP(fmt
, "DEFAULT") == 0) {
731 (void) Strncpy(buf
, gVersion
+ 5, 12);
734 #ifdef HAVE_VSNPRINTF
735 (void) vsnprintf(buf
, sizeof(buf
) - 1, fmt
, ap
);
736 buf
[sizeof(buf
) - 1] = '\0';
738 (void) vsprintf(buf
, fmt
, ap
);
742 if (buf
[0] != '\0') {
743 #if (defined(WIN32) || defined(_WINDOWS)) && defined(_CONSOLE)
744 SetConsoleTitle(buf
);
746 if (strcmp(gCurXtermTitleStr
, buf
) != 0) {
747 fprintf(stderr
, "\033]0;%s\007", buf
);
748 STRNCPY(gCurXtermTitleStr
, buf
);
753 } /* SetXtermTitle */
759 PrintStartupBanner(void)
764 /* Print selected information from the version ID. */
766 (void) STRNCPY(v
, gVersion
+ 5);
771 (void) STRNCPY(vdate
, " (");
772 (void) STRNCAT(vdate
, v
+ 16);
773 (void) STRNCAT(vdate
, ", ");
774 (void) STRNCAT(vdate
, cp
- 4);
775 (void) STRNCAT(vdate
, ")");
778 #if defined(BETA) && (BETA > 0)
779 (void) fprintf(stdout
, "%s%.11s beta %d%s%s by Mike Gleason (ncftp@ncftp.com).\n",
787 (void) fprintf(stdout
, "%s%.11s%s%s by Mike Gleason (ncftp@ncftp.com).\n",
794 (void) fflush(stdout
);
795 } /* PrintStartupBanner */
800 /* Print the command shell's prompt. */
802 MakePrompt(char *dst
, size_t dsize
)
806 # ifdef HAVE_SNPRINTF
807 if (gConn
.loggedIn
!= 0) {
808 AbbrevStr(acwd
, gRemoteCWD
, 25, 0);
809 snprintf(dst
, dsize
, "%sncftp%s %s %s>%s ",
810 tcap_boldface
, tcap_normal
, acwd
,
811 tcap_boldface
, tcap_normal
);
813 snprintf(dst
, dsize
, "%sncftp%s> ",
814 tcap_boldface
, tcap_normal
);
816 # else /* HAVE_SNPRINTF */
817 (void) Strncpy(dst
, tcap_boldface
, dsize
);
818 (void) Strncat(dst
, "ncftp", dsize
);
819 (void) Strncat(dst
, tcap_normal
, dsize
);
820 if (gConn
.loggedIn
!= 0) {
821 AbbrevStr(acwd
, gRemoteCWD
, 25, 0);
822 (void) Strncat(dst
, " ", dsize
);
823 (void) Strncat(dst
, acwd
, dsize
);
824 (void) Strncat(dst
, " ", dsize
);
826 (void) Strncat(dst
, tcap_boldface
, dsize
);
827 (void) Strncat(dst
, ">", dsize
);
828 (void) Strncat(dst
, tcap_normal
, dsize
);
829 (void) Strncat(dst
, " ", dsize
);
830 # endif /* HAVE_SNPRINTF */