mm(1)
This commit is contained in:
		
							parent
							
								
									eae95ebfca
								
							
						
					
					
						commit
						a164619397
					
				
							
								
								
									
										1
									
								
								Wip/mm/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Wip/mm/Makefile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
mm: mm.c
 | 
			
		||||
							
								
								
									
										326
									
								
								Wip/mm/mm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										326
									
								
								Wip/mm/mm.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,326 @@
 | 
			
		||||
#include <errno.h> /* errno */
 | 
			
		||||
#include <stdio.h> /* fclose(3), fopen(3), fprintf(3), getc(3), putc(3),
 | 
			
		||||
                    * setvbuf(3), size_t, _IONBF, NULL */
 | 
			
		||||
#include <stdlib.h> /* free(3), realloc(3) */
 | 
			
		||||
#include <string.h> /* strerror(3) */
 | 
			
		||||
#include <unistd.h> /* getopt(3) */
 | 
			
		||||
#if !defined EX_OK || !defined EX_USAGE
 | 
			
		||||
#	include <sysexits.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct Io_ex{
 | 
			
		||||
	size_t s;
 | 
			
		||||
	FILE **files;
 | 
			
		||||
	char **names;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Io{
 | 
			
		||||
	size_t s;
 | 
			
		||||
	FILE *files[10];
 | 
			
		||||
	char *names[10];
 | 
			
		||||
	char *fmode;
 | 
			
		||||
	struct Io_ex *ex;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static char *program_name = "<no argv[0]>";
 | 
			
		||||
static char *stdin_name = "<stdin>";
 | 
			
		||||
static char *stdout_name = "<stdout>";
 | 
			
		||||
static char *stderr_name = "<stderr>";
 | 
			
		||||
static char *rmode = "rb";
 | 
			
		||||
static char *wmode = "rb+";
 | 
			
		||||
static char *wharsh = "wb";
 | 
			
		||||
 | 
			
		||||
#define terminate(io) do{ \
 | 
			
		||||
	Io_destruct(&(io)[0]); \
 | 
			
		||||
	Io_destruct(&(io)[1]); }while(0)
 | 
			
		||||
 | 
			
		||||
static struct Io *
 | 
			
		||||
Io_construct(struct Io *io){
 | 
			
		||||
 | 
			
		||||
	io->s = 0;
 | 
			
		||||
	io->ex->s = 0;
 | 
			
		||||
	io->ex->files = NULL;
 | 
			
		||||
	io->ex->names = NULL;
 | 
			
		||||
 | 
			
		||||
	return io;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct Io *
 | 
			
		||||
Io_destruct(struct Io *io){
 | 
			
		||||
	size_t i;
 | 
			
		||||
 | 
			
		||||
	for(i = 0; i < io->s; ++i)
 | 
			
		||||
		fclose(io->files[i]);
 | 
			
		||||
 | 
			
		||||
	for(i = 0; i < io->ex->s; ++i)
 | 
			
		||||
		fclose(io->ex->files[i]);
 | 
			
		||||
	free(io->ex->files);
 | 
			
		||||
	free(io->ex->names);
 | 
			
		||||
 | 
			
		||||
	return Io_construct(io); /* counter-intuitive but NULLs and 0s */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static FILE *
 | 
			
		||||
Io_fappend(struct Io *io, FILE *f, char *s){
 | 
			
		||||
 | 
			
		||||
	if(io->s == -1)
 | 
			
		||||
		io->s = 0;
 | 
			
		||||
 | 
			
		||||
	if(io->s < (sizeof (io->files)) / (sizeof *(io->files))){
 | 
			
		||||
		io->names[io->s] = s;
 | 
			
		||||
		return io->files[io->s++] = f;
 | 
			
		||||
	}else{
 | 
			
		||||
		io->ex->files = realloc(io->ex->files,
 | 
			
		||||
			(sizeof *(io->ex->files)) * ++io->ex->s);
 | 
			
		||||
		io->ex->names = realloc(io->ex->names,
 | 
			
		||||
			(sizeof *(io->ex->names)) * io->ex->s);
 | 
			
		||||
		io->ex->names[io->ex->s] = s;
 | 
			
		||||
		return io->ex->files[io->ex->s++] = f;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
Io_fileindex(struct Io *io, FILE *f){
 | 
			
		||||
	size_t i;
 | 
			
		||||
 | 
			
		||||
	for(i = 0; i < io->s; ++i)
 | 
			
		||||
		if(io->files[i] == f)
 | 
			
		||||
			return i;
 | 
			
		||||
 | 
			
		||||
	for(i = 0; i < io->ex->s; ++i)
 | 
			
		||||
		if(io->ex->files[i] == f)
 | 
			
		||||
			return i + io->s;
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *
 | 
			
		||||
Io_filename(struct Io *io, FILE *f){
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if((i = Io_fileindex(io, f)) == -1)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	else if(i < io->s - 1)
 | 
			
		||||
		return io->names[i];
 | 
			
		||||
	else
 | 
			
		||||
		return io->ex->names[i - io->s];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static FILE *
 | 
			
		||||
Io_fopen(struct Io *io, char *s){
 | 
			
		||||
	FILE *f;
 | 
			
		||||
 | 
			
		||||
	if((f = fopen(s, io->fmode)) == NULL)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	else
 | 
			
		||||
		return Io_fappend(io, f, s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct Io *Io_fremove(struct Io *io, FILE *f);
 | 
			
		||||
static FILE *Io_nextfile(struct Io *io, FILE *f);
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
Io_fputc(struct Io *io, int c){
 | 
			
		||||
	FILE *f;
 | 
			
		||||
	FILE *lf;
 | 
			
		||||
	char *ln;
 | 
			
		||||
 | 
			
		||||
	f = NULL;
 | 
			
		||||
	lf = NULL;
 | 
			
		||||
	while((f = Io_nextfile(io, f)) != NULL){
 | 
			
		||||
		if(lf != NULL){
 | 
			
		||||
			if(fclose(lf) == EOF)
 | 
			
		||||
				fprintf(stderr, "%s: %s: %s\n",
 | 
			
		||||
					program_name, ln, strerror(errno));
 | 
			
		||||
			Io_fremove(io, lf);
 | 
			
		||||
			if(io->s == 0)
 | 
			
		||||
				return EOF;
 | 
			
		||||
			lf = NULL;
 | 
			
		||||
		}
 | 
			
		||||
		if(putc(c, f) == EOF){
 | 
			
		||||
			lf = f;
 | 
			
		||||
			ln = Io_filename(io, lf);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct Io *
 | 
			
		||||
Io_fremove(struct Io *io, FILE *f){
 | 
			
		||||
	size_t i;
 | 
			
		||||
 | 
			
		||||
	for(i = 0; i < io->s && io->files[i] != f; ++i);
 | 
			
		||||
	if(io->files[i] == f){
 | 
			
		||||
		for( ; i < io->s - 1; ++i){
 | 
			
		||||
			io->files[i] = io->files[i + 1];
 | 
			
		||||
			io->names[i] = io->names[i + 1];
 | 
			
		||||
		}
 | 
			
		||||
		if(io->ex->s > 0){
 | 
			
		||||
			io->files[io->s] = io->ex->files[0];
 | 
			
		||||
			io->names[io->s] = io->ex->names[0];
 | 
			
		||||
		}else{
 | 
			
		||||
			--io->s;
 | 
			
		||||
			return io;
 | 
			
		||||
		}
 | 
			
		||||
		i = 0;
 | 
			
		||||
	}else{
 | 
			
		||||
		for(i = 0; i < io->ex->s && io->ex->files[i] != f; ++i);
 | 
			
		||||
		if(io->ex->files[i] != f)
 | 
			
		||||
			return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for( ; i < io->ex->s - 1; ++i){
 | 
			
		||||
		io->ex->files[i] = io->ex->files[i + 1];
 | 
			
		||||
		io->ex->names[i] = io->ex->names[i + 1];
 | 
			
		||||
	}
 | 
			
		||||
	--io->ex->s;
 | 
			
		||||
 | 
			
		||||
	return io;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static FILE *
 | 
			
		||||
Io_nextfile(struct Io *io, FILE *f){
 | 
			
		||||
	size_t i;
 | 
			
		||||
 | 
			
		||||
	if(f == NULL)
 | 
			
		||||
		return io->files[0];
 | 
			
		||||
 | 
			
		||||
	for(i = 0; i < io->s; ++i)
 | 
			
		||||
		if(io->files[i] == f){
 | 
			
		||||
			if(i == io->s - 1 && io->ex->s == 0)
 | 
			
		||||
				return NULL;
 | 
			
		||||
			else if(i == io->s - 1)
 | 
			
		||||
				return io->ex->files[0];
 | 
			
		||||
			else
 | 
			
		||||
				return io->files[i + 1];
 | 
			
		||||
		}
 | 
			
		||||
	for(i = 0; i < io->ex->s; ++i)
 | 
			
		||||
		if(io->ex->files[i] == f){
 | 
			
		||||
			if(i == io->ex->s - 1)
 | 
			
		||||
				break;
 | 
			
		||||
			else
 | 
			
		||||
				return io->ex->files[i + 1];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct Io *
 | 
			
		||||
Io_unbuffer(struct Io *io){
 | 
			
		||||
	size_t i;
 | 
			
		||||
 | 
			
		||||
	for(i = 0; i < io->s; ++i)
 | 
			
		||||
		setvbuf(io->files[i], NULL, _IONBF, 0);
 | 
			
		||||
 | 
			
		||||
	for(i = 0; i < io->ex->s; ++i)
 | 
			
		||||
		setvbuf(io->ex->files[i], NULL, _IONBF, 0);
 | 
			
		||||
	
 | 
			
		||||
	return io;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
oserr(char *s, char *r){
 | 
			
		||||
 | 
			
		||||
	fprintf(stderr, "%s: %s: %s\n", s, r, strerror(errno));
 | 
			
		||||
 | 
			
		||||
	return EX_OSERR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
usage(char *s){
 | 
			
		||||
 | 
			
		||||
	fprintf(stderr, "Usage: %s (-ehu) (-i [input])... (-o [output])...\n",
 | 
			
		||||
		s);
 | 
			
		||||
 | 
			
		||||
	return EX_USAGE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[]){
 | 
			
		||||
	int c;
 | 
			
		||||
	FILE *f;
 | 
			
		||||
	size_t i;
 | 
			
		||||
	struct Io io[2]; /* {read, write, error} */
 | 
			
		||||
	struct Io_ex io_ex[2];
 | 
			
		||||
	FILE *lf;
 | 
			
		||||
	char *ln;
 | 
			
		||||
	char unbuffered;
 | 
			
		||||
 | 
			
		||||
	if(argc < 2){ /* simple invocation */
 | 
			
		||||
		while((c = getc(stdin)) != EOF)
 | 
			
		||||
			if(putc(c, stdout) == EOF)
 | 
			
		||||
				break;
 | 
			
		||||
		return EX_OK;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	io[0].ex = &io_ex[0];
 | 
			
		||||
	io[1].ex = &io_ex[1];
 | 
			
		||||
	Io_construct(&io[0]);
 | 
			
		||||
	Io_construct(&io[1]);
 | 
			
		||||
	io[0].fmode = rmode;
 | 
			
		||||
	io[1].fmode = wmode;
 | 
			
		||||
 | 
			
		||||
	Io_fappend(&io[0], stdin, stdin_name);
 | 
			
		||||
	Io_fappend(&io[1], stdout, stdout_name);
 | 
			
		||||
	io[0].s = -1;
 | 
			
		||||
	io[1].s = -1;
 | 
			
		||||
 | 
			
		||||
	unbuffered = 0;
 | 
			
		||||
 | 
			
		||||
	while((c = getopt(argc, argv, "ehi:o:u")) != -1)
 | 
			
		||||
		switch(c){
 | 
			
		||||
		case 'e':
 | 
			
		||||
			Io_fappend(&io[1], stderr, stderr_name);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'i': case 'o':
 | 
			
		||||
			if(optarg[0] == '-' && optarg[1] == '\0'){
 | 
			
		||||
				/* "-" */
 | 
			
		||||
				Io_fappend(&io[c == 'o'],
 | 
			
		||||
					c == 'i' ? stdin : stdout,
 | 
			
		||||
					c == 'i' ? stdin_name : stdout_name);
 | 
			
		||||
				break;
 | 
			
		||||
			}else if(Io_fopen(&io[c == 'o'], optarg)
 | 
			
		||||
					!= NULL)
 | 
			
		||||
				break;
 | 
			
		||||
			/* does not exist, so try to create it */
 | 
			
		||||
			if(c == 'o' && errno == ENOENT){
 | 
			
		||||
				io[1].fmode = wharsh;
 | 
			
		||||
				if(Io_fopen(&io[1], optarg) != NULL){
 | 
			
		||||
					io[1].fmode = wmode;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			terminate(io);
 | 
			
		||||
			return oserr(argv[0], optarg);;
 | 
			
		||||
		case 'u':
 | 
			
		||||
			unbuffered = 1;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			terminate(io);
 | 
			
		||||
			return usage(argv[0]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	if(unbuffered){
 | 
			
		||||
		Io_unbuffer(&io[0]);
 | 
			
		||||
		Io_unbuffer(&io[1]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(io[0].s == -1)
 | 
			
		||||
		io[0].s = 1;
 | 
			
		||||
	if(io[1].s == -1)
 | 
			
		||||
		io[1].s = 1;
 | 
			
		||||
 | 
			
		||||
	lf = NULL;
 | 
			
		||||
	
 | 
			
		||||
	for(i = 0; i < io[0].s; ++i){
 | 
			
		||||
		while((c = getc(io[0].files[i])) != EOF)
 | 
			
		||||
			if(Io_fputc(&io[1], c) == EOF){ /* notebook's full */
 | 
			
		||||
				terminate(io);
 | 
			
		||||
				return EX_OK;
 | 
			
		||||
			}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	terminate(io);
 | 
			
		||||
 | 
			
		||||
	return EX_OK;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user