home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / ncftp-2.3.0-base.tgz / ncftp-2.3.0-base.tar / contrib / ncftp / Prefs.c < prev    next >
C/C++ Source or Header  |  1995-11-26  |  18KB  |  803 lines

  1. /* Prefs.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 "Progress.h"
  13. #include "Hostwin.h"
  14. #include "Prefs.h"
  15. #include "RCmd.h"
  16. #include "Bookmark.h"
  17. #include "Main.h"
  18.  
  19. #ifdef USE_CURSES
  20.  
  21. #include "WGets.h"
  22.  
  23. /* This is the full-screen window that pops up when you run the
  24.  * preferences editor.
  25.  */
  26. WINDOW *gPrefsWin = NULL;
  27.  
  28. extern void WAddCenteredStr(WINDOW *w, int y, char *str);
  29. void WAttr(WINDOW *w, int attr, int on);
  30.  
  31. #endif    /* USE_CURSES */
  32.  
  33.  
  34.  
  35. jmp_buf gPrefsWinJmp;
  36. extern int gWinInit;
  37. extern string gAnonPassword;
  38. extern int gMayUTime, gStartupMsgs, gRemoteMsgs, gBlankLines;
  39. extern int gAnonOpen, gWhichProgMeter, gMaxLogSize, gMaxBookmarks;
  40. extern int gDefaultVisualMode, gTotalRuns, gRememberLCWD;
  41. extern int gNetworkTimeout, gTrace, gPreferredDataPortMode;
  42. extern longstring gLocalCWD, gPager, gDownloadDir;
  43. extern long gTotalXferKiloBytes, gTotalXferHSeconds;
  44. extern int gMarkTrailingSpace;
  45.  
  46.  
  47.  
  48. PrefOpt gPrefOpts[] = {
  49.     { "anonopen", "Default open mode:",
  50.         kToggleMsg,
  51.         PREFBOOL(gAnonOpen) },
  52.     { "anonpass", "Anonymous password:",
  53.         "Type new anonymous password, or hit <RETURN> to continue.",
  54.         PREFSTR(gAnonPassword, kNotOkayIfEmpty, kGetAndEcho) },
  55.     { "blank-lines", "Blank lines between cmds:",
  56.         kToggleMsg,
  57.         PREFBOOL(gBlankLines) },
  58.     { "ftp-mode", "Default FTP mode:",
  59.         kToggleMsg,
  60.         PREFTOGGLE(gPreferredDataPortMode, kSendPortMode, kFallBackToSendPortMode) },
  61.     { "logsize", "User log size:",
  62.         "Enter the maximum number of bytes allowed for log file, or 0 for no log.",
  63.         PREFINT(gMaxLogSize) },
  64.     { "maxbookmarks", "Max bookmarks to save:",
  65.         "Enter the max number of bookmarks for bookmarks file, or 0 for no limit.",
  66.         PREFINT(gMaxBookmarks) },
  67.     { "pager", "Pager:",
  68.         "Type the pathname of the program you use to view text a page at a time.",
  69.         PREFSTR(gPager, kOkayIfEmpty, kGetAndEcho) },
  70.     { "progress-meter", "Progress meter:",
  71.         kToggleMsg,
  72.         PREFTOGGLE(gWhichProgMeter, kPrNone, kPrLast) },
  73.     { "remote-msgs", "Remote messages:",
  74.         kToggleMsg,
  75.         PREFTOGGLE(gRemoteMsgs, kAllRmtMsgs, (kNoChdirMsgs | kNoConnectMsg)) },
  76. #if 0
  77.     { "restore-lcwd", "Restore local CWD:",
  78.         kToggleMsg,
  79.         PREFBOOL(gRememberLCWD) },
  80. #endif
  81.     { "startup-lcwd", "Startup in Local Dir:",
  82.         "Type directory to always lcd to, or hit <RETURN> to not lcd at startup.",
  83.         PREFSTR(gDownloadDir, kOkayIfEmpty, kGetAndEcho) },
  84.     { "startup-msgs", "Startup messages:",
  85.         kToggleMsg,
  86.         PREFTOGGLE(gStartupMsgs, kNoStartupMsgs, (kStartupMsg | kTips)) },
  87.     { "timeout", "Network timeout:",
  88.         "Enter the maximum amount of time to wait on a connection before giving up.",
  89.         PREFINT(gNetworkTimeout) },
  90.     { "trace", "Trace logging:",
  91.         kToggleMsg,
  92.         PREFBOOL(gTrace) },
  93.     { "utime", "File timestamps:",
  94.         kToggleMsg,
  95.         PREFBOOL(gMayUTime) },
  96.     { "visual", "Screen graphics:",
  97.         kToggleMsg,
  98.         PREFBOOL(gDefaultVisualMode) },
  99. };
  100.  
  101. /* These are options that are for information only, or options I don't feel
  102.  * like wasting screen space on in the prefs editor.
  103.  */
  104. PrefOpt gNonEditPrefOpts[] = {
  105.     { "show-trailing-space", "Show trailing space:",
  106.         kToggleMsg,
  107.         PREFBOOL(gMarkTrailingSpace) },
  108.     { "total-runs", NULL, NULL,
  109.         PREFINT(gTotalRuns) },
  110.     { "total-xfer-hundredths-of-seconds", NULL, NULL,
  111.         PREFINT(gTotalXferHSeconds) },
  112.     { "total-xfer-kbytes", NULL, NULL,
  113.         PREFINT(gTotalXferKiloBytes) },
  114. };
  115.  
  116. int gNumEditablePrefOpts = ((int)(sizeof(gPrefOpts) / sizeof(PrefOpt)));
  117. #define kNumNonEditablePrefOpts ((int)(sizeof(gNonEditPrefOpts) / sizeof(PrefOpt)))
  118.  
  119.  
  120.  
  121.  
  122.  
  123. void TogglePref(int *val, int min, int max)
  124. {
  125.     int newVal;
  126.  
  127.     newVal = *val + 1;
  128.     if (newVal > max)
  129.         newVal = min;
  130.     *val = newVal;
  131. }    /* TogglePref */
  132.  
  133.  
  134.  
  135.  
  136.  
  137. void GetPrefSetting(char *dst, size_t siz, int item)
  138. {
  139.     char *cp;
  140.     string str;
  141.     size_t len;
  142.  
  143.     *dst = '\0';
  144.     switch (item) {
  145.         case kAnonOpenPrefsWinItem:
  146.             cp = gAnonOpen ? "anonymous" : "user & password";
  147.             (void) Strncpy(dst, cp, siz);
  148.             break;
  149.  
  150.         case kAnonPassPrefsWinItem:
  151.             (void) Strncpy(dst, gAnonPassword, siz);
  152.             break;
  153.  
  154.         case kBlankLinesWinItem:
  155.             cp = gBlankLines ? "yes" : "no";
  156.             (void) Strncpy(dst, cp, siz);
  157.             break;
  158.  
  159.         case kFTPModePrefsWinItem:
  160.             if (gPreferredDataPortMode == kFallBackToSendPortMode)
  161.                 cp = "Passive, but fall back to port if needed";
  162.             else if (gPreferredDataPortMode == kPassiveMode)
  163.                 cp = "Passive FTP only (PASV)";
  164.             else
  165.                 cp = "Send-Port FTP only (PORT)";
  166.             (void) Strncpy(dst, cp, siz);
  167.             break;
  168.  
  169.         case kLogSizePrefsWinItem:
  170.             if (gMaxLogSize == 0)
  171.                 (void) Strncpy(str, "no logging", siz);
  172.             else
  173.                 sprintf(str, "%d", gMaxLogSize);
  174.             (void) Strncpy(dst, str, siz);
  175.             break;
  176.  
  177.         case kMaxBookmarksWinItem:
  178.             if (gMaxBookmarks == kNoBookmarkLimit)
  179.                 (void) Strncpy(str, "unlimited", siz);
  180.             else
  181.                 sprintf(str, "%d", gMaxBookmarks);
  182.             (void) Strncpy(dst, str, siz);
  183.             break;
  184.  
  185.         case kPagerPrefsWinItem:
  186.             if ((len = strlen(gPager)) > 47) {
  187.                 /* Abbreviate a long program path. */
  188.                 STRNCPY(str, "...");
  189.                 STRNCAT(str, gPager + len - 44);
  190.             } else {
  191.                 if (gPager[0] == '\0')
  192.                     STRNCPY(str, "(none)");
  193.                 else
  194.                     STRNCPY(str, gPager);
  195.             }
  196.             (void) Strncpy(dst, str, siz);
  197.             break;
  198.  
  199.         case kProgressPrefsWinItem:
  200.             switch (gWhichProgMeter) {
  201.                 case kPrPercent: cp = "percent meter"; break;
  202.                 case kPrPhilBar: cp = "bar graph"; break;
  203.                 case kPrKBytes: cp = "kilobyte meter"; break;
  204.                 case kPrDots: cp = "hash dots"; break;
  205.                 case kPrStatBar: cp = "stat meter"; break;
  206.                 default: cp = "no progress reports";
  207.             }
  208.             (void) Strncpy(dst, cp, siz);
  209.             break;
  210.  
  211.         case kRmtMsgsPrefsWinItem:
  212.             if ((gRemoteMsgs & (kNoChdirMsgs | kNoConnectMsg)) == (kNoChdirMsgs | kNoConnectMsg))
  213.                 cp = "ignore startup messages and chdir messages";
  214.             else if ((gRemoteMsgs & kNoChdirMsgs) != 0)
  215.                 cp = "ignore change-directory messages";
  216.             else if ((gRemoteMsgs & kNoConnectMsg) != 0)
  217.                 cp = "ignore startup messages";
  218.             else
  219.                 cp = "allow startup messages and chdir messages";
  220.             (void) Strncpy(dst, cp, siz);
  221.             break;
  222.  
  223.         case kStartupLCWDWinItem:
  224.             cp = (gDownloadDir[0] != '\0') ? gDownloadDir :
  225.                 "(none)";
  226.             (void) Strncpy(dst, cp, siz);
  227.             break;
  228.  
  229.         case kStartupMsgsPrefsWinItem:
  230.             if ((gStartupMsgs & (kStartupMsg | kTips)) == (kStartupMsg | kTips))
  231.                 cp = "headers and tips";
  232.             else if ((gStartupMsgs & kStartupMsg) != 0)
  233.                 cp = "headers only";
  234.             else if ((gStartupMsgs & kTips) != 0)
  235.                 cp = "tips only";
  236.             else
  237.                 cp = "no startup messages";
  238.             (void) Strncpy(dst, cp, siz);
  239.             break;
  240.  
  241.         case kTimeoutPrefsWinItem:
  242.             sprintf(dst, "%d", gNetworkTimeout);
  243.             break;
  244.  
  245.         case kTracePrefsWinItem:
  246.             if (gTrace) {
  247.                 cp = "yes";
  248.                 OpenTraceLog();
  249.             } else {
  250.                 cp = "no";
  251.                 CloseTraceLog();
  252.             }
  253.             (void) Strncpy(dst, cp, siz);
  254.             break;
  255.  
  256.         case kUTimePrefsWinItem:
  257.             cp = gMayUTime ? "try to preserve file timestamps" :
  258.                 "do not preserve file timestamps";
  259.             (void) Strncpy(dst, cp, siz);
  260.             break;
  261.  
  262.         case kVisualPrefsWinItem:
  263.             cp = gDefaultVisualMode == 1 ? "visual (curses)" : "line-oriented";
  264.             (void) Strncpy(dst, cp, siz);
  265.             break;
  266.     }
  267. }    /* GetPrefSetting */
  268.  
  269.  
  270.  
  271. #ifdef USE_CURSES
  272.  
  273. /* Draws the screen when we're using the host editor's main screen.
  274.  * You can can specify whether to draw each character whether it needs
  275.  * it or not if you like.
  276.  */
  277. void UpdatePrefsWindow(int uptAll)
  278. {
  279.     if (uptAll) {
  280.         touchwin(gPrefsWin);
  281.     }
  282.     wnoutrefresh(gPrefsWin);
  283.     doupdate();
  284. }    /* UpdatePrefsWindow */
  285.  
  286.  
  287.  
  288.  
  289. /* This displays a message in the preferences window. */
  290. void PrefsWinWinMsg(char *msg)
  291. {
  292.     int maxx, maxy;
  293.  
  294.     getmaxyx(gPrefsWin, maxy, maxx);
  295.     mvwaddstr(gPrefsWin, maxy - 2, 0, msg);
  296.     wclrtoeol(gPrefsWin);
  297.     wmove(gPrefsWin, maxy - 1, 0);
  298.     wrefresh(gPrefsWin);
  299. }    /* PrefsWinWinMsg */
  300.  
  301.  
  302.  
  303.  
  304. /* Prompts for a line of input. */
  305. void PrefsWinGetStr(char *dst, int canBeEmpty, int canEcho)
  306. {
  307.     string str;
  308.     WGetsParams wgp;
  309.     int maxx, maxy;
  310.  
  311.     getmaxyx(gPrefsWin, maxy, maxx);
  312.     WAttr(gPrefsWin, kBold, 1);
  313.     mvwaddstr(gPrefsWin, maxy - 1, 0, "> ");
  314.     WAttr(gPrefsWin, kBold, 0);
  315.     wclrtoeol(gPrefsWin);
  316.     wrefresh(gPrefsWin);
  317.     curs_set(1);
  318.  
  319.     wgp.w = gPrefsWin;
  320.     wgp.sy = maxy - 1;
  321.     wgp.sx = 2;
  322.     wgp.fieldLen = maxx - 3;
  323.     wgp.dst = str;
  324.     wgp.dstSize = sizeof(str);
  325.     wgp.useCurrentContents = 0;
  326.     wgp.echoMode = canEcho ? wg_RegularEcho : wg_BulletEcho;
  327.     wgp.history = wg_NoHistory;
  328.     (void) wg_Gets(&wgp);
  329.     cbreak();                        /* wg_Gets turns off cbreak and delay. */
  330.  
  331.     TraceMsg("[%s]\n", str);
  332.     
  333.     /* See if the user just hit return.  We may not want to overwrite
  334.      * the dst here, which would make it an empty string.
  335.      */
  336.     if ((wgp.changed) || (canBeEmpty == kOkayIfEmpty))
  337.         strcpy(dst, str);
  338.  
  339.     wmove(gPrefsWin, maxy - 1, 0);
  340.     wclrtoeol(gPrefsWin);
  341.     wrefresh(gPrefsWin);
  342.     curs_set(0);
  343. }    /* PrefsWinGetStr */
  344.  
  345.  
  346.  
  347.  
  348.  
  349. /* Prompts for an integer of input. */
  350. void PrefsWinGetNum(int *dst)
  351. {
  352.     string str;
  353.     WGetsParams wgp;
  354.     int maxx, maxy;
  355.  
  356.     getmaxyx(gPrefsWin, maxy, maxx);
  357.     WAttr(gPrefsWin, kBold, 1);
  358.     mvwaddstr(gPrefsWin, maxy - 1, 0, "> ");
  359.     WAttr(gPrefsWin, kBold, 0);
  360.     wclrtoeol(gPrefsWin);
  361.     wrefresh(gPrefsWin);
  362.     curs_set(1);
  363.  
  364.     wgp.w = gPrefsWin;
  365.     wgp.sy = maxy - 1;
  366.     wgp.sx = 2;
  367.     wgp.fieldLen = maxx - 3;
  368.     wgp.dst = str;
  369.     wgp.dstSize = sizeof(str);
  370.     wgp.useCurrentContents = 0;
  371.     wgp.echoMode = wg_RegularEcho;
  372.     wgp.history = wg_NoHistory;
  373.     (void) wg_Gets(&wgp);
  374.     cbreak();                        /* wg_Gets turns off cbreak and delay. */
  375.  
  376.     TraceMsg("[%s]\n", str);
  377.     AtoIMaybe(dst, str);
  378.     wmove(gPrefsWin, maxy - 1, 0);
  379.     wclrtoeol(gPrefsWin);
  380.     wrefresh(gPrefsWin);
  381.     curs_set(0);
  382. }    /* PrefsWinGetNum */
  383.  
  384.  
  385.  
  386.  
  387. /* This is the meat of the preferences window.  We can selectively update
  388.  * portions of the window by using a bitmask with bits set for items
  389.  * we want to update.
  390.  */
  391. void PrefsWinDraw(int flags, int hilite)
  392. {
  393.     int i;
  394.     string value;
  395.     char spec[32];
  396.     int maxx, maxy;
  397.  
  398.     getmaxyx(gPrefsWin, maxy, maxx);
  399.     /* Draw the keys the user can type in reverse text. */
  400.     WAttr(gPrefsWin, kReverse, 1);
  401.     for (i = kFirstPrefsWinItem; i <= kLastPrefsWinItem; i++) {
  402.         if (TESTBIT(flags, i))
  403.             mvwaddch(gPrefsWin, 2 + i, 2, 'A' + i);
  404.     }
  405.     
  406.     /* The "quit" item is a special item that is offset a line, and
  407.      * always has the "X" key assigned to it.
  408.      */
  409.     i = kQuitPrefsWinItem;
  410.     if (TESTBIT(flags, i))
  411.         mvwaddch(gPrefsWin, 3 + i, 2, 'X');
  412.     WAttr(gPrefsWin, kReverse, 0);
  413.     
  414.     /* We can use this to hilite a whole line, to indicate to the
  415.      * user that a certain item is being edited.
  416.      */
  417.     if (hilite)
  418.         WAttr(gPrefsWin, kReverse, 1);
  419.  
  420.     sprintf(spec, " %%-28s%%-%ds",
  421.         maxx - 28 - 6);
  422.  
  423.     for (i = kFirstPrefsWinItem; i <= kLastPrefsWinItem; i++) {
  424.         if (TESTBIT(flags, i)) {
  425.             GetPrefSetting(value, sizeof(value), i);
  426.             mvwprintw(gPrefsWin, i + 2 , 3, spec, gPrefOpts[i].label, value);
  427.             wclrtoeol(gPrefsWin);
  428.         }
  429.     }
  430.  
  431.     if (TESTBIT(flags, kQuitPrefsWinItem)) {
  432.         mvwprintw(gPrefsWin, kQuitPrefsWinItem + 3, 3, " %-28s",
  433.             "(Done editing)"
  434.         );
  435.         wclrtoeol(gPrefsWin);
  436.     } 
  437.  
  438.     if (hilite)
  439.         WAttr(gPrefsWin, kReverse, 0);
  440.  
  441.     wmove(gPrefsWin, maxy - 1, 0);
  442.     wrefresh(gPrefsWin);
  443. }    /* PrefsWinDraw */
  444.  
  445.  
  446.  
  447. /* The user can hit space to change values.  For these toggle
  448.  * functions we do an update each time so the user can see the change
  449.  * immediately.
  450.  */
  451. void PrefsWinToggle(int *val, int bitNum, int min, int max)
  452. {
  453.     int c;
  454.  
  455.     while (1) {
  456.         c = wgetch(gPrefsWin);
  457.         TraceMsg("[%c, 0x%x]\n", c, c);
  458.         if ((c == 'q') || (c == 10) || (c == 13)
  459. #ifdef KEY_ENTER
  460.             || (c == KEY_ENTER)
  461. #endif
  462.             )
  463.             break;
  464.         else if (isspace(c)) {
  465.             TogglePref(val, min, max);
  466.             PrefsWinDraw(BIT(bitNum), kHilite);
  467.         }
  468.     }
  469. }    /* PrefsWinToggle */
  470.  
  471.  
  472.  
  473.  
  474. /*ARGSUSED*/
  475. void SigIntPrefsWin(int sigNum)
  476. {
  477.     alarm(0);
  478.     longjmp(gPrefsWinJmp, 1);
  479. }    /* SigIntPrefsWin */
  480.  
  481.  
  482. #endif    /* USE_CURSES */
  483.  
  484.  
  485.         
  486.  
  487.  
  488. /* Runs the preferences editor. */
  489. int PrefsWindow(void)
  490. {
  491. #ifdef USE_CURSES
  492.     int c, field, i;
  493.  
  494.     if (gWinInit) {
  495.         gPrefsWin = newwin(LINES, COLS, 0, 0);
  496.         if (gPrefsWin == NULL)
  497.             return (kCmdErr);
  498.  
  499.         if (setjmp(gPrefsWinJmp) == 0) {
  500.             /* Gracefully cleanup the screen if the user ^C's. */
  501.             SIGNAL(SIGINT, SigIntPrefsWin);
  502.  
  503.             curs_set(0);
  504.             cbreak();
  505.             /* leaveok(gPrefsWin, TRUE);    * Not sure if I like this... */
  506.             
  507.             /* Set the clear flag for the first update. */
  508.             wclear(gPrefsWin);
  509.     
  510.             WAttr(gPrefsWin, kBold, 1);
  511.             WAddCenteredStr(gPrefsWin, 0, "Preferences");
  512.             WAttr(gPrefsWin, kBold, 0);
  513.     
  514.             PrefsWinDraw(kAllWindowItems, kNoHilite);
  515.             while (1) {
  516.                 PrefsWinWinMsg("Select an item to edit by typing its corresponding letter.");
  517.                 c = wgetch(gPrefsWin);
  518.                 TraceMsg("[%c, 0x%x]\n", c, c);
  519.                 if (islower(c))
  520.                     c = toupper(c);
  521.                 if (!isupper(c))
  522.                     continue;
  523.                 if (c == 'X')
  524.                     break;
  525.                 field = c - 'A';
  526.                 i = field;
  527.                 if (i > kLastPrefsWinItem)
  528.                     continue;
  529.  
  530.                 /* Hilite the current item to edit. */
  531.                 PrefsWinDraw(BIT(i), kHilite);
  532.  
  533.                 /* Print the instructions. */
  534.                 PrefsWinWinMsg(gPrefOpts[i].msg);
  535.  
  536.                 switch (gPrefOpts[i].type) {
  537.                     case kPrefInt:
  538.                         PrefsWinGetNum((int *) gPrefOpts[i].storage);
  539.                         break;
  540.                     case kPrefToggle:
  541.                         PrefsWinToggle((int *) gPrefOpts[i].storage,
  542.                             i, gPrefOpts[i].min, gPrefOpts[i].max);
  543.                         break;
  544.                     case kPrefStr:
  545.                         PrefsWinGetStr((char *) gPrefOpts[i].storage,
  546.                             gPrefOpts[i].min,    /* Used for Empty */
  547.                             gPrefOpts[i].max    /* Used for Echo */
  548.                         );
  549.                         break;
  550.                 }
  551.                 
  552.                 /* Update and unhilite it. */
  553.                 PrefsWinDraw(BIT(field), kNoHilite);
  554.             }
  555.         }
  556.  
  557.         delwin(gPrefsWin);
  558.         gPrefsWin = NULL;
  559.         UpdateScreen(1);
  560.         flushinp();
  561.         nocbreak();
  562.         curs_set(1);
  563.     }
  564. #endif    /* USE_CURSES */
  565.     return (kNoErr);
  566. }    /* PrefsWindow */
  567.  
  568.  
  569.  
  570.  
  571.  
  572. void ShowAll(void)
  573. {
  574.     int i;
  575.     string value;
  576.  
  577.     MultiLineInit();
  578.     for (i = kFirstPrefsWinItem; i <= kLastPrefsWinItem; i++) {
  579.         GetPrefSetting(value, sizeof(value), i);
  580.         MultiLinePrintF("%-28s%s\n", gPrefOpts[i].label, value);
  581.     }
  582. }    /* ShowAll */
  583.  
  584.  
  585.  
  586.  
  587. static
  588. void ShowSetHelp(void)
  589. {
  590.     int i;
  591.  
  592.     MultiLineInit();
  593.     for (i = kFirstPrefsWinItem; i <= kLastPrefsWinItem; i++) {
  594.         MultiLinePrintF("%-15s %s\n", gPrefOpts[i].name, gPrefOpts[i].label);
  595.     }
  596. }    /* ShowSetHelp */
  597.  
  598.  
  599.  
  600.  
  601.  
  602. int SetCmd(int argc, char **argv)
  603. {
  604.     int i, j, match;
  605.     size_t len;
  606.     string value;
  607.  
  608.     if ((argc == 1) || ISTREQ(argv[1], "all")) {
  609.         ShowAll();
  610.     } else if (ISTREQ(argv[1], "help")) {
  611.         ShowSetHelp();
  612.     } else {
  613.         len = strlen(argv[1]);
  614.         for (i = kFirstPrefsWinItem, match = -1; i <= kLastPrefsWinItem; i++) {
  615.             if (ISTRNEQ(gPrefOpts[i].name, argv[1], len)) {
  616.                 if (match >= 0) {
  617.                     Error(kDontPerror, "Ambiguous option name \"%s.\"\n",
  618.                         argv[1]);
  619.                     return (kCmdErr);
  620.                 }
  621.                 match = i;
  622.             }
  623.         }
  624.         if (match < 0) {
  625.             Error(kDontPerror, "Unknown option name \"%s.\"\n",
  626.                 argv[1]);
  627.             return (kCmdErr);
  628.         }
  629.         i = match;
  630.         switch (gPrefOpts[i].type) {
  631.             case kPrefInt:
  632.                 if (argc > 2)
  633.                     AtoIMaybe((int *) gPrefOpts[i].storage, argv[2]);
  634.                 break;
  635.             case kPrefToggle:
  636.                 if (argc > 2) {
  637.                     /* User can set it directly instead of cycling through
  638.                      * the choices.
  639.                      */
  640.                     AtoIMaybe((int *) &j, argv[2]);
  641.                     if (j < gPrefOpts[i].min)
  642.                         j = gPrefOpts[i].min;
  643.                     else if (j > gPrefOpts[i].max)
  644.                         j = gPrefOpts[i].max;
  645.                     * (int *) gPrefOpts[i].storage = j;
  646.                 } else {
  647.                     /* User toggled to the next choice. */
  648.                     TogglePref((int *) gPrefOpts[i].storage,
  649.                         gPrefOpts[i].min, gPrefOpts[i].max);
  650.                 }
  651.                 break;
  652.             case kPrefStr:
  653.                 if (argc > 2)
  654.                     (void) Strncpy((char *) gPrefOpts[i].storage,
  655.                         argv[2], gPrefOpts[i].siz);
  656.                 break;
  657.         }
  658.  
  659.         /* Print the (possibly new) value. */
  660.         GetPrefSetting(value, sizeof(value), i);
  661.         PrintF("%-28s%s\n", gPrefOpts[i].label, value);
  662.     }
  663.     return (kNoErr);
  664. }    /* SetCmd */
  665.  
  666.  
  667.  
  668.  
  669.  
  670. int PrefsCmd(int argc, char **argv)
  671. {
  672. #ifdef USE_CURSES
  673.     int err;
  674.  
  675.     if (!gWinInit) {
  676.         EPrintF("%s\n%s\n",
  677.         "The preferences editor only works in visual mode.",
  678.         "However, you can use the 'set' command to set a single option at a time."
  679.         );
  680.         return (kCmdErr);
  681.     }
  682.     err = PrefsWindow();
  683.     Beep(0);    /* User should be aware that it took a while, so no beep. */
  684.     return (err);
  685. #else
  686.     EPrintF("%s\n%s\n",
  687.     "You can't do this because the program doesn't have the curses library.",
  688.     "However, you can use the 'set' command to set a single option at a time."
  689.     );
  690.     return (kCmdErr);
  691. #endif    /* USE_CURSES */
  692. }    /* PrefsCmd */
  693.  
  694.  
  695.  
  696.  
  697. void WritePrefs(void)
  698. {
  699.     longstring path;
  700.     FILE *fp;
  701.     int i;
  702.  
  703.     if (gOurDirectoryPath[0] == '\0')
  704.         return;        /* Don't create in root directory. */
  705.  
  706.     OurDirectoryPath(path, sizeof(path), kPrefsName);
  707.     fp = fopen(path, "w");
  708.     if (fp == NULL) {
  709.         Error(kDoPerror, "Can't open %s for writing.\n", path);
  710.         return;
  711.     }
  712.     for (i = kFirstPrefsWinItem; i <= kLastPrefsWinItem; i++) {
  713.         switch (gPrefOpts[i].type) {
  714.             case kPrefInt:
  715.             case kPrefToggle:
  716.                 fprintf(fp, "%s %d\n", gPrefOpts[i].name,
  717.                     * (int *) gPrefOpts[i].storage);
  718.                 break;
  719.             case kPrefStr:
  720.                 fprintf(fp, "%s %s\n", gPrefOpts[i].name,
  721.                     (char *) gPrefOpts[i].storage);
  722.                 break;
  723.         }
  724.     }
  725.     for (i = 0; i < kNumNonEditablePrefOpts; i++) {
  726.         switch (gNonEditPrefOpts[i].type) {
  727.             case kPrefInt:
  728.             case kPrefToggle:
  729.                 fprintf(fp, "%s %d\n", gNonEditPrefOpts[i].name,
  730.                     * (int *) gNonEditPrefOpts[i].storage);
  731.                 break;
  732.             case kPrefStr:
  733.                 fprintf(fp, "%s %s\n", gNonEditPrefOpts[i].name,
  734.                     (char *) gNonEditPrefOpts[i].storage);
  735.                 break;
  736.         }
  737.     }
  738.  
  739.     fclose(fp);
  740. }    /* WritePrefs */
  741.  
  742.  
  743.  
  744.  
  745.  
  746. static
  747. int PrefSearchProc(char *key, const PrefOpt *b)
  748. {
  749.     return (ISTRCMP(key, (*b).name));    
  750. }    /* PrefSearchProc */
  751.  
  752.  
  753.  
  754.  
  755.  
  756. void ReadPrefs(void)
  757. {
  758.     longstring path;
  759.     FILE *fp;
  760.     string option;
  761.     longstring val;
  762.     longstring line;
  763.     int o;
  764.     PrefOpt *pop;
  765.  
  766.     OurDirectoryPath(path, sizeof(path), kPrefsName);
  767.     fp = fopen(path, "r");
  768.     if (fp == NULL) {
  769.         /* It's okay if we don't have one. */
  770.         return;
  771.     }
  772.  
  773.     while (FGets(line, (int) sizeof(line) - 1, fp) != NULL) {
  774.         if (sscanf(line, "%s", option) < 1)
  775.             continue;
  776.         o = strlen(option) + 1;
  777.         STRNCPY(val, line + o);
  778.         pop = (PrefOpt *) BSEARCH(option, gPrefOpts, SZ(gNumEditablePrefOpts),
  779.             sizeof(PrefOpt), PrefSearchProc);
  780.         if (pop == NULL) {
  781.             pop = (PrefOpt *) BSEARCH(option, gNonEditPrefOpts,
  782.                 SZ(kNumNonEditablePrefOpts),
  783.                 sizeof(PrefOpt), PrefSearchProc);
  784.             if (pop == NULL) {
  785.                 Error(kDontPerror, "Unrecognized preference option \"%s\".\n",
  786.                     option);
  787.                 continue;
  788.             }
  789.         }
  790.         switch (pop->type) {
  791.             case kPrefInt:
  792.             case kPrefToggle:
  793.                 * (int *) pop->storage = atoi(val);
  794.                 break;
  795.             case kPrefStr:
  796.                 (void) Strncpy((char *) pop->storage, val, pop->siz);
  797.                 break;
  798.         }
  799.     }
  800.  
  801.     fclose(fp);
  802. }    /* ReadPrefs */
  803.