- Fix KiDispatchException to unmask KI_EXCEPTION_INTERNAL when setting the exception...
[reactos.git] / rosapps / net / ncftp / ncftp / readln.c
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
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
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 *
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
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 *
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 *
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 *
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 *
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 *
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 *
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
515 ReCacheBookmarks(void)
516 {
517 (void) LoadBookmarkTable();
518 }
519
520
521
522 static int
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 *
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
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
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
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
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 *
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
699 AddHistory(char *line)
700 {
701 gl_histadd(line);
702 } /* AddHistory */
703
704
705
706 void
707 DisposeReadline(void)
708 {
709 SaveHistory();
710 } /* DisposeReadline */
711
712
713
714
715
716 /*VARARGS*/
717 void
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
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
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 */