121739Sdist /* 221739Sdist * Copyright (c) 1980 Regents of the University of California. 321739Sdist * All rights reserved. The Berkeley software License Agreement 421739Sdist * specifies the terms and conditions for redistribution. 521739Sdist */ 621739Sdist 710296Ssam #ifndef lint 8*25907Smckusick static char sccsid[] = "@(#)ftp.c 5.5 (Berkeley) 01/13/86"; 921739Sdist #endif not lint 1010296Ssam 1110296Ssam #include <sys/param.h> 1210296Ssam #include <sys/stat.h> 1310296Ssam #include <sys/ioctl.h> 1410296Ssam #include <sys/socket.h> 1513614Ssam #include <sys/time.h> 1610296Ssam 1710296Ssam #include <netinet/in.h> 1812397Ssam #include <arpa/ftp.h> 1910296Ssam 2010296Ssam #include <stdio.h> 2110296Ssam #include <signal.h> 2210296Ssam #include <errno.h> 2310296Ssam #include <netdb.h> 2410296Ssam 2510296Ssam #include "ftp_var.h" 2610296Ssam 2710296Ssam struct sockaddr_in hisctladdr; 2810296Ssam struct sockaddr_in data_addr; 2910296Ssam int data = -1; 3010296Ssam int connected; 3110296Ssam struct sockaddr_in myctladdr; 3210296Ssam 3310296Ssam FILE *cin, *cout; 3410296Ssam FILE *dataconn(); 3510296Ssam 3625904Skarels char * 3710296Ssam hookup(host, port) 3810296Ssam char *host; 3910296Ssam int port; 4010296Ssam { 4125904Skarels register struct hostent *hp = 0; 4225904Skarels static char hostnamebuf[80]; 4311627Ssam int s, len; 4410296Ssam 4510296Ssam bzero((char *)&hisctladdr, sizeof (hisctladdr)); 4625904Skarels hisctladdr.sin_addr.s_addr = inet_addr(host); 4725904Skarels if (hisctladdr.sin_addr.s_addr != -1) { 4825904Skarels hisctladdr.sin_family = AF_INET; 4925904Skarels (void) strcpy(hostnamebuf, host); 5025904Skarels } else { 5125100Sbloom hp = gethostbyname(host); 5225904Skarels if (hp == NULL) { 5325904Skarels printf("%s: unknown host\n", host); 5425904Skarels return (0); 5525904Skarels } 5625904Skarels hisctladdr.sin_family = hp->h_addrtype; 5725904Skarels bcopy(hp->h_addr_list[0], 5825904Skarels (caddr_t)&hisctladdr.sin_addr, hp->h_length); 5925904Skarels (void) strcpy(hostnamebuf, hp->h_name); 6010296Ssam } 6125904Skarels hostname = hostnamebuf; 6225904Skarels s = socket(hisctladdr.sin_family, SOCK_STREAM, 0); 6310296Ssam if (s < 0) { 6410296Ssam perror("ftp: socket"); 6510296Ssam return (0); 6610296Ssam } 6710296Ssam hisctladdr.sin_port = port; 6825904Skarels while (connect(s, (caddr_t)&hisctladdr, sizeof (hisctladdr)) < 0) { 6925904Skarels if (hp && hp->h_addr_list[1]) { 7025904Skarels int oerrno = errno; 7125904Skarels 7225904Skarels fprintf(stderr, "ftp: connect to address %s: ", 7325904Skarels inet_ntoa(hisctladdr.sin_addr)); 7425904Skarels errno = oerrno; 7525904Skarels perror(0); 7625904Skarels hp->h_addr_list++; 7725904Skarels bcopy(hp->h_addr_list[0], 7825904Skarels (caddr_t)&hisctladdr.sin_addr, hp->h_length); 7925904Skarels fprintf(stderr, "Trying %s...\n", 8025904Skarels inet_ntoa(hisctladdr.sin_addr)); 8125904Skarels continue; 8225904Skarels } 8310296Ssam perror("ftp: connect"); 8410296Ssam goto bad; 8510296Ssam } 8611627Ssam len = sizeof (myctladdr); 8711627Ssam if (getsockname(s, (char *)&myctladdr, &len) < 0) { 8811627Ssam perror("ftp: getsockname"); 8910296Ssam goto bad; 9010296Ssam } 9110296Ssam cin = fdopen(s, "r"); 9210296Ssam cout = fdopen(s, "w"); 9311219Ssam if (cin == NULL || cout == NULL) { 9410296Ssam fprintf(stderr, "ftp: fdopen failed.\n"); 9510296Ssam if (cin) 9610296Ssam fclose(cin); 9710296Ssam if (cout) 9810296Ssam fclose(cout); 9910296Ssam goto bad; 10010296Ssam } 10110296Ssam if (verbose) 10225904Skarels printf("Connected to %s.\n", hostname); 10310296Ssam (void) getreply(0); /* read startup message from server */ 10425904Skarels return (hostname); 10510296Ssam bad: 10610296Ssam close(s); 10725904Skarels return ((char *)0); 10810296Ssam } 10910296Ssam 11025904Skarels login(host) 11125904Skarels char *host; 11210296Ssam { 11310296Ssam char acct[80]; 11410296Ssam char *user, *pass; 11510296Ssam int n; 11610296Ssam 11710296Ssam user = pass = 0; 11825904Skarels ruserpass(host, &user, &pass); 11910296Ssam n = command("USER %s", user); 12010296Ssam if (n == CONTINUE) 12110296Ssam n = command("PASS %s", pass); 12210296Ssam if (n == CONTINUE) { 12310296Ssam printf("Account: "); (void) fflush(stdout); 12410296Ssam (void) fgets(acct, sizeof(acct) - 1, stdin); 12510296Ssam acct[strlen(acct) - 1] = '\0'; 12610296Ssam n = command("ACCT %s", acct); 12710296Ssam } 12810296Ssam if (n != COMPLETE) { 12910296Ssam fprintf(stderr, "Login failed.\n"); 13010296Ssam return (0); 13110296Ssam } 13210296Ssam return (1); 13310296Ssam } 13410296Ssam 13510296Ssam /*VARARGS 1*/ 13610296Ssam command(fmt, args) 13710296Ssam char *fmt; 13810296Ssam { 13910296Ssam 14010296Ssam if (debug) { 14110296Ssam printf("---> "); 14210296Ssam _doprnt(fmt, &args, stdout); 14310296Ssam printf("\n"); 14410296Ssam (void) fflush(stdout); 14510296Ssam } 14611219Ssam if (cout == NULL) { 14711219Ssam perror ("No control connection for command"); 14811219Ssam return (0); 14911219Ssam } 15010296Ssam _doprnt(fmt, &args, cout); 15110296Ssam fprintf(cout, "\r\n"); 15210296Ssam (void) fflush(cout); 15310296Ssam return (getreply(!strcmp(fmt, "QUIT"))); 15410296Ssam } 15510296Ssam 15610296Ssam #include <ctype.h> 15710296Ssam 15810296Ssam getreply(expecteof) 15910296Ssam int expecteof; 16010296Ssam { 16111219Ssam register int c, n; 16210296Ssam register int code, dig; 16310296Ssam int originalcode = 0, continuation = 0; 16410296Ssam 16510296Ssam for (;;) { 16610296Ssam dig = n = code = 0; 16710296Ssam while ((c = getc(cin)) != '\n') { 16810296Ssam dig++; 16910296Ssam if (c == EOF) { 17010296Ssam if (expecteof) 17110296Ssam return (0); 17210296Ssam lostpeer(); 17310296Ssam exit(1); 17410296Ssam } 17510296Ssam if (verbose && c != '\r' || 17610296Ssam (n == '5' && dig > 4)) 17710296Ssam putchar(c); 17810296Ssam if (dig < 4 && isdigit(c)) 17910296Ssam code = code * 10 + (c - '0'); 18010296Ssam if (dig == 4 && c == '-') 18110296Ssam continuation++; 18210296Ssam if (n == 0) 18310296Ssam n = c; 18410296Ssam } 18511346Ssam if (verbose || n == '5') { 18610296Ssam putchar(c); 18711346Ssam (void) fflush (stdout); 18811346Ssam } 18910296Ssam if (continuation && code != originalcode) { 19010296Ssam if (originalcode == 0) 19110296Ssam originalcode = code; 19210296Ssam continue; 19310296Ssam } 194*25907Smckusick return (n - '0'); 19510296Ssam } 19610296Ssam } 19710296Ssam 19810296Ssam jmp_buf sendabort; 19910296Ssam 20010296Ssam abortsend() 20110296Ssam { 20210296Ssam 20310296Ssam longjmp(sendabort, 1); 20410296Ssam } 20510296Ssam 20610296Ssam sendrequest(cmd, local, remote) 20710296Ssam char *cmd, *local, *remote; 20810296Ssam { 20910296Ssam FILE *fin, *dout, *popen(); 21010296Ssam int (*closefunc)(), pclose(), fclose(), (*oldintr)(); 21111219Ssam char buf[BUFSIZ]; 212*25907Smckusick int expectingreply = 0; 21311651Ssam long bytes = 0, hashbytes = sizeof (buf); 21411346Ssam register int c, d; 21510296Ssam struct stat st; 21610296Ssam struct timeval start, stop; 21710296Ssam 21810296Ssam closefunc = NULL; 21910296Ssam if (setjmp(sendabort)) 22010296Ssam goto bad; 22110296Ssam oldintr = signal(SIGINT, abortsend); 22210296Ssam if (strcmp(local, "-") == 0) 22310296Ssam fin = stdin; 22410296Ssam else if (*local == '|') { 225*25907Smckusick /* 226*25907Smckusick * Advance local so further uses just yield file name 227*25907Smckusick * thus later references for error messages need not check 228*25907Smckusick * for '|' special case. 229*25907Smckusick */ 230*25907Smckusick local += 1; 231*25907Smckusick fin = popen(local, "r"); 23210296Ssam if (fin == NULL) { 233*25907Smckusick perror(local); 23410296Ssam goto bad; 23510296Ssam } 23610296Ssam closefunc = pclose; 23710296Ssam } else { 23810296Ssam fin = fopen(local, "r"); 23910296Ssam if (fin == NULL) { 24010296Ssam perror(local); 24110296Ssam goto bad; 24210296Ssam } 24310296Ssam closefunc = fclose; 24410296Ssam if (fstat(fileno(fin), &st) < 0 || 24510296Ssam (st.st_mode&S_IFMT) != S_IFREG) { 24617949Sralph fprintf(stderr, "%s: not a plain file.\n", local); 24710296Ssam goto bad; 24810296Ssam } 24910296Ssam } 25010296Ssam if (initconn()) 25110296Ssam goto bad; 25210296Ssam if (remote) { 25310296Ssam if (command("%s %s", cmd, remote) != PRELIM) 25410296Ssam goto bad; 25510296Ssam } else 25610296Ssam if (command("%s", cmd) != PRELIM) 25710296Ssam goto bad; 258*25907Smckusick expectingreply++; /* got preliminary reply, expecting final reply */ 25910296Ssam dout = dataconn("w"); 26010296Ssam if (dout == NULL) 26110296Ssam goto bad; 26210296Ssam gettimeofday(&start, (struct timezone *)0); 26311219Ssam switch (type) { 26411219Ssam 26511219Ssam case TYPE_I: 26611219Ssam case TYPE_L: 26711346Ssam errno = d = 0; 26811219Ssam while ((c = read(fileno (fin), buf, sizeof (buf))) > 0) { 26911346Ssam if ((d = write(fileno (dout), buf, c)) < 0) 27011219Ssam break; 27111219Ssam bytes += c; 27211651Ssam if (hash) { 27311651Ssam putchar('#'); 27411651Ssam fflush(stdout); 27511651Ssam } 27611219Ssam } 27713213Ssam if (hash && bytes > 0) { 27811651Ssam putchar('\n'); 27911651Ssam fflush(stdout); 28011651Ssam } 28111219Ssam if (c < 0) 28211219Ssam perror(local); 28311346Ssam if (d < 0) 28411219Ssam perror("netout"); 28511219Ssam break; 28611219Ssam 28711219Ssam case TYPE_A: 28811219Ssam while ((c = getc(fin)) != EOF) { 28911219Ssam if (c == '\n') { 29011651Ssam while (hash && (bytes >= hashbytes)) { 29111651Ssam putchar('#'); 29211651Ssam fflush(stdout); 29311651Ssam hashbytes += sizeof (buf); 29411651Ssam } 29511219Ssam if (ferror(dout)) 29611219Ssam break; 29711219Ssam putc('\r', dout); 29811219Ssam bytes++; 29911219Ssam } 30011219Ssam putc(c, dout); 30111219Ssam bytes++; 30211219Ssam if (c == '\r') { 30311219Ssam putc('\0', dout); 30411219Ssam bytes++; 30511219Ssam } 30611219Ssam } 30711651Ssam if (hash) { 30813213Ssam if (bytes < hashbytes) 30913213Ssam putchar('#'); 31011651Ssam putchar('\n'); 31111651Ssam fflush(stdout); 31211651Ssam } 31311219Ssam if (ferror(fin)) 31411219Ssam perror(local); 31511346Ssam if (ferror(dout)) 31611219Ssam perror("netout"); 31711219Ssam break; 31810296Ssam } 31910296Ssam gettimeofday(&stop, (struct timezone *)0); 32010296Ssam if (closefunc != NULL) 321*25907Smckusick (*closefunc)(fin), closefunc = NULL; 32210296Ssam (void) fclose(dout); 32310296Ssam done: 324*25907Smckusick if (expectingreply) { 325*25907Smckusick (void) getreply(0); 326*25907Smckusick expectingreply = 0; 327*25907Smckusick } 32810296Ssam signal(SIGINT, oldintr); 32910296Ssam if (bytes > 0 && verbose) 33010296Ssam ptransfer("sent", bytes, &start, &stop); 33110296Ssam return; 33210296Ssam bad: 33310296Ssam if (data >= 0) 33410296Ssam (void) close(data), data = -1; 33510296Ssam if (closefunc != NULL && fin != NULL) 336*25907Smckusick (*closefunc)(fin), closefunc = NULL; 337*25907Smckusick bytes = 0; /* so we don't print a message if the transfer was aborted */ 33810296Ssam goto done; 33910296Ssam } 34010296Ssam 34110296Ssam jmp_buf recvabort; 34210296Ssam 34310296Ssam abortrecv() 34410296Ssam { 34510296Ssam 34610296Ssam longjmp(recvabort, 1); 34710296Ssam } 34810296Ssam 34911651Ssam recvrequest(cmd, local, remote, mode) 35011651Ssam char *cmd, *local, *remote, *mode; 35110296Ssam { 35210296Ssam FILE *fout, *din, *popen(); 35311651Ssam int (*closefunc)(), pclose(), fclose(), (*oldintr)(); 35411219Ssam char buf[BUFSIZ]; 355*25907Smckusick int expectingreply = 0; 35611651Ssam long bytes = 0, hashbytes = sizeof (buf); 35711346Ssam register int c, d; 35810296Ssam struct timeval start, stop; 35910296Ssam 36010296Ssam closefunc = NULL; 36110296Ssam if (setjmp(recvabort)) 36210296Ssam goto bad; 36310296Ssam oldintr = signal(SIGINT, abortrecv); 36410296Ssam if (strcmp(local, "-") && *local != '|') 36510296Ssam if (access(local, 2) < 0) { 366*25907Smckusick if (errno == ENOENT) { 367*25907Smckusick char *dir = rindex(local, '/'); 36810296Ssam 369*25907Smckusick if (dir != NULL) 370*25907Smckusick *dir = 0; 371*25907Smckusick d = access(dir ? local : ".", 2); 372*25907Smckusick if (dir != NULL) 373*25907Smckusick *dir = '/'; 374*25907Smckusick if (d < 0) { 375*25907Smckusick perror(local); 376*25907Smckusick goto bad; 377*25907Smckusick } 378*25907Smckusick } else { 37910296Ssam perror(local); 38010296Ssam goto bad; 38110296Ssam } 38210296Ssam } 38310296Ssam if (initconn()) 38410296Ssam goto bad; 38510296Ssam if (remote) { 38610296Ssam if (command("%s %s", cmd, remote) != PRELIM) 38710296Ssam goto bad; 38810296Ssam } else 38910296Ssam if (command("%s", cmd) != PRELIM) 39010296Ssam goto bad; 391*25907Smckusick expectingreply++; /* got preliminary reply, expecting final reply */ 39210296Ssam if (strcmp(local, "-") == 0) 39310296Ssam fout = stdout; 39410296Ssam else if (*local == '|') { 395*25907Smckusick /* 396*25907Smckusick * Advance local over '|' so don't need to check for 397*25907Smckusick * '|' special case any further. 398*25907Smckusick */ 399*25907Smckusick local += 1; 400*25907Smckusick fout = popen(local, "w"); 40110296Ssam closefunc = pclose; 40210296Ssam } else { 40311651Ssam fout = fopen(local, mode); 40410296Ssam closefunc = fclose; 40510296Ssam } 40610296Ssam if (fout == NULL) { 407*25907Smckusick perror(local); 40810296Ssam goto bad; 40910296Ssam } 41010296Ssam din = dataconn("r"); 41110296Ssam if (din == NULL) 41210296Ssam goto bad; 41310296Ssam gettimeofday(&start, (struct timezone *)0); 41411219Ssam switch (type) { 41511219Ssam 41611219Ssam case TYPE_I: 41711219Ssam case TYPE_L: 41811346Ssam errno = d = 0; 41911219Ssam while ((c = read(fileno(din), buf, sizeof (buf))) > 0) { 42011346Ssam if ((d = write(fileno(fout), buf, c)) < 0) 42111219Ssam break; 42211219Ssam bytes += c; 42311651Ssam if (hash) { 42411651Ssam putchar('#'); 42511651Ssam fflush(stdout); 42611651Ssam } 42711219Ssam } 42813213Ssam if (hash && bytes > 0) { 42911651Ssam putchar('\n'); 43011651Ssam fflush(stdout); 43111651Ssam } 43211219Ssam if (c < 0) 43311219Ssam perror("netin"); 43411346Ssam if (d < 0) 43510296Ssam perror(local); 43611219Ssam break; 43711219Ssam 43811219Ssam case TYPE_A: 43911219Ssam while ((c = getc(din)) != EOF) { 44011219Ssam if (c == '\r') { 44111651Ssam while (hash && (bytes >= hashbytes)) { 44211651Ssam putchar('#'); 44311651Ssam fflush(stdout); 44411651Ssam hashbytes += sizeof (buf); 44511651Ssam } 44610296Ssam bytes++; 44711219Ssam if ((c = getc(din)) != '\n') { 44811219Ssam if (ferror (fout)) 44911219Ssam break; 45011219Ssam putc ('\r', fout); 45111219Ssam } 45211219Ssam if (c == '\0') { 45311219Ssam bytes++; 45411219Ssam continue; 45511219Ssam } 45611219Ssam } 45711219Ssam putc (c, fout); 45811219Ssam bytes++; 45910296Ssam } 46011651Ssam if (hash) { 46113213Ssam if (bytes < hashbytes) 46213213Ssam putchar('#'); 46311651Ssam putchar('\n'); 46411651Ssam fflush(stdout); 46511651Ssam } 46611219Ssam if (ferror (din)) 46711219Ssam perror ("netin"); 46811219Ssam if (ferror (fout)) 46911219Ssam perror (local); 47011219Ssam break; 47110296Ssam } 47210296Ssam gettimeofday(&stop, (struct timezone *)0); 47310296Ssam (void) fclose(din); 47410296Ssam if (closefunc != NULL) 475*25907Smckusick (*closefunc)(fout), closefunc = NULL; 47610296Ssam done: 477*25907Smckusick if (expectingreply) { 478*25907Smckusick (void) getreply(0); 479*25907Smckusick expectingreply = 0; 480*25907Smckusick } 48110296Ssam signal(SIGINT, oldintr); 48210296Ssam if (bytes > 0 && verbose) 48310296Ssam ptransfer("received", bytes, &start, &stop); 48410296Ssam return; 48510296Ssam bad: 48610296Ssam if (data >= 0) 48710296Ssam (void) close(data), data = -1; 48810296Ssam if (closefunc != NULL && fout != NULL) 48910296Ssam (*closefunc)(fout); 490*25907Smckusick bytes = 0; /* so we don't print a message if the transfer was aborted */ 49110296Ssam goto done; 49210296Ssam } 49310296Ssam 49410296Ssam /* 49510296Ssam * Need to start a listen on the data channel 49610296Ssam * before we send the command, otherwise the 49710296Ssam * server's connect may fail. 49810296Ssam */ 49911651Ssam static int sendport = -1; 50011651Ssam 50110296Ssam initconn() 50210296Ssam { 50310296Ssam register char *p, *a; 50411627Ssam int result, len; 50517450Slepreau int on = 1; 50610296Ssam 50711651Ssam noport: 50810296Ssam data_addr = myctladdr; 50911651Ssam if (sendport) 51011651Ssam data_addr.sin_port = 0; /* let system pick one */ 51111651Ssam if (data != -1) 51211651Ssam (void) close (data); 51318287Sralph data = socket(AF_INET, SOCK_STREAM, 0); 51410296Ssam if (data < 0) { 51510296Ssam perror("ftp: socket"); 51610296Ssam return (1); 51710296Ssam } 51812397Ssam if (!sendport) 51917450Slepreau if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) { 520*25907Smckusick perror("ftp: setsockopt (reuse address)"); 52112397Ssam goto bad; 52212397Ssam } 52310296Ssam if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) { 52410296Ssam perror("ftp: bind"); 52510296Ssam goto bad; 52610296Ssam } 52710296Ssam if (options & SO_DEBUG && 52817450Slepreau setsockopt(data, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0) 52910296Ssam perror("ftp: setsockopt (ignored)"); 53011627Ssam len = sizeof (data_addr); 53111627Ssam if (getsockname(data, (char *)&data_addr, &len) < 0) { 53211627Ssam perror("ftp: getsockname"); 53310296Ssam goto bad; 53410296Ssam } 53510296Ssam if (listen(data, 1) < 0) { 53610296Ssam perror("ftp: listen"); 53710296Ssam goto bad; 53810296Ssam } 53911651Ssam if (sendport) { 54011651Ssam a = (char *)&data_addr.sin_addr; 54111651Ssam p = (char *)&data_addr.sin_port; 54210296Ssam #define UC(b) (((int)b)&0xff) 54311651Ssam result = 54411651Ssam command("PORT %d,%d,%d,%d,%d,%d", 54511651Ssam UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 54611651Ssam UC(p[0]), UC(p[1])); 54711651Ssam if (result == ERROR && sendport == -1) { 54811651Ssam sendport = 0; 54911651Ssam goto noport; 55011651Ssam } 55111651Ssam return (result != COMPLETE); 55211651Ssam } 55311651Ssam return (0); 55410296Ssam bad: 55510296Ssam (void) close(data), data = -1; 55610296Ssam return (1); 55710296Ssam } 55810296Ssam 55910296Ssam FILE * 56010296Ssam dataconn(mode) 56110296Ssam char *mode; 56210296Ssam { 56310296Ssam struct sockaddr_in from; 56410296Ssam int s, fromlen = sizeof (from); 56510296Ssam 56610296Ssam s = accept(data, &from, &fromlen, 0); 56710296Ssam if (s < 0) { 56810296Ssam perror("ftp: accept"); 56910296Ssam (void) close(data), data = -1; 57010296Ssam return (NULL); 57110296Ssam } 57210296Ssam (void) close(data); 57310296Ssam data = s; 57410296Ssam return (fdopen(data, mode)); 57510296Ssam } 57610296Ssam 57710296Ssam ptransfer(direction, bytes, t0, t1) 57810296Ssam char *direction; 57911651Ssam long bytes; 58010296Ssam struct timeval *t0, *t1; 58110296Ssam { 58210296Ssam struct timeval td; 58316437Sleres float s, bs; 58410296Ssam 58510296Ssam tvsub(&td, t1, t0); 58616437Sleres s = td.tv_sec + (td.tv_usec / 1000000.); 58710296Ssam #define nz(x) ((x) == 0 ? 1 : (x)) 58816437Sleres bs = bytes / nz(s); 58916437Sleres printf("%ld bytes %s in %.2g seconds (%.2g Kbytes/s)\n", 59016437Sleres bytes, direction, s, bs / 1024.); 59110296Ssam } 59210296Ssam 59310296Ssam tvadd(tsum, t0) 59410296Ssam struct timeval *tsum, *t0; 59510296Ssam { 59610296Ssam 59710296Ssam tsum->tv_sec += t0->tv_sec; 59810296Ssam tsum->tv_usec += t0->tv_usec; 59910296Ssam if (tsum->tv_usec > 1000000) 60010296Ssam tsum->tv_sec++, tsum->tv_usec -= 1000000; 60110296Ssam } 60210296Ssam 60310296Ssam tvsub(tdiff, t1, t0) 60410296Ssam struct timeval *tdiff, *t1, *t0; 60510296Ssam { 60610296Ssam 60710296Ssam tdiff->tv_sec = t1->tv_sec - t0->tv_sec; 60810296Ssam tdiff->tv_usec = t1->tv_usec - t0->tv_usec; 60910296Ssam if (tdiff->tv_usec < 0) 61010296Ssam tdiff->tv_sec--, tdiff->tv_usec += 1000000; 61110296Ssam } 612