home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / k95source / ckop.c < prev    next >
C/C++ Source or Header  |  2020-01-01  |  17KB  |  615 lines

  1. /*****************************************************************************/
  2. /*             Copyright (c) 1994 by Jyrki Salmi <jytasa@jyu.fi>             */
  3. /*        You may modify, recompile and distribute this file freely.         */
  4. /*****************************************************************************/
  5.  
  6. /*
  7.    Routines that load P.DLL and get the address of p_tranfer() entry
  8.    function.
  9. */
  10.  
  11. #include <stdio.h>
  12.  
  13. #ifdef OS2
  14. #ifdef NT
  15. #include <windows.h>
  16. #else
  17. #define INCL_DOSMODULEMGR
  18. #define INCL_DOSPROCESS
  19. #define INCL_DOSDEVICES
  20. #define INCL_DOSDEVIOCTL
  21. #define INCL_DOSNLS
  22. #define INCL_DOSASYNCTIMER
  23. #define INCL_DOSDATETIME
  24. #define INCL_DOSERRORS
  25. #include <os2.h>
  26. #undef COMMENT
  27. #endif
  28. #endif /* OS2 */
  29.  
  30. #include "ckcdeb.h"
  31. #ifndef NOXFER
  32. #ifdef XYZ_INTERNAL
  33. #include "ckcker.h"
  34. #include "ckuusr.h"
  35. #include "ckcnet.h"
  36. #include "ckocon.h"
  37.  
  38. #include "p_type.h"
  39. #include "p.h"
  40. #include "ckop.h"
  41. #include "p_global.h"
  42. #include "p_callbk.h"
  43. #include "p_common.h"
  44.  
  45. extern int rpackets, spackets, spktl, rpktl, what ;
  46.  
  47. #ifdef XYZ_DLL
  48. #ifdef OS2
  49. static HMODULE dll_handle;
  50. U32 (* _System p_transfer)(P_CFG *) = NULL;
  51. #endif /* OS2 */
  52.  
  53. #define PINBUFSIZE 8192
  54. #define POUTBUFSIZE 4096
  55.  
  56. int
  57. load_p_dll(void) {
  58.     int rc=0;
  59.     CHAR *exe_path;
  60.     CHAR path[256];
  61.  
  62. #ifdef NT
  63.     dll_handle = LoadLibrary( "P95.DLL" ) ;
  64.     if ( !dll_handle )
  65.     {
  66.         rc = GetLastError() ;
  67.         debug(F101,"load_p_dll - Unable to load module: rc","",rc);
  68.         return rc;
  69.     }
  70.     (FARPROC) p_transfer = GetProcAddress( dll_handle, "p_transfer" ) ;
  71.     if ( !p_transfer )
  72.     {
  73.         rc = GetLastError() ;
  74.         debug(F101,"load_p_dll - Unable to find p_transfer()","",rc);
  75.         return rc;
  76.     }
  77. #else
  78.     exe_path = GetLoadPath();
  79.     sprintf(path, "%.*sP2.DLL", (int)get_dir_len(exe_path), exe_path);
  80.     rc = DosLoadModule(NULL, 0L, path, &dll_handle);
  81.     if (rc) {
  82.         /* P.DLL was not found in directory specified with LIBPATH, let's look */
  83.         /* up for it from the directory where P.EXE was ran from. */
  84.         rc = DosLoadModule(NULL, 0L, "P2", &dll_handle);
  85.         if (rc)
  86.             debug(F101,"load_p_dll - Unable to load module: rc","",rc);
  87.     }
  88.     /* Query the address of p_transfer() entry function */
  89.     rc = DosQueryProcAddr(dll_handle,
  90.                            0,
  91.                            "p_transfer",
  92.                            (PFN *)&p_transfer);
  93.     if (rc)
  94.         debug(F101,"load_p_dll - Unable to find p_transfer()","",rc);
  95. #endif
  96.     return rc ;
  97. }
  98.  
  99. int
  100. unload_p_dll(void) {
  101.   int rc=0;
  102. #ifdef NT
  103.    if ( !FreeLibrary( dll_handle ) )
  104.       rc = GetLastError() ;
  105. #else
  106.    rc = DosFreeModule(dll_handle);
  107. #endif
  108.   if (rc)
  109.     debug(F101,"unload_p_dll - Unable to unload module - you must be kidding if you see this one! :-)","",rc);
  110.   else
  111.      p_transfer = NULL ;
  112.    return rc ;
  113. }
  114. #endif /* XYZ_DLL */
  115.  
  116.  
  117. U32 _System
  118. pushback_func( U8 * buf, U32 len )
  119. {
  120.     return le_puts( buf, len );
  121. }
  122.  
  123. U32 _System
  124. in_func( U8 * buf, U32 len, U32 * bytes_received )
  125. {
  126.     extern int network, carrier;
  127.     int rc, avail, read, i ;
  128.  
  129.     if (!network) {                     /* if not a network */
  130.         if (carrier != CAR_OFF &&       /* && carrier-watch enabled */
  131.              (ttgmdm() & BM_DCD)==0) { /* check for carrier detect */
  132.             debug(F100,"P in_func carrier dropped","",0);
  133.             *bytes_received = 0;
  134.             *buf='\0';
  135.             return(-1);         /* no? Carrier dropped */
  136.         }
  137.     }
  138.     avail = ttchk() ;
  139.     debug(F111,"P in_func","ttchk",avail);
  140.  
  141.     if ( avail <= 0 )
  142.         return(avail);
  143.  
  144.     if ( !buf || !bytes_received )
  145.         return -1 ;
  146.  
  147.     *bytes_received = 0 ;
  148.     *buf = '\0' ;
  149.  
  150.     if ( !len )
  151.         return 0 ;
  152.  
  153.     read = len > avail ? avail : len ;
  154.     rc = ttxin( read, buf ) ;
  155.  
  156.     if ( rc > 0 ) {
  157.         *bytes_received = rc ;
  158.         hexdump("P in_func",buf,rc);
  159.  
  160.         if ( what == W_RECV ) {
  161.             rpktl = rc ;
  162.             rpackets++;
  163.         }
  164.  
  165.         return 0;
  166.     } else if ( rc < -1 )
  167.         return(rc);
  168.     return(ERROR_NO_DATA); /* Either no data was received or timeout */
  169. }
  170.  
  171. U32 _System
  172. out_func( U8 * buf, U32 len, U32 * bytes_written )
  173. {
  174.     int rc = 0 ;
  175.    if ( !buf || !bytes_written )
  176.       return -1 ;
  177.  
  178.    *bytes_written = 0 ;
  179.  
  180.    if ( !len )
  181.       return 0 ;
  182.  
  183.     if ( what == W_SEND ) {
  184.         spktl = len ;
  185.         spackets++ ;
  186.     }
  187.  
  188.     hexdump("P out_func",buf,len);
  189.     rc = ttxout( buf, len ) ;
  190.     if ( rc >= 0 ) {
  191.         *bytes_written = rc ;
  192.         return 0;
  193.     }
  194.     else if ( rc == -1 )
  195.         return 0;       /* No data written but connection not dropped */
  196.     else return 1 ;
  197. }
  198.  
  199. #ifdef OS2ONLY
  200. USHORT DosDevIOCtl32(PVOID pData, USHORT cbData, PVOID pParms, USHORT cbParms,
  201.                      USHORT usFunction, USHORT usCategory, HFILE hDevice);
  202. #endif /* OS2ONLY */
  203.  
  204. U32 _System
  205. break_func( U8 on )
  206. {
  207.    extern int ttyfd, ttmdm ;
  208. #ifndef NT
  209.     MODEMSTATUS ms;
  210.     UINT data, i;
  211.  
  212. #endif /* NT */
  213.  
  214.     debug(F101,"P break_func","",on);
  215.  
  216. #ifdef NETCONN
  217.     if (ttmdm < 0)
  218.     {
  219.        if (on)
  220.           return os2_netbreak();
  221.        else return (0);
  222.     }
  223. #endif /* NETCONN */
  224. #ifdef NT
  225.     if (on) SetCommBreak( (HANDLE) ttyfd ) ;
  226.     else ClearCommBreak( (HANDLE) ttyfd ) ;
  227. #else /* NT */
  228.          ms.fbModemOn = RTS_ON;
  229.      ms.fbModemOff = 255;
  230.      DosDevIOCtl32(&data,sizeof(data),&ms,sizeof(ms),
  231.                 ASYNC_SETMODEMCTRL,IOCTL_ASYNC,ttyfd);
  232.  
  233.    if ( on )
  234.     DosDevIOCtl32(&i,sizeof(i),NULL,0,
  235.                 ASYNC_SETBREAKON,IOCTL_ASYNC,ttyfd);    /* Break on */
  236.    else
  237.     DosDevIOCtl32(&i,sizeof(i),NULL,0,
  238.                 ASYNC_SETBREAKOFF,IOCTL_ASYNC,ttyfd);   /* Break off */
  239. #endif /* NT */
  240.     return 0;
  241. }
  242.  
  243. U32 _System
  244. available_func( U32 * available )
  245. {
  246.     int rc = ttchk() ;
  247.  
  248.     if ( rc < 0 ) {
  249.         *available = 0 ;
  250.         return(1);
  251.     }
  252.     else {
  253.         *available = rc ;
  254.         return(0);
  255.     }
  256. }
  257.  
  258.  
  259. int
  260. pxyz(int sstate) {
  261.     extern struct ck_p ptab[] ;
  262.     extern int ttyfd, protocol, mdmtyp, fncact, binary, moving, sendmode,
  263.       prefixing, carrier, local, fdispla, nfils, parity, ttprty;
  264.     extern int network;
  265. #ifndef NOLOCAL
  266.     extern term_io;
  267.     int term_io_sav = term_io;
  268. #endif /* NOLOCAL */
  269. #ifdef TCPSOCKET
  270.     extern int u_binary, me_binary, ttnproto, tn_b_meu, tn_b_ume ;
  271. #endif /* TCPSOCKET */
  272.     extern int retrans, crunched ;
  273.     extern int timeouts, tsecs ;
  274.     extern char * cmarg, * cmarg2, ** cmlist ;
  275.     extern long ffc, filcnt ;
  276.     extern short ctlp[] ;
  277. #ifdef NT
  278.     extern int owwait, maxow ;  /* overlapped writes wait for return ? */
  279. #endif /* NT */
  280.     extern int nzxpand(char *,int) ;
  281.     extern int znext( char *) ;
  282. #ifdef CK_TMPDIR
  283.     extern char * dldir ;
  284.     extern int f_tmpdir;
  285.     extern char savdir[] ;
  286.     extern char *fncnam[] ;
  287. #endif /* CK_TMPDIR */
  288. #ifndef NOMSEND
  289.     extern struct filelist * filehead, * filenext;
  290.     extern int addlist;
  291. #endif /* NOMSEND */
  292.     char filename[260] ;
  293.     int savbin = binary ;
  294.     char *tp;
  295.     APIRET rc = 0;
  296.     int i;
  297. #ifdef GFTIMER
  298.     extern float fptsecs;
  299. #endif /* GFTIMER */
  300.  
  301. #ifdef PIPESEND
  302. #undef PIPESEND
  303. #endif /* PIPESEND */
  304. #ifdef PIPESEND
  305.     extern int pipesend;
  306. #endif /* PIPESEND */
  307.  
  308.     savfnc = fncact ;
  309.  
  310. #ifdef XYZ_DLL
  311.     if ( !p_transfer )
  312.       if ( load_p_dll() )
  313.         return -1 ;
  314. #endif /* XYZ_DLL */
  315.  
  316.     memset(&p_cfg, '\0', sizeof(p_cfg));
  317.     p_cfg.inbuf_size = PINBUFSIZE;
  318.     p_cfg.outbuf_size = POUTBUFSIZE;
  319.  
  320.     /* Initialize Receiver state flags */
  321.     checking_method = 0;
  322.     receiver_flags = 0;
  323.     receiver_window_size = -1;
  324.  
  325.     if (sstate != 's' && sstate != 'v') {
  326.         printf("?Invalid start state for %s\n",
  327.                ptab[protocol].p_name);
  328.         return -1 ;
  329.     }
  330.  
  331.     p_cfg.version = P_INTERFACE_VERSION;
  332.     p_cfg.serial_num = 0 ;              /* 0 means no serial number */
  333.     p_cfg.attn_seq = NULL;              /* By default, we don't */
  334.  
  335.     switch ( protocol ) {
  336.     case PROTO_X:
  337.         debug(F111,"pxyz()","Xmodem",protocol);
  338.         p_cfg.protocol_type = PROTOCOL_X ;
  339.         if (ptab[protocol].spktlen >= 1000)
  340.             p_cfg.attr |= CFG_1K_BLOCKS ;
  341.         break;
  342.     case PROTO_XC:
  343.         debug(F111,"pxyz()","Xmodem C",protocol);
  344.         p_cfg.protocol_type = PROTOCOL_X ;
  345.         p_cfg.attr |= CFG_ALTERNATIVE_CHECKING;
  346.         if (ptab[protocol].spktlen >= 1000)
  347.             p_cfg.attr |= CFG_1K_BLOCKS;
  348.         break;
  349.     case PROTO_Y:
  350.         debug(F111,"pxyz()","Ymodem",protocol);
  351.         p_cfg.protocol_type = PROTOCOL_Y ;
  352.         if (ptab[protocol].spktlen >= 1000)
  353.             p_cfg.attr |= CFG_1K_BLOCKS ;
  354.         break;
  355.     case PROTO_G:
  356.         debug(F111,"pxyz()","Ymodem G",protocol);
  357.         p_cfg.protocol_type = PROTOCOL_G ;
  358.         if (ptab[protocol].spktlen >= 1000)
  359.             p_cfg.attr |= CFG_1K_BLOCKS ;
  360.         break;
  361.     case PROTO_Z:
  362.         debug(F111,"pxyz()","Zmodem",protocol);
  363.         p_cfg.protocol_type = PROTOCOL_Z ;
  364. #ifdef COMMENT
  365.         /* this is handled by ptab[protocol].h_init now */
  366.         p_cfg.attr |= CFG_SEND_RZ_CR ;
  367. #endif /* COMMENT */
  368.         if (ptab[protocol].winsize > -1)
  369.           p_cfg.blk_size = ptab[protocol].winsize ;
  370.         break;
  371.     default:
  372.         debug(F101,"P - unsupported protocol","",protocol);
  373.         return -1;
  374.     }
  375.     p_cfg.dev_type = DEV_TYPE_EXE_IO ;
  376. #ifdef TCPSOCKET
  377.     /* Just for debug purposes */
  378.     if ( deblog && mdmtyp < 0 && network && ttnproto == NP_TELNET) {
  379.         debug(F110,"pxyz","CFG_DEV_TELNET",0);
  380.         if ( TELOPT_U(TELOPT_BINARY) || (TELOPT_ME(TELOPT_BINARY) && tn_b_meu) ) {
  381.             debug(F110,"pxyz","CFG_DEV_TELNET_U_BINARY",0);
  382.         }
  383.         if ( TELOPT_ME(TELOPT_BINARY) || (TELOPT_U(TELOPT_BINARY) && tn_b_ume) ) {
  384.             debug(F110,"pxyz","CFG_DEV_TELNET_ME_BINARY",0);
  385.         }
  386.     }
  387. #endif /* TCPSOCKET */
  388.     if ( moving )
  389.       p_cfg.attr |= CFG_FILE_MOVE ;
  390.  
  391.     if (local && fdispla ) {
  392.         opt_mileage = 1 ;
  393.         opt_frameends = 0 ;
  394.         opt_headers = 0 ;
  395.     }
  396.  
  397.     switch (fncact) {                   /* Filename collision */
  398.       case XYFX_A:                      /* Append */
  399.         opt_management = Z_MANAGEMENT_APPEND ;
  400.         break;
  401.       case XYFX_B:                      /* Backup */
  402.         opt_management = Z_MANAGEMENT_BACKUP ;
  403.         break;
  404.       case XYFX_D:                      /* Discard */
  405.         opt_management = Z_MANAGEMENT_PROTECT ;
  406.         break;
  407.       case XYFX_R:                      /* Rename */
  408.         opt_management = Z_MANAGEMENT_RENAME ;
  409.         break;
  410.       case XYFX_X:                      /* Replace */
  411.         opt_management = Z_MANAGEMENT_REPLACE ;
  412.         break;
  413.       case XYFX_U:                      /* Update */
  414.         opt_management = Z_MANAGEMENT_NEWER ;
  415.         break;
  416.     }
  417.       opt_text = !binary ; /* Text mode */
  418.  
  419.     if (!cmarg2) cmarg2 = "";
  420.  
  421.     if (sstate == 'v') {                /* Receiving */
  422. #ifdef CK_TMPDIR
  423.         if (dldir && !f_tmpdir) {       /* If they have a download directory */
  424.             char * s = NULL ;
  425.             debug(F110,"pxyz() download directory",dldir,0);
  426.             if (s = zgtdir()) {         /* Get current directory */
  427.                 if (zchdir(dldir)) {    /* Change to download directory */
  428.                     strncpy(savdir,s,TMPDIRLEN);
  429.                     f_tmpdir = 1;       /* Remember that we did this */
  430.                 }
  431.             }
  432.         }
  433. #endif /* CK_TMPDIR */
  434.  
  435.         if (ptab[protocol].fnrp == 0) {
  436.             opt_paths = 1 ;
  437.             opt_create = 1 ;
  438.         }
  439.         what = W_RECV ;
  440.         p_cfg.transfer_direction = DIR_RECV; /* Must supply output filename */
  441.         if (p_cfg.protocol_type == PROTOCOL_X) { /* for XMODEM receive */
  442.             if (!*cmarg2)
  443.               cmarg2 = "XMODEM.XXX";
  444.         }
  445.         if (*cmarg2) {
  446.             tl_add( &tl, cmarg2, 0, NULL, !binary ) ;
  447.             cmarg2 = "";
  448.         }
  449.     } else {                            /* Sending */
  450.         int i, j, x, y;
  451.         if (ptab[protocol].fnsp == 0)
  452.           opt_paths = 1 ;
  453.         if (binary && sendmode == SM_RESEND && protocol == PROTO_Z ) {
  454.             opt_resume = 1 ;
  455.         }
  456.         for (i=0;i<256;i++) {
  457.             if (ctlp[i]) {
  458.                 p_cfg.attr |= CFG_ESC_TABLE ;
  459.                 p_cfg.control_prefix_table = (unsigned short *)ctlp ;
  460.                 break;
  461.             }
  462.         }
  463.         debug(F111,"ckop()","parity",parity);
  464.         debug(F111,"ckop()","ttprty",ttprty);
  465.         if ( parity ) {
  466.             p_cfg.attr |= CFG_ESC_8TH ;
  467.         }
  468.         p_cfg.transfer_direction = DIR_SEND ;
  469.         what = W_SEND ;
  470.  
  471.         if ( nfils < 0 ) {
  472.             if (!cmarg[0])
  473.                 return -1;
  474.  
  475. #ifdef PIPESEND
  476.             if ( pipesend ) {
  477.                 tl_add( &tl, cmarg, 0,
  478.                         (cmarg2 && *cmarg2) ? cmarg2 : NULL,
  479.                         !binary ) ;
  480.             }
  481.             else
  482. #endif /* PIPESEND */
  483.             for ( i=0, y=x=nzxpand(cmarg,1); i < x ; i++ ) {
  484.                 y = znext(filename);
  485.                 if ( !isdir(filename) )
  486.                     tl_add( &tl, filename, zchki(filename),
  487.                             (x==1 && cmarg2 && *cmarg2) ? cmarg2 : NULL,
  488.                             !binary ) ;
  489.             }
  490.         }
  491.         else {
  492. #ifndef NOMSEND
  493.            if ( addlist ) {
  494.               while( filenext )
  495.                  if ( filenext->fl_name )
  496.                  {
  497.                     for ( i=0, y=x=nzxpand(filenext->fl_name,1);
  498.                           i < x ; i++ ) {
  499.                         y = znext(filename);
  500.                         if ( !isdir(filename) )
  501.                             tl_add( &tl, filename, zchki(filename),
  502.                                (x == 1 && filenext->fl_alias) ?
  503.                                filenext->fl_alias : NULL,
  504.                                !filenext->fl_mode ) ;
  505.                     }
  506.                     filenext = filenext->fl_next ;
  507.                  }
  508.                  else
  509.                  {
  510.                     printf("?Internal error expanding ADD list\n");
  511.                     return(-1);
  512.                  }
  513.            }
  514.            else
  515. #endif /* NOMSEND */
  516.               for ( j=0; j<nfils ; j++ )
  517.               {
  518.                   for ( i=0,y=x=nzxpand(cmlist[j],1); i < x ; i++ ) {
  519.                       y = znext(filename);
  520.                       tl_add( &tl, filename, zchki(filename),
  521.                               (x==1 && cmarg2 && *cmarg2) ? cmarg2 : NULL,
  522.                               !binary ) ;
  523.                   }
  524.               }
  525.         }
  526.         files_left = tl->cnt ;
  527.         bytes_left = tl->size ;
  528.     }
  529.     p_cfg.status_func = status_func;
  530.     p_cfg.r_open_func = r_open_func;
  531.     p_cfg.s_open_func = s_open_func;
  532.     p_cfg.close_func = close_func;
  533.     p_cfg.seek_func = seek_func;
  534.     p_cfg.read_func = read_func;
  535.     p_cfg.write_func = write_func;
  536.     p_cfg.exe_out_func = out_func;
  537.     p_cfg.exe_in_func = in_func;
  538.     p_cfg.exe_break_func = break_func;
  539.     p_cfg.exe_available_func = available_func;
  540.     p_cfg.exe_pushback_func = pushback_func;
  541.  
  542.     /* Transaction Log Begin */
  543.     ztime(&tp);
  544.     tlog(F110,"Transaction begins",tp,0L); /* Make transaction log entry */
  545.     tlog(F110,"Global file mode:", binary ? "binary" : "text", 0L);
  546.     if ( p_cfg.transfer_direction == DIR_RECV )
  547.         tlog(F110,"Collision action:", fncnam[fncact],0);
  548.     tlog(F100,"","",0);
  549.  
  550.     ckscreen(SCR_PT,'S',0L,"");
  551.     sprintf(filename, "%s %s is being initiated",
  552.            ptab[protocol].p_name,
  553.            p_cfg.transfer_direction == DIR_RECV ?
  554.            "receiving" : "sending" );
  555.     ckscreen(SCR_ST,ST_MSG,0L,filename);
  556.  
  557.     install_interrupt_handler( 1 ) ;
  558.     resetc() ;                          /* Reset per transaction counters */
  559.     rtimer() ;                          /* Reset timers for file transfer */
  560. #ifdef GFTIMER
  561.     rftimer();
  562. #endif /* GFTIMER */
  563.     spktl = 0 ;
  564.     rpktl = 0 ;
  565.     tsecs = -1 ;
  566. #ifndef NOLOCAL
  567.     term_io = 0;                        /* Disable Emulator I/O */
  568. #endif /* NOLOCAL */
  569.     filcnt = 0;
  570.  
  571.     if ( rc = p_transfer( &p_cfg ) ) {
  572.        bleep(BP_FAIL) ;
  573.        if ( we_aborted )
  574.           ckscreen(SCR_EM,0,0L,"Transfer cancelled");
  575.        else
  576.            ckscreen(SCR_EM,0,0L,"Transfer failed");
  577.  
  578.     }
  579.     else
  580.        bleep(BP_NOTE);
  581.  
  582.     install_interrupt_handler( 0 ) ;
  583.     tl_free(&tl) ;
  584.  
  585. #ifdef CK_TMPDIR
  586.     /* If we were cd'd temporarily to another device or directory ... */
  587.     if (f_tmpdir) {
  588.         int x;
  589.         x = zchdir((char *) savdir);    /* ... restore previous directory */
  590.         f_tmpdir = 0;                   /* and remember we did it. */
  591.         debug(F111,"ckcpro.w B tmpdir restoring",savdir,x);
  592.     }
  593. #endif /* CK_TMPDIR */
  594.  
  595.     tsecs = gtimer();
  596. #ifdef GFTIMER
  597.     fptsecs = gftimer();
  598. #endif /* GFTIMER */
  599.     ckscreen(SCR_TC,0,0L,"");             /* Close Display */
  600.  
  601.     tstats() ;                          /* Generate output for Transaction Log */
  602.  
  603.     binary = savbin ;                   /* Restore original settings */
  604.     fncact = savfnc ;
  605. #ifndef NOLOCAL
  606.     term_io = term_io_sav;
  607. #endif /* NOLOCAL */
  608. #ifdef PIPESEND
  609.     pipesend = 0;                       /* Reset it for the next time */
  610. #endif /* PIPESEND */
  611.  
  612.     return rc ;
  613. }
  614. #endif /* XYZ_INTERNAL */
  615. #endif /* NOXFER */