home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 402.lha / DFC_v4.0 / dfc.c < prev    next >
C/C++ Source or Header  |  1990-07-26  |  41KB  |  1,350 lines

  1. /*
  2.  *   This is dfc, a disk copying and formatting utility written by Radical
  3.  *   Eye Software.  This program is in the public domain; anyone can do
  4.  *   anything they want with it.  This software is made available on an as-is
  5.  *   basis; don't come to me if you destroy your entire fish disk library
  6.  *   with it!  Of course, it was tested rather extensively before it was
  7.  *   released . . .
  8.  *
  9.  *   Version 4 fixed a dumb, dumb, dumb bug in formatting in version 3.
  10.  *
  11.  *   Version 3 released on 6 July 1990.  Checked for `DOS\0' on track 0,
  12.  *   block 0 before munging disks, so that kickstart disks still can be
  13.  *   copied correctly.  Note that this means that, while copying non-DOS
  14.  *   disks such as Kickstarts, you can have two identical floppies.  I
  15.  *   don't think this will confuse DOS, since they are not DOS disks, but
  16.  *   I'd be careful anyway about exiting the program with clones in the
  17.  *   drives; remove the disks before exiting the program.
  18.  *
  19.  *   Version 2 released on 4 Febuary 1989.  Supplied default `Empty' name
  20.  *   on format if no name given.  Automatically retries first cylinder to
  21.  *   avoid `errors' because of too-late diskchange.  Parses workbench
  22.  *   message.  Supplied with `fformat' for sys:system.
  23.  *
  24.  *   To make these programs replace `diskcopy' and `format' on a workbench
  25.  *   disk, simply copy dfc to `sys:system/diskcopy' and format to
  26.  *   `sys:system/fformat'.  Everything should work fine.
  27.  *
  28.  *   This code was written with Manx 3.4b on February 8, 1988.
  29.  *
  30.  *   dfc is meant to replace both format and diskcopy, and be smaller than
  31.  *   either.  It is meant to work exactly the same as either, depending on
  32.  *   the command line arguments you give it.  In addition, it has several
  33.  *   additional options, such as buffering of a disk for a quick additional
  34.  *   copy, the ability to toggle verify mode on and off, and the ability to
  35.  *   write to multiple disks at the same time.  It also has a nice Intuition
  36.  *   interface.  It will not replace the `DiskCopy' or `Format' options from
  37.  *   the workbench, however; these have to work with the Workbench startup
  38.  *   message, which this program does not.
  39.  *
  40.  *   This program can be invoked with no command line options.  In this case,
  41.  *   it defaults to a diskcopy from df0: to df1: with verify on and buffering
  42.  *   on.  All keyword options can be preceded by a dash, for you Unix lovers
  43.  *   out there, and can be abbreviated to one character.  The following
  44.  *   parameters are accepted:
  45.  *
  46.  *   f[rom] disk       Use `disk' as the source drive.  The format for disk
  47.  *                     is completely free; only the numerals in the argument
  48.  *                     are looked at.
  49.  *   t[o] disks        Use `disks' as the destination drives.  Again, the
  50.  *                     format is unspecified.
  51.  *   v[erify]          Verify all writes (default).
  52.  *   nov[erify]        Turn off the verify mode.
  53.  *   b[uffer]          Use extra buffer memory.  If your system has enough
  54.  *                     memory, an entire disk will be cached in RAM.
  55.  *                     Otherwise, almost all of the system memory will be
  56.  *                     used.
  57.  *   nob[uffer]        Do not buffer; use minimum memory.  (Default.)
  58.  *   n[ame] diskname   Format the destination disk with name `diskname'.
  59.  *                     Otherwise a diskcopy is assumed.
  60.  *   d[...]            A drive argument.  Might not have a d; simple numbers
  61.  *                     work as well.  The first occurance of such an argument
  62.  *                     sets the source and destination fields; a subsequent
  63.  *                     occurance only sets the destination.
  64.  *
  65.  *   Thus,
  66.  *      dfc df0: name "Foo bar baz"
  67.  *   formats drive df0: and names the resultant disk "Foo bar baz".
  68.  *      dfc df0: df1:
  69.  *   diskcopies from df0: to df1:.
  70.  *      dfc -nov 0 123
  71.  *   diskcopies from drive 0 to drives 1, 2, and 3, with verify turned off.
  72.  *      dfc from df0: to df0:,df1:,df2:,df3:
  73.  *   diskcopies from drive df0: to all four drives.
  74.  *      dfc name Foo
  75.  *   formats drive df1 with the name "Foo".
  76.  *
  77.  *   Once the program opens its window up, you will have a bunch of gadgets.
  78.  *   The left-most column of gadgets is the source selection; here you can
  79.  *   choose either format, or one of the four drives.  The next column is
  80.  *   the destination column; choose any combination of the four drives by
  81.  *   clicking on the gadgets.  Lines will be drawn between these two columns
  82.  *   indicating the current selections.  If a drive is not available, no line
  83.  *   will be drawn to it.
  84.  *
  85.  *   The topmost gadget is the name gadget.  This is used to set the name of
  86.  *   your disk while formatting.
  87.  *
  88.  *   The `Go' gadget starts the program up; you had better have the disks in
  89.  *   the drives when you hit this gadget!  The `Again' gadget makes another
  90.  *   copy of the last disk, if buffering is on and enough buffers were
  91.  *   allocated for an entire disk.  This gadget requires all destination
  92.  *   disks to be in their drives already.
  93.  *
  94.  *   The `Retry' gadget tells the system to retry after a disk error.  The
  95.  *   `Quit' gadget tells the system to abort an operation; you can hit this
  96.  *   at an error, which aborts the current operation; you can hit this during
  97.  *   a copy or format operation, which does the same; or you can hit this
  98.  *   with the drives inactive, which exits the program.
  99.  *
  100.  *   The `Verify' gadget turns the verify mode on and off.  The `Buffer'
  101.  *   gadget turns buffering on and off.
  102.  *
  103.  *   All of these gadgets have keyboard shortcuts which are simply the first
  104.  *   letter of the gadget name.  The source gadgets can be selected with the
  105.  *   digits `0', `1', `2', or `3'; the destination gadgets with the shifted
  106.  *   versions of these keys (')', `!', `@', and `#'.)  The carriage return
  107.  *   key is a synonym for `Go', for added diskcopy and format compatibility.
  108.  *
  109.  *   Enjoy this program!  Please send any bug reports to Tomas Rokicki,
  110.  *   Box 2081, Stanford, CA  94309.
  111.  */
  112. #define TITLE "dfc 4 Radical Eye Software"
  113. /*
  114.  *   A handful of includes for good luck.
  115.  */
  116. #include "intuition/intuition.h"
  117. #include "functions.h"
  118. #include <exec/exec.h>
  119. #include <exec/execbase.h>
  120. #include <devices/trackdisk.h>
  121. #include <libraries/dosextens.h>
  122. #include <workbench/startup.h>
  123. /*
  124.  *   These are the various globals this routine uses.
  125.  */
  126. struct StandardPacket *gpacket ;   /* packet to send various things */
  127. struct IntuiMessage *message ;     /* the message we are working on */
  128. struct Gadget *gadad ;             /* address of gadget from message */
  129. struct Window *window ;            /* our window */
  130. struct MsgPort *port ;             /* I/O port for dos communication */
  131. struct IntuitionBase *IntuitionBase ;
  132.                                    /* we need Intuition */
  133. struct GfxBase *GfxBase ;          /* and graphics */
  134. int havedisk ;                     /* do we have a full disk in RAM? */
  135. int hibuf ;                        /* the highest buffer we have */
  136. long output ;                      /* can we write output? */
  137. char *buffers[81] ;                /* pointers to our buffers */
  138. struct IOExtTD *diskreq[4] ;       /* our I/O request blocks */
  139. long diskChangeCount[4] ;          /* last time disk was changed */
  140. int source = 1 ;                   /* the source disk, not a mask */
  141. int dest = 2 ;                     /* all destination drives */
  142. int verify = 1 ;                   /* verify writes? */
  143. int buffer = 0 ;                   /* are we using lots of memory? */
  144. int isdosdisk ;                    /* is the current disk a dos disk? */
  145. char namebuf[32] ;                 /* this buffer holds disk names */
  146. char df0[] = "DF0:" ;              /* use only one copy of this name */
  147. char df1[] = "DF1:" ;              /* ditto */
  148. char df2[] = "DF2:" ;              /* ditto */
  149. char df3[] = "DF3:" ;              /* ditto */
  150. char *df[]={df0, df1, df2, df3};   /* for easy access to drive names */
  151. char blank[] =   "                    " ;
  152.                                    /* used to blank out the middle line */
  153. char msg[] = "Disk df  is not complete" ;
  154.                                    /* error message for a disk */
  155. char reading[] = "Reading track xx dfx" ;
  156.                                    /* message for reading disks */
  157. char writing[] = "Writing track xx dfx" ;
  158.                                    /* message for writing disks */
  159. char veriing[] = "Ver'ing track xx dfx" ;
  160.                                    /* message for ver'ing disks */
  161. char nobuf[] = "! couldn't get buffer" ;
  162.                                    /* if we can't get a buffer */
  163. /*
  164.  *   detach stuff.
  165.  *
  166.  */
  167. #ifdef DETACHME
  168. long _stack = 6000 ;
  169. long _priority = 0 ;
  170. long _BackGroundIO = 1 ;
  171. char *_procname = "dfc" ;
  172. #endif
  173. /*
  174.  *   Always use topaz 80, and let's define an intuition buffer for our
  175.  *   general string writing stuff.
  176.  */
  177. struct TextAttr myfont = {(STRPTR)"topaz.font", TOPAZ_EIGHTY, 0, 0 };
  178. struct IntuiText intuitext = {1, 0, JAM2, 0, 0, &myfont} ;
  179. /*
  180.  *   Some defines setting the sizes of various things.  Play with these to
  181.  *   resize things.  Too bad string gadgets still have problems with large
  182.  *   fonts.
  183.  */
  184. #define WIDEGADG 83
  185. #define NARROWGADG 51
  186. #define STRGADG 256
  187. #define GADGHEIGHT 13
  188. #define LINE1 13
  189. #define LINE2 LINE1+11
  190. #define LINE3 LINE2+GADGHEIGHT
  191. #define LINE4 LINE3+GADGHEIGHT
  192. #define LINE5 LINE4+GADGHEIGHT
  193. #define LINE6 LINE5+GADGHEIGHT
  194. #define WINDOWHEIGHT LINE6+GADGHEIGHT+2
  195. #define COL1 4
  196. #define LINESTART COL1+NARROWGADG
  197. #define STRSTART COL1+NARROWGADG+23
  198. #define COL2 COL1+NARROWGADG+42
  199. #define LINEEND COL2-1
  200. #define COL3 COL2+NARROWGADG+10
  201. #define COL4 COL3+WIDEGADG+10
  202. #define WINDOWWIDTH COL4+WIDEGADG+4
  203. #define TRACKSIZE (2*512*11)
  204. /*
  205.  *   Now, some defines to assist in declaring a few things, and
  206.  *   centering strings, and the like.  These macros make the Intuition
  207.  *   crap a lot easier to put together.
  208.  */
  209. #define makeintuitext(name,string,size) struct IntuiText name={1,0,JAM2,\
  210.    (1+size-8*(sizeof(string)-1))/2,3,&myfont,(UBYTE*)string}
  211. #define makebox(name,tname,width,height,off) short tname[]={0,0,width,0,\
  212.    width,1,0,1,0,height-1,width,height-1,width,height,0,height,width,height,\
  213.    width,0};\
  214.    struct Border name={off,off,1,0,JAM2,10,tname}
  215. #define makengadg(name,prev,xpos,ypos,text,ch) struct Gadget name={prev,\
  216.    xpos,ypos,NARROWGADG,GADGHEIGHT,GADGHCOMP,RELVERIFY,BOOLGADGET,\
  217.    (APTR)&narrowbox,NULL,&text,NULL,NULL,ch}
  218. #define makewgadg(name,prev,xpos,ypos,text,ch) struct Gadget name={prev,\
  219.    xpos,ypos,WIDEGADG,GADGHEIGHT,GADGHCOMP,RELVERIFY,BOOLGADGET,\
  220.    (APTR)&widebox,NULL,&text,NULL,NULL,ch}
  221. /*
  222.  *   Now we declare all of our structures with the above macros.
  223.  *   First, the text for our gadgets, and then the gadgets.  No sweat.
  224.  */
  225. makeintuitext(formattext,"Format",NARROWGADG);
  226. makeintuitext(df0text,df0,NARROWGADG);
  227. makeintuitext(df1text,df1,NARROWGADG);
  228. makeintuitext(df2text,df2,NARROWGADG);
  229. makeintuitext(df3text,df3,NARROWGADG);
  230. makeintuitext(againtext,"Again",WIDEGADG);
  231. makeintuitext(retrytext,"Retry",WIDEGADG);
  232. makeintuitext(quittext,"Quit",WIDEGADG);
  233. makeintuitext(verifyontext,"Verify On ",WIDEGADG);
  234. makeintuitext(verifyofftext,"Verify Off",WIDEGADG);
  235. makeintuitext(bufferontext,"Buffer On ",WIDEGADG);
  236. makeintuitext(bufferofftext,"Buffer Off",WIDEGADG);
  237. makeintuitext(gotext,"Go",WIDEGADG);
  238. makebox(narrowbox,tn1,NARROWGADG-1,GADGHEIGHT-1,0);
  239. makebox(widebox,tn2,WIDEGADG-1,GADGHEIGHT-1,0);
  240. makebox(strbox,tn3,STRGADG-1,GADGHEIGHT-1,-2);
  241. makengadg(formatgadg,NULL,COL1,LINE2,formattext,'f');
  242. makengadg(sdf0gadg,&formatgadg,COL1,LINE3,df0text,'0');
  243. makengadg(sdf1gadg,&sdf0gadg,COL1,LINE4,df1text,'1');
  244. makengadg(sdf2gadg,&sdf1gadg,COL1,LINE5,df2text,'2');
  245. makengadg(sdf3gadg,&sdf2gadg,COL1,LINE6,df3text,'3');
  246. makengadg(ddf0gadg,&sdf3gadg,COL2,LINE3,df0text,')');
  247. makengadg(ddf1gadg,&ddf0gadg,COL2,LINE4,df1text,'!');
  248. makengadg(ddf2gadg,&ddf1gadg,COL2,LINE5,df2text,'@');
  249. makengadg(ddf3gadg,&ddf2gadg,COL2,LINE6,df3text,'#');
  250. makewgadg(gogadg,&ddf3gadg,COL3,LINE4,gotext,'g');
  251. makewgadg(retrygadg,&gogadg,COL3,LINE5,retrytext,'r');
  252. makewgadg(verifygadg,&retrygadg,COL3,LINE6,verifyontext,'v');
  253. makewgadg(againgadg,&verifygadg,COL4,LINE4,againtext,'a');
  254. makewgadg(quitgadg,&againgadg,COL4,LINE5,quittext,'q');
  255. makewgadg(buffergadg,&quitgadg,COL4,LINE6,bufferontext,'b');
  256. /*
  257.  *   We need one last gadget, the string gadget, and its associated
  258.  *   special info structure.
  259.  */
  260. struct StringInfo nameinfo={(UBYTE*)namebuf,NULL,0,31};
  261. struct Gadget namegadg={&buffergadg,STRSTART+2,LINE2+2,STRGADG-4,
  262.    GADGHEIGHT-4,GADGHCOMP,STRINGCENTER|RELVERIFY,STRGADGET,(APTR)&strbox,
  263.    NULL,NULL,NULL,(APTR)&nameinfo,'n'};
  264. /*
  265.  *   Now we have our window structure.  Initially not resizeable.
  266.  */
  267. struct NewWindow newwindow = {200,20,WINDOWWIDTH,WINDOWHEIGHT,0,1,
  268.    CLOSEWINDOW|VANILLAKEY|GADGETDOWN|GADGETUP,
  269.    WINDOWDEPTH|WINDOWCLOSE|WINDOWDRAG|SMART_REFRESH|ACTIVATE,
  270.    &namegadg,NULL,(UBYTE *)TITLE,NULL,NULL,-1,-1,-1,-1,WBENCHSCREEN};
  271. /*
  272.  *   Now we start coding.  This routine draws a string into the window at
  273.  *   a specific X, Y location.
  274.  */
  275. draw(s, x, y)
  276. char *s ;
  277. int x, y ;
  278. {
  279.    intuitext.IText = (UBYTE *)s ;
  280.    PrintIText(window->RPort, &intuitext, (long)x, (long)y) ;
  281. }
  282. /*
  283.  *   This routine gives us those pretty little DF0:BUSY things, which keep
  284.  *   AmigaDOS from futzing with the drives when we are playing with them.
  285.  */
  286. inhibit(d, t)
  287. int d ;
  288. long t ;
  289. {
  290.    register struct MsgPort *handler ;
  291.    register struct StandardPacket *packet = gpacket ;
  292.  
  293.    handler = (struct MsgPort *)DeviceProc(df[d]) ;
  294.    if (handler == NULL || port == NULL)
  295.       return ;
  296.    packet->sp_Msg.mn_Node.ln_Name = (char *)&(packet->sp_Pkt) ;
  297.    packet->sp_Pkt.dp_Link = &(packet->sp_Msg) ;
  298.    packet->sp_Pkt.dp_Port = port ;
  299.    packet->sp_Pkt.dp_Type = ACTION_INHIBIT ;
  300.    packet->sp_Pkt.dp_Arg1 = t ;
  301.    PutMsg(handler, packet) ;
  302.    WaitPort(port) ;
  303.    GetMsg(port) ;
  304. }
  305. /*
  306.  *   This routine looks at the global message and returns a character
  307.  *   indicating the selected gadget.  This gives us easy equivalence
  308.  *   of gadgets to vanillakeys, for instance.  This also replymsg()'s
  309.  *   the message.
  310.  */
  311. int getop() {
  312.    register long class ;
  313.    register int op ;
  314.    short code ;
  315.  
  316.    class = message->Class ;
  317.    code = message->Code ;
  318.    op = ' ' ;
  319.    gadad = (struct Gadget *)(message->IAddress) ;
  320.    ReplyMsg(message) ;
  321.    message = NULL ;
  322.    if (class == CLOSEWINDOW) {
  323.       op = 'q' ;
  324.    } else if (class == GADGETDOWN || class == GADGETUP) {
  325.       op = gadad->GadgetID ;
  326.    } else if (class == VANILLAKEY) {
  327.       op = code ;
  328.       gadad = NULL ;
  329.    }
  330.    return(upcase(op)) ;
  331. }
  332. /*
  333.  *   We don't want to queue up messages, because the user might hit
  334.  *   'g' 100 times accidentally.  We flush all pending messages, and
  335.  *   return the last operation.  This way, this routine can be used
  336.  *   to see if the user typed 'Q' to exit some operation.
  337.  */
  338. int disposemsgs() {
  339.    register int op = 0 ;
  340.  
  341.    while (message = (struct IntuiMessage *)
  342.                         GetMsg(window->UserPort))
  343.       op = getop() ;
  344.    return(op) ;
  345. }
  346. /*
  347.  *   This is put into a function to help make the program smaller.
  348.  *   It takes a character and makes sure it is uppercase.
  349.  */
  350. int upcase(c)
  351. register int c ;
  352. {
  353.    if ('a' <= c && c <= 'z')
  354.       return(c-32) ;
  355.    else
  356.       return(c) ;
  357. }
  358. /*
  359.  *   Here we wait for a quit or retry key.  We also accept go, space,
  360.  *   and carriage return.
  361.  */
  362. int abortretry() {
  363.    register int op ;
  364.  
  365.    disposemsgs() ;
  366.    while (1) {
  367.       while ((message = (struct IntuiMessage *)
  368.                      GetMsg(window->UserPort))==NULL)
  369.          WaitPort(window->UserPort) ;
  370.       op = getop() ;
  371.       if (op == 'Q' || op == 'R' || op == ' ' || op == 'G' || op == 10)
  372.          return(op) ;
  373.    }
  374. }
  375. /*
  376.  *   This routine pads a string out to 40 characters.  Used to write to
  377.  *   the top line of the window.
  378.  */
  379. static char ibuf[41] ;
  380. char *to40(s)
  381. register char *s ;
  382. {
  383.    register int i = 0 ;
  384.    register char *p = ibuf ;
  385.  
  386.    while (*s != 0)
  387.       p[i++] = *s++ ;
  388.    while (i < 40)
  389.       p[i++] = ' ' ;
  390.    return(p) ;
  391. }
  392. /*
  393.  *   This error routine draws a message up at the top of the screen, and
  394.  *   waits for a response.  After it gets one, it returns the response.
  395.  *   It clears the top line afterwards.
  396.  */
  397. int error(s)
  398. register char *s ;
  399. {
  400.    int op ;
  401.  
  402.    DisplayBeep(NULL) ;
  403.    if (*s == '!' || !window) {
  404.       if (output) {
  405.          Write(output, s, (long)strlen(s)) ;
  406.          Write(output, "\n", 1L) ;
  407.       }
  408.       cleanup() ;
  409.    } else {
  410.       draw(to40(s), COL1, LINE1+2) ;
  411.       op = abortretry() ;
  412.    }
  413.    draw(to40(""), COL1, LINE1+2) ;
  414.    return(op) ;
  415. }
  416. /*
  417.  *   If the user selects a set of drives, this routine first tries to
  418.  *   allocate them.  Then, based on the success of the allocation, it
  419.  *   draws lines in the window indicating the source and destination
  420.  *   drives.
  421.  */
  422. redrawlines() {
  423.    register int i, j ;
  424.  
  425.    allocdisks() ;
  426.    SetAPen(window->RPort, 0L) ;
  427.    RectFill(window->RPort, (long)LINESTART, (long)LINE2, (long)STRSTART-1,
  428.                            (long) LINE6+GADGHEIGHT) ;
  429.    RectFill(window->RPort, (long)STRSTART, (long)LINE3, (long)LINEEND,
  430.                            (long)LINE6+GADGHEIGHT) ;
  431.    SetAPen(window->RPort, 1L) ;
  432.    i = LINE3 + (GADGHEIGHT+1)/2 + source * GADGHEIGHT ;
  433.    for (j=0; j<4; j++)
  434.       if (dest & (1 << j)) {
  435.          Move(window->RPort, (long)LINESTART, (long)i) ;
  436.          Draw(window->RPort, (long)LINEEND,
  437.                          (long)(LINE3 + (GADGHEIGHT+1)/2 + j * GADGHEIGHT)) ;
  438.       }
  439. }
  440. /*
  441.  *   This routine parses a string, usually something like 'df0:' or
  442.  *   'df0:,df1:,df2:', but can be even '012', into just a mask indicating
  443.  *   which drives were selected.  It simply looks for the characters '0',
  444.  *   '1', '2', and '3'.
  445.  */
  446. int getmask(s)
  447. register char *s ;
  448. {
  449.    register int t = 0 ;
  450.  
  451.    while (*s != 0) {
  452.       if ('0' <= *s && *s <= '3')
  453.          t |= 1 << (*s - '0') ;
  454.       s++ ;
  455.    }
  456.    return(t) ;
  457. }
  458. /*
  459.  *   This is our exit routine.  It frees the drives, deletes the ports,
  460.  *   buffers, closes the window, and libraries.  Then it exits.
  461.  */
  462. cleanup() {
  463.    register int i ;
  464.  
  465.    for (i=0; i<4; i++)
  466.       FreeDisk(i) ;
  467.    if (gpacket)
  468.       FreeMem(gpacket, (long)sizeof(struct StandardPacket)) ;
  469.    if (port)
  470.       DeletePort(port) ;
  471.    freebuffers() ;
  472.    if (window)
  473.       CloseWindow(window) ;
  474.    if (GfxBase)
  475.       CloseLibrary(GfxBase) ;
  476.    if (IntuitionBase)
  477.       CloseLibrary(IntuitionBase) ;
  478.    exit(0) ;
  479. }
  480. /*
  481.  *   This routine attempts to allocate m buffers.  We try and leave at
  482.  *   least 32K of memory, even with the allocations.  We set hibuf at the
  483.  *   exit point.
  484.  */
  485. getbuffers(m)
  486. int m ;
  487. {
  488.    register int i ;
  489.  
  490.    if (buffers[80]==NULL &&
  491.       (buffers[80]=(char *)
  492.               AllocMem((long)TRACKSIZE, MEMF_CHIP | MEMF_PUBLIC))==NULL)
  493.       error(nobuf) ;
  494.    hibuf = 0 ;
  495.    for (i=0; i<m; i++)
  496.       if (buffers[i]==NULL &&
  497.          (AvailMem(MEMF_PUBLIC) < 32000 ||
  498.          (buffers[i]=(char *)AllocMem((long)TRACKSIZE, MEMF_PUBLIC))==NULL))
  499.             break ;
  500.       else
  501.          hibuf = i+1 ;
  502.    if (hibuf < 1)
  503.       error(nobuf) ;
  504.    for (; i<80; i++)
  505.       if (buffers[i] != NULL) {
  506.          FreeMem(buffers[i], (long)TRACKSIZE) ;
  507.          buffers[i] = NULL ;
  508.       }
  509. }
  510. /*
  511.  *   And this routine lets them all go.
  512.  */
  513. int freebuffers() {
  514.    register int i ;
  515.  
  516.    for (i=0; i<81; i++)
  517.       if (buffers[i] != NULL) {
  518.          FreeMem(buffers[i], (long)TRACKSIZE) ;
  519.          buffers[i] = NULL ;
  520.       }
  521. }
  522. /*
  523.  *   We attempt to create an I/O request.  We allocate memory for it, and
  524.  *   then initialize some of the ports.
  525.  */
  526. struct IORequest *CreatExtIO() {
  527.    register struct IORequest *ioReq ;
  528.  
  529.    ioReq = (struct IORequest *)AllocMem((long)sizeof(struct IOExtTD),
  530.                                         MEMF_CLEAR | MEMF_PUBLIC) ;
  531.    if (ioReq == NULL)
  532.       return (NULL) ;
  533.    ioReq->io_Message.mn_Node.ln_Type = NT_MESSAGE ;
  534.    ioReq->io_Message.mn_Node.ln_Pri = 0 ;
  535.    ioReq->io_Message.mn_ReplyPort = port ;
  536.    return(ioReq) ;
  537. }
  538. /*
  539.  *   This routine frees an I/O request.
  540.  */
  541. DeleteIO(ioExt)
  542. struct IORequest *ioExt ;
  543. {
  544.    FreeMem(ioExt, (long)sizeof(struct IOExtTD)) ;
  545. }
  546. /*
  547.  *   Now we try to allocate a disk.  First, we attempt to allocate an
  548.  *   I/O request, and then we actually open the device.  If either of
  549.  *   these fail, we return 0.  This can occur if a drive is opened that
  550.  *   doesn't exist.  After we have the disk, we inhibit AmigaDOS from
  551.  *   putzing with it.
  552.  */
  553. int OpenDisk(i)
  554. register int i ;
  555. {
  556.    register struct IOExtTD **p = diskreq+i ;
  557.  
  558.    if (*p)
  559.       return(1) ;
  560.    if ((*p = (struct IOExtTD *)CreatExtIO()) &&
  561.        OpenDevice(TD_NAME, (long)i, *p, 0L) == 0) {
  562.       inhibit(i, TRUE) ;
  563.       return(1) ;
  564.    } else {
  565.       if (*p) {
  566.          DeleteIO(*p) ;
  567.          *p = NULL ;
  568.       }
  569.       return(0) ;
  570.    }
  571. }
  572. /*
  573.  *   Here we release a disk.  We check that we have it first!  Then, we
  574.  *   close the device, uninhibit the drive, kill the I/O request, and
  575.  *   exit.
  576.  */
  577. FreeDisk(i)
  578. register int i ;
  579. {
  580.    register struct IOExtTD **p = diskreq+i ;
  581.  
  582.    if (*p) {
  583.       CloseDevice(*p) ;
  584.       inhibit(i, FALSE) ;
  585.       DeleteIO(*p) ;
  586.       *p = NULL ;
  587.    }
  588. }
  589. /*
  590.  *   This routine attempts to allocate all of the disks we need, based on the
  591.  *   current settings of source and dest.  If a disk cannot be allocated, it
  592.  *   is removed from both source or dest.  Source might be set to point to df0,
  593.  *   if this occurs.  Then, if we can't allocate df0, we exit fatally.  This
  594.  *   occurance can actually happen, if some other program has df0:.  (I think.)
  595.  */
  596. allocdisks() {
  597.    register int i, need ;
  598.  
  599. top:
  600.    need = dest ;
  601.    if (source != -1)
  602.       need |= 1 << source ;
  603.    for (i=0; i<4; i++, need >>= 1) {
  604.       if (need & 1) {
  605.          if (! OpenDisk(i)) {
  606.             dest &= ~(1 << i) ;
  607.             if (source == i) {
  608.                if (i == 0)
  609.                   error("! I couldn't allocate the internal drive") ;
  610.                source = 0 ;
  611.                goto top ;
  612.             }
  613.         }
  614.       } else
  615.         FreeDisk(i) ;
  616.    }
  617. }
  618. /*
  619.  *   We call this routine if we are going to be accessing this
  620.  *   disk.  It gets the changecount, so our read/write routines work.
  621.  */
  622. int InitDisk(d)
  623. register int d ;
  624. {
  625.    register struct IOExtTD *p = diskreq[d] ;
  626.    register int result ;
  627.  
  628.    p->iotd_Req.io_Command = TD_CHANGENUM ;
  629.    DoIO(p) ;
  630.    result = (diskChangeCount[d] != p->iotd_Req.io_Actual) ;
  631.    diskChangeCount[d] = p->iotd_Req.io_Actual ;
  632.    return(result) ;
  633. }
  634. /*
  635.  *   The plural of the above routine takes a mask and initializes all of
  636.  *   the drives.  It adds the source drive to the list automatically.
  637.  */
  638. initdisks(mask)
  639. register int mask ;
  640. {
  641.    register int d ;
  642.  
  643.    if (source != -1)
  644.       mask |= (1 << source) ;
  645.    for (d=0; d<4; d++)
  646.       if (mask & (1 << d))
  647.          InitDisk(d) ;
  648. }
  649. /*
  650.  *   Kill the motor of a drive.  So someone can stick disks in and out.
  651.  */
  652. motoroff(d)
  653. register int d ;
  654. {
  655.    register struct IOExtTD *p = diskreq[d] ;
  656.  
  657.    p->iotd_Req.io_Length = 0 ;
  658.    p->iotd_Req.io_Command = TD_MOTOR ;
  659.    DoIO(p) ;
  660. }
  661. /*
  662.  *   This routine is a fast machine-language block move, that moves
  663.  *   exactly one block of data.  Do not change TRACKSIZE and expect
  664.  *   this still to work!
  665.  */
  666. fcpy(dest, src)
  667. long *dest, *src ;
  668. {
  669. #asm
  670.     movem.l    a0-a6/d0-d7,-(a7)
  671.     move.l    12(a5),a0
  672.     move.l    8(a5),a1
  673.     move.l    #43,d0
  674. lsdf:
  675.     movem.l    (a0)+,a2-a6/d1-d7
  676.     movem.l    a2-a6/d1-d7,(a1)
  677.     add.w    #48,a1
  678.     movem.l    (a0)+,a2-a6/d1-d7
  679.     movem.l    a2-a6/d1-d7,(a1)
  680.     add.w    #48,a1
  681.     movem.l    (a0)+,a2-a6/d1-d7
  682.     movem.l    a2-a6/d1-d7,(a1)
  683.     add.w    #48,a1
  684.     movem.l    (a0)+,a2-a6/d1-d7
  685.     movem.l    a2-a6/d1-d7,(a1)
  686.     add.w    #48,a1
  687.     movem.l    (a0)+,a2-a6/d1-d7
  688.     movem.l    a2-a6/d1-d7,(a1)
  689.     add.w    #48,a1
  690.     movem.l    (a0)+,a2-a5
  691.     movem.l    a2-a5,(a1)
  692.     add.w    #16,a1
  693.     dbra    d0,lsdf
  694.     movem.l    (a7)+,a0-a6/d0-d7
  695. #endasm
  696. }
  697. /*
  698.  *   Another fast assembly language routine for verifying a buffer.  This
  699.  *   routine returns 0 if the two buffers are the same, and something else
  700.  *   otherwise.
  701.  */
  702. int fcmp(dest, src)
  703. long *dest, *src ;
  704. {
  705.     register int foo = 0 ;
  706. #asm
  707.     movem.l    d0/a0/a1,-(a7)
  708.     move.l    12(a5),a0
  709.     move.l    8(a5),a1
  710.     move.l    #2815,d0
  711. alsdf:
  712.     cmp.l    (a0)+,(a1)+
  713.     dbne    d0,alsdf
  714.     move.w    d0,d4
  715.     addq.w    #1,d4
  716.     movem.l    (a7)+,a0/a1/d0
  717. #endasm
  718.    return(foo) ;
  719. }
  720. /*
  721.  *   This routine turns on a particular gadget.
  722.  */
  723. turnon(g)
  724. register struct Gadget *g ;
  725. {
  726.    if (g->Flags & GADGDISABLED) {
  727.       SetAPen(window->RPort, 0L) ;
  728.       RectFill(window->RPort, (long)g->LeftEdge, (long)g->TopEdge,
  729.                               (long)g->LeftEdge+g->Width-1,
  730.                               (long)g->TopEdge+g->Height-1) ;
  731.       SetAPen(window->RPort, 1L) ;
  732.       OnGadget(g, window, NULL) ;
  733.    }
  734. }
  735. /*
  736.  *   This routine turns off a particular gadget.
  737.  */
  738. turnoff(g)
  739. register struct Gadget *g ;
  740. {
  741.    if (!(g->Flags & GADGDISABLED))
  742.       OffGadget(g, window, NULL) ;
  743. }
  744. /*
  745.  *   Our main copy routine.  The variable j holds the current destinations
  746.  *   that are being written into; as disks drop like flies, j will drop
  747.  *   them; at the end, we print a message about all the disks that dropped
  748.  *   out.  We start by initializing the disks.
  749.  */
  750. goforit(again)
  751. int again ;
  752. {
  753.    register int i, j, k, t ;
  754.    register int ohbuf ;
  755.  
  756.    turnoff(&gogadg) ;
  757.    turnoff(&verifygadg) ;
  758.    turnoff(&buffergadg) ;
  759.    turnoff(&againgadg) ;
  760.    turnon(&retrygadg) ;
  761.    turnoff(&namegadg) ;
  762.    RefreshGadgets(window->FirstGadget, window, NULL) ;
  763.    j = dest ;
  764.    initdisks(j) ;
  765.    ohbuf = hibuf ;
  766. /*
  767.  *   If we are formatting, we only use one buffer.  This avoids the
  768.  *   unsightly delay which happens if we build up the formatted disk in
  769.  *   memory first; the user wonders what the hell is going on.  We first
  770.  *   check that the user isn't trying to read and write from the same disk;
  771.  *   if he is, he will have to swap about 160 times, so we tell him no go.
  772.  */
  773.    if (source == -1)
  774.       ohbuf = 1 ;
  775.    if (ohbuf == 1 && source >= 0 && (dest & (1 << source))) {
  776.       while (error("No buffering?") == 'R') ;
  777.       goto finishup ;
  778.    }
  779.    for (t=0; t<80; t+=ohbuf) {
  780.       if (! again) {
  781.          for (k=0; k<ohbuf && t+k<80; k++) {
  782.             if (disposemsgs()=='Q')
  783.                goto aborted ;
  784.             if (getdata(source, t+k, k)==0)
  785.                goto finishup ;
  786.          }
  787.          if (source != -1) {
  788.             if (dest & (1 << source)) {
  789.                for (i=0; i<4; i++)
  790.                   if (j & (1 << i))
  791.                      motoroff(i) ;
  792.                do {
  793.                   if (error("Enter destination disks")=='Q')
  794.                      goto finishup ;
  795.                } while (! InitDisk(source)) ;
  796.                initdisks(j) ;
  797.             }
  798.          }
  799.          if (ohbuf == 80)
  800.             havedisk = 1 ;
  801.       }
  802.       for (k=0; k<ohbuf && t+k<80; k++) {
  803.          if (disposemsgs()=='Q')
  804.             goto aborted ;
  805.          for (i=0; i<4; i++) {
  806.             if (j & (1 << i)) {
  807.                if (writedata(i, t+k, k)==0) {
  808.                   j &= ~(1 << i) ;
  809.                   motoroff(i) ;
  810.                }
  811.             }
  812.          }
  813.       }
  814.       if (! again) {
  815.          if (source != -1 && (dest & (1 << source)) && t+k < 80) {
  816.             motoroff(source) ;
  817.             do {
  818.                if (error("Enter source disk")=='Q')
  819.                   goto finishup ;
  820.             } while (! InitDisk(source)) ;
  821.          }
  822.       }
  823.    }
  824.    goto finishup ;
  825. /*
  826.  *   On exit, we turn off all the motors, clear out the middle line, and
  827.  *   return.
  828.  */
  829. aborted:
  830.    j = 0 ;
  831. finishup:
  832.    for (i=0; i<4; i++)
  833.       if (diskreq[i])
  834.          motoroff(i) ;
  835.    for (i=0; i<4; i++)
  836.       if ((dest-j) & (1<<i)) {
  837.          msg[7] = i + '0' ;
  838.          error(msg) ;
  839.       }
  840.    draw(blank, COL3, LINE3+3) ;
  841.    disposemsgs() ;
  842. }
  843. /*
  844.  *   This routine writes a given message to the screen; either reading,
  845.  *   writing, or ver'ing.  It fills in the track and disk number.  It
  846.  *   checks first that the source isn't `format', which is created behind
  847.  *   the scenes instead of being read from an actual disk.  If we are
  848.  *   reading from track 40, we get the name of the disk and put it up on
  849.  *   the screen.
  850.  */
  851. writeop(s, t, d)
  852. register char *s ;
  853. register int t, d ;
  854. {
  855.    if (d != -1) {
  856.       s[14] = '0' + t / 10 ;
  857.       s[15] = '0' + t % 10 ;
  858.       s[19] = '0' + d ;
  859.       draw(s, COL3, LINE3+3) ;
  860.    }
  861. }
  862. /*
  863.  *   This routine gets data from disk d, track t, into buffer b.
  864.  *   If the disk is -1, it gets it from the format routine (later.)
  865.  *   We return 1 if success; 0 if failure.  If there is an error, we allow
  866.  *   the user to retry as many times as he likes.
  867.  */
  868. int getdata(d,t,b)
  869. register int d, t, b ;
  870. {
  871.    register struct IOExtTD *p = diskreq[d] ;
  872.    register int i = 0 ;
  873.  
  874.    if (d==-1) {
  875.       makeformatdata(t, b) ;
  876.       return(1) ;
  877.    }
  878.    do {
  879.       writeop(reading, t, d) ;
  880.       p->iotd_Req.io_Length = TRACKSIZE ;
  881.       p->iotd_Req.io_Data = (APTR)buffers[80] ;
  882.       p->iotd_Req.io_Command = ETD_READ ;
  883.       p->iotd_Count = diskChangeCount[d] ;
  884.       p->iotd_Req.io_Offset = t * (long)TRACKSIZE ;
  885.       DoIO(p) ;
  886.       fcpy(buffers[b], buffers[80]) ;
  887.    } while (p->iotd_Req.io_Error != 0 &&
  888.             ((i=error("Read Error; Quit/Retry?"))!='Q')) ;
  889.    if (i == 'Q')
  890.       return 0 ;
  891.    else {
  892.       if (t == 0)
  893.          isdosdisk = (*(long *)(buffers[b])) == 'DOS\0' ;
  894.       else if (t == 40)
  895.          writename(b) ;
  896.       return 1 ;
  897.    }
  898.    return (i != 'Q') ;
  899. }
  900. /*
  901.  *   writedata is analagous to the above routine.  However, if verify is
  902.  *   turned on, then we read the data back in from the disk and make sure
  903.  *   that it is correct.  Note that whenever we write track 40, we first
  904.  *   update the root block creation date and last modified date.
  905.  */
  906. int writedata(d,t,b)
  907. register int d, t, b ;
  908. {
  909.    register struct IOExtTD *p = diskreq[d] ;
  910.    register int i = 0 ;
  911.    register int flag = t ;
  912.  
  913. top:
  914.    if (t==40)
  915.       updaterootblock(b) ;
  916.    do {
  917.       writeop(writing, t, d) ;
  918.       fcpy(buffers[80], buffers[b]) ;
  919.       p->iotd_Req.io_Length = TRACKSIZE ;
  920.       p->iotd_Req.io_Data = (APTR)buffers[80] ;
  921.       p->iotd_Req.io_Command = TD_FORMAT ;
  922.       p->iotd_Count = diskChangeCount[d] ;
  923.       p->iotd_Req.io_Offset = t * (long)TRACKSIZE ;
  924.       DoIO(p) ;
  925.    } while (p->iotd_Req.io_Error != 0 && (flag++ == 0 ||
  926.             ((i=error("Write Error; Quit/Retry?"))!='Q'))) ;
  927.    if (i=='Q')
  928.       return(0) ;
  929.    if (verify) {
  930.       writeop(veriing, t, d) ;
  931.       p->iotd_Req.io_Length = TRACKSIZE ;
  932.       p->iotd_Req.io_Data = (APTR)buffers[80] ;
  933.       p->iotd_Req.io_Command = ETD_READ ;
  934.       p->iotd_Count = diskChangeCount[d] ;
  935.       p->iotd_Req.io_Offset = t * (long)TRACKSIZE ;
  936.       DoIO(p) ;
  937.       if ((p->iotd_Req.io_Error != 0 ||
  938.           fcmp(buffers[80], buffers[b])) &&
  939.           ((i=error("Verify Error; Quit/Retry?"))!='Q')) {
  940.          if (t==0)
  941.             InitDisk(d) ;
  942.          goto top ;
  943.       }
  944.    }
  945.    return (i != 'Q') ;
  946. }
  947. /*
  948.  *   This routine creates data for the format option.  Note the clever
  949.  *   way the data is built up; this routine should build a disk exactly
  950.  *   the same way the standard AmigaDOS format does.  If we are on track
  951.  *   40, some additional work must be done to create a root block and
  952.  *   bitmap, but it's not too bad.
  953.  */
  954. makeformatdata(t, b)
  955. int t, b ;
  956. {
  957.    register long *p ;
  958.    register long cs ;
  959.    register long i ;
  960.    register unsigned char *q ;
  961.  
  962.    isdosdisk = 1 ;
  963.    p = (long *)buffers[b] ;
  964.    cs = 'DOS\0' + (((long)t & 48) << 16) ;
  965.    for (i=0; i<TRACKSIZE/4; i++)
  966.       *p++ = cs + (i & 3327) ;
  967.    if (t != 40)
  968.       return ;
  969.    p = (long *)buffers[b] ;
  970.    for (i=0; i<256; i++)
  971.       *p++ = 0 ;
  972.    p = (long *)buffers[b] ;
  973.    p[0] = 2 ;
  974.    p[3] = 0x48 ;
  975.    p[78] = 1 ;
  976.    p[79] = 0x371 ;
  977.    q = (unsigned char *)(p + 108) ;
  978.    if (namebuf[0]==0) {
  979.       strcpy(q+1, "Empty") ;
  980.       *q = strlen(q+1) ;
  981.       writename(b) ;
  982.    } else {
  983.       *q++ = strlen(namebuf) ;
  984.       strcpy(q, namebuf) ;
  985.    }
  986.    p[127] = 1 ;
  987.    p += 128 ;
  988.    for (i=1; i<55; i++)
  989.       p[i] = 0xffffffff ;
  990.    p[0] = 0xc000c037 ;
  991.    p[28] = 0xffff3fff ;
  992.    p[55] = 0x3fffffff ;
  993. }
  994. /*
  995.  *   This routine recalculates the checksum for a block, and updates it in
  996.  *   word 5.  The sum of all the words in a block must be 0.
  997.  */
  998. recheck(w)
  999. register long *w ;
  1000. {
  1001.    register int i ;
  1002.    register long cs ;
  1003.  
  1004.    cs = 0 ;
  1005.    for (i=0; i<128; i++)
  1006.       cs += w[i] ;
  1007.    w[5] -= cs ;
  1008. }
  1009. /*
  1010.  *   We simply DateStamp the creation date, modification date, and
  1011.  *   rechecksum the block.
  1012.  */
  1013. updaterootblock(b)
  1014. register int b ;
  1015. {
  1016.    if (isdosdisk) {
  1017.       DateStamp(buffers[b] + 420) ;
  1018.       DateStamp(buffers[b] + 484) ;
  1019.       recheck(buffers[b]) ;
  1020.    }
  1021. }
  1022. /*
  1023.  *   If we read from track 40, we write the name on the screen in
  1024.  *   the string gadget supplied for that purpose.
  1025.  */
  1026. writename(b)
  1027. int b ;
  1028. {
  1029.    register int i = buffers[b][432] ;
  1030.    register int j ;
  1031.  
  1032.    RemoveGadget(window, &namegadg) ;
  1033.    if (isdosdisk) {
  1034.       if (i > 31)
  1035.          i = 31 ;
  1036.       for (j=0; j<i; j++)
  1037.          namebuf[j] = buffers[b][j + 433] ;
  1038.       namebuf[j] = 0 ;
  1039.    } else
  1040.       strcpy(namebuf, "(Not a DOS disk)") ;
  1041.    AddGadget(window, &namegadg, -1L) ;
  1042.    RefreshGadgets(&namegadg, window, NULL) ;
  1043. }
  1044. /*
  1045.  *   And finally, our main routine!  This thing is awfully long; they do
  1046.  *   get that way sometimes, don't they?
  1047.  */
  1048. #include "stdio.h"
  1049. main(argc, argv)
  1050. int argc ;
  1051. char *argv[] ;
  1052. {
  1053.    register int op ;
  1054.    register char *p, *q ;
  1055.    register int seen = 0 ;
  1056.    struct WBStartup *wbm;
  1057.    char *args[5] ;
  1058.  
  1059.    output = (long)Output() ;
  1060. /*
  1061.  *   First, we parse the arguments.  Arguments allowed are documented at
  1062.  *   the top of this file.  If there is a dash as the first character of
  1063.  *   an argument, we ignore it, thus allowing Unix-style options.
  1064.  */
  1065.    if (argc == 0) {
  1066.       FILE *f ;
  1067.  
  1068.       wbm = (struct WBStartup *)argv ;
  1069.       argc = wbm->sm_NumArgs ;
  1070.       argv = args ;
  1071.       for (op = 0 ; op < argc ; op++) {
  1072.          args[op] = wbm->sm_ArgList[op].wa_Name ;
  1073.      if (args[op][0] == 0)
  1074.         args[op] = "0" ;
  1075.       }
  1076.    }
  1077.    while (argc > 1) {
  1078.       argc-- ;
  1079.       argv++ ;
  1080.       p = *argv ;
  1081.       if (argc > 1)
  1082.          q = argv[1] ;
  1083.       else
  1084.          q = "" ;
  1085.       if (*p == '-')
  1086.          p++ ;
  1087.       switch (upcase(*p)) {
  1088. /*
  1089.  *   If the argument starts with a D, or a number, it specifies a drive.
  1090.  *   Actually, it might be a `DRIVE' keyword, which would return a mask of
  1091.  *   0, so we check the return value for 0.
  1092.  */
  1093. case '0' : case '1' : case '2' : case '3' : case 'D' :
  1094.          dest = getmask(p) ;
  1095.          if (dest != 0) {
  1096.             if (! seen)
  1097.                source = dest ;
  1098.             seen = 1 ;
  1099.          }
  1100.          break ;
  1101. /*
  1102.  *   The from keyword sets the source . . .
  1103.  */
  1104. case 'F' :
  1105.          argc-- ;
  1106.          argv++ ;
  1107.          source = getmask(q) ;
  1108.          seen = 1 ;
  1109.          break ;
  1110. /*
  1111.  *   The to keyword sets the destination . . .
  1112.  */
  1113. case 'T' :
  1114.          argc-- ;
  1115.          argv++ ;
  1116.          dest = getmask(q) ;
  1117.          break ;
  1118. /*
  1119.  *   This could either be a `nobuffer', `noverify', or `name' keyword.
  1120.  *   We check for any of these.
  1121.  */
  1122. case 'N' :
  1123.          if (p[1]=='o' || p[1]=='O') {
  1124.             if (p[2]=='v' || p[2]=='V')
  1125.                verify = 0 ;
  1126.             else if (p[2]=='b' || p[2]=='B')
  1127.                buffer = 0 ;
  1128.             else goto errorarg ;
  1129.          } else {
  1130.             if (strlen(q) > 30)
  1131.                q[30] = 0 ;
  1132.             strcpy(namebuf, q) ;
  1133.             source = 0 ;
  1134.             argc-- ;
  1135.             argv++ ;
  1136.          }
  1137.          break ;
  1138. /*
  1139.  *   Verify keyword is easy
  1140.  */
  1141. case 'V' :
  1142.          verify = 1 ;
  1143.          break ;
  1144. /*
  1145.  *   As is the buffer keyword.
  1146.  */
  1147. case 'B' :
  1148.          buffer = 1 ;
  1149.          break ;
  1150. /*
  1151.  *   We go ahead and print an error message if we didn't understand an
  1152.  *   option.
  1153.  */
  1154. default:
  1155. errorarg:
  1156.          if (output) {
  1157.             Write(output, "Unknown option ", 15L) ;
  1158.             Write(output, p, (long)strlen(p)) ;
  1159.             Write(output, "\n", 1L) ;
  1160.          }
  1161.       }
  1162.    }
  1163. /*
  1164.  *   Up to this point, the source has been a mask.  Now we turn it into an
  1165.  *   integer.
  1166.  */
  1167.    if (source & 8)
  1168.       source = 3 ;
  1169.    else if (source & 4)
  1170.       source = 2 ;
  1171.    else if (source & 2)
  1172.       source = 1 ;
  1173.    else if (source & 1)
  1174.       source = 0 ;
  1175.    else
  1176.       source = -1 ;
  1177. /*
  1178.  *   We initialize a few gadgets based on the parameters the user selected.
  1179.  */
  1180.    if (verify) {
  1181.       verifygadg.GadgetText = &verifyontext ;
  1182.    } else {
  1183.       verifygadg.GadgetText = &verifyofftext ;
  1184.    }
  1185.    if (buffer) {
  1186.       buffergadg.GadgetText = &bufferontext ;
  1187.       hibuf = 80 ;
  1188.    } else {
  1189.       buffergadg.GadgetText = &bufferofftext ;
  1190.       hibuf = 1 ;
  1191.    }
  1192. /*
  1193.  *   And now we try and open things up!  First intuition, then graphics,
  1194.  *   then our window and an I/O port.  If any of these fail, we simply
  1195.  *   exit.
  1196.  */
  1197.    if ((IntuitionBase = (struct IntuitionBase *)OpenLibrary(
  1198.          "intuition.library",33L))!=NULL &&
  1199.        (GfxBase = (struct GfxBase *)OpenLibrary(
  1200.          "graphics.library",0L))!=NULL &&
  1201.          (window=OpenWindow(&newwindow))!=NULL &&
  1202.        (port=CreatePort(0L, 0L)) &&
  1203.        (gpacket=(struct StandardPacket *)AllocMem(
  1204.                (long)sizeof(struct StandardPacket),MEMF_PUBLIC|MEMF_CLEAR))) {
  1205. /*
  1206.  *   We draw the lines between what the user has selected, and enter into
  1207.  *   our main command loop.  Then, we wait for a message.  After getting one,
  1208.  *   we drop into a case statement keying off what message it was.
  1209.  */
  1210.       redrawlines() ;
  1211.       getbuffers(hibuf) ;
  1212.       while (1) {
  1213.          if (havedisk)
  1214.             turnon(&againgadg) ;
  1215.          else
  1216.             turnoff(&againgadg) ;
  1217.          turnoff(&retrygadg) ;
  1218.          turnon(&gogadg) ;
  1219.          turnon(&verifygadg) ;
  1220.          turnon(&buffergadg) ;
  1221.          if (source == -1)
  1222.             turnon(&namegadg) ;
  1223.          else
  1224.             turnoff(&namegadg) ;
  1225.          RefreshGadgets(window->FirstGadget, window, NULL) ;
  1226.          while ((message = (struct IntuiMessage *)
  1227.                         GetMsg(window->UserPort))==NULL)
  1228.             WaitPort(window->UserPort) ;
  1229.          op = getop() ;
  1230.          switch(op) {
  1231. /*
  1232.  *   A shift-0, shift-1, shift-2, or shift-3 toggles the appropriate bit
  1233.  *   in the destination, and redraws the lines.  A 0, 1, 2, or 3 sets the
  1234.  *   source to that drive, and continues.
  1235.  */
  1236. case ')' :
  1237.             dest ^= 1 ;
  1238.             goto redrawem ;
  1239. case '!' :
  1240.             dest ^= 2 ;
  1241.             goto redrawem ;
  1242. case '@' :
  1243.             dest ^= 4 ;
  1244.             goto redrawem ;
  1245. case '#' :
  1246.             dest ^= 8 ;
  1247.             goto redrawem ;
  1248. case '0' : case '1' : case '2' : case '3' :
  1249.             source = op - '0' ;
  1250. redrawem :
  1251.             redrawlines() ;
  1252.             break ;
  1253. /*
  1254.  *   If verify is selected, we toggle the state of the verify flag, and
  1255.  *   update the gadget to reflect this state.
  1256.  */
  1257. case 'V' :
  1258.             verify = ! verify ;
  1259.             RemoveGadget(window, &verifygadg) ;
  1260.             if (verify) {
  1261.                verifygadg.GadgetText = &verifyontext ;
  1262.             } else {
  1263.                verifygadg.GadgetText = &verifyofftext ;
  1264.             }
  1265.             AddGadget(window, &verifygadg, -1L) ;
  1266.             RefreshGadgets(&verifygadg, window, NULL) ;
  1267.             break ;
  1268. /*
  1269.  *   The buffer option does essentially the same thing, only for buffering.
  1270.  */
  1271. case 'B' :
  1272.             buffer = ! buffer ;
  1273.             RemoveGadget(window, &buffergadg) ;
  1274.             if (buffer) {
  1275.                buffergadg.GadgetText = &bufferontext ;
  1276.                getbuffers(80) ;
  1277.             } else {
  1278.                buffergadg.GadgetText = &bufferofftext ;
  1279.                getbuffers(1) ;
  1280.             }
  1281.             havedisk = 0 ;
  1282.             AddGadget(window, &buffergadg, -1L) ;
  1283.             RefreshGadgets(&buffergadg, window, NULL) ;
  1284.             break ;
  1285. /*
  1286.  *   If the user selects `format', then we set the source appropriately and
  1287.  *   redraw the lines.  Of course, we no longer have a disk, as when the
  1288.  *   format is executed, it will destroy buffer 0.
  1289.  */
  1290. case 'F' :
  1291.             source = -1 ;
  1292.             havedisk = 0 ;
  1293.             goto redrawem ;
  1294. /*
  1295.  *   The name gadget, if invoked from the keyboard with the `n' key,
  1296.  *   Activates the string gadget.
  1297.  */
  1298. case 'N' :
  1299.             if (gadad == NULL && source == -1) {
  1300.                ActivateGadget(&namegadg, window, NULL) ;
  1301.             }
  1302.             break ;
  1303. /*
  1304.  *   The `G' gadget, or carriage return, starts us on our merry way.
  1305.  */
  1306. case 'G' : case 10 : case 32 : case 13 :
  1307.             goforit(0) ;
  1308.             break ;
  1309. /*
  1310.  *   The `A' gadget insures that we have a disk first, otherwise it
  1311.  *   complains.
  1312.  */
  1313. case 'A' :
  1314.             if (! havedisk) {
  1315.                while (error("No disk in memory")=='R') ;
  1316.             } else {
  1317.                goforit(1) ;
  1318.             }
  1319.             break ;
  1320. /*
  1321.  *   Retry is ignored in the main command loop, as there is nothing to
  1322.  *   retry!
  1323.  */
  1324. case 'R' :
  1325. /*
  1326.  *   Quit is handled at the bottom of the loop.
  1327.  */
  1328. case 'Q' :
  1329. /*
  1330.  *   As a default, we do nothing; not even an error message.
  1331.  */
  1332. default :
  1333.             break ;
  1334.          }
  1335. /*
  1336.  *   If the last gadget selected was 'Q', we exit.
  1337.  */
  1338.          if (op == 'Q')
  1339.             break ;
  1340.       }
  1341.    } else {
  1342.       error("! couldn't open window") ;
  1343.    }
  1344. /*
  1345.  *   Release memory and exit.
  1346.  */
  1347.    cleanup() ;
  1348. }
  1349. _wb_parse() {}
  1350.