/*****************************************************************************/
/*             Copyright (c) 1994 by Jyrki Salmi <jytasa@jyu.fi>             */
/*        You may modify, recompile and distribute this file freely.         */
/*****************************************************************************/

/*
   Routines that load P.DLL and get the address of p_tranfer() entry
   function.
*/

#include <stdio.h>

#ifdef OS2
#ifdef NT
#include <windows.h>
#else
#define INCL_DOSMODULEMGR
#define INCL_DOSPROCESS
#define INCL_DOSDEVICES
#define INCL_DOSDEVIOCTL
#define INCL_DOSNLS
#define INCL_DOSASYNCTIMER
#define INCL_DOSDATETIME
#define INCL_DOSERRORS
#include <os2.h>
#undef COMMENT
#endif
#endif /* OS2 */

#include "ckcdeb.h"
#ifndef NOXFER
#ifdef XYZ_INTERNAL
#include "ckcker.h"
#include "ckuusr.h"
#include "ckcnet.h"
#include "ckocon.h"

#include "p_type.h"
#include "p.h"
#include "ckop.h"
#include "p_global.h"
#include "p_callbk.h"
#include "p_common.h"

extern int rpackets, spackets, spktl, rpktl, what ;

#ifdef XYZ_DLL
#ifdef OS2
static HMODULE dll_handle;
typedef U32 (* _System p_transfer_t)(P_CFG *);
p_transfer_t p_transfer = NULL;
#endif /* OS2 */

#define PINBUFSIZE 8192
#define POUTBUFSIZE 4096

int
load_p_dll(void) {
    int rc=0;
    CHAR *exe_path;
    CHAR path[256];

#ifdef NT
    dll_handle = LoadLibrary( "P95.DLL" ) ;
    if ( !dll_handle )
    {
        rc = GetLastError() ;
        debug(F101,"load_p_dll - Unable to load module: rc","",rc);
        return rc;
    }
    p_transfer = (p_transfer_t)GetProcAddress( dll_handle, "p_transfer" ) ;
    if ( !p_transfer )
    {
        rc = GetLastError() ;
        debug(F101,"load_p_dll - Unable to find p_transfer()","",rc);
        return rc;
    }
#else
    exe_path = GetLoadPath();
    sprintf(path, "%.*sP2.DLL", (int)get_dir_len(exe_path), exe_path);
    rc = DosLoadModule(NULL, 0L, path, &dll_handle);
    if (rc) {
        /* P.DLL was not found in directory specified with LIBPATH, let's look */
        /* up for it from the directory where P.EXE was ran from. */
        rc = DosLoadModule(NULL, 0L, "P2", &dll_handle);
        if (rc)
            debug(F101,"load_p_dll - Unable to load module: rc","",rc);
    }
    /* Query the address of p_transfer() entry function */
    rc = DosQueryProcAddr(dll_handle,
                           0,
                           "p_transfer",
                           (PFN *)&p_transfer);
    if (rc)
        debug(F101,"load_p_dll - Unable to find p_transfer()","",rc);
#endif
    return rc ;
}

int
unload_p_dll(void) {
  int rc=0;
#ifdef NT
   if ( !FreeLibrary( dll_handle ) )
      rc = GetLastError() ;
#else
   rc = DosFreeModule(dll_handle);
#endif
  if (rc)
    debug(F101,"unload_p_dll - Unable to unload module - you must be kidding if you see this one! :-)","",rc);
  else
     p_transfer = NULL ;
   return rc ;
}
#endif /* XYZ_DLL */


U32 _System
pushback_func( U8 * buf, U32 len )
{
    return le_puts( buf, len );
}

U32 _System
in_func( U8 * buf, U32 len, U32 * bytes_received )
{
    extern int network, carrier;
    int rc, avail, read, i ;

    if (!network) {                     /* if not a network */
        if (carrier != CAR_OFF &&       /* && carrier-watch enabled */
             (ttgmdm() & BM_DCD)==0) { /* check for carrier detect */
            debug(F100,"P in_func carrier dropped","",0);
            *bytes_received = 0;
            *buf='\0';
            return(-1);         /* no? Carrier dropped */
        }
    }
    avail = ttchk() ;
    debug(F111,"P in_func","ttchk",avail);

    if ( avail <= 0 )
        return(avail);

    if ( !buf || !bytes_received )
        return -1 ;

    *bytes_received = 0 ;
    *buf = '\0' ;

    if ( !len )
        return 0 ;

    read = len > avail ? avail : len ;
    rc = ttxin( read, buf ) ;

    if ( rc > 0 ) {
        *bytes_received = rc ;
        ckhexdump("P in_func",buf,rc);

        if ( what == W_RECV ) {
            rpktl = rc ;
            rpackets++;
        }

        return 0;
    } else if ( rc < -1 )
        return(rc);
    return(ERROR_NO_DATA); /* Either no data was received or timeout */
}

U32 _System
out_func( U8 * buf, U32 len, U32 * bytes_written )
{
    int rc = 0 ;
   if ( !buf || !bytes_written )
      return -1 ;

   *bytes_written = 0 ;

   if ( !len )
      return 0 ;

    if ( what == W_SEND ) {
        spktl = len ;
        spackets++ ;
    }

    ckhexdump("P out_func",buf,len);
    rc = ttxout( buf, len ) ;
    if ( rc >= 0 ) {
        *bytes_written = rc ;
        return 0;
    }
    else if ( rc == -1 )
        return 0;       /* No data written but connection not dropped */
    else return 1 ;
}

#ifdef OS2ONLY
USHORT DosDevIOCtl32(PVOID pData, USHORT cbData, PVOID pParms, USHORT cbParms,
                     USHORT usFunction, USHORT usCategory, HFILE hDevice);
#endif /* OS2ONLY */

U32 _System
break_func( U8 on )
{
   extern int ttmdm ;
   extern CK_TTYFD_T ttyfd;
#ifndef NT
    MODEMSTATUS ms;
    UINT data, i;

#endif /* NT */

    debug(F101,"P break_func","",on);

#ifdef NETCONN
    if (ttmdm < 0)
    {
       if (on)
          return os2_netbreak();
       else return (0);
    }
#endif /* NETCONN */
#ifdef NT
    if (on) SetCommBreak( (HANDLE) ttyfd ) ;
    else ClearCommBreak( (HANDLE) ttyfd ) ;
#else /* NT */
         ms.fbModemOn = RTS_ON;
     ms.fbModemOff = 255;
     DosDevIOCtl32(&data,sizeof(data),&ms,sizeof(ms),
                ASYNC_SETMODEMCTRL,IOCTL_ASYNC,ttyfd);

   if ( on )
    DosDevIOCtl32(&i,sizeof(i),NULL,0,
                ASYNC_SETBREAKON,IOCTL_ASYNC,ttyfd);    /* Break on */
   else
    DosDevIOCtl32(&i,sizeof(i),NULL,0,
                ASYNC_SETBREAKOFF,IOCTL_ASYNC,ttyfd);   /* Break off */
#endif /* NT */
    return 0;
}

U32 _System
available_func( U32 * available )
{
    int rc = ttchk() ;

    if ( rc < 0 ) {
        *available = 0 ;
        return(1);
    }
    else {
        *available = rc ;
        return(0);
    }
}


int
pxyz(int sstate) {
    extern struct ck_p ptab[] ;
    extern int protocol, mdmtyp, fncact, binary, moving, sendmode,
      prefixing, carrier, local, fdispla, nfils, parity, ttprty;
    extern CK_TTYFD_T ttyfd;
    extern int network;
#ifndef NOLOCAL
    extern term_io;
    int term_io_sav = term_io;
#endif /* NOLOCAL */
#ifdef TCPSOCKET
    extern int u_binary, me_binary, ttnproto, tn_b_meu, tn_b_ume ;
#endif /* TCPSOCKET */
    extern int retrans, crunched ;
    extern int timeouts, tsecs ;
    extern char * cmarg, * cmarg2, ** cmlist ;
    extern long ffc, filcnt ;
    extern short ctlp[] ;
#ifdef NT
    extern int owwait, maxow ;  /* overlapped writes wait for return ? */
#endif /* NT */
    extern int nzxpand(CHAR *,int) ;
    extern int znext( char *) ;
#ifdef CK_TMPDIR
    extern char * dldir ;
    extern int f_tmpdir;
    extern char savdir[] ;
    extern char *fncnam[] ;
#endif /* CK_TMPDIR */
#ifndef NOMSEND
    extern struct filelist * filehead, * filenext;
    extern int addlist;
#endif /* NOMSEND */
    char filename[260] ;
    int savbin = binary ;
    char *tp;
    APIRET rc = 0;
    int i;
#ifdef GFTIMER
    extern float fptsecs;
#endif /* GFTIMER */

#ifdef PIPESEND
#undef PIPESEND
#endif /* PIPESEND */
#ifdef PIPESEND
    extern int pipesend;
#endif /* PIPESEND */

    savfnc = fncact ;

#ifdef XYZ_DLL
    if ( !p_transfer )
      if ( load_p_dll() )
        return -1 ;
#endif /* XYZ_DLL */

    memset(&p_cfg, '\0', sizeof(p_cfg));
    p_cfg.inbuf_size = PINBUFSIZE;
    p_cfg.outbuf_size = POUTBUFSIZE;

    /* Initialize Receiver state flags */
    checking_method = 0;
    receiver_flags = 0;
    receiver_window_size = -1;

    if (sstate != 's' && sstate != 'v') {
        printf("?Invalid start state for %s\n",
               ptab[protocol].p_name);
        return -1 ;
    }

    p_cfg.version = P_INTERFACE_VERSION;
    p_cfg.serial_num = 0 ;              /* 0 means no serial number */
    p_cfg.attn_seq = NULL;              /* By default, we don't */

    switch ( protocol ) {
    case PROTO_X:
        debug(F111,"pxyz()","Xmodem",protocol);
        p_cfg.protocol_type = PROTOCOL_X ;
        if (ptab[protocol].spktlen >= 1000)
            p_cfg.attr |= CFG_1K_BLOCKS ;
        break;
    case PROTO_XC:
        debug(F111,"pxyz()","Xmodem C",protocol);
        p_cfg.protocol_type = PROTOCOL_X ;
        p_cfg.attr |= CFG_ALTERNATIVE_CHECKING;
        if (ptab[protocol].spktlen >= 1000)
            p_cfg.attr |= CFG_1K_BLOCKS;
        break;
    case PROTO_Y:
        debug(F111,"pxyz()","Ymodem",protocol);
        p_cfg.protocol_type = PROTOCOL_Y ;
        if (ptab[protocol].spktlen >= 1000)
            p_cfg.attr |= CFG_1K_BLOCKS ;
        break;
    case PROTO_G:
        debug(F111,"pxyz()","Ymodem G",protocol);
        p_cfg.protocol_type = PROTOCOL_G ;
        if (ptab[protocol].spktlen >= 1000)
            p_cfg.attr |= CFG_1K_BLOCKS ;
        break;
    case PROTO_Z:
        debug(F111,"pxyz()","Zmodem",protocol);
        p_cfg.protocol_type = PROTOCOL_Z ;
#ifdef COMMENT
        /* this is handled by ptab[protocol].h_init now */
        p_cfg.attr |= CFG_SEND_RZ_CR ;
#endif /* COMMENT */
        if (ptab[protocol].winsize > -1)
          p_cfg.blk_size = ptab[protocol].winsize ;
        break;
    default:
        debug(F101,"P - unsupported protocol","",protocol);
        return -1;
    }
    p_cfg.dev_type = DEV_TYPE_EXE_IO ;
#ifdef TCPSOCKET
    /* Just for debug purposes */
    if ( deblog && mdmtyp < 0 && network && ttnproto == NP_TELNET) {
        debug(F110,"pxyz","CFG_DEV_TELNET",0);
        if ( TELOPT_U(TELOPT_BINARY) || (TELOPT_ME(TELOPT_BINARY) && tn_b_meu) ) {
            debug(F110,"pxyz","CFG_DEV_TELNET_U_BINARY",0);
        }
        if ( TELOPT_ME(TELOPT_BINARY) || (TELOPT_U(TELOPT_BINARY) && tn_b_ume) ) {
            debug(F110,"pxyz","CFG_DEV_TELNET_ME_BINARY",0);
        }
    }
#endif /* TCPSOCKET */
    if ( moving )
      p_cfg.attr |= CFG_FILE_MOVE ;

    if (local && fdispla ) {
        opt_mileage = 1 ;
        opt_frameends = 0 ;
        opt_headers = 0 ;
    }

    switch (fncact) {                   /* Filename collision */
      case XYFX_A:                      /* Append */
        opt_management = Z_MANAGEMENT_APPEND ;
        break;
      case XYFX_B:                      /* Backup */
        opt_management = Z_MANAGEMENT_BACKUP ;
        break;
      case XYFX_D:                      /* Discard */
        opt_management = Z_MANAGEMENT_PROTECT ;
        break;
      case XYFX_R:                      /* Rename */
        opt_management = Z_MANAGEMENT_RENAME ;
        break;
      case XYFX_X:                      /* Replace */
        opt_management = Z_MANAGEMENT_REPLACE ;
        break;
      case XYFX_U:                      /* Update */
        opt_management = Z_MANAGEMENT_NEWER ;
        break;
    }
      opt_text = !binary ; /* Text mode */

    if (!cmarg2) cmarg2 = "";

    if (sstate == 'v') {                /* Receiving */
#ifdef CK_TMPDIR
        if (dldir && !f_tmpdir) {       /* If they have a download directory */
            char * s = NULL ;
            debug(F110,"pxyz() download directory",dldir,0);
            if (s = zgtdir()) {         /* Get current directory */
                if (zchdir(dldir)) {    /* Change to download directory */
                    strncpy(savdir,s,TMPDIRLEN);
                    f_tmpdir = 1;       /* Remember that we did this */
                }
            }
        }
#endif /* CK_TMPDIR */

        if (ptab[protocol].fnrp == 0) {
            opt_paths = 1 ;
            opt_create = 1 ;
        }
        what = W_RECV ;
        p_cfg.transfer_direction = DIR_RECV; /* Must supply output filename */
        if (p_cfg.protocol_type == PROTOCOL_X) { /* for XMODEM receive */
            if (!*cmarg2)
              cmarg2 = "XMODEM.XXX";
        }
        if (*cmarg2) {
            tl_add( &tl, cmarg2, 0, NULL, !binary ) ;
            cmarg2 = "";
        }
    } else {                            /* Sending */
        int i, j, x, y;
        if (ptab[protocol].fnsp == 0)
          opt_paths = 1 ;
        if (binary && sendmode == SM_RESEND && protocol == PROTO_Z ) {
            opt_resume = 1 ;
        }
        for (i=0;i<256;i++) {
            if (ctlp[i]) {
                p_cfg.attr |= CFG_ESC_TABLE ;
                p_cfg.control_prefix_table = (unsigned short *)ctlp ;
                break;
            }
        }
        debug(F111,"ckop()","parity",parity);
        debug(F111,"ckop()","ttprty",ttprty);
        if ( parity ) {
            p_cfg.attr |= CFG_ESC_8TH ;
        }
        p_cfg.transfer_direction = DIR_SEND ;
        what = W_SEND ;

        if ( nfils < 0 ) {
			if (!cmarg[0])
				return -1;

#ifdef PIPESEND
            if ( pipesend ) {
                tl_add( &tl, cmarg, 0,
                        (cmarg2 && *cmarg2) ? cmarg2 : NULL,
                        !binary ) ;
            }
            else
#endif /* PIPESEND */
            for ( i=0, y=x=nzxpand(cmarg,1); i < x ; i++ ) {
                y = znext(filename);
                if ( !isdir(filename) )
                    tl_add( &tl, filename, zchki(filename),
                            (x==1 && cmarg2 && *cmarg2) ? cmarg2 : NULL,
                            !binary ) ;
            }
        }
        else {
#ifndef NOMSEND
           if ( addlist ) {
              while( filenext )
                 if ( filenext->fl_name )
                 {
                    for ( i=0, y=x=nzxpand(filenext->fl_name,1);
                          i < x ; i++ ) {
                        y = znext(filename);
                        if ( !isdir(filename) )
                            tl_add( &tl, filename, zchki(filename),
                               (x == 1 && filenext->fl_alias) ?
                               filenext->fl_alias : NULL,
                               !filenext->fl_mode ) ;
                    }
                    filenext = filenext->fl_next ;
                 }
                 else
                 {
                    printf("?Internal error expanding ADD list\n");
                    return(-1);
                 }
           }
           else
#endif /* NOMSEND */
              for ( j=0; j<nfils ; j++ )
              {
                  for ( i=0,y=x=nzxpand(cmlist[j],1); i < x ; i++ ) {
                      y = znext(filename);
                      tl_add( &tl, filename, zchki(filename),
                              (x==1 && cmarg2 && *cmarg2) ? cmarg2 : NULL,
                              !binary ) ;
                  }
              }
        }
        files_left = tl->cnt ;
        bytes_left = tl->size ;
    }
    p_cfg.status_func = status_func;
    p_cfg.r_open_func = r_open_func;
    p_cfg.s_open_func = s_open_func;
    p_cfg.close_func = close_func;
    p_cfg.seek_func = seek_func;
    p_cfg.read_func = read_func;
    p_cfg.write_func = write_func;
    p_cfg.exe_out_func = out_func;
    p_cfg.exe_in_func = in_func;
    p_cfg.exe_break_func = break_func;
    p_cfg.exe_available_func = available_func;
    p_cfg.exe_pushback_func = pushback_func;

    /* Transaction Log Begin */
    ztime(&tp);
    tlog(F110,"Transaction begins",tp,0L); /* Make transaction log entry */
    tlog(F110,"Global file mode:", binary ? "binary" : "text", 0L);
    if ( p_cfg.transfer_direction == DIR_RECV )
        tlog(F110,"Collision action:", fncnam[fncact],0);
    tlog(F100,"","",0);

    ckscreen(SCR_PT,'S',0L,"");
    sprintf(filename, "%s %s is being initiated",
           ptab[protocol].p_name,
           p_cfg.transfer_direction == DIR_RECV ?
           "receiving" : "sending" );
    ckscreen(SCR_ST,ST_MSG,0L,filename);

    install_interrupt_handler( 1 ) ;
    resetc() ;                          /* Reset per transaction counters */
    rtimer() ;                          /* Reset timers for file transfer */
#ifdef GFTIMER
    rftimer();
#endif /* GFTIMER */
    spktl = 0 ;
    rpktl = 0 ;
    tsecs = -1 ;
#ifndef NOLOCAL
    term_io = 0;                        /* Disable Emulator I/O */
#endif /* NOLOCAL */
    filcnt = 0;

    if ( rc = p_transfer( &p_cfg ) ) {
       bleep(BP_FAIL) ;
       if ( we_aborted )
          ckscreen(SCR_EM,0,0L,"Transfer cancelled");
       else
           ckscreen(SCR_EM,0,0L,"Transfer failed");

    }
    else
       bleep(BP_NOTE);

    install_interrupt_handler( 0 ) ;
    tl_free(&tl) ;

#ifdef CK_TMPDIR
    /* If we were cd'd temporarily to another device or directory ... */
    if (f_tmpdir) {
        int x;
        x = zchdir((char *) savdir);    /* ... restore previous directory */
        f_tmpdir = 0;                   /* and remember we did it. */
        debug(F111,"ckcpro.w B tmpdir restoring",savdir,x);
    }
#endif /* CK_TMPDIR */

    tsecs = gtimer();
#ifdef GFTIMER
    fptsecs = gftimer();
#endif /* GFTIMER */
    ckscreen(SCR_TC,0,0L,"");             /* Close Display */

    tstats() ;                          /* Generate output for Transaction Log */

    binary = savbin ;                   /* Restore original settings */
    fncact = savfnc ;
#ifndef NOLOCAL
    term_io = term_io_sav;
#endif /* NOLOCAL */
#ifdef PIPESEND
    pipesend = 0;                       /* Reset it for the next time */
#endif /* PIPESEND */

    return rc ;
}
#endif /* XYZ_INTERNAL */
#endif /* NOXFER */