* updated fastfetch (2.11.0 -> 2.18.1)
[t2sde.git] / misc / tools-source / cmd_wrapper.c
blob93739b73f0a8ec904c62e877e71bd9f4c61e4ca3
1 /*
2 * --- T2-COPYRIGHT-NOTE-BEGIN ---
3 * This copyright note is auto-generated by scripts/Create-CopyPatch.
4 *
5 * T2 SDE: misc/tools-source/cmd_wrapper.c
6 * Copyright (C) 2004 - 2022 The T2 SDE Project
7 * Copyright (C) 2006 - 2022 Rene Rebe <rene@exactcode.de>
8 * Copyright (C) 1998 - 2003 ROCK Linux Project
9 *
10 * More information can be found in the files COPYING and README.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; version 2 of the License. A copy of the
15 * GNU General Public License can be found in the file COPYING.
16 * --- T2-COPYRIGHT-NOTE-END ---
18 * Generic command wrapper.
20 * Recognised Variables:
22 * prefix_WRAPPER_MYPATH "/path/to/binary"
23 * prefix_WRAPPER_LOGFILE ".cmd_wrapper_log"
25 * prefix_WRAPPER_DEBUG 0
26 * prefix_WRAPPER_BYPASS 0
28 * prefix_WRAPPER_OTHERS "other_cmd"
30 * prefix_WRAPPER_INSERT "-1st-opt -2nd-opt"
31 * prefix_WRAPPER_REMOVE "-del-this-opt -this-also [!-]*"
32 * prefix_WRAPPER_APPEND "-last-opt"
34 * prefix_WRAPPER_FILTER "sed '...' | awk '...' | foobar"
36 * prefix_WRAPPER_NOLOOP Internal use only.
37 * prefix_WRAPPER_OTHERS_DONE Internal use only.
41 #include <stdlib.h>
42 #include <string.h>
43 #include <stdio.h>
44 #include <unistd.h>
45 #include <ctype.h>
46 #include <sys/wait.h>
47 #include <libgen.h>
48 #include <fnmatch.h>
49 #include <errno.h>
51 #ifndef ENVPREFIX
52 # error You must use -DENVPREFIX=".." when compiling this tool.
53 #endif
55 #ifndef MYNAME
56 # error You must use -DMYNAME=".." when compiling this tool.
57 #endif
59 #define VERBOSE_DEBUG 1
60 int debug=1;
63 * Clean config vars before using them
65 void cleanenv(const char * name, const char ch) {
66 int pos1=0, pos2=0, delim=1;
67 char *tmp1, *tmp2;
69 setenv(name, "", 0); /* no overwrite - make sure it is defined */
70 tmp1 = getenv(name);
71 tmp2 = malloc(strlen(tmp1)+1);
73 while ( tmp1[pos1] ) {
74 if ( tmp1[pos1] == ch ) {
75 if ( ! delim ) {
76 tmp2[pos2++] = ch;
77 delim = 1;
79 } else {
80 tmp2[pos2++] = tmp1[pos1];
81 delim = 0;
83 pos1++;
85 if (pos2 > 0 && tmp2[pos2-1] == ch) pos2--;
86 tmp2[pos2] = 0; setenv(name, tmp2, 1);
90 * Evaluate conditional argument in the form:
91 * "condition?matched-value:unmatched-value"
92 * Nesting is currently only impelemented for rhs matches.
94 char* eval_cond_arg(char * arg, int argc, char ** argv) {
95 char * c = arg;
96 char * lhs = NULL, * rhs;
97 int i;
99 /* scan for '?' */
100 while (*c && *c != '?') c++;
102 if (*c != '?')
103 return arg;
105 #if VERBOSE_DEBUG
106 if (debug) fprintf(stderr, "Conditonal arg: '%s'\n", arg);
107 #endif
109 /* split conditional (arg), left hand and right hand statement */
110 *c++ = 0;
111 lhs = c;
113 /* scan for ':' and 0 terminate */
114 while (*c && *c != ':') c++;
115 if (*c == ':')
116 *c++ = 0;
117 rhs = c;
119 if (debug) fprintf(stderr, "Conditonal: '%s', lhs: '%s', rhs: '%s'\n", arg, lhs, rhs);
121 /* match arguments against conditional */
122 for (i = 0; i < argc; ++i) {
123 if (!fnmatch(arg, argv[i], 0)) {
124 #if VERBOSE_DEBUG
125 if (debug) fprintf(stderr, "Conditonal: '%s', matched: '%s'\n", arg, argv[i]);
126 #endif
127 /* TODO: support nested lhs expressions, too */
128 return lhs;
132 /* support nested rhs, evaluate again */
133 return eval_cond_arg(rhs, argc, argv);
136 /* newargv memory management, realloc if newargv is filled */
137 static inline char** realloc_if_needed(int c1, int* newargc, char** newargv) {
138 if (c1 + 1 >= *newargc) { /* +1 for NULL-sentinel */
139 *newargc *= 2;
140 newargv = realloc (newargv, sizeof(char*) * *newargc);
142 return newargv;
146 * Main function.
148 int main(int argc, char ** argv) {
149 int newargc = 64; /* initial newargv size, anyhting >3 (name, other, NULL) */
150 char **newargv;
151 char *other, *other_done;
152 int c1,c2,c3;
153 char *delim, *optbuf, *wrdir;
154 FILE *logfile = NULL;
156 /* Calling the wrapper with an absolute path results in an
157 endless-loop. Use basename only to force a $PATH lookup. */
158 argv[0] = basename(argv[0]);
159 if ( !strcmp(argv[0], MYNAME) ) {
160 if ( argc > 1 ) {
161 argv++; argc--;
162 argv[0] = basename(argv[0]);
163 } else {
164 exit(250);
168 /* Open logfile (if any) */
169 delim = getenv(ENVPREFIX "_WRAPPER_LOGFILE");
170 if (delim && delim[0]) {
171 logfile = fopen(delim, "a");
173 if (logfile) {
174 delim = malloc(FILENAME_MAX);
175 if ( getcwd(delim, FILENAME_MAX) == NULL ) delim[0]=0;
176 fprintf(logfile, "\n%s:\n-", delim);
177 for (c3=0; c3<argc; c3++) fprintf(logfile, " %s", argv[c3]);
178 fprintf(logfile, "\n");
179 free(delim);
183 * Read prefix_WRAPPER_DEBUG and prefix_WRAPPER_BYPASS
186 if ( (delim=getenv(ENVPREFIX "_WRAPPER_DEBUG")) != NULL &&
187 delim[0] ) debug = atoi(delim);
189 if ( (delim=getenv(ENVPREFIX "_WRAPPER_BYPASS")) != NULL &&
190 delim[0] && atoi(delim)) {
191 #if VERBOSE_DEBUG
192 if (debug) fprintf(stderr, "Bypassing cmd_wrapper by "
193 "clearing all config variables.\n");
194 #endif
195 setenv(ENVPREFIX "_WRAPPER_OTHERS", "", 1);
196 setenv(ENVPREFIX "_WRAPPER_INSERT", "", 1);
197 setenv(ENVPREFIX "_WRAPPER_REMOVE", "", 1);
198 setenv(ENVPREFIX "_WRAPPER_APPEND", "", 1);
199 setenv(ENVPREFIX "_WRAPPER_FILTER", "", 1);
202 cleanenv(ENVPREFIX "_WRAPPER_OTHERS", ':');
203 cleanenv(ENVPREFIX "_WRAPPER_INSERT", ' ');
204 cleanenv(ENVPREFIX "_WRAPPER_REMOVE", ' ');
205 cleanenv(ENVPREFIX "_WRAPPER_APPEND", ' ');
206 cleanenv(ENVPREFIX "_WRAPPER_FILTER", '|');
208 #if VERBOSE_DEBUG
209 if (debug) {
210 fprintf(stderr, "Old Command:");
211 for (c3=0; c3<argc; c3++) fprintf(stderr, " %s", argv[c3]);
212 fprintf(stderr, "\n");
213 fprintf(stderr, "ENVPREFIX = '%s'\n", ENVPREFIX);
214 fprintf(stderr, "OTHERS = '%s'\n",
215 getenv(ENVPREFIX "_WRAPPER_OTHERS"));
216 if (getenv(ENVPREFIX "_WRAPPER_OTHERS_DONE"))
217 fprintf(stderr, "OTHERS DONE = '%s'\n",
218 getenv(ENVPREFIX "_WRAPPER_OTHERS_DONE"));
219 fprintf(stderr, "INSERT = '%s'\n",
220 getenv(ENVPREFIX "_WRAPPER_INSERT"));
221 fprintf(stderr, "REMOVE = '%s'\n",
222 getenv(ENVPREFIX "_WRAPPER_REMOVE"));
223 fprintf(stderr, "APPEND = '%s'\n",
224 getenv(ENVPREFIX "_WRAPPER_APPEND"));
225 fprintf(stderr, "FILTER = '%s'\n",
226 getenv(ENVPREFIX "_WRAPPER_FILTER"));
228 #endif
230 /* extract the next other wrapper */
231 other = getenv(ENVPREFIX "_WRAPPER_OTHERS");
232 other_done = getenv(ENVPREFIX "_WRAPPER_OTHERS_DONE");
234 if (other && strlen(other) > 0) {
235 other = strdup(other);
236 char *newother_done;
238 if (other_done) {
239 char *str = strstr(other, other_done);
240 if (str != other) {
241 fprintf(stderr, "OTHERS_DONE set but does not match.\n");
242 return 249;
245 other += strlen(other_done);
246 if (*other == 0) {
247 other = 0;
249 else {
250 if (*other == ':')
251 other++;
253 newother_done = (char *) malloc(strlen(getenv(ENVPREFIX "_WRAPPER_OTHERS")));
254 strcpy(newother_done, other_done);
255 strcat(newother_done, ":");
256 strcat(newother_done, other);
258 setenv(ENVPREFIX "_WRAPPER_OTHERS_DONE", newother_done, 1);
261 else {
262 other = strtok(other, ":");
263 setenv(ENVPREFIX "_WRAPPER_OTHERS_DONE", other, 1);
268 * Main work.
271 newargv = malloc( sizeof(char*) * newargc );
273 /* init newargv[], c1 and c2 */
274 c1 = c2 = 0;
275 if (other && strlen(other) > 0)
276 newargv[c1++] = other;
277 newargv[c1++] = argv[c2++];
280 * Copy options from prefix_WRAPPER_INSERT to newargv[]
282 if ( (delim=getenv(ENVPREFIX "_WRAPPER_INSERT")) != NULL ) {
283 optbuf = malloc( strlen(delim) + 1 );
284 strcpy(optbuf, delim);
286 /* TODO: handle escapes to support cond. lhs nesting */
287 delim = strtok(optbuf, " ");
288 while (delim != NULL) {
289 delim = eval_cond_arg(delim, argc, argv);
291 if (delim[0]) {
292 #if VERBOSE_DEBUG
293 if (debug) fprintf(stderr, "Insert: %s\n", delim);
294 #endif
295 newargv = realloc_if_needed(c1, &newargc, newargv);
296 newargv[c1++] = delim;
298 delim = strtok(NULL, " ");
304 * Copy options from argv[] to newargv[] if they are not listed
305 * in prefix_WRAPPER_REMOVE
307 for (; c2<argc; c2++) {
308 if ( (delim=getenv(ENVPREFIX "_WRAPPER_REMOVE")) != NULL ) {
309 optbuf = malloc( strlen(delim) + 1 );
310 strcpy(optbuf, delim);
312 /* TODO: handle escapes to support cond. lhs nesting */
313 delim = strtok(optbuf, " ");
314 while (delim != NULL) {
315 delim = eval_cond_arg(delim, argc, argv);
316 if (delim[0] &&
317 !fnmatch(delim, argv[c2], 0)) break;
318 delim = strtok(NULL, " ");
320 free(optbuf);
322 if (delim == NULL) {
323 #if VERBOSE_DEBUG
324 if (debug) fprintf(stderr, "Copy: %s\n", argv[c2]);
325 #endif
327 newargv = realloc_if_needed(c1, &newargc, newargv);
328 newargv[c1++] = argv[c2];
329 #if VERBOSE_DEBUG
330 } else {
331 if (debug) fprintf(stderr, "Remove: %s\n", argv[c2]);
332 #endif
338 * Copy options from prefix_WRAPPER_APPEND to newargv[]
340 if ( (delim=getenv(ENVPREFIX "_WRAPPER_APPEND")) != NULL ) {
341 optbuf = malloc( strlen(delim) + 1 );
342 strcpy(optbuf, delim);
344 /* TODO: handle escapes to support cond. lhs nesting */
345 delim = strtok(optbuf, " ");
346 while (delim != NULL) {
347 delim = eval_cond_arg(delim, argc, argv);
348 if (delim[0]) {
349 #if VERBOSE_DEBUG
350 if (debug) fprintf(stderr, "Append: %s\n", delim);
351 #endif
353 newargv = realloc_if_needed(c1, &newargc, newargv);
354 newargv[c1++] = delim;
356 delim = strtok(NULL, " ");
362 * Use prefix_WRAPPER_FILTER if set and not ""
364 * (Maybe we make a nice re-write of this code-block later.)
366 if ( (delim=getenv(ENVPREFIX "_WRAPPER_FILTER")) != NULL && delim[0] ) {
368 /* Open temp files. */
369 char outfn[] = "/tmp/gccfilter_out.XXXXXX";
370 char infn[] = "/tmp/gccfilter_in.XXXXXX";
371 int outfd = mkstemp(outfn);
372 int infd = mkstemp(infn);
373 int pid, status;
375 /* Create content of input file */
376 for (c3=0; c3<c1; c3++) {
377 write(infd, newargv[c3], strlen(newargv[c3]));
378 write(infd, "\n", 1);
380 lseek(infd, 0, SEEK_SET);
382 /* Run filter command with shell (sh -c xxx) */
383 #if VERBOSE_DEBUG
384 if (debug) fprintf(stderr, "Run Filter: %s\n", delim);
385 #endif
386 pid = fork();
387 if (!pid) {
388 dup2(infd, 0); close(infd);
389 dup2(outfd, 1); close(outfd);
390 execlp("sh", "sh", "-c", delim, NULL);
391 return 1;
392 } else if (pid == -1) {
393 fprintf(stderr, "Fork failed: %d: %s\n", errno, strerror(errno));
394 } else {
395 /* We don't expect any signals and have no other child processes. */
396 wait(&status);
397 if (WEXITSTATUS(status) != 0) {
398 fprintf(stderr, "Filter failed: %d\n", WEXITSTATUS(status));
402 /* Re-read parameter list. */
404 size_t argvsize = lseek(outfd, 0, SEEK_END);
405 char* argvmem = malloc (argvsize + 1); /* might not have trailing \n */
406 lseek(outfd, 0, SEEK_SET);
407 read(outfd, argvmem, argvsize);
409 if (argvsize == 0) {
410 fprintf(stderr, "Filter produced no output!\n");
411 return 248;
414 for (c1 = c2 = 0; c2 < argvsize; ++c2) {
415 newargv = realloc_if_needed(c1, &newargc, newargv);
416 newargv[c1++] = argvmem + c2;
418 /* scan for newlines, terminate, next */
419 while (c2 < argvsize && argvmem[c2] != '\n')
420 ++c2;
421 argvmem[c2] = 0;
425 /* Close and remove temp files */
426 close(outfd); unlink(outfn);
427 close(infd); unlink(infn);
431 * Run other wrappers first. They will re-start us.
433 if (other && strlen(other) > 0) {
435 #if VERBOSE_DEBUG
436 if (debug) {
437 fprintf(stderr, "Running external wrapper: %s\n", newargv[0]);
438 for (c3=0; c3<c1; c3++)
439 fprintf(stderr, " %s", newargv[c3]);
440 fprintf(stderr, "\n");
442 #endif
444 if (logfile) {
445 fprintf(logfile, "+");
446 for (c3=0; c3<c1; c3++)
447 fprintf(logfile, " %s", newargv[c3]);
448 fprintf(logfile, "\n");
449 fclose(logfile);
452 newargv[c1] = NULL;
453 execvp(newargv[0], newargv);
454 fprintf(stderr, "cmd_wrapper: execvp(%s,...) - %s\n",
455 newargv[0], strerror(errno));
457 return 253;
461 * Remove the wrapper dir from PATH
463 if ( (delim=getenv("PATH")) != NULL && delim[0] &&
464 (wrdir=getenv(ENVPREFIX "_WRAPPER_MYPATH")) != NULL &&
465 wrdir[0] ) {
466 optbuf = malloc( strlen(delim) + 1 );
467 optbuf[0] = 0;
469 #if VERBOSE_DEBUG
470 if (debug)
471 fprintf(stderr, "Old PATH: %s\n", delim);
472 #endif
474 delim = strtok(delim, ":");
475 while ( delim != NULL ) {
476 if (strcmp(delim, wrdir)) {
477 if (optbuf[0]) strcat(optbuf, ":");
478 strcat(optbuf, delim);
480 delim = strtok(NULL, ":");
482 setenv("PATH", optbuf, 1);
484 #if VERBOSE_DEBUG
485 if (debug) fprintf(stderr, "New PATH: %s\n", optbuf);
486 #endif
487 } else {
488 return 252;
492 * Detect loops
495 if ( (delim=getenv(ENVPREFIX "_WRAPPER_NOLOOP")) != NULL &&
496 delim[0] && delim[0] != '0') {
497 return 251;
499 setenv(ENVPREFIX "_WRAPPER_NOLOOP", "1", 1);
502 * Run the new command
505 #if VERBOSE_DEBUG
506 if (debug) {
507 fprintf(stderr, "New Command:");
508 for (c3=0; c3<c1; c3++) fprintf(stderr, " %s", newargv[c3]);
509 fprintf(stderr, "\n");
511 #endif
513 if (logfile) {
514 fprintf(logfile, "+");
515 for (c3=0; c3<c1; c3++) fprintf(logfile, " %s", newargv[c3]);
516 fprintf(logfile, "\n");
517 fclose(logfile);
520 newargv[c1]=NULL;
521 execvp(newargv[0], newargv);
522 fprintf(stderr, "cmd_wrapper: execvp(%s,...) - %s\n",
523 newargv[0], strerror(errno));
525 return 254;