| /* |
| lsz - send files with x/y/zmodem |
| Copyright (C) until 1988 Chuck Forsberg (Omen Technology INC) |
| Copyright (C) 1994 Matt Porter, Michael D. Black |
| Copyright (C) 1996, 1997 Uwe Ohse |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
| 02111-1307, USA. |
| |
| originally written by Chuck Forsberg |
| */ |
| #include "zglobal.h" |
| |
| /* char *getenv(); */ |
| |
| #define SS_NORMAL 0 |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <signal.h> |
| #include <setjmp.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <getopt.h> |
| |
| #ifndef R_OK |
| # define R_OK 4 |
| #endif |
| |
| #if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MMAP) |
| # include <sys/mman.h> |
| size_t mm_size; |
| void *mm_addr=NULL; |
| #else |
| # undef HAVE_MMAP |
| #endif |
| #include "timing.h" |
| #include "long-options.h" |
| #include "xstrtoul.h" |
| #include "error.h" |
| |
| #ifndef STRICT_PROTOTYPES |
| extern time_t time(); |
| extern char *strerror(); |
| #endif |
| |
| #ifndef HAVE_ERRNO_DECLARATION |
| extern int errno; |
| #endif |
| |
| unsigned Baudrate=2400; /* Default, should be set by first mode() call */ |
| unsigned Txwindow; /* Control the size of the transmitted window */ |
| unsigned Txwspac; /* Spacing between zcrcq requests */ |
| unsigned Txwcnt; /* Counter used to space ack requests */ |
| size_t Lrxpos; /* Receiver's last reported offset */ |
| int errors; |
| enum zm_type_enum protocol; |
| int under_rsh=FALSE; |
| extern int turbo_escape; |
| static int no_unixmode; |
| |
| int Canseek=1; /* 1: can; 0: only rewind, -1: neither */ |
| |
| static int zsendfile __P ((struct zm_fileinfo *zi, const char *buf, size_t blen)); |
| static int getnak __P ((void)); |
| static int wctxpn __P ((struct zm_fileinfo *)); |
| static int wcs __P ((const char *oname, const char *remotename)); |
| static size_t zfilbuf __P ((struct zm_fileinfo *zi)); |
| static size_t filbuf __P ((char *buf, size_t count)); |
| static int getzrxinit __P ((void)); |
| static int calc_blklen __P ((long total_sent)); |
| static int sendzsinit __P ((void)); |
| static int wctx __P ((struct zm_fileinfo *)); |
| static int zsendfdata __P ((struct zm_fileinfo *)); |
| static int getinsync __P ((struct zm_fileinfo *, int flag)); |
| static void countem __P ((int argc, char **argv)); |
| static void chkinvok __P ((const char *s)); |
| static void usage __P ((int exitcode, const char *what)); |
| static int zsendcmd __P ((const char *buf, size_t blen)); |
| static void saybibi __P ((void)); |
| static int wcsend __P ((int argc, char *argp[])); |
| static int wcputsec __P ((char *buf, int sectnum, size_t cseclen)); |
| static void usage1 __P ((int exitcode)); |
| |
| #ifdef ENABLE_SYSLOG |
| #define DO_SYSLOG(message) do { \ |
| if (enable_syslog) { \ |
| const char *shortname; \ |
| if (!zi->fname) \ |
| shortname="no.name"; \ |
| else { \ |
| shortname=strrchr(zi->fname,'/'); \ |
| if (!shortname) \ |
| shortname=zi->fname; \ |
| else \ |
| shortname++; \ |
| } \ |
| lsyslog message ; \ |
| } \ |
| } while(0) |
| #else |
| #define DO_SYSLOG(message) do { } while(0) |
| #endif |
| |
| #define ZSDATA(x,y,z) \ |
| do { if (Crc32t) {zsda32(x,y,z); } else {zsdata(x,y,z);}} while(0) |
| #ifdef HAVE_MMAP |
| #define DATAADR (mm_addr ? ((char *)mm_addr)+zi->bytes_sent : txbuf) |
| #else |
| #define DATAADR (txbuf) |
| #endif |
| |
| int Filesleft; |
| long Totalleft; |
| size_t buffersize=16384; |
| #ifdef HAVE_MMAP |
| int use_mmap=1; |
| #endif |
| |
| /* |
| * Attention string to be executed by receiver to interrupt streaming data |
| * when an error is detected. A pause (0336) may be needed before the |
| * ^C (03) or after it. |
| */ |
| #ifdef READCHECK |
| char Myattn[] = { 0 }; |
| #else |
| char Myattn[] = { 03, 0336, 0 }; |
| #endif |
| |
| FILE *input_f; |
| |
| #define MAX_BLOCK 8192 |
| char *txbuf; |
| |
| long vpos = 0; /* Number of bytes read from file */ |
| |
| char Lastrx; |
| char Crcflg; |
| int Verbose=0; |
| int Restricted=0; /* restricted; no /.. or ../ in filenames */ |
| int Quiet=0; /* overrides logic that would otherwise set verbose */ |
| int Ascii=0; /* Add CR's for brain damaged programs */ |
| int Fullname=0; /* transmit full pathname */ |
| int Unlinkafter=0; /* Unlink file after it is sent */ |
| int Dottoslash=0; /* Change foo.bar.baz to foo/bar/baz */ |
| int firstsec; |
| int errcnt=0; /* number of files unreadable */ |
| size_t blklen=128; /* length of transmitted records */ |
| int Optiong; /* Let it rip no wait for sector ACK's */ |
| int Totsecs; /* total number of sectors this file */ |
| int Filcnt=0; /* count of number of files opened */ |
| int Lfseen=0; |
| unsigned Rxbuflen = 16384; /* Receiver's max buffer length */ |
| unsigned Tframlen = 0; /* Override for tx frame length */ |
| unsigned blkopt=0; /* Override value for zmodem blklen */ |
| int Rxflags = 0; |
| int Rxflags2 = 0; |
| size_t bytcnt; |
| int Wantfcs32 = TRUE; /* want to send 32 bit FCS */ |
| char Lzconv; /* Local ZMODEM file conversion request */ |
| char Lzmanag; /* Local ZMODEM file management request */ |
| int Lskipnocor; |
| char Lztrans; |
| char zconv; /* ZMODEM file conversion request */ |
| char zmanag; /* ZMODEM file management request */ |
| char ztrans; /* ZMODEM file transport request */ |
| int command_mode; /* Send a command, then exit. */ |
| int Cmdtries = 11; |
| int Cmdack1; /* Rx ACKs command, then do it */ |
| int Exitcode; |
| int enable_timesync=0; |
| size_t Lastsync; /* Last offset to which we got a ZRPOS */ |
| int Beenhereb4; /* How many times we've been ZRPOS'd same place */ |
| |
| int no_timeout=FALSE; |
| size_t max_blklen=1024; |
| size_t start_blklen=0; |
| int zmodem_requested; |
| time_t stop_time=0; |
| char *tcp_server_address=0; |
| int tcp_socket=-1; |
| int tcp_flag=0; |
| |
| int error_count; |
| #define OVERHEAD 18 |
| #define OVER_ERR 20 |
| |
| #define MK_STRING(x) #x |
| |
| #ifdef ENABLE_SYSLOG |
| # if defined(ENABLE_SYSLOG_FORCE) || defined(ENABLE_SYSLOG_DEFAULT) |
| int enable_syslog=TRUE; |
| # else |
| int enable_syslog=FALSE; |
| # endif |
| #endif |
| |
| jmp_buf intrjmp; /* For the interrupt on RX CAN */ |
| |
| static long min_bps; |
| static long min_bps_time; |
| |
| static int io_mode_fd=0; |
| static int zrqinits_sent=0; |
| static int play_with_sigint=0; |
| |
| /* called by signal interrupt or terminate to clean things up */ |
| RETSIGTYPE |
| bibi (int n) |
| { |
| canit(STDOUT_FILENO); |
| fflush (stdout); |
| io_mode (io_mode_fd,0); |
| if (n == 99) |
| error (0, 0, _ ("io_mode(,2) in rbsb.c not implemented\n")); |
| else |
| error (0, 0, _ ("caught signal %d; exiting"), n); |
| if (n == SIGQUIT) |
| abort (); |
| exit (128 + n); |
| } |
| |
| /* Called when ZMODEM gets an interrupt (^C) */ |
| static RETSIGTYPE |
| onintr(int n) |
| { |
| signal(SIGINT, SIG_IGN); |
| n++; /* use it */ |
| longjmp(intrjmp, -1); |
| } |
| |
| int Zctlesc; /* Encode control characters */ |
| const char *program_name = "sz"; |
| int Zrwindow = 1400; /* RX window size (controls garbage count) */ |
| |
| static struct option const long_options[] = |
| { |
| {"append", no_argument, NULL, '+'}, |
| {"twostop", no_argument, NULL, '2'}, |
| {"try-8k", no_argument, NULL, '8'}, |
| {"start-8k", no_argument, NULL, '9'}, |
| {"try-4k", no_argument, NULL, '4'}, |
| {"start-4k", no_argument, NULL, '5'}, |
| {"ascii", no_argument, NULL, 'a'}, |
| {"binary", no_argument, NULL, 'b'}, |
| {"bufsize", required_argument, NULL, 'B'}, |
| {"cmdtries", required_argument, NULL, 'C'}, |
| {"command", required_argument, NULL, 'c'}, |
| {"immediate-command", required_argument, NULL, 'i'}, |
| {"dot-to-slash", no_argument, NULL, 'd'}, |
| {"full-path", no_argument, NULL, 'f'}, |
| {"escape", no_argument, NULL, 'e'}, |
| {"rename", no_argument, NULL, 'E'}, |
| {"help", no_argument, NULL, 'h'}, |
| {"crc-check", no_argument, NULL, 'H'}, |
| {"1024", no_argument, NULL, 'k'}, |
| {"1k", no_argument, NULL, 'k'}, |
| {"packetlen", required_argument, NULL, 'L'}, |
| {"framelen", required_argument, NULL, 'l'}, |
| {"min-bps", required_argument, NULL, 'm'}, |
| {"min-bps-time", required_argument, NULL, 'M'}, |
| {"newer", no_argument, NULL, 'n'}, |
| {"newer-or-longer", no_argument, NULL, 'N'}, |
| {"16-bit-crc", no_argument, NULL, 'o'}, |
| {"disable-timeouts", no_argument, NULL, 'O'}, |
| {"disable-timeout", no_argument, NULL, 'O'}, /* i can't get it right */ |
| {"protect", no_argument, NULL, 'p'}, |
| {"resume", no_argument, NULL, 'r'}, |
| {"restricted", no_argument, NULL, 'R'}, |
| {"quiet", no_argument, NULL, 'q'}, |
| {"stop-at", required_argument, NULL, 's'}, |
| {"syslog", optional_argument, NULL , 2}, |
| {"timesync", no_argument, NULL, 'S'}, |
| {"timeout", required_argument, NULL, 't'}, |
| {"turbo", no_argument, NULL, 'T'}, |
| {"unlink", no_argument, NULL, 'u'}, |
| {"unrestrict", no_argument, NULL, 'U'}, |
| {"verbose", no_argument, NULL, 'v'}, |
| {"windowsize", required_argument, NULL, 'w'}, |
| {"xmodem", no_argument, NULL, 'X'}, |
| {"ymodem", no_argument, NULL, 1}, |
| {"zmodem", no_argument, NULL, 'Z'}, |
| {"overwrite", no_argument, NULL, 'y'}, |
| {"overwrite-or-skip", no_argument, NULL, 'Y'}, |
| |
| {"delay-startup", required_argument, NULL, 4}, |
| {"tcp-server", no_argument, NULL, 6}, |
| {"tcp-client", required_argument, NULL, 7}, |
| {"no-unixmode", no_argument, NULL, 8}, |
| {NULL, 0, NULL, 0} |
| }; |
| |
| static void |
| show_version(void) |
| { |
| printf ("%s (%s) %s\n", program_name, PACKAGE, VERSION); |
| } |
| |
| |
| int |
| main(int argc, char **argv) |
| { |
| char *cp; |
| int npats; |
| int dm; |
| int i; |
| int stdin_files; |
| char **patts; |
| int c; |
| const char *Cmdstr=NULL; /* Pointer to the command string */ |
| unsigned int startup_delay=0; |
| |
| if (((cp = getenv("ZNULLS")) != NULL) && *cp) |
| Znulls = atoi(cp); |
| if (((cp=getenv("SHELL"))!=NULL) && (strstr(cp, "rsh") || strstr(cp, "rksh") |
| || strstr(cp, "rbash") || strstr(cp,"rshell"))) |
| { |
| under_rsh=TRUE; |
| Restricted=1; |
| } |
| if ((cp=getenv("ZMODEM_RESTRICTED"))!=NULL) |
| Restricted=1; |
| from_cu(); |
| chkinvok(argv[0]); |
| |
| #ifdef ENABLE_SYSLOG |
| openlog(program_name,LOG_PID,ENABLE_SYSLOG); |
| #endif |
| |
| setlocale (LC_ALL, ""); |
| bindtextdomain (PACKAGE, LOCALEDIR); |
| textdomain (PACKAGE); |
| |
| parse_long_options (argc, argv, show_version, usage1); |
| |
| Rxtimeout = 600; |
| |
| while ((c = getopt_long (argc, argv, |
| "2+48abB:C:c:dfeEghHi:kL:l:m:M:NnOopRrqsSt:TUuvw:XYy", |
| long_options, (int *) 0))!=EOF) |
| { |
| unsigned long int tmp; |
| char *tmpptr; |
| enum strtol_error s_err; |
| |
| switch (c) |
| { |
| case 0: |
| break; |
| case '+': Lzmanag = ZF1_ZMAPND; break; |
| case '2': Twostop = TRUE; break; |
| case '8': |
| if (max_blklen==8192) |
| start_blklen=8192; |
| else |
| max_blklen=8192; |
| break; |
| case '9': /* this is a longopt .. */ |
| start_blklen=8192; |
| max_blklen=8192; |
| break; |
| case '4': |
| if (max_blklen==4096) |
| start_blklen=4096; |
| else |
| max_blklen=4096; |
| break; |
| case '5': /* this is a longopt .. */ |
| start_blklen=4096; |
| max_blklen=4096; |
| break; |
| case 'a': Lzconv = ZCNL; Ascii = TRUE; break; |
| case 'b': Lzconv = ZCBIN; break; |
| case 'B': |
| if (0==strcmp(optarg,"auto")) |
| buffersize= (size_t) -1; |
| else |
| buffersize=strtol(optarg,NULL,10); |
| #ifdef HAVE_MMAP |
| use_mmap=0; |
| #endif |
| break; |
| case 'C': |
| s_err = xstrtoul (optarg, NULL, 0, &tmp, NULL); |
| Cmdtries = tmp; |
| if (s_err != LONGINT_OK) |
| STRTOL_FATAL_ERROR (optarg, _("command tries"), s_err); |
| break; |
| case 'i': |
| Cmdack1 = ZCACK1; |
| /* **** FALL THROUGH TO **** */ |
| case 'c': |
| command_mode = TRUE; |
| Cmdstr = optarg; |
| break; |
| case 'd': |
| ++Dottoslash; |
| /* **** FALL THROUGH TO **** */ |
| case 'f': Fullname=TRUE; break; |
| case 'e': Zctlesc = 1; break; |
| case 'E': Lzmanag = ZF1_ZMCHNG; break; |
| case 'h': usage(0,NULL); break; |
| case 'H': Lzmanag = ZF1_ZMCRC; break; |
| case 'k': start_blklen=1024; break; |
| case 'L': |
| s_err = xstrtoul (optarg, NULL, 0, &tmp, "ck"); |
| blkopt = tmp; |
| if (s_err != LONGINT_OK) |
| STRTOL_FATAL_ERROR (optarg, _("packetlength"), s_err); |
| if (blkopt<24 || blkopt>MAX_BLOCK) |
| { |
| char meld[256]; |
| sprintf(meld, |
| _("packetlength out of range 24..%ld"), |
| (long) MAX_BLOCK); |
| usage(2,meld); |
| } |
| break; |
| case 'l': |
| s_err = xstrtoul (optarg, NULL, 0, &tmp, "ck"); |
| Tframlen = tmp; |
| if (s_err != LONGINT_OK) |
| STRTOL_FATAL_ERROR (optarg, _("framelength"), s_err); |
| if (Tframlen<32 || Tframlen>MAX_BLOCK) |
| { |
| char meld[256]; |
| sprintf(meld, |
| _("framelength out of range 32..%ld"), |
| (long) MAX_BLOCK); |
| usage(2,meld); |
| } |
| break; |
| case 'm': |
| s_err = xstrtoul (optarg, &tmpptr, 0, &tmp, "km"); |
| min_bps = tmp; |
| if (s_err != LONGINT_OK) |
| STRTOL_FATAL_ERROR (optarg, _("min_bps"), s_err); |
| if (min_bps<0) |
| usage(2,_("min_bps must be >= 0")); |
| break; |
| case 'M': |
| s_err = xstrtoul (optarg, NULL, 0, &tmp, NULL); |
| min_bps_time = tmp; |
| if (s_err != LONGINT_OK) |
| STRTOL_FATAL_ERROR (optarg, _("min_bps_time"), s_err); |
| if (min_bps_time<=1) |
| usage(2,_("min_bps_time must be > 1")); |
| break; |
| case 'N': Lzmanag = ZF1_ZMNEWL; break; |
| case 'n': Lzmanag = ZF1_ZMNEW; break; |
| case 'o': Wantfcs32 = FALSE; break; |
| case 'O': no_timeout = TRUE; break; |
| case 'p': Lzmanag = ZF1_ZMPROT; break; |
| case 'r': |
| if (Lzconv == ZCRESUM) |
| Lzmanag = ZF1_ZMCRC; |
| else |
| Lzconv = ZCRESUM; |
| break; |
| case 'R': Restricted = TRUE; break; |
| case 'q': Quiet=TRUE; Verbose=0; break; |
| case 's': |
| if (isdigit((unsigned char) (*optarg))) { |
| struct tm *tm; |
| time_t t; |
| int hh,mm; |
| char *nex; |
| |
| hh = strtoul (optarg, &nex, 10); |
| if (hh>23) |
| usage(2,_("hour to large (0..23)")); |
| if (*nex!=':') |
| usage(2, _("unparsable stop time\n")); |
| nex++; |
| mm = strtoul (optarg, &nex, 10); |
| if (mm>59) |
| usage(2,_("minute to large (0..59)")); |
| |
| t=time(NULL); |
| tm=localtime(&t); |
| tm->tm_hour=hh; |
| tm->tm_min=hh; |
| stop_time=mktime(tm); |
| if (stop_time<t) |
| stop_time+=86400L; /* one day more */ |
| if (stop_time - t <10) |
| usage(2,_("stop time to small")); |
| } else { |
| s_err = xstrtoul (optarg, NULL, 0, &tmp, NULL); |
| stop_time = tmp + time(0); |
| if (s_err != LONGINT_OK) |
| STRTOL_FATAL_ERROR (optarg, _("stop-at"), s_err); |
| if (tmp<10) |
| usage(2,_("stop time to small")); |
| } |
| break; |
| case 'S': enable_timesync=1; break; |
| case 'T': turbo_escape=1; break; |
| case 't': |
| s_err = xstrtoul (optarg, NULL, 0, &tmp, NULL); |
| Rxtimeout = tmp; |
| if (s_err != LONGINT_OK) |
| STRTOL_FATAL_ERROR (optarg, _("timeout"), s_err); |
| if (Rxtimeout<10 || Rxtimeout>1000) |
| usage(2,_("timeout out of range 10..1000")); |
| break; |
| case 'u': ++Unlinkafter; break; |
| case 'U': |
| if (!under_rsh) |
| Restricted=0; |
| else |
| error(1,0, |
| _("security violation: can't do that under restricted shell\n")); |
| break; |
| case 'v': ++Verbose; break; |
| case 'w': |
| s_err = xstrtoul (optarg, NULL, 0, &tmp, NULL); |
| Txwindow = tmp; |
| if (s_err != LONGINT_OK) |
| STRTOL_FATAL_ERROR (optarg, _("window size"), s_err); |
| if (Txwindow < 256) |
| Txwindow = 256; |
| Txwindow = (Txwindow/64) * 64; |
| Txwspac = Txwindow/4; |
| if (blkopt > Txwspac |
| || (!blkopt && Txwspac < MAX_BLOCK)) |
| blkopt = Txwspac; |
| break; |
| case 'X': protocol=ZM_XMODEM; break; |
| case 1: protocol=ZM_YMODEM; break; |
| case 'Z': protocol=ZM_ZMODEM; break; |
| case 'Y': |
| Lskipnocor = TRUE; |
| /* **** FALLL THROUGH TO **** */ |
| case 'y': |
| Lzmanag = ZF1_ZMCLOB; break; |
| case 2: |
| #ifdef ENABLE_SYSLOG |
| # ifndef ENABLE_SYSLOG_FORCE |
| if (optarg && (!strcmp(optarg,"off") || !strcmp(optarg,"no"))) |
| { |
| if (under_rsh) |
| error(0,0, _("cannot turnoff syslog")); |
| else |
| enable_syslog=FALSE; |
| } |
| else |
| enable_syslog=TRUE; |
| # else |
| error(0,0, _("cannot turnoff syslog")); |
| # endif |
| #endif |
| break; |
| case 4: |
| s_err = xstrtoul (optarg, NULL, 0, &tmp, NULL); |
| startup_delay = tmp; |
| if (s_err != LONGINT_OK) |
| STRTOL_FATAL_ERROR (optarg, _("startup delay"), s_err); |
| break; |
| case 6: |
| tcp_flag=2; |
| break; |
| case 7: |
| tcp_flag=3; |
| tcp_server_address=(char *)strdup(optarg); |
| if (!tcp_server_address) { |
| error(1,0,_("out of memory")); |
| } |
| break; |
| case 8: no_unixmode=1; break; |
| default: |
| usage (2,NULL); |
| break; |
| } |
| } |
| |
| if (getuid()!=geteuid()) { |
| error(1,0, |
| _("this program was never intended to be used setuid\n")); |
| } |
| |
| txbuf=malloc(MAX_BLOCK); |
| if (!txbuf) error(1,0,_("out of memory")); |
| |
| zsendline_init(); |
| |
| if (start_blklen==0) { |
| if (protocol == ZM_ZMODEM) { |
| start_blklen=1024; |
| if (Tframlen) { |
| start_blklen=max_blklen=Tframlen; |
| } |
| } |
| else |
| start_blklen=128; |
| } |
| |
| if (argc<2) |
| usage(2,_("need at least one file to send")); |
| |
| if (startup_delay) |
| sleep(startup_delay); |
| |
| #ifdef HAVE_SIGINTERRUPT |
| /* we want interrupted system calls to fail and not to be restarted. */ |
| siginterrupt(SIGALRM,1); |
| #endif |
| |
| |
| npats = argc - optind; |
| patts=&argv[optind]; |
| |
| if (npats < 1 && !command_mode) |
| usage(2,_("need at least one file to send")); |
| if (command_mode && Restricted) { |
| printf(_("Can't send command in restricted mode\n")); |
| exit(1); |
| } |
| |
| if (Fromcu && !Quiet) { |
| if (Verbose == 0) |
| Verbose = 2; |
| } |
| vfile("%s %s\n", program_name, VERSION); |
| |
| if (tcp_flag==2) { |
| char buf[256]; |
| #ifdef MAXHOSTNAMELEN |
| char hn[MAXHOSTNAMELEN]; |
| #else |
| char hn[256]; |
| #endif |
| char *p,*q; |
| int d; |
| |
| /* tell receiver to receive via tcp */ |
| d=tcp_server(buf); |
| p=strchr(buf+1,'<'); |
| p++; |
| q=strchr(p,'>'); |
| *q=0; |
| if (gethostname(hn,sizeof(hn))==-1) { |
| error(1,0, _("hostname too long\n")); |
| } |
| fprintf(stdout,"connect with lrz --tcp-client \"%s:%s\"\n",hn,p); |
| fflush(stdout); |
| /* ok, now that this file is sent we can switch to tcp */ |
| |
| tcp_socket=tcp_accept(d); |
| dup2(tcp_socket,0); |
| dup2(tcp_socket,1); |
| } |
| if (tcp_flag==3) { |
| char buf[256]; |
| char *p; |
| p=strchr(tcp_server_address,':'); |
| if (!p) |
| error(1,0, _("illegal server address\n")); |
| *p++=0; |
| sprintf(buf,"[%s] <%s>\n",tcp_server_address,p); |
| |
| fprintf(stdout,"connecting to %s\n",buf); |
| fflush(stdout); |
| |
| /* we need to switch to tcp mode */ |
| tcp_socket=tcp_connect(buf); |
| dup2(tcp_socket,0); |
| dup2(tcp_socket,1); |
| } |
| |
| |
| { |
| /* we write max_blocklen (data) + 18 (ZModem protocol overhead) |
| * + escape overhead (about 4 %), so buffer has to be |
| * somewhat larger than max_blklen |
| */ |
| char *s=malloc(max_blklen+1024); |
| if (!s) |
| { |
| zperr(_("out of memory")); |
| exit(1); |
| } |
| #ifdef SETVBUF_REVERSED |
| setvbuf(stdout,_IOFBF,s,max_blklen+1024); |
| #else |
| setvbuf(stdout,s,_IOFBF,max_blklen+1024); |
| #endif |
| } |
| blklen=start_blklen; |
| |
| for (i=optind,stdin_files=0;i<argc;i++) { |
| if (0==strcmp(argv[i],"-")) |
| stdin_files++; |
| } |
| |
| if (stdin_files>1) { |
| usage(1,_("can read only one file from stdin")); |
| } else if (stdin_files==1) { |
| io_mode_fd=1; |
| } |
| io_mode(io_mode_fd,1); |
| readline_setup(io_mode_fd, 128, 256); |
| |
| if (signal(SIGINT, bibi) == SIG_IGN) |
| signal(SIGINT, SIG_IGN); |
| else { |
| signal(SIGINT, bibi); |
| play_with_sigint=1; |
| } |
| signal(SIGTERM, bibi); |
| signal(SIGPIPE, bibi); |
| signal(SIGHUP, bibi); |
| |
| if ( protocol!=ZM_XMODEM) { |
| if (protocol==ZM_ZMODEM) { |
| printf("rz\r"); |
| fflush(stdout); |
| } |
| countem(npats, patts); |
| if (protocol == ZM_ZMODEM) { |
| /* throw away any input already received. This doesn't harm |
| * as we invite the receiver to send it's data again, and |
| * might be useful if the receiver has already died or |
| * if there is dirt left if the line |
| */ |
| #ifdef HAVE_SELECT |
| struct timeval t; |
| unsigned char throwaway; |
| fd_set f; |
| #endif |
| |
| purgeline(io_mode_fd); |
| |
| #ifdef HAVE_SELECT |
| t.tv_sec = 0; |
| t.tv_usec = 0; |
| |
| FD_ZERO(&f); |
| FD_SET(io_mode_fd,&f); |
| |
| while (select(1,&f,NULL,NULL,&t)) { |
| if (0==read(io_mode_fd,&throwaway,1)) /* EOF ... */ |
| break; |
| } |
| #endif |
| |
| purgeline(io_mode_fd); |
| stohdr(0L); |
| if (command_mode) |
| Txhdr[ZF0] = ZCOMMAND; |
| zshhdr(ZRQINIT, Txhdr); |
| zrqinits_sent++; |
| #if defined(ENABLE_TIMESYNC) |
| if (Rxflags2 != ZF1_TIMESYNC) |
| /* disable timesync if there are any flags we don't know. |
| * dsz/gsz seems to use some other flags! */ |
| enable_timesync=FALSE; |
| if (Rxflags2 & ZF1_TIMESYNC && enable_timesync) { |
| Totalleft+=6; /* TIMESYNC never needs more */ |
| Filesleft++; |
| } |
| #endif |
| } |
| } |
| fflush(stdout); |
| |
| if (Cmdstr) { |
| if (getzrxinit()) { |
| Exitcode=0200; canit(STDOUT_FILENO); |
| } |
| else if (zsendcmd(Cmdstr, strlen(Cmdstr)+1)) { |
| Exitcode=0200; canit(STDOUT_FILENO); |
| } |
| } else if (wcsend(npats, patts)==ERROR) { |
| Exitcode=0200; |
| canit(STDOUT_FILENO); |
| } |
| fflush(stdout); |
| io_mode(io_mode_fd,0); |
| if (Exitcode) |
| dm=Exitcode; |
| else if (errcnt) |
| dm=1; |
| else |
| dm=0; |
| if (Verbose) |
| { |
| fputs("\r\n",stderr); |
| if (dm) |
| fputs(_("Transfer incomplete\n"),stderr); |
| else |
| fputs(_("Transfer complete\n"),stderr); |
| } |
| exit(dm); |
| /*NOTREACHED*/ |
| } |
| |
| static int |
| send_pseudo(const char *name, const char *data) |
| { |
| char *tmp; |
| const char *p; |
| int ret=0; /* ok */ |
| size_t plen; |
| int fd; |
| int lfd; |
| |
| p = getenv ("TMPDIR"); |
| if (!p) |
| p = getenv ("TMP"); |
| if (!p) |
| p = "/tmp"; |
| tmp=malloc(PATH_MAX+1); |
| if (!tmp) |
| error(1,0,_("out of memory")); |
| |
| plen=strlen(p); |
| memcpy(tmp,p,plen); |
| tmp[plen++]='/'; |
| |
| lfd=0; |
| do { |
| if (lfd++==10) { |
| free(tmp); |
| vstringf (_ ("send_pseudo %s: cannot open tmpfile %s: %s"), |
| name, tmp, strerror (errno)); |
| vstring ("\r\n"); |
| return 1; |
| } |
| sprintf(tmp+plen,"%s.%lu.%d",name,(unsigned long) getpid(),lfd); |
| fd=open(tmp,O_WRONLY|O_CREAT|O_EXCL,0700); |
| /* is O_EXCL guaranted to not follow symlinks? |
| * I don`t know ... so be careful |
| */ |
| if (fd!=-1) { |
| struct stat st; |
| if (0!=lstat(tmp,&st)) { |
| vstringf (_ ("send_pseudo %s: cannot lstat tmpfile %s: %s"), |
| name, tmp, strerror (errno)); |
| vstring ("\r\n"); |
| unlink(tmp); |
| close(fd); |
| fd=-1; |
| } else { |
| if (S_ISLNK(st.st_mode)) { |
| vstringf (_ ("send_pseudo %s: avoiding symlink trap"),name); |
| vstring ("\r\n"); |
| unlink(tmp); |
| close(fd); |
| fd=-1; |
| } |
| } |
| } |
| } while (fd==-1); |
| if (write(fd,data,strlen(data))!=(signed long) strlen(data) |
| || close(fd)!=0) { |
| vstringf (_ ("send_pseudo %s: cannot write to tmpfile %s: %s"), |
| name, tmp, strerror (errno)); |
| vstring ("\r\n"); |
| free(tmp); |
| return 1; |
| } |
| |
| if (wcs (tmp,name) == ERROR) { |
| if (Verbose) |
| vstringf (_ ("send_pseudo %s: failed"),name); |
| else { |
| if (Verbose) |
| vstringf (_ ("send_pseudo %s: ok"),name); |
| Filcnt--; |
| } |
| vstring ("\r\n"); |
| ret=1; |
| } |
| unlink (tmp); |
| free(tmp); |
| return ret; |
| } |
| |
| static int |
| wcsend (int argc, char *argp[]) |
| { |
| int n; |
| |
| Crcflg = FALSE; |
| firstsec = TRUE; |
| bytcnt = (size_t) -1; |
| |
| for (n = 0; n < argc; ++n) { |
| Totsecs = 0; |
| if (wcs (argp[n],NULL) == ERROR) |
| return ERROR; |
| } |
| #if defined(ENABLE_TIMESYNC) |
| if (Rxflags2 & ZF1_TIMESYNC && enable_timesync) { |
| /* implement Peter Mandrellas extension */ |
| char buf[60]; |
| time_t t = time (NULL); |
| struct tm *tm = localtime (&t); /* sets timezone */ |
| strftime (buf, sizeof (buf) - 1, "%H:%M:%S", tm); |
| if (Verbose) { |
| vstring ("\r\n"); |
| vstringf (_("Answering TIMESYNC at %s"),buf); |
| } |
| #if defined(HAVE_TIMEZONE_VAR) |
| sprintf(buf+strlen(buf),"%ld\r\n", timezone / 60); |
| if (Verbose) |
| vstringf (" (%s %ld)\r\n", _ ("timezone"), timezone / 60); |
| #else |
| if (Verbose) |
| vstringf (" (%s)\r\n", _ ("timezone unknown")); |
| #endif |
| send_pseudo("/$time$.t",buf); |
| } |
| #endif |
| Totsecs = 0; |
| if (Filcnt == 0) { /* bitch if we couldn't open ANY files */ |
| #if 0 |
| /* i *really* do not like this */ |
| if (protocol != ZM_XMODEM) { |
| const char *Cmdstr; /* Pointer to the command string */ |
| command_mode = TRUE; |
| Cmdstr = "echo \"lsz: Can't open any requested files\""; |
| if (getnak ()) { |
| Exitcode = 0200; |
| canit(STDOUT_FILENO); |
| } |
| if (!zmodem_requested) |
| canit(STDOUT_FILENO); |
| else if (zsendcmd (Cmdstr, 1 + strlen (Cmdstr))) { |
| Exitcode = 0200; |
| canit(STDOUT_FILENO); |
| } |
| Exitcode = 1; |
| return OK; |
| } |
| #endif |
| canit(STDOUT_FILENO); |
| vstring ("\r\n"); |
| vstringf (_ ("Can't open any requested files.")); |
| vstring ("\r\n"); |
| return ERROR; |
| } |
| if (zmodem_requested) |
| saybibi (); |
| else if (protocol != ZM_XMODEM) { |
| struct zm_fileinfo zi; |
| char *pa; |
| pa=alloca(PATH_MAX+1); |
| *pa='\0'; |
| zi.fname = pa; |
| zi.modtime = 0; |
| zi.mode = 0; |
| zi.bytes_total = 0; |
| zi.bytes_sent = 0; |
| zi.bytes_received = 0; |
| zi.bytes_skipped = 0; |
| wctxpn (&zi); |
| } |
| return OK; |
| } |
| |
| static int |
| wcs(const char *oname, const char *remotename) |
| { |
| #if !defined(S_ISDIR) |
| int c; |
| #endif |
| struct stat f; |
| char *name; |
| struct zm_fileinfo zi; |
| #ifdef HAVE_MMAP |
| int dont_mmap_this=0; |
| #endif |
| #ifdef ENABLE_SYSLOG |
| const char *shortname; |
| shortname=strrchr(oname,'/'); |
| if (shortname) |
| shortname++; |
| else |
| shortname=oname; |
| #endif |
| |
| |
| if (Restricted) { |
| /* restrict pathnames to current tree or uucppublic */ |
| if ( strstr(oname, "../") |
| #ifdef PUBDIR |
| || (oname[0]== '/' && strncmp(oname, MK_STRING(PUBDIR), |
| strlen(MK_STRING(PUBDIR)))) |
| #endif |
| ) { |
| canit(STDOUT_FILENO); |
| vchar('\r'); |
| error(1,0, |
| _("security violation: not allowed to upload from %s"),oname); |
| } |
| } |
| |
| if (0==strcmp(oname,"-")) { |
| char *p=getenv("ONAME"); |
| name=alloca(PATH_MAX+1); |
| if (p) { |
| strcpy(name, p); |
| } else { |
| sprintf(name, "s%lu.lsz", (unsigned long) getpid()); |
| } |
| input_f=stdin; |
| #ifdef HAVE_MMAP |
| dont_mmap_this=1; |
| #endif |
| } else if ((input_f=fopen(oname, "r"))==NULL) { |
| int e=errno; |
| error(0,e, _("cannot open %s"),oname); |
| ++errcnt; |
| return OK; /* pass over it, there may be others */ |
| } else { |
| name=alloca(PATH_MAX+1); |
| strcpy(name, oname); |
| } |
| #ifdef HAVE_MMAP |
| if (!use_mmap || dont_mmap_this) |
| #endif |
| { |
| static char *s=NULL; |
| static size_t last_length=0; |
| struct stat st; |
| if (fstat(fileno(input_f),&st)==-1) |
| st.st_size=1024*1024; |
| if (buffersize==(size_t) -1 && s) { |
| if ((size_t) st.st_size > last_length) { |
| free(s); |
| s=NULL; |
| last_length=0; |
| } |
| } |
| if (!s && buffersize) { |
| last_length=16384; |
| if (buffersize==(size_t) -1) { |
| if (st.st_size>0) |
| last_length=st.st_size; |
| } else |
| last_length=buffersize; |
| /* buffer whole pages */ |
| last_length=(last_length+4095)&0xfffff000; |
| s=malloc(last_length); |
| if (!s) { |
| zpfatal(_("out of memory")); |
| exit(1); |
| } |
| } |
| if (s) { |
| #ifdef SETVBUF_REVERSED |
| setvbuf(input_f,_IOFBF,s,last_length); |
| #else |
| setvbuf(input_f,s,_IOFBF,last_length); |
| #endif |
| } |
| } |
| vpos = 0; |
| /* Check for directory or block special files */ |
| fstat(fileno(input_f), &f); |
| #if defined(S_ISDIR) |
| if (S_ISDIR(f.st_mode) || S_ISBLK(f.st_mode)) { |
| #else |
| c = f.st_mode & S_IFMT; |
| if (c == S_IFDIR || c == S_IFBLK) { |
| #endif |
| error(0,0, _("is not a file: %s"),name); |
| fclose(input_f); |
| return OK; |
| } |
| |
| if (remotename) { |
| /* disqualify const */ |
| union { |
| const char *c; |
| char *s; |
| } cheat; |
| cheat.c=remotename; |
| zi.fname=cheat.s; |
| } else |
| zi.fname=name; |
| zi.modtime=f.st_mtime; |
| zi.mode=f.st_mode; |
| #if defined(S_ISFIFO) |
| zi.bytes_total= (S_ISFIFO(f.st_mode)) ? DEFBYTL : f.st_size; |
| #else |
| zi.bytes_total= c == S_IFIFO ? DEFBYTL : f.st_size; |
| #endif |
| zi.bytes_sent=0; |
| zi.bytes_received=0; |
| zi.bytes_skipped=0; |
| zi.eof_seen=0; |
| timing(1,NULL); |
| |
| ++Filcnt; |
| switch (wctxpn(&zi)) { |
| case ERROR: |
| #ifdef ENABLE_SYSLOG |
| if (enable_syslog) |
| lsyslog(LOG_INFO, _("%s/%s: error occured"),protname(),shortname); |
| #endif |
| return ERROR; |
| case ZSKIP: |
| error(0,0, _("skipped: %s"),name); |
| #ifdef ENABLE_SYSLOG |
| if (enable_syslog) |
| lsyslog(LOG_INFO, _("%s/%s: skipped"),protname(),shortname); |
| #endif |
| return OK; |
| } |
| if (!zmodem_requested && wctx(&zi)==ERROR) |
| { |
| #ifdef ENABLE_SYSLOG |
| if (enable_syslog) |
| lsyslog(LOG_INFO, _("%s/%s: error occured"),protname(),shortname); |
| #endif |
| return ERROR; |
| } |
| if (Unlinkafter) |
| unlink(oname); |
| |
| if (Verbose > 1 |
| #ifdef ENABLE_SYSLOG |
| || enable_syslog |
| #endif |
| ) { |
| long bps; |
| double d=timing(0,NULL); |
| if (d==0) /* can happen if timing() uses time() */ |
| d=0.5; |
| bps=zi.bytes_sent/d; |
| vchar('\r'); |
| if (Verbose > 1) |
| vstringf(_("Bytes Sent:%7ld BPS:%-8ld \n"), |
| (long) zi.bytes_sent,bps); |
| #ifdef ENABLE_SYSLOG |
| if (enable_syslog) |
| lsyslog(LOG_INFO, "%s/%s: %ld Bytes, %ld BPS",shortname, |
| protname(), (long) zi.bytes_sent,bps); |
| #endif |
| } |
| return 0; |
| } |
| |
| /* |
| * generate and transmit pathname block consisting of |
| * pathname (null terminated), |
| * file length, mode time and file mode in octal |
| * as provided by the Unix fstat call. |
| * N.B.: modifies the passed name, may extend it! |
| */ |
| static int |
| wctxpn(struct zm_fileinfo *zi) |
| { |
| register char *p, *q; |
| char *name2; |
| struct stat f; |
| |
| name2=alloca(PATH_MAX+1); |
| |
| if (protocol==ZM_XMODEM) { |
| if (Verbose && *zi->fname && fstat(fileno(input_f), &f)!= -1) { |
| vstringf(_("Sending %s, %ld blocks: "), |
| zi->fname, (long) (f.st_size>>7)); |
| } |
| vstringf(_("Give your local XMODEM receive command now.")); |
| vstring("\r\n"); |
| return OK; |
| } |
| if (!zmodem_requested) |
| if (getnak()) { |
| vfile("getnak failed"); |
| DO_SYSLOG((LOG_INFO, "%s/%s: getnak failed", |
| shortname,protname())); |
| return ERROR; |
| } |
| |
| q = (char *) 0; |
| if (Dottoslash) { /* change . to . */ |
| for (p=zi->fname; *p; ++p) { |
| if (*p == '/') |
| q = p; |
| else if (*p == '.') |
| *(q=p) = '/'; |
| } |
| if (q && strlen(++q) > 8) { /* If name>8 chars */ |
| q += 8; /* make it .ext */ |
| strcpy(name2, q); /* save excess of name */ |
| *q = '.'; |
| strcpy(++q, name2); /* add it back */ |
| } |
| } |
| |
| for (p=zi->fname, q=txbuf ; *p; ) |
| if ((*q++ = *p++) == '/' && !Fullname) |
| q = txbuf; |
| *q++ = 0; |
| p=q; |
| while (q < (txbuf + MAX_BLOCK)) |
| *q++ = 0; |
| /* note that we may lose some information here in case mode_t is wider than an |
| * int. But i believe sending %lo instead of %o _could_ break compatability |
| */ |
| if (!Ascii && (input_f!=stdin) && *zi->fname && fstat(fileno(input_f), &f)!= -1) |
| sprintf(p, "%lu %lo %o 0 %d %ld", (long) f.st_size, f.st_mtime, |
| (unsigned int)((no_unixmode) ? 0 : f.st_mode), |
| Filesleft, Totalleft); |
| if (Verbose) |
| vstringf(_("Sending: %s\n"),txbuf); |
| Totalleft -= f.st_size; |
| if (--Filesleft <= 0) |
| Totalleft = 0; |
| if (Totalleft < 0) |
| Totalleft = 0; |
| |
| /* force 1k blocks if name won't fit in 128 byte block */ |
| if (txbuf[125]) |
| blklen=1024; |
| else { /* A little goodie for IMP/KMD */ |
| txbuf[127] = (f.st_size + 127) >>7; |
| txbuf[126] = (f.st_size + 127) >>15; |
| } |
| if (zmodem_requested) |
| return zsendfile(zi,txbuf, 1+strlen(p)+(p-txbuf)); |
| if (wcputsec(txbuf, 0, 128)==ERROR) { |
| vfile("wcputsec failed"); |
| DO_SYSLOG((LOG_INFO, "%s/%s: wcputsec failed", |
| shortname,protname())); |
| return ERROR; |
| } |
| return OK; |
| } |
| |
| static int |
| getnak(void) |
| { |
| int firstch; |
| int tries=0; |
| |
| Lastrx = 0; |
| for (;;) { |
| tries++; |
| switch (firstch = READLINE_PF(100)) { |
| case ZPAD: |
| if (getzrxinit()) |
| return ERROR; |
| Ascii = 0; /* Receiver does the conversion */ |
| return FALSE; |
| case TIMEOUT: |
| /* 30 seconds are enough */ |
| if (tries==3) { |
| zperr(_("Timeout on pathname")); |
| return TRUE; |
| } |
| /* don't send a second ZRQINIT _directly_ after the |
| * first one. Never send more then 4 ZRQINIT, because |
| * omen rz stops if it saw 5 of them */ |
| if ((zrqinits_sent>1 || tries>1) && zrqinits_sent<4) { |
| /* if we already sent a ZRQINIT we are using zmodem |
| * protocol and may send further ZRQINITs |
| */ |
| stohdr(0L); |
| zshhdr(ZRQINIT, Txhdr); |
| zrqinits_sent++; |
| } |
| continue; |
| case WANTG: |
| io_mode(io_mode_fd,2); /* Set cbreak, XON/XOFF, etc. */ |
| Optiong = TRUE; |
| blklen=1024; |
| case WANTCRC: |
| Crcflg = TRUE; |
| case NAK: |
| return FALSE; |
| case CAN: |
| if ((firstch = READLINE_PF(20)) == CAN && Lastrx == CAN) |
| return TRUE; |
| default: |
| break; |
| } |
| Lastrx = firstch; |
| } |
| } |
| |
| |
| static int |
| wctx(struct zm_fileinfo *zi) |
| { |
| register size_t thisblklen; |
| register int sectnum, attempts, firstch; |
| |
| firstsec=TRUE; thisblklen = blklen; |
| vfile("wctx:file length=%ld", (long) zi->bytes_total); |
| |
| while ((firstch=READLINE_PF(Rxtimeout))!=NAK && firstch != WANTCRC |
| && firstch != WANTG && firstch!=TIMEOUT && firstch!=CAN) |
| ; |
| if (firstch==CAN) { |
| zperr(_("Receiver Cancelled")); |
| return ERROR; |
| } |
| if (firstch==WANTCRC) |
| Crcflg=TRUE; |
| if (firstch==WANTG) |
| Crcflg=TRUE; |
| sectnum=0; |
| for (;;) { |
| if (zi->bytes_total <= (zi->bytes_sent + 896L)) |
| thisblklen = 128; |
| if ( !filbuf(txbuf, thisblklen)) |
| break; |
| if (wcputsec(txbuf, ++sectnum, thisblklen)==ERROR) |
| return ERROR; |
| zi->bytes_sent += thisblklen; |
| } |
| fclose(input_f); |
| attempts=0; |
| do { |
| purgeline(io_mode_fd); |
| sendline(EOT); |
| flushmo(); |
| ++attempts; |
| } while ((firstch=(READLINE_PF(Rxtimeout)) != ACK) && attempts < RETRYMAX); |
| if (attempts == RETRYMAX) { |
| zperr(_("No ACK on EOT")); |
| return ERROR; |
| } |
| else |
| return OK; |
| } |
| |
| static int |
| wcputsec(char *buf, int sectnum, size_t cseclen) |
| { |
| int checksum, wcj; |
| char *cp; |
| unsigned oldcrc; |
| int firstch; |
| int attempts; |
| |
| firstch=0; /* part of logic to detect CAN CAN */ |
| |
| if (Verbose>1) { |
| vchar('\r'); |
| if (protocol==ZM_XMODEM) { |
| vstringf(_("Xmodem sectors/kbytes sent: %3d/%2dk"), Totsecs, Totsecs/8 ); |
| } else { |
| vstringf(_("Ymodem sectors/kbytes sent: %3d/%2dk"), Totsecs, Totsecs/8 ); |
| } |
| } |
| for (attempts=0; attempts <= RETRYMAX; attempts++) { |
| Lastrx= firstch; |
| sendline(cseclen==1024?STX:SOH); |
| sendline(sectnum); |
| sendline(-sectnum -1); |
| oldcrc=checksum=0; |
| for (wcj=cseclen,cp=buf; --wcj>=0; ) { |
| sendline(*cp); |
| oldcrc=updcrc((0377& *cp), oldcrc); |
| checksum += *cp++; |
| } |
| if (Crcflg) { |
| oldcrc=updcrc(0,updcrc(0,oldcrc)); |
| sendline((int)oldcrc>>8); |
| sendline((int)oldcrc); |
| } |
| else |
| sendline(checksum); |
| |
| flushmo(); |
| if (Optiong) { |
| firstsec = FALSE; return OK; |
| } |
| firstch = READLINE_PF(Rxtimeout); |
| gotnak: |
| switch (firstch) { |
| case CAN: |
| if(Lastrx == CAN) { |
| cancan: |
| zperr(_("Cancelled")); return ERROR; |
| } |
| break; |
| case TIMEOUT: |
| zperr(_("Timeout on sector ACK")); continue; |
| case WANTCRC: |
| if (firstsec) |
| Crcflg = TRUE; |
| case NAK: |
| zperr(_("NAK on sector")); continue; |
| case ACK: |
| firstsec=FALSE; |
| Totsecs += (cseclen>>7); |
| return OK; |
| case ERROR: |
| zperr(_("Got burst for sector ACK")); break; |
| default: |
| zperr(_("Got %02x for sector ACK"), firstch); break; |
| } |
| for (;;) { |
| Lastrx = firstch; |
| if ((firstch = READLINE_PF(Rxtimeout)) == TIMEOUT) |
| break; |
| if (firstch == NAK || firstch == WANTCRC) |
| goto gotnak; |
| if (firstch == CAN && Lastrx == CAN) |
| goto cancan; |
| } |
| } |
| zperr(_("Retry Count Exceeded")); |
| return ERROR; |
| } |
| |
| /* fill buf with count chars padding with ^Z for CPM */ |
| static size_t |
| filbuf(char *buf, size_t count) |
| { |
| int c; |
| size_t m; |
| |
| if ( !Ascii) { |
| m = read(fileno(input_f), buf, count); |
| if (m <= 0) |
| return 0; |
| while (m < count) |
| buf[m++] = 032; |
| return count; |
| } |
| m=count; |
| if (Lfseen) { |
| *buf++ = 012; --m; Lfseen = 0; |
| } |
| while ((c=getc(input_f))!=EOF) { |
| if (c == 012) { |
| *buf++ = 015; |
| if (--m == 0) { |
| Lfseen = TRUE; break; |
| } |
| } |
| *buf++ =c; |
| if (--m == 0) |
| break; |
| } |
| if (m==count) |
| return 0; |
| else |
| while (m--!=0) |
| *buf++ = CPMEOF; |
| return count; |
| } |
| |
| /* Fill buffer with blklen chars */ |
| static size_t |
| zfilbuf (struct zm_fileinfo *zi) |
| { |
| size_t n; |
| |
| n = fread (txbuf, 1, blklen, input_f); |
| if (n < blklen) |
| zi->eof_seen = 1; |
| else { |
| /* save one empty paket in case file ends ob blklen boundary */ |
| int c = getc(input_f); |
| |
| if (c != EOF || !feof(input_f)) |
| ungetc(c, input_f); |
| else |
| zi->eof_seen = 1; |
| } |
| return n; |
| } |
| |
| static void |
| usage1 (int exitcode) |
| { |
| usage (exitcode, NULL); |
| } |
| |
| static void |
| usage(int exitcode, const char *what) |
| { |
| FILE *f=stdout; |
| |
| if (exitcode) |
| { |
| if (what) |
| fprintf(stderr, "%s: %s\n",program_name,what); |
| fprintf (stderr, _("Try `%s --help' for more information.\n"), |
| program_name); |
| exit(exitcode); |
| } |
| |
| fprintf(f, _("%s version %s\n"), program_name, |
| VERSION); |
| |
| fprintf(f,_("Usage: %s [options] file ...\n"), |
| program_name); |
| fprintf(f,_(" or: %s [options] -{c|i} COMMAND\n"),program_name); |
| fputs(_("Send file(s) with ZMODEM/YMODEM/XMODEM protocol\n"),f); |
| fputs(_( |
| " (X) = option applies to XMODEM only\n" |
| " (Y) = option applies to YMODEM only\n" |
| " (Z) = option applies to ZMODEM only\n" |
| ),f); |
| /* splitted into two halves for really bad compilers */ |
| fputs(_( |
| " -+, --append append to existing destination file (Z)\n" |
| " -2, --twostop use 2 stop bits\n" |
| " -4, --try-4k go up to 4K blocksize\n" |
| " --start-4k start with 4K blocksize (doesn't try 8)\n" |
| " -8, --try-8k go up to 8K blocksize\n" |
| " --start-8k start with 8K blocksize\n" |
| " -a, --ascii ASCII transfer (change CR/LF to LF)\n" |
| " -b, --binary binary transfer\n" |
| " -B, --bufsize N buffer N bytes (N==auto: buffer whole file)\n" |
| " -c, --command COMMAND execute remote command COMMAND (Z)\n" |
| " -C, --command-tries N try N times to execute a command (Z)\n" |
| " -d, --dot-to-slash change '.' to '/' in pathnames (Y/Z)\n" |
| " --delay-startup N sleep N seconds before doing anything\n" |
| " -e, --escape escape all control characters (Z)\n" |
| " -E, --rename force receiver to rename files it already has\n" |
| " -f, --full-path send full pathname (Y/Z)\n" |
| " -i, --immediate-command CMD send remote CMD, return immediately (Z)\n" |
| " -h, --help print this usage message\n" |
| " -k, --1k send 1024 byte packets (X)\n" |
| " -L, --packetlen N limit subpacket length to N bytes (Z)\n" |
| " -l, --framelen N limit frame length to N bytes (l>=L) (Z)\n" |
| " -m, --min-bps N stop transmission if BPS below N\n" |
| " -M, --min-bps-time N for at least N seconds (default: 120)\n" |
| ),f); |
| fputs(_( |
| " -n, --newer send file if source newer (Z)\n" |
| " -N, --newer-or-longer send file if source newer or longer (Z)\n" |
| " -o, --16-bit-crc use 16 bit CRC instead of 32 bit CRC (Z)\n" |
| " -O, --disable-timeouts disable timeout code, wait forever\n" |
| " -p, --protect protect existing destination file (Z)\n" |
| " -r, --resume resume interrupted file transfer (Z)\n" |
| " -R, --restricted restricted, more secure mode\n" |
| " -q, --quiet quiet (no progress reports)\n" |
| " -s, --stop-at {HH:MM|+N} stop transmission at HH:MM or in N seconds\n" |
| " --tcp-server open socket, wait for connection (Z)\n" |
| " --tcp-client ADDR:PORT open socket, connect to ... (Z)\n" |
| " -u, --unlink unlink file after transmission\n" |
| " -U, --unrestrict turn off restricted mode (if allowed to)\n" |
| " -v, --verbose be verbose, provide debugging information\n" |
| " -w, --windowsize N Window is N bytes (Z)\n" |
| " -X, --xmodem use XMODEM protocol\n" |
| " -y, --overwrite overwrite existing files\n" |
| " -Y, --overwrite-or-skip overwrite existing files, else skip\n" |
| " --ymodem use YMODEM protocol\n" |
| " -Z, --zmodem use ZMODEM protocol\n" |
| "\n" |
| "short options use the same arguments as the long ones\n" |
| ),f); |
| exit(exitcode); |
| } |
| |
| /* |
| * Get the receiver's init parameters |
| */ |
| static int |
| getzrxinit(void) |
| { |
| static int dont_send_zrqinit=1; |
| int old_timeout=Rxtimeout; |
| int n; |
| struct stat f; |
| size_t rxpos; |
| int timeouts=0; |
| |
| Rxtimeout=100; /* 10 seconds */ |
| /* XXX purgeline(io_mode_fd); this makes _real_ trouble. why? -- uwe */ |
| |
| for (n=10; --n>=0; ) { |
| /* we might need to send another zrqinit in case the first is |
| * lost. But *not* if getting here for the first time - in |
| * this case we might just get a ZRINIT for our first ZRQINIT. |
| * Never send more then 4 ZRQINIT, because |
| * omen rz stops if it saw 5 of them. |
| */ |
| if (zrqinits_sent<4 && n!=10 && !dont_send_zrqinit) { |
| zrqinits_sent++; |
| stohdr(0L); |
| zshhdr(ZRQINIT, Txhdr); |
| } |
| dont_send_zrqinit=0; |
| |
| switch (zgethdr(Rxhdr, 1,&rxpos)) { |
| case ZCHALLENGE: /* Echo receiver's challenge numbr */ |
| stohdr(rxpos); |
| zshhdr(ZACK, Txhdr); |
| continue; |
| case ZCOMMAND: /* They didn't see our ZRQINIT */ |
| /* ??? Since when does a receiver send ZCOMMAND? -- uwe */ |
| continue; |
| case ZRINIT: |
| Rxflags = 0377 & Rxhdr[ZF0]; |
| Rxflags2 = 0377 & Rxhdr[ZF1]; |
| Txfcs32 = (Wantfcs32 && (Rxflags & CANFC32)); |
| { |
| int old=Zctlesc; |
| Zctlesc |= Rxflags & TESCCTL; |
| /* update table - was initialised to not escape */ |
| if (Zctlesc && !old) |
| zsendline_init(); |
| } |
| Rxbuflen = (0377 & Rxhdr[ZP0])+((0377 & Rxhdr[ZP1])<<8); |
| if ( !(Rxflags & CANFDX)) |
| Txwindow = 0; |
| vfile("Rxbuflen=%d Tframlen=%d", Rxbuflen, Tframlen); |
| if ( play_with_sigint) |
| signal(SIGINT, SIG_IGN); |
| io_mode(io_mode_fd,2); /* Set cbreak, XON/XOFF, etc. */ |
| #ifndef READCHECK |
| /* Use MAX_BLOCK byte frames if no sample/interrupt */ |
| if (Rxbuflen < 32 || Rxbuflen > MAX_BLOCK) { |
| Rxbuflen = MAX_BLOCK; |
| vfile("Rxbuflen=%d", Rxbuflen); |
| } |
| #endif |
| /* Override to force shorter frame length */ |
| if (Tframlen && Rxbuflen > Tframlen) |
| Rxbuflen = Tframlen; |
| if ( !Rxbuflen) |
| Rxbuflen = 1024; |
| vfile("Rxbuflen=%d", Rxbuflen); |
| |
| /* If using a pipe for testing set lower buf len */ |
| fstat(0, &f); |
| #if defined(S_ISCHR) |
| if (! (S_ISCHR(f.st_mode))) { |
| #else |
| if ((f.st_mode & S_IFMT) != S_IFCHR) { |
| #endif |
| Rxbuflen = MAX_BLOCK; |
| } |
| /* |
| * If input is not a regular file, force ACK's to |
| * prevent running beyond the buffer limits |
| */ |
| if ( !command_mode) { |
| fstat(fileno(input_f), &f); |
| #if defined(S_ISREG) |
| if (!(S_ISREG(f.st_mode))) { |
| #else |
| if ((f.st_mode & S_IFMT) != S_IFREG) { |
| #endif |
| Canseek = -1; |
| /* return ERROR; */ |
| } |
| } |
| /* Set initial subpacket length */ |
| if (blklen < 1024) { /* Command line override? */ |
| if (Baudrate > 300) |
| blklen = 256; |
| if (Baudrate > 1200) |
| blklen = 512; |
| if (Baudrate > 2400) |
| blklen = 1024; |
| } |
| if (Rxbuflen && blklen>Rxbuflen) |
| blklen = Rxbuflen; |
| if (blkopt && blklen > blkopt) |
| blklen = blkopt; |
| vfile("Rxbuflen=%d blklen=%d", Rxbuflen, blklen); |
| vfile("Txwindow = %u Txwspac = %d", Txwindow, Txwspac); |
| Rxtimeout=old_timeout; |
| return (sendzsinit()); |
| case ZCAN: |
| case TIMEOUT: |
| if (timeouts++==0) |
| continue; /* force one other ZRQINIT to be sent */ |
| return ERROR; |
| case ZRQINIT: |
| if (Rxhdr[ZF0] == ZCOMMAND) |
| continue; |
| default: |
| zshhdr(ZNAK, Txhdr); |
| continue; |
| } |
| } |
| return ERROR; |
| } |
| |
| /* Send send-init information */ |
| static int |
| sendzsinit(void) |
| { |
| int c; |
| |
| if (Myattn[0] == '\0' && (!Zctlesc || (Rxflags & TESCCTL))) |
| return OK; |
| errors = 0; |
| for (;;) { |
| stohdr(0L); |
| if (Zctlesc) { |
| Txhdr[ZF0] |= TESCCTL; zshhdr(ZSINIT, Txhdr); |
| } |
| else |
| zsbhdr(ZSINIT, Txhdr); |
| ZSDATA(Myattn, 1+strlen(Myattn), ZCRCW); |
| c = zgethdr(Rxhdr, 1,NULL); |
| switch (c) { |
| case ZCAN: |
| return ERROR; |
| case ZACK: |
| return OK; |
| default: |
| if (++errors > 19) |
| return ERROR; |
| continue; |
| } |
| } |
| } |
| |
| /* Send file name and related info */ |
| static int |
| zsendfile(struct zm_fileinfo *zi, const char *buf, size_t blen) |
| { |
| unsigned long crc; |
| size_t rxpos; |
| |
| /* we are going to send a ZFILE. There cannot be much useful |
| * stuff in the line right now (*except* ZCAN?). |
| */ |
| #if 0 |
| purgeline(io_mode_fd); /* might possibly fix stefan glasers problems */ |
| #endif |
| |
| for (;;) { |
| int gotblock; |
| int gotchar; |
| Txhdr[ZF0] = Lzconv; /* file conversion request */ |
| Txhdr[ZF1] = Lzmanag; /* file management request */ |
| if (Lskipnocor) |
| Txhdr[ZF1] |= ZF1_ZMSKNOLOC; |
| Txhdr[ZF2] = Lztrans; /* file transport request */ |
| Txhdr[ZF3] = 0; |
| zsbhdr(ZFILE, Txhdr); |
| ZSDATA(buf, blen, ZCRCW); |
| again: |
| gotblock = zgethdr(Rxhdr, 1, &rxpos); |
| switch (gotblock) { |
| case ZRINIT: |
| while ((gotchar = READLINE_PF(50)) > 0) |
| if (gotchar == ZPAD) { |
| goto again; |
| } |
| /* **** FALL THRU TO **** */ |
| default: |
| continue; |
| case ZRQINIT: /* remote site is sender! */ |
| if (Verbose) |
| vstringf(_("got ZRQINIT")); |
| DO_SYSLOG((LOG_INFO, "%s/%s: got ZRQINIT - sz talks to sz", |
| shortname,protname())); |
| return ERROR; |
| case ZCAN: |
| if (Verbose) |
| vstringf(_("got ZCAN")); |
| DO_SYSLOG((LOG_INFO, "%s/%s: got ZCAN - receiver canceled", |
| shortname,protname())); |
| return ERROR; |
| case TIMEOUT: |
| DO_SYSLOG((LOG_INFO, "%s/%s: got TIMEOUT", |
| shortname,protname())); |
| return ERROR; |
| case ZABORT: |
| DO_SYSLOG((LOG_INFO, "%s/%s: got ZABORT", |
| shortname,protname())); |
| return ERROR; |
| case ZFIN: |
| DO_SYSLOG((LOG_INFO, "%s/%s: got ZFIN", |
| shortname,protname())); |
| return ERROR; |
| case ZCRC: |
| crc = 0xFFFFFFFFL; |
| #ifdef HAVE_MMAP |
| if (use_mmap && !mm_addr) |
| { |
| struct stat st; |
| /* mmap on files of 0 length can give 0 as result .. under linux 2.2.9 at least */ |
| if (fstat (fileno (input_f), &st) == 0 && st.st_size!=0) { |
| mm_size = st.st_size; |
| mm_addr = mmap (0, mm_size, PROT_READ, |
| MAP_SHARED, fileno (input_f), 0); |
| if ((caddr_t) mm_addr == (caddr_t) - 1) |
| mm_addr = NULL; |
| else { |
| fclose (input_f); |
| input_f = NULL; |
| } |
| } |
| } |
| if (mm_addr) { |
| size_t i; |
| size_t count; |
| char *p=mm_addr; |
| count=(rxpos < mm_size && rxpos > 0)? rxpos: mm_size; |
| for (i=0;i<count;i++,p++) { |
| crc = UPDC32(*p, crc); |
| } |
| crc = ~crc; |
| } else |
| #endif |
| if (Canseek >= 0) { |
| if (rxpos==0) { |
| struct stat st; |
| if (0==fstat(fileno(input_f),&st)) { |
| rxpos=st.st_size; |
| } else |
| rxpos=-1; |
| } |
| while (rxpos-- && ((gotchar = getc(input_f)) != EOF)) |
| crc = UPDC32(gotchar, crc); |
| crc = ~crc; |
| clearerr(input_f); /* Clear EOF */ |
| fseek(input_f, 0L, 0); |
| } |
| stohdr(crc); |
| zsbhdr(ZCRC, Txhdr); |
| goto again; |
| case ZSKIP: |
| if (input_f) { |
| fclose(input_f); |
| input_f=NULL; |
| } |
| #ifdef HAVE_MMAP |
| else if (mm_addr) { |
| munmap(mm_addr,mm_size); |
| mm_addr=NULL; |
| } |
| #endif |
| |
| vfile("receiver skipped"); |
| DO_SYSLOG((LOG_INFO, "%s/%s: receiver skipped", |
| shortname, protname())); |
| return ZSKIP; |
| case ZRPOS: |
| /* |
| * Suppress zcrcw request otherwise triggered by |
| * lastsync==bytcnt |
| */ |
| #ifdef HAVE_MMAP |
| if (!mm_addr) |
| #endif |
| if (rxpos && fseek(input_f, (long) rxpos, 0)) { |
| int er=errno; |
| vfile("fseek failed: %s", strerror(er)); |
| DO_SYSLOG((LOG_INFO, "%s/%s: fseek failed: %s", |
| shortname, protname(), strerror(er))); |
| return ERROR; |
| } |
| if (rxpos) |
| zi->bytes_skipped=rxpos; |
| bytcnt = zi->bytes_sent = rxpos; |
| Lastsync = rxpos -1; |
| return zsendfdata(zi); |
| } |
| } |
| } |
| |
| /* Send the data in the file */ |
| static int |
| zsendfdata (struct zm_fileinfo *zi) |
| { |
| static int c; |
| int newcnt; |
| static int junkcount; /* Counts garbage chars received by TX */ |
| static size_t last_txpos = 0; |
| static long last_bps = 0; |
| static long not_printed = 0; |
| static long total_sent = 0; |
| static time_t low_bps=0; |
| |
| #ifdef HAVE_MMAP |
| if (use_mmap && !mm_addr) |
| { |
| struct stat st; |
| if (fstat (fileno (input_f), &st) == 0 && st.st_size!=0) { |
| mm_size = st.st_size; |
| mm_addr = mmap (0, mm_size, PROT_READ, |
| MAP_SHARED, fileno (input_f), 0); |
| if ((caddr_t) mm_addr == (caddr_t) - 1) |
| mm_addr = NULL; |
| else { |
| fclose (input_f); |
| input_f = NULL; |
| } |
| } |
| } |
| #endif |
| |
| if (play_with_sigint) |
| signal (SIGINT, onintr); |
| |
| Lrxpos = 0; |
| junkcount = 0; |
| Beenhereb4 = 0; |
| somemore: |
| if (setjmp (intrjmp)) { |
| if (play_with_sigint) |
| signal (SIGINT, onintr); |
| waitack: |
| junkcount = 0; |
| c = getinsync (zi, 0); |
| gotack: |
| switch (c) { |
| default: |
| if (input_f) { |
| fclose (input_f); |
| input_f=NULL; |
| } |
| DO_SYSLOG((LOG_INFO, "%s/%s: got %d", |
| shortname, protname(), c)); |
| return ERROR; |
| case ZCAN: |
| if (input_f) { |
| fclose (input_f); |
| input_f=NULL; |
| } |
| DO_SYSLOG((LOG_INFO, "%s/%s: got ZCAN", |
| shortname, protname())); |
| return ERROR; |
| case ZSKIP: |
| if (input_f) { |
| fclose (input_f); |
| input_f=NULL; |
| } |
| DO_SYSLOG((LOG_INFO, "%s/%s: got ZSKIP", |
| shortname, protname())); |
| return ZSKIP; |
| case ZACK: |
| case ZRPOS: |
| break; |
| case ZRINIT: |
| return OK; |
| } |
| #ifdef READCHECK |
| /* |
| * If the reverse channel can be tested for data, |
| * this logic may be used to detect error packets |
| * sent by the receiver, in place of setjmp/longjmp |
| * rdchk(fdes) returns non 0 if a character is available |
| */ |
| while (rdchk (io_mode_fd)) { |
| #ifdef READCHECK_READS |
| switch (checked) |
| #else |
| switch (READLINE_PF (1)) |
| #endif |
| { |
| case CAN: |
| case ZPAD: |
| c = getinsync (zi, 1); |
| goto gotack; |
| case XOFF: /* Wait a while for an XON */ |
| case XOFF | 0200: |
| READLINE_PF (100); |
| } |
| } |
| #endif |
| } |
| |
| newcnt = Rxbuflen; |
| Txwcnt = 0; |
| stohdr (zi->bytes_sent); |
| zsbhdr (ZDATA, Txhdr); |
| |
| do { |
| size_t n; |
| int e; |
| unsigned old = blklen; |
| blklen = calc_blklen (total_sent); |
| total_sent += blklen + OVERHEAD; |
| if (Verbose > 2 && blklen != old) |
| vstringf (_("blklen now %d\n"), blklen); |
| #ifdef HAVE_MMAP |
| if (mm_addr) { |
| if (zi->bytes_sent + blklen < mm_size) |
| n = blklen; |
| else { |
| n = mm_size - zi->bytes_sent; |
| zi->eof_seen = 1; |
| } |
| } else |
| #endif |
| n = zfilbuf (zi); |
| if (zi->eof_seen) { |
| e = ZCRCE; |
| if (Verbose>3) |
| vstring("e=ZCRCE/eof seen"); |
| } else if (junkcount > 3) { |
| e = ZCRCW; |
| if (Verbose>3) |
| vstring("e=ZCRCW/junkcount > 3"); |
| } else if (bytcnt == Lastsync) { |
| e = ZCRCW; |
| if (Verbose>3) |
| vstringf("e=ZCRCW/bytcnt == Lastsync == %ld", |
| (unsigned long) Lastsync); |
| #if 0 |
| /* what is this good for? Rxbuflen/newcnt normally are short - so after |
| * a few KB ZCRCW will be used? (newcnt is never incremented) |
| */ |
| } else if (Rxbuflen && (newcnt -= n) <= 0) { |
| e = ZCRCW; |
| if (Verbose>3) |
| vstringf("e=ZCRCW/Rxbuflen(newcnt=%ld,n=%ld)", |
| (unsigned long) newcnt,(unsigned long) n); |
| #endif |
| } else if (Txwindow && (Txwcnt += n) >= Txwspac) { |
| Txwcnt = 0; |
| e = ZCRCQ; |
| if (Verbose>3) |
| vstring("e=ZCRCQ/Window"); |
| } else { |
| e = ZCRCG; |
| if (Verbose>3) |
| vstring("e=ZCRCG"); |
| } |
| if ((Verbose > 1 || min_bps || stop_time) |
| && (not_printed > (min_bps ? 3 : 7) |
| || zi->bytes_sent > last_bps / 2 + last_txpos)) { |
| int minleft = 0; |
| int secleft = 0; |
| time_t now; |
| last_bps = (zi->bytes_sent / timing (0,&now)); |
| if (last_bps > 0) { |
| minleft = (zi->bytes_total - zi->bytes_sent) / last_bps / 60; |
| secleft = ((zi->bytes_total - zi->bytes_sent) / last_bps) % 60; |
| } |
| if (min_bps) { |
| if (low_bps) { |
| if (last_bps<min_bps) { |
| if (now-low_bps>=min_bps_time) { |
| /* too bad */ |
| if (Verbose) { |
| vstringf(_("zsendfdata: bps rate %ld below min %ld"), |
| last_bps, min_bps); |
| vstring("\r\n"); |
| } |
| DO_SYSLOG((LOG_INFO, "%s/%s: bps rate low: %ld <%ld", |
| shortname, protname(), last_bps, min_bps)); |
| return ERROR; |
| } |
| } else |
| low_bps=0; |
| } else if (last_bps < min_bps) { |
| low_bps=now; |
| } |
| } |
| if (stop_time && now>=stop_time) { |
| /* too bad */ |
| if (Verbose) { |
| vstring(_("zsendfdata: reached stop time")); |
| vstring("\r\n"); |
| } |
| DO_SYSLOG((LOG_INFO, "%s/%s: reached stop time", |
| shortname, protname())); |
| return ERROR; |
| } |
| |
| if (Verbose > 1) { |
| vchar ('\r'); |
| vstringf (_("Bytes Sent:%7ld/%7ld BPS:%-8ld ETA %02d:%02d "), |
| (long) zi->bytes_sent, (long) zi->bytes_total, |
| last_bps, minleft, secleft); |
| } |
| last_txpos = zi->bytes_sent; |
| } else if (Verbose) |
| not_printed++; |
| ZSDATA (DATAADR, n, e); |
| bytcnt = zi->bytes_sent += n; |
| if (e == ZCRCW) |
| goto waitack; |
| #ifdef READCHECK |
| /* |
| * If the reverse channel can be tested for data, |
| * this logic may be used to detect error packets |
| * sent by the receiver, in place of setjmp/longjmp |
| * rdchk(fdes) returns non 0 if a character is available |
| */ |
| fflush (stdout); |
| while (rdchk (io_mode_fd)) { |
| #ifdef READCHECK_READS |
| switch (checked) |
| #else |
| switch (READLINE_PF (1)) |
| #endif |
| { |
| case CAN: |
| case ZPAD: |
| c = getinsync (zi, 1); |
| if (c == ZACK) |
| break; |
| /* zcrce - dinna wanna starta ping-pong game */ |
| ZSDATA (txbuf, 0, ZCRCE); |
| goto gotack; |
| case XOFF: /* Wait a while for an XON */ |
| case XOFF | 0200: |
| READLINE_PF (100); |
| default: |
| ++junkcount; |
| } |
| } |
| #endif /* READCHECK */ |
| if (Txwindow) { |
| size_t tcount = 0; |
| while ((tcount = zi->bytes_sent - Lrxpos) >= Txwindow) { |
| vfile ("%ld (%ld,%ld) window >= %u", tcount, |
| (long) zi->bytes_sent, (long) Lrxpos, |
| Txwindow); |
| if (e != ZCRCQ) |
| ZSDATA (txbuf, 0, e = ZCRCQ); |
| c = getinsync (zi, 1); |
| if (c != ZACK) { |
| ZSDATA (txbuf, 0, ZCRCE); |
| goto gotack; |
| } |
| } |
| vfile ("window = %ld", tcount); |
| } |
| } while (!zi->eof_seen); |
| |
| |
| if (play_with_sigint) |
| signal (SIGINT, SIG_IGN); |
| |
| for (;;) { |
| stohdr (zi->bytes_sent); |
| zsbhdr (ZEOF, Txhdr); |
| switch (getinsync (zi, 0)) { |
| case ZACK: |
| continue; |
| case ZRPOS: |
| goto somemore; |
| case ZRINIT: |
| return OK; |
| case ZSKIP: |
| if (input_f) { |
| fclose (input_f); |
| input_f=NULL; |
| } |
| DO_SYSLOG((LOG_INFO, "%s/%s: got ZSKIP", |
| shortname, protname())); |
| return c; |
| default: |
| if (input_f) { |
| fclose (input_f); |
| input_f=NULL; |
| } |
| DO_SYSLOG((LOG_INFO, "%s/%s: got %d", |
| shortname, protname(), c)); |
| return ERROR; |
| } |
| } |
| } |
| |
| static int |
| calc_blklen(long total_sent) |
| { |
| static long total_bytes=0; |
| static int calcs_done=0; |
| static long last_error_count=0; |
| static int last_blklen=0; |
| static long last_bytes_per_error=0; |
| unsigned long best_bytes=0; |
| long best_size=0; |
| long this_bytes_per_error; |
| long d; |
| unsigned int i; |
| if (total_bytes==0) |
| { |
| /* called from countem */ |
| total_bytes=total_sent; |
| return 0; |
| } |
| |
| /* it's not good to calc blklen too early */ |
| if (calcs_done++ < 5) { |
| if (error_count && start_blklen >1024) |
| return last_blklen=1024; |
| else |
| last_blklen/=2; |
| return last_blklen=start_blklen; |
| } |
| |
| if (!error_count) { |
| /* that's fine */ |
| if (start_blklen==max_blklen) |
| return start_blklen; |
| this_bytes_per_error=LONG_MAX; |
| goto calcit; |
| } |
| |
| if (error_count!=last_error_count) { |
| /* the last block was bad. shorten blocks until one block is |
| * ok. this is because very often many errors come in an |
| * short period */ |
| if (error_count & 2) |
| { |
| last_blklen/=2; |
| if (last_blklen < 32) |
| last_blklen = 32; |
| else if (last_blklen > 512) |
| last_blklen=512; |
| if (Verbose > 3) |
| vstringf(_("calc_blklen: reduced to %d due to error\n"), |
| last_blklen); |
| } |
| last_error_count=error_count; |
| last_bytes_per_error=0; /* force recalc */ |
| return last_blklen; |
| } |
| |
| this_bytes_per_error=total_sent / error_count; |
| /* we do not get told about every error, because |
| * there may be more than one error per failed block. |
| * but one the other hand some errors are reported more |
| * than once: If a modem buffers more than one block we |
| * get at least two ZRPOS for the same position in case |
| * *one* block has to be resent. |
| * so don't do this: |
| * this_bytes_per_error/=2; |
| */ |
| /* there has to be a margin */ |
| if (this_bytes_per_error<100) |
| this_bytes_per_error=100; |
| |
| /* be nice to the poor machine and do the complicated things not |
| * too often |
| */ |
| if (last_bytes_per_error>this_bytes_per_error) |
| d=last_bytes_per_error-this_bytes_per_error; |
| else |
| d=this_bytes_per_error-last_bytes_per_error; |
| if (d<4) |
| { |
| if (Verbose > 3) |
| { |
| vstringf(_("calc_blklen: returned old value %d due to low bpe diff\n"), |
| last_blklen); |
| vstringf(_("calc_blklen: old %ld, new %ld, d %ld\n"), |
| last_bytes_per_error,this_bytes_per_error,d ); |
| } |
| return last_blklen; |
| } |
| last_bytes_per_error=this_bytes_per_error; |
| |
| calcit: |
| if (Verbose > 3) |
| vstringf(_("calc_blklen: calc total_bytes=%ld, bpe=%ld, ec=%ld\n"), |
| total_bytes,this_bytes_per_error,(long) error_count); |
| for (i=32;i<=max_blklen;i*=2) { |
| long ok; /* some many ok blocks do we need */ |
| long failed; /* and that's the number of blocks not transmitted ok */ |
| unsigned long transmitted; |
| ok=total_bytes / i + 1; |
| failed=((long) i + OVERHEAD) * ok / this_bytes_per_error; |
| transmitted=total_bytes + ok * OVERHEAD |
| + failed * ((long) i+OVERHEAD+OVER_ERR); |
| if (Verbose > 4) |
| vstringf(_("calc_blklen: blklen %d, ok %ld, failed %ld -> %lu\n"), |
| i,ok,failed,transmitted); |
| if (transmitted < best_bytes || !best_bytes) |
| { |
| best_bytes=transmitted; |
| best_size=i; |
| } |
| } |
| if (best_size > 2*last_blklen) |
| best_size=2*last_blklen; |
| last_blklen=best_size; |
| if (Verbose > 3) |
| vstringf(_("calc_blklen: returned %d as best\n"), |
| last_blklen); |
| return last_blklen; |
| } |
| |
| /* |
| * Respond to receiver's complaint, get back in sync with receiver |
| */ |
| static int |
| getinsync(struct zm_fileinfo *zi, int flag) |
| { |
| size_t rxpos; |
| |
| for (;;) { |
| int gotblock; |
| gotblock = zgethdr(Rxhdr, 0, &rxpos); |
| switch (gotblock) { |
| case ZCAN: |
| case ZABORT: |
| case ZFIN: |
| case TIMEOUT: |
| return ERROR; |
| case ZRPOS: |
| /* ************************************* */ |
| /* If sending to a buffered modem, you */ |
| /* might send a break at this point to */ |
| /* dump the modem's buffer. */ |
| if (input_f) |
| clearerr(input_f); /* In case file EOF seen */ |
| #ifdef HAVE_MMAP |
| if (!mm_addr) |
| #endif |
| if (fseek(input_f, (long) rxpos, 0)) |
| return ERROR; |
| zi->eof_seen = 0; |
| bytcnt = Lrxpos = zi->bytes_sent = rxpos; |
| if (Lastsync == rxpos) { |
| error_count++; |
| } |
| Lastsync = rxpos; |
| return ZRPOS; |
| case ZACK: |
| Lrxpos = rxpos; |
| if (flag || zi->bytes_sent == rxpos) |
| return ZACK; |
| continue; |
| case ZRINIT: |
| case ZSKIP: |
| if (input_f) { |
| fclose (input_f); |
| input_f=NULL; |
| } |
| #ifdef HAVE_MMAP |
| else if (mm_addr) { |
| munmap(mm_addr,mm_size); |
| mm_addr=NULL; |
| } |
| #endif |
| return ZSKIP; |
| case ERROR: |
| default: |
| error_count++; |
| zsbhdr(ZNAK, Txhdr); |
| continue; |
| } |
| } |
| } |
| |
| |
| /* Say "bibi" to the receiver, try to do it cleanly */ |
| static void |
| saybibi(void) |
| { |
| for (;;) { |
| stohdr(0L); /* CAF Was zsbhdr - minor change */ |
| zshhdr(ZFIN, Txhdr); /* to make debugging easier */ |
| switch (zgethdr(Rxhdr, 0,NULL)) { |
| case ZFIN: |
| sendline('O'); |
| sendline('O'); |
| flushmo(); |
| case ZCAN: |
| case TIMEOUT: |
| return; |
| } |
| } |
| } |
| |
| /* Send command and related info */ |
| static int |
| zsendcmd(const char *buf, size_t blen) |
| { |
| int c; |
| pid_t cmdnum; |
| size_t rxpos; |
| |
| cmdnum = getpid(); |
| errors = 0; |
| for (;;) { |
| stohdr((size_t) cmdnum); |
| Txhdr[ZF0] = Cmdack1; |
| zsbhdr(ZCOMMAND, Txhdr); |
| ZSDATA(buf, blen, ZCRCW); |
| listen: |
| Rxtimeout = 100; /* Ten second wait for resp. */ |
| c = zgethdr(Rxhdr, 1, &rxpos); |
| |
| switch (c) { |
| case ZRINIT: |
| goto listen; /* CAF 8-21-87 */ |
| case ERROR: |
| case TIMEOUT: |
| if (++errors > Cmdtries) |
| return ERROR; |
| continue; |
| case ZCAN: |
| case ZABORT: |
| case ZFIN: |
| case ZSKIP: |
| case ZRPOS: |
| return ERROR; |
| default: |
| if (++errors > 20) |
| return ERROR; |
| continue; |
| case ZCOMPL: |
| Exitcode = rxpos; |
| saybibi(); |
| return OK; |
| case ZRQINIT: |
| vfile("******** RZ *******"); |
| system("rz"); |
| vfile("******** SZ *******"); |
| goto listen; |
| } |
| } |
| } |
| |
| /* |
| * If called as lsb use YMODEM protocol |
| */ |
| static void |
| chkinvok (const char *s) |
| { |
| const char *p; |
| |
| p = s; |
| while (*p == '-') |
| s = ++p; |
| while (*p) |
| if (*p++ == '/') |
| s = p; |
| if (*s == 'v') { |
| Verbose = 1; |
| ++s; |
| } |
| program_name = s; |
| if (*s == 'l') |
| s++; /* lsz -> sz */ |
| protocol = ZM_ZMODEM; |
| if (s[0] == 's' && s[1] == 'x') |
| protocol = ZM_XMODEM; |
| if (s[0] == 's' && (s[1] == 'b' || s[1] == 'y')) { |
| protocol = ZM_YMODEM; |
| } |
| } |
| |
| static void |
| countem (int argc, char **argv) |
| { |
| struct stat f; |
| |
| for (Totalleft = 0, Filesleft = 0; --argc >= 0; ++argv) { |
| f.st_size = -1; |
| if (Verbose > 2) { |
| vstringf ("\nCountem: %03d %s ", argc, *argv); |
| } |
| if (access (*argv, R_OK) >= 0 && stat (*argv, &f) >= 0) { |
| #if defined(S_ISDIR) |
| if (!S_ISDIR(f.st_mode) && !S_ISBLK(f.st_mode)) { |
| #else |
| int c; |
| c = f.st_mode & S_IFMT; |
| if (c != S_IFDIR && c != S_IFBLK) { |
| #endif |
| ++Filesleft; |
| Totalleft += f.st_size; |
| } |
| } else if (strcmp (*argv, "-") == 0) { |
| ++Filesleft; |
| Totalleft += DEFBYTL; |
| } |
| if (Verbose > 2) |
| vstringf (" %ld", (long) f.st_size); |
| } |
| if (Verbose > 2) |
| vstringf (_("\ncountem: Total %d %ld\n"), |
| Filesleft, Totalleft); |
| calc_blklen (Totalleft); |
| } |
| |
| /* End of lsz.c */ |
| |
| |