home *** CD-ROM | disk | FTP | other *** search
/ ftp.ncftp.com / ftp.ncftp.com.zip / ftp.ncftp.com / ncftp / older_versions / ncftp-3.2.2-src.tar.bz2 / ncftp-3.2.2-src.tar / ncftp-3.2.2 / vis / bmed.c next >
C/C++ Source or Header  |  2005-01-01  |  35KB  |  1,456 lines

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