#define INCL_DOS
#define INCL_WIN

#include <os2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <share.h>
#include "fm3dll.h"
#include "fm3dlg.h"
#include "fm3str.h"

#pragma data_seg(DATA1)

typedef struct {
  CHAR  mask[CCHMAXPATH];
  CHAR  cl[1001];
  CHAR  sig[CCHMAXPATH];
  LONG  offset;
  ULONG flags;
} ASSOC;

typedef struct LINKASSOC {
  CHAR             *mask;
  CHAR             *cl;
  CHAR             *sig;
  LONG              offset;
  ULONG             flags;
  struct LINKASSOC *prev;
  struct LINKASSOC *next;
} LINKASSOC;

static LINKASSOC *asshead     = NULL,*asstail = NULL;
static BOOL       assloaded = FALSE;

#pragma alloc_text(ASSOC2,free_commands,load_associations,save_associations)
#pragma alloc_text(ASSOC2,ExecAssociation,AssocTextProc)


MRESULT EXPENTRY AssocTextProc (HWND hwnd,ULONG msg,MPARAM mp1,MPARAM mp2) {

  PFNWP       oldproc = (PFNWP)WinQueryWindowPtr(hwnd,0);
  static BOOL emphasized = FALSE;

  switch(msg) {
    case DM_DRAGOVER:
      if(!emphasized) {
        emphasized = TRUE;
        DrawTargetEmphasis(hwnd,emphasized);
      }
      if(AcceptOneDrop(mp1,mp2))
        return MRFROM2SHORT(DOR_DROP,
                            DO_MOVE);
      return MRFROM2SHORT(DOR_NEVERDROP,0);

    case DM_DRAGLEAVE:
      if(emphasized) {
        emphasized = FALSE;
        DrawTargetEmphasis(hwnd,emphasized);
      }
      break;

    case DM_DROPHELP:
      DropHelp(mp1,mp2,hwnd,GetPString(IDS_ASSOCDROPHELPTEXT));
      return 0;

    case DM_DROP:
      {
        char szFrom[CCHMAXPATH + 5];

        if(emphasized) {
          emphasized = FALSE;
          DrawTargetEmphasis(hwnd,emphasized);
        }
        if(GetOneDrop(mp1,mp2,szFrom,CCHMAXPATH)) {
          strcat(szFrom," %a");
          WinSetWindowText(hwnd,szFrom);
        }
      }
      return 0;
  }
  return (oldproc) ? oldproc(hwnd,msg,mp1,mp2) :
                     WinDefWindowProc(hwnd,msg,mp1,mp2);
}


VOID free_associations (VOID) {

  LINKASSOC *info,*next;

  info = asshead;
  while(info) {
    next = info->next;
    free(info->mask);
    free(info->cl);
    if(info->sig)
      free(info->sig);
    free(info);
    info = next;
  }
  asshead = asstail = NULL;
}


VOID load_associations (VOID) {

  FILE      *fp;
  LINKASSOC *info;
  CHAR       cl[1024];
  CHAR       mask[CCHMAXPATH + 24];
  CHAR       sig[CCHMAXPATH + 24];
  CHAR       offset[72];
  CHAR       flags[72];

  if(asshead)
    free_associations();
  assloaded = TRUE;
  save_dir2(mask);
  if(mask[strlen(mask) - 1] != '\\')
    strcat(mask,"\\");
  strcat(mask,"ASSOC.DAT");
  fp = _fsopen(mask,"r",SH_DENYWR);
  if(fp) {
    while(!feof(fp)) {
      if(!fgets(mask,CCHMAXPATH + 24,fp))
        break;
      mask[CCHMAXPATH] = 0;
      stripcr(mask);
      lstrip(rstrip(mask));
      if(!*mask || *mask == ';')
        continue;
      if(!fgets(cl,1024,fp) ||
         !fgets(sig,CCHMAXPATH + 24,fp) ||
         !fgets(offset,72,fp) ||
         !fgets(flags,72,fp))
        break;                       /* error! */
      cl[1000] = 0;
      sig[CCHMAXPATH] = 0;
      offset[34] = 0;
      flags[34] = 0;
      stripcr(cl);
      stripcr(sig);
      stripcr(offset);
      stripcr(flags);
      lstrip(rstrip(cl));
      lstrip(rstrip(sig));
      lstrip(rstrip(offset));
      lstrip(rstrip(flags));
      if(!*cl)
        continue;
      info = malloc(sizeof(LINKASSOC));
      if(info) {
        memset(info,0,sizeof(LINKASSOC));
        info->cl = strdup(cl);
        info->mask = strdup(mask);
        if(*sig)
          info->sig = strdup(sig);
        info->offset = atol(offset);
        info->flags = atol(flags);
        if(!info->cl || !info->mask) {
          if(info->cl)
            free(info->cl);
          if(info->mask)
            free(info->mask);
          free(info);
          break;
        }
        if(!asshead)
          asshead = info;
        else {
          asstail->next = info;
          info->prev = asstail;
        }
        asstail = info;
      }
    }
    fclose(fp);
  }
}


VOID save_associations (VOID) {

  LINKASSOC *info;
  FILE      *fp;
  CHAR       s[CCHMAXPATH + 14];

  if(!assloaded || !asshead)
    return;
  info = asshead;
#ifdef NEVER
  while(info) {
    next = info->next;
    if(!strcmp("*",info->mask)) {
      if(info != asshead) {                 /* already top record */
        if(info->prev)
          (info->prev)->next = info->next;
        if(info->next)
          (info->next)->prev = info->prev;
        if(info == asstail)
          asstail = info->prev;
        info->next = asshead->next;
        info->prev = NULL;
        asshead = info;
      }
    }
    info = next;
  }
#endif
  save_dir2(s);
  if(s[strlen(s) - 1] != '\\')
    strcat(s,"\\");
  strcat(s,"ASSOC.DAT");
  fp = fopen(s,"w");
  if(fp) {
    fputs(GetPString(IDS_ASSOCFILETEXT),fp);
    info = asshead;
    while(info) {
      fprintf(fp,
              ";\n%0.*s\n%0.1000s\n%0.*s\n%lu\n%lu\n",
              CCHMAXPATH,
              info->mask,
              info->cl,
              CCHMAXPATH,
              (info->sig) ? info->sig : NullStr,
              info->offset,
              info->flags);
      info = info->next;
    }
    fclose(fp);
  }
}


#pragma alloc_text(ASSOC,add_association,kill_association,AssocDlgProc,EditAssociations)


LINKASSOC * add_association (ASSOC *addme) {

  LINKASSOC *info;

  if(addme && *addme->cl && *addme->mask) {
    info = asshead;
    while(info) {
      if(!stricmp(info->mask,addme->mask) &&
         ((!info->sig && !*addme->sig) ||
          (info->sig && !strcmp(addme->sig,info->sig))))
        return NULL;
      info = info->next;
    }
    if(!info) {
      info = malloc(sizeof(LINKASSOC));
      if(info) {
        memset(info,0,sizeof(LINKASSOC));
        info->cl = strdup(addme->cl);
        info->mask = strdup(addme->mask);
        if(*addme->sig)
          info->sig = strdup(addme->sig);
        if(addme->offset)
          info->offset = addme->offset;
        if(addme->flags)
          info->flags = addme->flags;
        if(!info->cl || !info->mask) {
          if(info->cl)
            free(info->cl);
          if(info->mask)
            free(info->mask);
          free(info);
        }
        else {
          if(!asshead)                         /* only item in list */
            asshead = asstail = info;
          else {
            if(asstail) {                      /* place at tail */
              asstail->next = info;
              info->prev = asstail;
            }
            asstail = info;
          }
          return info;
        }
      }
    }
  }
  return NULL;
}


BOOL kill_association (ASSOC *killme) {

  LINKASSOC *info;

  if(killme && *killme->mask) {
    info = asshead;
    while(info) {
      if(!stricmp(info->mask,killme->mask) &&
         info->offset == killme->offset &&
         (((!info->sig || !*info->sig) && !*killme->sig) ||
          (info->sig && !strcmp(killme->sig,info->sig)))) {
        if(info == asshead) {
          asshead = info->next;
          if(info == asstail)
            asstail = info->prev;
        }
        else {
          if(info->next)
            (info->next)->prev = info->prev;
          if(info->prev)
            (info->prev)->next = info->next;
          if(info == asstail)
            asstail = info->prev;
        }
        free(info->cl);
        free(info->mask);
        if(info->sig)
          free(info->sig);
        free(info);
        return TRUE;
      }
      info = info->next;
    }
  }
  return FALSE;
}


INT ExecAssociation (HWND hwnd, CHAR *datafile) {

  CHAR      *file,sig[CCHMAXPATH],sigl[CCHMAXPATH],mask[CCHMAXPATH],*p;
  FILE      *fp;
  BOOL       didmatch,exclude;
  ULONG      offset;
  LINKASSOC *info;

  if(!assloaded)
    load_associations();
  if(!asshead)
    return -1;
  if(!datafile ||
     !*datafile)
    return -1;
  file = strrchr(datafile,'\\');
  if(!file)
    file = strrchr(datafile,':');
  if(file)
    file++;
  else
    file = datafile;
  info = asshead;
  while(info) {
    strcpy(mask,
           info->mask);
    p = strtok(mask,";");
    while(p) {
      if(*p == '/') {
        p++;
        exclude = TRUE;
      }
      else
        exclude = FALSE;
      didmatch = wildcard((strchr(p,'\\') ||
                           strchr(p,':')) ?
                           datafile :
                           file,
                          p,
                          FALSE);
      if(exclude &&
         didmatch)
        didmatch = FALSE;
      if(didmatch) {
        if(info->sig &&
           *info->sig) {
          strcpy(sigl,
                 info->sig);
          literal(sigl);
          fp = _fsopen(datafile,
                       "rb",
                       SH_DENYNO);
          if(fp) {
            if(info->offset < 0L) {
              fseek(fp,
                    0L,
                    SEEK_END);
              offset = ftell(fp) + info->offset;
            }
            else
              offset = info->offset;
            fseek(fp,
                  offset,
                  SEEK_SET);
            if(fread(sig,
                     1,
                     strlen(sigl),
                     fp) !=
               strlen(sigl) ||
               strncmp(sigl,
                       sig,
                       strlen(sigl)))
              didmatch = FALSE;
            fclose(fp);
          }
        }
      }
      if(didmatch) {   /* got a match; do it... */

        CHAR *list[2];
        INT   flags,rc;
        BOOL  dieafter = FALSE;

        if(fAmAV2) {
          if(stristr(info->cl,"AV2.EXE") ||
             stristr(info->cl,"AV2.CMD") ||
             stristr(info->cl,"<>"))
            return -1;
        }
        if(!strcmp(info->cl,"<>")) {
          OpenObject(datafile,
                     Default,
                     hwnd);
          return 0;
        }
        list[0] = datafile;
        list[1] = NULL;
        flags = info->flags;
        if(!(flags & FULLSCREEN))
          flags |= WINDOWED;
        if(flags & KEEP)
          flags |= SEPARATEKEEP;
        else
          flags |= SEPARATE;
        flags &= (~KEEP);
        if(flags & DIEAFTER)
          dieafter = TRUE;
        flags &= (~DIEAFTER);
        rc = ExecOnList(hwnd,
                        info->cl,
                        flags,
                        NULL,
                        list,
                        GetPString(IDS_EXECASSOCTITLETEXT));
        if(rc != -1 &&
           dieafter)
          PostMsg((HWND)0,
                  WM_QUIT,
                  MPVOID,
                  MPVOID);
        return rc;
      }
      p = strtok(0,";");
    }
    info = info->next;
  }
  return -1;
}


MRESULT EXPENTRY AssocDlgProc (HWND hwnd,ULONG msg,MPARAM mp1,MPARAM mp2) {

  LINKASSOC *info;
  SHORT      x;

  switch(msg) {
    case WM_INITDLG:
      WinSendDlgItemMsg(hwnd,ASS_LISTBOX,LM_DELETEALL,MPVOID,MPVOID);
      WinSendDlgItemMsg(hwnd,ASS_MASK,EM_SETTEXTLIMIT,
                        MPFROM2SHORT(CCHMAXPATH,0),MPVOID);
      WinSendDlgItemMsg(hwnd,ASS_CL,EM_SETTEXTLIMIT,
                        MPFROM2SHORT(1000,0),MPVOID);
      WinSendDlgItemMsg(hwnd,ASS_SIG,EM_SETTEXTLIMIT,
                        MPFROM2SHORT(CCHMAXPATH,0),MPVOID);
      WinSendDlgItemMsg(hwnd,ASS_OFFSET,EM_SETTEXTLIMIT,
                        MPFROM2SHORT(34,0),MPVOID);
      WinSetDlgItemText(hwnd,ASS_MASK,NullStr);
      WinSetDlgItemText(hwnd,ASS_CL,NullStr);
      WinSetDlgItemText(hwnd,ASS_SIG,NullStr);
      WinSetDlgItemText(hwnd,ASS_OFFSET,"0");
      WinCheckButton(hwnd,ASS_DEFAULT,TRUE);
      WinCheckButton(hwnd,ASS_PROMPT,FALSE);
      WinCheckButton(hwnd,ASS_KEEP,FALSE);
      WinCheckButton(hwnd,ASS_DIEAFTER,FALSE);
      PostMsg(hwnd,UM_UNDO,MPVOID,MPVOID);
      {
        PFNWP oldproc;

        oldproc = WinSubclassWindow(WinWindowFromID(hwnd,ASS_CL),
                                    (PFNWP)AssocTextProc);
        if(oldproc)
          WinSetWindowPtr(WinWindowFromID(hwnd,ASS_CL),0,(PVOID)oldproc);
      }
      break;

    case UM_UNDO:
      {
        CHAR s[1002 + CCHMAXPATH + 6];

        WinSendDlgItemMsg(hwnd,
                          ASS_LISTBOX,
                          LM_DELETEALL,
                          MPVOID,
                          MPVOID);
        info = asshead;
        while(info) {
          sprintf(s,
                  "%-12s \x1a %-24s %s%s%s",
                  info->mask,
                  info->cl,
                  (info->sig && *info->sig) ?
                   "[" : NullStr,
                   (info->sig && *info->sig) ? info->sig : NullStr,
                  (info->sig && *info->sig) ? "]" : NullStr);
          x = (SHORT)WinSendDlgItemMsg(hwnd,
                                       ASS_LISTBOX,
                                       LM_INSERTITEM,
                                       MPFROM2SHORT(LIT_END,0),
                                       MPFROMP(s));
          if(x >= 0)
            WinSendDlgItemMsg(hwnd,
                              ASS_LISTBOX,
                              LM_SETITEMHANDLE,
                              MPFROMSHORT(x),
                              MPFROMP(info));
          info = info->next;
        }
        WinSendDlgItemMsg(hwnd,
                          ASS_LISTBOX,
                          LM_SELECTITEM,
                          MPFROMSHORT(0),
                          MPFROMSHORT(TRUE));
      }
      return 0;

    case WM_CONTROL:
      if(SHORT1FROMMP(mp1) == ASS_LISTBOX) {
        switch(SHORT2FROMMP(mp1)) {
          case LN_ENTER:
          case LN_SELECT:
            x = (SHORT)WinSendDlgItemMsg(hwnd,
                                         ASS_LISTBOX,
                                         LM_QUERYSELECTION,
                                         MPFROMSHORT(LIT_FIRST),
                                         MPVOID);
            if(x >= 0) {

              CHAR s[36];

              info = (LINKASSOC *)WinSendDlgItemMsg(hwnd,
                                                    ASS_LISTBOX,
                                                    LM_QUERYITEMHANDLE,
                                                    MPFROMSHORT(x),
                                                    MPVOID);
              if(!info) {
                DosBeep(50,100);
                break;
              }
              WinSetDlgItemText(hwnd,ASS_MASK,info->mask);
              WinSetDlgItemText(hwnd,ASS_CL,info->cl);
              WinSetDlgItemText(hwnd,ASS_SIG,
                                (info->sig && *info->sig) ? info->sig : NullStr);
              sprintf(s,"%ld",info->offset);
              WinSetDlgItemText(hwnd,ASS_OFFSET,s);
              if(!(info->flags & 1023))
                WinCheckButton(hwnd,ASS_DEFAULT,TRUE);
              else {
                if(info->flags & FULLSCREEN)
                  WinCheckButton(hwnd,ASS_FULLSCREEN,TRUE);
                else if(info->flags & MINIMIZED)
                  WinCheckButton(hwnd,ASS_MINIMIZED,TRUE);
                else if(info->flags & MAXIMIZED)
                  WinCheckButton(hwnd,ASS_MAXIMIZED,TRUE);
                else if(info->flags & INVISIBLE)
                  WinCheckButton(hwnd,ASS_INVISIBLE,TRUE);
              }
              WinCheckButton(hwnd,ASS_KEEP,((info->flags & KEEP) != 0));
              WinCheckButton(hwnd,ASS_DIEAFTER,((info->flags & DIEAFTER) != 0));
              WinCheckButton(hwnd,ASS_PROMPT,((info->flags & PROMPT) != 0));
              {
                CHAR  env[1002];
                ULONG size;

                *env = 0;
                size = sizeof(env) - 1;
                if(PrfQueryProfileData(fmprof,
                                       FM3Str,
                                       info->cl,
                                       env,
                                       &size) &&
                   *env)
                  WinSetDlgItemText(hwnd,
                                    ASS_ENVIRON,
                                    env);
                else
                  WinSetDlgItemText(hwnd,
                                    ASS_ENVIRON,
                                    NullStr);
              }
            }
            break;
        }
      }
      return 0;

    case WM_COMMAND:
      switch(SHORT1FROMMP(mp1)) {
        case ASS_TOP:
          x = (SHORT)WinSendDlgItemMsg(hwnd,ASS_LISTBOX,
                                       LM_QUERYSELECTION,
                                       MPFROMSHORT(LIT_FIRST),
                                       MPVOID);
          if(x >= 0) {
            info = (LINKASSOC *)WinSendDlgItemMsg(hwnd,ASS_LISTBOX,
                                                  LM_QUERYITEMHANDLE,
                                                  MPFROMSHORT(x),
                                                  MPVOID);
            if(info) {
              if(info != asshead) {
                if(info->prev)
                  info->prev->next = info->next;
                if(info->next)
                  info->next->prev = info->prev;
                if(info == asstail)
                  asstail = info->prev;
                info->prev = NULL;
                info->next = asshead;
                asshead->prev = info;
                asshead = info;
                WinSendMsg(hwnd,UM_UNDO,MPVOID,MPVOID);
              }
            }
          }
          break;

        case ASS_FIND:
          {
            CHAR filename[CCHMAXPATH + 9];

            *filename = 0;
            if(insert_filename(hwnd,filename,2,FALSE) && *filename) {
              strcat(filename," %a");
              WinSetDlgItemText(hwnd,ASS_CL,filename);
            }
          }
          break;

        case DID_OK:
          WinDismissDlg(hwnd,1);
          break;

        case DID_CANCEL:
          WinDismissDlg(hwnd,0);
          break;

        case IDM_HELP:
          if(hwndHelp)
            WinSendMsg(hwndHelp,HM_DISPLAY_HELP,
                       MPFROM2SHORT(HELP_ASSOC,0),
                       MPFROMSHORT(HM_RESOURCEID));
          break;

        case ASS_ADD:
          {
            ASSOC temp;
            CHAR  dummy[34];

            memset(&temp,0,sizeof(ASSOC));
            WinQueryDlgItemText(hwnd,ASS_MASK,sizeof(temp.mask),temp.mask);
            WinQueryDlgItemText(hwnd,ASS_CL,sizeof(temp.cl),temp.cl);
            WinQueryDlgItemText(hwnd,ASS_SIG,sizeof(temp.sig),temp.sig);
            rstrip(temp.sig);
            if(*temp.sig) {
              WinQueryDlgItemText(hwnd,ASS_OFFSET,sizeof(dummy),dummy);
              temp.offset = atol(dummy);
            }
            lstrip(rstrip(temp.mask));
            lstrip(rstrip(temp.cl));
            if(WinQueryButtonCheckstate(hwnd,ASS_DEFAULT))
              temp.flags = 0;
            else if(WinQueryButtonCheckstate(hwnd,ASS_FULLSCREEN))
              temp.flags = FULLSCREEN;
            else if(WinQueryButtonCheckstate(hwnd,ASS_MINIMIZED))
              temp.flags = MINIMIZED;
            else if(WinQueryButtonCheckstate(hwnd,ASS_MAXIMIZED))
              temp.flags = MAXIMIZED;
            else if(WinQueryButtonCheckstate(hwnd,ASS_INVISIBLE))
              temp.flags = INVISIBLE;
            if(WinQueryButtonCheckstate(hwnd,ASS_KEEP))
              temp.flags |= KEEP;
            if(WinQueryButtonCheckstate(hwnd,ASS_DIEAFTER))
              temp.flags |= DIEAFTER;
            if(WinQueryButtonCheckstate(hwnd,ASS_PROMPT))
              temp.flags |= PROMPT;
            info = add_association(&temp);
            if(info) {

              CHAR s[1002 + CCHMAXPATH + 6];

              *s = 0;
              WinQueryDlgItemText(hwnd,ASS_ENVIRON,1000,s);
              stripcr(s);
              lstrip(rstrip(s));
              if(*s)
                PrfWriteProfileString(fmprof,FM3Str,temp.cl,s);
              sprintf(s,"%-12s \x1a %-24s %s%s%s",temp.mask,
                      temp.cl,(*temp.sig) ?
                      "[" : NullStr,(*temp.sig) ? temp.sig : NullStr,
                      (*temp.sig) ? "]" : NullStr);
              x = (SHORT)WinSendDlgItemMsg(hwnd,
                                           ASS_LISTBOX,
                                           LM_INSERTITEM,
                                           MPFROM2SHORT(LIT_END,0),
                                           MPFROMP(s));
              if(x >= 0) {
                WinSendDlgItemMsg(hwnd,
                                  ASS_LISTBOX,
                                  LM_SETITEMHANDLE,
                                  MPFROMSHORT(x),
                                  MPFROMP(info));
                WinSendDlgItemMsg(hwnd,
                                  ASS_LISTBOX,
                                  LM_SELECTITEM,
                                  MPFROMSHORT(x),
                                  MPFROMSHORT(TRUE));
              }
              save_associations();
            }
            else
              DosBeep(50,200);
          }
          break;

        case ASS_DELETE:
          {
            ASSOC temp;
            CHAR  dummy[34];

            memset(&temp,0,sizeof(ASSOC));
            WinQueryDlgItemText(hwnd,ASS_MASK,sizeof(temp.mask),temp.mask);
            WinQueryDlgItemText(hwnd,ASS_SIG,sizeof(temp.sig),temp.sig);
            rstrip(temp.sig);
            if(*temp.sig) {
              WinQueryDlgItemText(hwnd,ASS_OFFSET,sizeof(dummy),dummy);
              temp.offset = atol(dummy);
            }
            lstrip(rstrip(temp.mask));
            PrfWriteProfileData(fmprof,
                                FM3Str,
                                temp.mask,
                                NULL,
                                0L);
            if(kill_association(&temp)) {
              x = (SHORT)WinSendDlgItemMsg(hwnd,
                                           ASS_LISTBOX,
                                           LM_QUERYSELECTION,
                                           MPFROMSHORT(LIT_FIRST),
                                           MPVOID);
              if(x >= 0) {
                WinSendDlgItemMsg(hwnd,
                                  ASS_LISTBOX,
                                  LM_DELETEITEM,
                                  MPFROMSHORT(x),
                                  MPVOID);
                WinSendDlgItemMsg(hwnd,ASS_LISTBOX,LM_SELECTITEM,
                                  MPFROMSHORT(LIT_NONE),
                                  MPFROMSHORT(FALSE));
              }
              save_associations();
            }
            else
              DosBeep(50,200);
          }
          break;
      }
      return 0;
  }
  return WinDefDlgProc(hwnd,msg,mp1,mp2);
}


VOID EditAssociations (HWND hwnd) {

  static CHAR stop = 0;

  if(stop)
    return;
  stop++;
  if(!assloaded)
    load_associations();
  WinDlgBox(HWND_DESKTOP,hwnd,AssocDlgProc,FM3ModHandle,ASS_FRAME,NULL);
  stop = 0;
}

