/***********************************************\
*                                               *
*                     Snap                      *
*         © Mikael Karlsson 1988-1991           *
*                                               *
\***********************************************/
/* Auto: make "CCEXTRA=-wq -qf"
*/

#ifdef SNAPREXX
#include "minrexx.h"
#endif SNAPREXX
#include <stdio.h>

#define ARGVAL() ((*argv && *++(*argv)) || (--argc && *++argv))

static char Version[] = "$VER: 1.61 © 1991 Mikael Karlsson & Absolut Software\n";

BOOL Kick36;

/* signals */
LONGBITS startsignal, insertsignal, cancelsignal;
LONGBITS donesignal, movesignal, clicksignal;
LONGBITS timersignal, initsignal, cwsignal, ticksignal;
ULONG startsignum = -1L;
ULONG insertsignum = -1L;
ULONG cancelsignum = -1L;
ULONG donesignum = -1L;
ULONG movesignum = -1L;
ULONG clicksignum = -1L;
ULONG initsignum = -1L;
ULONG cwsignum = -1L;
ULONG ticksignum = -1L;
ULONG WaitSignal;

/* program */
struct SnapRsrc *SnapRsrc = NULL;
struct Task *MyTask;

/* Snap state machine */
WORD action;
WORD state;

/* clipboard */
struct IOClipReq *ClipReq = NULL;
struct MsgPort *ClipPort = NULL;

/* timer device */
struct MsgPort *TimerPort = NULL;
struct timerequest MyTR;

/* input device */
struct MsgPort *inputDevPort = NULL;
struct Interrupt handlerStuff;
struct IOStdReq *inputRequestBlock = NULL;
struct InputEvent SimEvent;
WORD textqual;
WORD gfxqual;
WORD insertkey;
WORD cwkey;
WORD modinsert;

UBYTE *CharData = NULL;
UBYTE TrueUnderscore;

/* console */
struct MsgPort *ConPort = NULL;
struct IOStdReq *ConIOR = NULL;
struct KeyMap keymap;

/* windows */
#ifdef SNAPGFX
struct MsgPort *Sharedport = NULL;
SHORT Sharedrefs;
IMPORT struct Window *ControlWindow;
struct Window *SaveWin = NULL;
IMPORT struct Gadget SaveGad;
IMPORT struct Gadget NameGad;
struct GfxSnap *SwapGS = NULL;
IMPORT struct StringInfo TranspSI;
IMPORT struct Image ActiveDiskImage;
IMPORT struct Image InactiveDiskImage;
IMPORT struct Image ActiveClipImage;
IMPORT struct Image InactiveClipImage;
#endif SNAPGFX
IMPORT UBYTE *WindowTitle;

/* libraries */
IMPORT struct ExecBase *SysBase;
struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase       *GfxBase = NULL;
struct LayersBase    *LayersBase = NULL;
struct DiskFontBase  *DiskfontBase = NULL;
struct Library       *IconBase = NULL;
#ifdef SNAPGFX
#ifdef REQLIB
struct ReqBase       *ReqBase = NULL;
#endif REQLIB
#ifdef ASLLIB
struct Library       *AslBase = NULL;
#endif ASLLIB
#endif SNAPGFX

/* graphics */
struct Screen *theScreen;
struct Layer *theLayer;
struct RastPort rp, TempRp, MyRP;
struct BitMap TempBM, MyBM;
UBYTE *TempRaster = NULL;

#ifdef SNAPGFX
#ifdef REQLIB
struct ReqFileRequester *NameFR = NULL;
#endif REQLIB
#ifdef ASLLIB
struct FileRequester *AslNameFR = NULL;
#endif ASLLIB
char SaveName[DSIZE+FCHARS+2];
BPTR SnapFile;
#endif SNAPGFX

/* ARexx stuff */
#ifdef SNAPREXX
ULONG rexxsignal;
IMPORT struct rexxCommandList rcl[];
WORD disp();
#endif SNAPREXX

#ifdef AZTEC_C
int stricmp(char *s1, char *s2 )
{
   char c1, c2;

   while (*s1 && *s2) {
      if ((c1 = *(s1++)) >= 'a' && c1 <= 'z') {
         c1 -= 32;
      }
      if ((c2 = *(s2++)) >= 'a' && c2 <= 'z') {
         c2 -= 32;
      }

      if ( c1 != c2 ) {
         return( c1 - c2 );
      }
   }

   return( *s1 - *s2 );
}


int strnicmp(char *s1, char *s2, int n)
{
   char c1, c2;

   while (*s1 && *s2 && --n > 0) {
      c1 = *(s1++);
      c2 = *(s2++);

      if (c1 >= 'a' && c1 <= 'z') {
         c1 -= 32;
      }
      if (c2 >= 'a' && c2 <= 'z') {
         c2 -= 32;
      }

      if (c1 != c2) {
         return(c1 - c2);
      }
   }

   c1 = *s1;
   c2 = *s2;

   if (c1 >= 'a' && c1 <= 'z') {
      c1 -= 32;
   }
   if (c2 >= 'a' && c2 <= 'z') {
      c2 -= 32;
   }

   return(c1 - c2);
}
#endif

LONG dectoint(REGISTER char *str)
{
    REGISTER long val = 0;
    REGISTER char c;
    while ((c = *str) >= '0' && c <= '9') {
        val = (((val<<2)+val)<<1) + c-'0';
        str++;
    }
    return(val);
}

LONG hextoint(REGISTER char *str)
{
    REGISTER long val = 0;
    REGISTER char c;

    if (!strnicmp(str, "0x", 2)) {
        str += 2;
    }
    while (c = *str) {
        val <<= 4;
        val |= (c & 15) + ((c>='0' && c<='9') ? 0 : 9);
        str++;
    }
    return(val);
}

WORD insertcount;

VOID InsertAscii(ULONG ascii)
{
    if (insertcount == 1) {   /* Time for second char */
          /* Not necessary to patch here but it guarantees
             that all inserted chars end up in the same window. */
        SafePatch();
    }
    if (AsciiToInputEvent(ascii, &SimEvent, &keymap)) {
        DoIO((struct IORequest *)inputRequestBlock);
        if (SnapRsrc->chardelay) {
            MyTR.tr_node.io_Command = TR_ADDREQUEST;
            MyTR.tr_time.tv_micro = SnapRsrc->chardelay;
            MyTR.tr_time.tv_secs = 0;
            DoIO((struct IORequest *)&MyTR);
        }
    }
    ++insertcount;
}

#ifdef SNAPGFX
VOID GadText(struct Gadget *Gad, char *Str, LONG Len)
{
    char temp[256];
    SHORT i;

    SetDrMd(ControlWindow->RPort, JAM2);
    SetAPen(ControlWindow->RPort, 0L);
    RectFill(ControlWindow->RPort, (LONG)Gad->LeftEdge, (LONG)Gad->TopEdge,
      (LONG)Gad->LeftEdge + Gad->Width - 1, (LONG)Gad->TopEdge + Gad->Height - 1);
    SetAPen(ControlWindow->RPort, 1L);
    SetBPen(ControlWindow->RPort, 0L);
    Move(ControlWindow->RPort,
      (LONG)Gad->LeftEdge + 1,
      (LONG)Gad->TopEdge + ControlWindow->RPort->Font->tf_Baseline + 1);
    if (TextLength(ControlWindow->RPort, Str, Len) > Gad->Width) {
        i = Len;
        strncpy(temp, Str, i - 3);
        strcat(temp, "...");
        while (TextLength(ControlWindow->RPort, temp, (LONG)i) > Gad->Width) {
            --i;
            temp[i] = '\0';
            temp[i-3] = '.';
        }
        Text(ControlWindow->RPort, temp, (LONG)i);
    } else {
        Text(ControlWindow->RPort, Str, Len);
    }
}

VOID SwapColorMap(struct GfxSnap *GS)
{
    struct ViewPort *vp = &GS->window->WScreen->ViewPort;
    LONG i = (GS->viewmode & HAM ? 16 : 1L << GS->depth);
    ULONG col;

    while (i-- && (col = GetRGB4(vp->ColorMap, i)) != -1L) {
        SetRGB4(vp, i,
          (LONG)GS->rgb[i][0] >> 4,
          (LONG)GS->rgb[i][1] >> 4,
          (LONG)GS->rgb[i][2] >> 4);
        GS->rgb[i][0] = ((col >> 8) & 0x0f) << 4;
        GS->rgb[i][1] = ((col >> 4) & 0x0f) << 4;
        GS->rgb[i][2] = ((col >> 0) & 0x0f) << 4;
    }
}

VOID CheckWindowMsgs()
{
    struct IntuiMessage *Msg;
    struct IntuiMessage *QdMsg = NULL;
    ULONG Class;
    USHORT Code;
    struct Window *Win;
    struct Gadget *Gad;

    while (Sharedport &&
      (QdMsg || (Msg = (struct IntuiMessage *)GetMsg(Sharedport)))) {
        if (QdMsg) {
            Msg = QdMsg;
            QdMsg = NULL;
        }
        Class = Msg->Class;
        Code  = Msg->Code;
        Win   = Msg->IDCMPWindow;
        Gad   = (struct Gadget *)Msg->IAddress;
        ReplyMsg((struct Message *)Msg);
        switch (Class) {
            case CLOSEWINDOW: {
                struct GfxSnap *GS = NULL;
                if (Win == ControlWindow) {
                    ControlWindow = NULL;
                    if (SaveWin) {
                        SetWindowTitles(SaveWin, (char *)WindowTitle, (char *)-1);
                        SaveWin = NULL;
                    }
                } else {
                    GS = (struct GfxSnap *)Win->UserData;
                    FreePlanes(&GS->BM, GS->width, GS->height);
                    if (Win == SaveWin || Sharedrefs == 1) {
                        if (ControlWindow) {
                            OffGadget(&SaveGad, ControlWindow, NULL);
                        }
                        SaveWin = NULL;
                    }
                }
                closesharedwindow(Win);
                if (GS) {
                    Kill(GS);
                }
                break;
            }
            case NEWSIZE: {
                AdjustSize((struct GfxSnap *)Win->UserData);
                break;
            }
            case MOUSEMOVE: {
                  /* Collapse all consecutively queued MOUSEMOVE msgs */
                while ((QdMsg = (struct IntuiMessage *)GetMsg(Sharedport))
                  && (QdMsg->Class == MOUSEMOVE)) {
                    ReplyMsg((struct Message *)QdMsg);
                }
                SyncGS((struct GfxSnap *)Win->UserData);
                break;
            }
            case REFRESHWINDOW: {
                BeginRefresh(Win);
                SyncGS((struct GfxSnap *)Win->UserData);
                EndRefresh(Win, 1L);
            }
            case INACTIVEWINDOW: {
                if (Win != ControlWindow) {
                    struct GfxSnap *GS;
                    GS = (struct GfxSnap *)Win->UserData;
                    if (SwapGS) {
                        SwapColorMap(SwapGS);
                        SwapGS = NULL;
                    }
                    GS->DiskGad.GadgetRender = (APTR)&InactiveDiskImage;
                    GS->ClipGad.GadgetRender = (APTR)&InactiveClipImage;
                    RefreshGList(&GS->DiskGad, GS->window, NULL, -1);
                }
                break;
            }
            case ACTIVEWINDOW: {
                if (Win != ControlWindow) {
                    struct GfxSnap *GS;
                    GS = (struct GfxSnap *)Win->UserData;
                    GS->DiskGad.GadgetRender = (APTR)&ActiveDiskImage;
                    GS->ClipGad.GadgetRender = (APTR)&ActiveClipImage;
                    RefreshGList(&GS->DiskGad, GS->window, NULL, -1);
                }
                break;
            }
            case GADGETUP: {
                switch (Gad->GadgetID) {
                    case VPROP:
                    case HPROP: {
                        SyncGS((struct GfxSnap *)Win->UserData);
                        break;
                    }
                    case SAVEGAD: {
savepic:
                        if (SaveWin) {
                            WORD success = 0;
                            struct GfxSnap *GS;
                            GS = (struct GfxSnap *)SaveWin->UserData;

                            if (SaveName[0] == '\0') {
                                DisplayBeep(NULL);
                                ActivateGadget(&NameGad, ControlWindow, NULL);
                                break;
                            }
                            SnapFile = (BPTR)Open((char *)SaveName, MODE_NEWFILE);
                            if (SnapFile) {
                                struct CBFHandle CBFH;
                                CBFH.Type = CBFFILE;
                                CBFH.Handle.File = SnapFile;
                                success = SaveGS(GS, &CBFH);
                                Close(SnapFile);
                            }
                            if (success) {
                                SetWindowTitles(ControlWindow, "Saved ok", (char *)-1);
                            } else {
                                DeleteFile((char *)SaveName);
                                DisplayBeep(NULL);
                                SetWindowTitles(ControlWindow, "Save failed", (char *)-1);
                            }
                        }
                        break;
                    }
                    case NAMEGAD: { /* Should only happen with Req */
#ifdef ASLLIB
                        if ((AslNameFR) && (Kick36)) {
                            if (RequestFile(AslNameFR)) {
                                strncpy(SaveName, AslNameFR->rf_Dir, sizeof(SaveName));
                                AddPart((UBYTE *)SaveName,
                                        (UBYTE *)AslNameFR->rf_File,
                                        sizeof(SaveName));
                            }
                            GadText(&NameGad, SaveName, (LONG)strlen(SaveName));
                        }
#else
                        if (0) {}
#endif ASLLIB
#ifdef REQLIB
                        else if (NameFR) {
                            NameFR->Title = "Save picture as...";
                            NameFR->PathName = SaveName;
                            NameFR->Window = ControlWindow;
                            (VOID)BruceFileRequester(NameFR);
                            GadText(&NameGad, SaveName, (LONG)strlen(SaveName));
                        }
#endif REQLIB
                        break;
                    }
                    case DISKGAD: {
                        struct GfxSnap *GS;
                        if (!ControlWindow && !OpenCW()) {
                            DisplayBeep(NULL);
                            break;
                        }
                        if (Win == SaveWin) {
                            goto savepic;
                        }
                        if (SaveWin) {
                            SetWindowTitles(SaveWin, (char *)WindowTitle, (char *)-1);
                            GS = (struct GfxSnap *)SaveWin->UserData;
                            RefreshGList(&GS->DiskGad, GS->window, NULL, 1L);
                        } else {
                            GadText(&SaveGad, "Save", 4L);
                            OnGadget(&SaveGad, ControlWindow, NULL);
                        }
                        SaveWin = Win;
                        SetWindowTitles(SaveWin, "Selected", (char *)-1);
                        GS = (struct GfxSnap *)SaveWin->UserData;
                        RefreshGList(&GS->DiskGad, GS->window, NULL, 1L);
                        break;
                    }
                    case CLIPGAD: {
                        struct CBFHandle CBFH;
                        struct GfxSnap *GS = (struct GfxSnap *)Win->UserData;

                        CBFH.Type = CBFCLIP;
                        CBFH.Handle.ClipReq = ClipReq;
                        if (!SaveGS(GS, &CBFH)) {
                            DisplayBeep(NULL);
                        }
                        break;
                    }
                    default: {
                        break;
                    }
                }
                break;
            }
            case MOUSEBUTTONS: {
                if (Win != ControlWindow) {
                    if (Code == SELECTDOWN && !SwapGS) {
                        SwapGS = (struct GfxSnap *)Win->UserData;
                        SwapColorMap(SwapGS);
                    } else if (Code == SELECTUP && SwapGS) {
                        SwapColorMap(SwapGS);
                        SwapGS = NULL;
                    }
                }
                break;
            }
            default: {
                break;
            }
        }
    }
    if (QdMsg) {
        ReplyMsg((struct Message *)QdMsg);
    }
}
#endif SNAPGFX

#define WriteStr(str) fputs(str, stdout)
#define WriteChar(char) fputc(char, stdout)

struct QualPair {
    char *str;
    WORD val;
};

struct QualPair Qualifiers[] = {
    { "LSHIFT",    IEQUALIFIER_LSHIFT },
    { "RSHIFT",    IEQUALIFIER_RSHIFT },
    { "CONTROL",   IEQUALIFIER_CONTROL },
    { "LALT",      IEQUALIFIER_LALT },
    { "RALT",      IEQUALIFIER_RALT },
    { "LCOMMAND",  IEQUALIFIER_LCOMMAND },
    { "RCOMMAND",  IEQUALIFIER_RCOMMAND },
    { "MIDBUTTON", IEQUALIFIER_MIDBUTTON }
};

WORD ParseQual(char *quals)
{
    char *temp;
    WORD i;
    char oldc;
    WORD qual = 0;

    if (('0' >= *quals) && (*quals <= '9')) {
        return hextoint(quals);
    }

    while (*quals) {
        temp = quals;
        while ((oldc = *quals) && oldc != '+') {
            quals++;
        }
        *quals = 0;
        for (i = 0; i < (sizeof(Qualifiers) / sizeof(Qualifiers[0])); i++) {
            if (!stricmp(temp, Qualifiers[i].str)) {
                qual |= Qualifiers[i].val;
                break;
            }
        }
        if (*quals = oldc) {
            quals++;
        }
    }
    return qual;
}

WORD SetAltFont(char *str)
{
    struct TextAttr ta;
    struct TextFont *NewFont;
    char temp[33];
    WORD pos = 0;

    if (strlen(str) <= 32) {

        strcpy(temp, str);
        ta.ta_Name = (UBYTE *)temp;
        while (temp[pos] != '\0' && temp[pos] != '/') {
            ++pos;
        }
        if (temp[pos] == '\0') {     /* No size */
            ta.ta_YSize = 8;
        } else {
            temp[pos] = '\0';
            ta.ta_YSize = dectoint(&temp[pos + 1]);
        }
        if (pos > 5) {
            if (stricmp(&temp[pos - 5], ".font")) {
                strcpy(&temp[pos], ".font");
            }
        } else {
            strcpy(&temp[pos], ".font");
        }
        ta.ta_Style = 0;
        ta.ta_Flags = 0;
        NewFont = SmartOpenFont(&ta);
        if (NewFont) {
            if (SnapRsrc->AlternateFont) {
                CacheSync(SnapRsrc->AlternateFont);
                CloseFont(SnapRsrc->AlternateFont);
            }
            SnapRsrc->AlternateFont = NewFont;
        }
    }
    return (NewFont ? 1 : 0);
}

struct SnapRsrc DefaultRsrc = {
    { NULL, NULL, NT_RESOURCE, 0, SNAPRSRC },   /* Node */
    NULL,                                       /* Task */
    52,                                         /* Priority */
    IEQUALIFIER_RCOMMAND,                       /* Graphics qualifier */
    IEQUALIFIER_LCOMMAND,                       /* Text qualifier */
    0x17,                                       /* Insert key */
    0x11,                                       /* Control Window key */
    { '>', ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },  /* Prepend */
    {   0,   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },  /* Append */
    TRUEUNDERSCORE,                             /* Flags */
    0,                                          /* Char delay */
    0,                                          /* Line delay */
    0x7777,                                     /* Crawl pattern */
    UNIT_FRAME,                                 /* Starting unit */
    1,                                          /* Frame mask */
    '?',                                        /* Bad char */
    10,                                         /* Cache size */
    1,                                          /* Extra line spacing */
    { NULL, NULL, NULL },                       /* MinList, Cached windows */
    NULL                                        /* Alternate font */
};

#ifdef LATTICE
VOID main(int argc, char **argv)
#else
WORD main(int argc, char **argv)
#endif
{
    WORD create = 0, usage = 0;

#ifdef AZTEC_C
    Enable_Abort = 0;
#endif AZTEC_C

    Kick36 = (SysBase->LibNode.lib_Version >= 36 ? 1 : 0);

    if (!(SnapRsrc = (struct SnapRsrc *)OpenResource(SNAPRSRC))) {
        create = 1;
        SnapRsrc = Create(SnapRsrc);
        CopyMem((char *)&DefaultRsrc, (char *)SnapRsrc, sizeof(struct SnapRsrc));
        SnapRsrc->Task = FindTask(NULL);
        NewList((struct List *)&SnapRsrc->CachedWindows);
        AddResource((struct MiscResource *)SnapRsrc);
    }

      /* Open libraries since we might need them when parsing arguments */
    if (!OpenLibs()) {
        goto exitpoint;
    }

    if (!argc) {    /* WB Startup */
        if (!create) {     /* Second time from WB -- Remove Snap */
            Signal(SnapRsrc->Task, SIGBREAKF_CTRL_C);
            goto exitpoint;
        } else if (IconBase) {   /* Is it possible to be started from Workbench
                                    without icon.library being available. */
            BPTR olddir;
            struct WBStartup *wbstartup;
            struct DiskObject *diskobject;
            char *val;

            wbstartup = (struct WBStartup *)argv;
            olddir = CurrentDir(wbstartup->sm_ArgList[0].wa_Lock);
            diskobject = GetDiskObject(wbstartup->sm_ArgList[0].wa_Name);
            if (!diskobject) {
                goto skipargs;
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "PRIORITY")) {
                WORD pri = dectoint(val);
                if (pri>50 && pri<128) {
                    SnapRsrc->Priority = pri;
                }
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "TEXTQUAL")) {
                if (!(SnapRsrc->textqual = ParseQual(val))) {
                    SnapRsrc->textqual = IEQUALIFIER_LCOMMAND;
                }
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "GFXQUAL")) {
                if (!(SnapRsrc->gfxqual = ParseQual(val))) {
                    SnapRsrc->gfxqual = IEQUALIFIER_RCOMMAND;
                }
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "INSERTKEY")) {
                SnapRsrc->insertkey = hextoint(val);
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "CWKEY")) {
                SnapRsrc->cwkey = hextoint(val);
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "APPEND")) {
                strncpy(&SnapRsrc->Append[0], val, 16);
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "PREPEND")) {
                strncpy(&SnapRsrc->Prepend[0], val, 16);
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "CHARDELAY")) {
                SnapRsrc->chardelay = dectoint(val);
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "LINEDELAY")) {
                SnapRsrc->linedelay = dectoint(val) * 1000;
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "CRAWLPTRN")) {
                SnapRsrc->CrawlPtrn = hextoint(val);
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "XEROX")) {
                SnapRsrc->flags |= XEROX;
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "NOXEROX")) {
                SnapRsrc->flags &= ~XEROX;
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "EARLYPATCH")) {
                SnapRsrc->flags |= EARLYPATCH;
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "NOEARLYPATCH")) {
                SnapRsrc->flags &= ~EARLYPATCH;
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "STARTUNIT")) {
                if (*val == '1') {
                    SnapRsrc->StartUnit = UNIT_CHAR;
                } else {
                    SnapRsrc->StartUnit = UNIT_FRAME;
                }
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "TRUEUNDERSCORE")) {
                SnapRsrc->flags |= TRUEUNDERSCORE;
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "FAKEUNDERSCORE")) {
                SnapRsrc->flags &= ~TRUEUNDERSCORE;
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "JOINLONG")) {
                SnapRsrc->flags |= JOINLONG;
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "NOJOINLONG")) {
                SnapRsrc->flags &= ~JOINLONG;
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "SIMPLEREFRESH")) {
                SnapRsrc->flags |= SIMPLEREFRESH;
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "SMARTREFRESH")) {
                SnapRsrc->flags &= ~SIMPLEREFRESH;
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "PLANEMASK")) {
                SnapRsrc->FrameMask = hextoint(val);
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "CACHESIZE")) {
                SnapRsrc->CacheSize = dectoint(val);
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "LEADING")) {
                SnapRsrc->Leading = dectoint(val);
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "BADCHAR")) {
                SnapRsrc->BadChar = (*val ? *val : 0);
            }
            if (val = FindToolType(diskobject->do_ToolTypes, "ALTFONT")) {
                SetAltFont(val);
            }
            FreeDiskObject(diskobject);
            goto skipargs;
        }
    }

    if (create) {
        WriteStr("Snap");
        WriteStr(&Version[5]);
    }

    for (argc--, argv++; argc > 0; argc--, argv++) {
        if (**argv == '-') { /* Argument coming up */
            switch(*++(*argv)) {
                case 'p': priority: {  /* Priority */
                    if (ARGVAL()) {
                        WORD pri = dectoint(*argv);
                        if (pri>50 && pri<128) {
                            SnapRsrc->Priority = pri;
                        }
                    } else {
                        usage = 1;
                    }
                    break;
                }
                case 't': textqual: {
                    if (ARGVAL()) {
                        if (!(SnapRsrc->textqual = ParseQual(*argv))) {
                            SnapRsrc->textqual = IEQUALIFIER_LCOMMAND;
                        }
                    } else {
                        usage = 1;
                    }
                    break;
                }
#ifdef SNAPGFX
                case 'g': gfxqual: {
                    if (ARGVAL()) {
                        if (!(SnapRsrc->gfxqual = ParseQual(*argv))) {
                            SnapRsrc->gfxqual = IEQUALIFIER_RCOMMAND;
                        }
                    } else {
                        usage = 1;
                    }
                    break;
                }
#endif SNAPGFX
                case 'i': insertkey: {
                    if (ARGVAL()) {
                        SnapRsrc->insertkey = hextoint(*argv);
                    } else {
                        usage = 1;
                    }
                    break;
                }
#ifdef SNAPGFX
                case 'w': cwkey: {
                    if (ARGVAL()) {
                        SnapRsrc->cwkey = hextoint(*argv);
                    } else {
                        usage = 1;
                    }
                    break;
                }
#endif SNAPGFX
                case 'c': chardelay: {
                    if (ARGVAL()) {
                        SnapRsrc->chardelay = dectoint(*argv) * 1000;
                    } else {
                        usage = 1;
                    }
                    break;
                }
                case 'l': linedelay: {
                    if (ARGVAL()) {
                        SnapRsrc->linedelay = dectoint(*argv) * 1000;
                    } else {
                        usage = 1;
                    }
                    break;
                }
                case 'a': crawlptrn: {
                    if (ARGVAL()) {
                        SnapRsrc->CrawlPtrn = hextoint(*argv);
                    } else {
                        usage = 1;
                    }
                    break;
                }
                case 'X': noxerox: {
                    SnapRsrc->flags &= ~XEROX;
                    break;
                }
                case 'x': xerox: {
                    SnapRsrc->flags |= XEROX;
                    break;
                }
                case 'E': noearlypatch: {
                    SnapRsrc->flags &= ~EARLYPATCH;
                    break;
                }
                case 'e': earlypatch: {
                    SnapRsrc->flags |= EARLYPATCH;
                    break;
                }
                case 'R': fakeunderscore: {
                    SnapRsrc->flags &= ~TRUEUNDERSCORE;
                    break;
                }
                case 'r': realunderscore: {
                    SnapRsrc->flags |= TRUEUNDERSCORE;
                    break;
                }
                case 'J': nojoinlong: {
                    SnapRsrc->flags &= ~JOINLONG;
                    break;
                }
                case 'j': joinlong: {
                    SnapRsrc->flags |= JOINLONG;
                    break;
                }
                case 'A': append: {
                    char *dest = &SnapRsrc->Append[0];
                    if ((*argv && *++(*argv)) || (--argc && ++argv)) { /* "" is ok */
                        char *src = *argv;
                        WORD i = 16;
                        while (*src && i--) {
                            *dest++ = *src++;
                        }
                        *dest = '\0';
                    } else {
                        usage = 1;
                    }
                    break;
                }
                case 'P': prepend: {
                    char *dest = &SnapRsrc->Prepend[0];
                    if ((*argv && *++(*argv)) || (--argc && ++argv)) { /* "" is ok */
                        char *src = *argv;
                        WORD i = 16;
                        while (*src && i--) {
                            *dest++ = *src++;
                        }
                        *dest = '\0';
                    } else {
                        usage = 1;
                    }
                    break;
                }
                case 'u': startunit: {
                    if (ARGVAL()) {
                        if (**argv == '1') {
                            SnapRsrc->StartUnit = UNIT_CHAR;
                        } else {
                            SnapRsrc->StartUnit = UNIT_FRAME;
                        }
                    } else {
                        usage = 1;
                    }
                    break;
                }
                case 'b': planemask: {
                    if (ARGVAL()) {
                        SnapRsrc->FrameMask = hextoint(*argv);
                    } else {
                        usage = 1;
                    }
                    break;
                }
#ifdef SNAPGFX
                case 's': smartrefresh: {
                    SnapRsrc->flags &= ~SIMPLEREFRESH;
                    break;
                }
                case 'S': simplerefresh: {
                    SnapRsrc->flags |= SIMPLEREFRESH;
                    break;
                }
#endif SNAPGFX
                case 'C': cachesize: {
                    if (ARGVAL()) {
                        SnapRsrc->CacheSize = dectoint(*argv);
                    } else {
                        usage = 1;
                    }
                    break;
                }
                case 'L': leading: {
                    if (ARGVAL()) {
                        SnapRsrc->Leading = dectoint(*argv);
                    } else {
                        usage = 1;
                    }
                    break;
                }
                case 'B': badchar: {
                    if (ARGVAL()) {
                        SnapRsrc->BadChar = dectoint(*argv);
                    } else {
                        usage = 1;
                    }
                    break;
                }
                case 'F': altfont: {
                    if (ARGVAL()) {
                        SetAltFont(*argv);
                    } else {
                        usage = 1;
                    }
                    break;
                }
                case 'Q':
                case 'q': quit: {
                    if (create) {
                        goto close;
                    } else {
                        Signal(SnapRsrc->Task, SIGBREAKF_CTRL_C);
                        goto exitpoint;
                    }
                }
                case '?': {
                    usage = 1;
                    break;
                }
                default: {
                    WriteStr("Bad option: -");
                    WriteChar((int)**argv);
                    WriteStr(".\n");
                    usage = 1;
                    break;
                }
            }
        } else {
#ifdef SNAPGFX
            char *keyword = *argv;
            *argv = NULL;
            if (!stricmp(keyword, "PRIORITY")) {
                goto priority;                      /* Terrible, ain't it? */
            } else if (!stricmp(keyword, "TEXTQUAL")) {
                goto textqual;
            } else if (!stricmp(keyword, "GFXQUAL")) {
                goto gfxqual;
            } else if (!stricmp(keyword, "INSERTKEY")) {
                goto insertkey;
            } else if (!stricmp(keyword, "CWKEY")) {
                goto cwkey;
            } else if (!stricmp(keyword, "PREPEND")) {
                goto prepend;
            } else if (!stricmp(keyword, "APPEND")) {
                goto append;
            } else if (!stricmp(keyword, "CHARDELAY")) {
                goto chardelay;
            } else if (!stricmp(keyword, "LINEDELAY")) {
                goto linedelay;
            } else if (!stricmp(keyword, "CRAWLPTRN")) {
                goto crawlptrn;
            } else if (!stricmp(keyword, "XEROX")) {
                goto xerox;
            } else if (!stricmp(keyword, "NOXEROX")) {
                goto noxerox;
            } else if (!stricmp(keyword, "EARLYPATCH")) {
                goto earlypatch;
            } else if (!stricmp(keyword, "NOEARLYPATCH")) {
                goto noearlypatch;
            } else if (!stricmp(keyword, "TRUEUNDERSCORE")) {
                goto realunderscore;
            } else if (!stricmp(keyword, "FAKEUNDERSCORE")) {
                goto fakeunderscore;
            } else if (!stricmp(keyword, "JOINLONG")) {
                goto joinlong;
            } else if (!stricmp(keyword, "NOJOINLONG")) {
                goto nojoinlong;
            } else if (!stricmp(keyword, "SIMPLEREFRESH")) {
                goto simplerefresh;
            } else if (!stricmp(keyword, "SMARTREFRESH")) {
                goto smartrefresh;
            } else if (!stricmp(keyword, "STARTUNIT")) {
                goto startunit;
            } else if (!stricmp(keyword, "PLANEMASK")) {
                goto planemask;
            } else if (!stricmp(keyword, "CACHESIZE")) {
                goto cachesize;
            } else if (!stricmp(keyword, "LEADING")) {
                goto leading;
            } else if (!stricmp(keyword, "BADCHAR")) {
                goto badchar;
            } else if (!stricmp(keyword, "ALTFONT")) {
                goto altfont;
            } else if (!stricmp(keyword, "QUIT")) {
                goto quit;
            } else if (stricmp(keyword, "?")) {
                WriteStr("Bad switch/keyword: ");
                WriteStr(keyword);
                WriteStr(".\n");
            }
#endif SNAPGFX
            usage = 1;
        }
    }

    if (usage) {
        WriteStr("Usage:\n");
#ifdef SNAPGFX
        WriteStr(" snap -pNN -tQQ -gQQ -iXX -wXX -Pstr -Astr -cNN -lNN\n");
        WriteStr("   -aXXXX -x -X -e -E -uN -r -R -j -J -s -S -bXX -CNN\n");
        WriteStr("   -LNN -BNN -Ffont -Q\n");
        WriteStr(" or\n");
        WriteStr(" snap PRIORITY/K TEXTQUAL/K GFXQUAL/K INSERTKEY/K CWKEY/K\n");
        WriteStr("   PREPEND/K APPEND/K CHARDELAY/K LINEDELAY/K CRAWLPTRN/K\n");
        WriteStr("   XEROX/S NOXEROX/S EARLYPATCH/S NOEARLYPATCH/S STARTUNIT/K\n");
        WriteStr("   TRUEUNDERSCORE/S FAKEUNDERSCORE/S JOINLONG/S NOJOINLONG/S\n");
        WriteStr("   SIMPLEREFRESH/S SMARTREFRESH/S PLANEMASK/K CACHESIZE/K\n");
        WriteStr("   LEADING/K BADCHAR/K ALTFONT/K QUIT/S\n");
#else
        WriteStr(" snap -pNN -tQQ -iXX -Pstr -Astr -cNN -lNN\n");
        WriteStr("   -aXXXX -x -X -e -E -uN -r -R -j -J -bXX -CNN\n");
        WriteStr("   -LNN -BNN -Ffont -Q\n");
#endif SNAPGFX
        WriteStr("\n");
        WriteStr("S-Mail: Mikael Karlsson     | E-Mail: micke@slaka.sirius.se\n");
        WriteStr("        Lövsättersvägen 10  |         micke@slaka.UUCP\n");
        WriteStr("        S-585 98  LINKÖPING |         {mxvax|seismo}!sunic!liuida!slaka!micke\n");
        WriteStr("        Sweden              | Phone:  +46 (0)13 50479\n");
        goto exitpoint;
    }

skipargs:
    freopen("NIL:", "w", stdout);

    if (!create) {
          /* Tell him there are new settings available */
        Signal(SnapRsrc->Task, SIGBREAKF_CTRL_F);
        goto exitpoint;
    }

    if (!OpenStuff()) {
        goto close;
    }

    textqual = SnapRsrc->textqual;
    gfxqual = SnapRsrc->gfxqual;
    insertkey = SnapRsrc->insertkey;
    cwkey = SnapRsrc->cwkey;
    TrueUnderscore = SnapRsrc->flags & TRUEUNDERSCORE ? 1 : 0;
#ifdef SNAPGFX
    SaveName[0] = '\0';
#endif

#ifdef SNAPREXX
    rexxsignal = upRexxPort("SNAP", &rcl, NULL, &disp);
#endif SNAPREXX

    /* This is what we're waiting for */
    WaitSignal = startsignal | insertsignal | initsignal | cancelsignal |
#ifdef SNAPREXX
                 rexxsignal |
#endif SNAPREXX
                 cwsignal | SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_F;

    FOREVER {
        REGISTER LONGBITS sig;

        sig = Wait(WaitSignal | timersignal
#ifdef SNAPGFX
            | (Sharedport ? (1L << Sharedport->mp_SigBit) : 0L)
#endif SNAPGFX
            );

#ifdef SNAPGFX
        CheckWindowMsgs();
#endif SNAPGFX

#ifdef SNAPREXX
        if (sig & rexxsignal) {
            dispRexxPort();
        }
#endif SNAPREXX
        if (sig & SIGBREAKF_CTRL_C) {
            /* This is my cue. Exit if there are no open windows depending on us */
#ifdef SNAPGFX
            if (Sharedrefs) {
                DisplayBeep(NULL);
            } else
#endif SNAPGFX
            {
                goto close;
            }
        }
        if (sig & SIGBREAKF_CTRL_F) {
            /* Hey, seems like there are new settings available. */
            textqual = SnapRsrc->textqual;
            gfxqual = SnapRsrc->gfxqual;
            insertkey = SnapRsrc->insertkey;
            cwkey = SnapRsrc->cwkey;
            TrueUnderscore = SnapRsrc->flags & TRUEUNDERSCORE ? 1 : 0;
        }
        if (sig & initsignal) {
            if (SnapRsrc->flags & (XEROX | EARLYPATCH)) {
                SafePatch();               /* Patch dangerous functions */
            }
        }
        if (sig & cancelsignal) {
            SafeRestore();
        }
        if (sig & startsignal) { /* The handler wants a word in. */
            SafePatch();
#ifdef SNAPGFX
            if (action == snapgfx) {       /* Check user action */
                HandleGfx();               /* Get the picture :-) */
            } else
#endif SNAPGFX
            if (action == snaptext && HandleChars()) {  /* Snap some chars */
                if (SnapRsrc->flags & XEROX) {
                    sig |= insertsignal;
                }
            } else {
                  /* Previous snap wasn't finished when this one started
                     or this snap failed. */
                SetSignal(0L, movesignal | cancelsignal |
                  donesignal | clicksignal | ticksignal);
                DisplayBeep(NULL);
                action = noaction;
            }
            if (!(sig & insertsignal)) {
                SafeRestore();             /* Layers unlocked - all safe */
            }
        }

        if (sig & insertsignal) {
            LONG i;
            struct Snap *Snap;
            ULONG ascii;

            action = insert;
            if (Snap = FetchClip()) {  /* Get clipboard data */
                  /* get the current keymap  */
                ConIOR->io_Command =  CD_ASKDEFAULTKEYMAP;
                ConIOR->io_Length  = sizeof (struct KeyMap);
                ConIOR->io_Data    = (APTR) &keymap;
                ConIOR->io_Flags   = 1;    /* no IOQuick   */
                DoIO((struct IORequest *)ConIOR);
                  /* Set up an input request */
                inputRequestBlock->io_Command = IND_WRITEEVENT;
                inputRequestBlock->io_Flags   = 0L;
                inputRequestBlock->io_Length  = (long)sizeof(struct InputEvent);
                inputRequestBlock->io_Data    = (APTR)&SimEvent;
                  /* Translate chars in SnapSpace and insert them
                     into the input stream. */
                insertcount = 0;
                ascii = 13;  /* Simulate start of new line */
                for (i = 0; Snap->Chars[i] && (action == insert); ++i) {
                    if (ascii == 13 && modinsert) {
                        int cnt = 0;
                        while (ascii = SnapRsrc->Prepend[cnt++]) {
                            InsertAscii(ascii);
                        }
                    }

                    ascii = Snap->Chars[i];
                    if (ascii == 10) {
                        if (modinsert) {
                            int cnt = 0;
                            while (ascii = SnapRsrc->Append[cnt++]) {
                                InsertAscii(ascii);
                            }
                        }
                        ascii = 13;     /* WYSIWYG? Hah! */
                    }
                    InsertAscii(ascii);
                    if (ascii == 13 && SnapRsrc->linedelay) {
                        MyTR.tr_node.io_Command = TR_ADDREQUEST;
                        MyTR.tr_time.tv_micro = SnapRsrc->linedelay;
                        MyTR.tr_time.tv_secs = 0;
                        DoIO((struct IORequest *)&MyTR);
                    }
                }
                if (modinsert) {
                    int cnt = 0;
                    while (ascii = SnapRsrc->Append[cnt++]) {
                        InsertAscii(ascii);
                    }
                }
                SafeRestore();  /* "Depatch" */
                  /* Free memory given to us by FetchClip() */
                FreeMem(Snap, Snap->Size);
            }
            action = noaction;
            modinsert = 0;
        }

#ifdef SNAPGFX
        if (sig & cwsignal) {
            if (!ControlWindow && !OpenCW()) {
                DisplayBeep(NULL);
            }
        }
#endif SNAPGFX
    }

close:
#ifdef SNAPREXX
    dnRexxPort();
#endif SNAPREXX
    CloseStuff();  /* Guess what */
exitpoint:
    CloseLibs();
#ifdef AZTEC_C
    return 0;
#endif
}
