mm(1): rewritten in Rust
This commit is contained in:
		
							parent
							
								
									b7bc1f16ad
								
							
						
					
					
						commit
						eea61467a0
					
				
							
								
								
									
										4
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								Makefile
									
									
									
									
									
								
							@ -114,8 +114,8 @@ build/bin/intcmp: src/intcmp.c build
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
.PHONY: mm
 | 
					.PHONY: mm
 | 
				
			||||||
mm: build/bin/mm
 | 
					mm: build/bin/mm
 | 
				
			||||||
build/bin/mm: src/mm.c build
 | 
					build/bin/mm: src/mm.rs build rustlibs 
 | 
				
			||||||
	$(CC) $(CFLAGS) -o $@ src/mm.c
 | 
						$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/mm.rs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: npc
 | 
					.PHONY: npc
 | 
				
			||||||
npc: build/bin/npc
 | 
					npc: build/bin/npc
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										236
									
								
								src/mm.c
									
									
									
									
									
								
							
							
						
						
									
										236
									
								
								src/mm.c
									
									
									
									
									
								
							@ -1,236 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * Copyright (c) 2024 DTB <trinity@trinity.moe>
 | 
					 | 
				
			||||||
 * SPDX-License-Identifier: AGPL-3.0-or-later
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify it under
 | 
					 | 
				
			||||||
 * the terms of the GNU Affero General Public License as published by the Free
 | 
					 | 
				
			||||||
 * Software Foundation, either version 3 of the License, or (at your option) any
 | 
					 | 
				
			||||||
 * later version.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					 | 
				
			||||||
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					 | 
				
			||||||
 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 | 
					 | 
				
			||||||
 * details.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
 * along with this program. If not, see https://www.gnu.org/licenses/.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <errno.h> /* errno */
 | 
					 | 
				
			||||||
#include <signal.h> /* signal(2), SIG_ERR, SIG_IGN, SIGINT */
 | 
					 | 
				
			||||||
#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> /* strcmp(3), strerror(3) */
 | 
					 | 
				
			||||||
#include <unistd.h> /* getopt(3) */
 | 
					 | 
				
			||||||
#if !defined EX_IOERR || !defined EX_OK || !defined EX_OSERR \
 | 
					 | 
				
			||||||
		|| !defined EX_USAGE
 | 
					 | 
				
			||||||
#	include <sysexits.h>
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
extern int errno;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* This structure is how open files are tracked. */
 | 
					 | 
				
			||||||
struct Files{
 | 
					 | 
				
			||||||
	size_t a;	/* allocation */
 | 
					 | 
				
			||||||
	size_t s;	/* used size */
 | 
					 | 
				
			||||||
	char *mode;	/* file opening mode */
 | 
					 | 
				
			||||||
	char **names;	/* file names */
 | 
					 | 
				
			||||||
	FILE **files;	/* file pointers */
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* How much to grow the allocation when it's saturated. */
 | 
					 | 
				
			||||||
#ifndef ALLOC_INCREMENT
 | 
					 | 
				
			||||||
#	define ALLOC_INCREMENT 1
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* How much to grow the allocation at program start. */
 | 
					 | 
				
			||||||
#ifndef ALLOC_INITIAL
 | 
					 | 
				
			||||||
#	define ALLOC_INITIAL 10
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* pre-allocated strings */
 | 
					 | 
				
			||||||
static char *program_name = "<no argv[0]>";
 | 
					 | 
				
			||||||
static char *stdin_name = "<stdin>";
 | 
					 | 
				
			||||||
static char *stdout_name = "<stdout>";
 | 
					 | 
				
			||||||
static char *stderr_name = "<stderr>";
 | 
					 | 
				
			||||||
static char *(fmode[]) = { (char []){"rb"}, (char []){"rb+"} };
 | 
					 | 
				
			||||||
static char *wharsh = "wb";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Adds the open FILE pointer for the file at the path s to the files struct,
 | 
					 | 
				
			||||||
 * returning the FILE if successful and NULL if not, allocating more memory in
 | 
					 | 
				
			||||||
 * the files buffers as needed. */
 | 
					 | 
				
			||||||
static FILE *
 | 
					 | 
				
			||||||
Files_append(struct Files *files, FILE *file, char *name){
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(file == NULL || (files->s == files->a
 | 
					 | 
				
			||||||
			&& ((files->files = realloc(files->files,
 | 
					 | 
				
			||||||
					(files->a += (files->a == 0)
 | 
					 | 
				
			||||||
						? ALLOC_INITIAL
 | 
					 | 
				
			||||||
						: ALLOC_INCREMENT)
 | 
					 | 
				
			||||||
					* sizeof *(files->files))) == NULL
 | 
					 | 
				
			||||||
				|| (files->names = realloc(files->names,
 | 
					 | 
				
			||||||
					files->a * sizeof *(files->names))) == NULL)))
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	files->names[files->s] = name;
 | 
					 | 
				
			||||||
	return files->files[files->s++] = file;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Opens the file at the path p and puts it in the files struct, returning NULL
 | 
					 | 
				
			||||||
 * if either the opening or the placement of the open FILE pointer fail. */
 | 
					 | 
				
			||||||
#define Files_open(files, p) \
 | 
					 | 
				
			||||||
	Files_append((files), fopen((p), (files)->mode), (p))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Prints a diagnostic message based on errno and returns an exit status
 | 
					 | 
				
			||||||
 * appropriate for an OS error. */
 | 
					 | 
				
			||||||
static int
 | 
					 | 
				
			||||||
oserr(char *s, char *r){
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fprintf(stderr, "%s: %s: %s\n", s, r, strerror(errno));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return EX_OSERR;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Hijacks i and j from main and destructs the files[2] struct used by main by
 | 
					 | 
				
			||||||
 * closing its files and freeing its files and names arrays, returning retval
 | 
					 | 
				
			||||||
 * from main. */
 | 
					 | 
				
			||||||
#define terminate \
 | 
					 | 
				
			||||||
	for(i = 0; i < 2; ++i){ \
 | 
					 | 
				
			||||||
		for(j = 0; j < files[i].s; ++j) \
 | 
					 | 
				
			||||||
			if(files[i].files[j] != stdin \
 | 
					 | 
				
			||||||
					&& files[i].files[j] != stdout \
 | 
					 | 
				
			||||||
					&& files[i].files[j] != stderr) \
 | 
					 | 
				
			||||||
				fclose(files[i].files[j]); \
 | 
					 | 
				
			||||||
		free(files[i].files); \
 | 
					 | 
				
			||||||
		free(files[i].names); \
 | 
					 | 
				
			||||||
	} \
 | 
					 | 
				
			||||||
	return retval
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Prints a usage text, in which s is the program being run (i.e. argv[0]), and
 | 
					 | 
				
			||||||
 * returns an exit status appropriate for a usage error. */
 | 
					 | 
				
			||||||
int usage(char *s){
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fprintf(stderr, "Usage: %s [-aenu] [-i input]... [-o output]...\n", s);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return EX_USAGE;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int main(int argc, char *argv[]){
 | 
					 | 
				
			||||||
	int c;
 | 
					 | 
				
			||||||
	struct Files files[2]; /* {read, write} */
 | 
					 | 
				
			||||||
	size_t i;
 | 
					 | 
				
			||||||
	size_t j;
 | 
					 | 
				
			||||||
	size_t k; /* loop index but also unbuffer status */
 | 
					 | 
				
			||||||
	int retval;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Initializes the files structs with their default values, standard
 | 
					 | 
				
			||||||
	 * input and standard output. If an input or an output is specified
 | 
					 | 
				
			||||||
	 * these initial values will be overwritten, so to, say, use mm(1)
 | 
					 | 
				
			||||||
	 * equivalently to tee(1p), -o - will need to be specified before
 | 
					 | 
				
			||||||
	 * additional files to ensure standard output is still written. */
 | 
					 | 
				
			||||||
	for(i = 0; i < 2; ++i){
 | 
					 | 
				
			||||||
		files[i].a = 0;
 | 
					 | 
				
			||||||
		files[i].s = 0;
 | 
					 | 
				
			||||||
		files[i].mode = fmode[i];
 | 
					 | 
				
			||||||
		files[i].files = NULL;
 | 
					 | 
				
			||||||
		files[i].names = NULL;
 | 
					 | 
				
			||||||
		Files_append(&files[i], i == 0 ? stdin : stdout,
 | 
					 | 
				
			||||||
			i == 0 ? stdin_name : stdout_name);
 | 
					 | 
				
			||||||
		files[i].s = 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	k = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(argc > 0)
 | 
					 | 
				
			||||||
		program_name = argv[0];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(argc > 1)
 | 
					 | 
				
			||||||
		while((c = getopt(argc, argv, "aehi:no:u")) != -1)
 | 
					 | 
				
			||||||
			switch(c){
 | 
					 | 
				
			||||||
			case 'a': /* "rb+" -> "ab" */
 | 
					 | 
				
			||||||
				files[1].mode[0] = 'a';
 | 
					 | 
				
			||||||
				files[1].mode[2] = '\0';
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
			case 'e':
 | 
					 | 
				
			||||||
				if(Files_append(&files[1], stderr, stderr_name) != NULL)
 | 
					 | 
				
			||||||
					break;
 | 
					 | 
				
			||||||
				retval = oserr(argv[0], "-e");
 | 
					 | 
				
			||||||
				terminate;
 | 
					 | 
				
			||||||
			case 'i':
 | 
					 | 
				
			||||||
				if((strcmp(optarg, "-") == 0 && Files_append(&files[0],
 | 
					 | 
				
			||||||
							stdin, stdin_name) != NULL)
 | 
					 | 
				
			||||||
						|| Files_open(&files[0], optarg) != NULL)
 | 
					 | 
				
			||||||
					break;
 | 
					 | 
				
			||||||
				retval = oserr(argv[0], optarg);
 | 
					 | 
				
			||||||
				terminate;
 | 
					 | 
				
			||||||
			case 'o':
 | 
					 | 
				
			||||||
				if((strcmp(optarg, "-") == 0 && Files_append(&files[1],
 | 
					 | 
				
			||||||
							stdout, stdout_name) != NULL)
 | 
					 | 
				
			||||||
						|| Files_open(&files[1], optarg) != NULL)
 | 
					 | 
				
			||||||
					break;
 | 
					 | 
				
			||||||
				/* does not exist, so try to create it */
 | 
					 | 
				
			||||||
				if(errno == ENOENT){
 | 
					 | 
				
			||||||
					files[1].mode = wharsh;
 | 
					 | 
				
			||||||
					if(Files_open(&files[1], optarg) != NULL){
 | 
					 | 
				
			||||||
						files[1].mode = fmode[1];
 | 
					 | 
				
			||||||
						break;
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				retval = oserr(argv[0], optarg);
 | 
					 | 
				
			||||||
				terminate;
 | 
					 | 
				
			||||||
			case 'n':
 | 
					 | 
				
			||||||
				if(signal(SIGINT, SIG_IGN) != SIG_ERR)
 | 
					 | 
				
			||||||
					break;
 | 
					 | 
				
			||||||
				retval = oserr(argv[0], "-n");
 | 
					 | 
				
			||||||
				terminate;
 | 
					 | 
				
			||||||
			case 'u':
 | 
					 | 
				
			||||||
				k = 1;
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
			default:
 | 
					 | 
				
			||||||
				retval = usage(argv[0]);
 | 
					 | 
				
			||||||
				terminate;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(optind != argc){
 | 
					 | 
				
			||||||
		retval = usage(argv[0]);
 | 
					 | 
				
			||||||
		terminate;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	files[0].s += files[0].s == 0;
 | 
					 | 
				
			||||||
	files[1].s += files[1].s == 0;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	/* Unbuffer files. */
 | 
					 | 
				
			||||||
	if(k){
 | 
					 | 
				
			||||||
		for(i = 0;
 | 
					 | 
				
			||||||
			i < files[0].s;
 | 
					 | 
				
			||||||
			setvbuf(files[0].files[i++], NULL, _IONBF, 0));
 | 
					 | 
				
			||||||
		for(i = 0;
 | 
					 | 
				
			||||||
			i < files[1].s;
 | 
					 | 
				
			||||||
			setvbuf(files[1].files[i++], NULL, _IONBF, 0));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	retval = EX_OK;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Actual program loop. */
 | 
					 | 
				
			||||||
	for(i = 0; i < files[0].s; ++i) /* iterate ins */
 | 
					 | 
				
			||||||
		while((c = getc(files[0].files[i])) != EOF) /* iterate chars */
 | 
					 | 
				
			||||||
			for(j = 0; j < files[1].s; ++j) /* iterate outs */
 | 
					 | 
				
			||||||
				if(putc(c, files[1].files[j]) == EOF){
 | 
					 | 
				
			||||||
					/* notebook's full */
 | 
					 | 
				
			||||||
					retval = EX_IOERR;
 | 
					 | 
				
			||||||
					fprintf(stderr, "%s: %s: %s\n",
 | 
					 | 
				
			||||||
						program_name, files[1].names[j], strerror(errno));
 | 
					 | 
				
			||||||
					if(fclose(files[1].files[j]) == EOF)
 | 
					 | 
				
			||||||
						fprintf(stderr, "%s: %s: %s\n",
 | 
					 | 
				
			||||||
							program_name, files[1].names[j], strerror(errno));
 | 
					 | 
				
			||||||
					/* massage out the tense muscle */
 | 
					 | 
				
			||||||
					for(k = j--; k < files[1].s - 1; ++k){
 | 
					 | 
				
			||||||
						files[1].files[k] = files[1].files[k+1];
 | 
					 | 
				
			||||||
						files[1].names[k] = files[1].names[k+1];
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					if(--files[1].s == 0)
 | 
					 | 
				
			||||||
						terminate;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	terminate;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										137
									
								
								src/mm.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								src/mm.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,137 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
 | 
				
			||||||
 | 
					 * SPDX-License-Identifier: AGPL-3.0-or-later
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify it under
 | 
				
			||||||
 | 
					 * the terms of the GNU Affero General Public License as published by the Free
 | 
				
			||||||
 | 
					 * Software Foundation, either version 3 of the License, or (at your option) any
 | 
				
			||||||
 | 
					 * later version.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful, but WITHOUT
 | 
				
			||||||
 | 
					 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
				
			||||||
 | 
					 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 | 
				
			||||||
 | 
					 * details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program. If not, see https://www.gnu.org/licenses/.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::{
 | 
				
			||||||
 | 
						env::args,
 | 
				
			||||||
 | 
						fs::File,
 | 
				
			||||||
 | 
						io::{ stdin, stdout, stderr, BufWriter, Read, Write },
 | 
				
			||||||
 | 
						os::fd::{ AsRawFd, FromRawFd },
 | 
				
			||||||
 | 
						process::{ exit, ExitCode },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern crate getopt;
 | 
				
			||||||
 | 
					extern crate strerror;
 | 
				
			||||||
 | 
					extern crate sysexits;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use getopt::GetOpt;
 | 
				
			||||||
 | 
					use strerror::StrError;
 | 
				
			||||||
 | 
					use sysexits::{ EX_IOERR, EX_USAGE };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() -> ExitCode {
 | 
				
			||||||
 | 
						let argv = args().collect::<Vec<_>>();
 | 
				
			||||||
 | 
						let usage = format!("Usage: {} [-aeu] [-i input] [-o output]", argv[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let mut a = false; /* append rather than update */
 | 
				
			||||||
 | 
						let mut e = false; /* use stderr as an output */
 | 
				
			||||||
 | 
						let mut u = false; /* unbuffer i/o */
 | 
				
			||||||
 | 
						let mut ins = Vec::new(); /* initial inputs vector */
 | 
				
			||||||
 | 
						let mut outs = Vec::new(); /* initial outputs vector */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while let Some(opt) = argv.getopt("aei:o:u") {
 | 
				
			||||||
 | 
							match opt.opt() {
 | 
				
			||||||
 | 
								Ok("a") => a = true,
 | 
				
			||||||
 | 
								Ok("e") =>  e = true, 
 | 
				
			||||||
 | 
								Ok("u") => u = true, 
 | 
				
			||||||
 | 
								Ok("i") => { /* add input */
 | 
				
			||||||
 | 
									let input = opt.arg().unwrap();
 | 
				
			||||||
 | 
									ins.push(input);
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								Ok("o") => { /* add output */
 | 
				
			||||||
 | 
									let output = opt.arg().unwrap(); 
 | 
				
			||||||
 | 
									outs.push(output);
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								Err(_) | Ok(_) => {
 | 
				
			||||||
 | 
									eprintln!("{}", usage);
 | 
				
			||||||
 | 
									return ExitCode::from(EX_USAGE as u8);
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* use stdin if no inputs are specified */
 | 
				
			||||||
 | 
						if ins.is_empty() { ins.push("-".to_string()); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* use stdout if no outputs are specified */
 | 
				
			||||||
 | 
						if outs.is_empty() { outs.push("-".to_string()); }
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						/* map all path strings to files */
 | 
				
			||||||
 | 
						let inputs = ins.iter().map(|file| {
 | 
				
			||||||
 | 
							/* if a file is “-”, it is stdin */
 | 
				
			||||||
 | 
							if *file == "-" {
 | 
				
			||||||
 | 
								return unsafe { File::from_raw_fd(stdin().as_raw_fd()) }; /* fd0 = stdin */
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							match File::options().append(a).open(file) {
 | 
				
			||||||
 | 
								Ok(f) => f,
 | 
				
			||||||
 | 
								Err(e) => {
 | 
				
			||||||
 | 
									eprintln!("{}: {}", argv[0], e.strerror());
 | 
				
			||||||
 | 
									exit(EX_IOERR);
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}).collect::<Vec<_>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* map all path strings to files */
 | 
				
			||||||
 | 
						let mut outputs = outs.iter().map(|file| {
 | 
				
			||||||
 | 
							/* of a file is “-”, it is stdout */
 | 
				
			||||||
 | 
							if *file == "-" {
 | 
				
			||||||
 | 
								return unsafe { File::from_raw_fd(stdout().as_raw_fd()) }; /* fd1 = stdout */
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							match File::options().append(a).open(file) {
 | 
				
			||||||
 | 
								Ok(f) => return f,
 | 
				
			||||||
 | 
								Err(e) => {
 | 
				
			||||||
 | 
									eprintln!("{}: {}", argv[0], e.strerror());
 | 
				
			||||||
 | 
									exit(EX_IOERR);
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}).collect::<Vec<_>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* if -e is specified, use stderr */
 | 
				
			||||||
 | 
						if e {
 | 
				
			||||||
 | 
							outputs.push(unsafe { File::from_raw_fd(stderr().as_raw_fd()) }); /* fd2 = stderr */
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let mut outputs = outputs.iter().map(|o| {
 | 
				
			||||||
 | 
							if u {
 | 
				
			||||||
 | 
								BufWriter::with_capacity(0, o)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								BufWriter::new(o)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}).collect::<Vec<_>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for file in inputs {
 | 
				
			||||||
 | 
							for byte in file.bytes().map(|b| {
 | 
				
			||||||
 | 
								b.unwrap_or_else(|e| {
 | 
				
			||||||
 | 
									eprintln!("{}: {}", argv[0], e.strerror());
 | 
				
			||||||
 | 
									exit(EX_IOERR);
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							}) {
 | 
				
			||||||
 | 
								for out in &mut outputs {
 | 
				
			||||||
 | 
									if let Err(e) = out.write(&[byte]) {
 | 
				
			||||||
 | 
										eprintln!("{}: {}", argv[0], e.strerror());
 | 
				
			||||||
 | 
										return ExitCode::from(EX_IOERR as u8);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if let Err(e) = out.flush() {
 | 
				
			||||||
 | 
										eprintln!("{}: {}", argv[0], e.strerror());
 | 
				
			||||||
 | 
										return ExitCode::from(EX_IOERR as u8);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ExitCode::SUCCESS
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user