home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 2: PC / frozenfish_august_1995.bin / bbs / d01xx / d0128.lha / MRBackup / Main.c < prev    next >
C/C++ Source or Header  |  1988-01-02  |  20KB  |  843 lines

  1. /* MRBackup - Amiga Hard Disk Backup Utility
  2.  * Filename:    Main.c
  3.  * Author:        Mark R. Rinfret
  4.  * Date:        08/01/87
  5.  *
  6.  * This program has been contributed to the public domain.  It represents
  7.  * a collection of original work and other public domain offerings that 
  8.  * were found to be useful in writing this application.  To the best of
  9.  * my knowledge, this program works as described in the accompanying
  10.  * document, but no warranties are made in this regard.
  11.  * USE AT YOUR OWN RISK.
  12.  *
  13.  * If you find this program useful, or if you have comments or suggestions
  14.  * for enhancing it, please send email to mark@unisec.USI.COM or U.S. Mail
  15.  * to:
  16.  *        Mark R. Rinfret
  17.  *        348 Indian Avenue
  18.  *        Portsmouth, RI  02871
  19.  *        401-846-7639 (home)
  20.  *        401-849-4174 (work)
  21.  *
  22.  * History:        (most recent change first)
  23.  *
  24.  * 09/04/87 -MRR- V1.3: Extracted routines related to the backup function
  25.  *                and placed them in package Backup.c.
  26.  *
  27.  * 09/03/87 -MRR- V1.3: Fixed a bug in the routines which handle the
  28.  *                listing file and pathname.
  29.  *                  Extended the IsCompressed() function to check for
  30.  *                .ARC and .ZOO files.
  31.  *
  32.  * 08/22/87 -MRR- V1.2: Extracted global data and definitions and placed
  33.  *                them in a common include file.
  34.  *
  35.  * 08/11/87 -MRR- V1.1: BUG!  The variable 'back' (backup sequence number) 
  36.  *                  was not set to 0 by Backup().
  37.  */
  38.  
  39. #define MAIN
  40.  
  41. #include "MRBackup.h"
  42. extern struct Requester *pathrequest;
  43. extern struct Window *pathwindow;
  44.  
  45.  
  46. /* Main program  - N.S.D.T.! */
  47.  
  48. main(argc,argv)
  49. int     argc;
  50. char   *argv[];
  51. {
  52.  
  53.     Initialize();
  54.     User();                    /* it's in the user's hands */
  55.     CleanUp(NULL, 0);
  56. }
  57.  
  58. /* Initialize the program. */
  59.  
  60. Initialize()
  61. {
  62.     if (! (IntuitionBase = (struct IntuitionBase *)
  63.             OpenLibrary("intuition.library", 33L ) ) ) {
  64.         CleanUp("Can't open Intuition library!", 20);
  65.     }
  66.  
  67.     if (!( mywindow = OpenWindow(&nw) ) )
  68.         CleanUp("Can't open program window!", 20);
  69.  
  70. #ifdef DEBUG
  71.     if (!(debugconsole = 
  72.         Open("CON:0/100/640/40/Debug Info", MODE_NEWFILE))) 
  73.         CleanUp("Can't open debug console!", 20);
  74. #endif
  75.  
  76.     if (!(console = 
  77.         Open("CON:0/100/640/90/Progress Report", MODE_NEWFILE)))
  78.         CleanUp("Can't open console!", 20);
  79.  
  80.     SetMenuStrip(mywindow, &Titles[0]);
  81.  
  82.     InitPathRequest();
  83.  
  84.     AddGadget(mywindow, &StopGad, -1L);
  85.     OnGadget(&StopGad, mywindow, NULL);
  86.  
  87.     now = (struct DateStamp *) 
  88.         AllocMem((long) sizeof(struct DateStamp), MEMF_PUBLIC);
  89.  
  90.     since = (struct DateStamp *)
  91.         AllocMem( (long) sizeof(struct DateStamp), MEMF_PUBLIC);
  92.  
  93.     InitBuffer();                    /* Allocate copy/compress buffer */
  94.     GetUserPrefs();                    /* Get user preferences */
  95.     SetSpeech();
  96. }
  97.  
  98. /* Allocate the buffer used for copy/compress operations. */
  99.  
  100. InitBuffer()
  101. {
  102.     for (bufsize = BUFMAX; bufsize > 2048; bufsize -= 2048 )
  103.         if (buffer = AllocMem(bufsize, MEMF_CHIP))
  104.             return;
  105.  
  106.     CleanUp("Not enough memory for copy buffer!\n",20);
  107. }
  108.  
  109. /* Handle program termination.
  110.  * Called with:
  111.  *        msg:        termination message or NULL
  112.  *        code:         exit code (0 => normal termination)
  113.  */
  114.  
  115. CleanUp(msg, code)
  116.     char *msg; int code;
  117. {
  118.     if (msg)
  119.         puts(msg);
  120.  
  121. #ifdef DEBUG
  122.     if (debugconsole) Close(debugconsole);
  123. #endif
  124.  
  125.     if (console) Close(console);
  126.  
  127.     if (listing) fclose(listing);
  128.  
  129.     if (mywindow) {
  130.         ClearMenuStrip(mywindow);
  131.         CancelPathRequest();
  132.         CloseWindow(mywindow);
  133.     }
  134.  
  135.     if (IntuitionBase) CloseLibrary(IntuitionBase);
  136.     if (now) FreeMem( now, (long) sizeof(struct DateStamp));
  137.     if (since) FreeMem( since, (long) sizeof(struct DateStamp));
  138.     if (buffer) FreeMem(buffer, bufsize);
  139.     exit(code);
  140. }
  141.  
  142. /* Break a full file specification into its volume and pathname components.
  143.  * Called with:
  144.  *        fullpath:    full pathname string (input)
  145.  *        volume:        volume name component, sans colon (output)
  146.  *        path:        pathname component (output)
  147.  * Returns:
  148.  *        status code: 1 => no volume name specified, 0 otherwise
  149.  */
  150.  
  151. BreakPath(fullpath,volume,path)
  152.     char *fullpath, *volume, *path;
  153. {
  154.     char c, *fp, *s;
  155.     unsigned has_volume = 1;            /* assume it has volume component */
  156.  
  157.     fp = fullpath;
  158.     s = volume;
  159.     if ( index(fp, ':') ) {                /* volume name specified? */
  160.         s = volume;
  161.         while ((c = *fp++) != ':') *s++ = c;
  162.     }
  163.     else
  164.         has_volume = 0;
  165.  
  166.     *s = '\0';                            /* terminate volume */
  167.     strcpy(path, fp);                    /* the rest is pathname stuff */
  168.     return has_volume;
  169. }
  170.  
  171. /* Do a quick poll on the STOP gadget.
  172.  * Returns 1 if STOP gadget hit, 0 otherwise.
  173.  */
  174.  
  175. int
  176. CheckStop()
  177. {
  178.     ULONG class;
  179.     struct Gadget *gadget;
  180.     struct IntuiMessage *msg;
  181.     int status = 0;
  182.  
  183.     if (msg = (struct IntuiMessage *) GetMsg(mywindow->UserPort)) {
  184.         class = msg->Class;
  185.         gadget = (struct Gadget *) msg->IAddress;
  186.         ReplyMsg(msg);
  187.         if (class == GADGETUP && gadget->GadgetID == STOPGAD) {
  188.             TypeAndSpeak("I am stopping, as you requested.\n");
  189.             status = ERR_ABORT;        /* quit */
  190.         }
  191.     }
  192.     return status;
  193. }
  194.  
  195. #ifdef DEBUG
  196. DebugWrite(msg)
  197.     char *msg;
  198. {
  199.     Write(debugconsole, msg, (long) strlen(msg));
  200. }
  201. #endif
  202.  
  203. /* Handle a gadget action.
  204.  * Called with:
  205.  *      window:        window that gadget is displayed in
  206.  *        class:        message class (GADGETUP/GADGETDOWN/?)
  207.  *        addr:        pointer to gadget structure
  208.  */
  209. DoGadget(window, class, addr)
  210.     struct Window *window; ULONG class; struct Gadget *addr;
  211. {
  212.     USHORT id;                    /* gadget identifier */
  213.     struct Gadget *upgad;
  214.     ULONG upclass;
  215.     struct IntuiMessage *upmsg;    /* require gadget up to complete */
  216.  
  217.     id = addr->GadgetID;
  218.  
  219. #ifdef TESTING
  220.     if (class == GADGETDOWN && window == pathwindow) {/* pathname gadget */
  221.         ActivateWindow(pathwindow);
  222.         do {
  223.             Wait(1L << pathwindow->UserPort->mp_SigBit);
  224.             if (upmsg = (struct IntuiMessage *) 
  225.                 GetMsg(pathwindow->UserPort)) {
  226.                 upclass = upmsg->Class;
  227.                 upgad = (struct Gadget *) upmsg->IAddress;
  228.                 ReplyMsg(upmsg);
  229.                 if (upclass == GADGETUP && upgad->GadgetID == id) {
  230.                     class = GADGETUP;    /* use new class */
  231.                 }
  232.                 else {
  233.                     upmsg = NULL;
  234.                     ActivateGadget(addr, pathwindow, pathrequest);
  235.                 }
  236.             }
  237.         }
  238.         while (! upmsg );
  239.     }
  240. #endif
  241.  
  242.     if (class == GADGETUP) {
  243. #ifdef DEBUG
  244.         sprintf(debugmsg,"GADGETUP: %d\n",id);
  245.         DebugWrite(debugmsg);
  246. #endif
  247.         switch (id) {
  248.             case HOMEPATHGAD:
  249.                 GetHomePath(addr);
  250.                 break;
  251.             case BACKPATHGAD:
  252.                 GetBackPath(addr);
  253.                 break;
  254.             case LISTPATHGAD:
  255.                 GetListPath(addr);
  256.                 break;
  257.             case XCLDPATHGAD:
  258.                 GetXcldPath(addr);
  259.                 break;
  260.             case STOPGAD:
  261.                 TypeAndSpeak(
  262.     "Use the STOP gadget during backup and restore operations.\n");
  263.                 break;
  264.             default:
  265.                 TypeAndSpeak("Unknown gadget - program error!\n");
  266.                 break;
  267.         }
  268.     }
  269. }
  270.  
  271. /* Get the backup device pathname specification. 
  272.  * Called with:
  273.  * Called with:
  274.  *        gadget:        pointer to relevant string gadget
  275.  *
  276.  * Side-effects:
  277.  *        If the pathname specification passes its requirements tests,
  278.  *        the global string backpath will be set to the pathname.
  279.  *        Otherwise, homepath is left unchanged and the gadget's string
  280.  *        is reset to the current contents of homevolume.
  281.  */
  282.  
  283. GetBackPath(gadget)
  284.     struct Gadget *gadget;
  285. {
  286.     char volume[VOLUME_MAX+1];
  287.     UBYTE *fullpath, path[PATH_MAX+1];
  288.  
  289.     fullpath = (UBYTE *) GadgetString(gadget);
  290.  
  291.     BreakPath(fullpath,volume,path);
  292.     if (strlen(volume) != 3 ||
  293.         tolower(volume[0]) != 'd' ||
  294.         tolower(volume[1]) != 'f' ||
  295.         volume[2] < '0' || volume[2] > '3') {
  296.         TypeAndSpeak("Backup device must be DF0 through DF3\n");
  297.         strcpy(fullpath, backpath);    /* restore previous value */
  298.     }
  299.     else
  300.         strcpy(backpath, fullpath);        /* use new value */
  301.  
  302.     RefreshGadgets(gadget, pathwindow, NULL);
  303. }
  304.  
  305. /* An error has occurred.  Find out what the user wants to do about it.
  306.  * Called with:
  307.  *        msg:        message string
  308.  *        options:    the "set" of options available
  309.  *                    0 => sorry - no options, ERR_ABORT returned
  310.  * Returns:
  311.  *        error recovery option
  312.  */
  313.  
  314. int
  315. GetErrOpt(msg, options)
  316.     char *msg; int options;
  317. {
  318.     int i, mask, option_count = 0, select;
  319.     int option_list[NERRCODE];
  320.  
  321.     sprintf(conmsg,"An error has interrupted processing:\n%s\n\n",msg);
  322.     TypeAndSpeak(conmsg);
  323.     return ERR_ABORT;                /* ...implement recovery... */
  324. }
  325.  
  326. /* Get the home pathname specification (volume and pathname).
  327.  * Called with:
  328.  *        gadget:        pointer to relevant string gadget
  329.  *
  330.  * Side-effects:
  331.  *        If the pathname specification passes its requirements tests,
  332.  *        the global string homepath will be set to the pathname.
  333.  *        Otherwise, homevolume is left unchanged and the gadget's string
  334.  *        is reset to the current contents of homevolume.
  335.  */
  336.  
  337. GetHomePath(gadget)
  338.     struct Gadget *gadget;
  339. {
  340.     UBYTE *fullpath, path[PATH_MAX+1], volume[VOLUME_MAX+1];
  341.  
  342.     RemoveGadget(pathwindow, gadget);
  343.     fullpath = (UBYTE *) GadgetString(gadget);
  344.  
  345.     BreakPath(fullpath, volume, path);
  346.     if (!IsDir(fullpath)) {
  347. badpath:
  348.         TypeAndSpeak(
  349.     "Home path must be a disk device with optional directory name!\n");
  350.         strcpy(fullpath, homepath);    /* restore previous value */
  351.     }
  352.     else if (strlen(volume) != 3 ||
  353.             tolower(volume[0]) != 'd' ||
  354.             (tolower(volume[1]) != 'h' && tolower(volume[1]) != 'f') ||
  355.             !isdigit(volume[2])) {
  356. #ifdef DEBUG
  357.             sprintf(debugmsg,"home device = \"%s\" ? \n",volume);
  358.             DebugWrite(debugmsg);
  359. #endif
  360.             goto badpath;
  361.     }
  362.     else {
  363.         strcpy(homepath, fullpath);
  364.         sprintf(conmsg,"Home path is %s\n", homepath);
  365.         WriteConsole(conmsg);
  366.     }
  367.     ResetStringInfo(gadget->SpecialInfo);
  368.     AddGadget(pathwindow, gadget, -1L);
  369.     RefreshGadgets(gadget, pathwindow, NULL);
  370. }    
  371.  
  372. /* Get the listing pathname.
  373.  * Called with:
  374.  *        gadget:        pointer to relevant string gadget
  375.  *
  376.  * Side-effects:
  377.  */
  378.  
  379. GetListPath(gadget)
  380.     struct Gadget *gadget;
  381. {
  382.     UBYTE *path;
  383.  
  384.     RemoveGadget(pathwindow, gadget);
  385.     path = (UBYTE *) GadgetString(gadget);
  386.     if (!do_listing) {
  387.         TypeAndSpeak("Listing mode is not active.  ");
  388.         TypeAndSpeak("Your change has been ignored.\n");
  389. badpath:
  390.         strcpy(path, listpath);
  391.     }
  392.     else {
  393.         if (strcmp(path, listpath)) {    /* not same pathname */
  394.             if (OpenList(path)) goto badpath;
  395.             strcpy(listpath, path);
  396.             sprintf(conmsg,"Listing path is %s\n", listpath);
  397.             WriteConsole(conmsg);
  398.         }
  399.     }
  400.     ResetStringInfo(gadget->SpecialInfo);
  401.     AddGadget(pathwindow, gadget, -1L);
  402.     RefreshGadgets(gadget, pathwindow, NULL);
  403. }
  404.  
  405. GetXcldPath(gadget)
  406.     struct Gadget *gadget;
  407. {
  408.     UBYTE *path;
  409.  
  410.     RemoveGadget(pathwindow, gadget);
  411.     path = (UBYTE *) GadgetString(gadget);
  412.     if (!strcmp(path, excludepath))        /* same pathname */
  413.         return;
  414.     if (access(path, 0)) {                /* can't access the file? */
  415.         TypeAndSpeak("I can't access the exclude file!\n");
  416.         strcpy(path, excludepath);        /* restore the gadget */
  417.     }
  418.     else {
  419.         strcpy(excludepath, path);
  420.         exclude_has_changed = true;
  421.         sprintf(conmsg,"Exclude path is %s\n", excludepath);
  422.     }
  423.     ResetStringInfo(gadget->SpecialInfo);
  424.     AddGadget(pathwindow, gadget, -1L);
  425.     RefreshGadgets(gadget, pathwindow, NULL);
  426. }
  427.  
  428. /* Output a new header to the listing file. */
  429.  
  430. Header()
  431. {
  432.     if (do_listing) {
  433.         fprintf(listing,"\f    MRBackup Listing of Volume %s\n\n", destvol);
  434.         linecount = 2;
  435.     }
  436. }
  437.  
  438. /* Determine if a file is compressed.  This is currently done by looking
  439.  * at the file name suffix.  A rather dumb assessment of the file type
  440.  * is made based on a match to one of these suffixes.
  441.  * 
  442.  * Called with:
  443.  *        filename:    file name string
  444.  * Returns:
  445.  *        1 => file is compressed, 0 => file is not compressed
  446.  */
  447.  
  448. int     
  449. IsCompressed(filename)
  450.     char *filename;
  451. {
  452. #define NSUFFIX 3            /* number of known suffixes */
  453. typedef struct {
  454.     char *sufx;
  455.     USHORT length;
  456.     } T_SUFFIX;
  457.  
  458. static T_SUFFIX suffixes[NSUFFIX] = {
  459.     {".z", 2} ,
  460.     {".arc", 4}, 
  461.     { ".zoo", 4}
  462.     };
  463.  
  464.     USHORT i, length;
  465.     char *s;
  466.  
  467.     length = strlen(filename);    /* get length of argument filename */
  468.  
  469.     for (i = 0; i < NSUFFIX; ++i) {
  470.         if (length < suffixes[i].length) continue;
  471.         /* We're just interested in the current suffix length. */
  472.         s = filename + length - suffixes[i].length;
  473.         if (!strcmpc(s, suffixes[i].sufx))
  474.             return true;        /* suffixes match */
  475.     }
  476.     return false;
  477. }
  478.  
  479. /* Output a line to the listing file.  Start a new line if necessary.
  480.  * The output string is assumed to have no form control characters.
  481.  * Called with:
  482.  *        str:        string to be output
  483.  */
  484. ListLine(str)
  485.     char *str;
  486. {    
  487.     if (do_listing) {
  488.         if (linecount >= LINES_PER_PAGE) Header();
  489.         fprintf(listing,"  %s\n",str);
  490.         fflush(listing);
  491.         ++linecount;
  492.     }
  493. }
  494.  
  495. /* Create a new directory on the destination disk.
  496.  * Called with:
  497.  *        name:        directory pathname
  498.  * Returns:
  499.  *        false => success
  500.  *        true  => failure
  501.  * Notes:
  502.  *        NewDir may be called by NewDisk() or BackupDirs().  You should
  503.  *        note that a condition of mutual recursion between CheckSize and
  504.  *        NewDir exists, due to the following:
  505.  *
  506.  *        When NewDir is called from BackupDirs(), it calls CheckSize(),
  507.  *        which in turn calls NewDisk() if there is no space left.
  508.  *        The create_dir parameter to CheckSize() (false) prevents a
  509.  *        secondary call to NewDir() since the job is already being
  510.  *        performed.
  511.  *
  512.  *        When CheckSize() is called as a result of BackupFiles()
  513.  *        processing, the create_dir parameter is true so that NewDir()
  514.  *        will be called if NewDisk() is called, thus creating a
  515.  *        continuation of the current directory.  Confused?  Me too :-).
  516.  */
  517. int
  518. NewDir(name)
  519.     char   *name;
  520. {
  521.     char c;
  522.     struct Lock *dirlock;
  523.     int dirleng;
  524.     int errnum;
  525.     char dirname[256];
  526.     int nameindx = 0, nameleng;
  527.  
  528.     size--;                            /* takes a block for a directory */
  529.     if (CheckSize(false))            /* have room on disk? */
  530.         return 1;
  531.  
  532.     strcpy(dirname,destvol);        /* start with volume name */
  533.     dirleng = strlen(dirname);
  534.     nameleng = strlen(name);
  535.  
  536.     /* Parse the pathname, one directory node at a time, creating
  537.      * directories as needed.
  538.      */
  539.  
  540.     while (nameindx < nameleng) {
  541.         if (nameindx)                /* 2nd - nth pass? */
  542.             dirname[dirleng++] = '/'; /* directory separator */
  543.         while ((c = name[nameindx++]) && c != '/')
  544.             dirname[dirleng++] = c;
  545.         dirname[dirleng] = '\0';    /* terminate with null */
  546.         if (dirlock = Lock(dirname,SHARED_LOCK)) /* subdir exists? */
  547.             UnLock(dirlock);
  548.         else {                        /* create subdirectory */
  549.             if ((dirlock = CreateDir(dirname))== NULL){
  550.                 if ((errnum = IoErr())== ERROR_DIRECTORY_NOT_EMPTY){
  551.                     sprintf(conmsg,
  552.                         "Directory %s already exists!\n",dirname);
  553.                     TypeAndSpeak(conmsg);
  554.                 }
  555.                 else {
  556.                     sprintf(conmsg,
  557.                         "ERROR %d: Unable to create directory %s\n",
  558.                         errnum,dirname);
  559.                     TypeAndSpeak(conmsg);
  560.                     return 1;
  561.                 }
  562.             }
  563.             else
  564.                 UnLock(dirlock);
  565.         }
  566.     }                                /* endwhile */
  567.     return 0;
  568. }
  569.  
  570. /* Skip 'n' lines in the listing file.
  571.  * Called with:
  572.  *        n:        number of lines to skip
  573.  */
  574.  
  575. NewLine(n)
  576.     int n;
  577. {
  578.     if (do_listing) {
  579.         if (n + linecount >= LINES_PER_PAGE)
  580.             Header();
  581.         else while (n--) {
  582.             fputc('\n', listing);
  583.             ++linecount;
  584.         }
  585.     }
  586. }
  587.  
  588. /* Open the listing file.
  589.  * Called with:
  590.  *        name:        file or device pathname
  591.  * Returns:
  592.  *        status (0 => success)
  593.  */
  594. int
  595. OpenList(name)
  596.     char *name;
  597. {
  598.     int status = 0;
  599.  
  600.     if (listing) fclose(listing);        /* prior listing file open? */
  601.     if (!(listing = fopen(name, "w"))) {
  602.         status = errno;
  603.         sprintf(conmsg,
  604.                 "I can't open the listing file \"%s\", error %d.\n",
  605.                 name, status);
  606.         TypeAndSpeak(conmsg);
  607.     }
  608.     else
  609.         linecount = LINES_PER_PAGE;
  610.     return status;
  611. }
  612.  
  613. /* Reset the variables in a StringInfo structure.
  614.  * Called with:
  615.  *        s:        pointer to a StringInfo
  616.  */
  617. ResetStringInfo(s)
  618.     struct StringInfo *s;
  619. {
  620.     *(s->UndoBuffer) = '\0';
  621.     s->BufferPos = 0;
  622.     s->DispPos = 0;
  623.     s->UndoPos = 0;
  624.     s->NumChars = strlen(s->Buffer);
  625. }
  626.  
  627. /* Enable/disable speech capability, based on global 'do_speech'. */
  628.  
  629. SetSpeech()
  630. {
  631.     if (do_speech) {
  632.         if (!SpeechOn()) {
  633.             DateStamp(now);
  634.             if (now->ds_Tick < 1500)
  635.                 Say("That feels good!  Thanks for turning me on!");
  636.             else
  637.                 Say("Hi.  How may I help you?");
  638.         }
  639.     }
  640.     else
  641.         SpeechOff();
  642. }
  643.  
  644. Speak(msg)
  645.     char *msg;
  646. {
  647.     if (do_speech) Say(msg);
  648. }
  649.  
  650. /* Perform a case-insensitive string compare.
  651.  * Called with:
  652.  *        s1, s2:        strings to be compared
  653.  * Returns:
  654.  *        comparison code:    0     => strings match
  655.  *                           <0    => s1 < s2
  656.  *                           >0    => s1 > s2
  657.  */
  658. int
  659. strcmpc(s1, s2)
  660.     register char *s1, *s2;
  661. {
  662.     int c1, c2, cd;
  663.  
  664.     do {
  665.         c1 = tolower(*s1++);
  666.         c2 = tolower(*s2++);
  667.         if (cd = (c1 - c2)) break;
  668.     } while (c1 && c2);
  669.  
  670.     return cd;
  671. }
  672.  
  673. /* Type a message to the console and optionally "speak" it.
  674.  * Called with:
  675.  *        msg:        message string
  676.  */
  677. TypeAndSpeak(msg)
  678.     char *msg;
  679. {
  680.     WriteConsole(msg);
  681.     if (do_speech) Say(msg);
  682. }
  683.  
  684. /* Handle IDCMP messages generated by user actions. */
  685.  
  686. User()
  687. {
  688.     ULONG class;                /* message class */
  689.     USHORT code;                /* message code */
  690.     USHORT gadgid;                /* gadget ID */
  691.     APTR Iadr;                    /* address field from message */
  692.     struct IntuiMessage *msg;    /* Intuition message pointer */
  693.     struct Window *msgwindow;    /* window message occurred in */
  694.     USHORT quit = 0;
  695.     SHORT x,y;                    /* mouse x and y position */
  696.     ULONG waitbits;
  697.  
  698.     waitbits = (1L << mywindow->UserPort->mp_SigBit) |
  699.                (1L << pathwindow->UserPort->mp_SigBit);
  700. #ifdef DEBUG
  701.     sprintf(debugmsg,"User: waitbits = %08lx\n", waitbits);
  702.     DebugWrite(debugmsg);
  703. #endif
  704.  
  705.     while (!quit) {
  706.         ActivateWindow(mywindow);
  707.         Wait(waitbits);
  708.         while (!quit) {
  709.             if (!(msg = (struct IntuiMessage *) 
  710.                 GetMsg(mywindow->UserPort)))
  711.                 if (!(msg = (struct IntuiMessage *)
  712.                       GetMsg(pathwindow->UserPort)))
  713.                     break;
  714.  
  715.             class = msg->Class;
  716.             code = msg->Code;
  717.             Iadr = msg->IAddress;
  718.             x = msg->MouseX;
  719.             y = msg->MouseY;
  720.             msgwindow = msg->IDCMPWindow;
  721.             ReplyMsg(msg);        /* acknowledge the message */
  722. #ifdef DEBUG
  723.             sprintf(debugmsg,"Message class: 0x%lx, code: 0x%x\n",
  724.                 class, code);
  725. #endif
  726.             switch (class) {
  727.             case CLOSEWINDOW:
  728.                 ++quit;
  729.                 break;
  730.  
  731.             case GADGETUP:
  732.             case GADGETDOWN:
  733.                 DoGadget(msgwindow, class, Iadr);
  734.                 break;
  735.  
  736.             case MENUPICK:
  737.                 quit = UserMenu(code);
  738.                 break;
  739.  
  740.             default:
  741.                 break;            /* ignore the rest */
  742.             }                    /* end switch(class) */
  743.         }
  744.     }
  745. }
  746.  
  747. /* Handle a menu selection. 
  748.  * Called with:
  749.  *        xcode:        menu selection code
  750.  * Returns:
  751.  *        status code (1 => Quit was selected)
  752.  */
  753.  
  754. int
  755. UserMenu(xcode)
  756.     USHORT xcode;
  757. {
  758.     USHORT code = xcode;
  759.     struct MenuItem *item;
  760.     USHORT itemnum,menunum;
  761.  
  762.     while (code != MENUNULL) {
  763.         menunum = MENUNUM(code);
  764.         itemnum = ITEMNUM(code);
  765.  
  766. #ifdef DEBUG
  767.         sprintf(debugmsg,"menu = %d, item = %d\n",menunum,itemnum);
  768.         DebugWrite(debugmsg);
  769. #endif
  770.         item = ItemAddress(&Titles[0], code);
  771.  
  772.         switch (menunum) {
  773.         case MENU_PROJECT:            /* Project Menu */
  774.             switch (itemnum) {
  775.             case ITEM_BACKUP:        /* Backup */
  776.                 Backup();
  777.                 break;
  778.             case ITEM_RESTORE:        /* Restore */
  779.                 Restore();
  780.                 break;
  781.             case ITEM_ABOUT:        /* About */
  782.                 About();
  783.                 break;
  784.             case ITEM_QUIT:            /* Quit */
  785.                 return 1;            
  786.             default:
  787.                 DisplayBeep(NULL);
  788.                 break;
  789.             }
  790.             break;
  791.  
  792.         case MENU_FLAGS:            /* Flags Menu */
  793.             switch (itemnum) {
  794.             case ITEM_COMPRESS:        /* Compression */
  795.                 do_compress = IsChecked(item); 
  796.                 break;
  797.             case ITEM_NOCOMPRESS:    /* No Compression */
  798.                 do_compress = !IsChecked(item);
  799.                 break;
  800.             case ITEM_LIST:            /* Listing */
  801.                 do_listing = IsChecked(item);
  802.                 break;
  803.             case ITEM_NOLIST:        /* No Listing */
  804.                 do_listing = !IsChecked(item);
  805.                 break;
  806.             case ITEM_SPEECH:        /* Speech */
  807.                 do_speech = IsChecked(item);
  808.                 SetSpeech();
  809.                 break;
  810.             case ITEM_NOSPEECH:        /* No Speech */
  811.                 do_speech = !IsChecked(item);
  812.                 SetSpeech();
  813.                 break;
  814.             default:
  815.                 DisplayBeep(NULL);
  816.                 break;
  817.             }
  818.         }
  819. #define EXTENDED_SELECT_WORKS
  820. #ifdef EXTENDED_SELECT_WORKS
  821.         code = item->NextSelect; 
  822.         /* This next line is a kludge.  Testing has revealed that
  823.          * the NextSelect field is returning 0000x.  Why?
  824.          */
  825.         if (!code) break;
  826. #else
  827.         break;
  828. #endif
  829.     }
  830.     return 0;
  831. }
  832.  
  833.  
  834. /* Write a message to the console window.
  835.  * Called with:
  836.  *        msg:    message string
  837.  */
  838. WriteConsole(msg)
  839.     char *msg;
  840. {
  841.     Write(console, msg, (long) strlen(msg));
  842. }
  843.