2 * Runs a command for all specified arguments.
3 * Copyright (c) 2013 by Michal Nazareicz (mina86@mina86.com)
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 * This is part of Tiny Applications Collection
19 * -> http://tinyapps.sourceforge.net/
22 #define _POSIX_C_SOURCE 2
32 #include <sys/select.h>
34 #include <sys/types.h>
39 static const char *const MARKER
= "{+}";
42 static const char *ARGV0
;
44 static void error(const char *fmt
, ...) {
47 fprintf(stderr
, "%s: ", ARGV0
);
48 vfprintf(stderr
, fmt
, ap
);
53 static void usage(bool full
) {
54 FILE *out
= full
? stdout
: stderr
;
55 fprintf(out
, "usage: %s [<options>] [--] <command> <arg>... -- <value> ...\n",
58 fputs("Possible <options>:\n"
59 " -j<jobs> run <jobs> jobs at the same time\n"
60 " -J run one job per processor\n"
61 " -K stop once a job returns non-zero\n"
62 "<value> values to pass to the <command>\n"
63 "<command> command to run for each <value>\n"
64 "<arg> arguments to pass to the <command>,\n"
65 " {+} will be substituted by <value>,\n"
66 " if no {+} given, <value> will be passed at the end\n",
69 fprintf(out
, " %s --help\n", ARGV0
);
77 const char **values
, **command
;
81 static unsigned parseJobs(char *arg
) {
86 val
= strtoul(arg
, &end
, 10);
87 if (!val
|| errno
|| val
> UINT_MAX
|| *end
) {
88 error("-j: %s: invalid argument\n", arg
);
95 static unsigned countProcessors(void) {
101 fd
= fopen("/proc/cpuinfo", "r");
103 error("/cpu/info: %s\n", strerror(errno
));
104 error("assuming -j1\n");
108 while (fgets(buffer
, sizeof buffer
, fd
)) {
109 count
+= !ignore
&& strncmp(buffer
, "processor\t", 10);
110 ignore
= !strchr(buffer
, '\n');
114 error("no processors detected, assuming -j1\n");
121 static void takeArguments(const char ***argvp
, const char ***outp
,
122 const char *marker
, bool wait_for_dd
) {
123 const char **argv
= *argvp
, **out
= *outp
, *arg
;
126 if (*argv
&& !strcmp(**argvp
, "--")) {
130 arg
= "--"; /* Whatever non-NULL will suffice */
131 for (; arg
; ++argv
) {
134 if (wait_for_dd
&& !strcmp(*argv
, "--")) {
136 } else if (marker
&& !strcmp(arg
, marker
)) {
144 if (marker
&& !found
) {
154 static int parseArgs(int argc
, char **_argv
, struct options
*opts
) {
155 const char **argv
, **out
= (const char **)_argv
;
160 const char *arg
= strrchr(ARGV0
, '/');
166 if (argc
> 1 && !strcmp(_argv
[1], "--help")) {
172 opts
->keep_going
= true;
174 while ((opt
= getopt(argc
, _argv
, "+:j:JKh")) != -1) {
180 opts
->keep_going
= false;
183 opts
->jobs
= parseJobs(optarg
);
189 opts
->jobs
= countProcessors();
192 error("-%c: requires an argument\n", optopt
);
195 error("-%c: unknown option\n", optopt
);
200 argv
= (const char **)(_argv
+ optind
);
203 takeArguments(&argv
, &out
, MARKER
, true);
204 if (!*opts
->command
) {
205 error("no command given\n");
210 takeArguments(&argv
, &out
, NULL
, false);
211 if (!*opts
->values
) {
212 error("no values given\n");
224 static bool run(struct options
*opts
, const char *value
) {
231 error("fork: %s\n", strerror(errno
));
237 if (*cmd
== MARKER
) {
243 execvp(opts
->command
[0], (char **)opts
->command
);
244 error("%s: %s\n", opts
->command
[0], strerror(errno
));
252 static bool reap(int *rc
, bool block
) {
254 pid_t pid
= waitpid(-1, &status
, block
? 0 : WNOHANG
);
257 } else if (!status
) {
259 } else if (WIFSIGNALED(status
)) {
260 error("[%d]: killed by a signal: %d\n",
261 pid
, WTERMSIG(status
));
264 error("[%d]: exited with exit code: %d\n",
265 pid
, WEXITSTATUS(status
));
272 static int runCommands(struct options
*opts
) {
277 for (value
= opts
->values
;
278 *value
&& (opts
->keep_going
|| !rc
);
280 if (!run(opts
, *value
)) {
285 while (reap(&rc
, jobs
>= opts
->jobs
)) {
291 error("terminating before all jobs were started\n");
303 int main(int argc
, char **argv
) {
307 rc
= parseArgs(argc
, argv
, &opts
);
309 return rc
< 0 ? 0 : rc
;
312 return runCommands(&opts
);