1 /* rdline.c
2  *
3  * Copyright (c) 1992-2001 by Mike Gleason.
4  * All rights reserved.
5  *
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
8  * implementation.
9  *
10  */
11 
12 #include "syshdrs.h"
13 
14 #include "shell.h"
15 #include "util.h"
16 #include "bookmark.h"
17 #include "cmds.h"
18 #include "pref.h"
19 #include "ls.h"
20 #include "readln.h"
21 #include "getline.h"
22 
23 const char *tcap_normal = "";
24 const char *tcap_boldface = "";
25 const char *tcap_underline = "";
26 const char *tcap_reverse = "";
27 const char *gTerm;
28 int gXterm;
29 int gXtermTitle;	/* Idea by forsberg@lysator.liu.se */
30 char gCurXtermTitleStr[256];
31 
32 #if (defined(WIN32) || defined(_WINDOWS)) && defined(_CONSOLE)
33 	char gSavedConsoleTitle[64];
34 #endif
35 
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;
48 extern int gIsTTYr;
49 extern int gUid;
50 
51 
52 
53 
54 void
GetScreenColumns(void)55 GetScreenColumns(void)
56 {
57 #if defined(WIN32) || defined(_WINDOWS)
58 	CONSOLE_SCREEN_BUFFER_INFO csbi;
59 
60 	if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
61 		gScreenColumns = (int) csbi.dwSize.X;
62 		if (gScreenColumns < 80)
63 			gScreenColumns = 80;
64 	}
65 #else	/* Unix */
66 #ifdef BINDIR
67 	char ncftpbookmarks[256];
68 	FILE *infp;
69 	vsigproc_t osigpipe;
70 	int columns;
71 #endif	/* BINDIR */
72 	char *cp;
73 
74 	if ((cp = (char *) getenv("COLUMNS")) == NULL) {
75 		gScreenColumns = 80;
76 	} else {
77 		gScreenColumns = atoi(cp);
78 		return;
79 	}
80 
81 #ifdef TIOCGWINSZ
82 	{
83 		struct winsize felix;
84 
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;
90 			else
91 				gScreenColumns = 80;
92 			return;
93 		}
94 	}
95 #endif
96 
97 #ifdef BINDIR
98 	/* Don't run things as root unless really necessary. */
99 	if (gUid == 0)
100 		return;
101 
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.
106 	 *
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.
115 	 */
116 
117 	STRNCPY(ncftpbookmarks, BINDIR);
118 	STRNCAT(ncftpbookmarks, "/");
119 	STRNCAT(ncftpbookmarks, "ncftpbookmarks");
120 
121 	if (access(ncftpbookmarks, X_OK) < 0)
122 		return;
123 
124 	STRNCAT(ncftpbookmarks, " --dimensions-terse");
125 
126 	osigpipe = NcSignal(SIGPIPE, SIG_IGN);
127 	infp = popen(ncftpbookmarks, "r");
128 	if (infp != NULL) {
129 		columns = 0;
130 		(void) fscanf(infp, "%d", &columns);
131 		while (getc(infp) != EOF) {}
132 		(void) pclose(infp);
133 
134 		if ((columns > 0) && (columns < GL_BUF_SIZE))
135 			gScreenColumns = columns;
136 	}
137 	(void) NcSignal(SIGPIPE, (sigproc_t) osigpipe);
138 #endif	/* BINDIR */
139 #endif	/* Windows */
140 }	/* GetScreenColumns */
141 
142 
143 
144 /* For a few selected terminal types, we'll print in boldface, etc.
145  * This isn't too important, though.
146  */
147 void
InitTermcap(void)148 InitTermcap(void)
149 {
150 #if (defined(WIN32) || defined(_WINDOWS)) && defined(_CONSOLE)
151 	gXterm = gXtermTitle = 0;
152 	gCurXtermTitleStr[0] = '\0';
153 
154 	tcap_normal = "";
155 	tcap_boldface = "";
156 	tcap_underline = "";
157 	tcap_reverse = "";
158 
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;
164 #else
165 	const char *term;
166 
167 	gXterm = gXtermTitle = 0;
168 	gCurXtermTitleStr[0] = '\0';
169 
170 	if ((gTerm = getenv("TERM")) == NULL) {
171 		tcap_normal = "";
172 		tcap_boldface = "";
173 		tcap_underline = "";
174 		tcap_reverse = "";
175 		return;
176 	}
177 
178 	term = gTerm;
179 	if (	(strstr(term, "xterm") != NULL) ||
180 		(strstr(term, "rxvt") != NULL) ||
181 		(strstr(term, "dtterm") != NULL) ||
182 		(ISTRCMP(term, "scoterm") == 0)
183 	) {
184 		gXterm = gXtermTitle = 1;
185 	}
186 
187 	if (	(gXterm != 0) ||
188 		(strcmp(term, "vt100") == 0) ||
189 		(strcmp(term, "linux") == 0) ||
190 		(strcmp(term, "vt220") == 0) ||
191 		(strcmp(term, "vt102") == 0)
192 	) {
193 		tcap_normal = "\033[0m";       /* Default ANSI escapes */
194 		tcap_boldface = "\033[1m";
195 		tcap_underline = "\033[4m";
196 		tcap_reverse = "\033[7m";
197 	} else {
198 		tcap_normal = "";
199 		tcap_boldface = "";
200 		tcap_underline = "";
201 		tcap_reverse = "";
202 	}
203 #endif
204 }	/* InitTermcap */
205 
206 
207 
208 
209 static char *
FindStartOfCurrentCommand(void)210 FindStartOfCurrentCommand(void)
211 {
212 	char *scp;
213 	char *start;
214 	int qc;
215 
216 	for (scp = gl_buf;;) {
217 		start = scp;
218 		for (;;) {
219 			if (*scp == '\0')
220 				goto done;
221 			if (!isspace((int) *scp))
222 				break;
223 			scp++;
224 		}
225 		start = scp;
226 
227 		for (;;) {
228 			if (*scp == '\0') {
229 				goto done;
230 			} else if ((*scp == '"') || (*scp == '\'')) {
231 				qc = *scp++;
232 
233 				for (;;) {
234 					if (*scp == '\0') {
235 						goto done;
236 					} else if (*scp == '\\') {
237 						scp++;
238 						if (*scp == '\0')
239 							goto done;
240 						scp++;
241 					} else if (*scp == qc) {
242 						scp++;
243 						break;
244 					} else {
245 						scp++;
246 					}
247 				}
248 			} else if (*scp == '\\') {
249 				scp++;
250 				if (*scp == '\0')
251 					goto done;
252 				scp++;
253 			} else if ((*scp == ';') || (*scp == '\n')) {
254 				/* command ended */
255 				scp++;
256 				if (*scp == '\0')
257 					goto done;
258 				break;
259 			} else {
260 				scp++;
261 			}
262 		}
263 	}
264 done:
265 	return (start);
266 }	/* FindStartOfCurrentCommand */
267 
268 
269 
270 static FileInfoListPtr
GetLsCacheFileList(const char * const item)271 GetLsCacheFileList(const char *const item)
272 {
273 	int ci;
274 	int sortBy;
275 	int sortOrder;
276 	FileInfoListPtr filp;
277 
278 	ci = LsCacheLookup(item);
279 	if (ci < 0) {
280 		/* This dir was not in the
281 		 * cache -- go get it.
282 		 */
283 		Ls(item, 'l', "", NULL);
284 		ci = LsCacheLookup(item);
285 		if (ci < 0)
286 			return NULL;
287 	}
288 
289 	sortBy = 'n';		/* Sort by filename. */
290 	sortOrder = 'a';	/* Sort in ascending order. */
291 	filp = &gLsCache[ci].fil;
292 	SortFileInfoList(filp, sortBy, sortOrder);
293 	return filp;
294 }	/* GetLsCacheFileList */
295 
296 
297 
298 
299 static char *
RemoteCompletionFunction(const char * text,int state,int fTypeFilter)300 RemoteCompletionFunction(const char *text, int state, int fTypeFilter)
301 {
302 	char rpath[256];
303 	char *cp;
304 	char *cp2;
305 	const char *textbasename;
306 	int fType;
307 	FileInfoPtr diritemp;
308 	FileInfoListPtr filp;
309 	int textdirlen;
310 	size_t tbnlen;
311 	size_t flen, mlen;
312 	static FileInfoVec diritemv;
313 	static int i;
314 
315 	textbasename = strrchr(text, '/');
316 	if (textbasename == NULL) {
317 		textbasename = text;
318 		textdirlen = -1;
319 	} else {
320 		textdirlen = (int) (textbasename - text);
321 		textbasename++;
322 	}
323 	tbnlen = strlen(textbasename);
324 
325 	if (state == 0) {
326 		if (text[0] == '\0') {
327 			/* Special case when they do "get <TAB><TAB> " */
328 			STRNCPY(rpath, gRemoteCWD);
329 		} else {
330 			PathCat(rpath, sizeof(rpath), gRemoteCWD, text);
331 			if (text[strlen(text) - 1] == '/') {
332 				/* Special case when they do "get /dir1/dir2/<TAB><TAB>" */
333 				STRNCAT(rpath, "/");
334 			}
335 			cp2 = strrchr(rpath, '/');
336 			if (cp2 == NULL) {
337 				return NULL;
338 			} else if (cp2 == rpath) {
339 				/* Item in root directory. */
340 				cp2++;
341 			}
342 			*cp2 = '\0';
343 		}
344 
345 		filp = GetLsCacheFileList(rpath);
346 		if (filp == NULL)
347 			return NULL;
348 
349 		diritemv = filp->vec;
350 		if (diritemv == NULL)
351 			return NULL;
352 
353 		i = 0;
354 	}
355 
356 	for ( ; ; ) {
357 		diritemp = diritemv[i];
358 		if (diritemp == NULL)
359 			break;
360 
361 		i++;
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) {
367 					mlen = flen + 2;
368 					cp = (char *) malloc(mlen);
369 					if (cp == NULL)
370 						return (NULL);
371 					(void) memcpy(cp, diritemp->relname, mlen);
372 				} else {
373 					mlen = textdirlen + 1 + flen + 2;
374 					cp = (char *) malloc(mlen);
375 					if (cp == NULL)
376 						return (NULL);
377 					(void) memcpy(cp, text, (size_t) textdirlen);
378 					cp[textdirlen] = '/';
379 					(void) strcpy(cp + textdirlen + 1, diritemp->relname);
380 				}
381 				if (fType == 'd') {
382 					gl_completion_exact_match_extra_char = '/';
383 				} else {
384 					gl_completion_exact_match_extra_char = ' ';
385 				}
386 				return cp;
387 			}
388 		}
389 	}
390 	return NULL;
391 }	/* RemoteCompletionFunction */
392 
393 
394 
395 
396 static char *
RemoteFileCompletionFunction(const char * text,int state)397 RemoteFileCompletionFunction(const char *text, int state)
398 {
399 	char *cp;
400 
401 	cp = RemoteCompletionFunction(text, state, 0);
402 	return cp;
403 }	/* RemoteFileCompletionFunction */
404 
405 
406 
407 
408 static char *
RemoteDirCompletionFunction(const char * text,int state)409 RemoteDirCompletionFunction(const char *text, int state)
410 {
411 	char *cp;
412 
413 	cp = RemoteCompletionFunction(text, state, 'd');
414 	return cp;
415 }	/* RemoteDirCompletionFunction */
416 
417 
418 
419 
420 static char *
BookmarkCompletionFunction(const char * text,int state)421 BookmarkCompletionFunction(const char *text, int state)
422 {
423 	char *cp;
424 	size_t textlen;
425 	int i, matches;
426 
427 	if ((gBookmarkTable == NULL) || (state >= gNumBookmarks))
428 		return (NULL);
429 
430 	textlen = strlen(text);
431 	if (textlen == 0) {
432 		cp = StrDup(gBookmarkTable[state].bookmarkName);
433 	} else {
434 		cp = NULL;
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);
439 					break;
440 				}
441 				matches++;
442 			}
443 		}
444 	}
445 	return cp;
446 }	/* BookmarkCompletionFunction */
447 
448 
449 
450 
451 static char *
CommandCompletionFunction(const char * text,int state)452 CommandCompletionFunction(const char *text, int state)
453 {
454 	char *cp;
455 	size_t textlen;
456 	int i, matches;
457 	CommandPtr cmdp;
458 
459 	textlen = strlen(text);
460 	if (textlen == 0) {
461 		cp = NULL;
462 	} else {
463 		cp = NULL;
464 		for (i=0, matches=0; ; i++) {
465 			cmdp = GetCommandByIndex(i);
466 			if (cmdp == kNoCommand)
467 				break;
468 			if (ISTRNCMP(cmdp->name, text, textlen) == 0) {
469 				if (matches >= state) {
470 					cp = StrDup(cmdp->name);
471 					break;
472 				}
473 				matches++;
474 			}
475 		}
476 	}
477 	return cp;
478 }	/* CommandCompletionFunction */
479 
480 
481 
482 
483 static char *
PrefOptCompletionFunction(const char * text,int state)484 PrefOptCompletionFunction(const char *text, int state)
485 {
486 	char *cp;
487 	size_t textlen;
488 	int i, matches;
489 
490 	if (state >= gNumPrefOpts)
491 		return (NULL);
492 
493 	textlen = strlen(text);
494 	if (textlen == 0) {
495 		cp = StrDup(gPrefOpts[state].varname);
496 	} else {
497 		cp = NULL;
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);
502 					break;
503 				}
504 				matches++;
505 			}
506 		}
507 	}
508 	return cp;
509 }	/* PrefOptCompletionFunction */
510 
511 
512 
513 
514 void
ReCacheBookmarks(void)515 ReCacheBookmarks(void)
516 {
517 	(void) LoadBookmarkTable();
518 }
519 
520 
521 
522 static int
HaveCommandNameOnly(char * cmdstart)523 HaveCommandNameOnly(char *cmdstart)
524 {
525 	char *cp;
526 	for (cp = cmdstart; *cp != '\0'; cp++) {
527 		if (isspace((int) *cp))
528 			return (0);	/* At least one argument in progress. */
529 	}
530 	return (1);
531 }	/* HaveCommandNameOnly */
532 
533 
534 
535 
536 static char *
CompletionFunction(const char * text,int state)537 CompletionFunction(const char *text, int state)
538 {
539 	char *cp;
540 	char *cmdstart;
541 	ArgvInfo ai;
542 	int bUsed;
543 	CommandPtr cmdp;
544 	static int flags;
545 
546 	if (state == 0) {
547 		flags = -1;
548 		cmdstart = FindStartOfCurrentCommand();
549 		if (cmdstart == NULL)
550 			return NULL;
551 		if (HaveCommandNameOnly(cmdstart)) {
552 			flags = -2;	/* special case */
553 			cp = CommandCompletionFunction(text, state);
554 			return cp;
555 		}
556 
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),
561 			ai.noglobargv, 1);
562 		if (bUsed <= 0)
563 			return NULL;
564 		if (ai.cargc == 0)
565 			return NULL;
566 
567 		cmdp = GetCommandByName(ai.cargv[0], 0);
568 		if (cmdp == kAmbiguousCommand) {
569 			return NULL;
570 		} else if (cmdp == kNoCommand) {
571 			return NULL;
572 		}
573 		flags = cmdp->flags;
574 	}
575 	if (flags == (-2)) {
576 		cp = CommandCompletionFunction(text, state);
577 		return cp;
578 	}
579 	if (flags < 0)
580 		return NULL;
581 	if ((flags & (kCompleteLocalFile|kCompleteLocalDir)) != 0) {
582 		cp = gl_local_filename_completion_proc(text, state);
583 		return cp;
584 	} else if ((flags & kCompleteRemoteFile) != 0) {
585 		gl_filename_quoting_desired = 1;
586 		cp = RemoteFileCompletionFunction(text, state);
587 		return cp;
588 	} else if ((flags & kCompleteRemoteDir) != 0) {
589 		gl_filename_quoting_desired = 1;
590 		cp = RemoteDirCompletionFunction(text, state);
591 		return cp;
592 	} else if ((flags & kCompleteBookmark) != 0) {
593 		cp = BookmarkCompletionFunction(text, state);
594 		return cp;
595 	} else if ((flags & kCompletePrefOpt) != 0) {
596 		cp = PrefOptCompletionFunction(text, state);
597 		return cp;
598 	}
599 	return NULL;
600 }	/* CompletionFunction */
601 
602 
603 
604 
605 void
LoadHistory(void)606 LoadHistory(void)
607 {
608 	char pathName[256];
609 
610 	if (gOurDirectoryPath[0] == '\0')
611 		return;
612 	(void) OurDirectoryPath(pathName, sizeof(pathName), kHistoryFileName);
613 
614 	gl_histloadfile(pathName);
615 }	/* LoadHistory */
616 
617 
618 
619 static size_t
Vt100VisibleStrlen(const char * src)620 Vt100VisibleStrlen(const char *src)
621 {
622 	const char *cp;
623 	size_t esc;
624 
625 	for (esc = 0, cp = src; *cp != '\0'; cp++) {
626 		if (*cp == '\033')
627 			esc++;
628 	}
629 
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.
632 	 */
633 	return ((size_t) (cp - src) - (esc * 4));
634 }	/* Vt100VisibleStrlen */
635 
636 
637 
638 /* Save the commands they typed in a history file, then they can use
639  * readline to go through them again next time.
640  */
641 void
SaveHistory(void)642 SaveHistory(void)
643 {
644 	char pathName[256];
645 
646 	if (gOurDirectoryPath[0] == '\0')
647 		return;		/* Don't create in root directory. */
648 	(void) OurDirectoryPath(pathName, sizeof(pathName), kHistoryFileName);
649 
650 	gl_strlen = Vt100VisibleStrlen;
651 	gl_histsavefile(pathName);
652 	(void) _chmod(pathName, 00600);
653 }	/* SaveHistory */
654 
655 
656 
657 
658 void
InitReadline(void)659 InitReadline(void)
660 {
661 	gl_completion_proc = CompletionFunction;
662 	gl_setwidth(gScreenColumns);
663 	LoadHistory();
664 	ReCacheBookmarks();
665 }	/* InitReadline */
666 
667 
668 
669 
670 char *
Readline(char * prompt)671 Readline(char *prompt)
672 {
673 	char *linecopy, *line, *cp;
674 	char lbuf[256];
675 
676 	if (gIsTTYr) {
677 		line = getline(prompt);
678 	} else {
679 		line = fgets(lbuf, sizeof(lbuf) - 1, stdin);
680 		if (line != NULL) {
681 			cp = line + strlen(line) - 1;
682 			if (*cp == '\n')
683 				*cp = '\0';
684 		}
685 	}
686 
687 	if (line != NULL) {
688 		if (line[0] == '\0')
689 			return NULL;	/* EOF */
690 		linecopy = StrDup(line);
691 		line = linecopy;
692 	}
693 	return (line);
694 }	/* Readline */
695 
696 
697 
698 void
AddHistory(char * line)699 AddHistory(char *line)
700 {
701 	gl_histadd(line);
702 }	/* AddHistory */
703 
704 
705 
706 void
DisposeReadline(void)707 DisposeReadline(void)
708 {
709 	SaveHistory();
710 }	/* DisposeReadline */
711 
712 
713 
714 
715 
716 /*VARARGS*/
717 void
SetXtermTitle(const char * const fmt,...)718 SetXtermTitle(const char *const fmt, ...)
719 {
720 	va_list ap;
721 	char buf[256];
722 
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);
727 #else
728 			STRNCPY(buf, gTerm);
729 #endif
730 		} else if (ISTRCMP(fmt, "DEFAULT") == 0) {
731 			(void) Strncpy(buf, gVersion + 5, 12);
732 		} else {
733 			va_start(ap, fmt);
734 #ifdef HAVE_VSNPRINTF
735 			(void) vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
736 			buf[sizeof(buf) - 1] = '\0';
737 #else
738 			(void) vsprintf(buf, fmt, ap);
739 #endif
740 			va_end(ap);
741 		}
742 		if (buf[0] != '\0') {
743 #if (defined(WIN32) || defined(_WINDOWS)) && defined(_CONSOLE)
744 			SetConsoleTitle(buf);
745 #else
746 			if (strcmp(gCurXtermTitleStr, buf) != 0) {
747 				fprintf(stderr, "\033]0;%s\007", buf);
748 				STRNCPY(gCurXtermTitleStr, buf);
749 			}
750 #endif
751 		}
752 	}
753 }	/* SetXtermTitle */
754 
755 
756 
757 
758 void
PrintStartupBanner(void)759 PrintStartupBanner(void)
760 {
761 	char v[80], *cp;
762 	char vdate[32];
763 
764 	/* Print selected information from the version ID. */
765 	vdate[0] = '\0';
766 	(void) STRNCPY(v, gVersion + 5);
767 	cp = strchr(v, ',');
768 	if (cp != NULL) {
769 		*cp = '\0';
770 		cp[-5] = '\0';
771 		(void) STRNCPY(vdate, " (");
772 		(void) STRNCAT(vdate, v + 16);
773 		(void) STRNCAT(vdate, ", ");
774 		(void) STRNCAT(vdate, cp - 4);
775 		(void) STRNCAT(vdate, ")");
776 	}
777 
778 #if defined(BETA) && (BETA > 0)
779 	(void) fprintf(stdout, "%s%.11s beta %d%s%s by Mike Gleason (ncftp@ncftp.com).\n",
780 		tcap_boldface,
781 		gVersion + 5,
782 		BETA,
783 		tcap_normal,
784 		vdate
785 	);
786 #else
787 	(void) fprintf(stdout, "%s%.11s%s%s by Mike Gleason (ncftp@ncftp.com).\n",
788 		tcap_boldface,
789 		gVersion + 5,
790 		tcap_normal,
791 		vdate
792 	);
793 #endif
794 	(void) fflush(stdout);
795 }	/* PrintStartupBanner */
796 
797 
798 
799 
800 /* Print the command shell's prompt. */
801 void
MakePrompt(char * dst,size_t dsize)802 MakePrompt(char *dst, size_t dsize)
803 {
804 	char acwd[64];
805 
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);
812 	} else {
813 		snprintf(dst, dsize, "%sncftp%s> ",
814 			tcap_boldface, tcap_normal);
815 	}
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);
825 	}
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 */
831 }	/* MakePrompt */
832