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 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #define n_FILE cmd_misc
38 #ifndef HAVE_AMALGAMATION
42 /* Expand the shell escape by expanding unescaped !'s into the last issued
43 * command where possible */
44 static char const *a_cmisc_bangexp(char const *cp
);
46 /* c_n?echo(), c_n?echoerr() */
47 static int a_cmisc_echo(void *vp
, FILE *fp
, bool_t donl
);
50 static bool_t
a_cmisc_read_set(char const *cp
, char const *value
);
53 static int a_cmisc_version_cmp(void const *s1
, void const *s2
);
56 a_cmisc_bangexp(char const *cp
){
57 static struct str last_bang
;
59 struct n_string xbang
, *bang
;
69 for(bang
= n_string_creat(&xbang
); (c
= *cp
++) != '\0';){
72 bang
= n_string_push_buf(bang
, last_bang
.s
, last_bang
.l
);
75 if(c
== '\\' && *cp
== '!'){
80 bang
= n_string_push_c(bang
, c
);
84 if(last_bang
.s
!= NULL
)
86 last_bang
.s
= n_string_cp(bang
);
87 last_bang
.l
= bang
->s_len
;
88 bang
= n_string_drop_ownership(bang
);
93 fprintf(n_stdout
, "!%s\n", cp
);
100 a_cmisc_echo(void *vp
, FILE *fp
, bool_t donl
){
101 struct n_string s
, *sp
;
104 char const **argv
, *varname
, **ap
, *cp
;
108 varname
= (n_pstate
& n_PS_ARGMOD_VPUT
) ? *argv
++ : NULL
;
109 sp
= n_string_reserve(n_string_creat_auto(&s
), 121/* XXX */);
111 doerr
= (fp
== n_stderr
&& (n_psonce
& n_PSO_INTERACTIVE
));
116 for(ap
= argv
; *ap
!= NULL
; ++ap
){
118 sp
= n_string_push_c(sp
, ' ');
119 if((cp
= fexpand(*ap
, FEXP_NSHORTCUT
| FEXP_NVAR
)) == NULL
)
121 sp
= n_string_push_cp(sp
, cp
);
124 sp
= n_string_push_c(sp
, '\n');
125 cp
= n_string_cp(sp
);
133 else if(fputs(cp
, fp
) == EOF
)
135 if((rv
= (fflush(fp
) == EOF
)))
137 rv
|= ferror(fp
) ? 1 : 0;
139 }else if(!n_var_vset(varname
, (uintptr_t)cp
)){
140 n_pstate_err_no
= n_ERR_NOTSUP
;
143 n_pstate_err_no
= n_ERR_NONE
;
151 a_cmisc_read_set(char const *cp
, char const *value
){
155 if(!n_shexp_is_valid_varname(cp
))
156 value
= N_("not a valid variable name");
157 else if(!n_var_is_user_writable(cp
))
158 value
= N_("variable is read-only");
159 else if(!n_var_vset(cp
, (uintptr_t)value
))
160 value
= N_("failed to update variable value");
165 n_err("`read': %s: %s\n", V_(value
), n_shexp_quote_cp(cp
, FAL0
));
173 a_cmisc_version_cmp(void const *s1
, void const *s2
){
174 char const * const *cp1
, * const *cp2
;
180 rv
= strcmp(&(*cp1
)[1], &(*cp2
)[1]);
193 if((n_idec_uiz_cp(&sec
, argv
[0], 0, NULL
) &
194 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
195 ) != n_IDEC_STATE_CONSUMED
)
200 else if((n_idec_uiz_cp(&msec
, argv
[1], 0, NULL
) &
201 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
202 ) != n_IDEC_STATE_CONSUMED
)
205 if(UIZ_MAX
/ n_DATE_MILLISSEC
< sec
)
207 sec
*= n_DATE_MILLISSEC
;
209 if(UIZ_MAX
- sec
< msec
)
213 n_pstate_err_no
= (n_msleep(msec
, (argv
[2] == NULL
)) > 0)
214 ? n_ERR_INTR
: n_ERR_NONE
;
217 return (argv
== NULL
);
219 n_err(_("`sleep': argument(s) overflow(s) datatype\n"));
220 n_pstate_err_no
= n_ERR_OVERFLOW
;
224 n_err(_("Synopsis: sleep: <seconds> [<milliseconds>] [uninterruptible]\n"));
225 n_pstate_err_no
= n_ERR_INVAL
;
236 char const **argv
, *varname
, *varres
, *cp
;
239 n_pstate_err_no
= n_ERR_NONE
;
241 varname
= (n_pstate
& n_PS_ARGMOD_VPUT
) ? *argv
++ : NULL
;
245 if(varname
!= NULL
&&
246 (fp
= Ftmp(NULL
, "shell", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)
248 n_pstate_err_no
= n_ERR_CANCELED
;
251 cp
= a_cmisc_bangexp(*argv
);
254 if(n_child_run(ok_vlook(SHELL
), &mask
,
255 n_CHILD_FD_PASS
, (fp
!= NULL
? fileno(fp
) : n_CHILD_FD_PASS
),
256 "-c", cp
, NULL
, NULL
, &rv
) < 0){
257 n_pstate_err_no
= n_err_no
;
260 rv
= WEXITSTATUS(rv
);
269 if(UICMP(64, l
, >=, UIZ_MAX
-42)){
270 n_pstate_err_no
= n_ERR_NOMEM
;
273 varres
= x
= n_autorec_alloc(l
+1);
275 for(; l
> 0 && (c
= getc(fp
)) != EOF
; --l
)
279 n_pstate_err_no
= n_err_no
;
280 varres
= n_empty
; /* xxx hmmm */
291 if(!n_var_vset(varname
, (uintptr_t)varres
)){
292 n_pstate_err_no
= n_ERR_NOTSUP
;
295 }else if(rv
>= 0 && (n_psonce
& n_PSO_INTERACTIVE
)){
296 fprintf(n_stdout
, "!\n");
297 /* Line buffered fflush(n_stdout); */
310 if(n_child_run(ok_vlook(SHELL
), 0, n_CHILD_FD_PASS
, n_CHILD_FD_PASS
, NULL
,
311 NULL
, NULL
, NULL
, &rv
) < 0){
312 n_pstate_err_no
= n_err_no
;
315 putc('\n', n_stdout
);
316 /* Line buffered fflush(n_stdout); */
317 n_pstate_err_no
= n_ERR_NONE
;
318 rv
= WEXITSTATUS(rv
);
326 struct n_string s_b
, *sp
;
331 sp
= n_string_creat_auto(&s_b
);
332 varname
= (n_pstate
& n_PS_ARGMOD_VPUT
) ? *(char const**)v
: NULL
;
335 for(;; l
+= PATH_MAX
){
336 sp
= n_string_resize(n_string_trunc(sp
, 0), l
);
338 if(getcwd(sp
->s_dat
, sp
->s_len
) == NULL
){
344 n_perr(_("Failed to getcwd(3)"), e
);
350 if(!n_var_vset(varname
, (uintptr_t)sp
->s_dat
))
353 l
= strlen(sp
->s_dat
);
354 sp
= n_string_trunc(sp
, l
);
355 if(fwrite(sp
->s_dat
, 1, sp
->s_len
, n_stdout
) == sp
->s_len
&&
356 putc('\n', n_stdout
) == EOF
)
372 if (*arglist
== NULL
)
374 else if ((cp
= fexpand(*arglist
, FEXP_LOCAL
| FEXP_NOPROTO
)) == NULL
)
376 if (chdir(cp
) == -1) {
390 rv
= a_cmisc_echo(v
, n_stdout
, TRU1
);
400 rv
= a_cmisc_echo(v
, n_stderr
, TRU1
);
410 rv
= a_cmisc_echo(v
, n_stdout
, FAL0
);
420 rv
= a_cmisc_echo(v
, n_stderr
, FAL0
);
426 c_read(void * volatile vp
){
429 struct n_string s
, *sp
;
433 char const *ifs
, **argv
, *cp
;
436 sp
= n_string_creat_auto(&s
);
437 sp
= n_string_reserve(sp
, 64 -1);
444 n_SIGMAN_ENTER_SWITCH(&sm
, n_SIGMAN_ALL
){
448 n_pstate_err_no
= n_ERR_INTR
;
453 n_pstate_err_no
= n_ERR_NONE
;
454 rv
= n_go_input(((n_pstate
& n_PS_COMPOSE_MODE
455 ? n_GO_INPUT_CTX_COMPOSE
: n_GO_INPUT_CTX_DEFAULT
) |
456 n_GO_INPUT_FORCE_STDIN
| n_GO_INPUT_NL_ESC
|
457 n_GO_INPUT_PROMPT_NONE
/* XXX POSIX: PS2: yes! */),
458 NULL
, &linebuf
, &linesize
, NULL
, NULL
);
460 if(!n_go_input_is_eof())
461 n_pstate_err_no
= n_ERR_BADF
;
464 if(n_go_input_is_eof()){
472 for(; *argv
!= NULL
; ++argv
){
473 if(trim
.l
== 0 || n_str_trim_ifs(&trim
, FAL0
)->l
== 0)
476 /* The last variable gets the remaining line less trailing IFS-WS */
479 sp
= n_string_assign_buf(sp
, trim
.s
, trim
.l
);
481 }else for(cp
= trim
.s
, i
= 1;; ++cp
, ++i
){
482 if(strchr(ifs
, *cp
) != NULL
){
483 sp
= n_string_assign_buf(sp
, trim
.s
, i
- 1);
492 if(!a_cmisc_read_set(*argv
, n_string_cp(sp
))){
493 n_pstate_err_no
= n_ERR_NOTSUP
;
500 /* Set the remains to the empty string */
501 for(; *argv
!= NULL
; ++argv
)
502 if(!a_cmisc_read_set(*argv
, n_empty
)){
503 n_pstate_err_no
= n_ERR_NOTSUP
;
508 n_sigman_cleanup_ping(&sm
);
513 n_sigman_leave(&sm
, n_SIGMAN_VIPSIGS_NTTYOUT
);
518 c_readall(void * vp
){ /* TODO 64-bit retval */
520 struct n_string s
, *sp
;
527 sp
= n_string_creat_auto(&s
);
528 sp
= n_string_reserve(sp
, 64 -1);
534 n_SIGMAN_ENTER_SWITCH(&sm
, n_SIGMAN_ALL
){
538 n_pstate_err_no
= n_ERR_INTR
;
543 n_pstate_err_no
= n_ERR_NONE
;
546 rv
= n_go_input(((n_pstate
& n_PS_COMPOSE_MODE
547 ? n_GO_INPUT_CTX_COMPOSE
: n_GO_INPUT_CTX_DEFAULT
) |
548 n_GO_INPUT_FORCE_STDIN
| /*n_GO_INPUT_NL_ESC |*/
549 n_GO_INPUT_PROMPT_NONE
),
550 NULL
, &linebuf
, &linesize
, NULL
, NULL
);
552 if(!n_go_input_is_eof()){
553 n_pstate_err_no
= n_ERR_BADF
;
559 }else if(rv
== 0){ /* xxx will not get*/
560 if(n_go_input_is_eof()){
567 }else if(UICMP(32, SI32_MAX
- sp
->s_len
, <=, rv
)){
568 n_pstate_err_no
= n_ERR_OVERFLOW
;
572 sp
= n_string_push_buf(sp
, linebuf
, rv
);
573 if(n_pstate
& n_PS_READLINE_NL
)
574 sp
= n_string_push_c(sp
, '\n');
578 if(!a_cmisc_read_set(argv
[0], n_string_cp(sp
))){
579 n_pstate_err_no
= n_ERR_NOTSUP
;
585 n_sigman_cleanup_ping(&sm
);
590 n_sigman_leave(&sm
, n_SIGMAN_VIPSIGS_NTTYOUT
);
598 char const *cp
, **arr
;
604 _("%s %s, %s (%s)\nFeatures included (+) or not (-)\n"),
605 n_uagent
, ok_vlook(version
), ok_vlook(version_date
),
606 ok_vlook(build_osenv
));
608 /* *features* starts with dummy byte to avoid + -> *folder* expansions */
609 i
= strlen(cp
= &ok_vlook(features
)[1]) +1;
610 iop
= n_autorec_alloc(i
);
613 arr
= n_autorec_alloc(sizeof(cp
) * VAL_FEATURES_CNT
);
614 for(longest
= 0, i
= 0; (cp
= n_strsep(&iop
, ',', TRU1
)) != NULL
; ++i
){
617 longest
= n_MAX(longest
, (int)i2
);
619 qsort(arr
, i
, sizeof(cp
), &a_cmisc_version_cmp
);
621 /* We use aligned columns, so don't use n_SCRNWIDTH_FOR_LISTS */
622 for(++longest
, i2
= 0; i
-- > 0;){
624 fprintf(n_stdout
, "%-*s ", longest
, cp
);
626 if(UICMP(z
, ++i2
+ longest
, >=, n_scrnwidth
) || i
== 0){
628 putc('\n', n_stdout
);
632 if((rv
= ferror(n_stdout
) != 0))