make-config.in: complete path (leftover of [807f64e2], 2015-12-26!)
[s-mailx.git] / cmd-misc.c
blobecd848ba5f2eee9b24ecc6b24afc19b9dcaeebb0
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Miscellaneous user commands, like `echo', `pwd', etc.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8 /*
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
36 #undef n_FILE
37 #define n_FILE cmd_misc
39 #ifndef HAVE_AMALGAMATION
40 # include "nail.h"
41 #endif
43 #include <sys/utsname.h>
45 /* Expand the shell escape by expanding unescaped !'s into the last issued
46 * command where possible */
47 static char const *a_cmisc_bangexp(char const *cp);
49 /* c_n?echo(), c_n?echoerr() */
50 static int a_cmisc_echo(void *vp, FILE *fp, bool_t donl);
52 /* c_read() */
53 static bool_t a_cmisc_read_set(char const *cp, char const *value);
55 /* c_version() */
56 static int a_cmisc_version_cmp(void const *s1, void const *s2);
58 static char const *
59 a_cmisc_bangexp(char const *cp){
60 static struct str last_bang;
62 struct n_string xbang, *bang;
63 char c;
64 bool_t changed;
65 NYD_ENTER;
67 if(!ok_blook(bang))
68 goto jleave;
70 changed = FAL0;
72 for(bang = n_string_creat(&xbang); (c = *cp++) != '\0';){
73 if(c == '!'){
74 if(last_bang.l > 0)
75 bang = n_string_push_buf(bang, last_bang.s, last_bang.l);
76 changed = TRU1;
77 }else{
78 if(c == '\\' && *cp == '!'){
79 ++cp;
80 c = '!';
81 changed = TRU1;
83 bang = n_string_push_c(bang, c);
87 if(last_bang.s != NULL)
88 n_free(last_bang.s);
89 last_bang.s = n_string_cp(bang);
90 last_bang.l = bang->s_len;
91 bang = n_string_drop_ownership(bang);
92 n_string_gut(bang);
94 cp = last_bang.s;
95 if(changed)
96 fprintf(n_stdout, "!%s\n", cp);
97 jleave:
98 NYD_LEAVE;
99 return cp;
102 static int
103 a_cmisc_echo(void *vp, FILE *fp, bool_t donl){
104 struct n_string s, *sp;
105 int rv;
106 bool_t doerr;
107 char const **argv, *varname, **ap, *cp;
108 NYD2_ENTER;
110 argv = vp;
111 varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *argv++ : NULL;
112 sp = n_string_reserve(n_string_creat_auto(&s), 121/* XXX */);
113 #ifdef HAVE_ERRORS
114 doerr = (fp == n_stderr && (n_psonce & n_PSO_INTERACTIVE));
115 #else
116 doerr = FAL0;
117 #endif
119 for(ap = argv; *ap != NULL; ++ap){
120 if(ap != argv)
121 sp = n_string_push_c(sp, ' ');
122 if((cp = fexpand(*ap, FEXP_NSHORTCUT | FEXP_NVAR)) == NULL)
123 cp = *ap;
124 sp = n_string_push_cp(sp, cp);
126 if(donl)
127 sp = n_string_push_c(sp, '\n');
128 cp = n_string_cp(sp);
130 if(varname == NULL){
131 si32_t e;
133 e = n_ERR_NONE;
134 if(doerr){
135 /* xxx Ensure *log-prefix* will be placed by n_err() for next msg */
136 if(donl)
137 cp = n_string_cp(n_string_trunc(sp, sp->s_len - 1));
138 n_err((donl ? "%s\n" : "%s"), cp);
139 }else if(fputs(cp, fp) == EOF)
140 e = n_err_no;
141 if((rv = (fflush(fp) == EOF)))
142 e = n_err_no;
143 rv |= ferror(fp) ? 1 : 0;
144 n_pstate_err_no = e;
145 }else if(!n_var_vset(varname, (uintptr_t)cp)){
146 n_pstate_err_no = n_ERR_NOTSUP;
147 rv = -1;
148 }else{
149 n_pstate_err_no = n_ERR_NONE;
150 rv = (int)sp->s_len;
152 NYD2_LEAVE;
153 return rv;
156 static bool_t
157 a_cmisc_read_set(char const *cp, char const *value){
158 bool_t rv;
159 NYD2_ENTER;
161 if(!n_shexp_is_valid_varname(cp))
162 value = N_("not a valid variable name");
163 else if(!n_var_is_user_writable(cp))
164 value = N_("variable is read-only");
165 else if(!n_var_vset(cp, (uintptr_t)value))
166 value = N_("failed to update variable value");
167 else{
168 rv = TRU1;
169 goto jleave;
171 n_err("`read': %s: %s\n", V_(value), n_shexp_quote_cp(cp, FAL0));
172 rv = FAL0;
173 jleave:
174 NYD2_LEAVE;
175 return rv;
178 static int
179 a_cmisc_version_cmp(void const *s1, void const *s2){
180 char const * const *cp1, * const *cp2;
181 int rv;
182 NYD2_ENTER;
184 cp1 = s1;
185 cp2 = s2;
186 rv = strcmp(&(*cp1)[1], &(*cp2)[1]);
187 NYD2_LEAVE;
188 return rv;
191 FL int
192 c_shell(void *v)
194 sigset_t mask;
195 int rv;
196 FILE *fp;
197 char const **argv, *varname, *varres, *cp;
198 NYD_ENTER;
200 n_pstate_err_no = n_ERR_NONE;
201 argv = v;
202 varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *argv++ : NULL;
203 varres = n_empty;
204 fp = NULL;
206 if(varname != NULL &&
207 (fp = Ftmp(NULL, "shell", OF_RDWR | OF_UNLINK | OF_REGISTER)
208 ) == NULL){
209 n_pstate_err_no = n_ERR_CANCELED;
210 rv = -1;
211 }else{
212 cp = a_cmisc_bangexp(*argv);
214 sigemptyset(&mask);
215 if(n_child_run(ok_vlook(SHELL), &mask,
216 n_CHILD_FD_PASS, (fp != NULL ? fileno(fp) : n_CHILD_FD_PASS),
217 "-c", cp, NULL, NULL, &rv) < 0){
218 n_pstate_err_no = n_err_no;
219 rv = -1;
220 }else{
221 rv = WEXITSTATUS(rv);
223 if(fp != NULL){
224 int c;
225 char *x;
226 off_t l;
228 fflush_rewind(fp);
229 l = fsize(fp);
230 if(UICMP(64, l, >=, UIZ_MAX -42)){
231 n_pstate_err_no = n_ERR_NOMEM;
232 varres = n_empty;
233 }else{
234 varres = x = n_autorec_alloc(l +1);
236 for(; l > 0 && (c = getc(fp)) != EOF; --l)
237 *x++ = c;
238 *x++ = '\0';
239 if(l != 0){
240 n_pstate_err_no = n_err_no;
241 varres = n_empty; /* xxx hmmm */
248 if(fp != NULL)
249 Fclose(fp);
251 if(varname != NULL){
252 if(!n_var_vset(varname, (uintptr_t)varres)){
253 n_pstate_err_no = n_ERR_NOTSUP;
254 rv = -1;
256 }else if(rv >= 0 && (n_psonce & n_PSO_INTERACTIVE)){
257 fprintf(n_stdout, "!\n");
258 /* Line buffered fflush(n_stdout); */
260 NYD_LEAVE;
261 return rv;
264 FL int
265 c_dosh(void *v)
267 int rv;
268 NYD_ENTER;
269 n_UNUSED(v);
271 if(n_child_run(ok_vlook(SHELL), 0, n_CHILD_FD_PASS, n_CHILD_FD_PASS, NULL,
272 NULL, NULL, NULL, &rv) < 0){
273 n_pstate_err_no = n_err_no;
274 rv = -1;
275 }else{
276 putc('\n', n_stdout);
277 /* Line buffered fflush(n_stdout); */
278 n_pstate_err_no = n_ERR_NONE;
279 rv = WEXITSTATUS(rv);
281 NYD_LEAVE;
282 return rv;
285 FL int
286 c_cwd(void *v){
287 struct n_string s_b, *sp;
288 size_t l;
289 char const *varname;
290 NYD_ENTER;
292 sp = n_string_creat_auto(&s_b);
293 varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *(char const**)v : NULL;
294 l = PATH_MAX;
296 for(;; l += PATH_MAX){
297 sp = n_string_resize(n_string_trunc(sp, 0), l);
299 if(getcwd(sp->s_dat, sp->s_len) == NULL){
300 int e;
302 e = n_err_no;
303 if(e == n_ERR_RANGE)
304 continue;
305 n_perr(_("Failed to getcwd(3)"), e);
306 v = NULL;
307 break;
310 if(varname != NULL){
311 if(!n_var_vset(varname, (uintptr_t)sp->s_dat))
312 v = NULL;
313 }else{
314 l = strlen(sp->s_dat);
315 sp = n_string_trunc(sp, l);
316 if(fwrite(sp->s_dat, 1, sp->s_len, n_stdout) == sp->s_len &&
317 putc('\n', n_stdout) == EOF)
318 v = NULL;
320 break;
322 NYD_LEAVE;
323 return (v == NULL);
326 FL int
327 c_chdir(void *v)
329 char **arglist = v;
330 char const *cp;
331 NYD_ENTER;
333 if (*arglist == NULL)
334 cp = ok_vlook(HOME);
335 else if ((cp = fexpand(*arglist, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
336 goto jleave;
337 if (chdir(cp) == -1) {
338 n_perr(cp, 0);
339 cp = NULL;
341 jleave:
342 NYD_LEAVE;
343 return (cp == NULL);
346 FL int
347 c_echo(void *v){
348 int rv;
349 NYD_ENTER;
351 rv = a_cmisc_echo(v, n_stdout, TRU1);
352 NYD_LEAVE;
353 return rv;
356 FL int
357 c_echoerr(void *v){
358 int rv;
359 NYD_ENTER;
361 rv = a_cmisc_echo(v, n_stderr, TRU1);
362 NYD_LEAVE;
363 return rv;
366 FL int
367 c_echon(void *v){
368 int rv;
369 NYD_ENTER;
371 rv = a_cmisc_echo(v, n_stdout, FAL0);
372 NYD_LEAVE;
373 return rv;
376 FL int
377 c_echoerrn(void *v){
378 int rv;
379 NYD_ENTER;
381 rv = a_cmisc_echo(v, n_stderr, FAL0);
382 NYD_LEAVE;
383 return rv;
386 FL int
387 c_read(void * volatile vp){
388 struct n_sigman sm;
389 struct str trim;
390 struct n_string s, *sp;
391 char *linebuf;
392 size_t linesize, i;
393 int rv;
394 char const *ifs, **argv, *cp;
395 NYD2_ENTER;
397 sp = n_string_creat_auto(&s);
398 sp = n_string_reserve(sp, 64 -1);
400 ifs = ok_vlook(ifs);
401 linesize = 0;
402 linebuf = NULL;
403 argv = vp;
405 n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL){
406 case 0:
407 break;
408 default:
409 n_pstate_err_no = n_ERR_INTR;
410 rv = -1;
411 goto jleave;
414 n_pstate_err_no = n_ERR_NONE;
415 rv = n_go_input(((n_pstate & n_PS_COMPOSE_MODE
416 ? n_GO_INPUT_CTX_COMPOSE : n_GO_INPUT_CTX_DEFAULT) |
417 n_GO_INPUT_FORCE_STDIN | n_GO_INPUT_NL_ESC |
418 n_GO_INPUT_PROMPT_NONE /* XXX POSIX: PS2: yes! */),
419 NULL, &linebuf, &linesize, NULL, NULL);
420 if(rv < 0){
421 if(!n_go_input_is_eof())
422 n_pstate_err_no = n_ERR_BADF;
423 goto jleave;
424 }else if(rv == 0){
425 if(n_go_input_is_eof()){
426 rv = -1;
427 goto jleave;
429 }else{
430 trim.s = linebuf;
431 trim.l = rv;
433 for(; *argv != NULL; ++argv){
434 if(trim.l == 0 || n_str_trim_ifs(&trim, FAL0)->l == 0)
435 break;
437 /* The last variable gets the remaining line less trailing IFS-WS */
438 if(argv[1] == NULL){
439 jitall:
440 sp = n_string_assign_buf(sp, trim.s, trim.l);
441 trim.l = 0;
442 }else for(cp = trim.s, i = 1;; ++cp, ++i){
443 if(strchr(ifs, *cp) != NULL){
444 sp = n_string_assign_buf(sp, trim.s, i - 1);
445 trim.s += i;
446 trim.l -= i;
447 break;
449 if(i == trim.l)
450 goto jitall;
453 if(!a_cmisc_read_set(*argv, n_string_cp(sp))){
454 n_pstate_err_no = n_ERR_NOTSUP;
455 rv = -1;
456 break;
461 /* Set the remains to the empty string */
462 for(; *argv != NULL; ++argv)
463 if(!a_cmisc_read_set(*argv, n_empty)){
464 n_pstate_err_no = n_ERR_NOTSUP;
465 rv = -1;
466 break;
469 n_sigman_cleanup_ping(&sm);
470 jleave:
471 if(linebuf != NULL)
472 n_free(linebuf);
473 NYD2_LEAVE;
474 n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
475 return rv;
478 FL int
479 c_readall(void * vp){ /* TODO 64-bit retval */
480 struct n_sigman sm;
481 struct n_string s, *sp;
482 char *linebuf;
483 size_t linesize;
484 int rv;
485 char const **argv;
486 NYD2_ENTER;
488 sp = n_string_creat_auto(&s);
489 sp = n_string_reserve(sp, 64 -1);
491 linesize = 0;
492 linebuf = NULL;
493 argv = vp;
495 n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL){
496 case 0:
497 break;
498 default:
499 n_pstate_err_no = n_ERR_INTR;
500 rv = -1;
501 goto jleave;
504 n_pstate_err_no = n_ERR_NONE;
506 for(;;){
507 rv = n_go_input(((n_pstate & n_PS_COMPOSE_MODE
508 ? n_GO_INPUT_CTX_COMPOSE : n_GO_INPUT_CTX_DEFAULT) |
509 n_GO_INPUT_FORCE_STDIN | /*n_GO_INPUT_NL_ESC |*/
510 n_GO_INPUT_PROMPT_NONE),
511 NULL, &linebuf, &linesize, NULL, NULL);
512 if(rv < 0){
513 if(!n_go_input_is_eof()){
514 n_pstate_err_no = n_ERR_BADF;
515 goto jleave;
517 if(sp->s_len == 0)
518 goto jleave;
519 break;
522 if(n_pstate & n_PS_READLINE_NL)
523 linebuf[rv++] = '\n'; /* Replace NUL with it */
525 if(n_UNLIKELY(rv == 0)){ /* xxx will not get*/
526 if(n_go_input_is_eof()){
527 if(sp->s_len == 0){
528 rv = -1;
529 goto jleave;
531 break;
533 }else if(n_LIKELY(UICMP(32, SI32_MAX - sp->s_len, >, rv)))
534 sp = n_string_push_buf(sp, linebuf, rv);
535 else{
536 n_pstate_err_no = n_ERR_OVERFLOW;
537 rv = -1;
538 goto jleave;
542 if(!a_cmisc_read_set(argv[0], n_string_cp(sp))){
543 n_pstate_err_no = n_ERR_NOTSUP;
544 rv = -1;
545 goto jleave;
547 rv = sp->s_len;
549 n_sigman_cleanup_ping(&sm);
550 jleave:
551 if(linebuf != NULL)
552 n_free(linebuf);
553 NYD2_LEAVE;
554 n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
555 return rv;
558 FL int
559 c_version(void *vp){
560 struct utsname ut;
561 struct n_string s, *sp = &s;
562 int rv;
563 char *iop;
564 char const *cp, **arr;
565 size_t i, lnlen, j;
566 NYD_ENTER;
568 sp = n_string_creat_auto(sp);
569 sp = n_string_book(sp, 1024);
571 /* First two lines */
572 sp = n_string_push_cp(sp, n_uagent);
573 sp = n_string_push_c(sp, ' ');
574 sp = n_string_push_cp(sp, ok_vlook(version));
575 sp = n_string_push_c(sp, ',');
576 sp = n_string_push_c(sp, ' ');
577 sp = n_string_push_cp(sp, ok_vlook(version_date));
578 sp = n_string_push_c(sp, ' ');
579 sp = n_string_push_c(sp, '(');
580 sp = n_string_push_cp(sp, _("build for "));
581 sp = n_string_push_cp(sp, ok_vlook(build_os));
582 sp = n_string_push_c(sp, ')');
583 sp = n_string_push_cp(sp, _("\nFeatures included (+) or not (-):\n"));
585 /* Some lines with the features.
586 * *features* starts with dummy byte to avoid + -> *folder* expansions */
587 i = strlen(cp = &ok_vlook(features)[1]) +1;
588 iop = n_autorec_alloc(i);
589 memcpy(iop, cp, i);
591 arr = n_autorec_alloc(sizeof(cp) * VAL_FEATURES_CNT);
592 for(i = 0; (cp = n_strsep(&iop, ',', TRU1)) != NULL; ++i)
593 arr[i] = cp;
594 qsort(arr, i, sizeof(cp), &a_cmisc_version_cmp);
596 for(lnlen = 0; i-- > 0;){
597 cp = *(arr++);
598 j = strlen(cp);
600 if((lnlen += j + 1) > 72){
601 sp = n_string_push_c(sp, '\n');
602 lnlen = j + 1;
604 sp = n_string_push_c(sp, ' ');
605 sp = n_string_push_buf(sp, cp, j);
607 sp = n_string_push_c(sp, '\n');
609 /* */
610 if(n_poption & n_PO_VERB){
611 sp = n_string_push_cp(sp, "Compile: ");
612 sp = n_string_push_cp(sp, ok_vlook(build_cc));
613 sp = n_string_push_cp(sp, "\nLink: ");
614 sp = n_string_push_cp(sp, ok_vlook(build_ld));
615 if(*(cp = ok_vlook(build_rest)) != '\0'){
616 sp = n_string_push_cp(sp, "\nRest: ");
617 sp = n_string_push_cp(sp, cp);
619 sp = n_string_push_c(sp, '\n');
621 /* A trailing line with info of the running machine */
622 uname(&ut);
623 sp = n_string_push_c(sp, '@');
624 sp = n_string_push_cp(sp, ut.sysname);
625 sp = n_string_push_c(sp, ' ');
626 sp = n_string_push_cp(sp, ut.release);
627 sp = n_string_push_c(sp, ' ');
628 sp = n_string_push_cp(sp, ut.version);
629 sp = n_string_push_c(sp, ' ');
630 sp = n_string_push_cp(sp, ut.machine);
631 sp = n_string_push_c(sp, '\n');
634 /* Done */
635 cp = n_string_cp(sp);
637 if(n_pstate & n_PS_ARGMOD_VPUT){
638 if(n_var_vset(*(char const**)vp, (uintptr_t)cp))
639 rv = 0;
640 else
641 rv = -1;
642 }else{
643 if(fputs(cp, n_stdout) != EOF)
644 rv = 0;
645 else{
646 clearerr(n_stdout);
647 rv = 1;
650 NYD_LEAVE;
651 return rv;
654 /* s-it-mode */