now mostly bug free (ignore -o)
This commit is contained in:
		
							parent
							
								
									564f7a4381
								
							
						
					
					
						commit
						c52dab536e
					
				
							
								
								
									
										249
									
								
								Wip/dj/dj.c
									
									
									
									
									
								
							
							
						
						
									
										249
									
								
								Wip/dj/dj.c
									
									
									
									
									
								
							| @ -9,15 +9,15 @@ | ||||
| 
 | ||||
| /* Macro to use the appropriate string for argv[0] in the case that
 | ||||
|  * argc == 0. */ | ||||
| #define ARGV0 (argv[0] == NULL ? "<no argv[0]>" : argv[0]) | ||||
| #define ARGV0(argv0) ((argv0) == NULL ? program_name : (argv0)) | ||||
| 
 | ||||
| #define MIN(a, b) (((a) < (b)) ? (a) : (b)) | ||||
| 
 | ||||
| /* Macro to ensure fd is not a std* file, e.g. stdin. */ | ||||
| #define fdisnotstd(fd) \ | ||||
| 		((fd) != STDIN_FILENO \ | ||||
| 		&& (fd) != STDOUT_FILENO \ | ||||
| 		&& (fd) != STDERR_FILENO) | ||||
| /* Macro to check if fd is a std* file, e.g. stdin. */ | ||||
| #define fdisstd(fd) \ | ||||
| 		((fd) == STDIN_FILENO \ | ||||
| 		|| (fd) == STDOUT_FILENO \ | ||||
| 		|| (fd) == STDERR_FILENO) | ||||
| 
 | ||||
| /* Macro to call the cleanup functions that operate on struct io on the
 | ||||
|  * particular io[2] used in main. Error conditions are not checked because this | ||||
| @ -28,29 +28,35 @@ | ||||
| 	Io_fdclose(&(io)[0]); \ | ||||
| 	Io_fdclose(&(io)[1]); }while(0) | ||||
| 
 | ||||
| static int read_flags = O_RDONLY; | ||||
| static int write_flags = O_WRONLY | O_CREAT; | ||||
| extern int errno; | ||||
| static char *fmt_asv = "%d\037%d\036%d\037%d\035%d\036%d\034"; | ||||
| static char *fmt_human = "%d+%d > %d+%d; %d > %d\n"; | ||||
| static char *fmt_output; | ||||
| static char *program_name = "<no argv[0]>"; | ||||
| static char *stdin_name = "<stdin>"; | ||||
| static char *stdout_name = "<stdout>"; | ||||
| static int read_flags = O_RDONLY; | ||||
| static int write_flags = O_WRONLY | O_CREAT; | ||||
| 
 | ||||
| /* information that needs to be kept about each file in the "pipe" */ | ||||
| struct Io{ | ||||
| 	int bs;			/* buffer size */ | ||||
| 	size_t bufuse;	/* buffer usage */ | ||||
| 	char *buf;		/* buffer */ | ||||
| 	int bytes;		/* bytes processed */ | ||||
| 	int fd;			/* file descriptor */ | ||||
| 	int fl;			/* file opening flags */ | ||||
| 	char *fn;		/* file name (may be stdin_name or stdout_name) */ | ||||
| 	int prec;		/* partial records processed */ | ||||
| 	int rec;		/* records processed */ | ||||
| 	int seek;		/* bytes to seek/skip (will be 0 after skippage) */ | ||||
| 	long seek;		/* bytes to seek/skip (will be 0 after skippage) */ | ||||
| }; | ||||
| 
 | ||||
| /* Allocates *io's buffer. Returns NULL if successful. */ | ||||
| static void * | ||||
| Io_bufalloc(struct Io *io){ | ||||
| 
 | ||||
| 	return (io->buf = malloc(io->bs * (sizeof io->buf))); | ||||
| 	return (io->buf = malloc(io->bs * (sizeof *io->buf))); | ||||
| } | ||||
| 
 | ||||
| /* Frees *io's buffer. Returns io. */ | ||||
| @ -73,6 +79,23 @@ Io_bufrpad(struct Io *io, int padding){ | ||||
| 	return io; | ||||
| } | ||||
| 
 | ||||
| /* Copies from the buffer in src as much as possible to the free space in the
 | ||||
|  * dest buffer, removing the copied units from src and permuting the remaining | ||||
|  * units in the src buffer to the start of the buffer, modifying both the src | ||||
|  * and dest bufuse and returning dest. */ | ||||
| static struct Io* | ||||
| Io_bufxapp(struct Io *dest, struct Io *src){ | ||||
| 	int n; | ||||
| 
 | ||||
| 	n = MIN(src->bufuse, dest->bs - dest->bufuse); | ||||
| 	memcpy(dest->buf + dest->bufuse, src->buf, n); | ||||
| 	dest->bufuse += n; | ||||
| 	memmove(src->buf, src->buf + n, src->bs - n); | ||||
| 	src->bufuse -= n; | ||||
| 
 | ||||
| 	return dest; | ||||
| } | ||||
| 
 | ||||
| /* Copies from the buffer in src to the buffer in dest no more than n units,
 | ||||
|  * removing the copied units from src and permuting the remaining units in the | ||||
|  * src buffer to the start of the buffer, modifying both the src and dest | ||||
| @ -90,9 +113,9 @@ Io_bufxfer(struct Io *dest, struct Io *src, int n){ | ||||
| static int | ||||
| Io_fdclose(struct Io *io){ | ||||
| 
 | ||||
| 	return fdisnotstd(io->fd) | ||||
| 		? close(io->fd) | ||||
| 		: 0; | ||||
| 	return fdisstd(io->fd) | ||||
| 		? 0 | ||||
| 		: close(io->fd); | ||||
| } | ||||
| 
 | ||||
| /* Opens io->fn and saves the file descriptor into io->fd. Returns io->fd,
 | ||||
| @ -102,7 +125,7 @@ Io_fdopen(struct Io *io, char *fn){ | ||||
| 	int fd; | ||||
| 	 | ||||
| 	if((fd = open(fn, io->fl)) != -1 | ||||
| 			&& (fdisnotstd(io->fd) || close(io->fd) == 0)){ | ||||
| 			&& Io_fdclose(io) == 0){ | ||||
| 		io->fd = fd; | ||||
| 		io->fn = fn; | ||||
| 	} | ||||
| @ -111,12 +134,61 @@ Io_fdopen(struct Io *io, char *fn){ | ||||
| } | ||||
| 
 | ||||
| /* Reads io->bs bytes from *io's file descriptor into io->buf, storing the
 | ||||
|  * number of read bytes in io->bufuse. If io->bufuse is 0, errno will probably | ||||
|  * be set. Returns io. */ | ||||
|  * number of read bytes in io->bufuse and updating io->bytes. If io->bufuse is | ||||
|  * 0, errno will probably be set. Returns io. */ | ||||
| static struct Io * | ||||
| Io_read(struct Io *io){ | ||||
| 
 | ||||
| 	io->bufuse = read(io->fd, io->buf, io->bs); | ||||
| 	io->bytes += (io->bufuse = read(io->fd, io->buf, io->bs)); | ||||
| 
 | ||||
| 	return io; | ||||
| } | ||||
| 
 | ||||
| /* Seeks io->seek bytes through *io's file descriptor, (counter-intuitively)
 | ||||
|  * returning -1 if successful and a sysexits.h exit code if an unrecoverable | ||||
|  * error occurred. */ | ||||
| static int | ||||
| Io_seek(struct Io *io){ | ||||
| 	ssize_t (*op)(int, void *, size_t); | ||||
| 	 | ||||
| 	if(!fdisstd(io->fd) && lseek(io->fd, io->seek, SEEK_SET) != -1) | ||||
| 		return -1; | ||||
| 	else if(io->fl == write_flags){ | ||||
| 		/* cheat and use bufuse as the retval for write */ | ||||
| 		memset(io->buf, '\0', io->bs); | ||||
| 		/* This is a dirty trick; rather than testing conditions and operating
 | ||||
| 		 * likewise, because the parameters to read or write are going to be | ||||
| 		 * the same either way, just use a function pointer to keep track of | ||||
| 		 * the intended operation. */ | ||||
| 		op = &write; /* Ignore -Wincompatible-pointer-types, it's fine. */ | ||||
| 		/* The difference is between const void * and void * which isn't
 | ||||
| 		 * significant enough to affect program behavior. */ | ||||
| 	}else | ||||
| 		op = &read; | ||||
| 
 | ||||
| 	do{	if(	(io->bufuse = (*op)(io->fd, io->buf, MIN(io->bs, io->seek))) == 0) | ||||
| 			/* second chance */ | ||||
| 			io->bufuse = (*op)(io->fd, io->buf, MIN(io->bs, io->seek)); | ||||
| 	}while((io->seek -= io->bufuse) > 0 && io->bufuse != 0); | ||||
| 
 | ||||
| 	io->bufuse = 0; | ||||
| 
 | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| /* Sets the variables in a struct *io to the defaults. Identifies the read/
 | ||||
|  * write ends of the "pipe" by checking io->fl. Returns io. */ | ||||
| static struct Io * | ||||
| Io_setdefaults(struct Io *io){ | ||||
| 
 | ||||
| 	io->bs = 1024; | ||||
| 	io->buf = NULL; | ||||
| 	io->bytes = 0; | ||||
| 	io->fd = (io->fl == read_flags) ? STDIN_FILENO : STDOUT_FILENO; | ||||
| 	io->fn = (io->fl == read_flags) ? stdin_name   : stdout_name; | ||||
| 	io->prec = 0; | ||||
| 	io->rec = 0; | ||||
| 	io->seek = 0; | ||||
| 
 | ||||
| 	return io; | ||||
| } | ||||
| @ -130,52 +202,7 @@ Io_write(struct Io *io){ | ||||
| 
 | ||||
| 	if((t = write(io->fd, io->buf, io->bufuse)) > 0) | ||||
| 		memmove(io->buf, io->buf + t, (io->bufuse -= t)); | ||||
| 
 | ||||
| 	return io; | ||||
| } | ||||
| 
 | ||||
| /* Seeks io->seek bytes through *io's file descriptor, (counter-intuitively)
 | ||||
|  * returning -1 if successful and a sysexits.h exit code if an unrecoverable | ||||
|  * error occurred. */ | ||||
| static int | ||||
| Io_seek(struct Io *io){ | ||||
| 	 | ||||
| 	if(fdisnotstd(io->fd) && lseek(io->fd, io->seek, SEEK_SET) == -1) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	memset(io->buf, '\0', io->bs); | ||||
| 
 | ||||
| 	do{	if(		(io->fl == read_flags | ||||
| 					&& (((io->bufuse = read(io->fd, io->buf, | ||||
| 							MIN(io->bs, io->seek))) != 0) | ||||
| 						/* second chance if 0 */ | ||||
| 						|| ((io->bufuse = read(io->fd, io->buf, | ||||
| 							MIN(io->bs, io->seek))) != 0))) | ||||
| 				/* cheat and use bufuse as the retval for write
 | ||||
| 				 * (the buffer is zeroed, it doesn't matter) */ | ||||
| 				|| (io->fl == write_flags | ||||
| 					&& ((io->bufuse = write(io->fd, io->buf, | ||||
| 							MIN(io->bs, io->seek))) != 0))) | ||||
| 			io->seek -= io->bufuse; | ||||
| 		else | ||||
| 			return EX_OK; | ||||
| 	}while(io->seek > 0); | ||||
| 
 | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| /* Sets the variables in a struct *io to the defaults. Identifies the read/
 | ||||
|  * write ends of the "pipe" by checking io->fl. Returns io. */ | ||||
| static struct Io * | ||||
| Io_setdefaults(struct Io *io){ | ||||
| 
 | ||||
| 	io->bs = 1024; | ||||
| 	io->buf = NULL; | ||||
| 	io->fd = (io->fl == read_flags) ? STDIN_FILENO : STDOUT_FILENO; | ||||
| 	io->fn = (io->fl == read_flags) ? stdin_name   : stdout_name; | ||||
| 	io->prec = 0; | ||||
| 	io->rec = 0; | ||||
| 	io->seek = 0; | ||||
| 	io->bytes += t; | ||||
| 
 | ||||
| 	return io; | ||||
| } | ||||
| @ -183,15 +210,16 @@ Io_setdefaults(struct Io *io){ | ||||
| /* Parses the string s to an integer, returning either the integer or in the
 | ||||
|  * case of an error a negative integer. This is used for argument parsing | ||||
|  * (e.g. -B [int]) in dj and no negative integer would be valid anyway. */ | ||||
| static int | ||||
| static long | ||||
| parse(char *s){ | ||||
| 	int r; | ||||
| 	long r; | ||||
| 
 | ||||
| 	errno = 0; | ||||
| 	r = (int)strtol(s, &s, 0); | ||||
| 	return (*s == '\0' /* empty string */ || errno != 0) | ||||
| 		? -1 | ||||
| 		: r; | ||||
| 	r = (long)strtol(s, &s, 0); | ||||
| 	if(*s == '\0' /* no chars left unparsed */ && errno == 0) | ||||
| 		return r; | ||||
| 	else | ||||
| 		return -1; | ||||
| } | ||||
| 
 | ||||
| /* Prints an error message suitable for the event of an operating system error,
 | ||||
| @ -209,20 +237,20 @@ oserr(char *argv0, char *s){ | ||||
| struct Io * | ||||
| output(struct Io io[2]){ | ||||
| 
 | ||||
| 	fprintf(stderr, "%d:%d > %d:%d\n", /* dd(1p) does %d+%d\n%d+%d\n */ | ||||
| 		io[0].rec, io[0].prec, io[1].rec, io[1].prec);; | ||||
| 	fprintf(stderr, fmt_output, | ||||
| 		io[0].rec, io[0].prec, io[1].rec, io[1].prec, | ||||
| 		io[0].bytes, io[1].bytes); | ||||
| 
 | ||||
| 	return io; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| usage(char **argv){ | ||||
| usage(char *argv0){ | ||||
| 
 | ||||
| 	fprintf(stderr, "Usage: %s (-fA) (-a [byte]) (-c [count]\n" | ||||
| 		"\t(-i [input file]) (-b [input block size])" | ||||
| 			" (-s [input offset])\n" | ||||
| 		"\t(-o [output file]) (-B [output block size])" | ||||
| 			" (-S [output offset])\n", ARGV0); | ||||
| 	fprintf(stderr, "Usage: %s (-fAH) (-a [byte]) (-c [count])\n" | ||||
| 		"\t(-i [input file]) (-b [input block size]) (-s [input offset])\n" | ||||
| 		"\t(-o [output file]) (-B [output block size]) (-S [output offset])\n", | ||||
| 		ARGV0(argv0)); | ||||
| 
 | ||||
| 	return EX_USAGE; | ||||
| } | ||||
| @ -239,6 +267,7 @@ int main(int argc, char *argv[]){ | ||||
| 	/* defaults */ | ||||
| 	align = -1; | ||||
| 	count = 0; | ||||
| 	fmt_output = fmt_asv; | ||||
| 	noerror = 0; | ||||
| 	io[0].fl = read_flags; | ||||
| 	Io_setdefaults(&io[0]); | ||||
| @ -246,41 +275,46 @@ int main(int argc, char *argv[]){ | ||||
| 	Io_setdefaults(&io[1]); | ||||
| 
 | ||||
| 	if(argc > 0) | ||||
| 		while((c = getopt(argc, argv, "aAc:i:b:fs:o:B:S:")) != -1) | ||||
| 			if((c == 'i' || c == 'o') | ||||
| 					&& Io_fdopen(&io[c == 'o'], optarg) != 0){ /* file args */ | ||||
| 				terminate(io); | ||||
| 				return oserr(argv[0], optarg); | ||||
| 		while((c = getopt(argc, argv, "a:Ac:i:b:fHs:o:B:S:")) != -1) | ||||
| 			if(c == 'i' || c == 'o'){ | ||||
| 				if(Io_fdopen(&io[c == 'o'], optarg) == -1){ | ||||
| 					terminate(io); | ||||
| 					return oserr(argv[0], optarg); | ||||
| 				} | ||||
| 			/* boolean/flag arguments */ | ||||
| 			}else if(c == 'f') | ||||
| 				noerror = 1; | ||||
| 			else if(c == 'A') | ||||
| 				align = '\0'; | ||||
| 			else if(c == 'H') | ||||
| 				fmt_output = fmt_human; | ||||
| 			/* optarg */ | ||||
| 			else if(c == 'a' && optarg[0] != '\0' && optarg[1] == '\0') | ||||
| 				align = optarg[0]; | ||||
| 			/* numeric args as a double negative to catch uncaught stuff */ | ||||
| 			else if(!((c == 'c' && (count = parse(optarg)) < 0) | ||||
| 					|| (c == 'b' && (io[0].bs = parse(optarg)) < 0) | ||||
| 					|| (c == 's' && (io[0].seek = parse(optarg)) < 0) | ||||
| 					|| (c == 'B' && (io[1].bs = parse(optarg)) < 0) | ||||
| 					|| (c == 'S' && (io[1].seek = parse(optarg)) < 0))){ | ||||
| 			/* numeric args */ | ||||
| 			else if(!((c == 'c' && (count = parse(optarg)) >= 0) | ||||
| 					|| (c == 'b' && (io[0].bs = parse(optarg)) >= 0) | ||||
| 					|| (c == 's' && (io[0].seek = parse(optarg)) >= 0) | ||||
| 					|| (c == 'B' && (io[1].bs = parse(optarg)) >= 0) | ||||
| 					|| (c == 'S' && (io[1].seek = parse(optarg)) >= 0))){ | ||||
| 				terminate(io); | ||||
| 				return usage(argv); | ||||
| 				return usage(argv[0]); | ||||
| 			} | ||||
| 
 | ||||
| 	if(argc > optind) | ||||
| 		return usage(argv); | ||||
| 	if(argc > optind){ | ||||
| 		terminate(io); | ||||
| 		return usage(argv[0]); | ||||
| 	} | ||||
| 
 | ||||
| 	for(p = 0; p <= 1; ++p){ | ||||
| 		if(Io_bufalloc(&io[p]) == NULL){ | ||||
| 			fprintf(stderr, "%s: Failed to allocate %d bytes\n", ARGV0, | ||||
| 				io[p].bs); | ||||
| 			fprintf(stderr, "%s: Failed to allocate %d bytes\n", | ||||
| 				ARGV0(argv[0]), io[p].bs); | ||||
| 			terminate(io); | ||||
| 			return EX_OSERR; | ||||
| 		}else if(io[p].seek > 0) /* seek */ | ||||
| 			switch(Io_seek(&io[p])){ | ||||
| 			case EX_OK:	terminate(output(io)); return EX_OK; | ||||
| 			case EX_OK:	output(io); terminate(io); return EX_OK; | ||||
| 			} | ||||
| 	} | ||||
| 
 | ||||
| @ -293,7 +327,7 @@ int main(int argc, char *argv[]){ | ||||
| 		else if(io[0].bufuse < io[0].bs){ | ||||
| 			++io[0].prec; | ||||
| 			if(noerror){ | ||||
| 				output(io); | ||||
| 				 | ||||
| 			}else | ||||
| 				count = 1; | ||||
| 			if(align >= 0) | ||||
| @ -301,26 +335,35 @@ int main(int argc, char *argv[]){ | ||||
| 		}else | ||||
| 			++io[0].rec; | ||||
| 
 | ||||
| 		/* write */ while(io[0].bufuse > 0){ | ||||
| 			Io_bufxfer(&io[1], &io[0], MIN(io[0].bufuse, io[1].bs)); | ||||
| 		/* write */ | ||||
| 		do{	if(io[1].bs > io[0].bs){ /* io[1].bs > io[0].bs */ | ||||
| 				Io_bufxapp(&io[1], &io[0]); | ||||
| 				if(io[0].bs + io[1].bufuse <= io[1].bs && count != 1) | ||||
| 					continue; /* we could write more */ | ||||
| 			}else | ||||
| 				Io_bufxfer(&io[1], &io[0], MIN(io[0].bufuse, io[1].bs)); | ||||
| 
 | ||||
| 			c = io[1].bufuse; | ||||
| 			Io_write(&io[1]); | ||||
| 			if(!noerror && io[1].bufuse == c) | ||||
| 				Io_write(&io[1]); /* second chance */ | ||||
| 			if(io[1].bufuse == c) /* no more love */ | ||||
| 			if(c == io[1].bufuse) /* no more love */ | ||||
| 				count = 1; | ||||
| 			else if(c > io[1].bufuse && io[1].bufuse > 0){ | ||||
| 				io[1].prec += 1; | ||||
| 				if(noerror){ | ||||
| 					output(io); | ||||
| 
 | ||||
| 				}else | ||||
| 					count = 1; | ||||
| 			}else if(io[1].bufuse == 0) | ||||
| 			}else if(io[1].bufuse == 0 && c < io[1].bs) | ||||
| 				++io[1].prec; | ||||
| 			else | ||||
| 				++io[1].rec; | ||||
| 		} | ||||
| 		}while(io[0].bufuse > 0); | ||||
| 	}while(count == 0 || --count > 0); | ||||
| 
 | ||||
| 	terminate(output(io)); | ||||
| 	 | ||||
| 	output(io); | ||||
| 	terminate(io); | ||||
| 
 | ||||
| 	return EX_OK; | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user