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…
Reference in New Issue
Block a user