/* File: C:\WACKER\xfer\hpr_snd1.c (Created: 26-Jan-1994) * created from HAWIN source file * hpr_snd1.c -- Routines to provide HyperProtocol file send function in * HyperACCESS. * * Copyright 1989,1994 by Hilgraeve Inc. -- Monroe, MI * All rights reserved * * $Revision: 1 $ * $Date: 10/05/98 1:16p $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(BYTE) #define BYTE unsigned char #endif #include "itime.h" #include "cmprs.h" #include "xfr_dsp.h" #include "xfr_todo.h" #include "xfr_srvc.h" #include "xfer.h" #include "xfer.hh" #include "xfer_tsc.h" #include "hpr.h" #include "hpr.hh" #include "hpr_sd.hh" /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * hs_namecheck * * DESCRIPTION: * This function is called to check the extension on a filename and to guess * if it should be compressed or not. * * ARGUEMENTS: * ft -- struct s_ftbl *, contains the name and gets the flag value * * RETURNS: * Nothing, but may set flags in the structure passed as arguement */ void hs_namecheck(struct s_ftbl *ft) { BYTE *ptr; if (strlen(ft->fname) < 5) return; ptr = ft->fname + strlen(ft->fname) - 4; if (*ptr++ != '.') return; /* the extensions we are currently checking for are: * ARC * LZH * PAK * ZIP * ZOO */ #if FALSE /* TODO: replace with something else ANSI compatible */ if ((strnicmp(ptr, "ARC", 3) == 0) || (strnicmp(ptr, "LZH", 3) == 0) || (strnicmp(ptr, "PAK", 3) == 0) || (strnicmp(ptr, "ZIP", 3) == 0) || (strnicmp(ptr, "ZOO", 3) == 0)) bitset(ft->cntrl, FTC_DONT_CMPRS); #endif } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * hs_filebreak * * DESCRIPTION: * Handles changes to the currently open file. This routine is called whenever * the data loading routine gets an EOF while attempting to load the next * data character. This can happen when the program first starts or when * a file is exhausted or when a file error is encountered. It can also * occur artificially when, for example, a restart is requested to force * a change in file or file position. * * ARGUMENTS: * nfiles -- Total number of files expected to be sent during this transfer. * This is only used in the initial (file 0) control message. * nbytes -- Total number of bytes expected to be sent during this transfer. * This is only used in the initial (file 0) control message. * * RETURNS: * nothing */ void hs_filebreak(struct s_hc *hc, int nfiles, long nbytes) { BYTE str[90]; BYTE name_to_send[FNAME_LEN]; struct s_ftbl *ft; /* we've either hit a real EOF, or the restart routine has backed us up into an aborted file, or we're just starting, or we've encountered a file error, or user cancelled. */ omsg_new(hc, 'C'); if (hc->datacnt > 0) { /* put partial block record in message */ wsprintf(str, "P%u,%u", hc->datacnt, (hc->usecrc ? hc->h_crc : hc->h_checksum)); omsg_add(hc, str); hc->datacnt = hc->h_crc = hc->h_checksum = 0; } if (hc->ucancel || hc->sc.rmtcancel) { hpr_id_get(hc, str); omsg_add(hc, str); omsg_add(hc, "X"); hs_waitack(hc); } if (hc->current_filen == 0) { /* just getting started or need to start over */ /* put 'V', 'B', 'D', 'N', and 'S' records in message */ hpr_id_get(hc, str); /* add 'V' field */ omsg_add(hc, str); wsprintf(str, "B%d;D%d;N%u;S%lu", hc->blocksize, (int)(hc->deadmantime / 10), nfiles, nbytes); omsg_add(hc, str); } else { #if FALSE /* TODO: put this back in later */ if (fio_error(hc->fhdl)) { /* print error message on the screen */ hc->sc.hs_ftbl[hc->sc.ft_current].status = TSC_DISK_ERROR; } #endif if (hc->sc.hs_ftbl[hc->sc.ft_current].status != TSC_OK) { strcpy(str, "A"); omsg_add(hc, str); } } /* scan up the file table looking for first non-cancelled file or * for top of table */ while (++hc->sc.ft_current <= hc->sc.ft_top && hc->sc.hs_ftbl[hc->sc.ft_current].status != TSC_OK) { xfer_name_to_send(hc->hSession, hc->sc.hs_ftbl[hc->sc.ft_current].fname, name_to_send); wsprintf(str, "F%u,%s", hc->current_filen = hc->sc.hs_ftbl[hc->sc.ft_current].filen, name_to_send); omsg_add(hc, str); /* put size field in so rcvr can keep display accurate */ wsprintf(str, "s%lu", hc->sc.hs_ftbl[hc->sc.ft_current].flength); omsg_add(hc, str); omsg_send(hc, 1, hc->usecrc, FALSE); /* now that we've started this file, tell receiver to abort it * right away since its status is no longer TSC_OK */ omsg_new(hc, 'C'); strcpy(str, "A"); omsg_add(hc, str); } /* Now we should be ready for next file. We're either pointing at a * normal file in the table to be restarted or we're at the top * of the table and its time to open the next file */ hc->sc.hs_ptrgetc = hs_getc; /* set pointer into table for speed */ ft = &hc->sc.hs_ftbl[hc->sc.ft_current]; if (hc->sc.ft_current <= hc->sc.ft_top) { /* try to reopen a file from the table */ hc->current_filen = ft->filen; if (hc->fhdl != NULL && hc->sc.ft_current == hc->sc.ft_open) { /* file is already open */ fio_seek(hc->fhdl, 0, FIO_SEEK_SET); } else { /* reopen previously opened file */ if (hc->fhdl) { fio_close(hc->fhdl); } hc->fhdl = NULL; hc->sc.ft_open = hc->sc.ft_current; hc->current_filen = ft->filen; hc->h_filebytes = hc->sc.bytes_sent = 0L; hc->total_dsp = ft->dsp_bytes; hc->total_thru = ft->thru_bytes; if (xfer_opensendfile(hc->hSession, &hc->fhdl, ft->fname, &ft->flength, NULL, NULL) != 0) { /* display error */ ft->status = TSC_CANT_OPEN; hc->sc.hs_ptrgetc = hs_reteof; hc->sc.ft_open = -1; } } } else if (xfer_nextfile(hc->hSession, name_to_send)) { /* There are no more previously started files in the table to be * restarted, but there is another brand new file to send */ if (hc->sc.ft_top >= hc->sc.ft_limit) { hsdsp_event(hc, HSE_FULL); hsdsp_status(hc, HSS_WAITACK); omsg_add(hc, "I"); omsg_send(hc, 1, hc->usecrc, FALSE); omsg_new(hc, 'C'); HS_XMIT_FLUSH(hc); while(hc->sc.ft_top >= hc->sc.ft_limit) { hs_background(hc); /* wait for space in table */ hs_logx(hc, FALSE); } hsdsp_event(hc, HSE_GOTACK); hsdsp_status(hc, HSS_SENDING); /* items may shift in hs_ftbl during hs_background */ ft = &hc->sc.hs_ftbl[hc->sc.ft_current]; } /* open next file and install it in the table */ if (hc->fhdl) { fio_close(hc->fhdl); } hc->fhdl = NULL; strcpy(ft->fname, name_to_send); ft->filen = ++hc->current_filen; ft->cntrl = 0; ft->status = TSC_OK; hs_namecheck(ft); hc->total_dsp = ft->dsp_bytes = hc->sc.hs_ftbl[hc->sc.ft_top].dsp_bytes + hc->sc.hs_ftbl[hc->sc.ft_top].flength; hc->total_thru = ft->thru_bytes = hc->sc.hs_ftbl[hc->sc.ft_top].thru_bytes + hc->h_filebytes; hc->sc.ft_top = hc->sc.ft_open = hc->sc.ft_current; hc->h_filebytes = hc->sc.bytes_sent = 0; if (xfer_opensendfile(hc->hSession, &hc->fhdl, ft->fname, &ft->flength, NULL, NULL) != 0) { /* display error? */ hc->fhdl = NULL; ft->status = TSC_CANT_OPEN; hc->sc.ft_open = -1; hc->sc.hs_ptrgetc = hs_reteof; } } else /* no more files in table and no new files to send */ { if (hc->fhdl) { fio_close(hc->fhdl); } hc->sc.ft_open = -1; hc->fhdl = NULL; --hc->sc.ft_current; /* don't point off top of table */ hsdsp_event(hc, HSE_DONE); omsg_add(hc, "E"); hs_waitack(hc); } /* put file name and attribute records in message */ xfer_name_to_send(hc->hSession, ft->fname, name_to_send); wsprintf(str, "F%d,%s", ft->filen, name_to_send); omsg_add(hc, str); wsprintf(str, "s%lu", ft->flength); omsg_add(hc, str); if (hc->fhdl) { unsigned long ftime; struct tm *pT; ftime = itimeGetFileTime(ft->fname); ftime += itimeGetBasetime(); /* fudge for C 7 and later */ pT = localtime(&ftime); wsprintf(str, "t%d,%d,%d,%d,%d,%d", pT->tm_year + 1900, pT->tm_mon, pT->tm_mday, pT->tm_hour, pT->tm_min, pT->tm_sec); omsg_add(hc, str); #if FALSE if (hc->sc.rmt_compress && h_trycompress && !bittest(ft->cntrl, FTC_DONT_CMPRS) && ft->flength > CMPRS_MINSIZE && compress_start(&hc->sc.hs_ptrgetc, &h_filebytes, FALSE)) #endif if (hc->sc.rmt_compress && hc->h_trycompress && !bittest(ft->cntrl, FTC_DONT_CMPRS) && ft->flength > CMPRS_MINSIZE && compress_start(&hc->sc.hs_ptrgetc, hc, &hc->h_filebytes, FALSE)) { hsdsp_compress(hc, ON); /* hsdsp_status(hc, HSS_SENDING); */ omsg_add(hc, "C"); bitset(ft->cntrl, FTC_COMPRESSED); } else { hsdsp_compress(hc, OFF); bitclear(ft->cntrl, FTC_COMPRESSED); } } omsg_send(hc, 1, hc->usecrc, FALSE); HS_XMIT_FLUSH(hc); hsdsp_newfile(hc, ft->filen, ft->fname, ft->flength); hsdsp_progress(hc, 0); } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * hs_waitack * * DESCRIPTION: * Called at the end of a transfer to wait for the final acknowldgement from * receiver or until a restart message forces us to back up and retransmit * some of the data. This routine never returns directly to its caller. It * always exits by way of a longjmp either during a hs_background call if * an 'END' or 'RESTART' message is received or at the end of the function * if it times out without receiving either message. * * ARGUMENTS: * none * * RETURNS: * nothing (see DESCRIPTION) */ void hs_waitack(struct s_hc *hc) { long timer; omsg_send(hc, 1, hc->usecrc, FALSE); hc->sc.receiver_timedout = FALSE; HS_XMIT_FLUSH(hc); hsdsp_status(hc, HSS_WAITACK); timer = startinterval(); /* wait for 60 seconds, until longjmp either restarts or ends transfer */ while (interval(timer) < FINAL_ACK_WAIT) { hs_background(hc); if (hc->sc.receiver_timedout) { /* receiver must have missed our end of transfer message */ omsg_send(hc, 1, hc->usecrc, FALSE); HS_XMIT_FLUSH(hc); hc->sc.receiver_timedout = FALSE; } } /* if we get here, we didn't get response from receiver for 60 seconds */ hsdsp_event(hc, HSE_NORESP); longjmp(hc->sc.jb_bailout, TSC_NO_RESPONSE); } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * hs_decode_rmsg * * DESCRIPTION: * Called by hs_rcvmsg when a complete message has been received. This routine * parses and interprets the message either by recording information sent by * the receiver or by altering the course of the transfer in response to an * interruption message. There may be several fields in a message but note * that certain messages transfer control elsewhere via a longjmp and thus * prevent any subsequent fields from being scanned ('E', 'X', and 'R' * messages specifically). All messages from the receiver to the sender are * sent in printable characters. * * ARGUMENTS: * data -- A string containing the messages to be interpreted. Each message * is in the form of: 'T;' * where T is a single character message identifier and is * optional associated information. * * RETURNS: * nothing * (exits via a longjmp when certain interruption messages are received) */ void hs_decode_rmsg(struct s_hc *hc, BYTE *data) { BYTE *field; int filenum; int mnum; int tval; if (data[0] != 'c') /* this is the only kind we know about now but don't */ return; /* consider it an error if newer versions send */ /* other types */ mnum = unchar(data[2]); field = strtok(&data[3], ";"); while (field != NULL) { switch(*field++) { case 'f': /* receiver has successfully received file fn */ hs_fileack(hc, atoi(field)); break; case 'B': /* receiver is requesting a particular block size */ if (hc->current_filen == 0) { tval = atoi(field); tval = max(tval, H_MINBLOCK); hc->blocksize = min(hc->blocksize, tval); } break; case 'D': /* receiver is requesting a particular deadman time */ tval = atoi(field); tval = max(tval, H_MINDEADMAN); hc->deadmantime = min(hc->deadmantime, tval * 10); break; case 'T': /* receiver is requesting check type */ hc->sc.rmtchkt = atoi(field); break; case 'C': /* receiver is agreeing to do compression */ if (!*field) hc->sc.rmt_compress = TRUE; break; case 'E': /* receiver is acknowledging end of transfer */ hc->xfertime = interval(hc->xfertimer); if (*field) hs_fileack(hc, atoi(field)); hsdsp_event(hc, HSE_GOTACK); if (!hc->ucancel && !hc->sc.rmtcancel) longjmp(hc->sc.jb_bailout, TSC_COMPLETE); else longjmp(hc->sc.jb_bailout, hc->ucancel ? TSC_USER_CANNED : TSC_RMT_CANNED); break; case 'X': /* receiver is requesting a cancellation of the xfer */ if (*field) hs_fileack(hc, atoi(field)); hsdsp_event(hc, HSE_RMTCANCEL); hc->sc.rmtcancel = TRUE; hc->sc.hs_ptrgetc = hs_reteof; break; case 'A': /* receiver is aborting a single file */ hs_fileack(hc, filenum = atoi(field)); /* all earlier files ok */ /* If we're still sending the file being cancelled, mark it * cancelled and set the character function pointer to hs_reteof * to force a switch to the next file, if any. * If we've already finished sending the file being cancelled, * look it up in the file table and mark if cancelled so that * if we're asked to back up into it, we won't bother resending * it */ if (hc->current_filen == filenum) { hc->sc.hs_ftbl[hc->sc.ft_current].status = TSC_RMT_CANNED; hc->sc.hs_ptrgetc = hs_reteof; /* force switch to next file */ } else { /* we're no longer sending file being canned */ for (tval = hc->sc.ft_current; tval >= 0; --tval) if (hc->sc.hs_ftbl[tval].filen == filenum) hc->sc.hs_ftbl[tval].status = TSC_RMT_CANNED; } break; case 'V': tval = atoi(field); field = strchr(field, ','); /* if version restrictions were found, stop the transfer */ if (!hpr_id_check(hc, tval, ++field)) { hsdsp_event(hc, HSE_RMTCANCEL); hc->sc.rmtcancel = TRUE; hc->sc.hs_ptrgetc = hs_reteof; } break; case 'R': /* receiver is requesting a restart */ if (!hc->sc.started) { hsdsp_event(hc, HSE_GOTSTART); hc->sc.started = TRUE; } else { hsdsp_event(hc, HSE_GOTRETRY); hsdsp_retries(hc, ++hc->total_tries); } tval = atoi(strtok(field, ",")); /* get file number */ hs_fileack(hc, tval); hs_dorestart(hc, tval, atoi(strtok(NULL, ",")), mnum, FALSE); break; case 't': /* receiver stopped receiving data */ hc->sc.receiver_timedout = TRUE; break; } field = strtok(NULL, ";"); } } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * hs_fileack * * DESCRIPTION: * Marks all transmitted files up to a specified file number as acknowledged, * that is, as transmitted completely. This routine merely marks the files * in the file table as complete; completed entries will be removed from the * table later by hs_logx. * * ARGUMENTS: * n -- The file number that the receiver considers current. All file numbers * lower than this can be considered complete. * * RETURNS: * nothing */ void hs_fileack(struct s_hc *hc, int n) { register int ix = 0; while (ix <= hc->sc.ft_current) { if (ix <= hc->sc.ft_limit && hc->sc.hs_ftbl[ix].filen < n) bitset(hc->sc.hs_ftbl[ix].cntrl, FTC_CONFIRMED); ++ix; } } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * hs_logx * * DESCRIPTION: * Logs either all acknowledged transfers or all transfers to the log file * and removes their entries from the file table. Note that the values of * hc->sc.ft_top, hc->sc.ft_current, and hc->sc.ft_open can change during this * operation. * * ARGUMENTS: * all -- if TRUE, log all regardless of confirmation * * RETURNS: * nothing */ void hs_logx(struct s_hc *hc, int all) { struct s_ftbl *ftptr = &hc->sc.hs_ftbl[0]; while (hc->sc.ft_top >= 0 && (all || (hc->sc.ft_current > 1 && bittest(hc->sc.hs_ftbl[0].cntrl,FTC_CONFIRMED)))) { if (ftptr->filen > 0) { if (ftptr->status == TSC_OK && !bittest(ftptr->cntrl,FTC_CONFIRMED)) ftptr->status = TSC_ERROR_LIMIT; xfer_log_xfer(hc->hSession, TRUE, ftptr->fname, NULL, ftptr->status); } MemCopy((char *)&hc->sc.hs_ftbl[0], (char *)&hc->sc.hs_ftbl[1], (unsigned)hc->sc.ft_top * sizeof(struct s_ftbl)); --hc->sc.ft_top; --hc->sc.ft_current; --hc->sc.ft_open; } } /************************** end of hpr_snd1.c ************************/