/* This program is in the public domain. */ #include #include #include #include #include #include #ifndef __NetBSD__ typedef unsigned long long int u_quad_t; #endif extern const char *__progname; static int pflag = 0; static int Pflag = 0; static int argno = 0; typedef struct state STATE; #define BUFFER_SIZE 65536 struct state { int ifd; int ofd; unsigned int closed : 1; int fill; int head; int tail; char buf[BUFFER_SIZE]; } ; static STATE state[2]; static u_quad_t offset = 0; static const char *fmt_u_quad_t(u_quad_t val) { static char buf[128]; char *bp; if (val == 0) return("0"); bp = &buf[128]; *--bp = '\0'; while (val) { *--bp = '0' + (val % 10); val /= 10; } return(bp); } static void handleargs(int ac, char **av) { long int iv; char *rp; int errs; errs = 0; for (ac--,av++;ac;ac--,av++) { if (!strcmp(*av,"-p")) { pflag = 1; continue; } if (!strcmp(*av,"-P")) { Pflag = 1; continue; } iv = strtol(*av,&rp,0); if (*rp || (rp == *av)) { fprintf(stderr,"%s: unrecognized argument %s\n",__progname,*av); errs ++; } if (iv < 0) iv = - iv; switch (argno++) { case 0: state[0].ifd = iv; break; case 1: state[1].ifd = iv; break; case 2: state[0].ofd = iv; break; case 3: state[1].ofd = iv; break; default: fprintf(stderr,"%s: unrecognized argument %s\n",__progname,*av); errs ++; break; } } if (argno != (Pflag?4:2)) { fprintf(stderr,"%s: need exactly %s descriptor numbers\n",__progname,Pflag?"four":"two"); errs ++; } if (errs) exit(1); } static void init_state(STATE *s) { s->closed = 0; s->fill = 0; s->head = 0; s->tail = 0; } static void init(void) { init_state(&state[0]); init_state(&state[1]); } static int n_avail(STATE *s) { int n; n = s->fill; if (s->tail+n > BUFFER_SIZE) n = BUFFER_SIZE - s->tail; return(n); } static int n_free(STATE *s) { int n; n = BUFFER_SIZE - s->fill; if (s->head+n > BUFFER_SIZE) n = BUFFER_SIZE - s->head; return(n); } static void *ptr_t(STATE *s) { return(&s->buf[s->tail]); } static void *ptr_h(STATE *s) { return(&s->buf[s->head]); } static void consume(STATE *s, int n) { s->tail += n; s->fill -= n; if (s->fill < 1) { if (s->fill < 0) abort(); s->fill = 0; s->head = 0; s->tail = 0; } else if (s->tail >= BUFFER_SIZE) { if (s->tail > BUFFER_SIZE) abort(); s->tail = 0; } } static void provide(STATE *s, int n) { s->head += n; s->fill += n; if (s->head >= BUFFER_SIZE) { if (s->head > BUFFER_SIZE) abort(); s->head = 0; } } static int compare_some(void) { int n; int n2; while (1) { n = n_avail(&state[0]); n2 = n_avail(&state[1]); if (n2 < n) n = n2; if (n < 1) return(0); if (!bcmp(ptr_t(&state[0]),ptr_t(&state[1]),n)) { consume(&state[0],n); consume(&state[1],n); offset += n; continue; } break; } while (n > 1) { n2 = n / 2; if (!bcmp(ptr_t(&state[0]),ptr_t(&state[1]),n2)) { consume(&state[0],n2); consume(&state[1],n2); offset += n2; n -= n2; } else { n = n2; } } printf("difference at %s\n",fmt_u_quad_t(offset)); return(1); } static void read_some(STATE *s) { int n; int r; if (s->closed) return; n = n_free(s); if (n < 1) return; r = read(s->ifd,ptr_h(s),n); if (r < 0) { fprintf(stderr,"%s: read error on %d: %s\n",__progname,s->ifd,strerror(errno)); exit(1); } if (r == 0) { s->closed = 1; return; } provide(s,r); } static void run_compare(void) { fd_set fds; int i; int maxfd; STATE *s; while (1) { if (compare_some()) return; FD_ZERO(&fds); maxfd = -1; for (i=0;i<2;i++) { s = &state[i]; if (s->closed) { if (s->fill == 0) { STATE *s2; s2 = &state[!i]; if ((s2->fill == 0) && !s2->closed) read_some(s2); if ((s2->fill != 0) || !s2->closed) { printf("EOF on %d at %s\n",s->ifd,fmt_u_quad_t(offset)); } else { printf("identical\n"); } return; } continue; } if (n_free(s) > 0) { FD_SET(s->ifd,&fds); if (s->ifd > maxfd) maxfd = s->ifd; } } if (maxfd < 0) abort(); i = select(maxfd+1,&fds,0,0,0); if (i < 0) { if (errno == EINTR) continue; fprintf(stderr,"%s: select; %s\n",__progname,strerror(errno)); exit(1); } for (i=0;i<2;i++) { s = &state[i]; if (FD_ISSET(s->ifd,&fds)) read_some(s); } } } static void run_drains(void) { fd_set fds; int i; int n; STATE *s; int maxfd; while (1) { maxfd = -1; FD_ZERO(&fds); for (i=0;i<2;i++) { s = &state[i]; while (1) { n = n_avail(s); if (n < 1) break; write(s->ofd,ptr_t(s),n); consume(s,n); } if (s->closed) continue; FD_SET(s->ifd,&fds); if (s->ifd > maxfd) maxfd = s->ifd; } if (maxfd < 0) break; i = select(maxfd+1,&fds,0,0,0); if (i < 0) { if (errno == EINTR) continue; fprintf(stderr,"%s: select; %s\n",__progname,strerror(errno)); exit(1); } for (i=0;i<2;i++) { s = &state[i]; if (FD_ISSET(s->ifd,&fds)) read_some(s); } } } static void run_drain(void) { STATE *s; int n; s = &state[0]; while (1) { read_some(s); if (! s->closed) break; n = n_avail(s); if (n == 0) continue; write(1,ptr_t(s),n); consume(s,n); } } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); init(); run_compare(); if (Pflag) run_drains(); if (pflag) run_drain(); exit(0); }