Annotation of sbbs/src/sbbs3/xmodem.c, revision 1.1.1.1

1.1       root        1: /* xmodem.c */
                      2: 
                      3: /* Synchronet X/YMODEM Functions */
                      4: 
                      5: /* $Id: xmodem.c,v 1.26 2006/02/24 09:50:50 rswindell Exp $ */
                      6: 
                      7: /****************************************************************************
                      8:  * @format.tab-size 4          (Plain Text/Source Code File Header)                    *
                      9:  * @format.use-tabs true       (see http://www.synchro.net/ptsc_hdr.html)              *
                     10:  *                                                                                                                                                     *
                     11:  * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html         *
                     12:  *                                                                                                                                                     *
                     13:  * This program is free software; you can redistribute it and/or                       *
                     14:  * modify it under the terms of the GNU General Public License                         *
                     15:  * as published by the Free Software Foundation; either version 2                      *
                     16:  * of the License, or (at your option) any later version.                                      *
                     17:  * See the GNU General Public License for more details: gpl.txt or                     *
                     18:  * http://www.fsf.org/copyleft/gpl.html                                                                                *
                     19:  *                                                                                                                                                     *
                     20:  * Anonymous FTP access to the most recent released source is available at     *
                     21:  * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net     *
                     22:  *                                                                                                                                                     *
                     23:  * Anonymous CVS access to the development source and modification history     *
                     24:  * is available at cvs.synchro.net:/cvsroot/sbbs, example:                                     *
                     25:  * cvs -d :pserver:[email protected]:/cvsroot/sbbs login                       *
                     26:  *     (just hit return, no password is necessary)                                                     *
                     27:  * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src                *
                     28:  *                                                                                                                                                     *
                     29:  * For Synchronet coding style and modification guidelines, see                                *
                     30:  * http://www.synchro.net/source.html                                                                          *
                     31:  *                                                                                                                                                     *
                     32:  * You are encouraged to submit any modifications (preferably in Unix diff     *
                     33:  * format) via e-mail to [email protected]                                                                      *
                     34:  *                                                                                                                                                     *
                     35:  * Note: If this box doesn't appear square, then you need to fix your tabs.    *
                     36:  ****************************************************************************/
                     37: 
                     38: /* Standard headers */
                     39: #include <stdio.h>
                     40: #include <sys/stat.h>  /* struct stat */
                     41: #include <stdarg.h>            /* va_list */
                     42: 
                     43: /* smblib */
                     44: #include "crc16.h"
                     45: 
                     46: /* xpdev */
                     47: #include "genwrap.h"   /* YIELD */
                     48: #include "dirwrap.h"   /* getfname */
                     49: 
                     50: /* sexyz */
                     51: #include "sexyz.h"
                     52: 
                     53: #define getcom(t)      xm->recv_byte(xm->cbdata,t)
                     54: #define putcom(ch)     xm->send_byte(xm->cbdata,ch,xm->send_timeout)
                     55: 
                     56: static int lprintf(xmodem_t* xm, int level, const char *fmt, ...)
                     57: {
                     58:        va_list argptr;
                     59:        char    sbuf[1024];
                     60: 
                     61:        if(xm->lputs==NULL)
                     62:                return(-1);
                     63: 
                     64:     va_start(argptr,fmt);
                     65:     vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
                     66:        sbuf[sizeof(sbuf)-1]=0;
                     67:     va_end(argptr);
                     68:     return(xm->lputs(xm->cbdata,level,sbuf));
                     69: }
                     70: 
                     71: static BOOL is_connected(xmodem_t* xm)
                     72: {
                     73:        if(xm->is_connected!=NULL)
                     74:                return(xm->is_connected(xm->cbdata));
                     75:        return(TRUE);
                     76: }
                     77: 
                     78: static BOOL is_cancelled(xmodem_t* xm)
                     79: {
                     80:        if(xm->is_cancelled!=NULL)
                     81:                return(xm->cancelled=xm->is_cancelled(xm->cbdata));
                     82:        return(xm->cancelled);
                     83: }
                     84: 
                     85: static char *chr(uchar ch)
                     86: {
                     87:        static char str[25];
                     88: 
                     89:        switch(ch) {
                     90:                case SOH:       return("SOH");
                     91:                case STX:       return("STX");
                     92:                case ETX:       return("ETX");
                     93:                case EOT:       return("EOT");
                     94:                case ACK:       return("ACK");
                     95:                case NAK:       return("NAK");
                     96:                case CAN:       return("CAN");
                     97:        }
                     98:        if(ch>=' ' && ch<='~')
                     99:                sprintf(str,"'%c' (%02Xh)",ch,ch);
                    100:        else
                    101:                sprintf(str,"%u (%02Xh)",ch,ch);
                    102:        return(str); 
                    103: }
                    104: 
                    105: 
                    106: int xmodem_put_ack(xmodem_t* xm)
                    107: {
                    108:        while(getcom(0)!=NOINP && is_connected(xm))
                    109:                ;                               /* wait for any trailing data */
                    110:        return putcom(ACK);
                    111: }
                    112: 
                    113: int xmodem_put_nak(xmodem_t* xm, unsigned block_num)
                    114: {
                    115:        while(getcom(0)!=NOINP && is_connected(xm))
                    116:                ;                               /* wait for any trailing data */
                    117: 
                    118:        if(block_num<=1) {
                    119:                if(*(xm->mode)&GMODE) {         /* G for Ymodem-G */
                    120:                        lprintf(xm,LOG_INFO,"Requesting mode: Streaming, 16-bit CRC");
                    121:                        return putcom('G');
                    122:                } else if(*(xm->mode)&CRC) {    /* C for CRC */
                    123:                        lprintf(xm,LOG_INFO,"Requesting mode: 16-bit CRC");
                    124:                        return putcom('C');
                    125:                } else {                                /* NAK for checksum */
                    126:                        lprintf(xm,LOG_INFO,"Requesting mode: 8-bit Checksum");
                    127:                        return putcom(NAK);
                    128:                }
                    129:        }
                    130:        return putcom(NAK);
                    131: }
                    132: 
                    133: int xmodem_cancel(xmodem_t* xm)
                    134: {
                    135:        int i;
                    136:        int result;
                    137: 
                    138:        if(!is_cancelled(xm) && is_connected(xm)) {
                    139:                for(i=0;i<8 && is_connected(xm);i++)
                    140:                        if((result=putcom(CAN))!=0)
                    141:                                return result;
                    142:                for(i=0;i<10 && is_connected(xm);i++)
                    143:                        if((result=putcom('\b'))!=0)
                    144:                                return result;
                    145:                xm->cancelled=TRUE;
                    146:        }
                    147: 
                    148:        return 0;
                    149: }
                    150: 
                    151: /****************************************************************************/
                    152: /* Return 0 on success                                                                                                         */
                    153: /****************************************************************************/
                    154: int xmodem_get_block(xmodem_t* xm, uchar* block, unsigned expected_block_num)
                    155: {
                    156:        uchar   block_num;                                      /* Block number received in header      */
                    157:        uchar   block_inv;
                    158:        uchar   chksum,calc_chksum;
                    159:        int             i,eot=0,can=0;
                    160:        uint    b,errors;
                    161:        ushort  crc,calc_crc;
                    162: 
                    163:        for(errors=0;errors<=xm->max_errors && is_connected(xm);errors++) {
                    164: 
                    165:                i=getcom(expected_block_num<=1 ? 5 : 10);
                    166:                if(eot && i!=EOT && i!=NOINP)
                    167:                        eot=0;
                    168:                if(can && i!=CAN)
                    169:                        can=0;
                    170:                switch(i) {
                    171:                        case SOH: /* 128 byte blocks */
                    172:                                xm->block_size=128;
                    173:                                break;
                    174:                        case STX: /* 1024 byte blocks */
                    175:                                xm->block_size=1024;
                    176:                                break;
                    177:                        case EOT:
                    178:                                lprintf(xm,LOG_DEBUG,"EOT");
                    179:                                if(/*((*xm->mode)&(YMODEM|GMODE))==YMODEM &&*/ !eot) {
                    180:                                        lprintf(xm,LOG_INFO,"NAKing first EOT");
                    181:                                        eot=1;  
                    182:                                        xmodem_put_nak(xm,expected_block_num); /* chuck's double EOT trick */
                    183:                                        continue; 
                    184:                                }
                    185:                                return(EOT);
                    186:                        case CAN:
                    187:                                if(!can) {                      /* must get two CANs in a row */
                    188:                                        can=1;
                    189:                                        lprintf(xm,LOG_WARNING,"Received CAN  Expected SOH, STX, or EOT");
                    190:                                        continue; 
                    191:                                }
                    192:                                lprintf(xm,LOG_WARNING,"Cancelled remotely");
                    193:                                return(CAN);
                    194:                        default:
                    195:                                lprintf(xm,LOG_WARNING,"Received %s  Expected SOH, STX, or EOT",chr((uchar)i));
                    196:                        case NOINP:     /* Nothing came in */
                    197:                                if(eot)
                    198:                                        return(EOT);
                    199:                                return(NOINP);
                    200:                }
                    201:                if((i=getcom(xm->byte_timeout))==NOINP)
                    202:                        break; 
                    203:                block_num=i;
                    204:                if((i=getcom(xm->byte_timeout))==NOINP)
                    205:                        break; 
                    206:                block_inv=i;
                    207:                calc_crc=calc_chksum=0;
                    208:                for(b=0;b<xm->block_size && is_connected(xm);b++) {
                    209:                        if((i=getcom(xm->byte_timeout))==NOINP)
                    210:                                break;
                    211:                        block[b]=i;
                    212:                        if((*xm->mode)&CRC)
                    213:                                calc_crc=ucrc16(block[b],calc_crc);
                    214:                        else
                    215:                                calc_chksum+=block[b]; 
                    216:                }
                    217: 
                    218:                if(b<xm->block_size)
                    219:                        break; 
                    220: 
                    221:                if((*xm->mode)&CRC) {
                    222:                        crc=getcom(xm->byte_timeout)<<8;
                    223:                        crc|=getcom(xm->byte_timeout); 
                    224:                }
                    225:                else
                    226:                        chksum=getcom(xm->byte_timeout);
                    227: 
                    228:                if(block_num!=(uchar)~block_inv) {
                    229:                        lprintf(xm,LOG_WARNING,"Block number bit error (0x%02X vs 0x%02x)"
                    230:                                ,block_num,(uchar)~block_inv);
                    231:                        break; 
                    232:                }
                    233: 
                    234:                if(block_num!=(uchar)(expected_block_num&0xff)) {
                    235:                        lprintf(xm,LOG_WARNING,"Block number error (%u received, expected %u)"
                    236:                                ,block_num,expected_block_num&0xff);
                    237:                        if(expected_block_num && block_num==(uchar)((expected_block_num-1)&0xff))
                    238:                                continue;       /* silently discard repeated packets (ymodem.doc 7.3.2) */
                    239:                        break; 
                    240:                }
                    241: 
                    242:                if((*xm->mode)&CRC) {
                    243:                        if(crc!=calc_crc) {
                    244:                                lprintf(xm,LOG_WARNING,"Block %u: CRC ERROR", expected_block_num); 
                    245:                                break;
                    246:                        }
                    247:                }
                    248:                else    /* CHKSUM */
                    249:                {       
                    250:                        if(chksum!=calc_chksum) {
                    251:                                lprintf(xm,LOG_WARNING,"Block %u: CHECKSUM ERROR", expected_block_num); 
                    252:                                break;
                    253:                        }
                    254:                }
                    255:                return(0);      /* Success */
                    256:        }
                    257: 
                    258:        return(-2);             /* Failure */
                    259: }
                    260: 
                    261: /*****************/
                    262: /* Sends a block */
                    263: /*****************/
                    264: int xmodem_put_block(xmodem_t* xm, uchar* block, unsigned block_size, unsigned block_num)
                    265: {
                    266:        int             result;
                    267:        uchar   ch,chksum;
                    268:     uint       i;
                    269:        ushort  crc;
                    270: 
                    271:        if(block_size==128)
                    272:                result=putcom(SOH);
                    273:        else                    /* 1024 */
                    274:                result=putcom(STX);
                    275:        if(result!=0)
                    276:                return(result);
                    277:        ch=(uchar)(block_num&0xff);
                    278:        if((result=putcom(ch))!=0)
                    279:                return result;
                    280:        if((result=putcom((uchar)~ch))!=0)
                    281:                return result;
                    282:        chksum=0;
                    283:        crc=0;
                    284:        for(i=0;i<block_size && is_connected(xm);i++) {
                    285:                if((result=putcom(block[i]))!=0)
                    286:                        return result;
                    287:                if((*xm->mode)&CRC)
                    288:                        crc=ucrc16(block[i],crc);
                    289:                else
                    290:                        chksum+=block[i]; 
                    291:        }
                    292: 
                    293:        if((*xm->mode)&CRC) {
                    294:                if((result=     putcom((uchar)(crc >> 8)))!=0)
                    295:                        return result;
                    296:                return          putcom((uchar)(crc&0xff)); 
                    297:        }
                    298:        return putcom(chksum);
                    299: }
                    300: 
                    301: /************************************************************/
                    302: /* Gets an acknowledgement - usually after sending a block     */
                    303: /* Returns 1 if ack received, 0 otherwise.                                     */
                    304: /************************************************************/
                    305: BOOL xmodem_get_ack(xmodem_t* xm, unsigned tries, unsigned block_num)
                    306: {
                    307:        int i,can=0;
                    308:        unsigned errors;
                    309: 
                    310:        for(errors=0;errors<tries && is_connected(xm);errors++) {
                    311: 
                    312:                if((*xm->mode)&GMODE) {         /* Don't wait for ACK on Ymodem-G */
                    313:                        SLEEP(xm->g_delay);
                    314:                        if(getcom(0)==CAN) {
                    315:                                lprintf(xm,LOG_WARNING,"Block %u: !Cancelled remotely", block_num);
                    316:                                xmodem_cancel(xm);
                    317:                                return(FALSE); 
                    318:                        }
                    319:                        return(TRUE); 
                    320:                }
                    321: 
                    322:                i=getcom(xm->ack_timeout);
                    323:                if(can && i!=CAN)
                    324:                        can=0;
                    325:                if(i==ACK)
                    326:                        return(TRUE);
                    327:                if(i==CAN) {
                    328:                        if(can) {
                    329:                                lprintf(xm,LOG_WARNING,"Block %u: !Cancelled remotely", block_num);
                    330:                                xmodem_cancel(xm);
                    331:                                return(FALSE); 
                    332:                        }
                    333:                        can=1; 
                    334:                }
                    335:                if(i!=NOINP) {
                    336:                        lprintf(xm,LOG_WARNING,"Block %u: !Received %s  Expected ACK"
                    337:                                ,block_num, chr((uchar)i));
                    338:                        if(i!=CAN)
                    339:                                return(FALSE); 
                    340:                } 
                    341:        }
                    342: 
                    343:        return(FALSE);
                    344: }
                    345: 
                    346: BOOL xmodem_get_mode(xmodem_t* xm)
                    347: {
                    348:        int                     i;
                    349:        unsigned        errors;
                    350:        unsigned        can;
                    351: 
                    352:        lprintf(xm,LOG_INFO,"Waiting for transfer mode request...");
                    353: 
                    354:        *(xm->mode)&=~(GMODE|CRC);
                    355:        for(errors=can=0;errors<=xm->max_errors && is_connected(xm);errors++) {
                    356:                i=getcom(xm->recv_timeout);
                    357:                if(can && i!=CAN)
                    358:                        can=0;
                    359:                switch(i) {
                    360:                        case NAK:               /* checksum */
                    361:                                lprintf(xm,LOG_INFO,"Receiver requested mode: 8-bit Checksum");
                    362:                                return(TRUE); 
                    363:                        case 'C':
                    364:                                lprintf(xm,LOG_INFO,"Receiver requested mode: 16-bit CRC");
                    365:                                *(xm->mode)|=CRC;
                    366:                                return(TRUE); 
                    367:                        case 'G':
                    368:                                lprintf(xm,LOG_INFO,"Receiver requested mode: Streaming, 16-bit CRC");
                    369:                                *(xm->mode)|=(GMODE|CRC);
                    370:                                return(TRUE); 
                    371:                        case CAN:
                    372:                                if(can) {
                    373:                                        lprintf(xm,LOG_WARNING,"Cancelled remotely");
                    374:                                        return(FALSE); 
                    375:                                }
                    376:                                can=1; 
                    377:                                break;
                    378:                        case NOINP:
                    379:                                break;
                    380:                        default:
                    381:                                lprintf(xm,LOG_WARNING,"Received %s  Expected NAK, C, or G"
                    382:                                        ,chr((uchar)i));
                    383:                                break;
                    384:                } 
                    385:        }
                    386: 
                    387:        lprintf(xm,LOG_ERR,"Failed to get transfer mode request from receiver");
                    388:        return(FALSE);
                    389: }
                    390: 
                    391: BOOL xmodem_put_eot(xmodem_t* xm)
                    392: {
                    393:        int ch;
                    394:        unsigned errors;
                    395:        unsigned cans=0;
                    396: 
                    397:        for(errors=0;errors<=xm->max_errors && is_connected(xm);errors++) {
                    398: 
                    399:                lprintf(xm,LOG_INFO,"Sending End-of-Text (EOT) indicator (%d)",errors+1);
                    400: 
                    401:                while((ch=getcom(0))!=NOINP && is_connected(xm))
                    402:                        lprintf(xm,LOG_INFO,"Throwing out received: %s",chr((uchar)ch));
                    403: 
                    404:                putcom(EOT);
                    405:                if((ch=getcom(xm->recv_timeout))==NOINP)
                    406:                        continue;
                    407:                lprintf(xm,LOG_INFO,"Received %s",chr((uchar)ch)); 
                    408:                if(ch==ACK)
                    409:                        return(TRUE);
                    410:                if(ch==CAN && ++cans>1)
                    411:                        break;
                    412:                if(ch==NAK && errors==0 && (*(xm->mode)&(YMODEM|GMODE))==YMODEM) {
                    413:                        continue;  /* chuck's double EOT trick so don't complain */
                    414:                }
                    415:                lprintf(xm,LOG_WARNING,"Expected ACK");
                    416:        }
                    417:        return(FALSE);
                    418: }
                    419: 
                    420: BOOL xmodem_send_file(xmodem_t* xm, const char* fname, FILE* fp, time_t* start, ulong* sent)
                    421: {
                    422:        BOOL            success=FALSE;
                    423:        ulong           sent_bytes=0;
                    424:        char            block[1024];
                    425:        size_t          block_len;
                    426:        unsigned        block_num;
                    427:        size_t          i;
                    428:        size_t          rd;
                    429:        time_t          startfile;
                    430:        struct          stat st;
                    431: 
                    432:        if(sent!=NULL)  
                    433:                *sent=0;
                    434: 
                    435:        if(start!=NULL)         
                    436:                *start=time(NULL);
                    437: 
                    438:        fstat(fileno(fp),&st);
                    439: 
                    440:        if(xm->total_files==0)
                    441:                xm->total_files=1;
                    442: 
                    443:        if(xm->total_bytes==0)
                    444:                xm->total_bytes=st.st_size;
                    445: 
                    446:        do {
                    447:        /* try */
                    448:                if(*(xm->mode)&YMODEM) {
                    449: 
                    450:                        if(!xmodem_get_mode(xm))
                    451:                                break;
                    452: 
                    453:                        memset(block,0,sizeof(block));
                    454:                        SAFECOPY(block,getfname(fname));
                    455:                        i=sprintf(block+strlen(block)+1,"%lu %lo 0 0 %d %ld"
                    456:                                ,(ulong)st.st_size
                    457:                                ,st.st_mtime
                    458:                                ,xm->total_files-xm->sent_files
                    459:                                ,xm->total_bytes-xm->sent_bytes);
                    460:                        
                    461:                        lprintf(xm,LOG_INFO,"Sending Ymodem header block: '%s'",block+strlen(block)+1);
                    462:                        
                    463:                        block_len=strlen(block)+1+i;
                    464:                        for(xm->errors=0;xm->errors<=xm->max_errors && !is_cancelled(xm) && is_connected(xm);xm->errors++) {
                    465:                                xmodem_put_block(xm, block, block_len <=128 ? 128:1024, 0  /* block_num */);
                    466:                                if(xmodem_get_ack(xm,1,0))
                    467:                                        break; 
                    468:                        }
                    469:                        if(xm->errors>=xm->max_errors || is_cancelled(xm)) {
                    470:                                lprintf(xm,LOG_ERR,"Failed to send header block");
                    471:                                break;
                    472:                        }
                    473:                }
                    474: 
                    475:                if(!xmodem_get_mode(xm))
                    476:                        break;
                    477: 
                    478:                startfile=time(NULL);   /* reset time, don't count header block */
                    479:                if(start!=NULL)
                    480:                        *start=startfile;
                    481: 
                    482:                block_num=1;
                    483:                xm->errors=0;
                    484:                while(sent_bytes < (ulong)st.st_size && xm->errors<=xm->max_errors && !is_cancelled(xm)
                    485:                        && is_connected(xm)) {
                    486:                        fseek(fp,sent_bytes,SEEK_SET);
                    487:                        memset(block,CPMEOF,xm->block_size);
                    488:                        if((rd=fread(block,1,xm->block_size,fp))!=xm->block_size 
                    489:                                && (long)(block_num*xm->block_size) < st.st_size) {
                    490:                                lprintf(xm,LOG_ERR,"READ ERROR %d instead of %d at offset %lu"
                    491:                                        ,rd,xm->block_size,(block_num-1)*(long)xm->block_size);
                    492:                                xm->errors++;
                    493:                                continue;
                    494:                        }
                    495:                        if(xm->progress!=NULL)
                    496:                                xm->progress(xm->cbdata,block_num,ftell(fp),st.st_size,startfile);
                    497:                        xmodem_put_block(xm, block, xm->block_size, block_num);
                    498:                        if(!xmodem_get_ack(xm,5,block_num)) {
                    499:                                xm->errors++;
                    500:                                lprintf(xm,LOG_WARNING,"Error #%d at offset %ld"
                    501:                                        ,xm->errors,ftell(fp)-xm->block_size);
                    502:                        } else {
                    503:                                block_num++; 
                    504:                                sent_bytes+=rd;
                    505:                        }
                    506:                }
                    507:                if(sent_bytes >= (ulong)st.st_size && !is_cancelled(xm) && is_connected(xm)) {
                    508: 
                    509:        #if 0 /* !SINGLE_THREADED */
                    510:                        lprintf(LOG_DEBUG,"Waiting for output buffer to empty... ");
                    511:                        if(WaitForEvent(outbuf_empty,5000)!=WAIT_OBJECT_0)
                    512:                                lprintf(xm,LOG_WARNING,"FAILURE");
                    513:        #endif
                    514:                        if(xmodem_put_eot(xm))  /* end-of-text, wait for ACK */
                    515:                                success=TRUE;
                    516:                }
                    517:        } while(0);
                    518:        /* finally */
                    519: 
                    520:        if(!success)
                    521:                xmodem_cancel(xm);
                    522: 
                    523:        if(sent!=NULL)
                    524:                *sent=sent_bytes;
                    525: 
                    526:        return(success);
                    527: }
                    528: 
                    529: 
                    530: const char* xmodem_source(void)
                    531: {
                    532:        return(__FILE__);
                    533: }
                    534: 
                    535: char* xmodem_ver(char *buf)
                    536: {
                    537:        sscanf("$Revision: 1.26 $", "%*s %s", buf);
                    538: 
                    539:        return(buf);
                    540: }
                    541: 
                    542: void xmodem_init(xmodem_t* xm, void* cbdata, long* mode
                    543:                                ,int    (*lputs)(void*, int level, const char* str)
                    544:                                ,void   (*progress)(void* unused, unsigned block_num, ulong offset, ulong fsize, time_t t)
                    545:                                ,int    (*send_byte)(void*, uchar ch, unsigned timeout)
                    546:                                ,int    (*recv_byte)(void*, unsigned timeout)
                    547:                                ,BOOL   (*is_connected)(void*)
                    548:                                ,BOOL   (*is_cancelled)(void*))
                    549: {
                    550:        memset(xm,0,sizeof(xmodem_t));
                    551: 
                    552:        /* Use sane default values */
                    553:        xm->send_timeout=10;            /* seconds */
                    554:        xm->recv_timeout=10;            /* seconds */
                    555:        xm->byte_timeout=3;                     /* seconds */
                    556:        xm->ack_timeout=10;                     /* seconds */
                    557: 
                    558:        xm->block_size=1024;
                    559:        xm->max_errors=9;
                    560:        xm->g_delay=1;
                    561: 
                    562:        xm->cbdata=cbdata;
                    563:        xm->mode=mode;
                    564:        xm->lputs=lputs;
                    565:        xm->progress=progress;
                    566:        xm->send_byte=send_byte;
                    567:        xm->recv_byte=recv_byte;
                    568:        xm->is_connected=is_connected;
                    569:        xm->is_cancelled=is_cancelled;
                    570: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.