1 /* bmed.c (bookmark editor)
2  *
3  * Copyright (c) 1996-2005 Mike Gleason, NcFTP Software.
4  * All rights reserved.
5  *
6  */
7 
8 #include "syshdrs.h"
9 #ifdef PRAGMA_HDRSTOP
10 #	pragma hdrstop
11 #endif
12 
13 #include "../ncftp/util.h"
14 #include "../ncftp/trace.h"
15 #include "../ncftp/pref.h"
16 #include "../ncftp/bookmark.h"
17 #include "wutil.h"
18 #include "wgets.h"
19 #include "bmed.h"
20 
21 /* Are we being run as a regular process, or a
22  * subprocess of ncftp?
23  */
24 int gStandAlone;
25 
26 /* This is the full-screen window that pops up when you run the
27  * host editor.  Not much is done with it, except display directions
28  * and getting host editor commands.
29  */
30 WINDOW *gHostWin = NULL;
31 
32 /* This is a window devoted solely to serve as a scrolling list
33  * of bookmarks.
34  */
35 WINDOW *gHostListWin = NULL;
36 
37 int gHostListWinWide;
38 
39 /* This is another full-screen window that opens when a user wants
40  * to edit the parameters for a site.
41  */
42 WINDOW *gEditHostWin = NULL;
43 
44 /* This is an index into the host list.  This indicates the position
45  * in the host list where we draw the current "page" of the host list.
46  */
47 int gHostListWinStart;
48 
49 /* This index is the currently selected host.  This index must be >=
50  * to gHostListWinStart and less than gHostListWinStart + pageLen - 1,
51  * so that this host will show up in the current page of the host list.
52  */
53 int gHilitedHost;
54 
55 /* This is a pointer to the actual information of the currently
56  * selected host.
57  */
58 BookmarkPtr gCurHostListItem;
59 
60 /* How many lines compose a "page" in the host list's scrolling window. */
61 int gHostListPageSize;
62 
63 /* A flag saying if we need to erase a message after the next input key. */
64 int gNeedToClearMsg = 0;
65 
66 /* When we edit gCurHostListItem's stuff, we actually edit a copy of it.
67  * This is so we could restore the information if the user wanted to
68  * abort the changes.
69  */
70 Bookmark gEditRsi;
71 
72 #ifdef HAVE_SIGSETJMP
73 sigjmp_buf gHostWinJmp;
74 #else	/* HAVE_SIGSETJMP */
75 jmp_buf gHostWinJmp;
76 #endif	/* HAVE_SIGSETJMP */
77 
78 /* If set to a valid pathname, hitting enter at the host selection
79  * screen will write the name of the bookmark into this file.
80  * This is a cheap form of IPC with a parent NcFTP process.
81  */
82 const char *gBookmarkSelectionFile = NULL;
83 
84 /* Needed by prefs. */
85 FTPLibraryInfo gLib;
86 FTPConnectionInfo gConn;
87 
88 extern int gWinInit;
89 extern int gScreenWidth;
90 extern int gScreenHeight;
91 extern int gNumBookmarks;
92 extern BookmarkPtr gBookmarkTable;
93 extern int gDebug;
94 
95 
96 
97 
AtoIMaybe(int * dst,char * str)98 void AtoIMaybe(int *dst, char *str)
99 {
100 	char *cp;
101 
102 	/* Don't change the value if the user just hit return. */
103 	for (cp = str; *cp != '\0'; cp++)
104 		if (isdigit((int) *cp))
105 			break;
106 	if (isdigit((int) *cp))
107 		*dst = atoi(str);
108 }	/* AtoIMaybe */
109 
110 
111 
112 
113 
114 /* Draws the screen when we're using the host editor's main screen.
115  * You can can specify whether to draw each character whether it needs
116  * it or not if you like.
117  */
UpdateHostWindows(int uptAll)118 void UpdateHostWindows(int uptAll)
119 {
120 	if (uptAll) {
121 		DrawHostList();
122 		touchwin(gHostListWin);
123 		touchwin(gHostWin);
124 	}
125 	wnoutrefresh(gHostListWin);
126 	wnoutrefresh(gHostWin);
127 	DOUPDATE(1);
128 }	/* UpdateHostWindows */
129 
130 
131 
132 /* This draws the scrolling list of bookmarks, and hilites the currently
133  * selected host.
134  */
DrawHostList(void)135 void DrawHostList(void)
136 {
137 	int lastLine, i;
138 	BookmarkPtr rsip;
139 	char str[256];
140 	char url[256];
141 	int maxy, maxx;
142 	int lmaxy, lmaxx;
143 	int begy, begx;
144 	char spec[32];
145 
146 #ifdef __clang__
147 #pragma clang diagnostic push
148 #pragma clang diagnostic ignored "-Wformat-nonliteral"
149 #endif
150 	getmaxyx(gHostListWin, lmaxy, lmaxx);
151 	getbegyx(gHostListWin, begy, begx);
152 	getmaxyx(gHostWin, maxy, maxx);
153 	/* We have a status line saying how many bookmarks there are in
154 	 * the list.  That way the user knows something is supposed to
155 	 * be there when the host list is totally empty, and also that
156 	 * there are more bookmarks to look at when the entire host list
157 	 * doesn't fit in the scroll window.
158 	 */
159 	WAttr(gHostWin, kUnderline, 1);
160 	mvwprintw(
161 		gHostWin,
162 		begy - 1,
163 		begx,
164 		strcpy(spec, "%s"),	/* avoid warnings on BSD */
165 		"Number of bookmarks"
166 	);
167 	WAttr(gHostWin, kUnderline, 0);
168 	wprintw(
169 		gHostWin,
170 		strcpy(spec, ": %3d"),
171 		gNumBookmarks
172 	);
173 
174 	if (gHostListWinWide == 0) {
175 		sprintf(spec, "%%-16.16s %%-%ds", lmaxx - 17);
176 		lastLine = lmaxy + gHostListWinStart;
177 		for (i=gHostListWinStart; (i<lastLine) && (i<gNumBookmarks); i++) {
178 			rsip = &gBookmarkTable[i];
179 			if (rsip == gCurHostListItem)
180 				WAttr(gHostListWin, kReverse, 1);
181 			sprintf(str, spec, rsip->bookmarkName, rsip->name);
182 			str[lmaxx] = '\0';
183 			DrawStrAt(gHostListWin, i - gHostListWinStart, 0, str);
184 			swclrtoeol(gHostListWin);
185 			if (rsip == gCurHostListItem) {
186 				WAttr(gHostListWin, kReverse, 0);
187 			}
188 		}
189 	} else {
190 		lastLine = lmaxy + gHostListWinStart;
191 		for (i=gHostListWinStart; (i<lastLine) && (i<gNumBookmarks); i++) {
192 			rsip = &gBookmarkTable[i];
193 			if (rsip == gCurHostListItem)
194 				WAttr(gHostListWin, kReverse, 1);
195 			BookmarkToURL(rsip, url, sizeof(url));
196 			sprintf(str, "%-16.16s  ", rsip->bookmarkName);
197 			STRNCAT(str, url);
198 			memset(url, 0, sizeof(url));
199 			AbbrevStr(url, str, (size_t) lmaxx, 1);
200 			DrawStrAt(gHostListWin, i - gHostListWinStart, 0, url);
201 			swclrtoeol(gHostListWin);
202 			if (rsip == gCurHostListItem) {
203 				WAttr(gHostListWin, kReverse, 0);
204 			}
205 		}
206 	}
207 
208 
209 	/* Add 'vi' style empty-lines. */
210 	for ( ; i<lastLine; ++i) {
211 		DrawStrAt(gHostListWin, i - gHostListWinStart, 0, "~");
212 		swclrtoeol(gHostListWin);
213 	}
214 	wmove(gHostWin, maxy - 3, 2);
215 	sprintf(spec, "%%-%ds", maxx - 4);
216 	if (gCurHostListItem == NULL) {
217 		str[0] = '\0';
218 	} else if (gCurHostListItem->comment[0] == '\0') {
219 		memset(str, 0, sizeof(str));
220 		if (gHostListWinWide == 0) {
221 			BookmarkToURL(gCurHostListItem, url, sizeof(url));
222 			AbbrevStr(str, url, (size_t) maxx - 2, 1);
223 		}
224 	} else {
225 		STRNCPY(str, "``");
226 		STRNCAT(str, gCurHostListItem->comment);
227 		AbbrevStr(str + 2, gCurHostListItem->comment, (size_t) maxx - 8, 1);
228 		STRNCAT(str, "''");
229 	}
230 	wprintw(gHostWin, spec, str);
231 	wmove(gHostWin, maxy - 1, 0);
232 	UpdateHostWindows(0);
233 
234 #ifdef __clang__
235 #pragma clang diagnostic pop
236 #endif
237 }	/* DrawHostList */
238 
239 
240 
241 /* This prompts for a key of input when in the main host editor window. */
HostWinGetKey(void)242 int HostWinGetKey(void)
243 {
244 	int c;
245 	unsigned int uc;
246 	int escmode;
247 	int maxy;
248 
249 	maxy = getmaxy(gHostWin);
250 	wmove(gHostWin, maxy - 1, 0);
251 	for (escmode = 0; ; escmode++) {
252 		uc = (unsigned int) wgetch(gHostWin);
253 		c = (int) uc;
254 		if (uc > 255) {
255 			Trace(1, "[0x%04X]\n", c);
256 		} else if (isprint(c) && !iscntrl(c)) {
257 			Trace(1, "[0x%04X, %c]\n", c, c);
258 		} else if (iscntrl(c)) {
259 			Trace(1, "[0x%04X, ^%c]\n", c, (c & 31) | ('A' - 1));
260 		} else {
261 			Trace(1, "[0x%04X]\n", c);
262 		}
263 
264 		/* Some implementations of curses (i.e. Mac OS X)
265 		 * don't seem to detect the arrow keys on
266 		 * typical terminal types like "vt100" or "ansi",
267 		 * so we try and detect them the hard way.
268 		 */
269 		switch (escmode) {
270 			case 0:
271 				if (uc != 0x001B) {
272 					goto gotch;
273 				}
274 				/* else ESC key (^[) */
275 				break;
276 			case 1:
277 				if ((c != '[') && (c != 'O')) {
278 					goto gotch;
279 				}
280 				/* else ANSI ESC sequence continues */
281 				break;
282 			case 2:
283 				switch (c) {
284 					case 'A':
285 					case 'a':
286 #ifdef KEY_UP
287 						c = KEY_UP;
288 						Trace(1, "  --> [0x%04X, %s]\n", c, "UP");
289 #else
290 						c = 'k';	/* vi UP */
291 						Trace(1, "  --> [0x%04X, %s]\n", c, "k");
292 #endif
293 						break;
294 					case 'B':
295 					case 'b':
296 #ifdef KEY_DOWN
297 						c = KEY_DOWN;
298 						Trace(1, "  --> [0x%04X, %s]\n", c, "DOWN");
299 #else
300 						c = 'j';	/* vi DOWN */
301 						Trace(1, "  --> [0x%04X, %s]\n", c, "j");
302 #endif
303 						break;
304 					case 'D':
305 					case 'd':
306 #ifdef KEY_LEFT
307 						c = KEY_LEFT;
308 						Trace(1, "  --> [0x%04X, %s]\n", c, "LEFT");
309 #else
310 						c = 'h';	/* vi LEFT */
311 						Trace(1, "  --> [0x%04X, %s]\n", c, "h");
312 #endif
313 						break;
314 					case 'C':
315 					case 'c':
316 #ifdef KEY_RIGHT
317 						c = KEY_RIGHT;
318 						Trace(1, "  --> [0x%04X, %s]\n", c, "RIGHT");
319 #else
320 						c = 'l';	/* vi RIGHT */
321 						Trace(1, "  --> [0x%04X, %s]\n", c, "l");
322 #endif
323 						break;
324 				}
325 				goto gotch;
326 		}
327 	}
328 gotch:
329 	return (c);
330 }	/* HostWinGetKey */
331 
332 
333 
334 static
NewHilitedHostIndex(int newIdx)335 void NewHilitedHostIndex(int newIdx)
336 {
337 	int oldIdx, lastLine;
338 
339 	if (gNumBookmarks <= 0) {
340 		HostWinMsg(
341 "No bookmarks in the list.  Try a /new, or open a site manually to add one.");
342 	} else {
343 		oldIdx = gHilitedHost;
344 		if (gNumBookmarks < gHostListPageSize)
345 			lastLine = gHostListWinStart + gNumBookmarks - 1;
346 		else
347 			lastLine = gHostListWinStart + gHostListPageSize - 1;
348 		if (newIdx < gHostListWinStart) {
349 			/* Will need to scroll the window up. */
350 			if (newIdx < 0) {
351 				newIdx = 0;
352 				if (oldIdx == newIdx)
353 					HostWinMsg("You are at the top of the list.");
354 			}
355 			gHilitedHost = gHostListWinStart = newIdx;
356 		} else if (newIdx > lastLine) {
357 			/* Will need to scroll the window down. */
358 			if (newIdx > (gNumBookmarks - 1)) {
359 				newIdx = gNumBookmarks - 1;
360 				if (oldIdx == newIdx)
361 					HostWinMsg("You are at the bottom of the list.");
362 			}
363 			gHilitedHost = newIdx;
364 			gHostListWinStart = newIdx - (gHostListPageSize - 1);
365 			if (gHostListWinStart < 0)
366 				gHostListWinStart = 0;
367 		} else {
368 			/* Don't need to scroll window, just move pointer. */
369 			gHilitedHost = newIdx;
370 		}
371 		gCurHostListItem = &gBookmarkTable[gHilitedHost];
372 		if (oldIdx != newIdx) {
373 			DrawHostList();
374 		}
375 	}
376 }	/* NewHilitedHostIndex */
377 
378 
379 
380 
381 /* You can zip to a different area of the list without using the arrow
382  * or page scrolling keys.  Just type a letter, and the list will scroll
383  * to the first host starting with that letter.
384  */
HostWinZoomTo(int c)385 void HostWinZoomTo(int c)
386 {
387 	int i, j;
388 
389 	if (gNumBookmarks > 0) {
390 		if (islower(c))
391 			c = toupper(c);
392 		for (i=0; i<gNumBookmarks - 1; i++) {
393 			j = gBookmarkTable[i].bookmarkName[0];
394 			if (islower(j))
395 				j = toupper(j);
396 			if (j >= c)
397 				break;
398 		}
399 		NewHilitedHostIndex(i);
400 	} else {
401 		HostWinMsg("No bookmarks to select.  Try a /new.");
402 	}
403 	DrawHostList();
404 }	/* HostWinZoomTo */
405 
406 
407 
408 
409 
HostListLineUp(void)410 void HostListLineUp(void)
411 {
412 	NewHilitedHostIndex(gHilitedHost - 1);
413 }	/* HostListLineUp */
414 
415 
416 
417 
418 
HostListLineDown(void)419 void HostListLineDown(void)
420 {
421 	NewHilitedHostIndex(gHilitedHost + 1);
422 }	/* HostListLineDown */
423 
424 
425 
426 
HostListPageUp(void)427 void HostListPageUp(void)
428 {
429 	NewHilitedHostIndex(gHilitedHost - gHostListPageSize);
430 }	/* HostListPageUp */
431 
432 
433 
434 
HostListPageDown(void)435 void HostListPageDown(void)
436 {
437 	NewHilitedHostIndex(gHilitedHost + gHostListPageSize);
438 }	/* HostListPageDown */
439 
440 
441 
442 /* This marks the start of a section that belongs to the Bookmark Options
443  * window.  This window pops up on top of the host editor's main window
444  * when you wish to edit a site's settings.  When the user finishes,
445  * we close it and the host editor resumes.
446  */
447 
448 /* This displays a message in the Bookmark Options window. */
EditHostWinMsg(const char * msg)449 void EditHostWinMsg(const char *msg)
450 {
451 	int maxy;
452 
453 	maxy = getmaxy(gEditHostWin);
454 	DrawStrAt(gEditHostWin, maxy - 2, 0, msg);
455 	wclrtoeol(gEditHostWin);
456 	wmove(gEditHostWin, maxy - 1, 0);
457 	wrefresh(gEditHostWin);
458 }	/* EditHostWinMsg */
459 
460 
461 
462 
463 /* Prompts for a line of input. */
EditHostWinGetStr(char * dst,size_t size,int canBeEmpty,int canEcho)464 void EditHostWinGetStr(char *dst, size_t size, int canBeEmpty, int canEcho)
465 {
466 	char str[256];
467 	WGetsParams wgp;
468 	int maxy, maxx;
469 
470 	WAttr(gEditHostWin, kBold, 1);
471 	getmaxyx(gEditHostWin, maxy, maxx);
472 	DrawStrAt(gEditHostWin, maxy - 1, 0, "> ");
473 	WAttr(gEditHostWin, kBold, 0);
474 	wclrtoeol(gEditHostWin);
475 	wrefresh(gEditHostWin);
476 	curs_set(1);
477 
478 	wgp.w = gEditHostWin;
479 	wgp.sy = maxy - 1;
480 	wgp.sx = 2;
481 	wgp.fieldLen = maxx - 3;
482 	wgp.dst = str;
483 	wgp.dstSize = size;
484 	wgp.useCurrentContents = 0;
485 	wgp.echoMode = canEcho ? wg_RegularEcho : wg_BulletEcho;
486 	wgp.history = wg_NoHistory;
487 	(void) wg_Gets(&wgp);
488 	cbreak();						/* wg_Gets turns off cbreak and delay. */
489 
490 	/* See if the user just hit return.  We may not want to overwrite
491 	 * the dst here, which would make it an empty string.
492 	 */
493 	if ((wgp.changed) || (canBeEmpty == kOkayIfEmpty))
494 		strcpy(dst, str);
495 
496 	wmove(gEditHostWin, maxy - 1, 0);
497 	wclrtoeol(gEditHostWin);
498 	wrefresh(gEditHostWin);
499 	curs_set(0);
500 }	/* EditHostWinGetStr */
501 
502 
503 
504 
505 
506 /* Prompts for an integer of input. */
EditHostWinGetNum(int * dst)507 void EditHostWinGetNum(int *dst)
508 {
509 	WGetsParams wgp;
510 	char str[256];
511 	int maxy, maxx;
512 
513 	getmaxyx(gEditHostWin, maxy, maxx);
514 	WAttr(gEditHostWin, kBold, 1);
515 	DrawStrAt(gEditHostWin, maxy - 1, 0, "> ");
516 	WAttr(gEditHostWin, kBold, 0);
517 	wclrtoeol(gEditHostWin);
518 	wrefresh(gEditHostWin);
519 	curs_set(1);
520 
521 	wgp.w = gEditHostWin;
522 	wgp.sy = maxy - 1;
523 	wgp.sx = 2;
524 	wgp.fieldLen = maxx - 3;
525 	wgp.dst = str;
526 	wgp.dstSize = sizeof(str);
527 	wgp.useCurrentContents = 0;
528 	wgp.echoMode = wg_RegularEcho;
529 	wgp.history = wg_NoHistory;
530 	(void) wg_Gets(&wgp);
531 	cbreak();						/* wg_Gets turns off cbreak and delay. */
532 
533 	AtoIMaybe(dst, str);
534 	wmove(gEditHostWin, maxy - 1, 0);
535 	wclrtoeol(gEditHostWin);
536 	wrefresh(gEditHostWin);
537 	curs_set(0);
538 }	/* EditHostWinGetNum */
539 
540 
541 
542 
543 /* This is the meat of the site options window.  We can selectively update
544  * portions of the window by using a bitmask with bits set for items
545  * we want to update.
546  */
EditHostWinDraw(int flags,int hilite)547 void EditHostWinDraw(int flags, int hilite)
548 {
549 	int maxy, maxx;
550 	int i, f;
551 	char str[256];
552 	char spec[32];
553 	const char *cp;
554 
555 	/* Draw the keys the user can type in reverse text. */
556 	WAttr(gEditHostWin, kReverse, 1);
557 	f = 5;
558 	for (i = kFirstEditWindowItem; i <= kLastEditWindowItem; i++) {
559 		if (TESTBIT(flags, i))
560 			mvwaddch(gEditHostWin, f + i, 2, 'A' + i);
561 	}
562 
563 	/* The "quit" item is a special item that is offset a line, and
564 	 * always has the "X" key assigned to it.
565 	 */
566 	i = kQuitEditWindowItem;
567 	if (TESTBIT(flags, i))
568 		mvwaddch(gEditHostWin, 1 + f + i, 2, 'X');
569 	WAttr(gEditHostWin, kReverse, 0);
570 
571 	/* We can use this to hilite a whole line, to indicate to the
572 	 * user that a certain item is being edited.
573 	 */
574 	if (hilite)
575 		WAttr(gEditHostWin, kReverse, 1);
576 	getmaxyx(gEditHostWin, maxy, maxx);
577 	sprintf(spec, " %%-26s%%-%ds",
578 		maxx - 32);
579 
580 	/* Now draw the items on a case-by-case basis. */
581 	if (TESTBIT(flags, kNicknameEditWindowItem)) {
582 		mvwprintw(gEditHostWin, kNicknameEditWindowItem + f, 3, spec,
583 			"Bookmark name:",
584 			gEditRsi.bookmarkName
585 		);
586 		wclrtoeol(gEditHostWin);
587 	}
588 	if (TESTBIT(flags, kHostnameEditWindowItem)) {
589 		mvwprintw(gEditHostWin, kHostnameEditWindowItem + f, 3, spec,
590 			"Hostname:",
591 			gEditRsi.name
592 		);
593 		wclrtoeol(gEditHostWin);
594 	}
595 	if (TESTBIT(flags, kUserEditWindowItem)) {
596 		mvwprintw(gEditHostWin, kUserEditWindowItem + f, 3, spec,
597 			"User:",
598 			gEditRsi.user[0] == '\0' ? "anonymous" : gEditRsi.user
599 		);
600 		wclrtoeol(gEditHostWin);
601 	}
602 	if (TESTBIT(flags, kPassEditWindowItem)) {
603 		if (gEditRsi.pass[0] == '\0' && gEditRsi.user[0] == '\0')
604 			STRNCPY(str, gLib.defaultAnonPassword);
605 		mvwprintw(gEditHostWin, kPassEditWindowItem + f, 3, spec,
606 			"Password:",
607 			strcmp(str, gLib.defaultAnonPassword) ? "********" : str
608 		);
609 		wclrtoeol(gEditHostWin);
610 	}
611 	if (TESTBIT(flags, kAcctEditWindowItem)) {
612 		mvwprintw(gEditHostWin, kAcctEditWindowItem + f, 3, spec,
613 			"Account:",
614 			gEditRsi.acct[0] == '\0' ? "none" : gEditRsi.acct
615 		);
616 		wclrtoeol(gEditHostWin);
617 	}
618 	if (TESTBIT(flags, kDirEditWindowItem)) {
619 		if (gEditRsi.dir[0] == '\0')
620 			STRNCPY(str, "/");
621 		else
622 			AbbrevStr(str, gEditRsi.dir, (size_t) maxx - 32, 0);
623 		mvwprintw(gEditHostWin, kDirEditWindowItem + f, 3, spec,
624 			"Remote Directory:",
625 			str
626 		);
627 		wclrtoeol(gEditHostWin);
628 	}
629 	if (TESTBIT(flags, kLDirEditWindowItem)) {
630 		if (gEditRsi.ldir[0] == '\0')
631 			STRNCPY(str, "(current)");
632 		else
633 			AbbrevStr(str, gEditRsi.ldir, (size_t) maxx - 32, 0);
634 		mvwprintw(gEditHostWin, kLDirEditWindowItem + f, 3, spec,
635 			"Local Directory:",
636 			str
637 		);
638 		wclrtoeol(gEditHostWin);
639 	}
640 	if (TESTBIT(flags, kXferTypeEditWindowItem)) {
641 		if ((gEditRsi.xferType == 'I') || (gEditRsi.xferType == 'B'))
642 			cp = "Binary";
643 		else if (gEditRsi.xferType == 'A')
644 			cp = "ASCII Text";
645 		else
646 			cp = "Tenex";
647 		mvwprintw(gEditHostWin, kXferTypeEditWindowItem + f, 3, spec,
648 			"Transfer type:",
649 			cp
650 		);
651 		wclrtoeol(gEditHostWin);
652 	}
653 	if (TESTBIT(flags, kPortEditWindowItem)) {
654 		sprintf(str, "%u", (gEditRsi.port == 0) ? 21 : (unsigned int) gEditRsi.port);
655 		mvwprintw(gEditHostWin, kPortEditWindowItem + f, 3, spec,
656 			"Port:",
657 			str
658 		);
659 		wclrtoeol(gEditHostWin);
660 	}
661 #if 0
662 	if (TESTBIT(flags, kSizeEditWindowItem)) {
663 		mvwprintw(gEditHostWin, kSizeEditWindowItem + f, 3, spec,
664 			"Has SIZE command:",
665 			gEditRsi.hasSIZE ? "Yes" : "No"
666 		);
667 		wclrtoeol(gEditHostWin);
668 	}
669 	if (TESTBIT(flags, kMdtmEditWindowItem)) {
670 		mvwprintw(gEditHostWin, kMdtmEditWindowItem + f, 3, spec,
671 			"Has MDTM command:",
672 			gEditRsi.hasMDTM ? "Yes" : "No"
673 		);
674 		wclrtoeol(gEditHostWin);
675 	}
676 	if (TESTBIT(flags, kPasvEditWindowItem)) {
677 		mvwprintw(gEditHostWin, kPasvEditWindowItem + f, 3, spec,
678 			"Can use passive FTP:",
679 			gEditRsi.hasPASV ? "Yes" : "No"
680 		);
681 		wclrtoeol(gEditHostWin);
682 	}
683 	if (TESTBIT(flags, kOSEditWindowItem)) {
684 		mvwprintw(gEditHostWin, kOSEditWindowItem + f, 3, spec,
685 			"Operating System:",
686 			(gEditRsi.isUnix == 1) ? "UNIX" : "Non-UNIX"
687 		);
688 		wclrtoeol(gEditHostWin);
689 	}
690 #endif
691 	if (TESTBIT(flags, kCommentEditWindowItem)) {
692 		if (gEditRsi.comment[0] == '\0')
693 			STRNCPY(str, "(none)");
694 		else
695 			AbbrevStr(str, gEditRsi.comment, (size_t) maxx - 32, 0);
696 		mvwprintw(gEditHostWin, kCommentEditWindowItem + f, 3, spec,
697 			"Comment:",
698 			str
699 		);
700 		wclrtoeol(gEditHostWin);
701 	}
702 	if (TESTBIT(flags, kQuitEditWindowItem)) {
703 		mvwprintw(gEditHostWin, kQuitEditWindowItem + f + 1, 3, spec,
704 			"(Done editing)",
705 			""
706 		);
707 		wclrtoeol(gEditHostWin);
708 	}
709 
710 	if (hilite)
711 		WAttr(gEditHostWin, kReverse, 0);
712 
713 	wmove(gEditHostWin, maxy - 1, 0);
714 	wrefresh(gEditHostWin);
715 }	/* EditHostWinDraw */
716 
717 
718 
719 /* The user can hit space to change the transfer type.  For these toggle
720  * functions we do an update each time so the user can see the change
721  * immediately.
722  */
ToggleXferType(void)723 void ToggleXferType(void)
724 {
725 	int c;
726 
727 	for (;;) {
728 		c = wgetch(gEditHostWin);
729 		if ((c == 'x') || (c == 10) || (c == 13)
730 #ifdef KEY_ENTER
731 			|| (c == KEY_ENTER)
732 #endif
733 			)
734 			break;
735 		else if (isspace(c)) {
736 			if (gEditRsi.xferType == 'A')
737 				gEditRsi.xferType = 'I';
738 			else if ((gEditRsi.xferType == 'B') || (gEditRsi.xferType == 'I'))
739 				gEditRsi.xferType = 'T';
740 			else
741 				gEditRsi.xferType = 'A';
742 			EditHostWinDraw(BIT(kXferTypeEditWindowItem), kHilite);
743 		}
744 	}
745 }	/* ToggleXferType */
746 
747 
748 
749 
EditWinToggle(int * val,int bitNum,int min,int max)750 void EditWinToggle(int *val, int bitNum, int min, int max)
751 {
752 	int c;
753 
754 	for (;;) {
755 		c = wgetch(gEditHostWin);
756 		if ((c == 'x') || (c == 10) || (c == 13)
757 #ifdef KEY_ENTER
758 			|| (c == KEY_ENTER)
759 #endif
760 			)
761 			break;
762 		else if (isspace(c)) {
763 			*val = *val + 1;
764 			if (*val > max)
765 				*val = min;
766 			EditHostWinDraw(BIT(bitNum), kHilite);
767 		}
768 	}
769 }	/* EditWinToggle */
770 
771 
772 
773 
774 static void
SaveAndReload(void)775 SaveAndReload(void)
776 {
777 	SaveBookmarkTable();
778 	if (LoadBookmarkTable() < 0) {
779 		fprintf(stderr, "Suddenly unable to re-load bookmarks.");
780 		Exit(1);
781 	}
782 }	/* SaveAndReload */
783 
784 
785 
786 /* This opens and handles the site options window. */
HostWinEdit(void)787 void HostWinEdit(void)
788 {
789 	int c, field;
790 	int needUpdate;
791 	char bmname[128];
792 	BookmarkPtr rsip;
793 
794 	if (gCurHostListItem != NULL) {
795 		gEditHostWin = newwin(LINES, COLS, 0, 0);
796 		if (gEditHostWin == NULL)
797 			return;
798 
799 		STRNCPY(bmname, gCurHostListItem->bookmarkName);
800 
801 		/* Set the clear flag for the first update. */
802 		wclear(gEditHostWin);
803 
804 		/* leaveok(gEditHostWin, TRUE);	* Not sure if I like this... */
805 		WAttr(gEditHostWin, kBold, 1);
806 		WAddCenteredStr(gEditHostWin, 0, "Bookmark Options");
807 		WAttr(gEditHostWin, kBold, 0);
808 
809 		/* We'll be editing a copy of the current host's settings. */
810 		gEditRsi = *gCurHostListItem;
811 
812 		EditHostWinDraw(kAllWindowItems, kNoHilite);
813 		field = 1;
814 		for (;;) {
815 			EditHostWinMsg("Select an item to edit by typing its corresponding letter.");
816 			c = wgetch(gEditHostWin);
817 			if (islower(c))
818 				c = toupper(c);
819 			if (!isupper(c))
820 				continue;
821 			if (c == 'X')
822 				break;
823 			field = c - 'A';
824 			needUpdate = 1;
825 
826 			/* Hilite the current item to edit. */
827 			EditHostWinDraw(BIT(field), kHilite);
828 			switch(field) {
829 				case kNicknameEditWindowItem:
830 					EditHostWinMsg("Type a new bookmark name, or hit <RETURN> to continue.");
831 					EditHostWinGetStr(gEditRsi.bookmarkName, sizeof(gEditRsi.bookmarkName), kNotOkayIfEmpty, kGetAndEcho);
832 					break;
833 
834 				case kHostnameEditWindowItem:
835 					EditHostWinMsg("Type a new hostname, or hit <RETURN> to continue.");
836 					EditHostWinGetStr(gEditRsi.name, sizeof(gEditRsi.name), kNotOkayIfEmpty, kGetAndEcho);
837 					gEditRsi.lastIP[0] = '\0';	/* In case it changed. */
838 					break;
839 
840 				case kUserEditWindowItem:
841 					EditHostWinMsg("Type a username, or hit <RETURN> to signify anonymous.");
842 					EditHostWinGetStr(gEditRsi.user, sizeof(gEditRsi.user), kOkayIfEmpty, kGetAndEcho);
843 					break;
844 
845 				case kPassEditWindowItem:
846 					EditHostWinMsg("Type a password, or hit <RETURN> if no password is required.");
847 					EditHostWinGetStr(gEditRsi.pass, sizeof(gEditRsi.pass), kOkayIfEmpty, kGetNoEcho);
848 					break;
849 
850 				case kAcctEditWindowItem:
851 					EditHostWinMsg("Type an account name, or hit <RETURN> if no account is required.");
852 					EditHostWinGetStr(gEditRsi.acct, sizeof(gEditRsi.acct), kOkayIfEmpty, kGetAndEcho);
853 					break;
854 
855 				case kDirEditWindowItem:
856 					EditHostWinMsg("Type a remote directory path to start in after a connection is established.");
857 					EditHostWinGetStr(gEditRsi.dir, sizeof(gEditRsi.dir), kOkayIfEmpty, kGetAndEcho);
858 					break;
859 
860 				case kLDirEditWindowItem:
861 					EditHostWinMsg("Type a local directory path to start in after a connection is established.");
862 					EditHostWinGetStr(gEditRsi.ldir, sizeof(gEditRsi.ldir), kOkayIfEmpty, kGetAndEcho);
863 					break;
864 
865 				case kXferTypeEditWindowItem:
866 					EditHostWinMsg(kToggleMsg);
867 					ToggleXferType();
868 					break;
869 
870 				case kPortEditWindowItem:
871 					EditHostWinMsg("Type a port number to use for FTP.");
872 					EditHostWinGetNum((int *) &gEditRsi.port);
873 					break;
874 
875 #if 0
876 				case kSizeEditWindowItem:
877 					EditHostWinMsg(kToggleMsg);
878 					EditWinToggle(&gEditRsi.hasSIZE, field, 0, 1);
879 					break;
880 
881 				case kMdtmEditWindowItem:
882 					EditHostWinMsg(kToggleMsg);
883 					EditWinToggle(&gEditRsi.hasMDTM, field, 0, 1);
884 					break;
885 
886 				case kPasvEditWindowItem:
887 					EditHostWinMsg(kToggleMsg);
888 					EditWinToggle(&gEditRsi.hasPASV, field, 0, 1);
889 					break;
890 
891 				case kOSEditWindowItem:
892 					EditHostWinMsg(kToggleMsg);
893 					EditWinToggle(&gEditRsi.isUnix, field, 0, 1);
894 					break;
895 #endif
896 
897 				case kCommentEditWindowItem:
898 					EditHostWinMsg("Enter a line of information to store about this site.");
899 					EditHostWinGetStr(gEditRsi.comment, sizeof(gEditRsi.comment), kOkayIfEmpty, kGetAndEcho);
900 					break;
901 
902 				default:
903 					needUpdate = 0;
904 					break;
905 			}
906 			if (needUpdate)
907 				EditHostWinDraw(BIT(field), kNoHilite);
908 		}
909 		delwin(gEditHostWin);
910 		gEditHostWin = NULL;
911 		*gCurHostListItem = gEditRsi;
912 
913 		SaveAndReload();
914 		/* Note:  newly reallocated array, modified gNumBookmarks */
915 
916 		rsip = SearchBookmarkTable(bmname);
917 		if (rsip == NULL)
918 			rsip = &gBookmarkTable[0];
919 		gCurHostListItem = rsip;
920 		gHilitedHost = BMTINDEX(rsip);
921 		gHostListWinStart = BMTINDEX(rsip) - gHostListPageSize + 1;
922 		if (gHostListWinStart < 0)
923 			gHostListWinStart = 0;
924 		UpdateHostWindows(1);
925 	}
926 }	/* HostWinEdit */
927 
928 
929 
930 /* Clones an existing site in the host list. */
HostWinDup(void)931 void HostWinDup(void)
932 {
933 	BookmarkPtr rsip;
934 	char bmname[128];
935 
936 	if (gCurHostListItem != NULL) {
937 		/* Use the extra slot in the array for the new one. */
938 		rsip = &gBookmarkTable[gNumBookmarks];
939 		*rsip = *gCurHostListItem;
940 		STRNCAT(rsip->bookmarkName, "-copy");
941 		STRNCPY(bmname, rsip->bookmarkName);
942 		gNumBookmarks++;
943 		SaveAndReload();
944 		/* Note:  newly reallocated array, modified gNumBookmarks */
945 
946 		rsip = SearchBookmarkTable(bmname);
947 		if (rsip == NULL)
948 			rsip = &gBookmarkTable[0];
949 		gCurHostListItem = rsip;
950 		gHilitedHost = BMTINDEX(rsip);
951 		gHostListWinStart = BMTINDEX(rsip) - gHostListPageSize + 1;
952 		if (gHostListWinStart < 0)
953 			gHostListWinStart = 0;
954 		DrawHostList();
955 	} else {
956 		HostWinMsg("Nothing to duplicate.");
957 	}
958 	DrawHostList();
959 }	/* HostWinDup */
960 
961 
962 
963 
964 static void
DeleteBookmark(BookmarkPtr bmp)965 DeleteBookmark(BookmarkPtr bmp)
966 {
967 	bmp->deleted = 1;
968 	SaveAndReload();
969 }	/* DeleteBookmark */
970 
971 
972 
973 
974 /* Removes a site from the host list. */
HostWinDelete(void)975 void HostWinDelete(void)
976 {
977 	BookmarkPtr toDelete;
978 	int newi;
979 
980 	if (gCurHostListItem != NULL) {
981 		toDelete = gCurHostListItem;
982 
983 		/* Need to choose a new active host after deletion. */
984 		if (gHilitedHost == gNumBookmarks - 1) {
985 			if (gNumBookmarks == 1) {
986 				newi = -1;	/* None left. */
987 			} else {
988 				/* At last one before delete. */
989 				newi = gHilitedHost - 1;
990 			}
991 		} else {
992 			/* Won't need to increment gHilitedHost here, since after deletion,
993 			 * the next one will move up into this slot.
994 			 */
995 			newi = gHilitedHost;
996 		}
997 		DeleteBookmark(toDelete);
998 		if (newi < 0) {
999 			gCurHostListItem = NULL;
1000 		} else if (newi < gNumBookmarks) {
1001 			gCurHostListItem = &gBookmarkTable[newi];
1002 			gHilitedHost = newi;
1003 		} else {
1004 			newi = 0;
1005 			gCurHostListItem = &gBookmarkTable[newi];
1006 			gHilitedHost = newi;
1007 		}
1008 	} else
1009 		HostWinMsg("Nothing to delete.");
1010 	DrawHostList();
1011 }	/* HostWinDelete */
1012 
1013 
1014 
1015 
1016 /* Adds a new site to the host list, with default settings in place. */
HostWinNew(void)1017 void HostWinNew(void)
1018 {
1019 	BookmarkPtr rsip;
1020 
1021 	/* Use the extra slot in the array for the new one. */
1022 	rsip = &gBookmarkTable[gNumBookmarks];
1023 	SetBookmarkDefaults(rsip);
1024 	STRNCPY(rsip->bookmarkName, "(untitled)");
1025 	STRNCPY(rsip->name, "(Use /ed to edit)");
1026 	gNumBookmarks++;
1027 	SaveAndReload();
1028 	/* Note:  newly reallocated array, modified gNumBookmarks */
1029 
1030 	rsip = &gBookmarkTable[0];
1031 	gCurHostListItem = rsip;
1032 	gHilitedHost = BMTINDEX(rsip);
1033 	gHostListWinStart = BMTINDEX(rsip) - gHostListPageSize + 1;
1034 	if (gHostListWinStart < 0)
1035 		gHostListWinStart = 0;
1036 	DrawHostList();
1037 }	/* HostWinNew */
1038 
1039 
1040 
1041 
1042 /* This displays a message in the host editor's main window.
1043  * Used mostly for error messages.
1044  */
HostWinMsg(const char * msg)1045 void HostWinMsg(const char *msg)
1046 {
1047 	int maxy;
1048 
1049 	maxy = getmaxy(gHostWin);
1050 	DrawStrAt(gHostWin, maxy - 2, 0, msg);
1051 	wclrtoeol(gHostWin);
1052 	wmove(gHostWin, maxy - 1, 0);
1053 	wrefresh(gHostWin);
1054 	BEEP(1);
1055 	gNeedToClearMsg = 1;
1056 }	/* HostWinMsg */
1057 
1058 
1059 
1060 
1061 /* Prompts for a line of input. */
HostWinGetStr(char * str,size_t size)1062 void HostWinGetStr(char *str, size_t size)
1063 {
1064 	WGetsParams wgp;
1065 	int maxy, maxx;
1066 
1067 	getmaxyx(gHostWin, maxy, maxx);
1068 	DrawStrAt(gHostWin, maxy - 1, 0, "/");
1069 	wclrtoeol(gHostWin);
1070 	wrefresh(gHostWin);
1071 	curs_set(1);
1072 	wgp.w = gHostWin;
1073 	wgp.sy = maxy - 1;
1074 	wgp.sx = 1;
1075 	wgp.fieldLen = maxx - 1;
1076 	wgp.dst = str;
1077 	wgp.dstSize = size;
1078 	wgp.useCurrentContents = 0;
1079 	wgp.echoMode = wg_RegularEcho;
1080 	wgp.history = wg_NoHistory;
1081 	(void) wg_Gets(&wgp);
1082 	cbreak();						/* wg_Gets turns off cbreak and delay. */
1083 
1084 	wmove(gHostWin, maxy - 1, 0);
1085 	wclrtoeol(gHostWin);
1086 	wrefresh(gHostWin);
1087 	curs_set(0);
1088 }	/* HostWinGetStr */
1089 
1090 
1091 
1092 
1093 /*ARGSUSED*/
1094 static void
1095 #if (defined(__GNUC__)) && (__GNUC__ >= 2)
1096 __attribute__ ((noreturn))
1097 #endif
SigIntHostWin(int UNUSED (sig))1098 SigIntHostWin(int UNUSED(sig))
1099 {
1100 	LIBNCFTP_USE_VAR(sig);
1101 	alarm(0);
1102 #ifdef HAVE_SIGSETJMP
1103 	siglongjmp(gHostWinJmp, 1);
1104 #else	/* HAVE_SIGSETJMP */
1105 	longjmp(gHostWinJmp, 1);
1106 #endif	/* HAVE_SIGSETJMP */
1107 }	/* SigIntHostWin */
1108 
1109 
1110 
1111 static void
WriteSelectedBMToFile(char * bookmarkName)1112 WriteSelectedBMToFile(char *bookmarkName)
1113 {
1114 	FILE *fp;
1115 
1116 	fp = fopen(gBookmarkSelectionFile, "w");
1117 	if (fp == NULL)
1118 		return;
1119 	(void) fprintf(fp, "%s\n", bookmarkName);
1120 	(void) fclose(fp);
1121 }	/* WriteSelectedBMToFile */
1122 
1123 
1124 
1125 static void
LaunchNcFTP(char * bookmarkName)1126 LaunchNcFTP(char *bookmarkName)
1127 {
1128 	char *av[8];
1129 
1130 	EndWin();
1131 
1132 	av[0] = strdup("ncftp");
1133 	av[1] = strdup(bookmarkName);
1134 	av[2] = NULL;
1135 
1136 #ifdef NCFTPPATH
1137 	(void) execv(NCFTPPATH, av);
1138 #else
1139 	(void) execvp(av[0], av);
1140 #endif
1141 	free(av[0]);
1142 	free(av[1]);
1143 }	/* LaunchNcFTP */
1144 
1145 
1146 
1147 
1148 
1149 /* Runs the host editor.  Another big use for this is to open sites
1150  * that are in your host list.
1151  */
HostWindow(void)1152 int HostWindow(void)
1153 {
1154 	int c;
1155 	char cmd[256];
1156 	volatile BookmarkPtr toOpen;
1157 	vsigproc_t si;
1158 	int maxy, maxx;
1159 	int lmaxy;
1160 
1161 	si = (sigproc_t) (-1);
1162 	if (gWinInit) {
1163 		gHostListWin = NULL;
1164 		gHostWin = NULL;
1165 
1166 		gHostWin = newwin(LINES, COLS, 0, 0);
1167 		if (gHostWin == NULL)
1168 			return (-1);
1169 
1170 		curs_set(0);
1171 		cbreak();
1172 
1173 		/* Set the clear flag for the first update. */
1174 		wclear(gHostWin);
1175 		keypad(gHostWin, TRUE);		/* For arrow keys. */
1176 #ifdef HAVE_NOTIMEOUT
1177 		notimeout(gHostWin, TRUE);
1178 #endif
1179 
1180 #ifdef HAVE_SIGSETJMP
1181 		if (sigsetjmp(gHostWinJmp, 1) == 0) {
1182 #else	/* HAVE_SIGSETJMP */
1183 		if (setjmp(gHostWinJmp) == 0) {
1184 #endif	/* HAVE_SIGSETJMP */
1185 			/* Gracefully cleanup the screen if the user ^C's. */
1186 			si = NcSignal(SIGINT, SigIntHostWin);
1187 
1188 			/* Initialize the page start and select a host to be
1189 			 * the current one.
1190 			 */
1191 			gHostListWinStart = 0;
1192 			gHilitedHost = 0;
1193 			if (gNumBookmarks == 0)
1194 				gCurHostListItem = NULL;
1195 			else
1196 				gCurHostListItem = &gBookmarkTable[gHilitedHost];
1197 
1198 			/* Initially, we don't want to connect to any site in
1199 			 * the host list.
1200 			 */
1201 			toOpen = NULL;
1202 
1203 			getmaxyx(gHostWin, maxy, maxx);
1204 			WAttr(gHostWin, kBold, 1);
1205 			WAddCenteredStr(gHostWin, 0, "NcFTP Bookmark Editor");
1206 			WAttr(gHostWin, kBold, 0);
1207 
1208 			DrawStrAt(gHostWin, 3, 2, "Open selected site:       <enter>");
1209 			DrawStrAt(gHostWin, 4, 2, "Edit selected site:       /ed");
1210 			DrawStrAt(gHostWin, 5, 2, "Delete selected site:     /del");
1211 			DrawStrAt(gHostWin, 6, 2, "Duplicate selected site:  /dup");
1212 			DrawStrAt(gHostWin, 7, 2, "Add a new site:           /new");
1213 			DrawStrAt(gHostWin, 9, 2, "Up one:                   <u>");
1214 			DrawStrAt(gHostWin, 10, 2, "Down one:                 <d>");
1215 			DrawStrAt(gHostWin, 11, 2, "Previous page:            <p>");
1216 			DrawStrAt(gHostWin, 12, 2, "Next page:                <n>");
1217 			DrawStrAt(gHostWin, 14, 2, "Capital letters selects first");
1218 			DrawStrAt(gHostWin, 15, 2, "  site starting with the letter.");
1219 			DrawStrAt(gHostWin, 17, 2, "Exit the bookmark editor: <x>");
1220 
1221 			/* Initialize the scrolling host list window. */
1222 			if (maxx < 110) {
1223 				gHostListWinWide = 0;
1224 				gHostListWin = subwin(
1225 					gHostWin,
1226 					LINES - 7,
1227 					40,
1228 					3,
1229 					COLS - 40 - 2
1230 				);
1231 			} else {
1232 				gHostListWinWide = COLS - 42;
1233 				gHostListWin = subwin(
1234 					gHostWin,
1235 					LINES - 7,
1236 					gHostListWinWide,
1237 					3,
1238 					38
1239 				);
1240 			}
1241 
1242 			if (gHostListWin == NULL)
1243 				return (-1);
1244 			lmaxy = getmaxy(gHostListWin);
1245 			gHostListPageSize = lmaxy;
1246 			DrawHostList();
1247 			wmove(gHostWin, maxy - 1, 0);
1248 			UpdateHostWindows(1);
1249 
1250 			for (;;) {
1251 				c = HostWinGetKey();
1252 				if (gNeedToClearMsg) {
1253 					wmove(gHostWin, maxy - 2, 0);
1254 					wclrtoeol(gHostWin);
1255 					wrefresh(gHostWin);
1256 				}
1257 				if ((c >= 'A') && (c <= 'Z')) {
1258 					/* isupper can coredump if wgetch returns a meta key. */
1259 					HostWinZoomTo(c);
1260 				} else if (c == '/') {
1261 					/* Get an "extended" command.  Sort of like vi's
1262 					 * :colon commands.
1263 					 */
1264 					HostWinGetStr(cmd, sizeof(cmd));
1265 
1266 					if (ISTREQ(cmd, "ed"))
1267 						HostWinEdit();
1268 					else if (ISTREQ(cmd, "dup"))
1269 						HostWinDup();
1270 					else if (ISTREQ(cmd, "del"))
1271 						HostWinDelete();
1272 					else if (ISTREQ(cmd, "new"))
1273 						HostWinNew();
1274 					else
1275 						HostWinMsg("Invalid bookmark editor command.");
1276 				} else switch(c) {
1277 					case 10:	/* ^J == newline */
1278 						goto enter;
1279 					case 13:	/* ^M == carriage return */
1280 						goto enter;
1281 #ifdef KEY_ENTER
1282 					case KEY_ENTER:
1283 						Trace(1, "  [0x%04X, %s]\n", c, "ENTER");
1284 #endif
1285 enter:
1286 						if (gCurHostListItem == NULL)
1287 							HostWinMsg("Nothing to open.  Try 'open sitename' from the main screen.");
1288 						else {
1289 							toOpen = (BookmarkPtr) gCurHostListItem;
1290 							goto done;
1291 						}
1292 						break;
1293 
1294 					case kControl_L:
1295 						UpdateHostWindows(1);
1296 						break;
1297 
1298 					case 'u':
1299 					case 'k':	/* vi up key */
1300 					case 'h':	/* vi left key */
1301 						HostListLineUp();
1302 						break;
1303 #ifdef KEY_UP
1304 					case KEY_UP:
1305 						Trace(1, "  [0x%04X, %s]\n", c, "UP");
1306 						HostListLineUp();
1307 						break;
1308 #endif
1309 
1310 #ifdef KEY_LEFT
1311 					case KEY_LEFT:
1312 						Trace(1, "  [0x%04X, %s]\n", c, "LEFT");
1313 						HostListLineUp();
1314 						break;
1315 #endif
1316 
1317 					case 'd':
1318 					case 'j':	/* vi down key */
1319 					case 'l':	/* vi right key */
1320 						HostListLineDown();
1321 						break;
1322 
1323 #ifdef KEY_DOWN
1324 					case KEY_DOWN:
1325 						Trace(1, "  [0x%04X, %s]\n", c, "DOWN");
1326 						HostListLineDown();
1327 						break;
1328 #endif
1329 
1330 #ifdef KEY_RIGHT
1331 					case KEY_RIGHT:
1332 						Trace(1, "  [0x%04X, %s]\n", c, "RIGHT");
1333 						HostListLineDown();
1334 						break;
1335 #endif
1336 
1337 					case 'p':
1338 						HostListPageUp();
1339 						break;
1340 
1341 #ifdef KEY_PPAGE
1342 					case KEY_PPAGE:
1343 						Trace(1, "  [0x%04X, %s]\n", c, "PPAGE");
1344 						HostListPageUp();
1345 						break;
1346 #endif
1347 
1348 					case 'n':
1349 						HostListPageDown();
1350 						break;
1351 
1352 #ifdef KEY_NPAGE
1353 					case KEY_NPAGE:
1354 						Trace(1, "  [0x%04X, %s]\n", c, "NPAGE");
1355 						HostListPageDown();
1356 						break;
1357 #endif
1358 
1359 					case 'x':
1360 					case 'q':
1361 						goto done;
1362 
1363 					default:
1364 						HostWinMsg("Invalid key.");
1365 						Trace(1, "  [0x%04X, %s]\n", c, "<invalid>");
1366 						break;
1367 				}
1368 			}
1369 		}
1370 		NcSignal(SIGINT, (FTPSigProc) SIG_IGN);
1371 done:
1372 		if (gHostListWin != NULL)
1373 			delwin(gHostListWin);
1374 		if (gHostWin != NULL)
1375 			delwin(gHostWin);
1376 		gHostListWin = gHostWin = NULL;
1377 		if (si != (sigproc_t) (-1))
1378 			NcSignal(SIGINT, si);
1379 		if (toOpen != (BookmarkPtr) 0) {
1380 			/* If the user selected a site to open, connect to it now. */
1381 			if (gStandAlone != 0) {
1382 				LaunchNcFTP(toOpen->bookmarkName);
1383 				/*NOTREACHED*/
1384 				Exit(0);
1385 			} else if (gBookmarkSelectionFile != NULL) {
1386 				WriteSelectedBMToFile(toOpen->bookmarkName);
1387 			}
1388 			return (kNoErr);
1389 		}
1390 	}
1391 	return (kNoErr);
1392 }	/* HostWindow */
1393 
1394 
1395 
1396 
1397 main_void_return_t
1398 main(int argc, const char **argv)
1399 {
1400 	int result;
1401 	int argi;
1402 
1403 	gStandAlone = 1;
1404 	gBookmarkSelectionFile = NULL;
1405 
1406 	InitUserInfo();
1407 	if (LoadBookmarkTable() < 0) {
1408 		exit(7);
1409 	}
1410 	if (argc > 1) {
1411 		/* The following hack is used by NcFTP
1412 		 * to get the number of columns without
1413 		 * having to link with curses/termcap.
1414 		 * This simplifies things since the
1415 		 * system may or may not have a good
1416 		 * curses implementation, and we don't
1417 		 * want to complicate NcFTP itself with
1418 		 * that.
1419 		 */
1420 		argi = 1;
1421 		if (strcmp(argv[1], "--dimensions") == 0) {
1422 			result = PrintDimensions(0);
1423 			exit((result == 0) ? 0 : 1);
1424 		} else if (strcmp(argv[1], "--dimensions-terse") == 0) {
1425 			result = PrintDimensions(1);
1426 			exit((result == 0) ? 0 : 1);
1427 		} else if (strcmp(argv[1], "--debug") == 0) {
1428 			SetDebug(1);
1429 			argi++;
1430 		}
1431 		/* Requested that we were run from ncftp. */
1432 		gStandAlone = 0;
1433 		if ((argc > argi) && (argv[argi][0] == '/'))
1434 			gBookmarkSelectionFile = (const char *) argv[argi];
1435 		if (gNumBookmarks < 1)
1436 			exit(7);
1437 	}
1438 
1439 	result = FTPInitLibrary(&gLib);
1440 	if (result < 0) {
1441 		(void) fprintf(stderr, "ncftp: init library error %d (%s).\n", result, FTPStrError(result));
1442 		exit(1);
1443 	}
1444 
1445 	result = FTPInitConnectionInfo(&gLib, &gConn, kDefaultFTPBufSize);
1446 	if (result < 0) {
1447 		(void) fprintf(stderr, "ncftp: init connection info error %d (%s).\n", result, FTPStrError(result));
1448 		exit(1);
1449 	}
1450 
1451 	if (gDebug > 0)
1452 		OpenTrace();
1453 	InitPrefs();
1454 	LoadFirewallPrefs(0);
1455 	LoadPrefs();
1456 
1457 	InitWindows();
1458 	Trace(1, "Terminal size is %d columns by %d rows.\n", gScreenWidth, gScreenHeight);
1459 	HostWindow();
1460 	if (gDebug > 0)
1461 		CloseTrace();
1462 	EndWin();
1463 	exit(0);
1464 }	/* main */
1465