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
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
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
37 #define n_FILE cmd_misc
39 #ifndef HAVE_AMALGAMATION
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
);
53 static bool_t
a_cmisc_read_set(char const *cp
, char const *value
);
56 static int a_cmisc_version_cmp(void const *s1
, void const *s2
);
59 a_cmisc_bangexp(char const *cp
){
60 static struct str last_bang
;
62 struct n_string xbang
, *bang
;
72 for(bang
= n_string_creat(&xbang
); (c
= *cp
++) != '\0';){
75 bang
= n_string_push_buf(bang
, last_bang
.s
, last_bang
.l
);
78 if(c
== '\\' && *cp
== '!'){
83 bang
= n_string_push_c(bang
, c
);
87 if(last_bang
.s
!= NULL
)
89 last_bang
.s
= n_string_cp(bang
);
90 last_bang
.l
= bang
->s_len
;
91 bang
= n_string_drop_ownership(bang
);
96 fprintf(n_stdout
, "!%s\n", cp
);
103 a_cmisc_echo(void *vp
, FILE *fp
, bool_t donl
){
104 struct n_string s
, *sp
;
107 char const **argv
, *varname
, **ap
, *cp
;
111 varname
= (n_pstate
& n_PS_ARGMOD_VPUT
) ? *argv
++ : NULL
;
112 sp
= n_string_reserve(n_string_creat_auto(&s
), 121/* XXX */);
114 doerr
= (fp
== n_stderr
&& (n_psonce
& n_PSO_INTERACTIVE
));
119 for(ap
= argv
; *ap
!= NULL
; ++ap
){
121 sp
= n_string_push_c(sp
, ' ');
122 if((cp
= fexpand(*ap
, FEXP_NSHORTCUT
| FEXP_NVAR
)) == NULL
)
124 sp
= n_string_push_cp(sp
, cp
);
127 sp
= n_string_push_c(sp
, '\n');
128 cp
= n_string_cp(sp
);
135 /* xxx Ensure *log-prefix* will be placed by n_err() for next msg */
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
)
141 if((rv
= (fflush(fp
) == EOF
)))
143 rv
|= ferror(fp
) ? 1 : 0;
145 }else if(!n_var_vset(varname
, (uintptr_t)cp
)){
146 n_pstate_err_no
= n_ERR_NOTSUP
;
149 n_pstate_err_no
= n_ERR_NONE
;
157 a_cmisc_read_set(char const *cp
, char const *value
){
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");
171 n_err("`read': %s: %s\n", V_(value
), n_shexp_quote_cp(cp
, FAL0
));
179 a_cmisc_version_cmp(void const *s1
, void const *s2
){
180 char const * const *cp1
, * const *cp2
;
186 rv
= strcmp(&(*cp1
)[1], &(*cp2
)[1]);
197 char const **argv
, *varname
, *varres
, *cp
;
200 n_pstate_err_no
= n_ERR_NONE
;
202 varname
= (n_pstate
& n_PS_ARGMOD_VPUT
) ? *argv
++ : NULL
;
206 if(varname
!= NULL
&&
207 (fp
= Ftmp(NULL
, "shell", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)
209 n_pstate_err_no
= n_ERR_CANCELED
;
212 cp
= a_cmisc_bangexp(*argv
);
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
;
221 rv
= WEXITSTATUS(rv
);
230 if(UICMP(64, l
, >=, UIZ_MAX
-42)){
231 n_pstate_err_no
= n_ERR_NOMEM
;
234 varres
= x
= n_autorec_alloc(l
+1);
236 for(; l
> 0 && (c
= getc(fp
)) != EOF
; --l
)
240 n_pstate_err_no
= n_err_no
;
241 varres
= n_empty
; /* xxx hmmm */
252 if(!n_var_vset(varname
, (uintptr_t)varres
)){
253 n_pstate_err_no
= n_ERR_NOTSUP
;
256 }else if(rv
>= 0 && (n_psonce
& n_PSO_INTERACTIVE
)){
257 fprintf(n_stdout
, "!\n");
258 /* Line buffered fflush(n_stdout); */
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
;
276 putc('\n', n_stdout
);
277 /* Line buffered fflush(n_stdout); */
278 n_pstate_err_no
= n_ERR_NONE
;
279 rv
= WEXITSTATUS(rv
);
287 struct n_string s_b
, *sp
;
292 sp
= n_string_creat_auto(&s_b
);
293 varname
= (n_pstate
& n_PS_ARGMOD_VPUT
) ? *(char const**)v
: NULL
;
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
){
305 n_perr(_("Failed to getcwd(3)"), e
);
311 if(!n_var_vset(varname
, (uintptr_t)sp
->s_dat
))
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
)
333 if (*arglist
== NULL
)
335 else if ((cp
= fexpand(*arglist
, FEXP_LOCAL
| FEXP_NOPROTO
)) == NULL
)
337 if (chdir(cp
) == -1) {
351 rv
= a_cmisc_echo(v
, n_stdout
, TRU1
);
361 rv
= a_cmisc_echo(v
, n_stderr
, TRU1
);
371 rv
= a_cmisc_echo(v
, n_stdout
, FAL0
);
381 rv
= a_cmisc_echo(v
, n_stderr
, FAL0
);
387 c_read(void * volatile vp
){
390 struct n_string s
, *sp
;
394 char const *ifs
, **argv
, *cp
;
397 sp
= n_string_creat_auto(&s
);
398 sp
= n_string_reserve(sp
, 64 -1);
405 n_SIGMAN_ENTER_SWITCH(&sm
, n_SIGMAN_ALL
){
409 n_pstate_err_no
= n_ERR_INTR
;
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
);
421 if(!n_go_input_is_eof())
422 n_pstate_err_no
= n_ERR_BADF
;
425 if(n_go_input_is_eof()){
433 for(; *argv
!= NULL
; ++argv
){
434 if(trim
.l
== 0 || n_str_trim_ifs(&trim
, FAL0
)->l
== 0)
437 /* The last variable gets the remaining line less trailing IFS-WS */
440 sp
= n_string_assign_buf(sp
, trim
.s
, trim
.l
);
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);
453 if(!a_cmisc_read_set(*argv
, n_string_cp(sp
))){
454 n_pstate_err_no
= n_ERR_NOTSUP
;
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
;
469 n_sigman_cleanup_ping(&sm
);
474 n_sigman_leave(&sm
, n_SIGMAN_VIPSIGS_NTTYOUT
);
479 c_readall(void * vp
){ /* TODO 64-bit retval */
481 struct n_string s
, *sp
;
488 sp
= n_string_creat_auto(&s
);
489 sp
= n_string_reserve(sp
, 64 -1);
495 n_SIGMAN_ENTER_SWITCH(&sm
, n_SIGMAN_ALL
){
499 n_pstate_err_no
= n_ERR_INTR
;
504 n_pstate_err_no
= n_ERR_NONE
;
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
);
513 if(!n_go_input_is_eof()){
514 n_pstate_err_no
= n_ERR_BADF
;
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()){
533 }else if(n_LIKELY(UICMP(32, SI32_MAX
- sp
->s_len
, >, rv
)))
534 sp
= n_string_push_buf(sp
, linebuf
, rv
);
536 n_pstate_err_no
= n_ERR_OVERFLOW
;
542 if(!a_cmisc_read_set(argv
[0], n_string_cp(sp
))){
543 n_pstate_err_no
= n_ERR_NOTSUP
;
549 n_sigman_cleanup_ping(&sm
);
554 n_sigman_leave(&sm
, n_SIGMAN_VIPSIGS_NTTYOUT
);
561 struct n_string s
, *sp
= &s
;
564 char const *cp
, **arr
;
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
);
591 arr
= n_autorec_alloc(sizeof(cp
) * VAL_FEATURES_CNT
);
592 for(i
= 0; (cp
= n_strsep(&iop
, ',', TRU1
)) != NULL
; ++i
)
594 qsort(arr
, i
, sizeof(cp
), &a_cmisc_version_cmp
);
596 for(lnlen
= 0; i
-- > 0;){
600 if((lnlen
+= j
+ 1) > 72){
601 sp
= n_string_push_c(sp
, '\n');
604 sp
= n_string_push_c(sp
, ' ');
605 sp
= n_string_push_buf(sp
, cp
, j
);
607 sp
= n_string_push_c(sp
, '\n');
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 */
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');
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
))
643 if(fputs(cp
, n_stdout
) != EOF
)