home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / ncftp-2.3.0-src.tgz / tar.out / contrib / ncftp / Hostwin.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  28KB  |  1,155 lines

  1. /* Hostwin.c */
  2.  
  3. #include "Sys.h"
  4. #include "Curses.h"
  5.  
  6. #include <ctype.h>
  7. #include <signal.h>
  8. #include <setjmp.h>
  9.  
  10. #include "Util.h"
  11. #include "Cmds.h"
  12. #include "Bookmark.h"
  13. #include "Open.h"
  14. #include "Hostwin.h"
  15.  
  16. #ifdef USE_CURSES
  17.  
  18. #include "WGets.h"
  19.  
  20. /* This is the full-screen window that pops up when you run the
  21.  * host editor.  Not much is done with it, except display directions
  22.  * and getting host editor commands.
  23.  */
  24. WINDOW *gHostWin = NULL;
  25.  
  26. /* This is a window devoted solely to serve as a scrolling list
  27.  * of bookmarks.
  28.  */
  29. WINDOW *gHostListWin = NULL;
  30.  
  31. /* This is another full-screen window that opens when a user wants
  32.  * to edit the parameters for a site.
  33.  */
  34. WINDOW *gEditHostWin = NULL;
  35.  
  36. extern void WAddCenteredStr(WINDOW *w, int y, char *str);
  37. void WAttr(WINDOW *w, int attr, int on);
  38.  
  39. #endif    /* USE_CURSES */
  40.  
  41. /* This is an index into the host list.  This indicates the position
  42.  * in the host list where we draw the current "page" of the host list.
  43.  */
  44. int gHostListWinStart;
  45.  
  46. /* This index is the currently selected host.  This index must be >=
  47.  * to gHostListWinStart and less than gHostListWinStart + pageLen - 1,
  48.  * so that this host will show up in the current page of the host list.
  49.  */
  50. int gHilitedHost;
  51.  
  52. /* This is a pointer to the actual information of the currently
  53.  * selected host.
  54.  */
  55. BookmarkPtr gCurHostListItem;
  56.  
  57. /* How many lines compose a "page" in the host list's scrolling window. */
  58. int gHostListPageSize;
  59.  
  60. /* A flag saying if we need to erase a message after the next input key. */
  61. int gNeedToClearMsg = 0;
  62.  
  63. /* When we edit gCurHostListItem's stuff, we actually edit a copy of it.
  64.  * This is so we could restore the information if the user wanted to
  65.  * abort the changes.
  66.  */
  67. Bookmark gEditRsi;
  68.  
  69. jmp_buf gHostWinJmp;
  70.  
  71. extern int gWinInit, gNumBookmarks, gRmtInfoIsNew;
  72. extern BookmarkPtrList gBookmarks;
  73. extern BookmarkPtr gHosts;
  74. extern Bookmark gRmtInfo;
  75. extern string gEmailAddress, gAnonPassword;
  76. extern unsigned int gFTPPort;
  77.  
  78. void AtoIMaybe(int *dst, char *str)
  79. {
  80.     char *cp;
  81.     
  82.     /* Don't change the value if the user just hit return. */
  83.     for (cp = str; *cp != '\0'; cp++)
  84.         if (isdigit(*cp))
  85.             break;
  86.     if (isdigit(*cp))
  87.         *dst = atoi(str);
  88. }    /* AtoIMaybe */
  89.  
  90.  
  91.  
  92.  
  93. #ifdef USE_CURSES
  94.  
  95. /* Draws the screen when we're using the host editor's main screen.
  96.  * You can can specify whether to draw each character whether it needs
  97.  * it or not if you like.
  98.  */
  99. void UpdateHostWindows(int uptAll)
  100. {
  101.     if (uptAll) {
  102.         DrawHostList();
  103.         touchwin(gHostListWin);
  104.         touchwin(gHostWin);
  105.     }
  106.     wnoutrefresh(gHostListWin);
  107.     wnoutrefresh(gHostWin);
  108.     doupdate();
  109. }    /* UpdateHostWindows */
  110.  
  111.  
  112.  
  113. /* This draws the scrolling list of bookmarks, and hilites the currently
  114.  * selected host.
  115.  */
  116. void DrawHostList(void)
  117. {
  118.     int lastLine, i;
  119.     BookmarkPtr rsip;
  120.     string str;
  121.     int maxy, maxx;
  122.     int lmaxy, lmaxx;
  123.     int begy, begx;
  124.     char spec[32];
  125.  
  126.     getmaxyx(gHostListWin, lmaxy, lmaxx);
  127.     getbegyx(gHostListWin, begy, begx);
  128.     getmaxyx(gHostWin, maxy, maxx);
  129.     /* We have a status line saying how many bookmarks there are in
  130.      * the list.  That way the user knows something is supposed to
  131.      * be there when the host list is totally empty, and also that
  132.      * there are more bookmarks to look at when the entire host list
  133.      * doesn't fit in the scroll window.
  134.      */
  135.     WAttr(gHostWin, kUnderline, 1);
  136.     mvwprintw(
  137.         gHostWin,
  138.         begy - 1,
  139.         begx,
  140.         "%s",
  141.         "Number of bookmarks"
  142.     );
  143.     WAttr(gHostWin, kUnderline, 0);
  144.     wprintw(
  145.         gHostWin,
  146.         ": %3d",
  147.         gNumBookmarks
  148.     );
  149.     sprintf(spec, "%%-16s %%-%ds", lmaxx - 17);
  150.     lastLine = lmaxy + gHostListWinStart;
  151.     for (i=gHostListWinStart; (i<lastLine) && (i<gNumBookmarks); i++) {
  152.         rsip = gBookmarks[i];
  153.         if (rsip == gCurHostListItem)
  154.             WAttr(gHostListWin, kReverse, 1);
  155.         sprintf(str, spec, rsip->bookmarkName, rsip->name);
  156.         str[lmaxx] = '\0';
  157.         mvwaddstr(gHostListWin, i - gHostListWinStart, 0, str);
  158.         if (rsip == gCurHostListItem)
  159.             WAttr(gHostListWin, kReverse, 0);
  160.     }
  161.  
  162.     /* Add 'vi' style empty-lines. */
  163.     for ( ; i<lastLine; ++i) {
  164.         mvwaddstr(gHostListWin, i - gHostListWinStart, 0, "~");
  165.         wclrtoeol(gHostListWin);
  166.     }
  167.     wmove(gHostWin, maxy - 3, 2);
  168.     sprintf(spec, "%%-%ds", maxx - 4);
  169.     if ((gCurHostListItem == NULL) || (gCurHostListItem->comment[0] == '\0'))
  170.         str[0] = '\0';
  171.     else {
  172.         STRNCPY(str, "``");
  173.         STRNCAT(str, gCurHostListItem->comment);
  174.         AbbrevStr(str + 2, gCurHostListItem->comment, maxx - 8, 1);
  175.         STRNCAT(str, "''");
  176.     }
  177.     wprintw(gHostWin, spec, str);
  178.     wmove(gHostWin, maxy - 1, 0);
  179.     UpdateHostWindows(0);
  180. }    /* DrawHostList */
  181.  
  182.  
  183.  
  184.  
  185. /* This prompts for a key of input when in the main host editor window. */
  186. int HostWinGetKey(void)
  187. {
  188.     int c;
  189.     int maxy, maxx;
  190.  
  191.     getmaxyx(gHostWin, maxy, maxx);
  192.     wmove(gHostWin, maxy - 1, 0);
  193.     c = wgetch(gHostWin);
  194.     TraceMsg("[%c, 0x%x]\n", c, c);
  195.     return (c);
  196. }    /* HostWinGetKey */
  197.  
  198.  
  199.  
  200. static
  201. void NewHilitedHostIndex(int newIdx)
  202. {
  203.     int oldIdx, lastLine;
  204.  
  205.     if (gNumBookmarks <= 0) {
  206.         HostWinMsg(
  207. "No bookmarks in the list.  Try a /new, or open a site manually to add one.");
  208.     } else {
  209.         oldIdx = gHilitedHost;
  210.         if (gNumBookmarks < gHostListPageSize)
  211.             lastLine = gHostListWinStart + gNumBookmarks - 1;
  212.         else
  213.             lastLine = gHostListWinStart + gHostListPageSize - 1;
  214.         if (newIdx < gHostListWinStart) {
  215.             /* Will need to scroll the window up. */
  216.             if (newIdx < 0) {
  217.                 newIdx = 0;
  218.                 if (oldIdx == newIdx)
  219.                     HostWinMsg("You are at the top of the list.");
  220.             }
  221.             gHilitedHost = gHostListWinStart = newIdx;
  222.         } else if (newIdx > lastLine) {
  223.             /* Will need to scroll the window down. */
  224.             if (newIdx > (gNumBookmarks - 1)) {
  225.                 newIdx = gNumBookmarks - 1;
  226.                 if (oldIdx == newIdx)
  227.                     HostWinMsg("You are at the bottom of the list.");
  228.             }
  229.             gHilitedHost = newIdx;
  230.             gHostListWinStart = newIdx - (gHostListPageSize - 1);
  231.             if (gHostListWinStart < 0)
  232.                 gHostListWinStart = 0;
  233.         } else {
  234.             /* Don't need to scroll window, just move pointer. */
  235.             gHilitedHost = newIdx;
  236.         }
  237.         gCurHostListItem = gBookmarks[gHilitedHost];
  238.         if (oldIdx != newIdx)
  239.             DrawHostList();
  240.     }
  241. }    /* NewHilitedHostIndex */
  242.  
  243.  
  244.  
  245.  
  246. /* You can zip to a different area of the list without using the arrow
  247.  * or page scrolling keys.  Just type a letter, and the list will scroll
  248.  * to the first host starting with that letter.
  249.  */
  250. void HostWinZoomTo(int c)
  251. {    
  252.     int i, j;
  253.  
  254.     if (gNumBookmarks > 0) {
  255.         if (islower(c))
  256.             c = toupper(c);
  257.         for (i=0; i<gNumBookmarks - 1; i++) {
  258.             j = gBookmarks[i]->bookmarkName[0];
  259.             if (islower(j))
  260.                 j = toupper(j);
  261.             if (j >= c)
  262.                 break;
  263.         }
  264.         NewHilitedHostIndex(i);
  265.     } else {
  266.         HostWinMsg("No bookmarks to select.  Try a /new.");
  267.     }
  268.     DrawHostList();
  269. }    /* HostWinZoomTo */
  270.  
  271.  
  272.  
  273.  
  274.  
  275. void HostListLineUp(void)
  276. {
  277.     NewHilitedHostIndex(gHilitedHost - 1);
  278. }    /* HostListLineUp */
  279.  
  280.  
  281.  
  282.  
  283.  
  284. void HostListLineDown(void)
  285. {
  286.     NewHilitedHostIndex(gHilitedHost + 1);
  287. }    /* HostListLineDown */
  288.  
  289.  
  290.  
  291.  
  292. void HostListPageUp(void)
  293. {
  294.     NewHilitedHostIndex(gHilitedHost - gHostListPageSize);
  295. }    /* HostListPageUp */
  296.  
  297.  
  298.  
  299.  
  300. void HostListPageDown(void)
  301. {
  302.     NewHilitedHostIndex(gHilitedHost + gHostListPageSize);
  303. }    /* HostListPageDown */
  304.  
  305.  
  306.  
  307. /* This marks the start of a section that belongs to the Bookmark Options
  308.  * window.  This window pops up on top of the host editor's main window
  309.  * when you wish to edit a site's settings.  When the user finishes,
  310.  * we close it and the host editor resumes.
  311.  */
  312.  
  313. /* This displays a message in the Bookmark Options window. */
  314. void EditHostWinMsg(char *msg)
  315. {
  316.     int maxy, maxx;
  317.  
  318.     getmaxyx(gEditHostWin, maxy, maxx);
  319.     mvwaddstr(gEditHostWin, maxy - 2, 0, msg);
  320.     wclrtoeol(gEditHostWin);
  321.     wmove(gEditHostWin, maxy - 1, 0);
  322.     wrefresh(gEditHostWin);
  323. }    /* EditHostWinMsg */
  324.  
  325.  
  326.  
  327.  
  328. /* Prompts for a line of input. */
  329. void EditHostWinGetStr(char *dst, size_t size, int canBeEmpty, int canEcho)
  330. {
  331.     string str;
  332.     WGetsParams wgp;
  333.     int maxy, maxx;
  334.  
  335.     WAttr(gEditHostWin, kBold, 1);
  336.     getmaxyx(gEditHostWin, maxy, maxx);
  337.     mvwaddstr(gEditHostWin, maxy - 1, 0, "> ");
  338.     WAttr(gEditHostWin, kBold, 0);
  339.     wclrtoeol(gEditHostWin);
  340.     wrefresh(gEditHostWin);
  341.     curs_set(1);
  342.  
  343.     wgp.w = gEditHostWin;
  344.     wgp.sy = maxy - 1;
  345.     wgp.sx = 2;
  346.     wgp.fieldLen = maxx - 3;
  347.     wgp.dst = str;
  348.     wgp.dstSize = size;
  349.     wgp.useCurrentContents = 0;
  350.     wgp.echoMode = canEcho ? wg_RegularEcho : wg_BulletEcho;
  351.     wgp.history = wg_NoHistory;
  352.     (void) wg_Gets(&wgp);
  353.     cbreak();                        /* wg_Gets turns off cbreak and delay. */
  354.  
  355.     TraceMsg("[%s]\n", wgp.dst);
  356.     
  357.     /* See if the user just hit return.  We may not want to overwrite
  358.      * the dst here, which would make it an empty string.
  359.      */
  360.     if ((wgp.changed) || (canBeEmpty == kOkayIfEmpty))
  361.         strcpy(dst, str);
  362.  
  363.     wmove(gEditHostWin, maxy - 1, 0);
  364.     wclrtoeol(gEditHostWin);
  365.     wrefresh(gEditHostWin);
  366.     curs_set(0);
  367. }    /* EditHostWinGetStr */
  368.  
  369.  
  370.  
  371.  
  372.  
  373. /* Prompts for an integer of input. */
  374. void EditHostWinGetNum(int *dst)
  375. {
  376.     WGetsParams wgp;
  377.     string str;
  378.     int maxy, maxx;
  379.  
  380.     getmaxyx(gEditHostWin, maxy, maxx);
  381.     WAttr(gEditHostWin, kBold, 1);
  382.     mvwaddstr(gEditHostWin, maxy - 1, 0, "> ");
  383.     WAttr(gEditHostWin, kBold, 0);
  384.     wclrtoeol(gEditHostWin);
  385.     wrefresh(gEditHostWin);
  386.     curs_set(1);
  387.  
  388.     wgp.w = gEditHostWin;
  389.     wgp.sy = maxy - 1;
  390.     wgp.sx = 2;
  391.     wgp.fieldLen = maxx - 3;
  392.     wgp.dst = str;
  393.     wgp.dstSize = sizeof(str);
  394.     wgp.useCurrentContents = 0;
  395.     wgp.echoMode = wg_RegularEcho;
  396.     wgp.history = wg_NoHistory;
  397.     (void) wg_Gets(&wgp);
  398.     cbreak();                        /* wg_Gets turns off cbreak and delay. */
  399.  
  400.     TraceMsg("[%s]\n", str);
  401.     AtoIMaybe(dst, str);
  402.     wmove(gEditHostWin, maxy - 1, 0);
  403.     wclrtoeol(gEditHostWin);
  404.     wrefresh(gEditHostWin);
  405.     curs_set(0);
  406. }    /* EditHostWinGetNum */
  407.  
  408.  
  409.  
  410.  
  411. /* This is the meat of the site options window.  We can selectively update
  412.  * portions of the window by using a bitmask with bits set for items
  413.  * we want to update.
  414.  */
  415. void EditHostWinDraw(int flags, int hilite)
  416. {
  417.     int maxy, maxx;
  418.     int i, f;
  419.     string str;
  420.     char spec[32];
  421.     char *cp;
  422.  
  423.     /* Draw the keys the user can type in reverse text. */
  424.     WAttr(gEditHostWin, kReverse, 1);
  425.     f = 5;
  426.     for (i = kFirstEditWindowItem; i <= kLastEditWindowItem; i++) {
  427.         if (TESTBIT(flags, i))
  428.             mvwaddch(gEditHostWin, f + i, 2, 'A' + i);
  429.     }
  430.     
  431.     /* The "quit" item is a special item that is offset a line, and
  432.      * always has the "X" key assigned to it.
  433.      */
  434.     i = kQuitEditWindowItem;
  435.     if (TESTBIT(flags, i))
  436.         mvwaddch(gEditHostWin, 1 + f + i, 2, 'X');
  437.     WAttr(gEditHostWin, kReverse, 0);
  438.     
  439.     /* We can use this to hilite a whole line, to indicate to the
  440.      * user that a certain item is being edited.
  441.      */
  442.     if (hilite)
  443.         WAttr(gEditHostWin, kReverse, 1);
  444.     getmaxyx(gEditHostWin, maxy, maxx);
  445.     sprintf(spec, " %%-26s%%-%ds",
  446.         maxx - 32);
  447.  
  448.     /* Now draw the items on a case-by-case basis. */
  449.     if (TESTBIT(flags, kNicknameEditWindowItem)) {
  450.         mvwprintw(gEditHostWin, kNicknameEditWindowItem + f, 3, spec,
  451.             "Bookmark name:",
  452.             gEditRsi.bookmarkName
  453.         );
  454.         wclrtoeol(gEditHostWin);
  455.     }
  456.     if (TESTBIT(flags, kHostnameEditWindowItem)) {
  457.         mvwprintw(gEditHostWin, kHostnameEditWindowItem + f, 3, spec,
  458.             "Hostname:",
  459.             gEditRsi.name
  460.         );
  461.         wclrtoeol(gEditHostWin);
  462.     }
  463.     if (TESTBIT(flags, kUserEditWindowItem)) {
  464.         mvwprintw(gEditHostWin, kUserEditWindowItem + f, 3, spec,
  465.             "User:",
  466.             gEditRsi.user[0] == '\0' ? "anonymous" : gEditRsi.user
  467.         );
  468.         wclrtoeol(gEditHostWin);
  469.     }
  470.     if (TESTBIT(flags, kPassEditWindowItem)) {
  471.         if (gEditRsi.pass[0] == '\0' && gEditRsi.user[0] == '\0')
  472.             STRNCPY(str, gAnonPassword);
  473.         mvwprintw(gEditHostWin, kPassEditWindowItem + f, 3, spec,
  474.             "Password:",
  475.             strcmp(str, gAnonPassword) ? "********" : str
  476.         );
  477.         wclrtoeol(gEditHostWin);
  478.     }
  479.     if (TESTBIT(flags, kAcctEditWindowItem)) {
  480.         mvwprintw(gEditHostWin, kAcctEditWindowItem + f, 3, spec,
  481.             "Account:",
  482.             gEditRsi.acct[0] == '\0' ? "none" : gEditRsi.acct
  483.         );
  484.         wclrtoeol(gEditHostWin);
  485.     }
  486.     if (TESTBIT(flags, kDirEditWindowItem)) {
  487.         if (gEditRsi.dir[0] == '\0')
  488.             STRNCPY(str, "/");
  489.         else
  490.             AbbrevStr(str, gEditRsi.dir, maxx - 32, 0);
  491.         mvwprintw(gEditHostWin, kDirEditWindowItem + f, 3, spec,
  492.             "Directory:",
  493.             str
  494.         );
  495.         wclrtoeol(gEditHostWin);
  496.     }
  497.     if (TESTBIT(flags, kXferTypeEditWindowItem)) {
  498.         if ((gEditRsi.xferType == 'I') || (gEditRsi.xferType == 'B'))
  499.             cp = "Binary";
  500.         else if (gEditRsi.xferType == 'A')
  501.             cp = "ASCII Text";
  502.         else
  503.             cp = "Tenex";
  504.         mvwprintw(gEditHostWin, kXferTypeEditWindowItem + f, 3, spec,
  505.             "Transfer type:",
  506.             cp
  507.         );
  508.         wclrtoeol(gEditHostWin);
  509.     }
  510.     if (TESTBIT(flags, kPortEditWindowItem)) {
  511.         sprintf(str, "%u", gEditRsi.port);
  512.         mvwprintw(gEditHostWin, kPortEditWindowItem + f, 3, spec,
  513.             "Port:",
  514.             str
  515.         );
  516.         wclrtoeol(gEditHostWin);
  517.     }
  518.     if (TESTBIT(flags, kSizeEditWindowItem)) {
  519.         mvwprintw(gEditHostWin, kSizeEditWindowItem + f, 3, spec,
  520.             "Has SIZE command:",
  521.             gEditRsi.hasSIZE ? "Yes" : "No"
  522.         );
  523.         wclrtoeol(gEditHostWin);
  524.     }
  525.     if (TESTBIT(flags, kMdtmEditWindowItem)) {
  526.         mvwprintw(gEditHostWin, kMdtmEditWindowItem + f, 3, spec,
  527.             "Has MDTM command:",
  528.             gEditRsi.hasMDTM ? "Yes" : "No"
  529.         );
  530.         wclrtoeol(gEditHostWin);
  531.     }
  532.     if (TESTBIT(flags, kPasvEditWindowItem)) {
  533.         mvwprintw(gEditHostWin, kPasvEditWindowItem + f, 3, spec,
  534.             "Can use passive FTP:",
  535.             gEditRsi.hasPASV ? "Yes" : "No"
  536.         );
  537.         wclrtoeol(gEditHostWin);
  538.     }
  539.     if (TESTBIT(flags, kOSEditWindowItem)) {
  540.         mvwprintw(gEditHostWin, kOSEditWindowItem + f, 3, spec,
  541.             "Operating System:",
  542.             (gEditRsi.isUnix == 1) ? "UNIX" : "Non-UNIX"
  543.         );
  544.         wclrtoeol(gEditHostWin);
  545.     } 
  546.     if (TESTBIT(flags, kCommentEditWindowItem)) {
  547.         if (gEditRsi.comment[0] == '\0')
  548.             STRNCPY(str, "(none)");
  549.         else
  550.             AbbrevStr(str, gEditRsi.comment, maxx - 32, 0);
  551.         mvwprintw(gEditHostWin, kCommentEditWindowItem + f, 3, spec,
  552.             "Comment:",
  553.             str
  554.         );
  555.         wclrtoeol(gEditHostWin);
  556.     }
  557.     if (TESTBIT(flags, kQuitEditWindowItem)) {
  558.         mvwprintw(gEditHostWin, kQuitEditWindowItem + f + 1, 3, spec,
  559.             "(Done editing)",
  560.             ""
  561.         );
  562.         wclrtoeol(gEditHostWin);
  563.     }
  564.  
  565.     if (hilite)
  566.         WAttr(gEditHostWin, kReverse, 0);
  567.  
  568.     wmove(gEditHostWin, maxy - 1, 0);
  569.     wrefresh(gEditHostWin);
  570. }    /* EditHostWinDraw */
  571.  
  572.  
  573.  
  574. /* The user can hit space to change the transfer type.  For these toggle
  575.  * functions we do an update each time so the user can see the change
  576.  * immediately.
  577.  */
  578. void ToggleXferType(void)
  579. {
  580.     int c;
  581.  
  582.     while (1) {
  583.         c = wgetch(gEditHostWin);
  584.         TraceMsg("[%c, 0x%x]\n", c, c);
  585.         if ((c == 'x') || (c == 10) || (c == 13)
  586. #ifdef KEY_ENTER
  587.             || (c == KEY_ENTER)
  588. #endif
  589.             )
  590.             break;
  591.         else if (isspace(c)) {
  592.             if (gEditRsi.xferType == 'A')
  593.                 gEditRsi.xferType = 'I';
  594.             else if ((gEditRsi.xferType == 'B') || (gEditRsi.xferType == 'I'))
  595.                 gEditRsi.xferType = 'T';
  596.             else
  597.                 gEditRsi.xferType = 'A';
  598.             EditHostWinDraw(BIT(kXferTypeEditWindowItem), kHilite);
  599.         }
  600.     }
  601. }    /* ToggleXferType */
  602.  
  603.  
  604.  
  605.  
  606. void EditWinToggle(int *val, int bitNum, int min, int max)
  607. {
  608.     int c;
  609.  
  610.     while (1) {
  611.         c = wgetch(gEditHostWin);
  612.         TraceMsg("[%c, 0x%x]\n", c, c);
  613.         if ((c == 'x') || (c == 10) || (c == 13)
  614. #ifdef KEY_ENTER
  615.             || (c == KEY_ENTER)
  616. #endif
  617.             )
  618.             break;
  619.         else if (isspace(c)) {
  620.             *val = *val + 1;
  621.             if (*val > max)
  622.                 *val = min;
  623.             EditHostWinDraw(BIT(bitNum), kHilite);
  624.         }
  625.     }
  626. }    /* EditWinToggle */
  627.  
  628.  
  629.  
  630. /* This opens and handles the site options window. */
  631. void HostWinEdit(void)
  632. {
  633.     long amt;
  634.     double rate;
  635.     char rstr[32];
  636.     int c, field;
  637.     int needUpdate;
  638.     string str;
  639.  
  640.     if (gCurHostListItem != NULL) {
  641.         gEditHostWin = newwin(LINES, COLS, 0, 0);
  642.         if (gEditHostWin == NULL)
  643.             return;
  644.         
  645.         
  646.         /* Set the clear flag for the first update. */
  647.         wclear(gEditHostWin);
  648.  
  649.         /* leaveok(gEditHostWin, TRUE);    * Not sure if I like this... */
  650.         WAttr(gEditHostWin, kBold, 1);
  651.         WAddCenteredStr(gEditHostWin, 0, "Bookmark Options");
  652.         WAttr(gEditHostWin, kBold, 0);
  653.         
  654.         /* We'll be editing a copy of the current host's settings. */
  655.         gEditRsi = *gCurHostListItem;
  656.  
  657.         if (gEditRsi.lastCall != (time_t) 0) {
  658.             strcpy(str, ctime(&gEditRsi.lastCall));
  659.             /* Why-o-why does ctime append a newline.  I hate that! */
  660.             str[strlen(str) - 1] = '\0';
  661.             mvwprintw(gEditHostWin, 2, 5,
  662.                 "Number of calls: %d.    Last call: %s",
  663.                 gEditRsi.nCalls,
  664.                 str
  665.             );
  666.             amt = gEditRsi.xferKbytes;
  667.             if (amt > 0) {
  668.                 if (amt < 1000) {
  669.                     sprintf(str, "%ld kBytes", amt);
  670.                 } else if (amt < 1000000) {
  671.                     sprintf(str, "%.2f MBytes", ((double) amt / 1000.0));
  672.                 } else {
  673.                     sprintf(str, "%.2f GBytes", ((double) amt / 1000000.0));
  674.                 }
  675.  
  676.                 rate = (double) amt / gEditRsi.xferHSeconds * 100.0;
  677.                 if (rate > 0) {
  678.                     if (rate < 1000.0) {
  679.                         sprintf(rstr, "%.2f kBytes/sec", rate);
  680.                     } else if (rate < 1000000.0) {
  681.                         sprintf(rstr, "%.2f MBytes/sec", rate / 1000.0);
  682.                     } else {
  683.                         sprintf(rstr, "%.2f GBytes/sec", rate / 1000000.0);
  684.                     }
  685.                     mvwprintw(gEditHostWin, 3, 5,
  686.                         "You have transferred %s, averaging %s.",
  687.                         str,
  688.                         rstr
  689.                     );
  690.                 }
  691.             }
  692.         }
  693.  
  694.         EditHostWinDraw(kAllWindowItems, kNoHilite);
  695.         field = 1;
  696.         while (1) {
  697.             EditHostWinMsg("Select an item to edit by typing its corresponding letter.");
  698.             c = wgetch(gEditHostWin);
  699.             TraceMsg("[%c, 0x%x]\n", c, c);
  700.             if (islower(c))
  701.                 c = toupper(c);
  702.             if (!isupper(c))
  703.                 continue;
  704.             if (c == 'X')
  705.                 break;
  706.             field = c - 'A';
  707.             needUpdate = 1;
  708.             
  709.             /* Hilite the current item to edit. */
  710.             EditHostWinDraw(BIT(field), kHilite);
  711.             switch(field) {
  712.                 case kNicknameEditWindowItem:
  713.                     EditHostWinMsg("Type a new bookmark name, or hit <RETURN> to continue.");
  714.                     EditHostWinGetStr(gEditRsi.bookmarkName, sizeof(gEditRsi.bookmarkName), kNotOkayIfEmpty, kGetAndEcho);
  715.                     break;
  716.                     
  717.                 case kHostnameEditWindowItem:
  718.                     EditHostWinMsg("Type a new hostname, or hit <RETURN> to continue.");
  719.                     EditHostWinGetStr(gEditRsi.name, sizeof(gEditRsi.name), kNotOkayIfEmpty, kGetAndEcho);
  720.                     break;
  721.  
  722.                 case kUserEditWindowItem:
  723.                     EditHostWinMsg("Type a username, or hit <RETURN> to signify anonymous.");
  724.                     EditHostWinGetStr(gEditRsi.user, sizeof(gEditRsi.user), kOkayIfEmpty, kGetAndEcho);
  725.                     break;
  726.  
  727.                 case kPassEditWindowItem:
  728.                     EditHostWinMsg("Type a password, or hit <RETURN> if no password is required.");
  729.                     EditHostWinGetStr(gEditRsi.pass, sizeof(gEditRsi.pass), kOkayIfEmpty, kGetNoEcho);
  730.                     break;
  731.  
  732.                 case kAcctEditWindowItem:
  733.                     EditHostWinMsg("Type an account name, or hit <RETURN> if no account is required.");
  734.                     EditHostWinGetStr(gEditRsi.acct, sizeof(gEditRsi.acct), kOkayIfEmpty, kGetAndEcho);
  735.                     break;
  736.  
  737.                 case kDirEditWindowItem:
  738.                     EditHostWinMsg("Type a directory path to start in after a connection is established.");
  739.                     EditHostWinGetStr(gEditRsi.dir, sizeof(gEditRsi.dir), kOkayIfEmpty, kGetAndEcho);
  740.                     break;
  741.  
  742.                 case kXferTypeEditWindowItem:
  743.                     EditHostWinMsg(kToggleMsg);
  744.                     ToggleXferType();
  745.                     break;
  746.  
  747.                 case kPortEditWindowItem:
  748.                     EditHostWinMsg("Type a port number to use for FTP.");
  749.                     EditHostWinGetNum((int *) &gEditRsi.port);
  750.                     break;
  751.  
  752.                 case kSizeEditWindowItem:
  753.                     EditHostWinMsg(kToggleMsg);
  754.                     EditWinToggle(&gEditRsi.hasSIZE, field, 0, 1);
  755.                     break;
  756.  
  757.                 case kMdtmEditWindowItem:
  758.                     EditHostWinMsg(kToggleMsg);
  759.                     EditWinToggle(&gEditRsi.hasMDTM, field, 0, 1);
  760.                     break;
  761.  
  762.                 case kPasvEditWindowItem:
  763.                     EditHostWinMsg(kToggleMsg);
  764.                     EditWinToggle(&gEditRsi.hasPASV, field, 0, 1);
  765.                     break;
  766.  
  767.                 case kOSEditWindowItem:
  768.                     EditHostWinMsg(kToggleMsg);
  769.                     EditWinToggle(&gEditRsi.isUnix, field, 0, 1);
  770.                     break;
  771.  
  772.                 case kCommentEditWindowItem:
  773.                     EditHostWinMsg("Enter a line of information to store about this site.");
  774.                     EditHostWinGetStr(gEditRsi.comment, sizeof(gEditRsi.comment), kOkayIfEmpty, kGetAndEcho);
  775.                     break;
  776.                 
  777.                 default:
  778.                     needUpdate = 0;
  779.                     break;
  780.             }
  781.             if (needUpdate)
  782.                 EditHostWinDraw(BIT(field), kNoHilite);
  783.         }
  784.         delwin(gEditHostWin);
  785.         gEditHostWin = NULL;
  786.         *gCurHostListItem = gEditRsi;
  787.         SortBookmarks();
  788.         NewHilitedHostIndex(gCurHostListItem->index);
  789.         UpdateHostWindows(1);
  790.     }
  791. }    /* HostWinEdit */
  792.  
  793.  
  794.  
  795. /* Clones an existing site in the host list. */
  796. void HostWinDup(void)
  797. {
  798.     if (gCurHostListItem != NULL) {
  799.         gCurHostListItem = DuplicateBookmark(gCurHostListItem);
  800.         gHilitedHost = gCurHostListItem->index;
  801.     } else
  802.         HostWinMsg("Nothing to duplicate.");
  803.     DrawHostList();
  804. }    /* HostWinDup */
  805.  
  806.  
  807.  
  808.  
  809. /* Removes a site from the host list. */
  810. void HostWinDelete(void)
  811. {
  812.     BookmarkPtr toDelete;
  813.     int newi;
  814.     
  815.     if (gCurHostListItem != NULL) {
  816.         toDelete = gCurHostListItem;
  817.  
  818.         /* Need to choose a new active host after deletion. */
  819.         if (gHilitedHost == gNumBookmarks - 1) {
  820.             if (gNumBookmarks == 1) {
  821.                 newi = -1;    /* None left. */
  822.             } else {
  823.                 /* At last one before delete. */
  824.                 newi = gHilitedHost - 1;
  825.             }
  826.         } else {
  827.             /* Won't need to increment gHilitedHost here, since after deletion,
  828.              * the next one will move up into this slot.
  829.              */
  830.             newi = gHilitedHost;
  831.         }
  832.         DeleteBookmark(toDelete);
  833.         if (newi < 0)
  834.             gCurHostListItem = NULL;
  835.         else {
  836.             gCurHostListItem = gBookmarks[newi];
  837.             gHilitedHost = newi;
  838.         }
  839.     } else
  840.         HostWinMsg("Nothing to delete.");
  841.     DrawHostList();
  842. }    /* HostWinDelete */
  843.  
  844.  
  845.  
  846.  
  847. /* Adds a new site to the host list, with default settings in place. */
  848. void HostWinNew(void)
  849. {
  850.     BookmarkPtr rsip;
  851.     Bookmark rsi;
  852.  
  853.     SetNewBookmarkDefaults(&rsi);
  854.     rsip = AddBookmarkPtr(&rsi);
  855.     rsip->port = gFTPPort;
  856.     SortBookmarks();
  857.     gCurHostListItem = rsip;
  858.     gHilitedHost = rsip->index;
  859.     gHostListWinStart = rsip->index - gHostListPageSize + 1;
  860.     if (gHostListWinStart < 0)
  861.         gHostListWinStart = 0;
  862.     DrawHostList();
  863. }    /* HostWinNew */
  864.  
  865.  
  866.  
  867.  
  868. /* This displays a message in the host editor's main window.
  869.  * Used mostly for error messages.
  870.  */
  871. void HostWinMsg(char *msg)
  872. {
  873.     int maxy, maxx;
  874.  
  875.     getmaxyx(gHostWin, maxy, maxx);
  876.     mvwaddstr(gHostWin, maxy - 2, 0, msg);
  877.     wclrtoeol(gHostWin);
  878.     wmove(gHostWin, maxy - 1, 0);
  879.     wrefresh(gHostWin);
  880.     beep();
  881.     gNeedToClearMsg = 1;
  882. }    /* HostWinMsg */
  883.  
  884.  
  885.  
  886.  
  887. /* Prompts for a line of input. */
  888. void HostWinGetStr(char *str, size_t size)
  889. {
  890.     WGetsParams wgp;
  891.     int maxy, maxx;
  892.  
  893.     getmaxyx(gHostWin, maxy, maxx);
  894.     mvwaddstr(gHostWin, maxy - 1, 0, "/");
  895.     wclrtoeol(gHostWin);
  896.     wrefresh(gHostWin);
  897.     curs_set(1);
  898.     wgp.w = gHostWin;
  899.     wgp.sy = maxy - 1;
  900.     wgp.sx = 1;
  901.     wgp.fieldLen = maxx - 1;
  902.     wgp.dst = str;
  903.     wgp.dstSize = size;
  904.     wgp.useCurrentContents = 0;
  905.     wgp.echoMode = wg_RegularEcho;
  906.     wgp.history = wg_NoHistory;
  907.     (void) wg_Gets(&wgp);
  908.     cbreak();                        /* wg_Gets turns off cbreak and delay. */
  909.  
  910.     TraceMsg("[%s]\n", str);
  911.     wmove(gHostWin, maxy - 1, 0);
  912.     wclrtoeol(gHostWin);
  913.     wrefresh(gHostWin);
  914.     curs_set(0);
  915. }    /* HostWinGetStr */
  916.  
  917.  
  918.  
  919.  
  920. /*ARGSUSED*/
  921. void SigIntHostWin(int sigNum)
  922. {
  923.     alarm(0);
  924.     longjmp(gHostWinJmp, 1);
  925. }    /* SigIntHostWin */
  926.  
  927.  
  928. #endif    /* USE_CURSES */
  929.  
  930.  
  931. /* Runs the host editor.  Another big use for this is to open sites
  932.  * that are in your host list.
  933.  */
  934. int HostWindow(void)
  935. {
  936. #ifdef USE_CURSES
  937.     int c;
  938.     string cmd;
  939.     volatile BookmarkPtr toOpen;
  940.     VSig_t si;
  941.     int maxy, maxx;
  942.     int lmaxy, lmaxx;
  943.     OpenOptions    openopt;
  944.  
  945.     si = kNoSignalHandler;
  946.     if (gWinInit) {
  947.         gHostListWin = NULL;
  948.         gHostWin = NULL;
  949.  
  950.         gHostWin = newwin(LINES, COLS, 0, 0);
  951.         if (gHostWin == NULL)
  952.             return (kCmdErr);
  953.  
  954.         curs_set(0);
  955.         cbreak();
  956.         
  957.         /* Set the clear flag for the first update. */
  958.         wclear(gHostWin);
  959.         keypad(gHostWin, TRUE);        /* For arrow keys. */
  960. #ifdef HAVE_NOTIMEOUT
  961.         notimeout(gHostWin, TRUE);
  962. #endif
  963.  
  964.         if (setjmp(gHostWinJmp) == 0) {
  965.             /* Gracefully cleanup the screen if the user ^C's. */
  966.             si = (VSig_t) SIGNAL(SIGINT, SigIntHostWin);
  967.             
  968.             /* Initialize the page start and select a host to be
  969.              * the current one.
  970.              */
  971.             gHostListWinStart = 0;
  972.             gHilitedHost = 0;
  973.             if (gNumBookmarks == 0)
  974.                 gCurHostListItem = NULL;
  975.             else
  976.                 gCurHostListItem = gBookmarks[gHilitedHost];
  977.             
  978.             /* Initially, we don't want to connect to any site in
  979.              * the host list.
  980.              */
  981.             toOpen = NULL;
  982.     
  983.             WAttr(gHostWin, kBold, 1);
  984.             WAddCenteredStr(gHostWin, 0, "Bookmark Editor");
  985.             WAttr(gHostWin, kBold, 0);
  986.             
  987.             mvwaddstr(gHostWin, 3, 2, "Open selected site:       <enter>");
  988.             mvwaddstr(gHostWin, 4, 2, "Edit selected site:       /ed");
  989.             mvwaddstr(gHostWin, 5, 2, "Delete selected site:     /del");
  990.             mvwaddstr(gHostWin, 6, 2, "Duplicate selected site:  /dup");
  991.             mvwaddstr(gHostWin, 7, 2, "Add a new site:           /new");
  992.             mvwaddstr(gHostWin, 9, 2, "Up one:                   <u>");
  993.             mvwaddstr(gHostWin, 10, 2, "Down one:                 <d>");
  994.             mvwaddstr(gHostWin, 11, 2, "Previous page:            <p>");
  995.             mvwaddstr(gHostWin, 12, 2, "Next page:                <n>");
  996.             mvwaddstr(gHostWin, 14, 2, "Capital letters selects first");
  997.             mvwaddstr(gHostWin, 15, 2, "  site starting with the letter.");
  998.             mvwaddstr(gHostWin, 17, 2, "Exit the bookmark editor: <x>");
  999.             
  1000.             /* Initialize the scrolling host list window. */
  1001.             gHostListWin = subwin(
  1002.                 gHostWin,
  1003.                 LINES - 7,
  1004.                 40,
  1005.                 3,
  1006.                 COLS - 40 - 2
  1007.             );
  1008.             if (gHostListWin == NULL)
  1009.                 return (kCmdErr);
  1010.             getmaxyx(gHostListWin, lmaxy, lmaxx);
  1011.             getmaxyx(gHostWin, maxy, maxx);
  1012.             gHostListPageSize = lmaxy;
  1013.             DrawHostList();
  1014.             wmove(gHostWin, maxy - 1, 0);
  1015.             UpdateHostWindows(1);
  1016.  
  1017.             while (1) {
  1018.                 c = HostWinGetKey();
  1019.                 if (gNeedToClearMsg) {
  1020.                     wmove(gHostWin, maxy - 2, 0);
  1021.                     wclrtoeol(gHostWin);
  1022.                     wrefresh(gHostWin);
  1023.                 }
  1024.                 if ((c >= 'A') && (c <= 'Z')) {
  1025.                     /* isupper can coredump if wgetch returns a meta key. */
  1026.                     HostWinZoomTo(c);
  1027.                 } else if (c == '/') {
  1028.                     /* Get an "extended" command.  Sort of like vi's
  1029.                      * :colon commands.
  1030.                      */
  1031.                     HostWinGetStr(cmd, sizeof(cmd));
  1032.     
  1033.                     if (ISTREQ(cmd, "ed"))
  1034.                         HostWinEdit();
  1035.                     else if (ISTREQ(cmd, "dup"))
  1036.                         HostWinDup();
  1037.                     else if (ISTREQ(cmd, "del"))
  1038.                         HostWinDelete();
  1039.                     else if (ISTREQ(cmd, "new"))
  1040.                         HostWinNew();
  1041.                     else
  1042.                         HostWinMsg("Invalid bookmark editor command.");
  1043.                 } else switch(c) {
  1044.                     case 10:    /* ^J == newline */
  1045.                     case 13:    /* ^M == carriage return */
  1046. #ifdef KEY_ENTER
  1047.                     case KEY_ENTER:
  1048. #endif
  1049.                         if (gCurHostListItem == NULL)
  1050.                             HostWinMsg("Nothing to open.  Try 'open sitename' from the main screen.");
  1051.                         else {
  1052.                             toOpen = (volatile BookmarkPtr) gCurHostListItem;
  1053.                             goto done;
  1054.                         }
  1055.                         break;
  1056.     
  1057.                     case kControl_L:
  1058.                         UpdateHostWindows(1);
  1059.                         break;
  1060.     
  1061.                     case 'u':
  1062.                     case 'k':    /* vi up key */
  1063. #ifdef KEY_UP
  1064.                     case KEY_UP:
  1065. #endif
  1066.                         HostListLineUp();
  1067.                         break;
  1068.                     
  1069.                     case 'd':
  1070.                     case 'j':    /* vi down key */
  1071. #ifdef KEY_DOWN
  1072.                     case KEY_DOWN:
  1073. #endif
  1074.                         HostListLineDown();
  1075.                         break;
  1076.                         
  1077.                     case 'p':
  1078. #ifdef KEY_LEFT
  1079.                     case KEY_LEFT:
  1080. #endif
  1081. #ifdef KEY_PPAGE
  1082.                     case KEY_PPAGE:
  1083. #endif
  1084.                         HostListPageUp();
  1085.                         break;
  1086.                         
  1087.                     case 'n':
  1088. #ifdef KEY_RIGHT
  1089.                     case KEY_RIGHT:
  1090. #endif
  1091. #ifdef KEY_NPAGE
  1092.                     case KEY_NPAGE:
  1093. #endif
  1094.                         HostListPageDown();
  1095.                         break;
  1096.     
  1097. #ifdef KEY_END
  1098.                     case KEY_END:
  1099. #endif
  1100.                     case 'x':
  1101.                     case 'q':
  1102.                         goto done;
  1103.     
  1104.                     default:
  1105.                         HostWinMsg("Invalid key.");
  1106.                         break;
  1107.                 }
  1108.             }
  1109.         }
  1110.         SIGNAL(SIGINT, SIG_IGN);
  1111. done:
  1112.         if (gHostListWin != NULL)
  1113.             delwin(gHostListWin);
  1114.         if (gHostWin != NULL)
  1115.             delwin(gHostWin);
  1116.         gHostListWin = gHostWin = NULL;
  1117.         curs_set(1);
  1118.         nocbreak();
  1119.         UpdateScreen(1);
  1120.         flushinp();
  1121.         if (si != (Sig_t) kNoSignalHandler)
  1122.             SIGNAL(SIGINT, si);
  1123.         if (toOpen != (volatile BookmarkPtr) 0) {
  1124.             /* If the user selected a site to open, connect to it now. */
  1125.             InitOpenOptions(&openopt);
  1126.             STRNCPY(openopt.hostname, ((BookmarkPtr) toOpen)->bookmarkName);
  1127.             GetBookmark(openopt.hostname, sizeof(openopt.hostname));
  1128.             openopt.port = gRmtInfo.port;
  1129.             Beep(0);    /* Reset beep timer. */
  1130.             return (Open(&openopt));
  1131.         }
  1132.     }
  1133. #endif    /* USE_CURSES */
  1134.     Beep(0);    /* User should be aware that it took a while, so no beep. */
  1135.     return (kNoErr);
  1136. }    /* HostWindow */
  1137.  
  1138.  
  1139.  
  1140.  
  1141. int HostsCmd(int argc, char **argv)
  1142. {
  1143. #ifdef USE_CURSES
  1144.     if (!gWinInit) {
  1145.         Error(kDontPerror, "This only works in visual mode.\n");
  1146.         return (kCmdErr);
  1147.     }
  1148.     return HostWindow();
  1149. #else
  1150.     Error(kDontPerror,
  1151.     "You can't do this because the program doesn't have the curses library.\n");
  1152.     return (kCmdErr);
  1153. #endif    /* USE_CURSES */
  1154. }    /* HostsCmd */
  1155.