5 Commits

Author SHA1 Message Date
DTB
0f12dcc552 scrut(1): fix ugly opt parsing 2024-07-15 14:28:00 -06:00
DTB
b9c4b49603 scrut(1): use libfileis 2024-07-15 14:13:53 -06:00
DTB
2c4349872c scrut(1): banish gotos 2024-07-15 13:55:01 -06:00
DTB
59fa3ed3d2 scrut(1): get rid of -h 2024-07-15 13:47:55 -06:00
DTB
71655a8559 libfileis(3): a library for scrutinizing files 2024-07-15 13:47:04 -06:00
7 changed files with 348 additions and 276 deletions

View File

@@ -29,7 +29,7 @@ RUSTC ?= rustc
RUSTLIBS = --extern getopt=build/o/libgetopt.rlib \
--extern sysexits=build/o/libsysexits.rlib \
--extern strerror=build/o/libstrerror.rlib
CFLAGS += -I$(SYSEXITS)
CFLAGS += -I$(SYSEXITS) -Iinclude
.PHONY: all
all: dj false fop hru intcmp mm npc rpn scrut str strcmp swab true
@@ -87,6 +87,11 @@ build/o/libsysexits.rlib: build/include/sysexits.h
build/include/sysexits.h: build $(SYSEXITS)sysexits.h
printf '\043define EXIT_FAILURE 1\n' | cat - $(SYSEXITS)sysexits.h > $@
.PHONY: libfileis
libfileis: build/o/libfileis.o
build/o/libfileis.o: build src/libfileis.c
$(CC) $(CFLAGS) -c -o $@ src/libfileis.c
.PHONY: dj
dj: build/bin/dj
build/bin/dj: src/dj.c build
@@ -114,8 +119,8 @@ build/bin/intcmp: src/intcmp.c build
.PHONY: mm
mm: build/bin/mm
build/bin/mm: src/mm.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/mm.rs
build/bin/mm: src/mm.c build
$(CC) $(CFLAGS) -o $@ src/mm.c
.PHONY: npc
npc: build/bin/npc
@@ -129,8 +134,8 @@ build/bin/rpn: src/rpn.rs build rustlibs
.PHONY: scrut
scrut: build/bin/scrut
build/bin/scrut: src/scrut.c build
$(CC) $(CFLAGS) -o $@ src/scrut.c
build/bin/scrut: src/scrut.c build libfileis
$(CC) $(CFLAGS) -o $@ src/scrut.c build/o/libfileis.o
.PHONY: str
str: build/bin/str

View File

@@ -3,14 +3,14 @@
.\" This work is licensed under CC BY-SA 4.0. To see a copy of this license,
.\" visit <http://creativecommons.org/licenses/by-sa/4.0/>.
.\"
.TH MM 1 2024-07-14 "Harakit X.X.X"
.TH MM 1 2024-06-17 "Harakit X.X.X"
.SH NAME
mm \(en middleman
.\"
.SH SYNOPSIS
mm
.RB [ -aetu ]
.RB [ -aenu ]
.RB [ -i\ input ]
.RB [ -o\ output ]
.\"
@@ -21,25 +21,19 @@ Catenate input files and write them to the start of each output file or stream.
.SH OPTIONS
.IP \fB-a\fP
Opens outputs for appending rather than updating.
Opens subsequent outputs for appending rather than updating.
.IP \fB-e\fP
Use the standard error as an output.
.IP \fB-t\fP
Causes outputs to be overwritten instead of being truncated.
.IP \fB-u\fP
Ensures neither input or output will be buffered.
.IP \fB-i\fP\ \fIinput\fP
Opens a path as an input. If one or more of the input files is \(lq-\(rq or if
no inputs are specified, the standard input shall be used. If specified as the
last option and if there are trailing arguments to the program, they shall be
appended to the list of files to use as inputs.
no inputs are specified, the standard input shall be used.
.IP \fB-o\fP\ \fIoutput\fP
Opens a path as an output. If one or more of the output files is \(lq-\(rq or if
no outputs are specified and the
.B -e
option is not specified, the standard output shall be used. If specified as the
last option and if there are trailing arguments to the program, they shall be
appended to the list of files to use as outputs.
no outputs are specified, the standard output shall be used.
.IP \fB-u\fP
Ensures neither input or output will be buffered.
.IP \fB-n\fP
Causes SIGINT signals to be ignored.
.\"
.SH DIAGNOSTICS
@@ -51,6 +45,10 @@ exits with the appropriate
.BR sysexits.h (3)
status.
.\"
.SH CAVEATS
Existing files are not truncated on ouput and are instead overwritten.
.\"
.SH RATIONALE
The

14
include/libfileis.h Normal file
View File

@@ -0,0 +1,14 @@
int fileis_block (char *fn);
int fileis_char (char *fn);
int fileis_dir (char *fn);
/* fileis_exec (char *fn); */
int fileis_exists (char *fn);
int fileis_fifo (char *fn);
int fileis_setgid (char *fn);
int fileis_link (char *fn);
/* fileis_read (char *fn); */
int fileis_regular(char *fn);
int fileis_socket (char *fn);
int fileis_setuid (char *fn);
int fileis_vtx (char *fn);
/* fileis_write (char *fn); */

27
src/libfileis.c Normal file
View File

@@ -0,0 +1,27 @@
#include <stddef.h> /* NULL */
#include <sys/stat.h> /* lstat(3), stat struct, S_ISBLK, S_ISCHR, S_ISDIR,
* S_ISFIFO, S_ISGID, S_ISREG, S_ISLNK, S_ISSOCK,
* S_ISUID, S_ISVTX */
static char *ofn = NULL;
static struct stat s;
int
fileis_exists(char *fn){
if (fn == NULL || fn == ofn) { return ofn != NULL; }
if (lstat(fn, &s) == -1) { return 0; }
ofn = fn; return 1;
}
int fileis_block(char *fn) { return fileis_exists(fn) && S_ISBLK(s.st_mode); }
int fileis_char(char *fn) { return fileis_exists(fn) && S_ISCHR(s.st_mode); }
int fileis_dir(char *fn) { return fileis_exists(fn) && S_ISDIR(s.st_mode); }
/* fileis_exec(char *fn) { return fileis_exists(fn) && } */
int fileis_fifo(char *fn) { return fileis_exists(fn) && S_ISFIFO(s.st_mode); }
int fileis_setgid(char *fn) { return fileis_exists(fn) && (s.st_mode & S_ISGID); }
int fileis_link(char *fn) { return fileis_exists(fn) && S_ISLNK(s.st_mode); }
/* fileis_read(char *fn) { return fileis_exists(fn) && } */
int fileis_regular(char *fn){ return fileis_exists(fn) && S_ISREG(s.st_mode); }
int fileis_socket(char *fn) { return fileis_exists(fn) && S_ISSOCK(s.st_mode); }
int fileis_setuid(char *fn) { return fileis_exists(fn) && (s.st_mode & S_ISUID); }
int fileis_vtx(char *fn) { return fileis_exists(fn) && (s.st_mode & S_ISVTX); }
/* fileis_write(char *fn) { return fileis_exists(fn) && } */

236
src/mm.c Normal file
View File

@@ -0,0 +1,236 @@
/*
* 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;
}

185
src/mm.rs
View File

@@ -1,185 +0,0 @@
/*
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* 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/.
*/
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 };
use ArgMode::*;
enum ArgMode { In, Out }
fn main() -> ExitCode {
let argv = args().collect::<Vec<_>>();
let usage = format!("Usage: {} [-aetu] [-i input] [-o output]", argv[0]);
let mut a = false; /* append to the file */
let mut e = false; /* use stderr as an output */
let mut t = true; /* do not truncate the file before writing */
let mut u = false; /* unbuffer i/o */
let mut ins = Vec::new(); /* initial input file path vector */
let mut outs = Vec::new(); /* initial output file path vector */
let mut mode: Option<ArgMode> = None; /* mode set by last-used option */
let mut optind = 0;
while let Some(opt) = argv.getopt("aei:o:tu") {
match opt.opt() {
Ok("a") => a = true,
Ok("e") => e = true,
Ok("u") => u = true,
Ok("t") => t = false,
Ok("i") => { /* add inputs */
let input = opt.arg().unwrap();
ins.push(input);
mode = Some(In); /* latest argument == -i */
},
Ok("o") => { /* add output */
let output = opt.arg().unwrap();
outs.push(output);
mode = Some(Out); /* latest argument == -o */
},
Err(_) | Ok(_) => {
eprintln!("{}", usage);
return ExitCode::from(EX_USAGE as u8);
},
};
optind = opt.ind();
}
let remaining = argv.iter().skip(optind);
/* check the last flag specified */
if let Some(m) = mode {
for arg in remaining {
/* move the subsequent arguments to the list of inputs or outputs */
match m {
In => ins.push(arg.to_string()),
Out => outs.push(arg.to_string()),
};
}
} else {
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() && !e { 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 == "-" {
/* portable way to access stdin as a file */
return unsafe { File::from_raw_fd(stdin().as_raw_fd()) };
}
match File::open(file) {
Ok(f) => f,
Err(e) => {
eprintln!("{}: {}: {}", argv[0], file, 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 == "-" {
/* portable way to access stdout as a file */
return unsafe { File::from_raw_fd(stdout().as_raw_fd()) };
}
let options = File::options()
/* dont truncate if -t is specified, append if -a is specified */
.truncate(t)
.append(a)
/* enable the ability to create and write to files */
.create(true)
.write(true)
/* finally, open the file! */
.open(file);
match options {
Ok(f) => return f,
Err(e) => {
eprintln!("{}: {}: {}", argv[0], file, e.strerror());
exit(EX_IOERR);
},
};
}).collect::<Vec<_>>();
/* if -e is specified, use stderr */
if e {
/* portable way to access stderr as a file */
outputs.push(unsafe { File::from_raw_fd(stderr().as_raw_fd()) });
}
let mut outputs = outputs.iter().map(|o| {
if u {
/* unbuffered writing through a buffer of capacity 0 */
BufWriter::with_capacity(0, o)
} else {
/* theoretically buffered writing */
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 u {
/* immediately flush the output for -u */
if let Err(e) = out.flush() {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_IOERR as u8);
}
}
}
}
}
ExitCode::SUCCESS
}

View File

@@ -17,91 +17,68 @@
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
#include <assert.h> /* assert(3) */
#include <stdio.h> /* fprintf(3), stderr, NULL */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */
#include <string.h> /* memset(3), strchr(3) */
#ifndef EX_USAGE
# include <sysexits.h>
#endif
#include <unistd.h> /* access(3), getopt(3), F_OK, R_OK, W_OK, X_OK */
#include <sys/stat.h> /* lstat(3), stat struct, S_ISBLK, S_ISCHR, S_ISDIR,
* S_ISFIFO, S_ISGID, S_ISREG, S_ISLNK, S_ISSOCK,
* S_ISUID, S_ISVTX */
#include <sysexits.h> /* EX_USAGE */
#include <unistd.h> /* access(3), getopt(3), R_OK, W_OK, X_OK */
#include <libfileis.h>
static char args[] = "bcdefghkprsuwxLS";
static char ops[(sizeof args) / (sizeof *args)];
static char *program_name = "scrut";
char *program_name = "scrut";
static char opts[] = "bcdefgkprsuwxLS";
static int usage(char *s){
fprintf(stderr, "Usage: %s [-%s] file...\n", s, opts);
return EX_USAGE;
}
int main(int argc, char *argv[]){
struct stat buf;
int c;
size_t i;
char *p;
char sel[(sizeof opts) / (sizeof *opts)];
if(argc < 2)
goto usage;
if (argc > 0) { program_name = argv[0]; }
if (argc < 2) { return usage(program_name); }
memset(ops, '\0', sizeof ops);
while((c = getopt(argc, argv, args)) != -1)
if((p = strchr(args, c)) == NULL)
goto usage;
else
ops[p - args] = c;
/* straighten out ops */
for(i = 0, p = ops; i < (sizeof ops) / (sizeof *ops); ++i)
if(ops[i] != '\0'){
*p = ops[i];
if(&ops[i] != p++)
ops[i] = '\0';
memset(sel, '\0', sizeof sel);
{
int c;
char *p = sel;
while ((c = getopt(argc, argv, opts)) != -1) {
if ((strchr(opts, c)) == NULL) { return usage(program_name); }
else if ((strchr(sel, c)) == NULL) { *p++ = c; }
assert(p - sel < (sizeof opts) / (sizeof *opts));
}
}
if(optind == argc)
goto usage;
if (optind == argc) { return usage(program_name); }
argv += optind;
do{ if(access(*argv, F_OK) != 0 || lstat(*argv, &buf) == -1)
return EXIT_FAILURE; /* doesn't exist or isn't stattable */
for(i = 0; ops[i] != '\0'; ++i)
if(ops[i] == 'e')
continue;
else if(ops[i] == 'h'){
usage: fprintf(stderr, "Usage: %s [-%s] file...\n",
argv[0] == NULL
? program_name
: argv[0],
args);
do{
if (!fileis_exists(*argv))
return EXIT_FAILURE;
return EX_USAGE;
}else if(
(ops[i] == 'b'
&& !S_ISBLK(buf.st_mode))
|| (ops[i] == 'c'
&& !S_ISCHR(buf.st_mode))
|| (ops[i] == 'd'
&& !S_ISDIR(buf.st_mode))
|| (ops[i] == 'f'
&& !S_ISREG(buf.st_mode))
|| (ops[i] == 'g'
&& !(buf.st_mode & S_ISGID))
|| (ops[i] == 'k'
&& !(buf.st_mode & S_ISVTX))
|| (ops[i] == 'p'
&& !S_ISFIFO(buf.st_mode))
|| (ops[i] == 'r'
&& access(*argv, R_OK) != 0)
|| (ops[i] == 'u'
&& !(buf.st_mode & S_ISUID))
|| (ops[i] == 'w'
&& access(*argv, W_OK) != 0)
|| (ops[i] == 'x'
&& access(*argv, X_OK) != 0)
|| (ops[i] == 'L'
&& !S_ISLNK(buf.st_mode))
|| (ops[i] == 'S'
&& !S_ISSOCK(buf.st_mode)))
return EXIT_FAILURE;
}while(*++argv != NULL);
for (size_t i = 0; sel[i] != '\0'; ++i) {
if ((sel[i] == 'b' && !fileis_block(*argv))
|| (sel[i] == 'c' && !fileis_char(*argv))
|| (sel[i] == 'd' && !fileis_dir(*argv))
|| (sel[i] != 'e')
|| (sel[i] == 'f' && !fileis_regular(*argv))
|| (sel[i] == 'g' && !fileis_setgid(*argv))
|| (sel[i] == 'k' && !fileis_vtx(*argv))
|| (sel[i] == 'p' && !fileis_fifo(*argv))
|| (sel[i] == 'r' && access(*argv, R_OK) != 0)
|| (sel[i] == 'u' && !fileis_setuid(*argv))
|| (sel[i] == 'w' && access(*argv, W_OK) != 0)
|| (sel[i] == 'x' && access(*argv, X_OK) != 0)
|| (sel[i] == 'L' && !fileis_link(*argv))
|| (sel[i] == 'S' && !fileis_socket(*argv))
) { return EXIT_FAILURE; }
}
} while (*++argv != NULL);
return EXIT_SUCCESS;
}