commit 68d8340
xplshn
·
2026-06-09 04:23:20 +0000 UTC
parent b4204c7
Feature gating and warning fixes Signed-off-by: xplshn <anto@xplshn.com.ar>
136 files changed,
+33764,
-262
M
Makefile
M
Makefile
+444,
-13
1@@ -97,11 +97,12 @@ LIBUTILOBJ =\
2 shared/libutil/proc.o\
3 shared/libutil/tty.o\
4 shared/libutil/fconcat.o\
5- shared/libutil/recurse_dir.o
6+ shared/libutil/recurse_dir.o\
7+ shared/libutil/sig.o
8
9 LIB = shared/libutf/libutf.a shared/libutil/libutil.a
10
11-POSIX_BIN =\
12+POSIX_BIN_ALL =\
13 cmd/posix/basename\
14 cmd/posix/cal\
15 cmd/posix/cat\
16@@ -175,9 +176,12 @@ POSIX_BIN =\
17 cmd/posix/uuencode\
18 cmd/posix/wc\
19 cmd/posix/who\
20- cmd/posix/xargs
21+ cmd/posix/xargs\
22+ cmd/posix/awk/awk\
23+ cmd/posix/sh/sh\
24+ cmd/posix/pax
25
26-LINUX_BIN =\
27+LINUX_BIN_ALL =\
28 cmd/linux/blkdiscard\
29 cmd/linux/chvt\
30 cmd/linux/ctrlaltdel\
31@@ -207,7 +211,7 @@ LINUX_BIN =\
32 cmd/linux/uptime\
33 cmd/linux/vtallow
34
35-NET_BIN =\
36+NET_BIN_ALL =\
37 cmd/net/netcat\
38 cmd/net/tftp\
39 cmd/net/tunctl\
40@@ -217,12 +221,12 @@ NET_BIN =\
41 cmd/net/host\
42 cmd/net/httpd
43
44-XSI_BIN =\
45+XSI_BIN_ALL =\
46 cmd/xsi/mknod\
47 cmd/xsi/passwd\
48 cmd/xsi/su
49
50-PSEUDO_BIN =\
51+PSEUDO_BIN_ALL =\
52 cmd/pseudo/chroot\
53 cmd/pseudo/clear\
54 cmd/pseudo/cols\
55@@ -259,7 +263,8 @@ PSEUDO_BIN =\
56 cmd/pseudo/which\
57 cmd/pseudo/whoami\
58 cmd/pseudo/xinstall\
59- cmd/pseudo/yes
60+ cmd/pseudo/yes\
61+ cmd/pseudo/base64
62
63 MAKEOBJ =\
64 cmd/posix/make/defaults.o\
65@@ -268,13 +273,340 @@ MAKEOBJ =\
66 cmd/posix/make/posix.o\
67 cmd/posix/make/rules.o
68
69+BIN_basename_1 = cmd/posix/basename
70+BIN_cal_1 = cmd/posix/cal
71+BIN_cat_1 = cmd/posix/cat
72+BIN_chgrp_1 = cmd/posix/chgrp
73+BIN_chmod_1 = cmd/posix/chmod
74+BIN_chown_1 = cmd/posix/chown
75+BIN_cksum_1 = cmd/posix/cksum
76+BIN_cmp_1 = cmd/posix/cmp
77+BIN_comm_1 = cmd/posix/comm
78+BIN_cp_1 = cmd/posix/cp
79+BIN_cut_1 = cmd/posix/cut
80+BIN_date_1 = cmd/posix/date
81+BIN_dd_1 = cmd/posix/dd
82+BIN_df_1 = cmd/posix/df
83+BIN_dirname_1 = cmd/posix/dirname
84+BIN_du_1 = cmd/posix/du
85+BIN_echo_1 = cmd/posix/echo
86+BIN_ed_1 = cmd/posix/ed
87+BIN_env_1 = cmd/posix/env
88+BIN_expand_1 = cmd/posix/expand
89+BIN_expr_1 = cmd/posix/expr
90+BIN_false_1 = cmd/posix/false
91+BIN_find_1 = cmd/posix/find
92+BIN_fold_1 = cmd/posix/fold
93+BIN_getconf_1 = cmd/posix/getconf
94+BIN_grep_1 = cmd/posix/grep
95+BIN_head_1 = cmd/posix/head
96+BIN_id_1 = cmd/posix/id
97+BIN_join_1 = cmd/posix/join
98+BIN_kill_1 = cmd/posix/kill
99+BIN_link_1 = cmd/posix/link
100+BIN_ln_1 = cmd/posix/ln
101+BIN_logger_1 = cmd/posix/logger
102+BIN_logname_1 = cmd/posix/logname
103+BIN_ls_1 = cmd/posix/ls
104+BIN_mesg_1 = cmd/posix/mesg
105+BIN_mkdir_1 = cmd/posix/mkdir
106+BIN_mkfifo_1 = cmd/posix/mkfifo
107+BIN_mv_1 = cmd/posix/mv
108+BIN_nice_1 = cmd/posix/nice
109+BIN_nl_1 = cmd/posix/nl
110+BIN_nohup_1 = cmd/posix/nohup
111+BIN_od_1 = cmd/posix/od
112+BIN_paste_1 = cmd/posix/paste
113+BIN_pathchk_1 = cmd/posix/pathchk
114+BIN_printf_1 = cmd/posix/printf
115+BIN_ps_1 = cmd/posix/ps
116+BIN_pwd_1 = cmd/posix/pwd
117+BIN_readlink_1 = cmd/posix/readlink
118+BIN_renice_1 = cmd/posix/renice
119+BIN_rm_1 = cmd/posix/rm
120+BIN_rmdir_1 = cmd/posix/rmdir
121+BIN_sed_1 = cmd/posix/sed
122+BIN_sleep_1 = cmd/posix/sleep
123+BIN_sort_1 = cmd/posix/sort
124+BIN_split_1 = cmd/posix/split
125+BIN_tail_1 = cmd/posix/tail
126+BIN_tee_1 = cmd/posix/tee
127+BIN_test_1 = cmd/posix/test
128+BIN_time_1 = cmd/posix/time
129+BIN_touch_1 = cmd/posix/touch
130+BIN_tr_1 = cmd/posix/tr
131+BIN_true_1 = cmd/posix/true
132+BIN_tsort_1 = cmd/posix/tsort
133+BIN_tty_1 = cmd/posix/tty
134+BIN_uname_1 = cmd/posix/uname
135+BIN_unexpand_1 = cmd/posix/unexpand
136+BIN_uniq_1 = cmd/posix/uniq
137+BIN_unlink_1 = cmd/posix/unlink
138+BIN_uudecode_1 = cmd/posix/uudecode
139+BIN_uuencode_1 = cmd/posix/uuencode
140+BIN_wc_1 = cmd/posix/wc
141+BIN_who_1 = cmd/posix/who
142+BIN_xargs_1 = cmd/posix/xargs
143+BIN_awk_1 = cmd/posix/awk/awk
144+BIN_sh_1 = cmd/posix/sh/sh
145+BIN_pax_1 = cmd/posix/pax
146+
147+BIN_blkdiscard_1 = cmd/linux/blkdiscard
148+BIN_chvt_1 = cmd/linux/chvt
149+BIN_ctrlaltdel_1 = cmd/linux/ctrlaltdel
150+BIN_dmesg_1 = cmd/linux/dmesg
151+BIN_eject_1 = cmd/linux/eject
152+BIN_fallocate_1 = cmd/linux/fallocate
153+BIN_free_1 = cmd/linux/free
154+BIN_freeramdisk_1 = cmd/linux/freeramdisk
155+BIN_fsfreeze_1 = cmd/linux/fsfreeze
156+BIN_hwclock_1 = cmd/linux/hwclock
157+BIN_insmod_1 = cmd/linux/insmod
158+BIN_lsmod_1 = cmd/linux/lsmod
159+BIN_mkswap_1 = cmd/linux/mkswap
160+BIN_mount_1 = cmd/linux/mount
161+BIN_mountpoint_1 = cmd/linux/mountpoint
162+BIN_pidof_1 = cmd/linux/pidof
163+BIN_pivot_root_1 = cmd/linux/pivot_root
164+BIN_pwdx_1 = cmd/linux/pwdx
165+BIN_readahead_1 = cmd/linux/readahead
166+BIN_rmmod_1 = cmd/linux/rmmod
167+BIN_swaplabel_1 = cmd/linux/swaplabel
168+BIN_swapoff_1 = cmd/linux/swapoff
169+BIN_swapon_1 = cmd/linux/swapon
170+BIN_switch_root_1 = cmd/linux/switch_root
171+BIN_umount_1 = cmd/linux/umount
172+BIN_unshare_1 = cmd/linux/unshare
173+BIN_uptime_1 = cmd/linux/uptime
174+BIN_vtallow_1 = cmd/linux/vtallow
175+
176+BIN_netcat_1 = cmd/net/netcat
177+BIN_tftp_1 = cmd/net/tftp
178+BIN_tunctl_1 = cmd/net/tunctl
179+BIN_wget_1 = cmd/net/wget
180+BIN_ping_1 = cmd/net/ping
181+BIN_ifconfig_1 = cmd/net/ifconfig
182+BIN_host_1 = cmd/net/host
183+BIN_httpd_1 = cmd/net/httpd
184+
185+BIN_mknod_1 = cmd/xsi/mknod
186+BIN_passwd_1 = cmd/xsi/passwd
187+BIN_su_1 = cmd/xsi/su
188+
189+BIN_chroot_1 = cmd/pseudo/chroot
190+BIN_clear_1 = cmd/pseudo/clear
191+BIN_cols_1 = cmd/pseudo/cols
192+BIN_cron_1 = cmd/pseudo/cron
193+BIN_flock_1 = cmd/pseudo/flock
194+BIN_getty_1 = cmd/pseudo/getty
195+BIN_halt_1 = cmd/pseudo/halt
196+BIN_hostname_1 = cmd/pseudo/hostname
197+BIN_killall5_1 = cmd/pseudo/killall5
198+BIN_last_1 = cmd/pseudo/last
199+BIN_lastlog_1 = cmd/pseudo/lastlog
200+BIN_login_1 = cmd/pseudo/login
201+BIN_md5sum_1 = cmd/pseudo/md5sum
202+BIN_mktemp_1 = cmd/pseudo/mktemp
203+BIN_nologin_1 = cmd/pseudo/nologin
204+BIN_pagesize_1 = cmd/pseudo/pagesize
205+BIN_printenv_1 = cmd/pseudo/printenv
206+BIN_respawn_1 = cmd/pseudo/respawn
207+BIN_rev_1 = cmd/pseudo/rev
208+BIN_seq_1 = cmd/pseudo/seq
209+BIN_setsid_1 = cmd/pseudo/setsid
210+BIN_sha1sum_1 = cmd/pseudo/sha1sum
211+BIN_sha224sum_1 = cmd/pseudo/sha224sum
212+BIN_sha256sum_1 = cmd/pseudo/sha256sum
213+BIN_sha384sum_1 = cmd/pseudo/sha384sum
214+BIN_sha512sum_1 = cmd/pseudo/sha512sum
215+BIN_sha512_224sum_1 = cmd/pseudo/sha512-224sum
216+BIN_sha512_256sum_1 = cmd/pseudo/sha512-256sum
217+BIN_sponge_1 = cmd/pseudo/sponge
218+BIN_stat_1 = cmd/pseudo/stat
219+BIN_tar_1 = cmd/pseudo/tar
220+BIN_truncate_1 = cmd/pseudo/truncate
221+BIN_watch_1 = cmd/pseudo/watch
222+BIN_which_1 = cmd/pseudo/which
223+BIN_whoami_1 = cmd/pseudo/whoami
224+BIN_xinstall_1 = cmd/pseudo/xinstall
225+BIN_yes_1 = cmd/pseudo/yes
226+BIN_base64_1 = cmd/pseudo/base64
227+
228+BIN_make_tool_1 = cmd/posix/make/make
229+
230+POSIX_BIN = \
231+ $(BIN_basename_$(BUILD_POSIX_BASENAME)) \
232+ $(BIN_cal_$(BUILD_POSIX_CAL)) \
233+ $(BIN_cat_$(BUILD_POSIX_CAT)) \
234+ $(BIN_chgrp_$(BUILD_POSIX_CHGRP)) \
235+ $(BIN_chmod_$(BUILD_POSIX_CHMOD)) \
236+ $(BIN_chown_$(BUILD_POSIX_CHOWN)) \
237+ $(BIN_cksum_$(BUILD_POSIX_CKSUM)) \
238+ $(BIN_cmp_$(BUILD_POSIX_CMP)) \
239+ $(BIN_comm_$(BUILD_POSIX_COMM)) \
240+ $(BIN_cp_$(BUILD_POSIX_CP)) \
241+ $(BIN_cut_$(BUILD_POSIX_CUT)) \
242+ $(BIN_date_$(BUILD_POSIX_DATE)) \
243+ $(BIN_dd_$(BUILD_POSIX_DD)) \
244+ $(BIN_df_$(BUILD_POSIX_DF)) \
245+ $(BIN_dirname_$(BUILD_POSIX_DIRNAME)) \
246+ $(BIN_du_$(BUILD_POSIX_DU)) \
247+ $(BIN_echo_$(BUILD_POSIX_ECHO)) \
248+ $(BIN_ed_$(BUILD_POSIX_ED)) \
249+ $(BIN_env_$(BUILD_POSIX_ENV)) \
250+ $(BIN_expand_$(BUILD_POSIX_EXPAND)) \
251+ $(BIN_expr_$(BUILD_POSIX_EXPR)) \
252+ $(BIN_false_$(BUILD_POSIX_FALSE)) \
253+ $(BIN_find_$(BUILD_POSIX_FIND)) \
254+ $(BIN_fold_$(BUILD_POSIX_FOLD)) \
255+ $(BIN_getconf_$(BUILD_POSIX_GETCONF)) \
256+ $(BIN_grep_$(BUILD_POSIX_GREP)) \
257+ $(BIN_head_$(BUILD_POSIX_HEAD)) \
258+ $(BIN_id_$(BUILD_POSIX_ID)) \
259+ $(BIN_join_$(BUILD_POSIX_JOIN)) \
260+ $(BIN_kill_$(BUILD_POSIX_KILL)) \
261+ $(BIN_link_$(BUILD_POSIX_LINK)) \
262+ $(BIN_ln_$(BUILD_POSIX_LN)) \
263+ $(BIN_logger_$(BUILD_POSIX_LOGGER)) \
264+ $(BIN_logname_$(BUILD_POSIX_LOGNAME)) \
265+ $(BIN_ls_$(BUILD_POSIX_LS)) \
266+ $(BIN_mesg_$(BUILD_POSIX_MESG)) \
267+ $(BIN_mkdir_$(BUILD_POSIX_MKDIR)) \
268+ $(BIN_mkfifo_$(BUILD_POSIX_MKFIFO)) \
269+ $(BIN_mv_$(BUILD_POSIX_MV)) \
270+ $(BIN_nice_$(BUILD_POSIX_NICE)) \
271+ $(BIN_nl_$(BUILD_POSIX_NL)) \
272+ $(BIN_nohup_$(BUILD_POSIX_NOHUP)) \
273+ $(BIN_od_$(BUILD_POSIX_OD)) \
274+ $(BIN_paste_$(BUILD_POSIX_PASTE)) \
275+ $(BIN_pathchk_$(BUILD_POSIX_PATHCHK)) \
276+ $(BIN_printf_$(BUILD_POSIX_PRINTF)) \
277+ $(BIN_ps_$(BUILD_POSIX_PS)) \
278+ $(BIN_pwd_$(BUILD_POSIX_PWD)) \
279+ $(BIN_readlink_$(BUILD_POSIX_READLINK)) \
280+ $(BIN_renice_$(BUILD_POSIX_RENICE)) \
281+ $(BIN_rm_$(BUILD_POSIX_RM)) \
282+ $(BIN_rmdir_$(BUILD_POSIX_RMDIR)) \
283+ $(BIN_sed_$(BUILD_POSIX_SED)) \
284+ $(BIN_sleep_$(BUILD_POSIX_SLEEP)) \
285+ $(BIN_sort_$(BUILD_POSIX_SORT)) \
286+ $(BIN_split_$(BUILD_POSIX_SPLIT)) \
287+ $(BIN_tail_$(BUILD_POSIX_TAIL)) \
288+ $(BIN_tee_$(BUILD_POSIX_TEE)) \
289+ $(BIN_test_$(BUILD_POSIX_TEST)) \
290+ $(BIN_time_$(BUILD_POSIX_TIME)) \
291+ $(BIN_touch_$(BUILD_POSIX_TOUCH)) \
292+ $(BIN_tr_$(BUILD_POSIX_TR)) \
293+ $(BIN_true_$(BUILD_POSIX_TRUE)) \
294+ $(BIN_tsort_$(BUILD_POSIX_TSORT)) \
295+ $(BIN_tty_$(BUILD_POSIX_TTY)) \
296+ $(BIN_uname_$(BUILD_POSIX_UNAME)) \
297+ $(BIN_unexpand_$(BUILD_POSIX_UNEXPAND)) \
298+ $(BIN_uniq_$(BUILD_POSIX_UNIQ)) \
299+ $(BIN_unlink_$(BUILD_POSIX_UNLINK)) \
300+ $(BIN_uudecode_$(BUILD_POSIX_UUDECODE)) \
301+ $(BIN_uuencode_$(BUILD_POSIX_UUENCODE)) \
302+ $(BIN_wc_$(BUILD_POSIX_WC)) \
303+ $(BIN_who_$(BUILD_POSIX_WHO)) \
304+ $(BIN_xargs_$(BUILD_POSIX_XARGS)) \
305+ $(BIN_awk_$(BUILD_POSIX_AWK)) \
306+ $(BIN_sh_$(BUILD_POSIX_SH)) \
307+ $(BIN_pax_$(BUILD_POSIX_PAX))
308+
309+LINUX_BIN = \
310+ $(BIN_blkdiscard_$(BUILD_LINUX_BLKDISCARD)) \
311+ $(BIN_chvt_$(BUILD_LINUX_CHVT)) \
312+ $(BIN_ctrlaltdel_$(BUILD_LINUX_CTRLALTDEL)) \
313+ $(BIN_dmesg_$(BUILD_LINUX_DMESG)) \
314+ $(BIN_eject_$(BUILD_LINUX_EJECT)) \
315+ $(BIN_fallocate_$(BUILD_LINUX_FALLOCATE)) \
316+ $(BIN_free_$(BUILD_LINUX_FREE)) \
317+ $(BIN_freeramdisk_$(BUILD_LINUX_FREERAMDISK)) \
318+ $(BIN_fsfreeze_$(BUILD_LINUX_FSFREEZE)) \
319+ $(BIN_hwclock_$(BUILD_LINUX_HWCLOCK)) \
320+ $(BIN_insmod_$(BUILD_LINUX_INSMOD)) \
321+ $(BIN_lsmod_$(BUILD_LINUX_LSMOD)) \
322+ $(BIN_mkswap_$(BUILD_LINUX_MKSWAP)) \
323+ $(BIN_mount_$(BUILD_LINUX_MOUNT)) \
324+ $(BIN_mountpoint_$(BUILD_LINUX_MOUNTPOINT)) \
325+ $(BIN_pidof_$(BUILD_LINUX_PIDOF)) \
326+ $(BIN_pivot_root_$(BUILD_LINUX_PIVOT_ROOT)) \
327+ $(BIN_pwdx_$(BUILD_LINUX_PWDX)) \
328+ $(BIN_readahead_$(BUILD_LINUX_READAHEAD)) \
329+ $(BIN_rmmod_$(BUILD_LINUX_RMMOD)) \
330+ $(BIN_swaplabel_$(BUILD_LINUX_SWAPLABEL)) \
331+ $(BIN_swapoff_$(BUILD_LINUX_SWAPOFF)) \
332+ $(BIN_swapon_$(BUILD_LINUX_SWAPON)) \
333+ $(BIN_switch_root_$(BUILD_LINUX_SWITCH_ROOT)) \
334+ $(BIN_umount_$(BUILD_LINUX_UMOUNT)) \
335+ $(BIN_unshare_$(BUILD_LINUX_UNSHARE)) \
336+ $(BIN_uptime_$(BUILD_LINUX_UPTIME)) \
337+ $(BIN_vtallow_$(BUILD_LINUX_VTALLOW))
338+
339+NET_BIN = \
340+ $(BIN_netcat_$(BUILD_NET_NETCAT)) \
341+ $(BIN_tftp_$(BUILD_NET_TFTP)) \
342+ $(BIN_tunctl_$(BUILD_NET_TUNCTL)) \
343+ $(BIN_wget_$(BUILD_NET_WGET)) \
344+ $(BIN_ping_$(BUILD_NET_PING)) \
345+ $(BIN_ifconfig_$(BUILD_NET_IFCONFIG)) \
346+ $(BIN_host_$(BUILD_NET_HOST)) \
347+ $(BIN_httpd_$(BUILD_NET_HTTPD))
348+
349+XSI_BIN = \
350+ $(BIN_mknod_$(BUILD_XSI_MKNOD)) \
351+ $(BIN_passwd_$(BUILD_XSI_PASSWD)) \
352+ $(BIN_su_$(BUILD_XSI_SU))
353+
354+PSEUDO_BIN = \
355+ $(BIN_chroot_$(BUILD_PSEUDO_CHROOT)) \
356+ $(BIN_clear_$(BUILD_PSEUDO_CLEAR)) \
357+ $(BIN_cols_$(BUILD_PSEUDO_COLS)) \
358+ $(BIN_cron_$(BUILD_PSEUDO_CRON)) \
359+ $(BIN_flock_$(BUILD_PSEUDO_FLOCK)) \
360+ $(BIN_getty_$(BUILD_PSEUDO_GETTY)) \
361+ $(BIN_halt_$(BUILD_PSEUDO_HALT)) \
362+ $(BIN_hostname_$(BUILD_PSEUDO_HOSTNAME)) \
363+ $(BIN_killall5_$(BUILD_PSEUDO_KILLALL5)) \
364+ $(BIN_last_$(BUILD_PSEUDO_LAST)) \
365+ $(BIN_lastlog_$(BUILD_PSEUDO_LASTLOG)) \
366+ $(BIN_login_$(BUILD_PSEUDO_LOGIN)) \
367+ $(BIN_md5sum_$(BUILD_PSEUDO_MD5SUM)) \
368+ $(BIN_mktemp_$(BUILD_PSEUDO_MKTEMP)) \
369+ $(BIN_nologin_$(BUILD_PSEUDO_NOLOGIN)) \
370+ $(BIN_pagesize_$(BUILD_PSEUDO_PAGESIZE)) \
371+ $(BIN_printenv_$(BUILD_PSEUDO_PRINTENV)) \
372+ $(BIN_respawn_$(BUILD_PSEUDO_RESPAWN)) \
373+ $(BIN_rev_$(BUILD_PSEUDO_REV)) \
374+ $(BIN_seq_$(BUILD_PSEUDO_SEQ)) \
375+ $(BIN_setsid_$(BUILD_PSEUDO_SETSID)) \
376+ $(BIN_sha1sum_$(BUILD_PSEUDO_SHA1SUM)) \
377+ $(BIN_sha224sum_$(BUILD_PSEUDO_SHA224SUM)) \
378+ $(BIN_sha256sum_$(BUILD_PSEUDO_SHA256SUM)) \
379+ $(BIN_sha384sum_$(BUILD_PSEUDO_SHA384SUM)) \
380+ $(BIN_sha512sum_$(BUILD_PSEUDO_SHA512SUM)) \
381+ $(BIN_sha512_224sum_$(BUILD_PSEUDO_SHA512_224SUM)) \
382+ $(BIN_sha512_256sum_$(BUILD_PSEUDO_SHA512_256SUM)) \
383+ $(BIN_sponge_$(BUILD_PSEUDO_SPONGE)) \
384+ $(BIN_stat_$(BUILD_PSEUDO_STAT)) \
385+ $(BIN_tar_$(BUILD_PSEUDO_TAR)) \
386+ $(BIN_truncate_$(BUILD_PSEUDO_TRUNCATE)) \
387+ $(BIN_watch_$(BUILD_PSEUDO_WATCH)) \
388+ $(BIN_which_$(BUILD_PSEUDO_WHICH)) \
389+ $(BIN_whoami_$(BUILD_PSEUDO_WHOAMI)) \
390+ $(BIN_xinstall_$(BUILD_PSEUDO_XINSTALL)) \
391+ $(BIN_yes_$(BUILD_PSEUDO_YES)) \
392+ $(BIN_base64_$(BUILD_PSEUDO_BASE64))
393+
394+MAKE_BIN = $(BIN_make_tool_$(BUILD_MAKE_MAKE))
395+
396 OBJ = $(LIBUTFOBJ) $(LIBUTILOBJ) $(MAKEOBJ)
397
398-all: $(LIB) $(POSIX_BIN) $(LINUX_BIN) $(NET_BIN) $(XSI_BIN) $(PSEUDO_BIN) cmd/posix/make/make
399+all: $(LIB) $(POSIX_BIN) $(LINUX_BIN) $(NET_BIN) $(XSI_BIN) $(PSEUDO_BIN) $(MAKE_BIN)
400
401-$(POSIX_BIN) $(LINUX_BIN) $(NET_BIN) $(XSI_BIN) $(PSEUDO_BIN): $(LIB)
402+$(POSIX_BIN_ALL) $(LINUX_BIN_ALL) $(NET_BIN_ALL) $(XSI_BIN_ALL) $(PSEUDO_BIN_ALL): $(LIB)
403
404-$(OBJ) $(POSIX_BIN) $(LINUX_BIN) $(NET_BIN) $(XSI_BIN) $(PSEUDO_BIN): $(HDR)
405+$(OBJ) $(POSIX_BIN_ALL) $(LINUX_BIN_ALL) $(NET_BIN_ALL) $(XSI_BIN_ALL) $(PSEUDO_BIN_ALL): $(HDR)
406
407 .o:
408 $(CC) $(LDFLAGS) -o $@ $< $(LIB) $(LDLIBS)
409@@ -315,7 +647,106 @@ box: $(LIB)
410 scripts/mkbox
411
412 clean:
413- rm -f shared/libutf/*.o shared/libutil/*.o cmd/posix/make/*.o
414- rm -f $(POSIX_BIN) $(LINUX_BIN) $(NET_BIN) $(XSI_BIN) $(PSEUDO_BIN) $(LIB)
415+ rm -f shared/libutf/*.o shared/libutil/*.o cmd/posix/make/*.o cmd/posix/awk/*.o cmd/posix/sh/*.o cmd/dev/ar/*.o cmd/dev/ld/*.o
416+ rm -f $(POSIX_BIN_ALL) $(LINUX_BIN_ALL) $(NET_BIN_ALL) $(XSI_BIN_ALL) $(PSEUDO_BIN_ALL) $(LIB)
417 rm -f cmd/posix/make/make cmd/posix/getconf.h cmd/posix/bc.c
418+ rm -f cmd/posix/awk/awk cmd/posix/awk/maketab cmd/posix/awk/awkgram.tab.c cmd/posix/awk/awkgram.tab.h cmd/posix/awk/proctab.c
419+ rm -f cmd/posix/sh/sh cmd/posix/sh/mknodes cmd/posix/sh/mksyntax
420+ rm -f cmd/posix/sh/syntax.c cmd/posix/sh/syntax.h cmd/posix/sh/nodes.c cmd/posix/sh/nodes.h cmd/posix/sh/builtins.c cmd/posix/sh/builtins.h cmd/posix/sh/token.h
421 rm -rf aruu-box .box
422+
423+AWKOBJ =\
424+ cmd/posix/awk/b.o\
425+ cmd/posix/awk/main.o\
426+ cmd/posix/awk/parse.o\
427+ cmd/posix/awk/proctab.o\
428+ cmd/posix/awk/tran.o\
429+ cmd/posix/awk/lib.o\
430+ cmd/posix/awk/run.o\
431+ cmd/posix/awk/lex.o\
432+ cmd/posix/awk/math.o\
433+ cmd/posix/awk/awkgram.tab.o
434+
435+SH_GENHDRS =\
436+ cmd/posix/sh/syntax.h\
437+ cmd/posix/sh/nodes.h\
438+ cmd/posix/sh/builtins.h\
439+ cmd/posix/sh/token.h
440+
441+SHOBJ =\
442+ cmd/posix/sh/alias.o\
443+ cmd/posix/sh/arith_yacc.o\
444+ cmd/posix/sh/arith_yylex.o\
445+ cmd/posix/sh/cd.o\
446+ cmd/posix/sh/echo.o\
447+ cmd/posix/sh/error.o\
448+ cmd/posix/sh/eval.o\
449+ cmd/posix/sh/exec.o\
450+ cmd/posix/sh/expand.o\
451+ cmd/posix/sh/histedit.o\
452+ cmd/posix/sh/input.o\
453+ cmd/posix/sh/jobs.o\
454+ cmd/posix/sh/kill.o\
455+ cmd/posix/sh/mail.o\
456+ cmd/posix/sh/main.o\
457+ cmd/posix/sh/memalloc.o\
458+ cmd/posix/sh/miscbltin.o\
459+ cmd/posix/sh/mystring.o\
460+ cmd/posix/sh/options.o\
461+ cmd/posix/sh/output.o\
462+ cmd/posix/sh/parser.o\
463+ cmd/posix/sh/printf.o\
464+ cmd/posix/sh/redir.o\
465+ cmd/posix/sh/show.o\
466+ cmd/posix/sh/test.o\
467+ cmd/posix/sh/trap.o\
468+ cmd/posix/sh/var.o\
469+ cmd/posix/sh/builtins.o\
470+ cmd/posix/sh/nodes.o\
471+ cmd/posix/sh/syntax.o
472+
473+cmd/posix/awk/awkgram.tab.c cmd/posix/awk/awkgram.tab.h: cmd/posix/awk/awkgram.y
474+ $(YACC) -d -o cmd/posix/awk/awkgram.tab.c cmd/posix/awk/awkgram.y
475+
476+cmd/posix/awk/maketab: cmd/posix/awk/maketab.c cmd/posix/awk/awkgram.tab.h
477+ $(CC) $(CFLAGS) -o $@ cmd/posix/awk/maketab.c
478+
479+cmd/posix/awk/proctab.c: cmd/posix/awk/maketab
480+ cmd/posix/awk/maketab cmd/posix/awk/awkgram.tab.h > $@
481+
482+$(AWKOBJ): cmd/posix/awk/awk.h cmd/posix/awk/awkgram.tab.h cmd/posix/awk/proto.h
483+
484+cmd/posix/awk/%.o: cmd/posix/awk/%.c
485+ $(CC) $(CPPFLAGS) -Icmd/posix/awk $(CFLAGS) -o $@ -c $<
486+
487+cmd/posix/awk/awk: $(AWKOBJ) $(LIB)
488+ $(CC) $(LDFLAGS) -o $@ $(AWKOBJ) $(LIB) $(LDLIBS) -lm
489+
490+cmd/posix/sh/mknodes: cmd/posix/sh/mknodes.c
491+ $(CC) $(CFLAGS) -o $@ cmd/posix/sh/mknodes.c
492+
493+cmd/posix/sh/mksyntax: cmd/posix/sh/mksyntax.c
494+ $(CC) $(CPPFLAGS) -Icmd/posix/sh $(CFLAGS) -o $@ cmd/posix/sh/mksyntax.c
495+
496+cmd/posix/sh/syntax.c cmd/posix/sh/syntax.h: cmd/posix/sh/mksyntax
497+ cd cmd/posix/sh && ./mksyntax
498+
499+cmd/posix/sh/nodes.c cmd/posix/sh/nodes.h: cmd/posix/sh/mknodes cmd/posix/sh/nodetypes cmd/posix/sh/nodes.c.pat
500+ cd cmd/posix/sh && ./mknodes nodetypes nodes.c.pat
501+
502+cmd/posix/sh/builtins.c cmd/posix/sh/builtins.h: cmd/posix/sh/mkbuiltins cmd/posix/sh/builtins.def cmd/posix/sh/shell.h
503+ cd cmd/posix/sh && sh mkbuiltins .
504+
505+cmd/posix/sh/token.h: cmd/posix/sh/mktokens
506+ cd cmd/posix/sh && sh mktokens
507+
508+$(SHOBJ): $(SH_GENHDRS)
509+
510+cmd/posix/sh/%.o: cmd/posix/sh/%.c
511+ $(CC) $(CPPFLAGS) -DSHELL -Icmd/posix/sh $(CFLAGS) -o $@ -c $<
512+
513+cmd/posix/sh/sh: $(SHOBJ) $(LIB)
514+ $(CC) $(LDFLAGS) -o $@ $(SHOBJ) $(LIB) $(LDLIBS)
515+
516+cmd/pseudo/base64: cmd/pseudo/base64.o $(LIB)
517+ $(CC) $(LDFLAGS) -o $@ cmd/pseudo/base64.o $(LIB) $(LDLIBS)
+2,
-2
1@@ -39,9 +39,9 @@ main(int argc, char *argv[])
2 if (fd < 0)
3 eprintf("open: %s:", argv[0]);
4 range[OFFSET_IDX] = 0;
5- if (ioctl(fd, BLKGETSIZE64, &range[LENGTH_IDX]) < 0)
6+ if (ioctl(fd, (int)BLKGETSIZE64, &range[LENGTH_IDX]) < 0)
7 eprintf("BLKGETSIZE64: %s:", argv[0]);
8- if (ioctl(fd, BLKDISCARD, range) < 0)
9+ if (ioctl(fd, (int)BLKDISCARD, range) < 0)
10 eprintf("BLKDISCARD: %s:", argv[0]);
11 close(fd);
12 return 0;
+2,
-2
1@@ -24,10 +24,10 @@ main(int argc, char *argv[])
2
3 ARGBEGIN {
4 case 'l':
5- size = estrtonum(EARGF(usage()), 1, MIN(LLONG_MAX, SIZE_MAX));
6+ size = estrtonum(EARGF(usage()), 1, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
7 break;
8 case 'o':
9- offset = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
10+ offset = estrtonum(EARGF(usage()), 0, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
11 break;
12 default:
13 usage();
+2,
-2
1@@ -20,7 +20,7 @@ readrtctm(struct tm *tm, int fd)
2 struct rtc_time rt;
3
4 memset(&rt, 0, sizeof(rt));
5- if (ioctl(fd, RTC_RD_TIME, &rt) < 0)
6+ if (ioctl(fd, (int)RTC_RD_TIME, &rt) < 0)
7 eprintf("RTC_RD_TIME:");
8 tm->tm_sec = rt.tm_sec;
9 tm->tm_min = rt.tm_min;
10@@ -47,7 +47,7 @@ writertctm(struct tm *tm, int fd)
11 rt.tm_wday = tm->tm_wday;
12 rt.tm_yday = tm->tm_yday;
13 rt.tm_isdst = tm->tm_isdst;
14- if (ioctl(fd, RTC_SET_TIME, &rt) < 0)
15+ if (ioctl(fd, (int)RTC_SET_TIME, &rt) < 0)
16 eprintf("RTC_SET_TIME:");
17 }
18
+1,
-1
1@@ -72,7 +72,7 @@ main(int argc, char *argv[])
2 hdr = (struct swap_hdr *)buf;
3 hdr->version = 1;
4 hdr->last_page = pages - 1;
5- strncpy(buf + pagesize - 10, "SWAPSPACE2", 10);
6+ memcpy(buf + pagesize - 10, "SWAPSPACE2", 10);
7
8 printf("Setting up swapspace version 1, size = %luKiB\n",
9 (pages - 1) * pagesize / 1024);
+2,
-1
1@@ -264,9 +264,10 @@ main(int argc, char *argv[])
2 target = me->mnt_dir;
3 source = me->mnt_fsname;
4 }
5- if (!fsopts[0])
6+ if (!fsopts[0]) {
7 estrlcat(fsopts, me->mnt_opts, sizeof(fsopts));
8 parseopts(fsopts, &flags, data, sizeof(data));
9+ }
10 if (!types)
11 types = me->mnt_type;
12 goto mountsingle;
+1,
-1
1@@ -31,7 +31,7 @@ main(int argc, char *argv[])
2
3 for (; argc > 0; argc--, argv++) {
4 n = snprintf(path, sizeof(path), "/proc/%s/cwd", *argv);
5- if (n < 0 || n >= sizeof(path)) {
6+ if (n < 0 || (size_t)n >= sizeof(path)) {
7 errno = ESRCH;
8 } else {
9 n = readlink(path, target, sizeof(target) - 1);
+1,
-1
1@@ -99,7 +99,7 @@ unpackerrc(unsigned char *buf)
2 int errc;
3
4 errc = (buf[2] << 8) | (buf[3] & 0xff);
5- if (errc < 0 || errc >= LEN(errtext))
6+ if (errc < 0 || (size_t)errc >= LEN(errtext))
7 eprintf("bad error code: %d\n", errc);
8 return errc;
9 }
+13,
-4
1@@ -130,13 +130,18 @@ find_header(const char *headers, const char *name)
2 static int
3 stream_getc(struct Stream *s)
4 {
5+ ssize_t r;
6+
7 if (s->idx < s->len) {
8 return (unsigned char)s->buf[s->idx++];
9 }
10 s->idx = 0;
11- s->len = read(s->fd, s->buf, sizeof(s->buf));
12- if (s->len <= 0)
13+ r = read(s->fd, s->buf, sizeof(s->buf));
14+ if (r <= 0) {
15+ s->len = 0;
16 return EOF;
17+ }
18+ s->len = (size_t)r;
19 return (unsigned char)s->buf[s->idx++];
20 }
21
22@@ -146,6 +151,7 @@ stream_read(struct Stream *s, void *ptr, size_t size)
23 size_t total = 0;
24 size_t n;
25 char *p = ptr;
26+ ssize_t r;
27
28 while (total < size) {
29 if (s->idx < s->len) {
30@@ -155,9 +161,12 @@ stream_read(struct Stream *s, void *ptr, size_t size)
31 total += n;
32 } else {
33 s->idx = 0;
34- s->len = read(s->fd, s->buf, sizeof(s->buf));
35- if (s->len <= 0)
36+ r = read(s->fd, s->buf, sizeof(s->buf));
37+ if (r <= 0) {
38+ s->len = 0;
39 break;
40+ }
41+ s->len = (size_t)r;
42 }
43 }
44 return total;
+283,
-0
1@@ -0,0 +1,283 @@
2+/****************************************************************
3+Copyright (C) Lucent Technologies 1997
4+All Rights Reserved
5+
6+Permission to use, copy, modify, and distribute this software and
7+its documentation for any purpose and without fee is hereby
8+granted, provided that the above copyright notice appear in all
9+copies and that both that the copyright notice and this
10+permission notice and warranty disclaimer appear in supporting
11+documentation, and that the name Lucent Technologies or any of
12+its entities not be used in advertising or publicity pertaining
13+to distribution of the software without specific, written prior
14+permission.
15+
16+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
18+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
19+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
21+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
22+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
23+THIS SOFTWARE.
24+****************************************************************/
25+
26+#include <assert.h>
27+#include <stdint.h>
28+
29+#ifndef bool
30+#define bool int
31+#define true 1
32+#define false 0
33+#endif
34+
35+#if __STDC_VERSION__ <= 199901L
36+#define noreturn
37+#else
38+#include <stdnoreturn.h>
39+#endif
40+
41+typedef double Awkfloat;
42+
43+/* unsigned char is more trouble than it's worth */
44+
45+typedef unsigned char uschar;
46+
47+#define xfree(a) { free((void *)(intptr_t)(a)); (a) = NULL; }
48+/*
49+ * We sometimes cheat writing read-only pointers to NUL-terminate them
50+ * and then put back the original value
51+ */
52+#define setptr(ptr, a) (*(char *)(intptr_t)(ptr)) = (a)
53+
54+#define NN(p) ((p) ? (p) : "(null)") /* guaranteed non-null for DPRINTF
55+*/
56+#define DEBUG
57+#ifdef DEBUG
58+# define DPRINTF(...) if (dbg) printf(__VA_ARGS__)
59+#else
60+# define DPRINTF(...)
61+#endif
62+
63+extern enum compile_states {
64+ RUNNING,
65+ COMPILING,
66+ ERROR_PRINTING
67+} compile_time;
68+
69+extern bool safe; /* false => unsafe, true => safe */
70+
71+#define RECSIZE (8 * 1024) /* sets limit on records, fields, etc., etc. */
72+extern int recsize; /* size of current record, orig RECSIZE */
73+
74+extern size_t awk_mb_cur_max; /* max size of a multi-byte character */
75+
76+extern char EMPTY[]; /* this avoid -Wwritable-strings issues */
77+extern char **FS;
78+extern char **RS;
79+extern char **ORS;
80+extern char **OFS;
81+extern char **OFMT;
82+extern Awkfloat *NR;
83+extern Awkfloat *FNR;
84+extern Awkfloat *NF;
85+extern char **FILENAME;
86+extern char **SUBSEP;
87+extern Awkfloat *RSTART;
88+extern Awkfloat *RLENGTH;
89+
90+extern bool CSV; /* true for csv input */
91+
92+extern char *record; /* points to $0 */
93+extern int lineno; /* line number in awk program */
94+extern int errorflag; /* 1 if error has occurred */
95+extern bool donefld; /* true if record broken into fields */
96+extern bool donerec; /* true if record is valid (no fld has changed */
97+extern int dbg;
98+
99+extern const char *patbeg; /* beginning of pattern matched */
100+extern int patlen; /* length of pattern matched. set in b.c */
101+
102+/* Cell: all information about a variable or constant */
103+
104+typedef struct Cell {
105+ uschar ctype; /* OCELL, OBOOL, OJUMP, etc. */
106+ uschar csub; /* CCON, CTEMP, CFLD, etc. */
107+ char *nval; /* name, for variables only */
108+ char *sval; /* string value */
109+ Awkfloat fval; /* value as number */
110+ int tval; /* type info: STR|NUM|ARR|FCN|FLD|CON|DONTFREE|CONVC|CONVO */
111+ char *fmt; /* CONVFMT/OFMT value used to convert from number */
112+ struct Cell *cnext; /* ptr to next if chained */
113+} Cell;
114+
115+typedef struct Array { /* symbol table array */
116+ int nelem; /* elements in table right now */
117+ int size; /* size of tab */
118+ Cell **tab; /* hash table pointers */
119+} Array;
120+
121+#define NSYMTAB 50 /* initial size of a symbol table */
122+extern Array *symtab;
123+
124+extern Cell *nrloc; /* NR */
125+extern Cell *fnrloc; /* FNR */
126+extern Cell *fsloc; /* FS */
127+extern Cell *nfloc; /* NF */
128+extern Cell *ofsloc; /* OFS */
129+extern Cell *orsloc; /* ORS */
130+extern Cell *rsloc; /* RS */
131+extern Cell *rstartloc; /* RSTART */
132+extern Cell *rlengthloc; /* RLENGTH */
133+extern Cell *subseploc; /* SUBSEP */
134+extern Cell *symtabloc; /* SYMTAB */
135+
136+/* Cell.tval values: */
137+#define NUM 01 /* number value is valid */
138+#define STR 02 /* string value is valid */
139+#define DONTFREE 04 /* string space is not freeable */
140+#define CON 010 /* this is a constant */
141+#define ARR 020 /* this is an array */
142+#define FCN 040 /* this is a function name */
143+#define FLD 0100 /* this is a field $1, $2, ... */
144+#define REC 0200 /* this is $0 */
145+#define CONVC 0400 /* string was converted from number via CONVFMT */
146+#define CONVO 01000 /* string was converted from number via OFMT */
147+
148+
149+/* function types */
150+#define FLENGTH 1
151+#define FSQRT 2
152+#define FEXP 3
153+#define FLOG 4
154+#define FINT 5
155+#define FSYSTEM 6
156+#define FRAND 7
157+#define FSRAND 8
158+#define FSIN 9
159+#define FCOS 10
160+#define FATAN 11
161+#define FTOUPPER 12
162+#define FTOLOWER 13
163+#define FFLUSH 14
164+
165+/* Node: parse tree is made of nodes, with Cell's at bottom */
166+
167+typedef struct Node {
168+ int ntype;
169+ struct Node *nnext;
170+ int lineno;
171+ int nobj;
172+ struct Node *narg[1]; /* variable: actual size set by calling malloc */
173+} Node;
174+
175+#define NIL ((Node *) 0)
176+
177+extern Node *winner;
178+extern Node *nullnode;
179+
180+/* ctypes */
181+#define OCELL 1
182+#define OBOOL 2
183+#define OJUMP 3
184+
185+/* Cell subtypes: csub */
186+#define CFREE 7
187+#define CCOPY 6
188+#define CCON 5
189+#define CTEMP 4
190+#define CNAME 3
191+#define CVAR 2
192+#define CFLD 1
193+#define CUNK 0
194+
195+/* bool subtypes */
196+#define BTRUE 11
197+#define BFALSE 12
198+
199+/* jump subtypes */
200+#define JEXIT 21
201+#define JNEXT 22
202+#define JBREAK 23
203+#define JCONT 24
204+#define JRET 25
205+#define JNEXTFILE 26
206+
207+/* node types */
208+#define NVALUE 1
209+#define NSTAT 2
210+#define NEXPR 3
211+
212+
213+extern int pairstack[], paircnt;
214+
215+#define notlegal(n) (n <= FIRSTTOKEN || n >= LASTTOKEN || proctab[n-FIRSTTOKEN] == nullproc)
216+#define isvalue(n) ((n)->ntype == NVALUE)
217+#define isexpr(n) ((n)->ntype == NEXPR)
218+#define isjump(n) ((n)->ctype == OJUMP)
219+#define isexit(n) ((n)->csub == JEXIT)
220+#define isbreak(n) ((n)->csub == JBREAK)
221+#define iscont(n) ((n)->csub == JCONT)
222+#define isnext(n) ((n)->csub == JNEXT || (n)->csub == JNEXTFILE)
223+#define isret(n) ((n)->csub == JRET)
224+#define isrec(n) ((n)->tval & REC)
225+#define isfld(n) ((n)->tval & FLD)
226+#define isstr(n) ((n)->tval & STR)
227+#define isnum(n) ((n)->tval & NUM)
228+#define isarr(n) ((n)->tval & ARR)
229+#define isfcn(n) ((n)->tval & FCN)
230+#define istrue(n) ((n)->csub == BTRUE)
231+#define istemp(n) ((n)->csub == CTEMP)
232+#define isargument(n) ((n)->nobj == ARG)
233+/* #define freeable(p) (!((p)->tval & DONTFREE)) */
234+#define freeable(p) ( ((p)->tval & (STR|DONTFREE)) == STR )
235+
236+/* structures used by regular expression matching machinery, mostly b.c: */
237+
238+#define NCHARS (1256+3) /* 256 handles 8-bit chars; 128 does 7-bit */
239+ /* BUG: some overflows (caught) if we use 256 */
240+ /* watch out in match(), etc. */
241+#define HAT (NCHARS+2) /* matches ^ in regular expr */
242+#define NSTATES 32
243+
244+typedef struct rrow {
245+ long ltype; /* long avoids pointer warnings on 64-bit */
246+ union {
247+ int i;
248+ Node *np;
249+ uschar *up;
250+ int *rp; /* rune representation of char class */
251+ } lval; /* because Al stores a pointer in it! */
252+ int *lfollow;
253+} rrow;
254+
255+typedef struct gtte { /* gototab entry */
256+ unsigned int ch;
257+ unsigned int state;
258+} gtte;
259+
260+#define GOTO_DIRECT 128 /* size of ASCII direct-index fast path */
261+
262+typedef struct gtt { /* gototab */
263+ size_t allocated;
264+ size_t inuse;
265+ gtte *entries;
266+ int direct[GOTO_DIRECT]; /* fast path for ASCII; 0 == miss */
267+} gtt;
268+
269+typedef struct fa {
270+ gtt *gototab;
271+ uschar *out;
272+ uschar *restr;
273+ int **posns;
274+ int state_count;
275+ bool anchor;
276+ int use;
277+ int initstat;
278+ int curstat;
279+ int accept;
280+ struct rrow re[1]; /* variable: actual size set by calling malloc */
281+} fa;
282+
283+
284+#include "proto.h"
+497,
-0
1@@ -0,0 +1,497 @@
2+/****************************************************************
3+Copyright (C) Lucent Technologies 1997
4+All Rights Reserved
5+
6+Permission to use, copy, modify, and distribute this software and
7+its documentation for any purpose and without fee is hereby
8+granted, provided that the above copyright notice appear in all
9+copies and that both that the copyright notice and this
10+permission notice and warranty disclaimer appear in supporting
11+documentation, and that the name Lucent Technologies or any of
12+its entities not be used in advertising or publicity pertaining
13+to distribution of the software without specific, written prior
14+permission.
15+
16+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
18+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
19+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
21+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
22+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
23+THIS SOFTWARE.
24+****************************************************************/
25+
26+%{
27+#include <stdio.h>
28+#include <string.h>
29+#include "awk.h"
30+
31+void checkdup(Node *list, Cell *item);
32+int yywrap(void) { return(1); }
33+
34+Node *beginloc = 0;
35+Node *endloc = 0;
36+bool infunc = false; /* = true if in arglist or body of func */
37+int inloop = 0; /* >= 1 if in while, for, do; can't be bool, since loops can next */
38+char *curfname = 0; /* current function name */
39+Node *arglist = 0; /* list of args for current function */
40+%}
41+
42+%union {
43+ Node *p;
44+ Cell *cp;
45+ int i;
46+ char *s;
47+}
48+
49+%token <i> FIRSTTOKEN /* must be first */
50+%token <p> PROGRAM PASTAT PASTAT2 XBEGIN XEND
51+%token <i> NL ',' '{' '(' '|' ';' '/' ')' '}' '[' ']'
52+%token <i> ARRAY
53+%token <i> MATCH NOTMATCH MATCHOP
54+%token <i> FINAL DOT ALL CCL NCCL CHAR OR STAR QUEST PLUS EMPTYRE ZERO
55+%token <i> AND BOR APPEND EQ GE GT LE LT NE IN
56+%token <i> ARG BLTIN BREAK CLOSE CONTINUE DELETE DO EXIT FOR FUNC
57+%token <i> SUB GSUB IF INDEX LSUBSTR MATCHFCN NEXT NEXTFILE
58+%token <i> ADD MINUS MULT DIVIDE MOD
59+%token <i> ASSIGN ASGNOP ADDEQ SUBEQ MULTEQ DIVEQ MODEQ POWEQ
60+%token <i> PRINT PRINTF SPRINTF
61+%token <p> ELSE INTEST CONDEXPR
62+%token <i> POSTINCR PREINCR POSTDECR PREDECR
63+%token <cp> VAR IVAR VARNF CALL NUMBER STRING
64+%token <s> REGEXPR
65+
66+%type <p> pas pattern ppattern plist pplist patlist prarg term re
67+%type <p> pa_pat pa_stat pa_stats
68+%type <s> reg_expr
69+%type <p> simple_stmt opt_simple_stmt stmt stmtlist
70+%type <p> var varname funcname varlist
71+%type <p> for if else while
72+%type <i> do st
73+%type <i> pst opt_pst lbrace rbrace rparen comma nl opt_nl and bor
74+%type <i> subop print
75+%type <cp> string
76+
77+%right ASGNOP
78+%right '?'
79+%right ':'
80+%left BOR
81+%left AND
82+%left GETLINE
83+%nonassoc APPEND EQ GE GT LE LT NE MATCHOP IN '|'
84+%left ARG BLTIN BREAK CALL CLOSE CONTINUE DELETE DO EXIT FOR FUNC
85+%left GSUB IF INDEX LSUBSTR MATCHFCN NEXT NUMBER
86+%left PRINT PRINTF RETURN SPLIT SPRINTF STRING SUB SUBSTR
87+%left REGEXPR VAR VARNF IVAR WHILE '('
88+%left CAT
89+%left '+' '-'
90+%left '*' '/' '%'
91+%left NOT UMINUS UPLUS
92+%right POWER
93+%right DECR INCR
94+%left INDIRECT
95+%token LASTTOKEN /* must be last */
96+
97+%%
98+
99+program:
100+ pas { if (errorflag==0)
101+ winner = (Node *)stat3(PROGRAM, beginloc, $1, endloc); }
102+ | error { yyclearin; bracecheck(); SYNTAX("bailing out"); }
103+ ;
104+
105+and:
106+ AND | and NL
107+ ;
108+
109+bor:
110+ BOR | bor NL
111+ ;
112+
113+comma:
114+ ',' | comma NL
115+ ;
116+
117+do:
118+ DO | do NL
119+ ;
120+
121+else:
122+ ELSE | else NL
123+ ;
124+
125+for:
126+ FOR '(' opt_simple_stmt ';' opt_nl pattern ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt
127+ { --inloop; $$ = stat4(FOR, $3, notnull($6), $9, $12); }
128+ | FOR '(' opt_simple_stmt ';' ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt
129+ { --inloop; $$ = stat4(FOR, $3, NIL, $7, $10); }
130+ | FOR '(' varname IN varname rparen {inloop++;} stmt
131+ { --inloop; $$ = stat3(IN, $3, makearr($5), $8); }
132+ ;
133+
134+funcname:
135+ VAR { setfname($1); }
136+ | CALL { setfname($1); }
137+ ;
138+
139+if:
140+ IF '(' pattern rparen { $$ = notnull($3); }
141+ ;
142+
143+lbrace:
144+ '{' | lbrace NL
145+ ;
146+
147+nl:
148+ NL | nl NL
149+ ;
150+
151+opt_nl:
152+ /* empty */ { $$ = 0; }
153+ | nl
154+ ;
155+
156+opt_pst:
157+ /* empty */ { $$ = 0; }
158+ | pst
159+ ;
160+
161+
162+opt_simple_stmt:
163+ /* empty */ { $$ = 0; }
164+ | simple_stmt
165+ ;
166+
167+pas:
168+ opt_pst { $$ = 0; }
169+ | opt_pst pa_stats opt_pst { $$ = $2; }
170+ ;
171+
172+pa_pat:
173+ pattern { $$ = notnull($1); }
174+ ;
175+
176+pa_stat:
177+ pa_pat { $$ = stat2(PASTAT, $1, stat2(PRINT, rectonode(), NIL)); }
178+ | pa_pat lbrace stmtlist '}' { $$ = stat2(PASTAT, $1, $3); }
179+ | pa_pat ',' opt_nl pa_pat { $$ = pa2stat($1, $4, stat2(PRINT, rectonode(), NIL)); }
180+ | pa_pat ',' opt_nl pa_pat lbrace stmtlist '}' { $$ = pa2stat($1, $4, $6); }
181+ | lbrace stmtlist '}' { $$ = stat2(PASTAT, NIL, $2); }
182+ | XBEGIN lbrace stmtlist '}'
183+ { beginloc = linkum(beginloc, $3); $$ = 0; }
184+ | XEND lbrace stmtlist '}'
185+ { endloc = linkum(endloc, $3); $$ = 0; }
186+ | FUNC funcname '(' varlist rparen {infunc = true;} lbrace stmtlist '}'
187+ { infunc = false; curfname=0; defn((Cell *)$2, $4, $8); $$ = 0; }
188+ ;
189+
190+pa_stats:
191+ pa_stat
192+ | pa_stats opt_pst pa_stat { $$ = linkum($1, $3); }
193+ ;
194+
195+patlist:
196+ pattern
197+ | patlist comma pattern { $$ = linkum($1, $3); }
198+ ;
199+
200+ppattern:
201+ var ASGNOP ppattern { $$ = op2($2, $1, $3); }
202+ | ppattern '?' ppattern ':' ppattern %prec '?'
203+ { $$ = op3(CONDEXPR, notnull($1), $3, $5); }
204+ | ppattern bor ppattern %prec BOR
205+ { $$ = op2(BOR, notnull($1), notnull($3)); }
206+ | ppattern and ppattern %prec AND
207+ { $$ = op2(AND, notnull($1), notnull($3)); }
208+ | ppattern MATCHOP reg_expr { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); free($3); }
209+ | ppattern MATCHOP ppattern
210+ { if (constnode($3)) {
211+ $$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0));
212+ free($3);
213+ } else
214+ $$ = op3($2, (Node *)1, $1, $3); }
215+ | ppattern IN varname { $$ = op2(INTEST, $1, makearr($3)); }
216+ | '(' plist ')' IN varname { $$ = op2(INTEST, $2, makearr($5)); }
217+ | ppattern term %prec CAT { $$ = op2(CAT, $1, $2); }
218+ | re
219+ | term
220+ ;
221+
222+pattern:
223+ var ASGNOP pattern { $$ = op2($2, $1, $3); }
224+ | pattern '?' pattern ':' pattern %prec '?'
225+ { $$ = op3(CONDEXPR, notnull($1), $3, $5); }
226+ | pattern bor pattern %prec BOR
227+ { $$ = op2(BOR, notnull($1), notnull($3)); }
228+ | pattern and pattern %prec AND
229+ { $$ = op2(AND, notnull($1), notnull($3)); }
230+ | pattern EQ pattern { $$ = op2($2, $1, $3); }
231+ | pattern GE pattern { $$ = op2($2, $1, $3); }
232+ | pattern GT pattern { $$ = op2($2, $1, $3); }
233+ | pattern LE pattern { $$ = op2($2, $1, $3); }
234+ | pattern LT pattern { $$ = op2($2, $1, $3); }
235+ | pattern NE pattern { $$ = op2($2, $1, $3); }
236+ | pattern MATCHOP reg_expr { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); free($3); }
237+ | pattern MATCHOP pattern
238+ { if (constnode($3)) {
239+ $$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0));
240+ free($3);
241+ } else
242+ $$ = op3($2, (Node *)1, $1, $3); }
243+ | pattern IN varname { $$ = op2(INTEST, $1, makearr($3)); }
244+ | '(' plist ')' IN varname { $$ = op2(INTEST, $2, makearr($5)); }
245+ | pattern '|' GETLINE var {
246+ if (safe) SYNTAX("cmd | getline is unsafe");
247+ else $$ = op3(GETLINE, $4, itonp($2), $1); }
248+ | pattern '|' GETLINE {
249+ if (safe) SYNTAX("cmd | getline is unsafe");
250+ else $$ = op3(GETLINE, (Node*)0, itonp($2), $1); }
251+ | pattern term %prec CAT { $$ = op2(CAT, $1, $2); }
252+ | re
253+ | term
254+ ;
255+
256+plist:
257+ pattern comma pattern { $$ = linkum($1, $3); }
258+ | plist comma pattern { $$ = linkum($1, $3); }
259+ ;
260+
261+pplist:
262+ ppattern
263+ | pplist comma ppattern { $$ = linkum($1, $3); }
264+ ;
265+
266+prarg:
267+ /* empty */ { $$ = rectonode(); }
268+ | pplist
269+ | '(' plist ')' { $$ = $2; }
270+ ;
271+
272+print:
273+ PRINT | PRINTF
274+ ;
275+
276+pst:
277+ NL | ';' | pst NL | pst ';'
278+ ;
279+
280+rbrace:
281+ '}' | rbrace NL
282+ ;
283+
284+re:
285+ reg_expr
286+ { $$ = op3(MATCH, NIL, rectonode(), (Node*)makedfa($1, 0)); free($1); }
287+ | NOT re { $$ = op1(NOT, notnull($2)); }
288+ ;
289+
290+reg_expr:
291+ '/' {startreg();} REGEXPR '/' { $$ = $3; }
292+ ;
293+
294+rparen:
295+ ')' | rparen NL
296+ ;
297+
298+simple_stmt:
299+ print prarg '|' term {
300+ if (safe) SYNTAX("print | is unsafe");
301+ else $$ = stat3($1, $2, itonp($3), $4); }
302+ | print prarg APPEND term {
303+ if (safe) SYNTAX("print >> is unsafe");
304+ else $$ = stat3($1, $2, itonp($3), $4); }
305+ | print prarg GT term {
306+ if (safe) SYNTAX("print > is unsafe");
307+ else $$ = stat3($1, $2, itonp($3), $4); }
308+ | print prarg { $$ = stat3($1, $2, NIL, NIL); }
309+ | DELETE varname '[' patlist ']' { $$ = stat2(DELETE, makearr($2), $4); }
310+ | DELETE varname { $$ = stat2(DELETE, makearr($2), 0); }
311+ | pattern { $$ = exptostat($1); }
312+ | error { yyclearin; SYNTAX("illegal statement"); }
313+ ;
314+
315+st:
316+ nl
317+ | ';' opt_nl
318+ ;
319+
320+stmt:
321+ BREAK st { if (!inloop) SYNTAX("break illegal outside of loops");
322+ $$ = stat1(BREAK, NIL); }
323+ | CONTINUE st { if (!inloop) SYNTAX("continue illegal outside of loops");
324+ $$ = stat1(CONTINUE, NIL); }
325+ | do {inloop++;} stmt {--inloop;} WHILE '(' pattern ')' st
326+ { $$ = stat2(DO, $3, notnull($7)); }
327+ | EXIT pattern st { $$ = stat1(EXIT, $2); }
328+ | EXIT st { $$ = stat1(EXIT, NIL); }
329+ | for
330+ | if stmt else stmt { $$ = stat3(IF, $1, $2, $4); }
331+ | if stmt { $$ = stat3(IF, $1, $2, NIL); }
332+ | lbrace stmtlist rbrace { $$ = $2; }
333+ | NEXT st { if (infunc)
334+ SYNTAX("next is illegal inside a function");
335+ $$ = stat1(NEXT, NIL); }
336+ | NEXTFILE st { if (infunc)
337+ SYNTAX("nextfile is illegal inside a function");
338+ $$ = stat1(NEXTFILE, NIL); }
339+ | RETURN pattern st { $$ = stat1(RETURN, $2); }
340+ | RETURN st { $$ = stat1(RETURN, NIL); }
341+ | simple_stmt st
342+ | while {inloop++;} stmt { --inloop; $$ = stat2(WHILE, $1, $3); }
343+ | ';' opt_nl { $$ = 0; }
344+ ;
345+
346+stmtlist:
347+ stmt
348+ | stmtlist stmt { $$ = linkum($1, $2); }
349+ ;
350+
351+subop:
352+ SUB | GSUB
353+ ;
354+
355+string:
356+ STRING
357+ | string STRING { $$ = catstr($1, $2); }
358+ ;
359+
360+term:
361+ term '/' ASGNOP term { $$ = op2(DIVEQ, $1, $4); }
362+ | term '+' term { $$ = op2(ADD, $1, $3); }
363+ | term '-' term { $$ = op2(MINUS, $1, $3); }
364+ | term '*' term { $$ = op2(MULT, $1, $3); }
365+ | term '/' term { $$ = op2(DIVIDE, $1, $3); }
366+ | term '%' term { $$ = op2(MOD, $1, $3); }
367+ | term POWER term { $$ = op2(POWER, $1, $3); }
368+ | '-' term %prec UMINUS { $$ = op1(UMINUS, $2); }
369+ | '+' term %prec UMINUS { $$ = op1(UPLUS, $2); }
370+ | NOT term %prec UMINUS { $$ = op1(NOT, notnull($2)); }
371+ | BLTIN '(' ')' { $$ = op2(BLTIN, itonp($1), rectonode()); }
372+ | BLTIN '(' patlist ')' { $$ = op2(BLTIN, itonp($1), $3); }
373+ | BLTIN { $$ = op2(BLTIN, itonp($1), rectonode()); }
374+ | CALL '(' ')' { $$ = op2(CALL, celltonode($1,CVAR), NIL); }
375+ | CALL '(' patlist ')' { $$ = op2(CALL, celltonode($1,CVAR), $3); }
376+ | CLOSE term { $$ = op1(CLOSE, $2); }
377+ | DECR var { $$ = op1(PREDECR, $2); }
378+ | INCR var { $$ = op1(PREINCR, $2); }
379+ | var DECR { $$ = op1(POSTDECR, $1); }
380+ | var INCR { $$ = op1(POSTINCR, $1); }
381+ | GETLINE var LT term { $$ = op3(GETLINE, $2, itonp($3), $4); }
382+ | GETLINE LT term { $$ = op3(GETLINE, NIL, itonp($2), $3); }
383+ | GETLINE var { $$ = op3(GETLINE, $2, NIL, NIL); }
384+ | GETLINE { $$ = op3(GETLINE, NIL, NIL, NIL); }
385+ | INDEX '(' pattern comma pattern ')'
386+ { $$ = op2(INDEX, $3, $5); }
387+ | INDEX '(' pattern comma reg_expr ')'
388+ { SYNTAX("index() doesn't permit regular expressions");
389+ $$ = op2(INDEX, $3, (Node*)$5); }
390+ | '(' pattern ')' { $$ = $2; }
391+ | MATCHFCN '(' pattern comma reg_expr ')'
392+ { $$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa($5, 1)); free($5); }
393+ | MATCHFCN '(' pattern comma pattern ')'
394+ { if (constnode($5)) {
395+ $$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa(strnode($5), 1));
396+ free($5);
397+ } else
398+ $$ = op3(MATCHFCN, (Node *)1, $3, $5); }
399+ | NUMBER { $$ = celltonode($1, CCON); }
400+ | SPLIT '(' pattern comma varname comma pattern ')' /* string */
401+ { $$ = op4(SPLIT, $3, makearr($5), $7, (Node*)STRING); }
402+ | SPLIT '(' pattern comma varname comma reg_expr ')' /* const /regexp/ */
403+ { $$ = op4(SPLIT, $3, makearr($5), (Node*)makedfa($7, 1), (Node *)REGEXPR); free($7); }
404+ | SPLIT '(' pattern comma varname ')'
405+ { $$ = op4(SPLIT, $3, makearr($5), NIL, (Node*)STRING); } /* default */
406+ | SPRINTF '(' patlist ')' { $$ = op1($1, $3); }
407+ | string { $$ = celltonode($1, CCON); }
408+ | subop '(' reg_expr comma pattern ')'
409+ { $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, rectonode()); free($3); }
410+ | subop '(' pattern comma pattern ')'
411+ { if (constnode($3)) {
412+ $$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, rectonode());
413+ free($3);
414+ } else
415+ $$ = op4($1, (Node *)1, $3, $5, rectonode()); }
416+ | subop '(' reg_expr comma pattern comma var ')'
417+ { $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, $7); free($3); }
418+ | subop '(' pattern comma pattern comma var ')'
419+ { if (constnode($3)) {
420+ $$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, $7);
421+ free($3);
422+ } else
423+ $$ = op4($1, (Node *)1, $3, $5, $7); }
424+ | SUBSTR '(' pattern comma pattern comma pattern ')'
425+ { $$ = op3(SUBSTR, $3, $5, $7); }
426+ | SUBSTR '(' pattern comma pattern ')'
427+ { $$ = op3(SUBSTR, $3, $5, NIL); }
428+ | var
429+ ;
430+
431+var:
432+ varname
433+ | varname '[' patlist ']' { $$ = op2(ARRAY, makearr($1), $3); }
434+ | IVAR { $$ = op1(INDIRECT, celltonode($1, CVAR)); }
435+ | INDIRECT term { $$ = op1(INDIRECT, $2); }
436+ ;
437+
438+varlist:
439+ /* nothing */ { arglist = $$ = 0; }
440+ | VAR { arglist = $$ = celltonode($1,CVAR); }
441+ | varlist comma VAR {
442+ checkdup($1, $3);
443+ arglist = $$ = linkum($1,celltonode($3,CVAR)); }
444+ ;
445+
446+varname:
447+ VAR { $$ = celltonode($1, CVAR); }
448+ | ARG { $$ = op1(ARG, itonp($1)); }
449+ | VARNF { $$ = op1(VARNF, (Node *) $1); }
450+ ;
451+
452+
453+while:
454+ WHILE '(' pattern rparen { $$ = notnull($3); }
455+ ;
456+
457+%%
458+
459+void setfname(Cell *p)
460+{
461+ if (isarr(p))
462+ SYNTAX("%s is an array, not a function", p->nval);
463+ else if (isfcn(p))
464+ SYNTAX("you can't define function %s more than once", p->nval);
465+ curfname = p->nval;
466+}
467+
468+int constnode(Node *p)
469+{
470+ return isvalue(p) && ((Cell *) (p->narg[0]))->csub == CCON;
471+}
472+
473+char *strnode(Node *p)
474+{
475+ return ((Cell *)(p->narg[0]))->sval;
476+}
477+
478+Node *notnull(Node *n)
479+{
480+ switch (n->nobj) {
481+ case LE: case LT: case EQ: case NE: case GT: case GE:
482+ case BOR: case AND: case NOT:
483+ return n;
484+ default:
485+ return op2(NE, n, nullnode);
486+ }
487+}
488+
489+void checkdup(Node *vl, Cell *cp) /* check if name already in list */
490+{
491+ char *s = cp->nval;
492+ for ( ; vl; vl = vl->nnext) {
493+ if (strcmp(s, ((Cell *)(vl->narg[0]))->nval) == 0) {
494+ SYNTAX("duplicate argument %s", s);
495+ break;
496+ }
497+ }
498+}
+1599,
-0
1@@ -0,0 +1,1599 @@
2+/****************************************************************
3+Copyright (C) Lucent Technologies 1997
4+All Rights Reserved
5+
6+Permission to use, copy, modify, and distribute this software and
7+its documentation for any purpose and without fee is hereby
8+granted, provided that the above copyright notice appear in all
9+copies and that both that the copyright notice and this
10+permission notice and warranty disclaimer appear in supporting
11+documentation, and that the name Lucent Technologies or any of
12+its entities not be used in advertising or publicity pertaining
13+to distribution of the software without specific, written prior
14+permission.
15+
16+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
18+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
19+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
21+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
22+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
23+THIS SOFTWARE.
24+****************************************************************/
25+
26+/* lasciate ogne speranza, voi ch'intrate. */
27+
28+#define DEBUG
29+
30+#include <ctype.h>
31+#include <limits.h>
32+#include <stdio.h>
33+#include <string.h>
34+#include <stdlib.h>
35+#include "awk.h"
36+#include "awkgram.tab.h"
37+
38+#define MAXLIN 22
39+
40+#define type(v) (v)->nobj /* badly overloaded here */
41+#define info(v) (v)->ntype /* badly overloaded here */
42+#define left(v) (v)->narg[0]
43+#define right(v) (v)->narg[1]
44+#define parent(v) (v)->nnext
45+
46+#define LEAF case CCL: case NCCL: case CHAR: case DOT: case FINAL: case ALL:
47+#define ELEAF case EMPTYRE: /* empty string in regexp */
48+#define UNARY case STAR: case PLUS: case QUEST:
49+
50+/* encoding in tree Nodes:
51+ leaf (CCL, NCCL, CHAR, DOT, FINAL, ALL, EMPTYRE):
52+ left is index, right contains value or pointer to value
53+ unary (STAR, PLUS, QUEST): left is child, right is null
54+ binary (CAT, OR): left and right are children
55+ parent contains pointer to parent
56+*/
57+
58+
59+int *setvec;
60+int *tmpset;
61+int maxsetvec = 0;
62+
63+int rtok; /* next token in current re */
64+int rlxval;
65+static const uschar *rlxstr;
66+static const uschar *prestr; /* current position in current re */
67+static const uschar *lastre; /* origin of last re */
68+static const uschar *lastatom; /* origin of last Atom */
69+static const uschar *starttok;
70+static const uschar *basestr; /* starts with original, replaced during
71+ repetition processing */
72+static const uschar *firstbasestr;
73+
74+static int setcnt;
75+static int poscnt;
76+
77+const char *patbeg;
78+int patlen;
79+
80+#define NFA 128 /* cache this many dynamic fa's */
81+fa *fatab[NFA];
82+int nfatab = 0; /* entries in fatab */
83+
84+extern int u8_nextlen(const char *s);
85+
86+
87+/* utf-8 mechanism:
88+
89+ For most of Awk, utf-8 strings just "work", since they look like
90+ null-terminated sequences of 8-bit bytes.
91+
92+ Functions like length(), index(), and substr() have to operate
93+ in units of utf-8 characters. The u8_* functions in run.c
94+ handle this.
95+
96+ Regular expressions are more complicated, since the basic
97+ mechanism of the goto table used 8-bit byte indices into the
98+ gototab entries to compute the next state. Unicode is a lot
99+ bigger, so the gototab entries are now structs with a character
100+ and a next state. These are sorted by code point and binary
101+ searched.
102+
103+ Throughout the RE mechanism in b.c, utf-8 characters are
104+ converted to their utf-32 value. This mostly shows up in
105+ cclenter, which expands character class ranges like a-z and now
106+ alpha-omega. The size of a gototab array is still about 256.
107+ This should be dynamic, but for now things work ok for a single
108+ code page of Unicode, which is the most likely case.
109+
110+ The code changes are localized in run.c and b.c. I have added a
111+ handful of functions to somewhat better hide the implementation,
112+ but a lot more could be done.
113+
114+ */
115+
116+static int entry_cmp(const void *l, const void *r);
117+static int get_gototab(fa*, int, int);
118+static int set_gototab(fa*, int, int, int);
119+static void clear_gototab(fa*, int);
120+extern int u8_rune(int *, const char *);
121+
122+static int *
123+intalloc(size_t n, const char *f)
124+{
125+ int *p = (int *) calloc(n, sizeof(int));
126+ if (p == NULL)
127+ overflo(f);
128+ return p;
129+}
130+
131+static void
132+resizesetvec(const char *f)
133+{
134+ if (maxsetvec == 0)
135+ maxsetvec = MAXLIN;
136+ else
137+ maxsetvec *= 4;
138+ setvec = (int *) realloc(setvec, maxsetvec * sizeof(*setvec));
139+ tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(*tmpset));
140+ if (setvec == NULL || tmpset == NULL)
141+ overflo(f);
142+}
143+
144+static void
145+resize_state(fa *f, int state)
146+{
147+ gtt *p;
148+ uschar *p2;
149+ int **p3;
150+ int i, new_count;
151+
152+ if (++state < f->state_count)
153+ return;
154+
155+ new_count = state + 10; /* needs to be tuned */
156+
157+ p = (gtt *) realloc(f->gototab, new_count * sizeof(gtt));
158+ if (p == NULL)
159+ goto out;
160+ f->gototab = p;
161+
162+ p2 = (uschar *) realloc(f->out, new_count * sizeof(f->out[0]));
163+ if (p2 == NULL)
164+ goto out;
165+ f->out = p2;
166+
167+ p3 = (int **) realloc(f->posns, new_count * sizeof(f->posns[0]));
168+ if (p3 == NULL)
169+ goto out;
170+ f->posns = p3;
171+
172+ for (i = f->state_count; i < new_count; ++i) {
173+ memset(f->gototab[i].direct, 0, sizeof(f->gototab[i].direct));
174+ f->gototab[i].entries = (gtte *) calloc(NCHARS, sizeof(gtte));
175+ if (f->gototab[i].entries == NULL)
176+ goto out;
177+ f->gototab[i].allocated = NCHARS;
178+ f->gototab[i].inuse = 0;
179+ f->out[i] = 0;
180+ f->posns[i] = NULL;
181+ }
182+ f->state_count = new_count;
183+ return;
184+out:
185+ overflo(__func__);
186+}
187+
188+fa *makedfa(const char *s, bool anchor) /* returns dfa for reg expr s */
189+{
190+ int i, use, nuse;
191+ fa *pfa;
192+ static int now = 1;
193+
194+ if (setvec == NULL) { /* first time through any RE */
195+ resizesetvec(__func__);
196+ }
197+
198+ if (compile_time != RUNNING) /* a constant for sure */
199+ return mkdfa(s, anchor);
200+ for (i = 0; i < nfatab; i++) /* is it there already? */
201+ if (fatab[i]->anchor == anchor
202+ && strcmp((const char *) fatab[i]->restr, s) == 0) {
203+ fatab[i]->use = now++;
204+ return fatab[i];
205+ }
206+ pfa = mkdfa(s, anchor);
207+ if (nfatab < NFA) { /* room for another */
208+ fatab[nfatab] = pfa;
209+ fatab[nfatab]->use = now++;
210+ nfatab++;
211+ return pfa;
212+ }
213+ use = fatab[0]->use; /* replace least-recently used */
214+ nuse = 0;
215+ for (i = 1; i < nfatab; i++)
216+ if (fatab[i]->use < use) {
217+ use = fatab[i]->use;
218+ nuse = i;
219+ }
220+ freefa(fatab[nuse]);
221+ fatab[nuse] = pfa;
222+ pfa->use = now++;
223+ return pfa;
224+}
225+
226+fa *mkdfa(const char *s, bool anchor) /* does the real work of making a dfa */
227+ /* anchor = true for anchored matches, else false */
228+{
229+ Node *p, *p1;
230+ fa *f;
231+
232+ firstbasestr = (const uschar *) s;
233+ basestr = firstbasestr;
234+ p = reparse(s);
235+ p1 = op2(CAT, op2(STAR, op2(ALL, NIL, NIL), NIL), p);
236+ /* put ALL STAR in front of reg. exp. */
237+ p1 = op2(CAT, p1, op2(FINAL, NIL, NIL));
238+ /* put FINAL after reg. exp. */
239+
240+ poscnt = 0;
241+ penter(p1); /* enter parent pointers and leaf indices */
242+ if ((f = (fa *) calloc(1, sizeof(fa) + poscnt * sizeof(rrow))) == NULL)
243+ overflo(__func__);
244+ f->accept = poscnt-1; /* penter has computed number of positions in re */
245+ cfoll(f, p1); /* set up follow sets */
246+ freetr(p1);
247+ resize_state(f, 1);
248+ f->posns[0] = intalloc(*(f->re[0].lfollow), __func__);
249+ f->posns[1] = intalloc(1, __func__);
250+ *f->posns[1] = 0;
251+ f->initstat = makeinit(f, anchor);
252+ f->anchor = anchor;
253+ f->restr = (uschar *) tostring(s);
254+ if (firstbasestr != basestr) {
255+ if (basestr)
256+ xfree(basestr);
257+ }
258+ return f;
259+}
260+
261+int makeinit(fa *f, bool anchor)
262+{
263+ int i, k;
264+
265+ f->curstat = 2;
266+ f->out[2] = 0;
267+ k = *(f->re[0].lfollow);
268+ xfree(f->posns[2]);
269+ f->posns[2] = intalloc(k + 1, __func__);
270+ for (i = 0; i <= k; i++) {
271+ (f->posns[2])[i] = (f->re[0].lfollow)[i];
272+ }
273+ if ((f->posns[2])[1] == f->accept)
274+ f->out[2] = 1;
275+ clear_gototab(f, 2);
276+ f->curstat = cgoto(f, 2, HAT);
277+ if (anchor) {
278+ *f->posns[2] = k-1; /* leave out position 0 */
279+ for (i = 0; i < k; i++) {
280+ (f->posns[0])[i] = (f->posns[2])[i];
281+ }
282+
283+ f->out[0] = f->out[2];
284+ if (f->curstat != 2)
285+ --(*f->posns[f->curstat]);
286+ }
287+ return f->curstat;
288+}
289+
290+void penter(Node *p) /* set up parent pointers and leaf indices */
291+{
292+ switch (type(p)) {
293+ ELEAF
294+ LEAF
295+ info(p) = poscnt;
296+ poscnt++;
297+ break;
298+ UNARY
299+ penter(left(p));
300+ parent(left(p)) = p;
301+ break;
302+ case CAT:
303+ case OR:
304+ penter(left(p));
305+ penter(right(p));
306+ parent(left(p)) = p;
307+ parent(right(p)) = p;
308+ break;
309+ case ZERO:
310+ break;
311+ default: /* can't happen */
312+ FATAL("can't happen: unknown type %d in penter", type(p));
313+ break;
314+ }
315+}
316+
317+void freetr(Node *p) /* free parse tree */
318+{
319+ switch (type(p)) {
320+ ELEAF
321+ LEAF
322+ xfree(p);
323+ break;
324+ UNARY
325+ case ZERO:
326+ freetr(left(p));
327+ xfree(p);
328+ break;
329+ case CAT:
330+ case OR:
331+ freetr(left(p));
332+ freetr(right(p));
333+ xfree(p);
334+ break;
335+ default: /* can't happen */
336+ FATAL("can't happen: unknown type %d in freetr", type(p));
337+ break;
338+ }
339+}
340+
341+/* in the parsing of regular expressions, metacharacters like . have */
342+/* to be seen literally; \056 is not a metacharacter. */
343+
344+int hexstr(const uschar **pp, int max) /* find and eval hex string at pp, return new p */
345+{ /* only pick up one 8-bit byte (2 chars) */
346+ const uschar *p;
347+ int n = 0;
348+ int i;
349+
350+ for (i = 0, p = *pp; i < max && isxdigit(*p); i++, p++) {
351+ if (isdigit((int) *p))
352+ n = 16 * n + *p - '0';
353+ else if (*p >= 'a' && *p <= 'f')
354+ n = 16 * n + *p - 'a' + 10;
355+ else if (*p >= 'A' && *p <= 'F')
356+ n = 16 * n + *p - 'A' + 10;
357+ }
358+ *pp = p;
359+ return n;
360+}
361+
362+
363+
364+#define isoctdigit(c) ((c) >= '0' && (c) <= '7') /* multiple use of arg */
365+
366+int quoted(const uschar **pp) /* pick up next thing after a \\ */
367+ /* and increment *pp */
368+{
369+ const uschar *p = *pp;
370+ int c;
371+
372+/* BUG: should advance by utf-8 char even if makes no sense */
373+
374+ switch ((c = *p++)) {
375+ case 't':
376+ c = '\t';
377+ break;
378+ case 'n':
379+ c = '\n';
380+ break;
381+ case 'f':
382+ c = '\f';
383+ break;
384+ case 'r':
385+ c = '\r';
386+ break;
387+ case 'b':
388+ c = '\b';
389+ break;
390+ case 'v':
391+ c = '\v';
392+ break;
393+ case 'a':
394+ c = '\a';
395+ break;
396+ case '\\':
397+ c = '\\';
398+ break;
399+ case 'x': /* 2 hex digits follow */
400+ c = hexstr(&p, 2); /* this adds a null if number is invalid */
401+ break;
402+ case 'u': /* unicode char number up to 8 hex digits */
403+ c = hexstr(&p, 8);
404+ break;
405+ default:
406+ if (isoctdigit(c)) { /* \d \dd \ddd */
407+ int n = c - '0';
408+ if (isoctdigit(*p)) {
409+ n = 8 * n + *p++ - '0';
410+ if (isoctdigit(*p))
411+ n = 8 * n + *p++ - '0';
412+ }
413+ c = n;
414+ }
415+ }
416+
417+ *pp = p;
418+ return c;
419+}
420+
421+int *cclenter(const char *argp) /* add a character class */
422+{
423+ int i, c, c2;
424+ int n;
425+ const uschar *p = (const uschar *) argp;
426+ int *bp, *retp;
427+ static int *buf = NULL;
428+ static int bufsz = 100;
429+
430+ if (buf == NULL && (buf = (int *) calloc(bufsz, sizeof(int))) == NULL)
431+ FATAL("out of space for character class [%.10s...] 1", p);
432+ bp = buf;
433+ for (i = 0; *p != 0; ) {
434+ n = u8_rune(&c, (const char *) p);
435+ p += n;
436+ if (c == '\\') {
437+ c = quoted(&p);
438+ } else if (c == '-' && i > 0 && bp[-1] != 0) {
439+ if (*p != 0) {
440+ c = bp[-1];
441+ /* c2 = *p++; */
442+ n = u8_rune(&c2, (const char *) p);
443+ p += n;
444+ if (c2 == '\\')
445+ c2 = quoted(&p); /* BUG: sets p, has to be u8 size */
446+ if (c > c2) { /* empty; ignore */
447+ bp--;
448+ i--;
449+ continue;
450+ }
451+ while (c < c2) {
452+ if (i >= bufsz) {
453+ bufsz *= 2;
454+ buf = (int *) realloc(buf, bufsz * sizeof(int));
455+ if (buf == NULL)
456+ FATAL("out of space for character class [%.10s...] 2", p);
457+ bp = buf + i;
458+ }
459+ *bp++ = ++c;
460+ i++;
461+ }
462+ continue;
463+ }
464+ }
465+ if (i >= bufsz) {
466+ bufsz *= 2;
467+ buf = (int *) realloc(buf, bufsz * sizeof(int));
468+ if (buf == NULL)
469+ FATAL("out of space for character class [%.10s...] 2", p);
470+ bp = buf + i;
471+ }
472+ *bp++ = c;
473+ i++;
474+ }
475+ *bp = 0;
476+ /* DPRINTF("cclenter: in = |%s|, out = |%s|\n", op, buf); BUG: can't print array of int */
477+ /* xfree(op); BUG: what are we freeing here? */
478+ retp = (int *) calloc(bp-buf+1, sizeof(int));
479+ for (i = 0; i < bp-buf+1; i++)
480+ retp[i] = buf[i];
481+ return retp;
482+}
483+
484+void overflo(const char *s)
485+{
486+ FATAL("regular expression too big: out of space in %.30s...", s);
487+}
488+
489+void cfoll(fa *f, Node *v) /* enter follow set of each leaf of vertex v into lfollow[leaf] */
490+{
491+ int i;
492+ int *p;
493+
494+ switch (type(v)) {
495+ ELEAF
496+ LEAF
497+ f->re[info(v)].ltype = type(v);
498+ f->re[info(v)].lval.np = right(v);
499+ while (f->accept >= maxsetvec) { /* guessing here! */
500+ resizesetvec(__func__);
501+ }
502+ for (i = 0; i <= f->accept; i++)
503+ setvec[i] = 0;
504+ setcnt = 0;
505+ follow(v); /* computes setvec and setcnt */
506+ p = intalloc(setcnt + 1, __func__);
507+ f->re[info(v)].lfollow = p;
508+ *p = setcnt;
509+ for (i = f->accept; i >= 0; i--)
510+ if (setvec[i] == 1)
511+ *++p = i;
512+ break;
513+ UNARY
514+ cfoll(f,left(v));
515+ break;
516+ case CAT:
517+ case OR:
518+ cfoll(f,left(v));
519+ cfoll(f,right(v));
520+ break;
521+ case ZERO:
522+ break;
523+ default: /* can't happen */
524+ FATAL("can't happen: unknown type %d in cfoll", type(v));
525+ }
526+}
527+
528+int first(Node *p) /* collects initially active leaves of p into setvec */
529+ /* returns 0 if p matches empty string */
530+{
531+ int b, lp;
532+
533+ switch (type(p)) {
534+ ELEAF
535+ LEAF
536+ lp = info(p); /* look for high-water mark of subscripts */
537+ while (setcnt >= maxsetvec || lp >= maxsetvec) { /* guessing here! */
538+ resizesetvec(__func__);
539+ }
540+ if (type(p) == EMPTYRE) {
541+ setvec[lp] = 0;
542+ return(0);
543+ }
544+ if (setvec[lp] != 1) {
545+ setvec[lp] = 1;
546+ setcnt++;
547+ }
548+ if (type(p) == CCL && (*(int *) right(p)) == 0)
549+ return(0); /* empty CCL */
550+ return(1);
551+ case PLUS:
552+ if (first(left(p)) == 0)
553+ return(0);
554+ return(1);
555+ case STAR:
556+ case QUEST:
557+ first(left(p));
558+ return(0);
559+ case CAT:
560+ if (first(left(p)) == 0 && first(right(p)) == 0) return(0);
561+ return(1);
562+ case OR:
563+ b = first(right(p));
564+ if (first(left(p)) == 0 || b == 0) return(0);
565+ return(1);
566+ case ZERO:
567+ return 0;
568+ }
569+ FATAL("can't happen: unknown type %d in first", type(p)); /* can't happen */
570+ return(-1);
571+}
572+
573+void follow(Node *v) /* collects leaves that can follow v into setvec */
574+{
575+ Node *p;
576+
577+ if (type(v) == FINAL)
578+ return;
579+ p = parent(v);
580+ switch (type(p)) {
581+ case STAR:
582+ case PLUS:
583+ first(v);
584+ follow(p);
585+ return;
586+
587+ case OR:
588+ case QUEST:
589+ follow(p);
590+ return;
591+
592+ case CAT:
593+ if (v == left(p)) { /* v is left child of p */
594+ if (first(right(p)) == 0) {
595+ follow(p);
596+ return;
597+ }
598+ } else /* v is right child */
599+ follow(p);
600+ return;
601+ }
602+}
603+
604+int member(int c, int *sarg) /* is c in s? */
605+{
606+ int *s = (int *) sarg;
607+
608+ while (*s)
609+ if (c == *s++)
610+ return(1);
611+ return(0);
612+}
613+
614+static void resize_gototab(fa *f, int state)
615+{
616+ size_t new_size = f->gototab[state].allocated * 2;
617+ gtte *p = (gtte *) realloc(f->gototab[state].entries, new_size * sizeof(gtte));
618+ if (p == NULL)
619+ overflo(__func__);
620+
621+ // need to initialize the new memory to zero
622+ size_t orig_size = f->gototab[state].allocated; // 2nd half of new mem is this size
623+ memset(p + orig_size, 0, orig_size * sizeof(gtte)); // clean it out
624+
625+ f->gototab[state].allocated = new_size; // update gototab info
626+ f->gototab[state].entries = p;
627+}
628+
629+static int get_gototab(fa *f, int state, int ch) /* hide gototab implementation */
630+{
631+ gtte key;
632+ gtte *item;
633+
634+ if ((unsigned)ch < GOTO_DIRECT)
635+ return f->gototab[state].direct[ch];
636+
637+ key.ch = ch;
638+ key.state = 0; /* irrelevant */
639+ item = (gtte *) bsearch(& key, f->gototab[state].entries,
640+ f->gototab[state].inuse, sizeof(gtte),
641+ entry_cmp);
642+
643+ if (item == NULL)
644+ return 0;
645+ else
646+ return item->state;
647+}
648+
649+static int entry_cmp(const void *l, const void *r)
650+{
651+ const gtte *left, *right;
652+
653+ left = (const gtte *) l;
654+ right = (const gtte *) r;
655+
656+ return left->ch - right->ch;
657+}
658+
659+static int set_gototab(fa *f, int state, int ch, int val) /* hide gototab implementation */
660+{
661+ if ((unsigned)ch < GOTO_DIRECT) {
662+ f->gototab[state].direct[ch] = val;
663+ return val;
664+ }
665+
666+ if (f->gototab[state].inuse == 0) {
667+ f->gototab[state].entries[0].ch = ch;
668+ f->gototab[state].entries[0].state = val;
669+ f->gototab[state].inuse++;
670+ return val;
671+ } else if ((unsigned)ch > f->gototab[state].entries[f->gototab[state].inuse-1].ch) {
672+ // not seen yet, insert and return
673+ gtt *tab = & f->gototab[state];
674+ if (tab->inuse + 1 >= tab->allocated)
675+ resize_gototab(f, state);
676+
677+ f->gototab[state].entries[f->gototab[state].inuse].ch = ch;
678+ f->gototab[state].entries[f->gototab[state].inuse].state = val;
679+ f->gototab[state].inuse++;
680+ return val;
681+ } else {
682+ // maybe we have it, maybe we don't
683+ gtte key;
684+ gtte *item;
685+
686+ key.ch = ch;
687+ key.state = 0; /* irrelevant */
688+ item = (gtte *) bsearch(& key, f->gototab[state].entries,
689+ f->gototab[state].inuse, sizeof(gtte),
690+ entry_cmp);
691+
692+ if (item != NULL) {
693+ // we have it, update state and return
694+ item->state = val;
695+ return item->state;
696+ }
697+ // otherwise, fall through to insert and reallocate.
698+ }
699+
700+ gtt *tab = & f->gototab[state];
701+ if (tab->inuse + 1 >= tab->allocated)
702+ resize_gototab(f, state);
703+ f->gototab[state].entries[tab->inuse].ch = ch;
704+ f->gototab[state].entries[tab->inuse].state = val;
705+ ++tab->inuse;
706+
707+ qsort(f->gototab[state].entries,
708+ f->gototab[state].inuse, sizeof(gtte), entry_cmp);
709+
710+ return val; /* not used anywhere at the moment */
711+}
712+
713+static void clear_gototab(fa *f, int state)
714+{
715+ memset(f->gototab[state].direct, 0, sizeof(f->gototab[state].direct));
716+ memset(f->gototab[state].entries, 0,
717+ f->gototab[state].allocated * sizeof(gtte));
718+ f->gototab[state].inuse = 0;
719+}
720+
721+int match(fa *f, const char *p0) /* shortest match ? */
722+{
723+ int s, ns;
724+ int n;
725+ int rune;
726+ const uschar *p = (const uschar *) p0;
727+
728+ /* return pmatch(f, p0); does it matter whether longest or shortest? */
729+
730+ s = f->initstat;
731+ assert (s < f->state_count);
732+
733+ if (f->out[s])
734+ return(1);
735+ do {
736+ /* assert(*p < NCHARS); */
737+ n = u8_rune(&rune, (const char *) p);
738+ if ((ns = get_gototab(f, s, rune)) != 0)
739+ s = ns;
740+ else
741+ s = cgoto(f, s, rune);
742+ if (f->out[s])
743+ return(1);
744+ if (*p == 0)
745+ break;
746+ p += n;
747+ } while (1); /* was *p++ != 0 */
748+ return(0);
749+}
750+
751+int pmatch(fa *f, const char *p0) /* longest match, for sub */
752+{
753+ int s, ns;
754+ int n;
755+ int rune;
756+ const uschar *p = (const uschar *) p0;
757+ const uschar *q;
758+
759+ s = f->initstat;
760+ assert(s < f->state_count);
761+
762+ patbeg = (const char *)p;
763+ patlen = -1;
764+ do {
765+ q = p;
766+ do {
767+ if (f->out[s]) /* final state */
768+ patlen = q-p;
769+ /* assert(*q < NCHARS); */
770+ n = u8_rune(&rune, (const char *) q);
771+ if ((ns = get_gototab(f, s, rune)) != 0)
772+ s = ns;
773+ else
774+ s = cgoto(f, s, rune);
775+
776+ assert(s < f->state_count);
777+
778+ if (s == 1) { /* no transition */
779+ if (patlen >= 0) {
780+ patbeg = (const char *) p;
781+ return(1);
782+ }
783+ else
784+ goto nextin; /* no match */
785+ }
786+ if (*q == 0)
787+ break;
788+ q += n;
789+ } while (1);
790+ q++; /* was *q++ */
791+ if (f->out[s])
792+ patlen = q-p-1; /* don't count $ */
793+ if (patlen >= 0) {
794+ patbeg = (const char *) p;
795+ return(1);
796+ }
797+ nextin:
798+ s = 2;
799+ if (*p == 0)
800+ break;
801+ n = u8_rune(&rune, (const char *) p);
802+ p += n;
803+ } while (1); /* was *p++ */
804+ return (0);
805+}
806+
807+int nematch(fa *f, const char *p0) /* non-empty match, for sub */
808+{
809+ int s, ns;
810+ int n;
811+ int rune;
812+ const uschar *p = (const uschar *) p0;
813+ const uschar *q;
814+
815+ s = f->initstat;
816+ assert(s < f->state_count);
817+
818+ patbeg = (const char *)p;
819+ patlen = -1;
820+ while (*p) {
821+ q = p;
822+ do {
823+ if (f->out[s]) /* final state */
824+ patlen = q-p;
825+ /* assert(*q < NCHARS); */
826+ n = u8_rune(&rune, (const char *) q);
827+ if ((ns = get_gototab(f, s, rune)) != 0)
828+ s = ns;
829+ else
830+ s = cgoto(f, s, rune);
831+ if (s == 1) { /* no transition */
832+ if (patlen > 0) {
833+ patbeg = (const char *) p;
834+ return(1);
835+ } else
836+ goto nnextin; /* no nonempty match */
837+ }
838+ if (*q == 0)
839+ break;
840+ q += n;
841+ } while (1);
842+ q++;
843+ if (f->out[s])
844+ patlen = q-p-1; /* don't count $ */
845+ if (patlen > 0 ) {
846+ patbeg = (const char *) p;
847+ return(1);
848+ }
849+ nnextin:
850+ s = 2;
851+ p++;
852+ }
853+ return (0);
854+}
855+
856+
857+/*
858+ * NAME
859+ * fnematch
860+ *
861+ * DESCRIPTION
862+ * A stream-fed version of nematch which transfers characters to a
863+ * null-terminated buffer. All characters up to and including the last
864+ * character of the matching text or EOF are placed in the buffer. If
865+ * a match is found, patbeg and patlen are set appropriately.
866+ *
867+ * RETURN VALUES
868+ * false No match found.
869+ * true Match found.
870+ */
871+
872+bool fnematch(fa *pfa, FILE *f, char **pbuf, int *pbufsize, int quantum)
873+{
874+ char *i, *j, *k, *buf = *pbuf;
875+ int bufsize = *pbufsize;
876+ int c, n, ns, s;
877+
878+ s = pfa->initstat;
879+ patlen = 0;
880+
881+ /*
882+ * buf <= i <= j <= k <= buf+bufsize
883+ *
884+ * i: origin of active substring
885+ * j: current character
886+ * k: destination of the next getc
887+ */
888+
889+ i = j = k = buf;
890+
891+ do {
892+ /*
893+ * Call u8_rune with at least awk_mb_cur_max ahead in
894+ * the buffer until EOF interferes.
895+ */
896+ if (k - j < (int)awk_mb_cur_max) {
897+ if (k + awk_mb_cur_max > buf + bufsize) {
898+ char *obuf = buf;
899+ adjbuf((char **) &buf, &bufsize,
900+ bufsize + awk_mb_cur_max,
901+ quantum, 0, "fnematch");
902+
903+ /* buf resized, maybe moved. update pointers */
904+ *pbufsize = bufsize;
905+ if (obuf != buf) {
906+ i = buf + (i - obuf);
907+ j = buf + (j - obuf);
908+ k = buf + (k - obuf);
909+ *pbuf = buf;
910+ if (patlen)
911+ patbeg = buf + (patbeg - obuf);
912+ }
913+ }
914+ for (n = awk_mb_cur_max ; n > 0; n--) {
915+ *k++ = (c = getc(f)) != EOF ? c : 0;
916+ if (c == EOF) {
917+ if (ferror(f))
918+ FATAL("fnematch: getc error");
919+ break;
920+ }
921+ }
922+ }
923+
924+ j += u8_rune(&c, j);
925+
926+ if ((ns = get_gototab(pfa, s, c)) != 0)
927+ s = ns;
928+ else
929+ s = cgoto(pfa, s, c);
930+
931+ if (pfa->out[s]) { /* final state */
932+ patbeg = i;
933+ patlen = j - i;
934+ if (c == 0) /* don't count $ */
935+ patlen--;
936+ }
937+
938+ if (c && s != 1)
939+ continue; /* origin i still viable, next j */
940+ if (patlen)
941+ break; /* best match found */
942+
943+ /* no match at origin i, next i and start over */
944+ i += u8_rune(&c, i);
945+ if (c == 0)
946+ break; /* no match */
947+ j = i;
948+ s = 2;
949+ } while (1);
950+
951+ if (patlen) {
952+ /*
953+ * Under no circumstances is the last character fed to
954+ * the automaton part of the match. It is EOF's nullbyte,
955+ * or it sent the automaton into a state with no further
956+ * transitions available (s==1), or both. Room for a
957+ * terminating nullbyte is guaranteed.
958+ *
959+ * ungetc any chars after the end of matching text
960+ * (except for EOF's nullbyte, if present) and null
961+ * terminate the buffer.
962+ */
963+ do
964+ if (*--k && ungetc(*k, f) == EOF)
965+ FATAL("unable to ungetc '%c'", *k);
966+ while (k > patbeg + patlen);
967+ *k = '\0';
968+ return true;
969+ }
970+ else
971+ return false;
972+}
973+
974+Node *reparse(const char *p) /* parses regular expression pointed to by p */
975+{ /* uses relex() to scan regular expression */
976+ Node *np;
977+
978+ DPRINTF("reparse <%s>\n", p);
979+ lastre = prestr = (const uschar *) p; /* prestr points to string to be parsed */
980+ rtok = relex();
981+ /* GNU compatibility: an empty regexp matches anything */
982+ if (rtok == '\0') {
983+ /* FATAL("empty regular expression"); previous */
984+ return(op2(EMPTYRE, NIL, NIL));
985+ }
986+ np = regexp();
987+ if (rtok != '\0')
988+ FATAL("syntax error in regular expression %s at %s", lastre, prestr);
989+ return(np);
990+}
991+
992+Node *regexp(void) /* top-level parse of reg expr */
993+{
994+ return (alt(concat(primary())));
995+}
996+
997+Node *primary(void)
998+{
999+ Node *np;
1000+ int savelastatom;
1001+
1002+ switch (rtok) {
1003+ case CHAR:
1004+ lastatom = starttok;
1005+ np = op2(CHAR, NIL, itonp(rlxval));
1006+ rtok = relex();
1007+ return (unary(np));
1008+ case ALL:
1009+ rtok = relex();
1010+ return (unary(op2(ALL, NIL, NIL)));
1011+ case EMPTYRE:
1012+ rtok = relex();
1013+ return (unary(op2(EMPTYRE, NIL, NIL)));
1014+ case DOT:
1015+ lastatom = starttok;
1016+ rtok = relex();
1017+ return (unary(op2(DOT, NIL, NIL)));
1018+ case CCL:
1019+ np = op2(CCL, NIL, (Node*) cclenter((const char *) rlxstr));
1020+ lastatom = starttok;
1021+ rtok = relex();
1022+ return (unary(np));
1023+ case NCCL:
1024+ np = op2(NCCL, NIL, (Node *) cclenter((const char *) rlxstr));
1025+ lastatom = starttok;
1026+ rtok = relex();
1027+ return (unary(np));
1028+ case '^':
1029+ rtok = relex();
1030+ return (unary(op2(CHAR, NIL, itonp(HAT))));
1031+ case '$':
1032+ rtok = relex();
1033+ return (unary(op2(CHAR, NIL, NIL)));
1034+ case '(':
1035+ lastatom = starttok;
1036+ savelastatom = starttok - basestr; /* Retain over recursion */
1037+ rtok = relex();
1038+ if (rtok == ')') { /* special pleading for () */
1039+ rtok = relex();
1040+ return unary(op2(CCL, NIL, (Node *) cclenter("")));
1041+ }
1042+ np = regexp();
1043+ if (rtok == ')') {
1044+ lastatom = basestr + savelastatom; /* Restore */
1045+ rtok = relex();
1046+ return (unary(np));
1047+ }
1048+ else {
1049+ FATAL("syntax error in regular expression %s at %s", lastre, prestr);
1050+ break;
1051+ }
1052+ default:
1053+ FATAL("illegal primary in regular expression %s at %s", lastre, prestr);
1054+ }
1055+ return 0; /*NOTREACHED*/
1056+}
1057+
1058+Node *concat(Node *np)
1059+{
1060+ switch (rtok) {
1061+ case CHAR: case DOT: case ALL: case CCL: case NCCL: case '$': case '(':
1062+ return (concat(op2(CAT, np, primary())));
1063+ case EMPTYRE:
1064+ rtok = relex();
1065+ return (concat(op2(CAT, op2(CCL, NIL, (Node *) cclenter("")),
1066+ primary())));
1067+ }
1068+ return (np);
1069+}
1070+
1071+Node *alt(Node *np)
1072+{
1073+ if (rtok == OR) {
1074+ rtok = relex();
1075+ return (alt(op2(OR, np, concat(primary()))));
1076+ }
1077+ return (np);
1078+}
1079+
1080+Node *unary(Node *np)
1081+{
1082+ switch (rtok) {
1083+ case STAR:
1084+ rtok = relex();
1085+ return (unary(op2(STAR, np, NIL)));
1086+ case PLUS:
1087+ rtok = relex();
1088+ return (unary(op2(PLUS, np, NIL)));
1089+ case QUEST:
1090+ rtok = relex();
1091+ return (unary(op2(QUEST, np, NIL)));
1092+ case ZERO:
1093+ rtok = relex();
1094+ return (unary(op2(ZERO, np, NIL)));
1095+ default:
1096+ return (np);
1097+ }
1098+}
1099+
1100+/*
1101+ * Character class definitions conformant to the POSIX locale as
1102+ * defined in IEEE P1003.1 draft 7 of June 2001, assuming the source
1103+ * and operating character sets are both ASCII (ISO646) or supersets
1104+ * thereof.
1105+ *
1106+ * Note that to avoid overflowing the temporary buffer used in
1107+ * relex(), the expanded character class (prior to range expansion)
1108+ * must be less than twice the size of their full name.
1109+ */
1110+
1111+/* Because isblank doesn't show up in any of the header files on any
1112+ * system i use, it's defined here. if some other locale has a richer
1113+ * definition of "blank", define HAS_ISBLANK and provide your own
1114+ * version.
1115+ * the parentheses here are an attempt to find a path through the maze
1116+ * of macro definition and/or function and/or version provided. thanks
1117+ * to nelson beebe for the suggestion; let's see if it works everywhere.
1118+ */
1119+
1120+/* #define HAS_ISBLANK */
1121+#ifndef HAS_ISBLANK
1122+
1123+int (xisblank)(int c)
1124+{
1125+ return c==' ' || c=='\t';
1126+}
1127+
1128+#endif
1129+
1130+static const struct charclass {
1131+ const char *cc_name;
1132+ int cc_namelen;
1133+ int (*cc_func)(int);
1134+} charclasses[] = {
1135+ { "alnum", 5, isalnum },
1136+ { "alpha", 5, isalpha },
1137+#ifndef HAS_ISBLANK
1138+ { "blank", 5, xisblank },
1139+#else
1140+ { "blank", 5, isblank },
1141+#endif
1142+ { "cntrl", 5, iscntrl },
1143+ { "digit", 5, isdigit },
1144+ { "graph", 5, isgraph },
1145+ { "lower", 5, islower },
1146+ { "print", 5, isprint },
1147+ { "punct", 5, ispunct },
1148+ { "space", 5, isspace },
1149+ { "upper", 5, isupper },
1150+ { "xdigit", 6, isxdigit },
1151+ { NULL, 0, NULL },
1152+};
1153+
1154+#define REPEAT_SIMPLE 0
1155+#define REPEAT_PLUS_APPENDED 1
1156+#define REPEAT_WITH_Q 2
1157+#define REPEAT_ZERO 3
1158+
1159+static int
1160+replace_repeat(const uschar *reptok, int reptoklen, const uschar *atom,
1161+ int atomlen, int firstnum, int secondnum, int special_case)
1162+{
1163+ int i, j;
1164+ uschar *buf = 0;
1165+ int ret = 1;
1166+ int init_q = (firstnum == 0); /* first added char will be ? */
1167+ int n_q_reps = secondnum-firstnum; /* m>n, so reduce until {1,m-n} left */
1168+ int prefix_length = reptok - basestr; /* prefix includes first rep */
1169+ int suffix_length = strlen((const char *) reptok) - reptoklen; /* string after rep specifier */
1170+ int size = prefix_length + suffix_length;
1171+
1172+ if (firstnum > 1) { /* add room for reps 2 through firstnum */
1173+ size += atomlen*(firstnum-1);
1174+ }
1175+
1176+ /* Adjust size of buffer for special cases */
1177+ if (special_case == REPEAT_PLUS_APPENDED) {
1178+ size++; /* for the final + */
1179+ } else if (special_case == REPEAT_WITH_Q) {
1180+ size += init_q + (atomlen+1)* (n_q_reps-init_q);
1181+ } else if (special_case == REPEAT_ZERO) {
1182+ size += 2; /* just a null ERE: () */
1183+ }
1184+ if ((buf = (uschar *) malloc(size + 1)) == NULL)
1185+ FATAL("out of space in reg expr %.10s..", lastre);
1186+ memcpy(buf, basestr, prefix_length); /* copy prefix */
1187+ j = prefix_length;
1188+ if (special_case == REPEAT_ZERO) {
1189+ j -= atomlen;
1190+ buf[j++] = '(';
1191+ buf[j++] = ')';
1192+ }
1193+ for (i = 1; i < firstnum; i++) { /* copy x reps */
1194+ memcpy(&buf[j], atom, atomlen);
1195+ j += atomlen;
1196+ }
1197+ if (special_case == REPEAT_PLUS_APPENDED) {
1198+ buf[j++] = '+';
1199+ } else if (special_case == REPEAT_WITH_Q) {
1200+ if (init_q)
1201+ buf[j++] = '?';
1202+ for (i = init_q; i < n_q_reps; i++) { /* copy x? reps */
1203+ memcpy(&buf[j], atom, atomlen);
1204+ j += atomlen;
1205+ buf[j++] = '?';
1206+ }
1207+ }
1208+ memcpy(&buf[j], reptok+reptoklen, suffix_length);
1209+ j += suffix_length;
1210+ buf[j] = '\0';
1211+ /* free old basestr */
1212+ if (firstbasestr != basestr) {
1213+ if (basestr)
1214+ xfree(basestr);
1215+ }
1216+ basestr = buf;
1217+ prestr = buf + prefix_length;
1218+ if (special_case == REPEAT_ZERO) {
1219+ prestr -= atomlen;
1220+ ret++;
1221+ }
1222+ return ret;
1223+}
1224+
1225+static int repeat(const uschar *reptok, int reptoklen, const uschar *atom,
1226+ int atomlen, int firstnum, int secondnum)
1227+{
1228+ if (atom == NULL)
1229+ return 0;
1230+
1231+ /*
1232+ In general, the repetition specifier or "bound" is replaced here
1233+ by an equivalent ERE string, repeating the immediately previous atom
1234+ and appending ? and + as needed. Note that the first copy of the
1235+ atom is left in place, except in the special_case of a zero-repeat
1236+ (i.e., {0}).
1237+ */
1238+ if (secondnum < 0) { /* means {n,} -> repeat n-1 times followed by PLUS */
1239+ if (firstnum < 2) {
1240+ /* 0 or 1: should be handled before you get here */
1241+ FATAL("internal error");
1242+ } else {
1243+ return replace_repeat(reptok, reptoklen, atom, atomlen,
1244+ firstnum, secondnum, REPEAT_PLUS_APPENDED);
1245+ }
1246+ } else if (firstnum == secondnum) { /* {n} or {n,n} -> simply repeat n-1 times */
1247+ if (firstnum == 0) { /* {0} or {0,0} */
1248+ /* This case is unusual because the resulting
1249+ replacement string might actually be SMALLER than
1250+ the original ERE */
1251+ return replace_repeat(reptok, reptoklen, atom, atomlen,
1252+ firstnum, secondnum, REPEAT_ZERO);
1253+ } else { /* (firstnum >= 1) */
1254+ return replace_repeat(reptok, reptoklen, atom, atomlen,
1255+ firstnum, secondnum, REPEAT_SIMPLE);
1256+ }
1257+ } else if (firstnum < secondnum) { /* {n,m} -> repeat n-1 times then alternate */
1258+ /* x{n,m} => xx...x{1, m-n+1} => xx...x?x?x?..x? */
1259+ return replace_repeat(reptok, reptoklen, atom, atomlen,
1260+ firstnum, secondnum, REPEAT_WITH_Q);
1261+ } else { /* Error - shouldn't be here (n>m) */
1262+ FATAL("internal error");
1263+ }
1264+ return 0;
1265+}
1266+
1267+int relex(void) /* lexical analyzer for reparse */
1268+{
1269+ int c, n;
1270+ int cflag;
1271+ static uschar *buf = NULL;
1272+ static int bufsz = 100;
1273+ uschar *bp;
1274+ const struct charclass *cc;
1275+ int i;
1276+ int num, m;
1277+ bool commafound, digitfound;
1278+ const uschar *startreptok;
1279+ static int parens = 0;
1280+
1281+rescan:
1282+ starttok = prestr;
1283+
1284+ if ((n = u8_rune(&rlxval, (const char *) prestr)) > 1) {
1285+ prestr += n;
1286+ starttok = prestr;
1287+ return CHAR;
1288+ }
1289+
1290+ switch (c = *prestr++) {
1291+ case '|': return OR;
1292+ case '*': return STAR;
1293+ case '+': return PLUS;
1294+ case '?': return QUEST;
1295+ case '.': return DOT;
1296+ case '\0': prestr--; return '\0';
1297+ case '^':
1298+ case '$':
1299+ return c;
1300+ case '(':
1301+ parens++;
1302+ return c;
1303+ case ')':
1304+ if (parens) {
1305+ parens--;
1306+ return c;
1307+ }
1308+ /* unmatched close parenthesis; per POSIX, treat as literal */
1309+ rlxval = c;
1310+ return CHAR;
1311+ case '\\':
1312+ rlxval = quoted(&prestr);
1313+ return CHAR;
1314+ default:
1315+ rlxval = c;
1316+ return CHAR;
1317+ case '[':
1318+ if (buf == NULL && (buf = (uschar *) malloc(bufsz)) == NULL)
1319+ FATAL("out of space in reg expr %.10s..", lastre);
1320+ bp = buf;
1321+ if (*prestr == '^') {
1322+ cflag = 1;
1323+ prestr++;
1324+ }
1325+ else
1326+ cflag = 0;
1327+ n = 5 * strlen((const char *) prestr)+1; /* BUG: was 2. what value? */
1328+ if (!adjbuf((char **) &buf, &bufsz, n, n, (char **) &bp, "relex1"))
1329+ FATAL("out of space for reg expr %.10s...", lastre);
1330+ for (; ; ) {
1331+ if ((n = u8_rune(&rlxval, (const char *) prestr)) > 1) {
1332+ for (i = 0; i < n; i++)
1333+ *bp++ = *prestr++;
1334+ continue;
1335+ }
1336+ if ((c = *prestr++) == '\\') {
1337+ *bp++ = '\\';
1338+ if ((c = *prestr++) == '\0')
1339+ FATAL("nonterminated character class %.20s...", lastre);
1340+ *bp++ = c;
1341+ /* } else if (c == '\n') { */
1342+ /* FATAL("newline in character class %.20s...", lastre); */
1343+ } else if (c == '[' && *prestr == ':') {
1344+ /* POSIX char class names, Dag-Erling Smorgrav, des@ofug.org */
1345+ for (cc = charclasses; cc->cc_name; cc++)
1346+ if (strncmp((const char *) prestr + 1, (const char *) cc->cc_name, cc->cc_namelen) == 0)
1347+ break;
1348+ if (cc->cc_name != NULL && prestr[1 + cc->cc_namelen] == ':' &&
1349+ prestr[2 + cc->cc_namelen] == ']') {
1350+ prestr += cc->cc_namelen + 3;
1351+ /*
1352+ * BUG: We begin at 1, instead of 0, since we
1353+ * would otherwise prematurely terminate the
1354+ * string for classes like [[:cntrl:]]. This
1355+ * means that we can't match the NUL character,
1356+ * not without first adapting the entire
1357+ * program to track each string's length.
1358+ */
1359+ for (i = 1; i <= UCHAR_MAX; i++) {
1360+ if (!adjbuf((char **) &buf, &bufsz, bp-buf+2, 100, (char **) &bp, "relex2"))
1361+ FATAL("out of space for reg expr %.10s...", lastre);
1362+ if (cc->cc_func(i)) {
1363+ /* escape backslash */
1364+ if (i == '\\') {
1365+ *bp++ = '\\';
1366+ n++;
1367+ }
1368+
1369+ *bp++ = i;
1370+ n++;
1371+ }
1372+ }
1373+ } else
1374+ *bp++ = c;
1375+ } else if (c == '[' && *prestr == '.') {
1376+ char collate_char;
1377+ prestr++;
1378+ collate_char = *prestr++;
1379+ if (*prestr == '.' && prestr[1] == ']') {
1380+ prestr += 2;
1381+ /* Found it: map via locale TBD: for
1382+ now, simply return this char. This
1383+ is sufficient to pass conformance
1384+ test awk.ex 156
1385+ */
1386+ if (*prestr == ']') {
1387+ prestr++;
1388+ rlxval = collate_char;
1389+ return CHAR;
1390+ }
1391+ }
1392+ } else if (c == '[' && *prestr == '=') {
1393+ char equiv_char;
1394+ prestr++;
1395+ equiv_char = *prestr++;
1396+ if (*prestr == '=' && prestr[1] == ']') {
1397+ prestr += 2;
1398+ /* Found it: map via locale TBD: for now
1399+ simply return this char. This is
1400+ sufficient to pass conformance test
1401+ awk.ex 156
1402+ */
1403+ if (*prestr == ']') {
1404+ prestr++;
1405+ rlxval = equiv_char;
1406+ return CHAR;
1407+ }
1408+ }
1409+ } else if (c == '\0') {
1410+ FATAL("nonterminated character class %.20s", lastre);
1411+ } else if (bp == buf) { /* 1st char is special */
1412+ *bp++ = c;
1413+ } else if (c == ']') {
1414+ *bp++ = 0;
1415+ rlxstr = (uschar *) tostring((char *) buf);
1416+ if (cflag == 0)
1417+ return CCL;
1418+ else
1419+ return NCCL;
1420+ } else
1421+ *bp++ = c;
1422+ }
1423+ break;
1424+ case '{':
1425+ if (isdigit((int) *(prestr))) {
1426+ num = 0; /* Process as a repetition */
1427+ n = -1; m = -1;
1428+ commafound = false;
1429+ digitfound = false;
1430+ startreptok = prestr-1;
1431+ /* Remember start of previous atom here ? */
1432+ } else { /* just a { char, not a repetition */
1433+ rlxval = c;
1434+ return CHAR;
1435+ }
1436+ for (; ; ) {
1437+ if ((c = *prestr++) == '}') {
1438+ if (commafound) {
1439+ if (digitfound) { /* {n,m} */
1440+ m = num;
1441+ if (m < n)
1442+ FATAL("illegal repetition expression: class %.20s",
1443+ lastre);
1444+ if (n == 0 && m == 1) {
1445+ return QUEST;
1446+ }
1447+ } else { /* {n,} */
1448+ if (n == 0)
1449+ return STAR;
1450+ else if (n == 1)
1451+ return PLUS;
1452+ }
1453+ } else {
1454+ if (digitfound) { /* {n} same as {n,n} */
1455+ n = num;
1456+ m = num;
1457+ } else { /* {} */
1458+ FATAL("illegal repetition expression: class %.20s",
1459+ lastre);
1460+ }
1461+ }
1462+ if (repeat(starttok, prestr-starttok, lastatom,
1463+ startreptok - lastatom, n, m) > 0) {
1464+ if (n == 0 && m == 0) {
1465+ return ZERO;
1466+ }
1467+ /* must rescan input for next token */
1468+ goto rescan;
1469+ }
1470+ /* Failed to replace: eat up {...} characters
1471+ and treat like just PLUS */
1472+ return PLUS;
1473+ } else if (c == '\0') {
1474+ FATAL("nonterminated character class %.20s",
1475+ lastre);
1476+ } else if (isdigit(c)) {
1477+ num = 10 * num + c - '0';
1478+ if (num > 255)
1479+ FATAL("repetition count %.20s too large",
1480+ lastre);
1481+ digitfound = true;
1482+ } else if (c == ',') {
1483+ if (commafound)
1484+ FATAL("illegal repetition expression: class %.20s",
1485+ lastre);
1486+ /* looking for {n,} or {n,m} */
1487+ commafound = true;
1488+ n = num;
1489+ digitfound = false; /* reset */
1490+ num = 0;
1491+ } else {
1492+ FATAL("illegal repetition expression: class %.20s",
1493+ lastre);
1494+ }
1495+ }
1496+ break;
1497+ }
1498+}
1499+
1500+int cgoto(fa *f, int s, int c)
1501+{
1502+ int *p, *q;
1503+ int i, j, k;
1504+
1505+ /* assert(c == HAT || c < NCHARS); BUG: seg fault if disable test */
1506+ while (f->accept >= maxsetvec) { /* guessing here! */
1507+ resizesetvec(__func__);
1508+ }
1509+ for (i = 0; i <= f->accept; i++)
1510+ setvec[i] = 0;
1511+ setcnt = 0;
1512+ resize_state(f, s);
1513+ /* compute positions of gototab[s,c] into setvec */
1514+ p = f->posns[s];
1515+ for (i = 1; i <= *p; i++) {
1516+ if ((k = f->re[p[i]].ltype) != FINAL) {
1517+ if ((k == CHAR && c == ptoi(f->re[p[i]].lval.np))
1518+ || (k == DOT && c != 0 && c != HAT)
1519+ || (k == ALL && c != 0)
1520+ || (k == EMPTYRE && c != 0)
1521+ || (k == CCL && member(c, (int *) f->re[p[i]].lval.rp))
1522+ || (k == NCCL && !member(c, (int *) f->re[p[i]].lval.rp) && c != 0 && c != HAT)) {
1523+ q = f->re[p[i]].lfollow;
1524+ for (j = 1; j <= *q; j++) {
1525+ if (q[j] >= maxsetvec) {
1526+ resizesetvec(__func__);
1527+ }
1528+ if (setvec[q[j]] == 0) {
1529+ setcnt++;
1530+ setvec[q[j]] = 1;
1531+ }
1532+ }
1533+ }
1534+ }
1535+ }
1536+ /* determine if setvec is a previous state */
1537+ tmpset[0] = setcnt;
1538+ j = 1;
1539+ for (i = f->accept; i >= 0; i--)
1540+ if (setvec[i]) {
1541+ tmpset[j++] = i;
1542+ }
1543+ resize_state(f, f->curstat > s ? f->curstat : s);
1544+ /* tmpset == previous state? */
1545+ for (i = 1; i <= f->curstat; i++) {
1546+ p = f->posns[i];
1547+ if ((k = tmpset[0]) != p[0])
1548+ goto different;
1549+ for (j = 1; j <= k; j++)
1550+ if (tmpset[j] != p[j])
1551+ goto different;
1552+ /* setvec is state i */
1553+ if (c != HAT)
1554+ set_gototab(f, s, c, i);
1555+ return i;
1556+ different:;
1557+ }
1558+
1559+ /* add tmpset to current set of states */
1560+ ++(f->curstat);
1561+ resize_state(f, f->curstat);
1562+ clear_gototab(f, f->curstat);
1563+ xfree(f->posns[f->curstat]);
1564+ p = intalloc(setcnt + 1, __func__);
1565+
1566+ f->posns[f->curstat] = p;
1567+ if (c != HAT)
1568+ set_gototab(f, s, c, f->curstat);
1569+ for (i = 0; i <= setcnt; i++)
1570+ p[i] = tmpset[i];
1571+ if (setvec[f->accept])
1572+ f->out[f->curstat] = 1;
1573+ else
1574+ f->out[f->curstat] = 0;
1575+ return f->curstat;
1576+}
1577+
1578+
1579+void freefa(fa *f) /* free a finite automaton */
1580+{
1581+ int i;
1582+
1583+ if (f == NULL)
1584+ return;
1585+ for (i = 0; i < f->state_count; i++)
1586+ xfree(f->gototab[i].entries);
1587+ xfree(f->gototab);
1588+ for (i = 0; i <= f->curstat; i++)
1589+ xfree(f->posns[i]);
1590+ for (i = 0; i <= f->accept; i++) {
1591+ xfree(f->re[i].lfollow);
1592+ if (f->re[i].ltype == CCL || f->re[i].ltype == NCCL)
1593+ xfree(f->re[i].lval.np);
1594+ }
1595+ xfree(f->restr);
1596+ xfree(f->out);
1597+ xfree(f->posns);
1598+ xfree(f->gototab);
1599+ xfree(f);
1600+}
+630,
-0
1@@ -0,0 +1,630 @@
2+/****************************************************************
3+Copyright (C) Lucent Technologies 1997
4+All Rights Reserved
5+
6+Permission to use, copy, modify, and distribute this software and
7+its documentation for any purpose and without fee is hereby
8+granted, provided that the above copyright notice appear in all
9+copies and that both that the copyright notice and this
10+permission notice and warranty disclaimer appear in supporting
11+documentation, and that the name Lucent Technologies or any of
12+its entities not be used in advertising or publicity pertaining
13+to distribution of the software without specific, written prior
14+permission.
15+
16+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
18+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
19+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
21+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
22+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
23+THIS SOFTWARE.
24+****************************************************************/
25+
26+#include <stdio.h>
27+#include <stdlib.h>
28+#include <string.h>
29+#include <ctype.h>
30+#include "awk.h"
31+#include "awkgram.tab.h"
32+
33+extern YYSTYPE yylval;
34+extern bool infunc;
35+
36+int lineno = 1;
37+int bracecnt = 0;
38+int brackcnt = 0;
39+int parencnt = 0;
40+
41+typedef struct Keyword {
42+ const char *word;
43+ int sub;
44+ int type;
45+} Keyword;
46+
47+const Keyword keywords[] = { /* keep sorted: binary searched */
48+ { "BEGIN", XBEGIN, XBEGIN },
49+ { "END", XEND, XEND },
50+ { "NF", VARNF, VARNF },
51+ { "atan2", FATAN, BLTIN },
52+ { "break", BREAK, BREAK },
53+ { "close", CLOSE, CLOSE },
54+ { "continue", CONTINUE, CONTINUE },
55+ { "cos", FCOS, BLTIN },
56+ { "delete", DELETE, DELETE },
57+ { "do", DO, DO },
58+ { "else", ELSE, ELSE },
59+ { "exit", EXIT, EXIT },
60+ { "exp", FEXP, BLTIN },
61+ { "fflush", FFLUSH, BLTIN },
62+ { "for", FOR, FOR },
63+ { "func", FUNC, FUNC },
64+ { "function", FUNC, FUNC },
65+ { "getline", GETLINE, GETLINE },
66+ { "gsub", GSUB, GSUB },
67+ { "if", IF, IF },
68+ { "in", IN, IN },
69+ { "index", INDEX, INDEX },
70+ { "int", FINT, BLTIN },
71+ { "length", FLENGTH, BLTIN },
72+ { "log", FLOG, BLTIN },
73+ { "match", MATCHFCN, MATCHFCN },
74+ { "next", NEXT, NEXT },
75+ { "nextfile", NEXTFILE, NEXTFILE },
76+ { "print", PRINT, PRINT },
77+ { "printf", PRINTF, PRINTF },
78+ { "rand", FRAND, BLTIN },
79+ { "return", RETURN, RETURN },
80+ { "sin", FSIN, BLTIN },
81+ { "split", SPLIT, SPLIT },
82+ { "sprintf", SPRINTF, SPRINTF },
83+ { "sqrt", FSQRT, BLTIN },
84+ { "srand", FSRAND, BLTIN },
85+ { "sub", SUB, SUB },
86+ { "substr", SUBSTR, SUBSTR },
87+ { "system", FSYSTEM, BLTIN },
88+ { "tolower", FTOLOWER, BLTIN },
89+ { "toupper", FTOUPPER, BLTIN },
90+ { "while", WHILE, WHILE },
91+};
92+
93+#define RET(x) { if(dbg)printf("lex %s\n", tokname(x)); return(x); }
94+
95+static int peek(void)
96+{
97+ int c = input();
98+ unput(c);
99+ return c;
100+}
101+
102+static int gettok(char **pbuf, int *psz) /* get next input token */
103+{
104+ int c, retc;
105+ char *buf = *pbuf;
106+ int sz = *psz;
107+ char *bp = buf;
108+
109+ c = input();
110+ if (c == 0)
111+ return 0;
112+ buf[0] = c;
113+ buf[1] = 0;
114+ if (!isalnum(c) && c != '.' && c != '_')
115+ return c;
116+
117+ *bp++ = c;
118+ if (isalpha(c) || c == '_') { /* it's a varname */
119+ for ( ; (c = input()) != 0; ) {
120+ if (bp-buf >= sz)
121+ if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, "gettok"))
122+ FATAL( "out of space for name %.10s...", buf );
123+ if (isalnum(c) || c == '_')
124+ *bp++ = c;
125+ else {
126+ *bp = 0;
127+ unput(c);
128+ break;
129+ }
130+ }
131+ *bp = 0;
132+ retc = 'a'; /* alphanumeric */
133+ } else { /* maybe it's a number, but could be . */
134+ char *rem;
135+ /* read input until can't be a number */
136+ for ( ; (c = input()) != 0; ) {
137+ if (bp-buf >= sz)
138+ if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, "gettok"))
139+ FATAL( "out of space for number %.10s...", buf );
140+ if (isdigit(c) || c == 'e' || c == 'E'
141+ || c == '.' || c == '+' || c == '-')
142+ *bp++ = c;
143+ else {
144+ unput(c);
145+ break;
146+ }
147+ }
148+ *bp = 0;
149+ strtod(buf, &rem); /* parse the number */
150+ if (rem == buf) { /* it wasn't a valid number at all */
151+ buf[1] = 0; /* return one character as token */
152+ retc = (uschar)buf[0]; /* character is its own type */
153+ unputstr(rem+1); /* put rest back for later */
154+ } else { /* some prefix was a number */
155+ unputstr(rem); /* put rest back for later */
156+ rem[0] = 0; /* truncate buf after number part */
157+ retc = '0'; /* type is number */
158+ }
159+ }
160+ *pbuf = buf;
161+ *psz = sz;
162+ return retc;
163+}
164+
165+int word(char *);
166+int string(void);
167+int regexpr(void);
168+bool sc = false; /* true => return a } right now */
169+bool reg = false; /* true => return a REGEXPR now */
170+
171+int yylex(void)
172+{
173+ int c;
174+ static char *buf = NULL;
175+ static int bufsize = 5; /* BUG: setting this small causes core dump! */
176+
177+ if (buf == NULL && (buf = (char *) malloc(bufsize)) == NULL)
178+ FATAL( "out of space in yylex" );
179+ if (sc) {
180+ sc = false;
181+ RET('}');
182+ }
183+ if (reg) {
184+ reg = false;
185+ return regexpr();
186+ }
187+ for (;;) {
188+ c = gettok(&buf, &bufsize);
189+ if (c == 0)
190+ return 0;
191+ if (isalpha(c) || c == '_')
192+ return word(buf);
193+ if (isdigit(c)) {
194+ char *cp = tostring(buf);
195+ double result;
196+
197+ if (is_number(cp, & result))
198+ yylval.cp = setsymtab(buf, cp, result, CON|NUM, symtab);
199+ else
200+ yylval.cp = setsymtab(buf, cp, 0.0, STR, symtab);
201+ free(cp);
202+ /* should this also have STR set? */
203+ RET(NUMBER);
204+ }
205+
206+ yylval.i = c;
207+ switch (c) {
208+ case '\n': /* {EOL} */
209+ lineno++;
210+ RET(NL);
211+ case '\r': /* assume \n is coming */
212+ case ' ': /* {WS}+ */
213+ case '\t':
214+ break;
215+ case '#': /* #.* strip comments */
216+ while ((c = input()) != '\n' && c != 0)
217+ ;
218+ unput(c);
219+ break;
220+ case ';':
221+ RET(';');
222+ case '\\':
223+ if (peek() == '\n') {
224+ input();
225+ lineno++;
226+ } else if (peek() == '\r') {
227+ input(); input(); /* \n */
228+ lineno++;
229+ } else {
230+ RET(c);
231+ }
232+ break;
233+ case '&':
234+ if (peek() == '&') {
235+ input(); RET(AND);
236+ } else
237+ RET('&');
238+ case '|':
239+ if (peek() == '|') {
240+ input(); RET(BOR);
241+ } else
242+ RET('|');
243+ case '!':
244+ if (peek() == '=') {
245+ input(); yylval.i = NE; RET(NE);
246+ } else if (peek() == '~') {
247+ input(); yylval.i = NOTMATCH; RET(MATCHOP);
248+ } else
249+ RET(NOT);
250+ case '~':
251+ yylval.i = MATCH;
252+ RET(MATCHOP);
253+ case '<':
254+ if (peek() == '=') {
255+ input(); yylval.i = LE; RET(LE);
256+ } else {
257+ yylval.i = LT; RET(LT);
258+ }
259+ case '=':
260+ if (peek() == '=') {
261+ input(); yylval.i = EQ; RET(EQ);
262+ } else {
263+ yylval.i = ASSIGN; RET(ASGNOP);
264+ }
265+ case '>':
266+ if (peek() == '=') {
267+ input(); yylval.i = GE; RET(GE);
268+ } else if (peek() == '>') {
269+ input(); yylval.i = APPEND; RET(APPEND);
270+ } else {
271+ yylval.i = GT; RET(GT);
272+ }
273+ case '+':
274+ if (peek() == '+') {
275+ input(); yylval.i = INCR; RET(INCR);
276+ } else if (peek() == '=') {
277+ input(); yylval.i = ADDEQ; RET(ASGNOP);
278+ } else
279+ RET('+');
280+ case '-':
281+ if (peek() == '-') {
282+ input(); yylval.i = DECR; RET(DECR);
283+ } else if (peek() == '=') {
284+ input(); yylval.i = SUBEQ; RET(ASGNOP);
285+ } else
286+ RET('-');
287+ case '*':
288+ if (peek() == '=') { /* *= */
289+ input(); yylval.i = MULTEQ; RET(ASGNOP);
290+ } else if (peek() == '*') { /* ** or **= */
291+ input(); /* eat 2nd * */
292+ if (peek() == '=') {
293+ input(); yylval.i = POWEQ; RET(ASGNOP);
294+ } else {
295+ RET(POWER);
296+ }
297+ } else
298+ RET('*');
299+ case '/':
300+ RET('/');
301+ case '%':
302+ if (peek() == '=') {
303+ input(); yylval.i = MODEQ; RET(ASGNOP);
304+ } else
305+ RET('%');
306+ case '^':
307+ if (peek() == '=') {
308+ input(); yylval.i = POWEQ; RET(ASGNOP);
309+ } else
310+ RET(POWER);
311+
312+ case '$':
313+ /* BUG: awkward, if not wrong */
314+ c = gettok(&buf, &bufsize);
315+ if (isalpha(c)) {
316+ if (strcmp(buf, "NF") == 0) { /* very special */
317+ unputstr("(NF)");
318+ RET(INDIRECT);
319+ }
320+ c = peek();
321+ if (c == '(' || c == '[' || (infunc && isarg(buf) >= 0)) {
322+ unputstr(buf);
323+ RET(INDIRECT);
324+ }
325+ yylval.cp = setsymtab(buf, "", 0.0, STR|NUM, symtab);
326+ RET(IVAR);
327+ } else if (c == 0) { /* */
328+ SYNTAX( "unexpected end of input after $" );
329+ RET(';');
330+ } else {
331+ unputstr(buf);
332+ RET(INDIRECT);
333+ }
334+
335+ case '}':
336+ if (--bracecnt < 0)
337+ SYNTAX( "extra }" );
338+ sc = true;
339+ RET(';');
340+ case ']':
341+ if (--brackcnt < 0)
342+ SYNTAX( "extra ]" );
343+ RET(']');
344+ case ')':
345+ if (--parencnt < 0)
346+ SYNTAX( "extra )" );
347+ RET(')');
348+ case '{':
349+ bracecnt++;
350+ RET('{');
351+ case '[':
352+ brackcnt++;
353+ RET('[');
354+ case '(':
355+ parencnt++;
356+ RET('(');
357+
358+ case '"':
359+ return string(); /* BUG: should be like tran.c ? */
360+
361+ default:
362+ RET(c);
363+ }
364+ }
365+}
366+
367+extern int runetochar(char *str, int c);
368+
369+int string(void)
370+{
371+ int c, n;
372+ char *s, *bp;
373+ static char *buf = NULL;
374+ static int bufsz = 500;
375+
376+ if (buf == NULL && (buf = (char *) malloc(bufsz)) == NULL)
377+ FATAL("out of space for strings");
378+ for (bp = buf; (c = input()) != '"'; ) {
379+ if (!adjbuf(&buf, &bufsz, bp-buf+2, 500, &bp, "string"))
380+ FATAL("out of space for string %.10s...", buf);
381+ switch (c) {
382+ case '\n':
383+ case '\r':
384+ case 0:
385+ *bp = '\0';
386+ SYNTAX( "non-terminated string %.10s...", buf );
387+ if (c == 0) /* hopeless */
388+ FATAL( "giving up" );
389+ lineno++;
390+ break;
391+ case '\\':
392+ c = input();
393+ switch (c) {
394+ case '\n': break;
395+ case '"': *bp++ = '"'; break;
396+ case 'n': *bp++ = '\n'; break;
397+ case 't': *bp++ = '\t'; break;
398+ case 'f': *bp++ = '\f'; break;
399+ case 'r': *bp++ = '\r'; break;
400+ case 'b': *bp++ = '\b'; break;
401+ case 'v': *bp++ = '\v'; break;
402+ case 'a': *bp++ = '\a'; break;
403+ case '\\': *bp++ = '\\'; break;
404+
405+ case '0': case '1': case '2': /* octal: \d \dd \ddd */
406+ case '3': case '4': case '5': case '6': case '7':
407+ n = c - '0';
408+ if ((c = peek()) >= '0' && c < '8') {
409+ n = 8 * n + input() - '0';
410+ if ((c = peek()) >= '0' && c < '8')
411+ n = 8 * n + input() - '0';
412+ }
413+ *bp++ = n;
414+ break;
415+
416+ case 'x': /* hex \x0-9a-fA-F (exactly two) */
417+ {
418+ int i;
419+
420+ if (!isxdigit(peek())) {
421+ unput(c);
422+ break;
423+ }
424+ n = 0;
425+ for (i = 0; i < 2; i++) {
426+ c = input();
427+ if (c == 0)
428+ break;
429+ if (isxdigit(c)) {
430+ c = tolower(c);
431+ n *= 16;
432+ if (isdigit(c))
433+ n += (c - '0');
434+ else
435+ n += 10 + (c - 'a');
436+ } else {
437+ unput(c);
438+ break;
439+ }
440+ }
441+ if (i)
442+ *bp++ = n;
443+ break;
444+ }
445+
446+ case 'u': /* utf \u0-9a-fA-F (1..8) */
447+ {
448+ int i;
449+
450+ n = 0;
451+ for (i = 0; i < 8; i++) {
452+ c = input();
453+ if (!isxdigit(c) || c == 0)
454+ break;
455+ c = tolower(c);
456+ n *= 16;
457+ if (isdigit(c))
458+ n += (c - '0');
459+ else
460+ n += 10 + (c - 'a');
461+ }
462+ unput(c);
463+ bp += runetochar(bp, n);
464+ break;
465+ }
466+
467+ default:
468+ *bp++ = c;
469+ break;
470+ }
471+ break;
472+ default:
473+ *bp++ = c;
474+ break;
475+ }
476+ }
477+ *bp = 0;
478+ s = tostring(buf);
479+ *bp++ = ' '; *bp++ = '\0';
480+ yylval.cp = setsymtab(buf, s, 0.0, CON|STR|DONTFREE, symtab);
481+ free(s);
482+ RET(STRING);
483+}
484+
485+
486+static int binsearch(char *w, const Keyword *kp, int n)
487+{
488+ int cond, low, mid, high;
489+
490+ low = 0;
491+ high = n - 1;
492+ while (low <= high) {
493+ mid = (low + high) / 2;
494+ if ((cond = strcmp(w, kp[mid].word)) < 0)
495+ high = mid - 1;
496+ else if (cond > 0)
497+ low = mid + 1;
498+ else
499+ return mid;
500+ }
501+ return -1;
502+}
503+
504+int word(char *w)
505+{
506+ const Keyword *kp;
507+ int c, n;
508+
509+ n = binsearch(w, keywords, sizeof(keywords)/sizeof(keywords[0]));
510+ if (n != -1) { /* found in table */
511+ kp = keywords + n;
512+ yylval.i = kp->sub;
513+ switch (kp->type) { /* special handling */
514+ case BLTIN:
515+ if (kp->sub == FSYSTEM && safe)
516+ SYNTAX( "system is unsafe" );
517+ RET(kp->type);
518+ case FUNC:
519+ if (infunc)
520+ SYNTAX( "illegal nested function" );
521+ RET(kp->type);
522+ case RETURN:
523+ if (!infunc)
524+ SYNTAX( "return not in function" );
525+ RET(kp->type);
526+ case VARNF:
527+ yylval.cp = setsymtab("NF", "", 0.0, NUM, symtab);
528+ RET(VARNF);
529+ default:
530+ RET(kp->type);
531+ }
532+ }
533+ c = peek(); /* look for '(' */
534+ if (c != '(' && infunc && (n=isarg(w)) >= 0) {
535+ yylval.i = n;
536+ RET(ARG);
537+ } else {
538+ yylval.cp = setsymtab(w, "", 0.0, STR|NUM|DONTFREE, symtab);
539+ if (c == '(') {
540+ RET(CALL);
541+ } else {
542+ RET(VAR);
543+ }
544+ }
545+}
546+
547+void startreg(void) /* next call to yylex will return a regular expression */
548+{
549+ reg = true;
550+}
551+
552+int regexpr(void)
553+{
554+ int c;
555+ static char *buf = NULL;
556+ static int bufsz = 500;
557+ char *bp;
558+
559+ if (buf == NULL && (buf = (char *) malloc(bufsz)) == NULL)
560+ FATAL("out of space for reg expr");
561+ bp = buf;
562+ for ( ; (c = input()) != '/' && c != 0; ) {
563+ if (!adjbuf(&buf, &bufsz, bp-buf+3, 500, &bp, "regexpr"))
564+ FATAL("out of space for reg expr %.10s...", buf);
565+ if (c == '\n') {
566+ *bp = '\0';
567+ SYNTAX( "newline in regular expression %.10s...", buf );
568+ unput('\n');
569+ break;
570+ } else if (c == '\\') {
571+ *bp++ = '\\';
572+ *bp++ = input();
573+ } else {
574+ *bp++ = c;
575+ }
576+ }
577+ *bp = 0;
578+ if (c == 0)
579+ SYNTAX("non-terminated regular expression %.10s...", buf);
580+ yylval.s = tostring(buf);
581+ unput('/');
582+ RET(REGEXPR);
583+}
584+
585+/* low-level lexical stuff, sort of inherited from lex */
586+
587+char ebuf[300];
588+char *ep = ebuf;
589+char yysbuf[100]; /* pushback buffer */
590+char *yysptr = yysbuf;
591+FILE *yyin = NULL;
592+
593+int input(void) /* get next lexical input character */
594+{
595+ int c;
596+ extern char *lexprog;
597+
598+ if (yysptr > yysbuf)
599+ c = (uschar)*--yysptr;
600+ else if (lexprog != NULL) { /* awk '...' */
601+ if ((c = (uschar)*lexprog) != 0)
602+ lexprog++;
603+ } else /* awk -f ... */
604+ c = pgetc();
605+ if (c == EOF)
606+ c = 0;
607+ if (ep >= ebuf + sizeof ebuf)
608+ ep = ebuf;
609+ *ep = c;
610+ if (c != 0) {
611+ ep++;
612+ }
613+ return (c);
614+}
615+
616+void unput(int c) /* put lexical character back on input */
617+{
618+ if (yysptr >= yysbuf + sizeof(yysbuf))
619+ FATAL("pushed back too much: %.20s...", yysbuf);
620+ *yysptr++ = c;
621+ if (--ep < ebuf)
622+ ep = ebuf + sizeof(ebuf) - 1;
623+}
624+
625+void unputstr(const char *s) /* put a string back on input */
626+{
627+ int i;
628+
629+ for (i = strlen(s)-1; i >= 0; i--)
630+ unput(s[i]);
631+}
+921,
-0
1@@ -0,0 +1,921 @@
2+/****************************************************************
3+Copyright (C) Lucent Technologies 1997
4+All Rights Reserved
5+
6+Permission to use, copy, modify, and distribute this software and
7+its documentation for any purpose and without fee is hereby
8+granted, provided that the above copyright notice appear in all
9+copies and that both that the copyright notice and this
10+permission notice and warranty disclaimer appear in supporting
11+documentation, and that the name Lucent Technologies or any of
12+its entities not be used in advertising or publicity pertaining
13+to distribution of the software without specific, written prior
14+permission.
15+
16+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
18+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
19+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
21+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
22+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
23+THIS SOFTWARE.
24+****************************************************************/
25+
26+#define DEBUG
27+#include <stdio.h>
28+#include <string.h>
29+#include <strings.h>
30+#include <ctype.h>
31+#include <errno.h>
32+#include <stdlib.h>
33+#include <stdarg.h>
34+#include <limits.h>
35+#include <math.h>
36+#include "awk.h"
37+
38+extern int u8_nextlen(const char *s);
39+
40+char EMPTY[] = { '\0' };
41+FILE *infile = NULL;
42+bool innew; /* true = infile has not been read by readrec */
43+char *file = EMPTY;
44+char *record;
45+int recsize = RECSIZE;
46+char *fields;
47+int fieldssize = RECSIZE;
48+
49+Cell **fldtab; /* pointers to Cells */
50+static size_t len_inputFS = 0;
51+static char *inputFS = NULL; /* FS at time of input, for field splitting */
52+
53+#define MAXFLD 2
54+int nfields = MAXFLD; /* last allocated slot for $i */
55+
56+bool donefld; /* true = implies rec broken into fields */
57+bool donerec; /* true = record is valid (no flds have changed) */
58+
59+int lastfld = 0; /* last used field */
60+int argno = 1; /* current input argument number */
61+extern Awkfloat *ARGC;
62+
63+static Cell dollar0 = { OCELL, CFLD, NULL, EMPTY, 0.0, REC|STR|DONTFREE, NULL, NULL };
64+static Cell dollar1 = { OCELL, CFLD, NULL, EMPTY, 0.0, FLD|STR|DONTFREE, NULL, NULL };
65+
66+void recinit(unsigned int n)
67+{
68+ if ( (record = (char *) malloc(n)) == NULL
69+ || (fields = (char *) malloc(n+1)) == NULL
70+ || (fldtab = (Cell **) calloc(nfields+2, sizeof(*fldtab))) == NULL
71+ || (fldtab[0] = (Cell *) malloc(sizeof(**fldtab))) == NULL)
72+ FATAL("out of space for $0 and fields");
73+ *record = '\0';
74+ *fldtab[0] = dollar0;
75+ fldtab[0]->sval = record;
76+ fldtab[0]->nval = tostring("0");
77+ makefields(1, nfields);
78+}
79+
80+void makefields(int n1, int n2) /* create $n1..$n2 inclusive */
81+{
82+ char temp[50];
83+ int i;
84+
85+ for (i = n1; i <= n2; i++) {
86+ fldtab[i] = (Cell *) malloc(sizeof(**fldtab));
87+ if (fldtab[i] == NULL)
88+ FATAL("out of space in makefields %d", i);
89+ *fldtab[i] = dollar1;
90+ snprintf(temp, sizeof(temp), "%d", i);
91+ fldtab[i]->nval = tostring(temp);
92+ }
93+}
94+
95+void initgetrec(void)
96+{
97+ int i;
98+ char *p;
99+
100+ for (i = 1; i < *ARGC; i++) {
101+ p = getargv(i); /* find 1st real filename */
102+ if (p == NULL || *p == '\0') { /* deleted or zapped */
103+ argno++;
104+ continue;
105+ }
106+ if (!isclvar(p)) {
107+ setsval(lookup("FILENAME", symtab), p);
108+ return;
109+ }
110+ setclvar(p); /* a commandline assignment before filename */
111+ argno++;
112+ }
113+ infile = stdin; /* no filenames, so use stdin */
114+ innew = true;
115+}
116+
117+/*
118+ * POSIX specifies that fields are supposed to be evaluated as if they were
119+ * split using the value of FS at the time that the record's value ($0) was
120+ * read.
121+ *
122+ * Since field-splitting is done lazily, we save the current value of FS
123+ * whenever a new record is read in (implicitly or via getline), or when
124+ * a new value is assigned to $0.
125+ */
126+void savefs(void)
127+{
128+ size_t len;
129+ if ((len = strlen(getsval(fsloc))) < len_inputFS) {
130+ strcpy(inputFS, *FS); /* for subsequent field splitting */
131+ return;
132+ }
133+
134+ len_inputFS = len + 1;
135+ inputFS = (char *) realloc(inputFS, len_inputFS);
136+ if (inputFS == NULL)
137+ FATAL("field separator %.10s... is too long", *FS);
138+ memcpy(inputFS, *FS, len_inputFS);
139+}
140+
141+static bool firsttime = true;
142+
143+int getrec(char **pbuf, int *pbufsize, bool isrecord) /* get next input record */
144+{ /* note: cares whether buf == record */
145+ int c;
146+ char *buf = *pbuf;
147+ uschar saveb0;
148+ int bufsize = *pbufsize, savebufsize = bufsize;
149+
150+ if (firsttime) {
151+ firsttime = false;
152+ initgetrec();
153+ }
154+ DPRINTF("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
155+ *RS, *FS, *ARGC, *FILENAME);
156+ saveb0 = buf[0];
157+ buf[0] = 0;
158+ while (argno < *ARGC || infile == stdin) {
159+ DPRINTF("argno=%d, file=|%s|\n", argno, file);
160+ if (infile == NULL) { /* have to open a new file */
161+ file = getargv(argno);
162+ if (file == NULL || *file == '\0') { /* deleted or zapped */
163+ argno++;
164+ continue;
165+ }
166+ if (isclvar(file)) { /* a var=value arg */
167+ setclvar(file);
168+ argno++;
169+ continue;
170+ }
171+ *FILENAME = file;
172+ DPRINTF("opening file %s\n", file);
173+ if (*file == '-' && *(file+1) == '\0')
174+ infile = stdin;
175+ else if ((infile = fopen(file, "r")) == NULL)
176+ FATAL("can't open file %s", file);
177+ innew = true;
178+ setfval(fnrloc, 0.0);
179+ }
180+ c = readrec(&buf, &bufsize, infile, innew);
181+ if (innew)
182+ innew = false;
183+ if (c != 0 || buf[0] != '\0') { /* normal record */
184+ if (isrecord) {
185+ double result;
186+
187+ if (freeable(fldtab[0]))
188+ xfree(fldtab[0]->sval);
189+ fldtab[0]->sval = buf; /* buf == record */
190+ fldtab[0]->tval = REC | STR | DONTFREE;
191+ if (is_number(fldtab[0]->sval, & result)) {
192+ fldtab[0]->fval = result;
193+ fldtab[0]->tval |= NUM;
194+ }
195+ donefld = false;
196+ donerec = true;
197+ savefs();
198+ }
199+ setfval(nrloc, nrloc->fval+1);
200+ setfval(fnrloc, fnrloc->fval+1);
201+ *pbuf = buf;
202+ *pbufsize = bufsize;
203+ return 1;
204+ }
205+ /* EOF arrived on this file; set up next */
206+ if (infile != stdin)
207+ fclose(infile);
208+ infile = NULL;
209+ argno++;
210+ }
211+ buf[0] = saveb0;
212+ *pbuf = buf;
213+ *pbufsize = savebufsize;
214+ return 0; /* true end of file */
215+}
216+
217+void nextfile(void)
218+{
219+ if (infile != NULL && infile != stdin)
220+ fclose(infile);
221+ infile = NULL;
222+ argno++;
223+}
224+
225+extern int readcsvrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag);
226+
227+int readrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag) /* read one record into buf */
228+{
229+ int sep, c, isrec; // POTENTIAL BUG? isrec is a macro in awk.h
230+ char *rr = *pbuf, *buf = *pbuf;
231+ int bufsize = *pbufsize;
232+ char *rs = getsval(rsloc);
233+
234+ if (CSV) {
235+ c = readcsvrec(&buf, &bufsize, inf, newflag);
236+ isrec = (c == EOF && rr == buf) ? false : true;
237+ } else if (*rs && rs[1]) {
238+ bool found;
239+
240+ memset(buf, 0, bufsize);
241+ fa *pfa = makedfa(rs, 1);
242+ if (newflag)
243+ found = fnematch(pfa, inf, &buf, &bufsize, recsize);
244+ else {
245+ int tempstat = pfa->initstat;
246+ pfa->initstat = 2;
247+ found = fnematch(pfa, inf, &buf, &bufsize, recsize);
248+ pfa->initstat = tempstat;
249+ }
250+ if (found)
251+ setptr(patbeg, '\0');
252+ isrec = (found == 0 && *buf == '\0') ? false : true;
253+
254+ } else {
255+ if ((sep = *rs) == 0) {
256+ sep = '\n';
257+ while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */
258+ ;
259+ if (c != EOF)
260+ ungetc(c, inf);
261+ }
262+ for (rr = buf; ; ) {
263+ for (; (c=getc(inf)) != sep && c != EOF; ) {
264+ if (rr-buf+1 > bufsize)
265+ if (!adjbuf(&buf, &bufsize, 1+rr-buf,
266+ recsize, &rr, "readrec 1"))
267+ FATAL("input record `%.30s...' too long", buf);
268+ *rr++ = c;
269+ }
270+ if (*rs == sep || c == EOF)
271+ break;
272+ if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
273+ break;
274+ if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr,
275+ "readrec 2"))
276+ FATAL("input record `%.30s...' too long", buf);
277+ *rr++ = '\n';
278+ *rr++ = c;
279+ }
280+ if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
281+ FATAL("input record `%.30s...' too long", buf);
282+ *rr = 0;
283+ isrec = (c == EOF && rr == buf) ? false : true;
284+ }
285+ *pbuf = buf;
286+ *pbufsize = bufsize;
287+ DPRINTF("readrec saw <%s>, returns %d\n", buf, isrec);
288+ return isrec;
289+}
290+
291+
292+/*******************
293+ * loose ends here:
294+ * \r\n should become \n
295+ * what about bare \r? Excel uses that for embedded newlines
296+ * can't have "" in unquoted fields, according to RFC 4180
297+*/
298+
299+
300+int readcsvrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag) /* csv can have \n's */
301+{ /* so read a complete record that might be multiple lines */
302+ int sep, c;
303+
304+ (void)newflag;
305+ char *rr = *pbuf, *buf = *pbuf;
306+ int bufsize = *pbufsize;
307+ bool in_quote = false;
308+
309+ sep = '\n'; /* the only separator; have to skip over \n embedded in "..." */
310+ rr = buf;
311+ while ((c = getc(inf)) != EOF) {
312+ if (c == sep) {
313+ if (! in_quote)
314+ break;
315+ if (rr > buf && rr[-1] == '\r') // remove \r if was \r\n
316+ rr--;
317+ }
318+
319+ if (rr-buf+1 > bufsize)
320+ if (!adjbuf(&buf, &bufsize, 1+rr-buf,
321+ recsize, &rr, "readcsvrec 1"))
322+ FATAL("input record `%.30s...' too long", buf);
323+ *rr++ = c;
324+ if (c == '"')
325+ in_quote = ! in_quote;
326+ }
327+ if (c == '\n' && rr > buf && rr[-1] == '\r') // remove \r if was \r\n
328+ rr--;
329+
330+ if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readcsvrec 4"))
331+ FATAL("input record `%.30s...' too long", buf);
332+ *rr = 0;
333+ *pbuf = buf;
334+ *pbufsize = bufsize;
335+ DPRINTF("readcsvrec saw <%s>, returns %d\n", buf, c);
336+ return c;
337+}
338+
339+char *getargv(int n) /* get ARGV[n] */
340+{
341+ Array *ap;
342+ Cell *x;
343+ char *s, temp[50];
344+ extern Cell *ARGVcell;
345+
346+ ap = (Array *)ARGVcell->sval;
347+ snprintf(temp, sizeof(temp), "%d", n);
348+ if (lookup(temp, ap) == NULL)
349+ return NULL;
350+ x = setsymtab(temp, "", 0.0, STR, ap);
351+ s = getsval(x);
352+ DPRINTF("getargv(%d) returns |%s|\n", n, s);
353+ return s;
354+}
355+
356+void setclvar(char *s) /* set var=value from s */
357+{
358+ char *e, *p;
359+ Cell *q;
360+ double result;
361+
362+/* commit f3d9187d4e0f02294fb1b0e31152070506314e67 broke T.argv test */
363+/* I don't understand why it was changed. */
364+
365+ for (p=s; *p != '='; p++)
366+ ;
367+ e = p;
368+ *p++ = 0;
369+ p = qstring(p, '\0');
370+ q = setsymtab(s, p, 0.0, STR, symtab);
371+ setsval(q, p);
372+ if (is_number(q->sval, & result)) {
373+ q->fval = result;
374+ q->tval |= NUM;
375+ }
376+ DPRINTF("command line set %s to |%s|\n", s, p);
377+ free(p);
378+ *e = '=';
379+}
380+
381+
382+void fldbld(void) /* create fields from current record */
383+{
384+ /* this relies on having fields[] the same length as $0 */
385+ /* the fields are all stored in this one array with \0's */
386+ /* possibly with a final trailing \0 not associated with any field */
387+ char *r, *fr, sep;
388+ Cell *p;
389+ int i, j, n;
390+
391+ if (donefld)
392+ return;
393+ if (!isstr(fldtab[0]))
394+ getsval(fldtab[0]);
395+ r = fldtab[0]->sval;
396+ n = strlen(r);
397+ if (n > fieldssize) {
398+ xfree(fields);
399+ if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */
400+ FATAL("out of space for fields in fldbld %d", n);
401+ fieldssize = n;
402+ }
403+ fr = fields;
404+ i = 0; /* number of fields accumulated here */
405+ if (inputFS == NULL) /* make sure we have a copy of FS */
406+ savefs();
407+ if (!CSV && strlen(inputFS) > 1) { /* it's a regular expression */
408+ i = refldbld(r, inputFS);
409+ } else if (!CSV && (sep = *inputFS) == ' ') { /* default whitespace */
410+ for (i = 0; ; ) {
411+ while (*r == ' ' || *r == '\t' || *r == '\n')
412+ r++;
413+ if (*r == 0)
414+ break;
415+ i++;
416+ if (i > nfields)
417+ growfldtab(i);
418+ if (freeable(fldtab[i]))
419+ xfree(fldtab[i]->sval);
420+ fldtab[i]->sval = fr;
421+ fldtab[i]->tval = FLD | STR | DONTFREE;
422+ do
423+ *fr++ = *r++;
424+ while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
425+ *fr++ = 0;
426+ }
427+ *fr = 0;
428+ } else if (CSV) { /* CSV processing. no error handling */
429+ if (*r != 0) {
430+ for (;;) {
431+ i++;
432+ if (i > nfields)
433+ growfldtab(i);
434+ if (freeable(fldtab[i]))
435+ xfree(fldtab[i]->sval);
436+ fldtab[i]->sval = fr;
437+ fldtab[i]->tval = FLD | STR | DONTFREE;
438+ if (*r == '"' ) { /* start of "..." */
439+ for (r++ ; *r != '\0'; ) {
440+ if (*r == '"' && r[1] != '\0' && r[1] == '"') {
441+ r += 2; /* doubled quote */
442+ *fr++ = '"';
443+ } else if (*r == '"' && (r[1] == '\0' || r[1] == ',')) {
444+ r++; /* skip over closing quote */
445+ break;
446+ } else {
447+ *fr++ = *r++;
448+ }
449+ }
450+ *fr++ = 0;
451+ } else { /* unquoted field */
452+ while (*r != ',' && *r != '\0')
453+ *fr++ = *r++;
454+ *fr++ = 0;
455+ }
456+ if (*r++ == 0)
457+ break;
458+
459+ }
460+ }
461+ *fr = 0;
462+ } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */
463+ for (i = 0; *r != '\0'; ) {
464+ char buf[10];
465+ i++;
466+ if (i > nfields)
467+ growfldtab(i);
468+ if (freeable(fldtab[i]))
469+ xfree(fldtab[i]->sval);
470+ n = u8_nextlen(r);
471+ for (j = 0; j < n; j++)
472+ buf[j] = *r++;
473+ buf[j] = '\0';
474+ fldtab[i]->sval = tostring(buf);
475+ fldtab[i]->tval = FLD | STR;
476+ }
477+ *fr = 0;
478+ } else if (*r != 0) { /* if 0, it's a null field */
479+ /* subtle case: if length(FS) == 1 && length(RS > 0)
480+ * \n is NOT a field separator (cf awk book 61,84).
481+ * this variable is tested in the inner while loop.
482+ */
483+ int rtest = '\n'; /* normal case */
484+ if (strlen(*RS) > 0)
485+ rtest = '\0';
486+ for (;;) {
487+ i++;
488+ if (i > nfields)
489+ growfldtab(i);
490+ if (freeable(fldtab[i]))
491+ xfree(fldtab[i]->sval);
492+ fldtab[i]->sval = fr;
493+ fldtab[i]->tval = FLD | STR | DONTFREE;
494+ while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */
495+ *fr++ = *r++;
496+ *fr++ = 0;
497+ if (*r++ == 0)
498+ break;
499+ }
500+ *fr = 0;
501+ }
502+ if (i > nfields)
503+ FATAL("record `%.30s...' has too many fields; can't happen", r);
504+ cleanfld(i+1, lastfld); /* clean out junk from previous record */
505+ lastfld = i;
506+ donefld = true;
507+ for (j = 1; j <= lastfld; j++) {
508+ double result;
509+
510+ p = fldtab[j];
511+ if(is_number(p->sval, & result)) {
512+ p->fval = result;
513+ p->tval |= NUM;
514+ }
515+ }
516+ setfval(nfloc, (Awkfloat) lastfld);
517+ donerec = true; /* restore */
518+ if (dbg) {
519+ for (j = 0; j <= lastfld; j++) {
520+ p = fldtab[j];
521+ printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
522+ }
523+ }
524+}
525+
526+void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */
527+{ /* nvals remain intact */
528+ Cell *p;
529+ int i;
530+
531+ for (i = n1; i <= n2; i++) {
532+ p = fldtab[i];
533+ if (freeable(p))
534+ xfree(p->sval);
535+ p->sval = EMPTY,
536+ p->tval = FLD | STR | DONTFREE;
537+ }
538+}
539+
540+void newfld(int n) /* add field n after end of existing lastfld */
541+{
542+ if (n > nfields)
543+ growfldtab(n);
544+ cleanfld(lastfld+1, n);
545+ lastfld = n;
546+ setfval(nfloc, (Awkfloat) n);
547+}
548+
549+void setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */
550+{
551+ if (n < 0)
552+ FATAL("cannot set NF to a negative value");
553+ if (n > nfields)
554+ growfldtab(n);
555+
556+ if (lastfld < n)
557+ cleanfld(lastfld+1, n);
558+ else
559+ cleanfld(n+1, lastfld);
560+
561+ lastfld = n;
562+}
563+
564+Cell *fieldadr(int n) /* get nth field */
565+{
566+ if (n < 0)
567+ FATAL("trying to access out of range field %d", n);
568+ if (n > nfields) /* fields after NF are empty */
569+ growfldtab(n); /* but does not increase NF */
570+ return(fldtab[n]);
571+}
572+
573+void growfldtab(int n) /* make new fields up to at least $n */
574+{
575+ int nf = 2 * nfields;
576+ size_t s;
577+
578+ if (n > nf)
579+ nf = n;
580+ s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */
581+ if (s / sizeof(struct Cell *) - 1 == (size_t)nf) /* didn't overflow */
582+ fldtab = (Cell **) realloc(fldtab, s);
583+ else /* overflow sizeof int */
584+ xfree(fldtab); /* make it null */
585+ if (fldtab == NULL)
586+ FATAL("out of space creating %d fields", nf);
587+ makefields(nfields+1, nf);
588+ nfields = nf;
589+}
590+
591+int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */
592+{
593+ /* this relies on having fields[] the same length as $0 */
594+ /* the fields are all stored in this one array with \0's */
595+ char *fr;
596+ int i, tempstat, n;
597+ fa *pfa;
598+
599+ n = strlen(rec);
600+ if (n > fieldssize) {
601+ xfree(fields);
602+ if ((fields = (char *) malloc(n+1)) == NULL)
603+ FATAL("out of space for fields in refldbld %d", n);
604+ fieldssize = n;
605+ }
606+ fr = fields;
607+ *fr = '\0';
608+ if (*rec == '\0')
609+ return 0;
610+ pfa = makedfa(fs, 1);
611+ DPRINTF("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs);
612+ tempstat = pfa->initstat;
613+ for (i = 1; ; i++) {
614+ if (i > nfields)
615+ growfldtab(i);
616+ if (freeable(fldtab[i]))
617+ xfree(fldtab[i]->sval);
618+ fldtab[i]->tval = FLD | STR | DONTFREE;
619+ fldtab[i]->sval = fr;
620+ DPRINTF("refldbld: i=%d\n", i);
621+ if (nematch(pfa, rec)) {
622+ pfa->initstat = 2; /* horrible coupling to b.c */
623+ DPRINTF("match %s (%d chars)\n", patbeg, patlen);
624+ strncpy(fr, rec, patbeg-rec);
625+ fr += patbeg - rec + 1;
626+ *(fr-1) = '\0';
627+ rec = patbeg + patlen;
628+ } else {
629+ DPRINTF("no match %s\n", rec);
630+ strcpy(fr, rec);
631+ pfa->initstat = tempstat;
632+ break;
633+ }
634+ }
635+ return i;
636+}
637+
638+void recbld(void) /* create $0 from $1..$NF if necessary */
639+{
640+ int i;
641+ char *r, *p;
642+ char *sep = getsval(ofsloc);
643+
644+ if (donerec)
645+ return;
646+ r = record;
647+ for (i = 1; i <= *NF; i++) {
648+ p = getsval(fldtab[i]);
649+ if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
650+ FATAL("created $0 `%.30s...' too long", record);
651+ while ((*r = *p++) != 0)
652+ r++;
653+ if (i < *NF) {
654+ if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2"))
655+ FATAL("created $0 `%.30s...' too long", record);
656+ for (p = sep; (*r = *p++) != 0; )
657+ r++;
658+ }
659+ }
660+ if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
661+ FATAL("built giant record `%.30s...'", record);
662+ *r = '\0';
663+ DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
664+
665+ if (freeable(fldtab[0]))
666+ xfree(fldtab[0]->sval);
667+ fldtab[0]->tval = REC | STR | DONTFREE;
668+ fldtab[0]->sval = record;
669+
670+ DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
671+ DPRINTF("recbld = |%s|\n", record);
672+ donerec = true;
673+}
674+
675+int errorflag = 0;
676+
677+void yyerror(const char *s)
678+{
679+ SYNTAX("%s", s);
680+}
681+
682+void SYNTAX(const char *fmt, ...)
683+{
684+ extern char *cmdname, *curfname;
685+ static int been_here = 0;
686+ va_list varg;
687+
688+ if (been_here++ > 2)
689+ return;
690+ fprintf(stderr, "%s: ", cmdname);
691+ va_start(varg, fmt);
692+ vfprintf(stderr, fmt, varg);
693+ va_end(varg);
694+ fprintf(stderr, " at source line %d", lineno);
695+ if (curfname != NULL)
696+ fprintf(stderr, " in function %s", curfname);
697+ if (compile_time == COMPILING && cursource() != NULL)
698+ fprintf(stderr, " source file %s", cursource());
699+ fprintf(stderr, "\n");
700+ errorflag = 2;
701+ eprint();
702+}
703+
704+extern int bracecnt, brackcnt, parencnt;
705+
706+void bracecheck(void)
707+{
708+ int c;
709+ static int beenhere = 0;
710+
711+ if (beenhere++)
712+ return;
713+ while ((c = input()) != EOF && c != '\0')
714+ bclass(c);
715+ bcheck2(bracecnt, '{', '}');
716+ bcheck2(brackcnt, '[', ']');
717+ bcheck2(parencnt, '(', ')');
718+}
719+
720+void bcheck2(int n, int c1, int c2)
721+{
722+ (void)c1;
723+ if (n == 1)
724+ fprintf(stderr, "\tmissing %c\n", c2);
725+ else if (n > 1)
726+ fprintf(stderr, "\t%d missing %c's\n", n, c2);
727+ else if (n == -1)
728+ fprintf(stderr, "\textra %c\n", c2);
729+ else if (n < -1)
730+ fprintf(stderr, "\t%d extra %c's\n", -n, c2);
731+}
732+
733+void FATAL(const char *fmt, ...)
734+{
735+ extern char *cmdname;
736+ va_list varg;
737+
738+ fflush(stdout);
739+ fprintf(stderr, "%s: ", cmdname);
740+ va_start(varg, fmt);
741+ vfprintf(stderr, fmt, varg);
742+ va_end(varg);
743+ error();
744+ if (dbg > 1) /* core dump if serious debugging on */
745+ abort();
746+ exit(2);
747+}
748+
749+void WARNING(const char *fmt, ...)
750+{
751+ extern char *cmdname;
752+ va_list varg;
753+
754+ fflush(stdout);
755+ fprintf(stderr, "%s: ", cmdname);
756+ va_start(varg, fmt);
757+ vfprintf(stderr, fmt, varg);
758+ va_end(varg);
759+ error();
760+}
761+
762+void error()
763+{
764+ extern Node *curnode;
765+
766+ fprintf(stderr, "\n");
767+ if (compile_time != ERROR_PRINTING) {
768+ if (NR && *NR > 0) {
769+ fprintf(stderr, " input record number %d", (int) (*FNR));
770+ if (strcmp(*FILENAME, "-") != 0)
771+ fprintf(stderr, ", file %s", *FILENAME);
772+ fprintf(stderr, "\n");
773+ }
774+ if (curnode)
775+ fprintf(stderr, " source line number %d", curnode->lineno);
776+ else if (lineno)
777+ fprintf(stderr, " source line number %d", lineno);
778+ if (compile_time == COMPILING && cursource() != NULL)
779+ fprintf(stderr, " source file %s", cursource());
780+ fprintf(stderr, "\n");
781+ eprint();
782+ }
783+}
784+
785+void eprint(void) /* try to print context around error */
786+{
787+ char *p, *q;
788+ int c;
789+ static int been_here = 0;
790+ extern char ebuf[], *ep;
791+
792+ if (compile_time != COMPILING || been_here++ > 0 || ebuf == ep)
793+ return;
794+ if (ebuf == ep)
795+ return;
796+ p = ep - 1;
797+ if (p > ebuf && *p == '\n')
798+ p--;
799+ for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
800+ ;
801+ while (*p == '\n')
802+ p++;
803+ fprintf(stderr, " context is\n\t");
804+ for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
805+ ;
806+ for ( ; p < q; p++)
807+ if (*p)
808+ putc(*p, stderr);
809+ fprintf(stderr, " >>> ");
810+ for ( ; p < ep; p++)
811+ if (*p)
812+ putc(*p, stderr);
813+ fprintf(stderr, " <<< ");
814+ if (*ep)
815+ while ((c = input()) != '\n' && c != '\0' && c != EOF) {
816+ putc(c, stderr);
817+ bclass(c);
818+ }
819+ putc('\n', stderr);
820+ ep = ebuf;
821+}
822+
823+void bclass(int c)
824+{
825+ switch (c) {
826+ case '{': bracecnt++; break;
827+ case '}': bracecnt--; break;
828+ case '[': brackcnt++; break;
829+ case ']': brackcnt--; break;
830+ case '(': parencnt++; break;
831+ case ')': parencnt--; break;
832+ }
833+}
834+
835+int isclvar(const char *s) /* is s of form var=something ? */
836+{
837+ const char *os = s;
838+
839+ if (!isalpha((int) *s) && *s != '_')
840+ return 0;
841+ for ( ; *s; s++)
842+ if (!(isalnum((int) *s) || *s == '_'))
843+ break;
844+ return *s == '=' && s > os;
845+}
846+
847+/* strtod is supposed to be a proper test of what's a valid number */
848+/* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
849+/* wrong: violates 4.10.1.4 of ansi C standard */
850+
851+/* well, not quite. As of C99, hex floating point is allowed. so this is
852+ * a bit of a mess. We work around the mess by checking for a hexadecimal
853+ * value and disallowing it. Similarly, we now follow gawk and allow only
854+ * +nan, -nan, +inf, and -inf for NaN and infinity values.
855+ */
856+
857+/*
858+ * This routine now has a more complicated interface, the main point
859+ * being to avoid the double conversion of a string to double, and
860+ * also to convey out, if requested, the information that the numeric
861+ * value was a leading string or is all of the string. The latter bit
862+ * is used in getfval().
863+ */
864+
865+bool is_valid_number(const char *s, bool trailing_stuff_ok,
866+ bool *no_trailing, double *result)
867+{
868+ double r;
869+ char *ep;
870+ bool retval = false;
871+ bool is_nan = false;
872+ bool is_inf = false;
873+
874+ if (no_trailing)
875+ *no_trailing = false;
876+
877+ while (isspace((int) *s))
878+ s++;
879+
880+ /* no hex floating point, sorry */
881+ if (s[0] == '0' && tolower(s[1]) == 'x' && isxdigit(s[2]))
882+ return false;
883+
884+ /* allow +nan, -nan, +inf, -inf, any other letter, no */
885+ if (s[0] == '+' || s[0] == '-') {
886+ is_nan = (strncasecmp(s+1, "nan", 3) == 0);
887+ is_inf = (strncasecmp(s+1, "inf", 3) == 0);
888+ if ((is_nan || is_inf)
889+ && (isspace((int) s[4]) || s[4] == '\0'))
890+ goto convert;
891+ else if (! isdigit(s[1]) && s[1] != '.')
892+ return false;
893+ }
894+ else if (! isdigit(s[0]) && s[0] != '.')
895+ return false;
896+
897+convert:
898+ errno = 0;
899+ r = strtod(s, &ep);
900+ if (ep == s || errno == ERANGE)
901+ return false;
902+
903+ if (isnan(r) && s[0] == '-' && signbit(r) == 0)
904+ r = -r;
905+
906+ if (result != NULL)
907+ *result = r;
908+
909+ /*
910+ * check for trailing stuff
911+ */
912+ while (isspace((int) *ep))
913+ ep++;
914+
915+ if (no_trailing != NULL)
916+ *no_trailing = (*ep == '\0');
917+
918+ /* return true if found the end, or trailing stuff is allowed */
919+ retval = *ep == '\0' || trailing_stuff_ok;
920+
921+ return retval;
922+}
+296,
-0
1@@ -0,0 +1,296 @@
2+/****************************************************************
3+Copyright (C) Lucent Technologies 1997
4+All Rights Reserved
5+
6+Permission to use, copy, modify, and distribute this software and
7+its documentation for any purpose and without fee is hereby
8+granted, provided that the above copyright notice appear in all
9+copies and that both that the copyright notice and this
10+permission notice and warranty disclaimer appear in supporting
11+documentation, and that the name Lucent Technologies or any of
12+its entities not be used in advertising or publicity pertaining
13+to distribution of the software without specific, written prior
14+permission.
15+
16+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
18+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
19+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
21+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
22+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
23+THIS SOFTWARE.
24+****************************************************************/
25+
26+const char *version = "version 20260426";
27+
28+#define DEBUG
29+#include <stdio.h>
30+#include <ctype.h>
31+#include <locale.h>
32+#include <stdlib.h>
33+#include <string.h>
34+#include <signal.h>
35+#include "awk.h"
36+
37+extern char **environ;
38+extern int nfields;
39+
40+int dbg = 0;
41+Awkfloat srand_seed = 1;
42+char *cmdname; /* gets argv[0] for error messages */
43+extern FILE *yyin; /* lex input file */
44+char *lexprog; /* points to program argument if it exists */
45+extern int errorflag; /* non-zero if any syntax errors; set by yyerror */
46+enum compile_states compile_time = ERROR_PRINTING;
47+
48+static char **pfile; /* program filenames from -f's */
49+static size_t maxpfile; /* max program filename */
50+static size_t npfile; /* number of filenames */
51+static size_t curpfile; /* current filename */
52+
53+bool CSV = false; /* true for csv input */
54+
55+bool safe = false; /* true => "safe" mode */
56+
57+size_t awk_mb_cur_max = 1;
58+
59+static noreturn void fpecatch(int n
60+#ifdef SA_SIGINFO
61+ , siginfo_t *si, void *uc
62+#endif
63+)
64+{
65+ (void)n;
66+#ifdef SA_SIGINFO
67+ (void)uc;
68+#endif
69+#ifdef SA_SIGINFO
70+ const char *mesg = NULL;
71+
72+ switch (si->si_code) {
73+ case FPE_INTDIV:
74+ mesg = "Integer divide by zero";
75+ break;
76+ case FPE_INTOVF:
77+ mesg = "Integer overflow";
78+ break;
79+ case FPE_FLTDIV:
80+ mesg = "Floating point divide by zero";
81+ break;
82+ case FPE_FLTOVF:
83+ mesg = "Floating point overflow";
84+ break;
85+ case FPE_FLTUND:
86+ mesg = "Floating point underflow";
87+ break;
88+ case FPE_FLTRES:
89+ mesg = "Floating point inexact result";
90+ break;
91+ case FPE_FLTINV:
92+ mesg = "Invalid Floating point operation";
93+ break;
94+ case FPE_FLTSUB:
95+ mesg = "Subscript out of range";
96+ break;
97+ case 0:
98+ default:
99+ mesg = "Unknown error";
100+ break;
101+ }
102+#endif
103+ FATAL("floating point exception"
104+#ifdef SA_SIGINFO
105+ ": %s", mesg
106+#endif
107+ );
108+}
109+
110+/* Can this work with recursive calls? I don't think so.
111+void segvcatch(int n)
112+{
113+ FATAL("segfault. Do you have an unbounded recursive call?", n);
114+}
115+*/
116+
117+static const char *
118+setfs(char *p)
119+{
120+ /* wart: t=>\t */
121+ if (p[0] == 't' && p[1] == '\0')
122+ return "\t";
123+ return p;
124+}
125+
126+static char *
127+getarg(int *argc, char ***argv, const char *msg)
128+{
129+ if ((*argv)[1][2] != '\0') { /* arg is -fsomething */
130+ return &(*argv)[1][2];
131+ } else { /* arg is -f something */
132+ (*argc)--; (*argv)++;
133+ if (*argc <= 1)
134+ FATAL("%s", msg);
135+ return (*argv)[1];
136+ }
137+}
138+
139+int main(int argc, char *argv[])
140+{
141+ const char *fs = NULL;
142+ char *fn, *vn;
143+
144+ setlocale(LC_CTYPE, "");
145+ setlocale(LC_NUMERIC, "C"); /* for parsing cmdline & prog */
146+ awk_mb_cur_max = MB_CUR_MAX;
147+ cmdname = argv[0];
148+ if (argc == 1) {
149+ fprintf(stderr,
150+ "usage: %s [-F fs | --csv] [-v var=value] [-f progfile | 'prog'] [file ...]\n",
151+ cmdname);
152+ exit(1);
153+ }
154+#ifdef SA_SIGINFO
155+ {
156+ struct sigaction sa;
157+ sa.sa_sigaction = fpecatch;
158+ sa.sa_flags = SA_SIGINFO;
159+ sigemptyset(&sa.sa_mask);
160+ (void)sigaction(SIGFPE, &sa, NULL);
161+ }
162+#else
163+ (void)signal(SIGFPE, fpecatch);
164+#endif
165+ /*signal(SIGSEGV, segvcatch); experiment */
166+
167+ /* Set and keep track of the random seed */
168+ srand_seed = 1;
169+ srandom((unsigned long) srand_seed);
170+
171+ yyin = NULL;
172+ symtab = makesymtab(NSYMTAB/NSYMTAB);
173+ while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') {
174+ if (strcmp(argv[1], "-version") == 0 || strcmp(argv[1], "--version") == 0) {
175+ printf("awk %s\n", version);
176+ return 0;
177+ }
178+ if (strcmp(argv[1], "--") == 0) { /* explicit end of args */
179+ argc--;
180+ argv++;
181+ break;
182+ }
183+ if (strcmp(argv[1], "--csv") == 0) { /* turn on csv input processing */
184+ CSV = true;
185+ argc--;
186+ argv++;
187+ continue;
188+ }
189+ switch (argv[1][1]) {
190+ case 's':
191+ if (strcmp(argv[1], "-safe") == 0)
192+ safe = true;
193+ break;
194+ case 'f': /* next argument is program filename */
195+ fn = getarg(&argc, &argv, "no program filename");
196+ if (npfile >= maxpfile) {
197+ maxpfile += 20;
198+ pfile = (char **) realloc(pfile, maxpfile * sizeof(*pfile));
199+ if (pfile == NULL)
200+ FATAL("error allocating space for -f options");
201+ }
202+ pfile[npfile++] = fn;
203+ break;
204+ case 'F': /* set field separator */
205+ fs = setfs(getarg(&argc, &argv, "no field separator"));
206+ break;
207+ case 'v': /* -v a=1 to be done NOW. one -v for each */
208+ vn = getarg(&argc, &argv, "no variable name");
209+ if (isclvar(vn))
210+ setclvar(vn);
211+ else
212+ FATAL("invalid -v option argument: %s", vn);
213+ break;
214+ case 'd':
215+ dbg = atoi(&argv[1][2]);
216+ if (dbg == 0)
217+ dbg = 1;
218+ printf("awk %s\n", version);
219+ break;
220+ default:
221+ WARNING("unknown option %s ignored", argv[1]);
222+ break;
223+ }
224+ argc--;
225+ argv++;
226+ }
227+
228+ if (CSV && (fs != NULL || lookup("FS", symtab) != NULL))
229+ WARNING("danger: don't set FS when --csv is in effect");
230+
231+ /* argv[1] is now the first argument */
232+ if (npfile == 0) { /* no -f; first argument is program */
233+ if (argc <= 1) {
234+ if (dbg)
235+ exit(0);
236+ FATAL("no program given");
237+ }
238+ DPRINTF("program = |%s|\n", argv[1]);
239+ lexprog = argv[1];
240+ argc--;
241+ argv++;
242+ }
243+ recinit(recsize);
244+ syminit();
245+ compile_time = COMPILING;
246+ argv[0] = cmdname; /* put prog name at front of arglist */
247+ DPRINTF("argc=%d, argv[0]=%s\n", argc, argv[0]);
248+ arginit(argc, argv);
249+ if (!safe)
250+ envinit(environ);
251+ yyparse();
252+#if 0
253+ // Doing this would comply with POSIX, but is not compatible with
254+ // other awks and with what most users expect. So comment it out.
255+ setlocale(LC_NUMERIC, ""); /* back to whatever it is locally */
256+#endif
257+ if (fs)
258+ *FS = qstring(fs, '\0');
259+ DPRINTF("errorflag=%d\n", errorflag);
260+ if (errorflag == 0) {
261+ compile_time = RUNNING;
262+ run(winner);
263+ } else
264+ bracecheck();
265+ return(errorflag);
266+}
267+
268+int pgetc(void) /* get 1 character from awk program */
269+{
270+ int c;
271+
272+ for (;;) {
273+ if (yyin == NULL) {
274+ if (curpfile >= npfile)
275+ return EOF;
276+ if (strcmp(pfile[curpfile], "-") == 0)
277+ yyin = stdin;
278+ else if ((yyin = fopen(pfile[curpfile], "r")) == NULL)
279+ FATAL("can't open file %s", pfile[curpfile]);
280+ lineno = 1;
281+ }
282+ if ((c = getc(yyin)) != EOF)
283+ return c;
284+ if (yyin != stdin)
285+ fclose(yyin);
286+ yyin = NULL;
287+ curpfile++;
288+ }
289+}
290+
291+char *cursource(void) /* current source file name */
292+{
293+ if (npfile > 0)
294+ return pfile[curpfile < npfile ? curpfile : curpfile - 1];
295+ else
296+ return NULL;
297+}
+193,
-0
1@@ -0,0 +1,193 @@
2+/****************************************************************
3+Copyright (C) Lucent Technologies 1997
4+All Rights Reserved
5+
6+Permission to use, copy, modify, and distribute this software and
7+its documentation for any purpose and without fee is hereby
8+granted, provided that the above copyright notice appear in all
9+copies and that both that the copyright notice and this
10+permission notice and warranty disclaimer appear in supporting
11+documentation, and that the name Lucent Technologies or any of
12+its entities not be used in advertising or publicity pertaining
13+to distribution of the software without specific, written prior
14+permission.
15+
16+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
18+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
19+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
21+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
22+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
23+THIS SOFTWARE.
24+****************************************************************/
25+
26+/*
27+ * this program makes the table to link function names
28+ * and type indices that is used by execute() in run.c.
29+ * it finds the indices in awkgram.tab.h, produced by bison.
30+ */
31+
32+#define _POSIX_C_SOURCE 200809L
33+#include <stdio.h>
34+#include <string.h>
35+#include <stdlib.h>
36+#include "awk.h"
37+#include "awkgram.tab.h"
38+
39+struct xx
40+{ int token;
41+ const char *name;
42+ const char *pname;
43+} proc[] = {
44+ { PROGRAM, "program", NULL },
45+ { BOR, "boolop", " || " },
46+ { AND, "boolop", " && " },
47+ { NOT, "boolop", " !" },
48+ { NE, "relop", " != " },
49+ { EQ, "relop", " == " },
50+ { LE, "relop", " <= " },
51+ { LT, "relop", " < " },
52+ { GE, "relop", " >= " },
53+ { GT, "relop", " > " },
54+ { ARRAY, "array", NULL },
55+ { INDIRECT, "indirect", "$(" },
56+ { SUBSTR, "substr", "substr" },
57+ { SUB, "dosub", "sub" },
58+ { GSUB, "dosub", "gsub" },
59+ { INDEX, "sindex", "sindex" },
60+ { SPRINTF, "awksprintf", "sprintf " },
61+ { ADD, "arith", " + " },
62+ { MINUS, "arith", " - " },
63+ { MULT, "arith", " * " },
64+ { DIVIDE, "arith", " / " },
65+ { MOD, "arith", " % " },
66+ { UMINUS, "arith", " -" },
67+ { UPLUS, "arith", " +" },
68+ { POWER, "arith", " **" },
69+ { PREINCR, "incrdecr", "++" },
70+ { POSTINCR, "incrdecr", "++" },
71+ { PREDECR, "incrdecr", "--" },
72+ { POSTDECR, "incrdecr", "--" },
73+ { CAT, "cat", " " },
74+ { PASTAT, "pastat", NULL },
75+ { PASTAT2, "dopa2", NULL },
76+ { MATCH, "matchop", " ~ " },
77+ { NOTMATCH, "matchop", " !~ " },
78+ { MATCHFCN, "matchop", "matchop" },
79+ { INTEST, "intest", "intest" },
80+ { PRINTF, "awkprintf", "printf" },
81+ { PRINT, "printstat", "print" },
82+ { CLOSE, "closefile", "closefile" },
83+ { DELETE, "awkdelete", "awkdelete" },
84+ { SPLIT, "split", "split" },
85+ { ASSIGN, "assign", " = " },
86+ { ADDEQ, "assign", " += " },
87+ { SUBEQ, "assign", " -= " },
88+ { MULTEQ, "assign", " *= " },
89+ { DIVEQ, "assign", " /= " },
90+ { MODEQ, "assign", " %= " },
91+ { POWEQ, "assign", " ^= " },
92+ { CONDEXPR, "condexpr", " ?: " },
93+ { IF, "ifstat", "if(" },
94+ { WHILE, "whilestat", "while(" },
95+ { FOR, "forstat", "for(" },
96+ { DO, "dostat", "do" },
97+ { IN, "instat", "instat" },
98+ { NEXT, "jump", "next" },
99+ { NEXTFILE, "jump", "nextfile" },
100+ { EXIT, "jump", "exit" },
101+ { BREAK, "jump", "break" },
102+ { CONTINUE, "jump", "continue" },
103+ { RETURN, "jump", "ret" },
104+ { BLTIN, "bltin", "bltin" },
105+ { CALL, "call", "call" },
106+ { ARG, "arg", "arg" },
107+ { VARNF, "getnf", "NF" },
108+ { GETLINE, "awkgetline", "getline" },
109+ { 0, "", "" },
110+};
111+
112+#define SIZE (LASTTOKEN - FIRSTTOKEN + 1)
113+const char *table[SIZE];
114+char *names[SIZE];
115+
116+int main(int argc, char *argv[])
117+{
118+ const struct xx *p;
119+ int i, n, tok;
120+ char c;
121+ FILE *fp;
122+ char buf[200], name[200], def[200];
123+ enum { TOK_UNKNOWN, TOK_ENUM, TOK_DEFINE } tokentype = TOK_UNKNOWN;
124+
125+ printf("#include <stdio.h>\n");
126+ printf("#include \"awk.h\"\n");
127+ printf("#include \"awkgram.tab.h\"\n\n");
128+
129+ if (argc != 2) {
130+ fprintf(stderr, "usage: maketab YTAB_H\n");
131+ exit(1);
132+ }
133+ if ((fp = fopen(argv[1], "r")) == NULL) {
134+ fprintf(stderr, "maketab can't open %s!\n", argv[1]);
135+ exit(1);
136+ }
137+ printf("static const char * const printname[%d] = {\n", SIZE);
138+ i = 0;
139+ while (fgets(buf, sizeof buf, fp) != NULL) {
140+ // 199 is sizeof(def) - 1
141+ if (tokentype != TOK_ENUM) {
142+ n = sscanf(buf, "%1c %199s %199s %d", &c, def, name,
143+ &tok);
144+ if (n == 4 && c == '#' && strcmp(def, "define") == 0) {
145+ tokentype = TOK_DEFINE;
146+ } else if (tokentype != TOK_UNKNOWN) {
147+ continue;
148+ }
149+ }
150+ if (tokentype != TOK_DEFINE) {
151+ /* not a valid #define, bison uses enums now */
152+ n = sscanf(buf, "%199s = %d,\n", name, &tok);
153+ if (n != 2)
154+ continue;
155+ tokentype = TOK_ENUM;
156+ }
157+ if (strcmp(name, "YYSTYPE_IS_DECLARED") == 0) {
158+ tokentype = TOK_UNKNOWN;
159+ continue;
160+ }
161+ if (tok < FIRSTTOKEN || tok > LASTTOKEN) {
162+ tokentype = TOK_UNKNOWN;
163+ /* fprintf(stderr, "maketab funny token %d %s ignored\n", tok, buf); */
164+ continue;
165+ }
166+ names[tok-FIRSTTOKEN] = strdup(name);
167+ if (names[tok-FIRSTTOKEN] == NULL) {
168+ fprintf(stderr, "maketab out of space copying %s", name);
169+ continue;
170+ }
171+ printf("\t\"%s\",\t/* %d */\n", name, tok);
172+ i++;
173+ }
174+ printf("};\n\n");
175+
176+ for (p=proc; p->token!=0; p++)
177+ table[p->token-FIRSTTOKEN] = p->name;
178+ printf("\nCell *(*proctab[%d])(Node **, int) = {\n", SIZE);
179+ for (i=0; i<SIZE; i++)
180+ printf("\t%s,\t/* %s */\n",
181+ table[i] ? table[i] : "nullproc", names[i] ? names[i] : "");
182+ printf("};\n\n");
183+
184+ printf("const char *tokname(int n)\n"); /* print a tokname() function */
185+ printf("{\n");
186+ printf("\tstatic char buf[100];\n\n");
187+ printf("\tif (n < FIRSTTOKEN || n > LASTTOKEN) {\n");
188+ printf("\t\tsnprintf(buf, sizeof(buf), \"token %%d\", n);\n");
189+ printf("\t\treturn buf;\n");
190+ printf("\t}\n");
191+ printf("\treturn printname[n-FIRSTTOKEN];\n");
192+ printf("}\n");
193+ return 0;
194+}
+97,
-0
1@@ -0,0 +1,97 @@
2+/****************************************************************
3+Copyright (C) Lucent Technologies 1997
4+All Rights Reserved
5+
6+Permission to use, copy, modify, and distribute this software and
7+its documentation for any purpose and without fee is hereby
8+granted, provided that the above copyright notice appear in all
9+copies and that both that the copyright notice and this
10+permission notice and warranty disclaimer appear in supporting
11+documentation, and that the name Lucent Technologies or any of
12+its entities not be used in advertising or publicity pertaining
13+to distribution of the software without specific, written prior
14+permission.
15+
16+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
18+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
19+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
21+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
22+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
23+THIS SOFTWARE.
24+****************************************************************/
25+
26+#ifdef __GNUC__
27+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
28+#endif
29+#pragma STDC FENV_ACCESS ON
30+
31+#include <errno.h>
32+#include <fenv.h>
33+#include <math.h>
34+#include <stdio.h>
35+
36+#include "awk.h"
37+
38+#ifndef FE_DIVBYZERO
39+#define FE_DIVBYZERO 0
40+#endif
41+
42+#ifndef FE_INVALID
43+#define FE_INVALID 0
44+#endif
45+
46+#ifndef FE_OVERFLOW
47+#define FE_OVERFLOW 0
48+#endif
49+
50+#ifndef FE_UNDERFLOW
51+#define FE_UNDERFLOW 0
52+#endif
53+
54+#define errclear() \
55+ do { \
56+ errno = 0; \
57+ feclearexcept(FE_ALL_EXCEPT); \
58+ } while (0)
59+
60+static double errcheck(double x, const char *s)
61+{
62+ if (errno == EDOM || fetestexcept(FE_INVALID)) {
63+ errno = 0;
64+ WARNING("%s argument out of domain", s);
65+ x = 1;
66+ } else if (errno == ERANGE || fetestexcept(FE_DIVBYZERO | FE_OVERFLOW |
67+ FE_UNDERFLOW)) {
68+ errno = 0;
69+ WARNING("%s result out of range", s);
70+ x = 1;
71+ }
72+
73+ return x;
74+}
75+
76+double exp_errcheck(double x)
77+{
78+ errclear();
79+ return errcheck(exp(x), "exp");
80+}
81+
82+double log_errcheck(double x)
83+{
84+ errclear();
85+ return errcheck(log(x), "log");
86+}
87+
88+double pow_errcheck(double x, double y)
89+{
90+ errclear();
91+ return errcheck(pow(x, y), "pow");
92+}
93+
94+double sqrt_errcheck(double x)
95+{
96+ errclear();
97+ return errcheck(sqrt(x), "sqrt");
98+}
+276,
-0
1@@ -0,0 +1,276 @@
2+/****************************************************************
3+Copyright (C) Lucent Technologies 1997
4+All Rights Reserved
5+
6+Permission to use, copy, modify, and distribute this software and
7+its documentation for any purpose and without fee is hereby
8+granted, provided that the above copyright notice appear in all
9+copies and that both that the copyright notice and this
10+permission notice and warranty disclaimer appear in supporting
11+documentation, and that the name Lucent Technologies or any of
12+its entities not be used in advertising or publicity pertaining
13+to distribution of the software without specific, written prior
14+permission.
15+
16+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
18+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
19+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
21+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
22+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
23+THIS SOFTWARE.
24+****************************************************************/
25+
26+#define DEBUG
27+#include <stdio.h>
28+#include <string.h>
29+#include <stdlib.h>
30+#include "awk.h"
31+#include "awkgram.tab.h"
32+
33+Node *nodealloc(size_t n)
34+{
35+ Node *x;
36+
37+ x = (Node *) malloc(sizeof(*x) + (n-1) * sizeof(x));
38+ if (x == NULL)
39+ FATAL("out of space in nodealloc");
40+ x->nnext = NULL;
41+ x->lineno = lineno;
42+ return(x);
43+}
44+
45+Node *exptostat(Node *a)
46+{
47+ a->ntype = NSTAT;
48+ return(a);
49+}
50+
51+Node *node1(int a, Node *b)
52+{
53+ Node *x;
54+
55+ x = nodealloc(1);
56+ x->nobj = a;
57+ x->narg[0]=b;
58+ return(x);
59+}
60+
61+Node *node2(int a, Node *b, Node *c)
62+{
63+ Node *x;
64+
65+ x = nodealloc(2);
66+ x->nobj = a;
67+ x->narg[0] = b;
68+ x->narg[1] = c;
69+ return(x);
70+}
71+
72+Node *node3(int a, Node *b, Node *c, Node *d)
73+{
74+ Node *x;
75+
76+ x = nodealloc(3);
77+ x->nobj = a;
78+ x->narg[0] = b;
79+ x->narg[1] = c;
80+ x->narg[2] = d;
81+ return(x);
82+}
83+
84+Node *node4(int a, Node *b, Node *c, Node *d, Node *e)
85+{
86+ Node *x;
87+
88+ x = nodealloc(4);
89+ x->nobj = a;
90+ x->narg[0] = b;
91+ x->narg[1] = c;
92+ x->narg[2] = d;
93+ x->narg[3] = e;
94+ return(x);
95+}
96+
97+Node *stat1(int a, Node *b)
98+{
99+ Node *x;
100+
101+ x = node1(a,b);
102+ x->ntype = NSTAT;
103+ return(x);
104+}
105+
106+Node *stat2(int a, Node *b, Node *c)
107+{
108+ Node *x;
109+
110+ x = node2(a,b,c);
111+ x->ntype = NSTAT;
112+ return(x);
113+}
114+
115+Node *stat3(int a, Node *b, Node *c, Node *d)
116+{
117+ Node *x;
118+
119+ x = node3(a,b,c,d);
120+ x->ntype = NSTAT;
121+ return(x);
122+}
123+
124+Node *stat4(int a, Node *b, Node *c, Node *d, Node *e)
125+{
126+ Node *x;
127+
128+ x = node4(a,b,c,d,e);
129+ x->ntype = NSTAT;
130+ return(x);
131+}
132+
133+Node *op1(int a, Node *b)
134+{
135+ Node *x;
136+
137+ x = node1(a,b);
138+ x->ntype = NEXPR;
139+ return(x);
140+}
141+
142+Node *op2(int a, Node *b, Node *c)
143+{
144+ Node *x;
145+
146+ x = node2(a,b,c);
147+ x->ntype = NEXPR;
148+ return(x);
149+}
150+
151+Node *op3(int a, Node *b, Node *c, Node *d)
152+{
153+ Node *x;
154+
155+ x = node3(a,b,c,d);
156+ x->ntype = NEXPR;
157+ return(x);
158+}
159+
160+Node *op4(int a, Node *b, Node *c, Node *d, Node *e)
161+{
162+ Node *x;
163+
164+ x = node4(a,b,c,d,e);
165+ x->ntype = NEXPR;
166+ return(x);
167+}
168+
169+Node *celltonode(Cell *a, int b)
170+{
171+ Node *x;
172+
173+ a->ctype = OCELL;
174+ a->csub = b;
175+ x = node1(0, (Node *) a);
176+ x->ntype = NVALUE;
177+ return(x);
178+}
179+
180+Node *rectonode(void) /* make $0 into a Node */
181+{
182+ extern Cell *literal0;
183+ return op1(INDIRECT, celltonode(literal0, CUNK));
184+}
185+
186+Node *makearr(Node *p)
187+{
188+ Cell *cp;
189+
190+ if (isvalue(p)) {
191+ cp = (Cell *) (p->narg[0]);
192+ if (isfcn(cp))
193+ SYNTAX( "%s is a function, not an array", cp->nval );
194+ else if (!isarr(cp)) {
195+ xfree(cp->sval);
196+ cp->sval = (char *) makesymtab(NSYMTAB);
197+ cp->tval = ARR;
198+ }
199+ }
200+ return p;
201+}
202+
203+#define PA2NUM 50 /* max number of pat,pat patterns allowed */
204+int paircnt; /* number of them in use */
205+int pairstack[PA2NUM]; /* state of each pat,pat */
206+
207+Node *pa2stat(Node *a, Node *b, Node *c) /* pat, pat {...} */
208+{
209+ Node *x;
210+
211+ x = node4(PASTAT2, a, b, c, itonp(paircnt));
212+ if (paircnt++ >= PA2NUM)
213+ SYNTAX( "limited to %d pat,pat statements", PA2NUM );
214+ x->ntype = NSTAT;
215+ return(x);
216+}
217+
218+Node *linkum(Node *a, Node *b)
219+{
220+ Node *c;
221+
222+ if (errorflag) /* don't link things that are wrong */
223+ return a;
224+ if (a == NULL)
225+ return(b);
226+ else if (b == NULL)
227+ return(a);
228+ for (c = a; c->nnext != NULL; c = c->nnext)
229+ ;
230+ c->nnext = b;
231+ return(a);
232+}
233+
234+void defn(Cell *v, Node *vl, Node *st) /* turn on FCN bit in definition, */
235+{ /* body of function, arglist */
236+ Node *p;
237+ int n;
238+
239+ if (isarr(v)) {
240+ SYNTAX( "`%s' is an array name and a function name", v->nval );
241+ return;
242+ }
243+ if (isarg(v->nval) != -1) {
244+ SYNTAX( "`%s' is both function name and argument name", v->nval );
245+ return;
246+ }
247+
248+ v->tval = FCN;
249+ v->sval = (char *) st;
250+ n = 0; /* count arguments */
251+ for (p = vl; p; p = p->nnext)
252+ n++;
253+ v->fval = n;
254+ DPRINTF("defining func %s (%d args)\n", v->nval, n);
255+}
256+
257+int isarg(const char *s) /* is s in argument list for current function? */
258+{ /* return -1 if not, otherwise arg # */
259+ extern Node *arglist;
260+ Node *p = arglist;
261+ int n;
262+
263+ for (n = 0; p != NULL; p = p->nnext, n++)
264+ if (strcmp(((Cell *)(p->narg[0]))->nval, s) == 0)
265+ return n;
266+ return -1;
267+}
268+
269+int ptoi(void *p) /* convert pointer to integer */
270+{
271+ return (int) (long) p; /* swearing that p fits, of course */
272+}
273+
274+Node *itonp(int i) /* and vice versa */
275+{
276+ return (Node *) (long) i;
277+}
+204,
-0
1@@ -0,0 +1,204 @@
2+/****************************************************************
3+Copyright (C) Lucent Technologies 1997
4+All Rights Reserved
5+
6+Permission to use, copy, modify, and distribute this software and
7+its documentation for any purpose and without fee is hereby
8+granted, provided that the above copyright notice appear in all
9+copies and that both that the copyright notice and this
10+permission notice and warranty disclaimer appear in supporting
11+documentation, and that the name Lucent Technologies or any of
12+its entities not be used in advertising or publicity pertaining
13+to distribution of the software without specific, written prior
14+permission.
15+
16+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
18+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
19+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
21+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
22+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
23+THIS SOFTWARE.
24+****************************************************************/
25+
26+extern int yywrap(void);
27+extern void setfname(Cell *);
28+extern int constnode(Node *);
29+extern char *strnode(Node *);
30+extern Node *notnull(Node *);
31+extern int yyparse(void);
32+
33+extern int yylex(void);
34+extern void startreg(void);
35+extern int input(void);
36+extern void unput(int);
37+extern void unputstr(const char *);
38+
39+extern fa *makedfa(const char *, bool);
40+extern fa *mkdfa(const char *, bool);
41+extern int makeinit(fa *, bool);
42+extern void penter(Node *);
43+extern void freetr(Node *);
44+extern int quoted(const uschar **);
45+extern int *cclenter(const char *);
46+extern noreturn void overflo(const char *);
47+extern void cfoll(fa *, Node *);
48+extern int first(Node *);
49+extern void follow(Node *);
50+extern int member(int, int *);
51+extern int match(fa *, const char *);
52+extern int pmatch(fa *, const char *);
53+extern int nematch(fa *, const char *);
54+extern bool fnematch(fa *, FILE *, char **, int *, int);
55+extern Node *reparse(const char *);
56+extern Node *regexp(void);
57+extern Node *primary(void);
58+extern Node *concat(Node *);
59+extern Node *alt(Node *);
60+extern Node *unary(Node *);
61+extern int relex(void);
62+extern int cgoto(fa *, int, int);
63+extern void freefa(fa *);
64+
65+extern int pgetc(void);
66+extern char *cursource(void);
67+
68+extern Node *nodealloc(size_t);
69+extern Node *exptostat(Node *);
70+extern Node *node1(int, Node *);
71+extern Node *node2(int, Node *, Node *);
72+extern Node *node3(int, Node *, Node *, Node *);
73+extern Node *node4(int, Node *, Node *, Node *, Node *);
74+extern Node *stat3(int, Node *, Node *, Node *);
75+extern Node *op2(int, Node *, Node *);
76+extern Node *op1(int, Node *);
77+extern Node *stat1(int, Node *);
78+extern Node *op3(int, Node *, Node *, Node *);
79+extern Node *op4(int, Node *, Node *, Node *, Node *);
80+extern Node *stat2(int, Node *, Node *);
81+extern Node *stat4(int, Node *, Node *, Node *, Node *);
82+extern Node *celltonode(Cell *, int);
83+extern Node *rectonode(void);
84+extern Node *makearr(Node *);
85+extern Node *pa2stat(Node *, Node *, Node *);
86+extern Node *linkum(Node *, Node *);
87+extern void defn(Cell *, Node *, Node *);
88+extern int isarg(const char *);
89+extern const char *tokname(int);
90+extern Cell *(*proctab[])(Node **, int);
91+extern int ptoi(void *);
92+extern Node *itonp(int);
93+
94+extern void syminit(void);
95+extern void arginit(int, char **);
96+extern void envinit(char **);
97+extern Array *makesymtab(int);
98+extern void freesymtab(Cell *);
99+extern void freeelem(Cell *, const char *);
100+extern Cell *setsymtab(const char *, const char *, double, unsigned int, Array *);
101+extern int hash(const char *, int);
102+extern void rehash(Array *);
103+extern Cell *lookup(const char *, Array *);
104+extern double setfval(Cell *, double);
105+extern void funnyvar(Cell *, const char *);
106+extern char *setsval(Cell *, const char *);
107+extern double getfval(Cell *);
108+extern char *getsval(Cell *);
109+extern char *getpssval(Cell *); /* for print */
110+extern char *tostring(const char *);
111+extern char *tostringN(const char *, size_t);
112+extern char *qstring(const char *, int);
113+extern Cell *catstr(Cell *, Cell *);
114+
115+extern void recinit(unsigned int);
116+extern void initgetrec(void);
117+extern void makefields(int, int);
118+extern void growfldtab(int n);
119+extern void savefs(void);
120+extern int getrec(char **, int *, bool);
121+extern void nextfile(void);
122+extern int readrec(char **buf, int *bufsize, FILE *inf, bool isnew);
123+extern char *getargv(int);
124+extern void setclvar(char *);
125+extern void fldbld(void);
126+extern void cleanfld(int, int);
127+extern void newfld(int);
128+extern void setlastfld(int);
129+extern int refldbld(const char *, const char *);
130+extern void recbld(void);
131+extern Cell *fieldadr(int);
132+extern void yyerror(const char *);
133+extern void bracecheck(void);
134+extern void bcheck2(int, int, int);
135+extern void SYNTAX(const char *, ...)
136+ __attribute__((__format__(__printf__, 1, 2)));
137+extern noreturn void FATAL(const char *, ...)
138+ __attribute__((__format__(__printf__, 1, 2)));
139+extern void WARNING(const char *, ...)
140+ __attribute__((__format__(__printf__, 1, 2)));
141+extern void error(void);
142+extern void eprint(void);
143+extern void bclass(int);
144+extern int isclvar(const char *);
145+extern bool is_valid_number(const char *s, bool trailing_stuff_ok,
146+ bool *no_trailing, double *result);
147+#define is_number(s, val) is_valid_number(s, false, NULL, val)
148+
149+extern int adjbuf(char **pb, int *sz, int min, int q, char **pbp, const char *what);
150+extern void run(Node *);
151+extern Cell *execute(Node *);
152+extern Cell *program(Node **, int);
153+extern Cell *call(Node **, int);
154+extern Cell *copycell(Cell *);
155+extern Cell *arg(Node **, int);
156+extern Cell *jump(Node **, int);
157+extern Cell *awkgetline(Node **, int);
158+extern Cell *getnf(Node **, int);
159+extern Cell *array(Node **, int);
160+extern Cell *awkdelete(Node **, int);
161+extern Cell *intest(Node **, int);
162+extern Cell *matchop(Node **, int);
163+extern Cell *boolop(Node **, int);
164+extern Cell *relop(Node **, int);
165+extern void tfree(Cell *);
166+extern Cell *gettemp(void);
167+extern Cell *indirect(Node **, int);
168+extern Cell *substr(Node **, int);
169+extern Cell *sindex(Node **, int);
170+extern int format(char **, int *, const char *, Node *);
171+extern Cell *awksprintf(Node **, int);
172+extern Cell *awkprintf(Node **, int);
173+extern Cell *arith(Node **, int);
174+extern double ipow(double, int);
175+extern Cell *incrdecr(Node **, int);
176+extern Cell *assign(Node **, int);
177+extern Cell *cat(Node **, int);
178+extern Cell *pastat(Node **, int);
179+extern Cell *dopa2(Node **, int);
180+extern Cell *split(Node **, int);
181+extern Cell *condexpr(Node **, int);
182+extern Cell *ifstat(Node **, int);
183+extern Cell *whilestat(Node **, int);
184+extern Cell *dostat(Node **, int);
185+extern Cell *forstat(Node **, int);
186+extern Cell *instat(Node **, int);
187+extern Cell *bltin(Node **, int);
188+extern Cell *printstat(Node **, int);
189+extern Cell *nullproc(Node **, int);
190+extern FILE *redirect(int, Node *);
191+extern FILE *openfile(int, const char *, bool *);
192+extern const char *filename(FILE *);
193+extern Cell *closefile(Node **, int);
194+extern void closeall(void);
195+extern Cell *dosub(Node **, int);
196+
197+extern FILE *popen(const char *, const char *);
198+extern int pclose(FILE *);
199+
200+extern const char *flags2str(int flags);
201+
202+extern double exp_errcheck(double);
203+extern double log_errcheck(double);
204+extern double pow_errcheck(double, double);
205+extern double sqrt_errcheck(double);
+2626,
-0
1@@ -0,0 +1,2626 @@
2+/****************************************************************
3+Copyright (C) Lucent Technologies 1997
4+All Rights Reserved
5+
6+Permission to use, copy, modify, and distribute this software and
7+its documentation for any purpose and without fee is hereby
8+granted, provided that the above copyright notice appear in all
9+copies and that both that the copyright notice and this
10+permission notice and warranty disclaimer appear in supporting
11+documentation, and that the name Lucent Technologies or any of
12+its entities not be used in advertising or publicity pertaining
13+to distribution of the software without specific, written prior
14+permission.
15+
16+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
18+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
19+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
21+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
22+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
23+THIS SOFTWARE.
24+****************************************************************/
25+
26+#ifdef __GNUC__
27+#pragma GCC diagnostic ignored "-Wunused-parameter"
28+#endif
29+
30+#define DEBUG
31+#include <stdio.h>
32+#include <ctype.h>
33+#include <wctype.h>
34+#include <fcntl.h>
35+#include <setjmp.h>
36+#include <limits.h>
37+#include <math.h>
38+#include <string.h>
39+#include <stdlib.h>
40+#include <time.h>
41+#include <sys/types.h>
42+#include <sys/stat.h>
43+#include <sys/wait.h>
44+#include "awk.h"
45+#include "awkgram.tab.h"
46+
47+
48+static void stdinit(void);
49+static void flush_all(void);
50+static char *wide_char_to_byte_str(int rune, size_t *outlen);
51+
52+#if 1
53+#define tempfree(x) do { if (istemp(x)) tfree(x); } while (/*CONSTCOND*/0)
54+#else
55+void tempfree(Cell *p) {
56+ if (p->ctype == OCELL && (p->csub < CUNK || p->csub > CFREE)) {
57+ WARNING("bad csub %d in Cell %d %s",
58+ p->csub, p->ctype, p->sval);
59+ }
60+ if (istemp(p))
61+ tfree(p);
62+}
63+#endif
64+
65+/* do we really need these? */
66+/* #ifdef _NFILE */
67+/* #ifndef FOPEN_MAX */
68+/* #define FOPEN_MAX _NFILE */
69+/* #endif */
70+/* #endif */
71+
72+/* #ifndef FOPEN_MAX */
73+/* #define FOPEN_MAX 40 */ /* max number of open files */
74+/* #endif */
75+
76+
77+jmp_buf env;
78+extern int pairstack[];
79+extern Awkfloat srand_seed;
80+
81+Node *winner = NULL; /* root of parse tree */
82+Cell *tmps; /* free temporary cells for execution */
83+
84+static Cell truecell ={ OBOOL, BTRUE, 0, 0, 1.0, NUM, NULL, NULL };
85+Cell *True = &truecell;
86+static Cell falsecell ={ OBOOL, BFALSE, 0, 0, 0.0, NUM, NULL, NULL };
87+Cell *False = &falsecell;
88+static Cell breakcell ={ OJUMP, JBREAK, 0, 0, 0.0, NUM, NULL, NULL };
89+Cell *jbreak = &breakcell;
90+static Cell contcell ={ OJUMP, JCONT, 0, 0, 0.0, NUM, NULL, NULL };
91+Cell *jcont = &contcell;
92+static Cell nextcell ={ OJUMP, JNEXT, 0, 0, 0.0, NUM, NULL, NULL };
93+Cell *jnext = &nextcell;
94+static Cell nextfilecell ={ OJUMP, JNEXTFILE, 0, 0, 0.0, NUM, NULL, NULL };
95+Cell *jnextfile = &nextfilecell;
96+static Cell exitcell ={ OJUMP, JEXIT, 0, 0, 0.0, NUM, NULL, NULL };
97+Cell *jexit = &exitcell;
98+static Cell retcell ={ OJUMP, JRET, 0, 0, 0.0, NUM, NULL, NULL };
99+Cell *jret = &retcell;
100+static Cell tempcell ={ OCELL, CTEMP, 0, EMPTY, 0.0, NUM|STR|DONTFREE, NULL, NULL };
101+
102+Node *curnode = NULL; /* the node being executed, for debugging */
103+
104+/* buffer memory management */
105+int adjbuf(char **pbuf, int *psiz, int minlen, int quantum, char **pbptr,
106+ const char *whatrtn)
107+/* pbuf: address of pointer to buffer being managed
108+ * psiz: address of buffer size variable
109+ * minlen: minimum length of buffer needed
110+ * quantum: buffer size quantum
111+ * pbptr: address of movable pointer into buffer, or 0 if none
112+ * whatrtn: name of the calling routine if failure should cause fatal error
113+ *
114+ * return 0 for realloc failure, !=0 for success
115+ */
116+{
117+ if (minlen > *psiz) {
118+ char *tbuf;
119+ int rminlen = quantum ? minlen % quantum : 0;
120+ int boff = pbptr ? *pbptr - *pbuf : 0;
121+ /* round up to next multiple of quantum */
122+ if (rminlen)
123+ minlen += quantum - rminlen;
124+ tbuf = (char *) realloc(*pbuf, minlen);
125+ DPRINTF("adjbuf %s: %d %d (pbuf=%p, tbuf=%p)\n", whatrtn, *psiz, minlen, (void*)*pbuf, (void*)tbuf);
126+ if (tbuf == NULL) {
127+ if (whatrtn)
128+ FATAL("out of memory in %s", whatrtn);
129+ return 0;
130+ }
131+ *pbuf = tbuf;
132+ *psiz = minlen;
133+ if (pbptr)
134+ *pbptr = tbuf + boff;
135+ }
136+ return 1;
137+}
138+
139+void run(Node *a) /* execution of parse tree starts here */
140+{
141+
142+ stdinit();
143+ execute(a);
144+ closeall();
145+}
146+
147+Cell *execute(Node *u) /* execute a node of the parse tree */
148+{
149+ Cell *(*proc)(Node **, int);
150+ Cell *x;
151+ Node *a;
152+
153+ if (u == NULL)
154+ return(True);
155+ for (a = u; ; a = a->nnext) {
156+ curnode = a;
157+ if (isvalue(a)) {
158+ x = (Cell *) (a->narg[0]);
159+ if (isfld(x) && !donefld)
160+ fldbld();
161+ else if (isrec(x) && !donerec)
162+ recbld();
163+ return(x);
164+ }
165+ if (notlegal(a->nobj)) /* probably a Cell* but too risky to print */
166+ FATAL("illegal statement");
167+ proc = proctab[a->nobj-FIRSTTOKEN];
168+ x = (*proc)(a->narg, a->nobj);
169+ if (isfld(x) && !donefld)
170+ fldbld();
171+ else if (isrec(x) && !donerec)
172+ recbld();
173+ if (isexpr(a))
174+ return(x);
175+ if (isjump(x))
176+ return(x);
177+ if (a->nnext == NULL)
178+ return(x);
179+ tempfree(x);
180+ }
181+}
182+
183+
184+Cell *program(Node **a, int n) /* execute an awk program */
185+{ /* a[0] = BEGIN, a[1] = body, a[2] = END */
186+ Cell *x;
187+
188+ if (setjmp(env) != 0)
189+ goto ex;
190+ if (a[0]) { /* BEGIN */
191+ x = execute(a[0]);
192+ if (isexit(x))
193+ return(True);
194+ if (isjump(x))
195+ FATAL("illegal break, continue, next or nextfile from BEGIN");
196+ tempfree(x);
197+ }
198+ if (a[1] || a[2])
199+ while (getrec(&record, &recsize, true) > 0) {
200+ x = execute(a[1]);
201+ if (isexit(x))
202+ break;
203+ tempfree(x);
204+ }
205+ ex:
206+ if (setjmp(env) != 0) /* handles exit within END */
207+ goto ex1;
208+ if (a[2]) { /* END */
209+ x = execute(a[2]);
210+ if (isbreak(x) || isnext(x) || iscont(x))
211+ FATAL("illegal break, continue, next or nextfile from END");
212+ tempfree(x);
213+ }
214+ ex1:
215+ return(True);
216+}
217+
218+struct Frame { /* stack frame for awk function calls */
219+ int nargs; /* number of arguments in this call */
220+ Cell *fcncell; /* pointer to Cell for function */
221+ Cell **args; /* pointer to array of arguments after execute */
222+ Cell *retval; /* return value */
223+};
224+
225+#define NARGS 50 /* max args in a call */
226+
227+struct Frame *frame = NULL; /* base of stack frames; dynamically allocated */
228+int nframe = 0; /* number of frames allocated */
229+struct Frame *frp = NULL; /* frame pointer. bottom level unused */
230+
231+Cell *call(Node **a, int n) /* function call. very kludgy and fragile */
232+{
233+ static const Cell newcopycell = { OCELL, CCOPY, 0, EMPTY, 0.0, NUM|STR|DONTFREE, NULL, NULL };
234+ int i, ncall, ndef;
235+ int freed = 0; /* handles potential double freeing when fcn & param share a tempcell */
236+ Node *x;
237+ Cell *args[NARGS], *oargs[NARGS]; /* BUG: fixed size arrays */
238+ Cell *y, *z, *fcn;
239+ char *s;
240+
241+ fcn = execute(a[0]); /* the function itself */
242+ s = fcn->nval;
243+ if (!isfcn(fcn))
244+ FATAL("calling undefined function %s", s);
245+ if (frame == NULL) {
246+ frp = frame = (struct Frame *) calloc(nframe += 100, sizeof(*frame));
247+ if (frame == NULL)
248+ FATAL("out of space for stack frames calling %s", s);
249+ }
250+ for (ncall = 0, x = a[1]; x != NULL; x = x->nnext) /* args in call */
251+ ncall++;
252+ ndef = (int) fcn->fval; /* args in defn */
253+ DPRINTF("calling %s, %d args (%d in defn), frp=%d\n", s, ncall, ndef, (int) (frp-frame));
254+ if (ncall > ndef)
255+ WARNING("function %s called with %d args, uses only %d",
256+ s, ncall, ndef);
257+ if (ncall + ndef > NARGS)
258+ FATAL("function %s has %d arguments, limit %d", s, ncall+ndef, NARGS);
259+ for (i = 0, x = a[1]; x != NULL; i++, x = x->nnext) { /* get call args */
260+ DPRINTF("evaluate args[%d], frp=%d:\n", i, (int) (frp-frame));
261+ y = execute(x);
262+ oargs[i] = y;
263+ DPRINTF("args[%d]: %s %f <%s>, t=%o\n",
264+ i, NN(y->nval), y->fval, isarr(y) ? "(array)" : NN(y->sval), y->tval);
265+ if (isfcn(y))
266+ FATAL("can't use function %s as argument in %s", y->nval, s);
267+ if (isarr(y))
268+ args[i] = y; /* arrays by ref */
269+ else
270+ args[i] = copycell(y);
271+ tempfree(y);
272+ }
273+ for ( ; i < ndef; i++) { /* add null args for ones not provided */
274+ args[i] = gettemp();
275+ *args[i] = newcopycell;
276+ }
277+ frp++; /* now ok to up frame */
278+ if (frp >= frame + nframe) {
279+ int dfp = frp - frame; /* old index */
280+ frame = (struct Frame *) realloc(frame, (nframe += 100) * sizeof(*frame));
281+ if (frame == NULL)
282+ FATAL("out of space for stack frames in %s", s);
283+ frp = frame + dfp;
284+ }
285+ frp->fcncell = fcn;
286+ frp->args = args;
287+ frp->nargs = ndef; /* number defined with (excess are locals) */
288+ frp->retval = gettemp();
289+
290+ DPRINTF("start exec of %s, frp=%d\n", s, (int) (frp-frame));
291+ y = execute((Node *)(fcn->sval)); /* execute body */
292+ DPRINTF("finished exec of %s, frp=%d\n", s, (int) (frp-frame));
293+
294+ for (i = 0; i < ndef; i++) {
295+ Cell *t = frp->args[i];
296+ if (isarr(t)) {
297+ if (t->csub == CCOPY) {
298+ if (i >= ncall) {
299+ freesymtab(t);
300+ t->csub = CTEMP;
301+ tempfree(t);
302+ } else {
303+ oargs[i]->tval = t->tval;
304+ oargs[i]->tval &= ~(STR|NUM|DONTFREE);
305+ oargs[i]->sval = t->sval;
306+ tempfree(t);
307+ }
308+ }
309+ } else if (t != y) { /* kludge to prevent freeing twice */
310+ t->csub = CTEMP;
311+ tempfree(t);
312+ } else if (t == y && t->csub == CCOPY) {
313+ t->csub = CTEMP;
314+ tempfree(t);
315+ freed = 1;
316+ }
317+ }
318+ tempfree(fcn);
319+ if (isexit(y) || isnext(y))
320+ return y;
321+ if (freed == 0) {
322+ tempfree(y); /* don't free twice! */
323+ }
324+ z = frp->retval; /* return value */
325+ DPRINTF("%s returns %g |%s| %o\n", s, getfval(z), getsval(z), z->tval);
326+ frp--;
327+ return(z);
328+}
329+
330+Cell *copycell(Cell *x) /* make a copy of a cell in a temp */
331+{
332+ Cell *y;
333+
334+ /* copy is not constant or field */
335+
336+ y = gettemp();
337+ y->tval = x->tval & ~(CON|FLD|REC);
338+ y->csub = CCOPY; /* prevents freeing until call is over */
339+ y->nval = x->nval; /* BUG? */
340+ if (isstr(x) /* || x->ctype == OCELL */) {
341+ y->sval = tostring(x->sval);
342+ y->tval &= ~DONTFREE;
343+ } else
344+ y->tval |= DONTFREE;
345+ y->fval = x->fval;
346+ return y;
347+}
348+
349+Cell *arg(Node **a, int n) /* nth argument of a function */
350+{
351+
352+ n = ptoi(a[0]); /* argument number, counting from 0 */
353+ DPRINTF("arg(%d), frp->nargs=%d\n", n, frp->nargs);
354+ if (n+1 > frp->nargs)
355+ FATAL("argument #%d of function %s was not supplied",
356+ n+1, frp->fcncell->nval);
357+ return frp->args[n];
358+}
359+
360+Cell *jump(Node **a, int n) /* break, continue, next, nextfile, return */
361+{
362+ Cell *y;
363+
364+ switch (n) {
365+ case EXIT:
366+ if (a[0] != NULL) {
367+ y = execute(a[0]);
368+ errorflag = (int) getfval(y);
369+ tempfree(y);
370+ }
371+ longjmp(env, 1);
372+ case RETURN:
373+ if (a[0] != NULL) {
374+ y = execute(a[0]);
375+ if ((y->tval & (STR|NUM)) == (STR|NUM)) {
376+ setsval(frp->retval, getsval(y));
377+ frp->retval->fval = getfval(y);
378+ frp->retval->tval |= NUM;
379+ }
380+ else if (y->tval & STR)
381+ setsval(frp->retval, getsval(y));
382+ else if (y->tval & NUM)
383+ setfval(frp->retval, getfval(y));
384+ else /* can't happen */
385+ FATAL("bad type variable %d", y->tval);
386+ tempfree(y);
387+ }
388+ return(jret);
389+ case NEXT:
390+ return(jnext);
391+ case NEXTFILE:
392+ nextfile();
393+ return(jnextfile);
394+ case BREAK:
395+ return(jbreak);
396+ case CONTINUE:
397+ return(jcont);
398+ default: /* can't happen */
399+ FATAL("illegal jump type %d", n);
400+ }
401+ return 0; /* not reached */
402+}
403+
404+Cell *awkgetline(Node **a, int n) /* get next line from specific input */
405+{ /* a[0] is variable, a[1] is operator, a[2] is filename */
406+ Cell *r, *x;
407+ extern Cell **fldtab;
408+ FILE *fp;
409+ char *buf;
410+ int bufsize = recsize;
411+ int mode;
412+ bool newflag;
413+ double result;
414+
415+ if ((buf = (char *) malloc(bufsize)) == NULL)
416+ FATAL("out of memory in getline");
417+
418+ fflush(stdout); /* in case someone is waiting for a prompt */
419+ r = gettemp();
420+ if (a[1] != NULL) { /* getline < file */
421+ x = execute(a[2]); /* filename */
422+ mode = ptoi(a[1]);
423+ if (mode == '|') /* input pipe */
424+ mode = LE; /* arbitrary flag */
425+ fp = openfile(mode, getsval(x), &newflag);
426+ tempfree(x);
427+ if (fp == NULL)
428+ n = -1;
429+ else
430+ n = readrec(&buf, &bufsize, fp, newflag);
431+ if (n <= 0) {
432+ ;
433+ } else if (a[0] != NULL) { /* getline var <file */
434+ x = execute(a[0]);
435+ setsval(x, buf);
436+ if (is_number(x->sval, & result)) {
437+ x->fval = result;
438+ x->tval |= NUM;
439+ }
440+ tempfree(x);
441+ } else { /* getline <file */
442+ setsval(fldtab[0], buf);
443+ if (is_number(fldtab[0]->sval, & result)) {
444+ fldtab[0]->fval = result;
445+ fldtab[0]->tval |= NUM;
446+ }
447+ }
448+ } else { /* bare getline; use current input */
449+ if (a[0] == NULL) /* getline */
450+ n = getrec(&record, &recsize, true);
451+ else { /* getline var */
452+ n = getrec(&buf, &bufsize, false);
453+ if (n > 0) {
454+ x = execute(a[0]);
455+ setsval(x, buf);
456+ if (is_number(x->sval, & result)) {
457+ x->fval = result;
458+ x->tval |= NUM;
459+ }
460+ tempfree(x);
461+ }
462+ }
463+ }
464+ setfval(r, (Awkfloat) n);
465+ free(buf);
466+ return r;
467+}
468+
469+Cell *getnf(Node **a, int n) /* get NF */
470+{
471+ if (!donefld)
472+ fldbld();
473+ return (Cell *) a[0];
474+}
475+
476+static char *
477+makearraystring(Node *p, const char *func)
478+{
479+ char *buf;
480+ int bufsz = recsize;
481+ size_t blen;
482+
483+ if ((buf = (char *) malloc(bufsz)) == NULL) {
484+ FATAL("%s: out of memory", func);
485+ }
486+
487+ blen = 0;
488+ buf[blen] = '\0';
489+
490+ for (; p; p = p->nnext) {
491+ Cell *x = execute(p); /* expr */
492+ char *s = getsval(x);
493+ size_t seplen = strlen(getsval(subseploc));
494+ size_t nsub = p->nnext ? seplen : 0;
495+ size_t slen = strlen(s);
496+ size_t tlen = blen + slen + nsub;
497+
498+ if (!adjbuf(&buf, &bufsz, tlen + 1, recsize, 0, func)) {
499+ FATAL("%s: out of memory %s[%s...]",
500+ func, x->nval, buf);
501+ }
502+ memcpy(buf + blen, s, slen);
503+ if (nsub) {
504+ memcpy(buf + blen + slen, *SUBSEP, nsub);
505+ }
506+ buf[tlen] = '\0';
507+ blen = tlen;
508+ tempfree(x);
509+ }
510+ return buf;
511+}
512+
513+Cell *array(Node **a, int n) /* a[0] is symtab, a[1] is list of subscripts */
514+{
515+ Cell *x, *z;
516+ char *buf;
517+
518+ x = execute(a[0]); /* Cell* for symbol table */
519+ buf = makearraystring(a[1], __func__);
520+ if (!isarr(x)) {
521+ DPRINTF("making %s into an array\n", NN(x->nval));
522+ if (freeable(x))
523+ xfree(x->sval);
524+ x->tval &= ~(STR|NUM|DONTFREE);
525+ x->tval |= ARR;
526+ x->sval = (char *) makesymtab(NSYMTAB);
527+ }
528+ z = setsymtab(buf, "", 0.0, STR|NUM, (Array *) x->sval);
529+ z->ctype = OCELL;
530+ z->csub = CVAR;
531+ tempfree(x);
532+ free(buf);
533+ return(z);
534+}
535+
536+Cell *awkdelete(Node **a, int n) /* a[0] is symtab, a[1] is list of subscripts */
537+{
538+ Cell *x;
539+
540+ x = execute(a[0]); /* Cell* for symbol table */
541+ if (x == symtabloc) {
542+ FATAL("cannot delete SYMTAB or its elements");
543+ }
544+ if (!isarr(x))
545+ return True;
546+ if (a[1] == NULL) { /* delete the elements, not the table */
547+ freesymtab(x);
548+ x->tval &= ~STR;
549+ x->tval |= ARR;
550+ x->sval = (char *) makesymtab(NSYMTAB);
551+ } else {
552+ char *buf = makearraystring(a[1], __func__);
553+ freeelem(x, buf);
554+ free(buf);
555+ }
556+ tempfree(x);
557+ return True;
558+}
559+
560+Cell *intest(Node **a, int n) /* a[0] is index (list), a[1] is symtab */
561+{
562+ Cell *ap, *k;
563+ char *buf;
564+
565+ ap = execute(a[1]); /* array name */
566+ if (!isarr(ap)) {
567+ DPRINTF("making %s into an array\n", ap->nval);
568+ if (freeable(ap))
569+ xfree(ap->sval);
570+ ap->tval &= ~(STR|NUM|DONTFREE);
571+ ap->tval |= ARR;
572+ ap->sval = (char *) makesymtab(NSYMTAB);
573+ }
574+ buf = makearraystring(a[0], __func__);
575+ k = lookup(buf, (Array *) ap->sval);
576+ tempfree(ap);
577+ free(buf);
578+ if (k == NULL)
579+ return(False);
580+ else
581+ return(True);
582+}
583+
584+
585+/* ======== utf-8 code ========== */
586+
587+/*
588+ * Awk strings can contain ascii, random 8-bit items (eg Latin-1),
589+ * or utf-8. u8_isutf tests whether a string starts with a valid
590+ * utf-8 sequence, and returns 0 if not (e.g., high bit set).
591+ * u8_nextlen returns length of next valid sequence, which is
592+ * 1 for ascii, 2..4 for utf-8, or 1 for high bit non-utf.
593+ * u8_strlen returns length of string in valid utf-8 sequences
594+ * and/or high-bit bytes. Conversion functions go between byte
595+ * number and character number.
596+ *
597+ * In theory, this behaves the same as before for non-utf8 bytes.
598+ *
599+ * Limited checking! This is a potential security hole.
600+ */
601+
602+/* is s the beginning of a valid utf-8 string? */
603+/* return length 1..4 if yes, 0 if no */
604+int u8_isutf(const char *s)
605+{
606+ int n, ret;
607+ unsigned char c;
608+
609+ c = s[0];
610+ if (c < 128 || awk_mb_cur_max == 1)
611+ return 1; /* what if it's 0? */
612+
613+ n = strlen(s);
614+ if (n >= 2 && ((c>>5) & 0x7) == 0x6 && (s[1] & 0xC0) == 0x80) {
615+ ret = 2; /* 110xxxxx 10xxxxxx */
616+ } else if (n >= 3 && ((c>>4) & 0xF) == 0xE && (s[1] & 0xC0) == 0x80
617+ && (s[2] & 0xC0) == 0x80) {
618+ ret = 3; /* 1110xxxx 10xxxxxx 10xxxxxx */
619+ } else if (n >= 4 && ((c>>3) & 0x1F) == 0x1E && (s[1] & 0xC0) == 0x80
620+ && (s[2] & 0xC0) == 0x80 && (s[3] & 0xC0) == 0x80) {
621+ ret = 4; /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
622+ } else {
623+ ret = 0;
624+ }
625+ return ret;
626+}
627+
628+/* Convert (prefix of) utf8 string to utf-32 rune. */
629+/* Sets *rune to the value, returns the length. */
630+/* No error checking: watch out. */
631+int u8_rune(int *rune, const char *s)
632+{
633+ int n, ret;
634+ unsigned char c;
635+
636+ c = s[0];
637+ if (c < 128 || awk_mb_cur_max == 1) {
638+ *rune = c;
639+ return 1;
640+ }
641+
642+ n = strlen(s);
643+ if (n >= 2 && ((c>>5) & 0x7) == 0x6 && (s[1] & 0xC0) == 0x80) {
644+ *rune = ((c & 0x1F) << 6) | (s[1] & 0x3F); /* 110xxxxx 10xxxxxx */
645+ ret = 2;
646+ } else if (n >= 3 && ((c>>4) & 0xF) == 0xE && (s[1] & 0xC0) == 0x80
647+ && (s[2] & 0xC0) == 0x80) {
648+ *rune = ((c & 0xF) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
649+ /* 1110xxxx 10xxxxxx 10xxxxxx */
650+ ret = 3;
651+ } else if (n >= 4 && ((c>>3) & 0x1F) == 0x1E && (s[1] & 0xC0) == 0x80
652+ && (s[2] & 0xC0) == 0x80 && (s[3] & 0xC0) == 0x80) {
653+ *rune = ((c & 0x7) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
654+ /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
655+ ret = 4;
656+ } else {
657+ *rune = c;
658+ ret = 1;
659+ }
660+ return ret; /* returns one byte if sequence doesn't look like utf */
661+}
662+
663+/* return length of next sequence: 1 for ascii or random, 2..4 for valid utf8 */
664+int u8_nextlen(const char *s)
665+{
666+ int len;
667+
668+ len = u8_isutf(s);
669+ if (len == 0)
670+ len = 1;
671+ return len;
672+}
673+
674+/* return number of utf characters or single non-utf bytes */
675+int u8_strlen(const char *s)
676+{
677+ int i, len, n, totlen;
678+ unsigned char c;
679+
680+ n = strlen(s);
681+ totlen = 0;
682+ for (i = 0; i < n; i += len) {
683+ c = s[i];
684+ if (c < 128 || awk_mb_cur_max == 1) {
685+ len = 1;
686+ } else {
687+ len = u8_nextlen(&s[i]);
688+ }
689+ totlen++;
690+ if (i > n)
691+ FATAL("bad utf count [%s] n=%d i=%d\n", s, n, i);
692+ }
693+ return totlen;
694+}
695+
696+/* convert utf-8 char number in a string to its byte offset */
697+int u8_char2byte(const char *s, int charnum)
698+{
699+ int n;
700+ int bytenum = 0;
701+
702+ while (charnum > 0) {
703+ n = u8_nextlen(s);
704+ s += n;
705+ bytenum += n;
706+ charnum--;
707+ }
708+ return bytenum;
709+}
710+
711+/* convert byte offset in s to utf-8 char number that starts there */
712+int u8_byte2char(const char *s, int bytenum)
713+{
714+ int i, len, b;
715+ int charnum = 0; /* BUG: what origin? */
716+ /* should be 0 to match start==0 which means no match */
717+
718+ b = strlen(s);
719+ if (bytenum > b) {
720+ return -1; /* ??? */
721+ }
722+ for (i = 0; i <= bytenum; i += len) {
723+ len = u8_nextlen(s+i);
724+ charnum++;
725+ }
726+ return charnum;
727+}
728+
729+/* runetochar() adapted from rune.c in the Plan 9 distribution */
730+
731+enum
732+{
733+ Runeerror = 128, /* from somewhere else */
734+ Runemax = 0x10FFFF,
735+
736+ Bit1 = 7,
737+ Bitx = 6,
738+ Bit2 = 5,
739+ Bit3 = 4,
740+ Bit4 = 3,
741+ Bit5 = 2,
742+
743+ T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */
744+ Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */
745+ T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */
746+ T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */
747+ T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */
748+ T5 = ((1<<(Bit5+1))-1) ^ 0xFF, /* 1111 1000 */
749+
750+ Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0000 0000 0111 1111 */
751+ Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0000 0000 0111 1111 1111 */
752+ Rune3 = (1<<(Bit3+2*Bitx))-1, /* 0000 0000 1111 1111 1111 1111 */
753+ Rune4 = (1<<(Bit4+3*Bitx))-1, /* 0011 1111 1111 1111 1111 1111 */
754+
755+ Maskx = (1<<Bitx)-1, /* 0011 1111 */
756+ Testx = Maskx ^ 0xFF, /* 1100 0000 */
757+
758+};
759+
760+int runetochar(char *str, int c)
761+{
762+ /* one character sequence 00000-0007F => 00-7F */
763+ if (c <= Rune1) {
764+ str[0] = c;
765+ return 1;
766+ }
767+
768+ /* two character sequence 00080-007FF => T2 Tx */
769+ if (c <= Rune2) {
770+ str[0] = T2 | (c >> 1*Bitx);
771+ str[1] = Tx | (c & Maskx);
772+ return 2;
773+ }
774+
775+ /* three character sequence 00800-0FFFF => T3 Tx Tx */
776+ if (c > Runemax)
777+ c = Runeerror;
778+ if (c <= Rune3) {
779+ str[0] = T3 | (c >> 2*Bitx);
780+ str[1] = Tx | ((c >> 1*Bitx) & Maskx);
781+ str[2] = Tx | (c & Maskx);
782+ return 3;
783+ }
784+
785+ /* four character sequence 010000-1FFFFF => T4 Tx Tx Tx */
786+ str[0] = T4 | (c >> 3*Bitx);
787+ str[1] = Tx | ((c >> 2*Bitx) & Maskx);
788+ str[2] = Tx | ((c >> 1*Bitx) & Maskx);
789+ str[3] = Tx | (c & Maskx);
790+ return 4;
791+}
792+
793+
794+/* ========== end of utf8 code =========== */
795+
796+
797+
798+Cell *matchop(Node **a, int n) /* ~ and match() */
799+{
800+ Cell *x, *y, *z;
801+ char *s, *t;
802+ int i;
803+ int cstart, cpatlen, len;
804+ fa *pfa;
805+ int (*mf)(fa *, const char *) = match, mode = 0;
806+
807+ if (n == MATCHFCN) {
808+ mf = pmatch;
809+ mode = 1;
810+ }
811+ x = execute(a[1]); /* a[1] = target text */
812+ s = getsval(x);
813+ if (a[0] == NULL) /* a[1] == 0: already-compiled reg expr */
814+ i = (*mf)((fa *) a[2], s);
815+ else {
816+ y = execute(a[2]); /* a[2] = regular expr */
817+ t = getsval(y);
818+ pfa = makedfa(t, mode);
819+ i = (*mf)(pfa, s);
820+ tempfree(y);
821+ }
822+ z = x;
823+ if (n == MATCHFCN) {
824+ int start = patbeg - s + 1; /* origin 1 */
825+ if (patlen < 0) {
826+ start = 0; /* not found */
827+ } else {
828+ cstart = u8_byte2char(s, start-1);
829+ cpatlen = 0;
830+ for (i = 0; i < patlen; i += len) {
831+ len = u8_nextlen(patbeg+i);
832+ cpatlen++;
833+ }
834+
835+ start = cstart;
836+ patlen = cpatlen;
837+ }
838+
839+ setfval(rstartloc, (Awkfloat) start);
840+ setfval(rlengthloc, (Awkfloat) patlen);
841+ x = gettemp();
842+ x->tval = NUM;
843+ x->fval = start;
844+ } else if ((n == MATCH && i == 1) || (n == NOTMATCH && i == 0))
845+ x = True;
846+ else
847+ x = False;
848+
849+ tempfree(z);
850+ return x;
851+}
852+
853+
854+Cell *boolop(Node **a, int n) /* a[0] || a[1], a[0] && a[1], !a[0] */
855+{
856+ Cell *x, *y;
857+ int i;
858+
859+ x = execute(a[0]);
860+ i = istrue(x);
861+ tempfree(x);
862+ switch (n) {
863+ case BOR:
864+ if (i) return(True);
865+ y = execute(a[1]);
866+ i = istrue(y);
867+ tempfree(y);
868+ if (i) return(True);
869+ else return(False);
870+ case AND:
871+ if ( !i ) return(False);
872+ y = execute(a[1]);
873+ i = istrue(y);
874+ tempfree(y);
875+ if (i) return(True);
876+ else return(False);
877+ case NOT:
878+ if (i) return(False);
879+ else return(True);
880+ default: /* can't happen */
881+ FATAL("unknown boolean operator %d", n);
882+ }
883+ return 0; /*NOTREACHED*/
884+}
885+
886+Cell *relop(Node **a, int n) /* a[0 < a[1], etc. */
887+{
888+ int i;
889+ Cell *x, *y;
890+ Awkfloat j;
891+ bool x_is_nan, y_is_nan;
892+
893+ x = execute(a[0]);
894+ y = execute(a[1]);
895+ x_is_nan = isnan(x->fval);
896+ y_is_nan = isnan(y->fval);
897+ if (x->tval&NUM && y->tval&NUM) {
898+ if ((x_is_nan || y_is_nan) && n != NE)
899+ return(False);
900+ j = x->fval - y->fval;
901+ i = j<0? -1: (j>0? 1: 0);
902+ } else {
903+ i = strcmp(getsval(x), getsval(y));
904+ }
905+ tempfree(x);
906+ tempfree(y);
907+ switch (n) {
908+ case LT: if (i<0) return(True);
909+ else return(False);
910+ case LE: if (i<=0) return(True);
911+ else return(False);
912+ case NE: if (x_is_nan && y_is_nan) return(True);
913+ else if (i!=0) return(True);
914+ else return(False);
915+ case EQ: if (i == 0) return(True);
916+ else return(False);
917+ case GE: if (i>=0) return(True);
918+ else return(False);
919+ case GT: if (i>0) return(True);
920+ else return(False);
921+ default: /* can't happen */
922+ FATAL("unknown relational operator %d", n);
923+ }
924+ return 0; /*NOTREACHED*/
925+}
926+
927+void tfree(Cell *a) /* free a tempcell */
928+{
929+ if (freeable(a)) {
930+ DPRINTF("freeing %s %s %o\n", NN(a->nval), NN(a->sval), a->tval);
931+ xfree(a->sval);
932+ }
933+ if (a == tmps)
934+ FATAL("tempcell list is curdled");
935+ a->cnext = tmps;
936+ tmps = a;
937+}
938+
939+Cell *gettemp(void) /* get a tempcell */
940+{ int i;
941+ Cell *x;
942+
943+ if (!tmps) {
944+ tmps = (Cell *) calloc(100, sizeof(*tmps));
945+ if (!tmps)
946+ FATAL("out of space for temporaries");
947+ for (i = 1; i < 100; i++)
948+ tmps[i-1].cnext = &tmps[i];
949+ tmps[i-1].cnext = NULL;
950+ }
951+ x = tmps;
952+ tmps = x->cnext;
953+ *x = tempcell;
954+ return(x);
955+}
956+
957+Cell *indirect(Node **a, int n) /* $( a[0] ) */
958+{
959+ Awkfloat val;
960+ Cell *x;
961+ int m;
962+
963+ x = execute(a[0]);
964+ val = getfval(x); /* freebsd: defend against super large field numbers */
965+ if ((Awkfloat)INT_MAX < val)
966+ FATAL("trying to access out of range field %s", x->nval);
967+ m = (int) val;
968+ tempfree(x);
969+ x = fieldadr(m);
970+ x->ctype = OCELL; /* BUG? why are these needed? */
971+ x->csub = CFLD;
972+ return(x);
973+}
974+
975+Cell *substr(Node **a, int nnn) /* substr(a[0], a[1], a[2]) */
976+{
977+ int k, m, n;
978+ int mb, nb;
979+ char *s;
980+ int temp;
981+ Cell *x, *y, *z = NULL;
982+
983+ x = execute(a[0]);
984+ y = execute(a[1]);
985+ if (a[2] != NULL)
986+ z = execute(a[2]);
987+ s = getsval(x);
988+ k = u8_strlen(s) + 1;
989+ if (k <= 1) {
990+ tempfree(x);
991+ tempfree(y);
992+ if (a[2] != NULL) {
993+ tempfree(z);
994+ }
995+ x = gettemp();
996+ setsval(x, "");
997+ return(x);
998+ }
999+ m = (int) getfval(y);
1000+ if (m <= 0)
1001+ m = 1;
1002+ else if (m > k)
1003+ m = k;
1004+ tempfree(y);
1005+ if (a[2] != NULL) {
1006+ n = (int) getfval(z);
1007+ tempfree(z);
1008+ } else
1009+ n = k - 1;
1010+ if (n < 0)
1011+ n = 0;
1012+ else if (n > k - m)
1013+ n = k - m;
1014+ /* m is start, n is length from there */
1015+ DPRINTF("substr: m=%d, n=%d, s=%s\n", m, n, s);
1016+ y = gettemp();
1017+ mb = u8_char2byte(s, m-1); /* byte offset of start char in s */
1018+ nb = u8_char2byte(s, m-1+n); /* byte offset of end+1 char in s */
1019+
1020+ temp = s[nb]; /* with thanks to John Linderman */
1021+ s[nb] = '\0';
1022+ setsval(y, s + mb);
1023+ s[nb] = temp;
1024+ tempfree(x);
1025+ return(y);
1026+}
1027+
1028+Cell *sindex(Node **a, int nnn) /* index(a[0], a[1]) */
1029+{
1030+ Cell *x, *y, *z;
1031+ char *s1, *s2, *p1, *p2, *q;
1032+ Awkfloat v = 0.0;
1033+
1034+ x = execute(a[0]);
1035+ s1 = getsval(x);
1036+ y = execute(a[1]);
1037+ s2 = getsval(y);
1038+
1039+ z = gettemp();
1040+ for (p1 = s1; *p1 != '\0'; p1++) {
1041+ for (q = p1, p2 = s2; *p2 != '\0' && *q == *p2; q++, p2++)
1042+ continue;
1043+ if (*p2 == '\0') {
1044+ /* v = (Awkfloat) (p1 - s1 + 1); origin 1 */
1045+
1046+ /* should be a function: used in match() as well */
1047+ int i, len;
1048+ v = 0;
1049+ for (i = 0; i < p1-s1+1; i += len) {
1050+ len = u8_nextlen(s1+i);
1051+ v++;
1052+ }
1053+ break;
1054+ }
1055+ }
1056+ tempfree(x);
1057+ tempfree(y);
1058+ setfval(z, v);
1059+ return(z);
1060+}
1061+
1062+int has_utf8(char *s) /* return 1 if s contains any utf-8 (2 bytes or more) character */
1063+{
1064+ int n;
1065+
1066+ for (n = 0; *s != 0; s += n) {
1067+ n = u8_nextlen(s);
1068+ if (n > 1)
1069+ return 1;
1070+ }
1071+ return 0;
1072+}
1073+
1074+#define MAXNUMSIZE 50
1075+
1076+int format(char **pbuf, int *pbufsize, const char *s, Node *a) /* printf-like conversions */
1077+{
1078+ char *fmt;
1079+ char *p, *t;
1080+ const char *os;
1081+ Cell *x;
1082+ int flag = 0, n;
1083+ int fmtwd; /* format width */
1084+ int fmtsz = recsize;
1085+ char *buf = *pbuf;
1086+ int bufsize = *pbufsize;
1087+#define FMTSZ(a) (fmtsz - ((a) - fmt))
1088+#define BUFSZ(a) (bufsize - ((a) - buf))
1089+
1090+ static bool first = true;
1091+ static bool have_a_format = false;
1092+
1093+ if (first) {
1094+ char xbuf[100];
1095+
1096+ snprintf(xbuf, sizeof(xbuf), "%a", 42.0);
1097+ have_a_format = (strcmp(xbuf, "0x1.5p+5") == 0);
1098+ first = false;
1099+ }
1100+
1101+ os = s;
1102+ p = buf;
1103+ if ((fmt = (char *) malloc(fmtsz)) == NULL)
1104+ FATAL("out of memory in format()");
1105+ while (*s) {
1106+ adjbuf(&buf, &bufsize, MAXNUMSIZE+1+p-buf, recsize, &p, "format1");
1107+ if (*s != '%') {
1108+ *p++ = *s++;
1109+ continue;
1110+ }
1111+ if (*(s+1) == '%') {
1112+ *p++ = '%';
1113+ s += 2;
1114+ continue;
1115+ }
1116+ fmtwd = atoi(s+1);
1117+ if (fmtwd < 0)
1118+ fmtwd = -fmtwd;
1119+ adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format2");
1120+ for (t = fmt; (*t++ = *s) != '\0'; s++) {
1121+ if (!adjbuf(&fmt, &fmtsz, MAXNUMSIZE+1+t-fmt, recsize, &t, "format3"))
1122+ FATAL("format item %.30s... ran format() out of memory", os);
1123+ /* Ignore size specifiers */
1124+ if (strchr("hjLlqtz", *s) != NULL) { /* the ansi panoply */
1125+ t--;
1126+ continue;
1127+ }
1128+ if (isalpha((uschar)*s))
1129+ break;
1130+ if (*s == '$') {
1131+ FATAL("'$' not permitted in awk formats");
1132+ }
1133+ if (*s == '*') {
1134+ if (a == NULL) {
1135+ FATAL("not enough args in printf(%s)", os);
1136+ }
1137+ x = execute(a);
1138+ a = a->nnext;
1139+ snprintf(t - 1, FMTSZ(t - 1),
1140+ "%d", fmtwd=(int) getfval(x));
1141+ if (fmtwd < 0)
1142+ fmtwd = -fmtwd;
1143+ adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format");
1144+ t = fmt + strlen(fmt);
1145+ tempfree(x);
1146+ }
1147+ }
1148+ *t = '\0';
1149+ if (fmtwd < 0)
1150+ fmtwd = -fmtwd;
1151+ adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format4");
1152+ switch (*s) {
1153+ case 'a': case 'A':
1154+ if (have_a_format)
1155+ flag = *s;
1156+ else
1157+ flag = 'f';
1158+ break;
1159+ case 'f': case 'e': case 'g': case 'E': case 'G':
1160+ flag = 'f';
1161+ break;
1162+ case 'd': case 'i': case 'o': case 'x': case 'X': case 'u':
1163+ flag = (*s == 'd' || *s == 'i') ? 'd' : 'u';
1164+ *(t-1) = 'j';
1165+ *t = *s;
1166+ *++t = '\0';
1167+ break;
1168+ case 's':
1169+ flag = 's';
1170+ break;
1171+ case 'c':
1172+ flag = 'c';
1173+ break;
1174+ default:
1175+ WARNING("weird printf conversion %s", fmt);
1176+ flag = '?';
1177+ break;
1178+ }
1179+ if (a == NULL)
1180+ FATAL("not enough args in printf(%s)", os);
1181+ x = execute(a);
1182+ a = a->nnext;
1183+ n = MAXNUMSIZE;
1184+ if (fmtwd > n)
1185+ n = fmtwd;
1186+ adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, "format5");
1187+ switch (flag) {
1188+ case '?':
1189+ snprintf(p, BUFSZ(p), "%s", fmt); /* unknown, so dump it too */
1190+ t = getsval(x);
1191+ n = strlen(t);
1192+ if (fmtwd > n)
1193+ n = fmtwd;
1194+ adjbuf(&buf, &bufsize, 1+strlen(p)+n+p-buf, recsize, &p, "format6");
1195+ p += strlen(p);
1196+ snprintf(p, BUFSZ(p), "%s", t);
1197+ break;
1198+ case 'a':
1199+ case 'A':
1200+ case 'f': snprintf(p, BUFSZ(p), fmt, getfval(x)); break;
1201+ case 'd': snprintf(p, BUFSZ(p), fmt, (intmax_t) getfval(x)); break;
1202+ case 'u': snprintf(p, BUFSZ(p), fmt, (uintmax_t) getfval(x)); break;
1203+
1204+ case 's': {
1205+ t = getsval(x);
1206+ n = strlen(t);
1207+ /* if simple format or no utf-8 in the string, sprintf works */
1208+ if (!has_utf8(t) || strcmp(fmt,"%s") == 0) {
1209+ if (fmtwd > n)
1210+ n = fmtwd;
1211+ if (!adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, "format7"))
1212+ FATAL("huge string/format (%d chars) in printf %.30s..." \
1213+ " ran format() out of memory", n, t);
1214+ snprintf(p, BUFSZ(p), fmt, t);
1215+ break;
1216+ }
1217+
1218+ /* get here if string has utf-8 chars and fmt is not plain %s */
1219+ /* "%-w.ps", where -, w and .p are all optional */
1220+ /* '0' before the w is a flag character */
1221+ /* fmt points at % */
1222+ int ljust = 0, wid = 0, prec = n, pad = 0;
1223+ char *f = fmt+1;
1224+ if (f[0] == '-') {
1225+ ljust = 1;
1226+ f++;
1227+ }
1228+ // flags '0' and '+' are recognized but skipped
1229+ if (f[0] == '0') {
1230+ f++;
1231+ if (f[0] == '+')
1232+ f++;
1233+ }
1234+ if (f[0] == '+') {
1235+ f++;
1236+ if (f[0] == '0')
1237+ f++;
1238+ }
1239+ if (isdigit(f[0])) { /* there is a wid */
1240+ wid = strtol(f, &f, 10);
1241+ }
1242+ if (f[0] == '.') { /* there is a .prec */
1243+ prec = strtol(++f, &f, 10);
1244+ }
1245+ if (prec > u8_strlen(t))
1246+ prec = u8_strlen(t);
1247+ pad = wid>prec ? wid - prec : 0; // has to be >= 0
1248+ int i, k, n;
1249+
1250+ if (ljust) { // print prec chars from t, then pad blanks
1251+ n = u8_char2byte(t, prec);
1252+ for (k = 0; k < n; k++) {
1253+ //putchar(t[k]);
1254+ *p++ = t[k];
1255+ }
1256+ for (i = 0; i < pad; i++) {
1257+ //printf(" ");
1258+ *p++ = ' ';
1259+ }
1260+ } else { // print pad blanks, then prec chars from t
1261+ for (i = 0; i < pad; i++) {
1262+ //printf(" ");
1263+ *p++ = ' ';
1264+ }
1265+ n = u8_char2byte(t, prec);
1266+ for (k = 0; k < n; k++) {
1267+ //putchar(t[k]);
1268+ *p++ = t[k];
1269+ }
1270+ }
1271+ *p = 0;
1272+ break;
1273+ }
1274+
1275+ case 'c': {
1276+ /*
1277+ * If a numeric value is given, awk should just turn
1278+ * it into a character and print it:
1279+ * BEGIN { printf("%c\n", 65) }
1280+ * prints "A".
1281+ *
1282+ * But what if the numeric value is > 128 and
1283+ * represents a valid Unicode code point?!? We do
1284+ * our best to convert it back into UTF-8. If we
1285+ * can't, we output the encoding of the Unicode
1286+ * "invalid character", 0xFFFD.
1287+ */
1288+ if (isnum(x)) {
1289+ int charval = (int) getfval(x);
1290+
1291+ if (charval != 0) {
1292+ if (charval < 128 || awk_mb_cur_max == 1)
1293+ snprintf(p, BUFSZ(p), fmt, charval);
1294+ else {
1295+ // possible unicode character
1296+ size_t count;
1297+ char *bs = wide_char_to_byte_str(charval, &count);
1298+
1299+ if (bs == NULL) { // invalid character
1300+ // use unicode invalid character, 0xFFFD
1301+ static char invalid_char[] = "\357\277\275";
1302+ bs = invalid_char;
1303+ count = 3;
1304+ }
1305+ t = bs;
1306+ n = count;
1307+ goto format_percent_c;
1308+ }
1309+ } else {
1310+ *p++ = '\0'; /* explicit null byte */
1311+ *p = '\0'; /* next output will start here */
1312+ }
1313+ break;
1314+ }
1315+ t = getsval(x);
1316+ n = u8_nextlen(t);
1317+ format_percent_c:
1318+ if (n < 2) { /* not utf8 */
1319+ snprintf(p, BUFSZ(p), fmt, getsval(x)[0]);
1320+ break;
1321+ }
1322+
1323+ // utf8 character, almost same song and dance as for %s
1324+ int ljust = 0, wid = 0, prec = n, pad = 0;
1325+ char *f = fmt+1;
1326+ if (f[0] == '-') {
1327+ ljust = 1;
1328+ f++;
1329+ }
1330+ // flags '0' and '+' are recognized but skipped
1331+ if (f[0] == '0') {
1332+ f++;
1333+ if (f[0] == '+')
1334+ f++;
1335+ }
1336+ if (f[0] == '+') {
1337+ f++;
1338+ if (f[0] == '0')
1339+ f++;
1340+ }
1341+ if (isdigit(f[0])) { /* there is a wid */
1342+ wid = strtol(f, &f, 10);
1343+ }
1344+ if (f[0] == '.') { /* there is a .prec */
1345+ prec = strtol(++f, &f, 10);
1346+ }
1347+ if (prec > 1) // %c --> only one character
1348+ prec = 1;
1349+ pad = wid>prec ? wid - prec : 0; // has to be >= 0
1350+ int i;
1351+
1352+ if (ljust) { // print one char from t, then pad blanks
1353+ for (i = 0; i < n; i++)
1354+ *p++ = t[i];
1355+ for (i = 0; i < pad; i++) {
1356+ //printf(" ");
1357+ *p++ = ' ';
1358+ }
1359+ } else { // print pad blanks, then prec chars from t
1360+ for (i = 0; i < pad; i++) {
1361+ //printf(" ");
1362+ *p++ = ' ';
1363+ }
1364+ for (i = 0; i < n; i++)
1365+ *p++ = t[i];
1366+ }
1367+ *p = 0;
1368+ break;
1369+ }
1370+ default:
1371+ FATAL("can't happen: bad conversion %c in format()", flag);
1372+ }
1373+
1374+ tempfree(x);
1375+ p += strlen(p);
1376+ s++;
1377+ }
1378+ *p = '\0';
1379+ free(fmt);
1380+ for ( ; a; a = a->nnext) { /* evaluate any remaining args */
1381+ x = execute(a);
1382+ tempfree(x);
1383+ }
1384+ *pbuf = buf;
1385+ *pbufsize = bufsize;
1386+ return p - buf;
1387+}
1388+
1389+Cell *awksprintf(Node **a, int n) /* sprintf(a[0]) */
1390+{
1391+ Cell *x;
1392+ Node *y;
1393+ char *buf;
1394+ int bufsz=3*recsize;
1395+
1396+ if ((buf = (char *) malloc(bufsz)) == NULL)
1397+ FATAL("out of memory in awksprintf");
1398+ y = a[0]->nnext;
1399+ x = execute(a[0]);
1400+ if (format(&buf, &bufsz, getsval(x), y) == -1)
1401+ FATAL("sprintf string %.30s... too long. can't happen.", buf);
1402+ tempfree(x);
1403+ x = gettemp();
1404+ x->sval = buf;
1405+ x->tval = STR;
1406+ return(x);
1407+}
1408+
1409+Cell *awkprintf(Node **a, int n) /* printf */
1410+{ /* a[0] is list of args, starting with format string */
1411+ /* a[1] is redirection operator, a[2] is redirection file */
1412+ FILE *fp;
1413+ Cell *x;
1414+ Node *y;
1415+ char *buf;
1416+ int len;
1417+ int bufsz=3*recsize;
1418+
1419+ if ((buf = (char *) malloc(bufsz)) == NULL)
1420+ FATAL("out of memory in awkprintf");
1421+ y = a[0]->nnext;
1422+ x = execute(a[0]);
1423+ if ((len = format(&buf, &bufsz, getsval(x), y)) == -1)
1424+ FATAL("printf string %.30s... too long. can't happen.", buf);
1425+ tempfree(x);
1426+ if (a[1] == NULL) {
1427+ /* fputs(buf, stdout); */
1428+ fwrite(buf, len, 1, stdout);
1429+ if (ferror(stdout))
1430+ FATAL("write error on stdout");
1431+ } else {
1432+ fp = redirect(ptoi(a[1]), a[2]);
1433+ /* fputs(buf, fp); */
1434+ fwrite(buf, len, 1, fp);
1435+ fflush(fp);
1436+ if (ferror(fp))
1437+ FATAL("write error on %s", filename(fp));
1438+ }
1439+ free(buf);
1440+ return(True);
1441+}
1442+
1443+Cell *arith(Node **a, int n) /* a[0] + a[1], etc. also -a[0] */
1444+{
1445+ Awkfloat i, j = 0;
1446+ double v;
1447+ Cell *x, *y, *z;
1448+
1449+ x = execute(a[0]);
1450+ i = getfval(x);
1451+ tempfree(x);
1452+ if (n != UMINUS && n != UPLUS) {
1453+ y = execute(a[1]);
1454+ j = getfval(y);
1455+ tempfree(y);
1456+ }
1457+ z = gettemp();
1458+ switch (n) {
1459+ case ADD:
1460+ i += j;
1461+ break;
1462+ case MINUS:
1463+ i -= j;
1464+ break;
1465+ case MULT:
1466+ i *= j;
1467+ break;
1468+ case DIVIDE:
1469+ if (j == 0)
1470+ FATAL("division by zero");
1471+ i /= j;
1472+ break;
1473+ case MOD:
1474+ if (j == 0)
1475+ FATAL("division by zero in mod");
1476+ modf(i/j, &v);
1477+ i = i - j * v;
1478+ break;
1479+ case UMINUS:
1480+ i = -i;
1481+ break;
1482+ case UPLUS: /* handled by getfval(), above */
1483+ break;
1484+ case POWER:
1485+ if (j >= 0 && modf(j, &v) == 0.0) /* pos integer exponent */
1486+ i = ipow(i, (int) j);
1487+ else
1488+ i = pow_errcheck(i, j);
1489+ break;
1490+ default: /* can't happen */
1491+ FATAL("illegal arithmetic operator %d", n);
1492+ }
1493+ setfval(z, i);
1494+ return(z);
1495+}
1496+
1497+double ipow(double x, int n) /* x**n. ought to be done by pow, but isn't always */
1498+{
1499+ double v;
1500+
1501+ if (n <= 0)
1502+ return 1;
1503+ v = ipow(x, n/2);
1504+ if (n % 2 == 0)
1505+ return v * v;
1506+ else
1507+ return x * v * v;
1508+}
1509+
1510+Cell *incrdecr(Node **a, int n) /* a[0]++, etc. */
1511+{
1512+ Cell *x, *z;
1513+ int k;
1514+ Awkfloat xf;
1515+
1516+ x = execute(a[0]);
1517+ xf = getfval(x);
1518+ k = (n == PREINCR || n == POSTINCR) ? 1 : -1;
1519+ if (n == PREINCR || n == PREDECR) {
1520+ setfval(x, xf + k);
1521+ return(x);
1522+ }
1523+ z = gettemp();
1524+ setfval(z, xf);
1525+ setfval(x, xf + k);
1526+ tempfree(x);
1527+ return(z);
1528+}
1529+
1530+Cell *assign(Node **a, int n) /* a[0] = a[1], a[0] += a[1], etc. */
1531+{ /* this is subtle; don't muck with it. */
1532+ Cell *x, *y;
1533+ Awkfloat xf, yf;
1534+ double v;
1535+
1536+ y = execute(a[1]);
1537+ x = execute(a[0]);
1538+ if (n == ASSIGN) { /* ordinary assignment */
1539+ if (x == y && !(x->tval & (FLD|REC)) && x != nfloc)
1540+ ; /* self-assignment: leave alone unless it's a field or NF */
1541+ else if ((y->tval & (STR|NUM)) == (STR|NUM)) {
1542+ yf = getfval(y);
1543+ setsval(x, getsval(y));
1544+ x->fval = yf;
1545+ x->tval |= NUM;
1546+ }
1547+ else if (isstr(y))
1548+ setsval(x, getsval(y));
1549+ else if (isnum(y))
1550+ setfval(x, getfval(y));
1551+ else
1552+ funnyvar(y, "read value of");
1553+ tempfree(y);
1554+ return(x);
1555+ }
1556+ xf = getfval(x);
1557+ yf = getfval(y);
1558+ switch (n) {
1559+ case ADDEQ:
1560+ xf += yf;
1561+ break;
1562+ case SUBEQ:
1563+ xf -= yf;
1564+ break;
1565+ case MULTEQ:
1566+ xf *= yf;
1567+ break;
1568+ case DIVEQ:
1569+ if ((x->tval & CON) != 0)
1570+ FATAL("non-constant required for left side of /=");
1571+ if (yf == 0)
1572+ FATAL("division by zero in /=");
1573+ xf /= yf;
1574+ break;
1575+ case MODEQ:
1576+ if (yf == 0)
1577+ FATAL("division by zero in %%=");
1578+ modf(xf/yf, &v);
1579+ xf = xf - yf * v;
1580+ break;
1581+ case POWEQ:
1582+ if (yf >= 0 && modf(yf, &v) == 0.0) /* pos integer exponent */
1583+ xf = ipow(xf, (int) yf);
1584+ else
1585+ xf = pow_errcheck(xf, yf);
1586+ break;
1587+ default:
1588+ FATAL("illegal assignment operator %d", n);
1589+ break;
1590+ }
1591+ tempfree(y);
1592+ setfval(x, xf);
1593+ return(x);
1594+}
1595+
1596+Cell *cat(Node **a, int q) /* a[0] cat a[1] */
1597+{
1598+ Cell *x, *y, *z;
1599+ int n1, n2;
1600+ char *s = NULL;
1601+ int ssz = 0;
1602+
1603+ x = execute(a[0]);
1604+ n1 = strlen(getsval(x));
1605+ adjbuf(&s, &ssz, n1 + 1, recsize, 0, "cat1");
1606+ memcpy(s, x->sval, n1);
1607+
1608+ tempfree(x);
1609+
1610+ y = execute(a[1]);
1611+ n2 = strlen(getsval(y));
1612+ adjbuf(&s, &ssz, n1 + n2 + 1, recsize, 0, "cat2");
1613+ memcpy(s + n1, y->sval, n2);
1614+ s[n1 + n2] = '\0';
1615+
1616+ tempfree(y);
1617+
1618+ z = gettemp();
1619+ z->sval = s;
1620+ z->tval = STR;
1621+
1622+ return(z);
1623+}
1624+
1625+Cell *pastat(Node **a, int n) /* a[0] { a[1] } */
1626+{
1627+ Cell *x;
1628+
1629+ if (a[0] == NULL)
1630+ x = execute(a[1]);
1631+ else {
1632+ x = execute(a[0]);
1633+ if (istrue(x)) {
1634+ tempfree(x);
1635+ x = execute(a[1]);
1636+ }
1637+ }
1638+ return x;
1639+}
1640+
1641+Cell *dopa2(Node **a, int n) /* a[0], a[1] { a[2] } */
1642+{
1643+ Cell *x;
1644+ int pair;
1645+
1646+ pair = ptoi(a[3]);
1647+ if (pairstack[pair] == 0) {
1648+ x = execute(a[0]);
1649+ if (istrue(x))
1650+ pairstack[pair] = 1;
1651+ tempfree(x);
1652+ }
1653+ if (pairstack[pair] == 1) {
1654+ x = execute(a[1]);
1655+ if (istrue(x))
1656+ pairstack[pair] = 0;
1657+ tempfree(x);
1658+ x = execute(a[2]);
1659+ return(x);
1660+ }
1661+ return(False);
1662+}
1663+
1664+Cell *split(Node **a, int nnn) /* split(a[0], a[1], a[2]); a[3] is type */
1665+{
1666+ Cell *x = NULL, *y, *ap;
1667+ const char *s, *origs, *t;
1668+ const char *fs = NULL;
1669+ char *origfs = NULL;
1670+ int sep;
1671+ char temp, num[50];
1672+ int n, tempstat, arg3type;
1673+ int j;
1674+ double result;
1675+
1676+ y = execute(a[0]); /* source string */
1677+ origs = s = strdup(getsval(y));
1678+ tempfree(y);
1679+ arg3type = ptoi(a[3]);
1680+ if (a[2] == NULL) { /* BUG: CSV should override implicit fs but not explicit */
1681+ fs = getsval(fsloc);
1682+ } else if (arg3type == STRING) { /* split(str,arr,"string") */
1683+ x = execute(a[2]);
1684+ fs = origfs = strdup(getsval(x));
1685+ tempfree(x);
1686+ } else if (arg3type == REGEXPR) {
1687+ fs = "(regexpr)"; /* split(str,arr,/regexpr/) */
1688+ } else {
1689+ FATAL("illegal type of split");
1690+ }
1691+ sep = *fs;
1692+ ap = execute(a[1]); /* array name */
1693+/* BUG 7/26/22: this appears not to reset array: see C1/asplit */
1694+ freesymtab(ap);
1695+ DPRINTF("split: s=|%s|, a=%s, sep=|%s|\n", s, NN(ap->nval), fs);
1696+ ap->tval &= ~STR;
1697+ ap->tval |= ARR;
1698+ ap->sval = (char *) makesymtab(NSYMTAB);
1699+
1700+ n = 0;
1701+ if (arg3type == REGEXPR && strlen((char*)((fa*)a[2])->restr) == 0) {
1702+ /* split(s, a, //); have to arrange that it looks like empty sep */
1703+ arg3type = 0;
1704+ fs = "";
1705+ sep = 0;
1706+ }
1707+ if (*s != '\0' && (strlen(fs) > 1 || arg3type == REGEXPR)) { /* reg expr */
1708+ fa *pfa;
1709+ if (arg3type == REGEXPR) { /* it's ready already */
1710+ pfa = (fa *) a[2];
1711+ } else {
1712+ pfa = makedfa(fs, 1);
1713+ }
1714+ if (nematch(pfa,s)) {
1715+ tempstat = pfa->initstat;
1716+ pfa->initstat = 2;
1717+ do {
1718+ n++;
1719+ snprintf(num, sizeof(num), "%d", n);
1720+ temp = *patbeg;
1721+ setptr(patbeg, '\0');
1722+ if (is_number(s, & result))
1723+ setsymtab(num, s, result, STR|NUM, (Array *) ap->sval);
1724+ else
1725+ setsymtab(num, s, 0.0, STR, (Array *) ap->sval);
1726+ setptr(patbeg, temp);
1727+ s = patbeg + patlen;
1728+ if (*(patbeg+patlen-1) == '\0' || *s == '\0') {
1729+ n++;
1730+ snprintf(num, sizeof(num), "%d", n);
1731+ setsymtab(num, "", 0.0, STR, (Array *) ap->sval);
1732+ pfa->initstat = tempstat;
1733+ goto spdone;
1734+ }
1735+ } while (nematch(pfa,s));
1736+ pfa->initstat = tempstat; /* bwk: has to be here to reset */
1737+ /* cf gsub and refldbld */
1738+ }
1739+ n++;
1740+ snprintf(num, sizeof(num), "%d", n);
1741+ if (is_number(s, & result))
1742+ setsymtab(num, s, result, STR|NUM, (Array *) ap->sval);
1743+ else
1744+ setsymtab(num, s, 0.0, STR, (Array *) ap->sval);
1745+ spdone:
1746+ pfa = NULL;
1747+
1748+ } else if (a[2] == NULL && CSV) { /* CSV only if no explicit separator */
1749+ char *newt = (char *) malloc(strlen(s) + 1); /* for building new string; reuse for each field */
1750+ if (newt == NULL)
1751+ FATAL("out of space in split");
1752+ for (;;) {
1753+ char *fr = newt;
1754+ n++;
1755+ if (*s == '"' ) { /* start of "..." */
1756+ for (s++ ; *s != '\0'; ) {
1757+ if (*s == '"' && s[1] != '\0' && s[1] == '"') {
1758+ s += 2; /* doubled quote */
1759+ *fr++ = '"';
1760+ } else if (*s == '"' && (s[1] == '\0' || s[1] == ',')) {
1761+ s++; /* skip over closing quote */
1762+ break;
1763+ } else {
1764+ *fr++ = *s++;
1765+ }
1766+ }
1767+ *fr++ = 0;
1768+ } else { /* unquoted field */
1769+ while (*s != ',' && *s != '\0')
1770+ *fr++ = *s++;
1771+ *fr++ = 0;
1772+ }
1773+ snprintf(num, sizeof(num), "%d", n);
1774+ if (is_number(newt, &result))
1775+ setsymtab(num, newt, result, STR|NUM, (Array *) ap->sval);
1776+ else
1777+ setsymtab(num, newt, 0.0, STR, (Array *) ap->sval);
1778+ if (*s++ == '\0')
1779+ break;
1780+ }
1781+ free(newt);
1782+
1783+ } else if (!CSV && sep == ' ') { /* usual case: split on white space */
1784+ for (n = 0; ; ) {
1785+#define ISWS(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
1786+ while (ISWS(*s))
1787+ s++;
1788+ if (*s == '\0')
1789+ break;
1790+ n++;
1791+ t = s;
1792+ do
1793+ s++;
1794+ while (*s != '\0' && !ISWS(*s));
1795+ temp = *s;
1796+ setptr(s, '\0');
1797+ snprintf(num, sizeof(num), "%d", n);
1798+ if (is_number(t, & result))
1799+ setsymtab(num, t, result, STR|NUM, (Array *) ap->sval);
1800+ else
1801+ setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
1802+ setptr(s, temp);
1803+ if (*s != '\0')
1804+ s++;
1805+ }
1806+
1807+ } else if (sep == 0) { /* new: split(s, a, "") => 1 char/elem */
1808+ for (n = 0; *s != '\0'; s += u8_nextlen(s)) {
1809+ char buf[10];
1810+ n++;
1811+ snprintf(num, sizeof(num), "%d", n);
1812+
1813+ for (j = 0; j < u8_nextlen(s); j++) {
1814+ buf[j] = s[j];
1815+ }
1816+ buf[j] = '\0';
1817+
1818+ if (isdigit((uschar)buf[0]))
1819+ setsymtab(num, buf, atof(buf), STR|NUM, (Array *) ap->sval);
1820+ else
1821+ setsymtab(num, buf, 0.0, STR, (Array *) ap->sval);
1822+ }
1823+
1824+ } else if (*s != '\0') { /* some random single character */
1825+ for (;;) {
1826+ n++;
1827+ t = s;
1828+ while (*s != sep && *s != '\0')
1829+ s++;
1830+ temp = *s;
1831+ setptr(s, '\0');
1832+ snprintf(num, sizeof(num), "%d", n);
1833+ if (is_number(t, & result))
1834+ setsymtab(num, t, result, STR|NUM, (Array *) ap->sval);
1835+ else
1836+ setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
1837+ setptr(s, temp);
1838+ if (*s++ == '\0')
1839+ break;
1840+ }
1841+ }
1842+ tempfree(ap);
1843+ xfree(origs);
1844+ xfree(origfs);
1845+ x = gettemp();
1846+ x->tval = NUM;
1847+ x->fval = n;
1848+ return(x);
1849+}
1850+
1851+Cell *condexpr(Node **a, int n) /* a[0] ? a[1] : a[2] */
1852+{
1853+ Cell *x;
1854+
1855+ x = execute(a[0]);
1856+ if (istrue(x)) {
1857+ tempfree(x);
1858+ x = execute(a[1]);
1859+ } else {
1860+ tempfree(x);
1861+ x = execute(a[2]);
1862+ }
1863+ return(x);
1864+}
1865+
1866+Cell *ifstat(Node **a, int n) /* if (a[0]) a[1]; else a[2] */
1867+{
1868+ Cell *x;
1869+
1870+ x = execute(a[0]);
1871+ if (istrue(x)) {
1872+ tempfree(x);
1873+ x = execute(a[1]);
1874+ } else if (a[2] != NULL) {
1875+ tempfree(x);
1876+ x = execute(a[2]);
1877+ }
1878+ return(x);
1879+}
1880+
1881+Cell *whilestat(Node **a, int n) /* while (a[0]) a[1] */
1882+{
1883+ Cell *x;
1884+
1885+ for (;;) {
1886+ x = execute(a[0]);
1887+ if (!istrue(x))
1888+ return(x);
1889+ tempfree(x);
1890+ x = execute(a[1]);
1891+ if (isbreak(x)) {
1892+ x = True;
1893+ return(x);
1894+ }
1895+ if (isnext(x) || isexit(x) || isret(x))
1896+ return(x);
1897+ tempfree(x);
1898+ }
1899+}
1900+
1901+Cell *dostat(Node **a, int n) /* do a[0]; while(a[1]) */
1902+{
1903+ Cell *x;
1904+
1905+ for (;;) {
1906+ x = execute(a[0]);
1907+ if (isbreak(x))
1908+ return True;
1909+ if (isnext(x) || isexit(x) || isret(x))
1910+ return(x);
1911+ tempfree(x);
1912+ x = execute(a[1]);
1913+ if (!istrue(x))
1914+ return(x);
1915+ tempfree(x);
1916+ }
1917+}
1918+
1919+Cell *forstat(Node **a, int n) /* for (a[0]; a[1]; a[2]) a[3] */
1920+{
1921+ Cell *x;
1922+
1923+ x = execute(a[0]);
1924+ tempfree(x);
1925+ for (;;) {
1926+ if (a[1]!=NULL) {
1927+ x = execute(a[1]);
1928+ if (!istrue(x)) return(x);
1929+ else tempfree(x);
1930+ }
1931+ x = execute(a[3]);
1932+ if (isbreak(x)) /* turn off break */
1933+ return True;
1934+ if (isnext(x) || isexit(x) || isret(x))
1935+ return(x);
1936+ tempfree(x);
1937+ x = execute(a[2]);
1938+ tempfree(x);
1939+ }
1940+}
1941+
1942+Cell *instat(Node **a, int n) /* for (a[0] in a[1]) a[2] */
1943+{
1944+ Cell *x, *vp, *arrayp, *cp, *ncp;
1945+ Array *tp;
1946+ int i;
1947+
1948+ vp = execute(a[0]);
1949+ arrayp = execute(a[1]);
1950+ if (!isarr(arrayp)) {
1951+ return True;
1952+ }
1953+ tp = (Array *) arrayp->sval;
1954+ tempfree(arrayp);
1955+ for (i = 0; i < tp->size; i++) { /* this routine knows too much */
1956+ for (cp = tp->tab[i]; cp != NULL; cp = ncp) {
1957+ setsval(vp, cp->nval);
1958+ ncp = cp->cnext;
1959+ x = execute(a[2]);
1960+ if (isbreak(x)) {
1961+ tempfree(vp);
1962+ return True;
1963+ }
1964+ if (isnext(x) || isexit(x) || isret(x)) {
1965+ tempfree(vp);
1966+ return(x);
1967+ }
1968+ tempfree(x);
1969+ }
1970+ }
1971+ return True;
1972+}
1973+
1974+static char *nawk_convert(const char *s, int (*fun_c)(int),
1975+ wint_t (*fun_wc)(wint_t))
1976+{
1977+ char *buf = NULL;
1978+ char *pbuf = NULL;
1979+ const char *ps = NULL;
1980+ size_t n = 0;
1981+ wchar_t wc;
1982+ const size_t sz = awk_mb_cur_max;
1983+ int unused;
1984+
1985+ if (sz == 1) {
1986+ buf = tostring(s);
1987+
1988+ for (pbuf = buf; *pbuf; pbuf++)
1989+ *pbuf = fun_c((uschar)*pbuf);
1990+
1991+ return buf;
1992+ } else {
1993+ /* upper/lower character may be shorter/longer */
1994+ buf = tostringN(s, strlen(s) * sz + 1);
1995+
1996+ (void) mbtowc(NULL, NULL, 0); /* reset internal state */
1997+ /*
1998+ * Reset internal state here too.
1999+ * Assign result to avoid a compiler warning. (Casting to void
2000+ * doesn't work.)
2001+ * Increment said variable to avoid a different warning.
2002+ */
2003+ unused = wctomb(NULL, L'\0');
2004+ unused++;
2005+
2006+ ps = s;
2007+ pbuf = buf;
2008+ while (n = mbtowc(&wc, ps, sz),
2009+ n > 0 && n != (size_t)-1 && n != (size_t)-2)
2010+ {
2011+ ps += n;
2012+
2013+ n = wctomb(pbuf, fun_wc(wc));
2014+ if (n == (size_t)-1)
2015+ FATAL("illegal wide character %s", s);
2016+
2017+ pbuf += n;
2018+ }
2019+
2020+ *pbuf = '\0';
2021+
2022+ if (n)
2023+ FATAL("illegal byte sequence %s", s);
2024+
2025+ return buf;
2026+ }
2027+}
2028+
2029+#ifdef __DJGPP__
2030+static wint_t towupper(wint_t wc)
2031+{
2032+ if (wc >= 0 && wc < 256)
2033+ return toupper(wc & 0xFF);
2034+
2035+ return wc;
2036+}
2037+
2038+static wint_t towlower(wint_t wc)
2039+{
2040+ if (wc >= 0 && wc < 256)
2041+ return tolower(wc & 0xFF);
2042+
2043+ return wc;
2044+}
2045+#endif
2046+
2047+static char *nawk_toupper(const char *s)
2048+{
2049+ return nawk_convert(s, toupper, towupper);
2050+}
2051+
2052+static char *nawk_tolower(const char *s)
2053+{
2054+ return nawk_convert(s, tolower, towlower);
2055+}
2056+
2057+
2058+
2059+Cell *bltin(Node **a, int n) /* builtin functions. a[0] is type, a[1] is arg list */
2060+{
2061+ Cell *x, *y;
2062+ Awkfloat u = 0;
2063+ int t;
2064+ Awkfloat tmp;
2065+ char *buf;
2066+ Node *nextarg;
2067+ FILE *fp;
2068+ int status = 0;
2069+ int estatus = 0;
2070+
2071+ t = ptoi(a[0]);
2072+ x = execute(a[1]);
2073+ nextarg = a[1]->nnext;
2074+ switch (t) {
2075+ case FLENGTH:
2076+ if (isarr(x))
2077+ u = ((Array *) x->sval)->nelem; /* GROT. should be function*/
2078+ else
2079+ u = u8_strlen(getsval(x));
2080+ break;
2081+ case FLOG:
2082+ u = log_errcheck(getfval(x));
2083+ break;
2084+ case FINT:
2085+ modf(getfval(x), &u); break;
2086+ case FEXP:
2087+ u = exp_errcheck(getfval(x));
2088+ break;
2089+ case FSQRT:
2090+ u = sqrt_errcheck(getfval(x));
2091+ break;
2092+ case FSIN:
2093+ u = sin(getfval(x)); break;
2094+ case FCOS:
2095+ u = cos(getfval(x)); break;
2096+ case FATAN:
2097+ if (nextarg == NULL) {
2098+ WARNING("atan2 requires two arguments; returning 1.0");
2099+ u = 1.0;
2100+ } else {
2101+ y = execute(a[1]->nnext);
2102+ u = atan2(getfval(x), getfval(y));
2103+ tempfree(y);
2104+ nextarg = nextarg->nnext;
2105+ }
2106+ break;
2107+ case FSYSTEM:
2108+ fflush(stdout); /* in case something is buffered already */
2109+ estatus = status = system(getsval(x));
2110+ if (status != -1) {
2111+ if (WIFEXITED(status)) {
2112+ estatus = WEXITSTATUS(status);
2113+ } else if (WIFSIGNALED(status)) {
2114+ estatus = WTERMSIG(status) + 256;
2115+#ifdef WCOREDUMP
2116+ if (WCOREDUMP(status))
2117+ estatus += 256;
2118+#endif
2119+ } else /* something else?!? */
2120+ estatus = 0;
2121+ }
2122+ /* else estatus was set to -1 */
2123+ u = estatus;
2124+ break;
2125+ case FRAND:
2126+ /* random() returns numbers in [0..2^31-1]
2127+ * in order to get a number in [0, 1), divide it by 2^31
2128+ */
2129+ do {
2130+ /* exact if Awkfloat wide enough */
2131+ u = (Awkfloat) random();
2132+ u /= 0x80000000; /* should be exact */
2133+ } while (u >= 1.0); /* in case Awkfloat is narrow */
2134+ break;
2135+ case FSRAND:
2136+ if (isrec(x)) /* no argument provided */
2137+ u = time((time_t *)0);
2138+ else
2139+ u = getfval(x);
2140+ tmp = u;
2141+ srandom((unsigned long) u);
2142+ u = srand_seed;
2143+ srand_seed = tmp;
2144+ break;
2145+ case FTOUPPER:
2146+ case FTOLOWER:
2147+ if (t == FTOUPPER)
2148+ buf = nawk_toupper(getsval(x));
2149+ else
2150+ buf = nawk_tolower(getsval(x));
2151+ tempfree(x);
2152+ x = gettemp();
2153+ setsval(x, buf);
2154+ free(buf);
2155+ return x;
2156+ case FFLUSH:
2157+ if (isrec(x) || strlen(getsval(x)) == 0) {
2158+ flush_all(); /* fflush() or fflush("") -> all */
2159+ u = 0;
2160+ } else if ((fp = openfile(FFLUSH, getsval(x), NULL)) == NULL)
2161+ u = EOF;
2162+ else
2163+ u = fflush(fp);
2164+ break;
2165+ default: /* can't happen */
2166+ FATAL("illegal function type %d", t);
2167+ break;
2168+ }
2169+ tempfree(x);
2170+ x = gettemp();
2171+ setfval(x, u);
2172+ if (nextarg != NULL) {
2173+ WARNING("warning: function has too many arguments");
2174+ for ( ; nextarg; nextarg = nextarg->nnext) {
2175+ y = execute(nextarg);
2176+ tempfree(y);
2177+ }
2178+ }
2179+ return(x);
2180+}
2181+
2182+Cell *printstat(Node **a, int n) /* print a[0] */
2183+{
2184+ Node *x;
2185+ Cell *y;
2186+ FILE *fp;
2187+
2188+ if (a[1] == NULL) /* a[1] is redirection operator, a[2] is file */
2189+ fp = stdout;
2190+ else
2191+ fp = redirect(ptoi(a[1]), a[2]);
2192+ for (x = a[0]; x != NULL; x = x->nnext) {
2193+ y = execute(x);
2194+ fputs(getpssval(y), fp);
2195+ tempfree(y);
2196+ if (x->nnext == NULL)
2197+ fputs(getsval(orsloc), fp);
2198+ else
2199+ fputs(getsval(ofsloc), fp);
2200+ }
2201+ if (a[1] != NULL)
2202+ fflush(fp);
2203+ if (ferror(fp))
2204+ FATAL("write error on %s", filename(fp));
2205+ return(True);
2206+}
2207+
2208+Cell *nullproc(Node **a, int n)
2209+{
2210+ return 0;
2211+}
2212+
2213+
2214+FILE *redirect(int a, Node *b) /* set up all i/o redirections */
2215+{
2216+ FILE *fp;
2217+ Cell *x;
2218+ char *fname;
2219+
2220+ x = execute(b);
2221+ fname = getsval(x);
2222+ fp = openfile(a, fname, NULL);
2223+ if (fp == NULL)
2224+ FATAL("can't open file %s", fname);
2225+ tempfree(x);
2226+ return fp;
2227+}
2228+
2229+struct files {
2230+ FILE *fp;
2231+ const char *fname;
2232+ int mode; /* '|', 'a', 'w' => LE/LT, GT */
2233+} *files;
2234+
2235+size_t nfiles;
2236+
2237+static void stdinit(void) /* in case stdin, etc., are not constants */
2238+{
2239+ nfiles = FOPEN_MAX;
2240+ files = (struct files *) calloc(nfiles, sizeof(*files));
2241+ if (files == NULL)
2242+ FATAL("can't allocate file memory for %zu files", nfiles);
2243+ files[0].fp = stdin;
2244+ files[0].fname = tostring("/dev/stdin");
2245+ files[0].mode = LT;
2246+ files[1].fp = stdout;
2247+ files[1].fname = tostring("/dev/stdout");
2248+ files[1].mode = GT;
2249+ files[2].fp = stderr;
2250+ files[2].fname = tostring("/dev/stderr");
2251+ files[2].mode = GT;
2252+}
2253+
2254+FILE *openfile(int a, const char *us, bool *pnewflag)
2255+{
2256+ const char *s = us;
2257+ size_t i;
2258+ int m;
2259+ FILE *fp = NULL;
2260+ struct stat sbuf;
2261+
2262+ if (*s == '\0')
2263+ FATAL("null file name in print or getline");
2264+
2265+ for (i = 0; i < nfiles; i++)
2266+ if (files[i].fname && strcmp(s, files[i].fname) == 0 &&
2267+ (a == files[i].mode || (a==APPEND && files[i].mode==GT) ||
2268+ a == FFLUSH)) {
2269+ if (pnewflag)
2270+ *pnewflag = false;
2271+ return files[i].fp;
2272+ }
2273+ if (a == FFLUSH) /* didn't find it, so don't create it! */
2274+ return NULL;
2275+ for (i = 0; i < nfiles; i++)
2276+ if (files[i].fp == NULL)
2277+ break;
2278+ if (i >= nfiles) {
2279+ struct files *nf;
2280+ size_t nnf = nfiles + FOPEN_MAX;
2281+ nf = (struct files *) realloc(files, nnf * sizeof(*nf));
2282+ if (nf == NULL)
2283+ FATAL("cannot grow files for %s and %zu files", s, nnf);
2284+ memset(&nf[nfiles], 0, FOPEN_MAX * sizeof(*nf));
2285+ nfiles = nnf;
2286+ files = nf;
2287+ }
2288+
2289+ fflush(stdout); /* force a semblance of order */
2290+
2291+ /* don't try to read or write a directory */
2292+ if (a == LT || a == GT || a == APPEND)
2293+ if (stat(s, &sbuf) == 0 && S_ISDIR(sbuf.st_mode))
2294+ return NULL;
2295+
2296+ m = a;
2297+ if (a == GT) {
2298+ fp = fopen(s, "w");
2299+ } else if (a == APPEND) {
2300+ fp = fopen(s, "a");
2301+ m = GT; /* so can mix > and >> */
2302+ } else if (a == '|') { /* output pipe */
2303+ fp = popen(s, "w");
2304+ } else if (a == LE) { /* input pipe */
2305+ fp = popen(s, "r");
2306+ } else if (a == LT) { /* getline <file */
2307+ fp = strcmp(s, "-") == 0 ? stdin : fopen(s, "r"); /* "-" is stdin */
2308+ } else /* can't happen */
2309+ FATAL("illegal redirection %d", a);
2310+ if (fp != NULL) {
2311+ files[i].fname = tostring(s);
2312+ files[i].fp = fp;
2313+ files[i].mode = m;
2314+ if (pnewflag)
2315+ *pnewflag = true;
2316+ if (fp != stdin && fp != stdout && fp != stderr)
2317+ (void) fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
2318+ }
2319+ return fp;
2320+}
2321+
2322+const char *filename(FILE *fp)
2323+{
2324+ size_t i;
2325+
2326+ for (i = 0; i < nfiles; i++)
2327+ if (fp == files[i].fp)
2328+ return files[i].fname;
2329+ return "???";
2330+}
2331+
2332+Cell *closefile(Node **a, int n)
2333+{
2334+ Cell *x;
2335+ size_t i;
2336+ bool stat;
2337+
2338+ x = execute(a[0]);
2339+ getsval(x);
2340+ stat = true;
2341+ for (i = 0; i < nfiles; i++) {
2342+ if (!files[i].fname || strcmp(x->sval, files[i].fname) != 0)
2343+ continue;
2344+ if (files[i].mode == GT || files[i].mode == '|')
2345+ fflush(files[i].fp);
2346+ if (ferror(files[i].fp)) {
2347+ if ((files[i].mode == GT && files[i].fp != stderr)
2348+ || files[i].mode == '|')
2349+ FATAL("write error on %s", files[i].fname);
2350+ else
2351+ WARNING("i/o error occurred on %s", files[i].fname);
2352+ }
2353+ if (files[i].fp == stdin || files[i].fp == stdout ||
2354+ files[i].fp == stderr)
2355+ stat = freopen("/dev/null", "r+", files[i].fp) == NULL;
2356+ else if (files[i].mode == '|' || files[i].mode == LE)
2357+ stat = pclose(files[i].fp) == -1;
2358+ else
2359+ stat = fclose(files[i].fp) == EOF;
2360+ if (stat)
2361+ WARNING("i/o error occurred closing %s", files[i].fname);
2362+ xfree(files[i].fname);
2363+ files[i].fname = NULL; /* watch out for ref thru this */
2364+ files[i].fp = NULL;
2365+ break;
2366+ }
2367+ tempfree(x);
2368+ x = gettemp();
2369+ setfval(x, (Awkfloat) (stat ? -1 : 0));
2370+ return(x);
2371+}
2372+
2373+void closeall(void)
2374+{
2375+ size_t i;
2376+ bool stat = false;
2377+
2378+ for (i = 0; i < nfiles; i++) {
2379+ if (! files[i].fp)
2380+ continue;
2381+ if (files[i].mode == GT || files[i].mode == '|')
2382+ fflush(files[i].fp);
2383+ if (ferror(files[i].fp)) {
2384+ if ((files[i].mode == GT && files[i].fp != stderr)
2385+ || files[i].mode == '|')
2386+ FATAL("write error on %s", files[i].fname);
2387+ else
2388+ WARNING("i/o error occurred on %s", files[i].fname);
2389+ }
2390+ if (files[i].fp == stdin || files[i].fp == stdout ||
2391+ files[i].fp == stderr)
2392+ continue;
2393+ if (files[i].mode == '|' || files[i].mode == LE)
2394+ stat = pclose(files[i].fp) == -1;
2395+ else
2396+ stat = fclose(files[i].fp) == EOF;
2397+ if (stat)
2398+ WARNING("i/o error occurred while closing %s", files[i].fname);
2399+ }
2400+}
2401+
2402+static void flush_all(void)
2403+{
2404+ size_t i;
2405+
2406+ for (i = 0; i < nfiles; i++)
2407+ if (files[i].fp)
2408+ fflush(files[i].fp);
2409+}
2410+
2411+void backsub(char **pb_ptr, const char **sptr_ptr);
2412+
2413+Cell *dosub(Node **a, int subop) /* sub and gsub */
2414+{
2415+ fa *pfa;
2416+ int tempstat = 0;
2417+ char *repl;
2418+ Cell *x;
2419+
2420+ char *buf = NULL;
2421+ char *pb = NULL;
2422+ int bufsz = recsize;
2423+
2424+ const char *r, *s;
2425+ const char *start;
2426+ const char *noempty = NULL; /* empty match disallowed here */
2427+ size_t m = 0; /* match count */
2428+ size_t whichm = 0; /* which match to select, 0 = global */
2429+ int mtype; /* match type */
2430+
2431+ if (a[0] == NULL) { /* 0 => a[1] is already-compiled regexpr */
2432+ pfa = (fa *) a[1];
2433+ } else {
2434+ x = execute(a[1]);
2435+ pfa = makedfa(getsval(x), 1);
2436+ tempfree(x);
2437+ }
2438+
2439+ x = execute(a[2]); /* replacement string */
2440+ repl = tostring(getsval(x));
2441+ tempfree(x);
2442+
2443+ switch (subop) {
2444+ case SUB:
2445+ whichm = 1;
2446+ x = execute(a[3]); /* source string */
2447+ break;
2448+ case GSUB:
2449+ whichm = 0;
2450+ x = execute(a[3]); /* source string */
2451+ break;
2452+ default:
2453+ FATAL("dosub: unrecognized subop: %d", subop);
2454+ }
2455+
2456+ start = getsval(x);
2457+ while (pmatch(pfa, start)) {
2458+ if (buf == NULL) {
2459+ if ((pb = buf = (char *) malloc(bufsz)) == NULL)
2460+ FATAL("out of memory in dosub");
2461+ tempstat = pfa->initstat;
2462+ pfa->initstat = 2;
2463+ }
2464+
2465+ /* match types */
2466+ #define MT_IGNORE 0 /* unselected or invalid */
2467+ #define MT_INSERT 1 /* selected, empty */
2468+ #define MT_REPLACE 2 /* selected, not empty */
2469+
2470+ /* an empty match just after replacement is invalid */
2471+
2472+ if (patbeg == noempty && patlen == 0) {
2473+ mtype = MT_IGNORE; /* invalid, not counted */
2474+ } else if (whichm == ++m || whichm == 0) {
2475+ mtype = patlen ? MT_REPLACE : MT_INSERT;
2476+ } else {
2477+ mtype = MT_IGNORE; /* unselected, but counted */
2478+ }
2479+
2480+ /* leading text: */
2481+ if (patbeg > start) {
2482+ adjbuf(&buf, &bufsz, (pb - buf) + (patbeg - start),
2483+ recsize, &pb, "dosub");
2484+ s = start;
2485+ while (s < patbeg)
2486+ *pb++ = *s++;
2487+ }
2488+
2489+ if (mtype == MT_IGNORE)
2490+ goto matching_text; /* skip replacement text */
2491+
2492+ r = repl;
2493+ while (*r != 0) {
2494+ adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "dosub");
2495+ if (*r == '\\') {
2496+ backsub(&pb, &r);
2497+ } else if (*r == '&') {
2498+ r++;
2499+ adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize,
2500+ &pb, "dosub");
2501+ for (s = patbeg; s < patbeg+patlen; )
2502+ *pb++ = *s++;
2503+ } else {
2504+ *pb++ = *r++;
2505+ }
2506+ }
2507+
2508+matching_text:
2509+ if (mtype == MT_REPLACE || *patbeg == '\0')
2510+ goto next_search; /* skip matching text */
2511+
2512+ if (patlen == 0)
2513+ patlen = u8_nextlen(patbeg);
2514+ adjbuf(&buf, &bufsz, (pb-buf) + patlen, recsize, &pb, "dosub");
2515+ s = patbeg;
2516+ while (s < patbeg + patlen)
2517+ *pb++ = *s++;
2518+
2519+next_search:
2520+ start = patbeg + patlen;
2521+ if (m == whichm || *patbeg == '\0')
2522+ break;
2523+ if (mtype == MT_REPLACE)
2524+ noempty = start;
2525+
2526+ #undef MT_IGNORE
2527+ #undef MT_INSERT
2528+ #undef MT_REPLACE
2529+ }
2530+
2531+ if (repl) {
2532+ free(repl);
2533+ }
2534+
2535+ if (buf != NULL) {
2536+ pfa->initstat = tempstat;
2537+
2538+ /* trailing text */
2539+ adjbuf(&buf, &bufsz, 1+strlen(start)+pb-buf, 0, &pb, "dosub");
2540+ while ((*pb++ = *start++) != '\0')
2541+ ;
2542+
2543+ setsval(x, buf);
2544+ free(buf);
2545+ }
2546+
2547+ tempfree(x);
2548+ x = gettemp();
2549+ x->tval = NUM;
2550+ x->fval = m;
2551+ return x;
2552+}
2553+
2554+void backsub(char **pb_ptr, const char **sptr_ptr) /* handle \\& variations */
2555+{ /* sptr[0] == '\\' */
2556+ char *pb = *pb_ptr;
2557+ const char *sptr = *sptr_ptr;
2558+ static bool first = true;
2559+ static bool do_posix = false;
2560+
2561+ if (first) {
2562+ first = false;
2563+ do_posix = (getenv("POSIXLY_CORRECT") != NULL);
2564+ }
2565+
2566+ if (sptr[1] == '\\') {
2567+ if (sptr[2] == '\\' && sptr[3] == '&') { /* \\\& -> \& */
2568+ *pb++ = '\\';
2569+ *pb++ = '&';
2570+ sptr += 4;
2571+ } else if (sptr[2] == '&') { /* \\& -> \ + matched */
2572+ *pb++ = '\\';
2573+ sptr += 2;
2574+ } else if (do_posix) { /* \\x -> \x */
2575+ sptr++;
2576+ *pb++ = *sptr++;
2577+ } else { /* \\x -> \\x */
2578+ *pb++ = *sptr++;
2579+ *pb++ = *sptr++;
2580+ }
2581+ } else if (sptr[1] == '&') { /* literal & */
2582+ sptr++;
2583+ *pb++ = *sptr++;
2584+ } else /* literal \ */
2585+ *pb++ = *sptr++;
2586+
2587+ *pb_ptr = pb;
2588+ *sptr_ptr = sptr;
2589+}
2590+
2591+static char *wide_char_to_byte_str(int rune, size_t *outlen)
2592+{
2593+ static char buf[5];
2594+ int len;
2595+
2596+ if (rune < 0 || rune > 0x10FFFF)
2597+ return NULL;
2598+
2599+ memset(buf, 0, sizeof(buf));
2600+
2601+ len = 0;
2602+ if (rune <= 0x0000007F) {
2603+ buf[len++] = rune;
2604+ } else if (rune <= 0x000007FF) {
2605+ // 110xxxxx 10xxxxxx
2606+ buf[len++] = 0xC0 | (rune >> 6);
2607+ buf[len++] = 0x80 | (rune & 0x3F);
2608+ } else if (rune <= 0x0000FFFF) {
2609+ // 1110xxxx 10xxxxxx 10xxxxxx
2610+ buf[len++] = 0xE0 | (rune >> 12);
2611+ buf[len++] = 0x80 | ((rune >> 6) & 0x3F);
2612+ buf[len++] = 0x80 | (rune & 0x3F);
2613+
2614+ } else {
2615+ // 0x00010000 - 0x10FFFF
2616+ // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
2617+ buf[len++] = 0xF0 | (rune >> 18);
2618+ buf[len++] = 0x80 | ((rune >> 12) & 0x3F);
2619+ buf[len++] = 0x80 | ((rune >> 6) & 0x3F);
2620+ buf[len++] = 0x80 | (rune & 0x3F);
2621+ }
2622+
2623+ *outlen = len;
2624+ buf[len++] = '\0';
2625+
2626+ return buf;
2627+}
+650,
-0
1@@ -0,0 +1,650 @@
2+/****************************************************************
3+Copyright (C) Lucent Technologies 1997
4+All Rights Reserved
5+
6+Permission to use, copy, modify, and distribute this software and
7+its documentation for any purpose and without fee is hereby
8+granted, provided that the above copyright notice appear in all
9+copies and that both that the copyright notice and this
10+permission notice and warranty disclaimer appear in supporting
11+documentation, and that the name Lucent Technologies or any of
12+its entities not be used in advertising or publicity pertaining
13+to distribution of the software without specific, written prior
14+permission.
15+
16+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
18+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
19+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
21+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
22+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
23+THIS SOFTWARE.
24+****************************************************************/
25+
26+#define DEBUG
27+#include <stdio.h>
28+#include <math.h>
29+#include <ctype.h>
30+#include <string.h>
31+#include <stdlib.h>
32+#include "awk.h"
33+
34+#define FULLTAB 2 /* rehash when table gets this x full */
35+#define GROWTAB 4 /* grow table by this factor */
36+
37+Array *symtab; /* main symbol table */
38+
39+char **FS; /* initial field sep */
40+char **RS; /* initial record sep */
41+char **OFS; /* output field sep */
42+char **ORS; /* output record sep */
43+char **OFMT; /* output format for numbers */
44+char **CONVFMT; /* format for conversions in getsval */
45+Awkfloat *NF; /* number of fields in current record */
46+Awkfloat *NR; /* number of current record */
47+Awkfloat *FNR; /* number of current record in current file */
48+char **FILENAME; /* current filename argument */
49+Awkfloat *ARGC; /* number of arguments from command line */
50+char **SUBSEP; /* subscript separator for a[i,j,k]; default \034 */
51+Awkfloat *RSTART; /* start of re matched with ~; origin 1 (!) */
52+Awkfloat *RLENGTH; /* length of same */
53+
54+Cell *fsloc; /* FS */
55+Cell *nrloc; /* NR */
56+Cell *nfloc; /* NF */
57+Cell *fnrloc; /* FNR */
58+Cell *ofsloc; /* OFS */
59+Cell *orsloc; /* ORS */
60+Cell *rsloc; /* RS */
61+Cell *ARGVcell; /* cell with symbol table containing ARGV[...] */
62+Cell *rstartloc; /* RSTART */
63+Cell *rlengthloc; /* RLENGTH */
64+Cell *subseploc; /* SUBSEP */
65+Cell *symtabloc; /* SYMTAB */
66+
67+Cell *nullloc; /* a guaranteed empty cell */
68+Node *nullnode; /* zero&null, converted into a node for comparisons */
69+Cell *literal0;
70+
71+extern Cell **fldtab;
72+
73+void syminit(void) /* initialize symbol table with builtin vars */
74+{
75+ literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
76+ /* this is used for if(x)... tests: */
77+ nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
78+ nullnode = celltonode(nullloc, CCON);
79+
80+ fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
81+ FS = &fsloc->sval;
82+ rsloc = setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab);
83+ RS = &rsloc->sval;
84+ ofsloc = setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab);
85+ OFS = &ofsloc->sval;
86+ orsloc = setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab);
87+ ORS = &orsloc->sval;
88+ OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
89+ CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
90+ FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
91+ nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
92+ NF = &nfloc->fval;
93+ nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
94+ NR = &nrloc->fval;
95+ fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
96+ FNR = &fnrloc->fval;
97+ subseploc = setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab);
98+ SUBSEP = &subseploc->sval;
99+ rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
100+ RSTART = &rstartloc->fval;
101+ rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
102+ RLENGTH = &rlengthloc->fval;
103+ symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
104+ free(symtabloc->sval);
105+ symtabloc->sval = (char *) symtab;
106+}
107+
108+void arginit(int ac, char **av) /* set up ARGV and ARGC */
109+{
110+ Array *ap;
111+ Cell *cp;
112+ int i;
113+ char temp[50];
114+
115+ ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval;
116+ cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
117+ ap = makesymtab(NSYMTAB); /* could be (int) ARGC as well */
118+ free(cp->sval);
119+ cp->sval = (char *) ap;
120+ for (i = 0; i < ac; i++) {
121+ double result;
122+
123+ sprintf(temp, "%d", i);
124+ if (is_number(*av, & result))
125+ setsymtab(temp, *av, result, STR|NUM, ap);
126+ else
127+ setsymtab(temp, *av, 0.0, STR, ap);
128+ av++;
129+ }
130+ ARGVcell = cp;
131+}
132+
133+void envinit(char **envp) /* set up ENVIRON variable */
134+{
135+ Array *ap;
136+ Cell *cp;
137+ char *p;
138+
139+ cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
140+ ap = makesymtab(NSYMTAB);
141+ free(cp->sval);
142+ cp->sval = (char *) ap;
143+ for ( ; *envp; envp++) {
144+ double result;
145+
146+ if ((p = strchr(*envp, '=')) == NULL)
147+ continue;
148+ if( p == *envp ) /* no left hand side name in env string */
149+ continue;
150+ *p++ = 0; /* split into two strings at = */
151+ if (is_number(p, & result))
152+ setsymtab(*envp, p, result, STR|NUM, ap);
153+ else
154+ setsymtab(*envp, p, 0.0, STR, ap);
155+ p[-1] = '='; /* restore in case env is passed down to a shell */
156+ }
157+}
158+
159+Array *makesymtab(int n) /* make a new symbol table */
160+{
161+ Array *ap;
162+ Cell **tp;
163+
164+ ap = (Array *) malloc(sizeof(*ap));
165+ tp = (Cell **) calloc(n, sizeof(*tp));
166+ if (ap == NULL || tp == NULL)
167+ FATAL("out of space in makesymtab");
168+ ap->nelem = 0;
169+ ap->size = n;
170+ ap->tab = tp;
171+ return(ap);
172+}
173+
174+void freesymtab(Cell *ap) /* free a symbol table */
175+{
176+ Cell *cp, *temp;
177+ Array *tp;
178+ int i;
179+
180+ if (!isarr(ap))
181+ return;
182+ tp = (Array *) ap->sval;
183+ if (tp == NULL)
184+ return;
185+ for (i = 0; i < tp->size; i++) {
186+ for (cp = tp->tab[i]; cp != NULL; cp = temp) {
187+ xfree(cp->nval);
188+ if (freeable(cp))
189+ xfree(cp->sval);
190+ temp = cp->cnext; /* avoids freeing then using */
191+ free(cp);
192+ tp->nelem--;
193+ }
194+ tp->tab[i] = NULL;
195+ }
196+ if (tp->nelem != 0)
197+ WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
198+ free(tp->tab);
199+ free(tp);
200+}
201+
202+void freeelem(Cell *ap, const char *s) /* free elem s from ap (i.e., ap["s"] */
203+{
204+ Array *tp;
205+ Cell *p, *prev = NULL;
206+ int h;
207+
208+ tp = (Array *) ap->sval;
209+ h = hash(s, tp->size);
210+ for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
211+ if (strcmp(s, p->nval) == 0) {
212+ if (prev == NULL) /* 1st one */
213+ tp->tab[h] = p->cnext;
214+ else /* middle somewhere */
215+ prev->cnext = p->cnext;
216+ if (freeable(p))
217+ xfree(p->sval);
218+ free(p->nval);
219+ free(p);
220+ tp->nelem--;
221+ return;
222+ }
223+}
224+
225+Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
226+{
227+ int h;
228+ Cell *p;
229+
230+ if (n != NULL && (p = lookup(n, tp)) != NULL) {
231+ DPRINTF("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
232+ (void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval);
233+ return(p);
234+ }
235+ p = (Cell *) malloc(sizeof(*p));
236+ if (p == NULL)
237+ FATAL("out of space for symbol table at %s", n);
238+ p->nval = tostring(n);
239+ p->sval = s ? tostring(s) : tostring("");
240+ p->fval = f;
241+ p->tval = t;
242+ p->csub = CUNK;
243+ p->ctype = OCELL;
244+ tp->nelem++;
245+ if (tp->nelem > FULLTAB * tp->size)
246+ rehash(tp);
247+ h = hash(n, tp->size);
248+ p->cnext = tp->tab[h];
249+ tp->tab[h] = p;
250+ DPRINTF("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
251+ (void*)p, p->nval, p->sval, p->fval, p->tval);
252+ return(p);
253+}
254+
255+int hash(const char *s, int n) /* form hash value for string s */
256+{
257+ unsigned hashval;
258+
259+ for (hashval = 0; *s != '\0'; s++)
260+ hashval = (*s + 31 * hashval);
261+ return hashval % n;
262+}
263+
264+void rehash(Array *tp) /* rehash items in small table into big one */
265+{
266+ int i, nh, nsz;
267+ Cell *cp, *op, **np;
268+
269+ nsz = GROWTAB * tp->size;
270+ np = (Cell **) calloc(nsz, sizeof(*np));
271+ if (np == NULL) /* can't do it, but can keep running. */
272+ return; /* someone else will run out later. */
273+ for (i = 0; i < tp->size; i++) {
274+ for (cp = tp->tab[i]; cp; cp = op) {
275+ op = cp->cnext;
276+ nh = hash(cp->nval, nsz);
277+ cp->cnext = np[nh];
278+ np[nh] = cp;
279+ }
280+ }
281+ free(tp->tab);
282+ tp->tab = np;
283+ tp->size = nsz;
284+}
285+
286+Cell *lookup(const char *s, Array *tp) /* look for s in tp */
287+{
288+ Cell *p;
289+ int h;
290+
291+ h = hash(s, tp->size);
292+ for (p = tp->tab[h]; p != NULL; p = p->cnext)
293+ if (strcmp(s, p->nval) == 0)
294+ return(p); /* found it */
295+ return(NULL); /* not found */
296+}
297+
298+Awkfloat setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */
299+{
300+ int fldno;
301+
302+ f += 0.0; /* normalise negative zero to positive zero */
303+ if ((vp->tval & (NUM | STR)) == 0)
304+ funnyvar(vp, "assign to");
305+ if (isfld(vp)) {
306+ donerec = false; /* mark $0 invalid */
307+ fldno = atoi(vp->nval);
308+ if (fldno > *NF)
309+ newfld(fldno);
310+ DPRINTF("setting field %d to %g\n", fldno, f);
311+ } else if (&vp->fval == NF) {
312+ donerec = false; /* mark $0 invalid */
313+ setlastfld(f);
314+ DPRINTF("setfval: setting NF to %g\n", f);
315+ } else if (isrec(vp)) {
316+ donefld = false; /* mark $1... invalid */
317+ donerec = true;
318+ savefs();
319+ } else if (vp == ofsloc) {
320+ if (!donerec)
321+ recbld();
322+ }
323+ if (freeable(vp))
324+ xfree(vp->sval); /* free any previous string */
325+ vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */
326+ vp->fmt = NULL;
327+ vp->tval |= NUM; /* mark number ok */
328+ if (f == -0) /* who would have thought this possible? */
329+ f = 0;
330+ DPRINTF("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval);
331+ return vp->fval = f;
332+}
333+
334+void funnyvar(Cell *vp, const char *rw)
335+{
336+ if (isarr(vp))
337+ FATAL("can't %s %s; it's an array name.", rw, vp->nval);
338+ if (vp->tval & FCN)
339+ FATAL("can't %s %s; it's a function.", rw, vp->nval);
340+ WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
341+ (void *)vp, vp->nval, vp->sval, vp->fval, vp->tval);
342+}
343+
344+char *setsval(Cell *vp, const char *s) /* set string val of a Cell */
345+{
346+ char *t;
347+ int fldno;
348+ Awkfloat f;
349+
350+ DPRINTF("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
351+ (void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld);
352+ if ((vp->tval & (NUM | STR)) == 0)
353+ funnyvar(vp, "assign to");
354+ if (CSV && (vp == rsloc))
355+ WARNING("danger: don't set RS when --csv is in effect");
356+ if (CSV && (vp == fsloc))
357+ WARNING("danger: don't set FS when --csv is in effect");
358+ if (isfld(vp)) {
359+ donerec = false; /* mark $0 invalid */
360+ fldno = atoi(vp->nval);
361+ if (fldno > *NF)
362+ newfld(fldno);
363+ DPRINTF("setting field %d to %s (%p)\n", fldno, s, (const void*)s);
364+ } else if (isrec(vp)) {
365+ donefld = false; /* mark $1... invalid */
366+ donerec = true;
367+ savefs();
368+ } else if (vp == ofsloc) {
369+ if (!donerec)
370+ recbld();
371+ }
372+ t = s ? tostring(s) : tostring(""); /* in case it's self-assign */
373+ if (freeable(vp))
374+ xfree(vp->sval);
375+ vp->tval &= ~(NUM|DONTFREE|CONVC|CONVO);
376+ vp->tval |= STR;
377+ vp->fmt = NULL;
378+ DPRINTF("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
379+ (void*)vp, NN(vp->nval), t, (void*)t, vp->tval, donerec, donefld);
380+ vp->sval = t;
381+ if (&vp->fval == NF) {
382+ donerec = false; /* mark $0 invalid */
383+ f = getfval(vp);
384+ setlastfld(f);
385+ DPRINTF("setsval: setting NF to %g\n", f);
386+ }
387+
388+ return(vp->sval);
389+}
390+
391+Awkfloat getfval(Cell *vp) /* get float val of a Cell */
392+{
393+ if ((vp->tval & (NUM | STR)) == 0)
394+ funnyvar(vp, "read value of");
395+ if (isfld(vp) && !donefld)
396+ fldbld();
397+ else if (isrec(vp) && !donerec)
398+ recbld();
399+ if (!isnum(vp)) { /* not a number */
400+ double fval;
401+ bool no_trailing;
402+
403+ if (is_valid_number(vp->sval, true, & no_trailing, & fval)) {
404+ vp->fval = fval;
405+ if (no_trailing && !(vp->tval&CON))
406+ vp->tval |= NUM; /* make NUM only sparingly */
407+ } else
408+ vp->fval = 0.0;
409+ }
410+ DPRINTF("getfval %p: %s = %g, t=%o\n",
411+ (void*)vp, NN(vp->nval), vp->fval, vp->tval);
412+ return(vp->fval);
413+}
414+
415+static const char *get_inf_nan(double d)
416+{
417+ if (isinf(d)) {
418+ return (d < 0 ? "-inf" : "+inf");
419+ } else if (isnan(d)) {
420+ return (signbit(d) != 0 ? "-nan" : "+nan");
421+ } else
422+ return NULL;
423+}
424+
425+static char *get_str_val(Cell *vp, char **fmt) /* get string val of a Cell */
426+{
427+ char s[256];
428+ double dtemp;
429+ const char *p;
430+
431+ if ((vp->tval & (NUM | STR)) == 0)
432+ funnyvar(vp, "read value of");
433+ if (isfld(vp) && ! donefld)
434+ fldbld();
435+ else if (isrec(vp) && ! donerec)
436+ recbld();
437+
438+ /*
439+ * ADR: This is complicated and more fragile than is desirable.
440+ * Retrieving a string value for a number associates the string
441+ * value with the scalar. Previously, the string value was
442+ * sticky, meaning if converted via OFMT that became the value
443+ * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT
444+ * changed after a string value was retrieved, the original value
445+ * was maintained and used. Also not per POSIX.
446+ *
447+ * We work around this design by adding two additional flags,
448+ * CONVC and CONVO, indicating how the string value was
449+ * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy
450+ * of the pointer to the xFMT format string used for the
451+ * conversion. This pointer is only read, **never** dereferenced.
452+ * The next time we do a conversion, if it's coming from the same
453+ * xFMT as last time, and the pointer value is different, we
454+ * know that the xFMT format string changed, and we need to
455+ * redo the conversion. If it's the same, we don't have to.
456+ *
457+ * There are also several cases where we don't do a conversion,
458+ * such as for a field (see the checks below).
459+ */
460+
461+ /* Don't duplicate the code for actually updating the value */
462+#define update_str_val(vp) \
463+ { \
464+ if (freeable(vp)) \
465+ xfree(vp->sval); \
466+ if ((p = get_inf_nan(vp->fval)) != NULL) \
467+ strcpy(s, p); \
468+ else if (modf(vp->fval, &dtemp) == 0) /* it's integral */ \
469+ snprintf(s, sizeof (s), "%.30g", vp->fval); \
470+ else \
471+ snprintf(s, sizeof (s), *fmt, vp->fval); \
472+ vp->sval = tostring(s); \
473+ vp->tval &= ~DONTFREE; \
474+ vp->tval |= STR; \
475+ }
476+
477+ if (isstr(vp) == 0) {
478+ update_str_val(vp);
479+ if (fmt == OFMT) {
480+ vp->tval &= ~CONVC;
481+ vp->tval |= CONVO;
482+ } else {
483+ /* CONVFMT */
484+ vp->tval &= ~CONVO;
485+ vp->tval |= CONVC;
486+ }
487+ vp->fmt = *fmt;
488+ } else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) {
489+ goto done;
490+ } else if (isstr(vp)) {
491+ if (fmt == OFMT) {
492+ if ((vp->tval & CONVC) != 0
493+ || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
494+ update_str_val(vp);
495+ vp->tval &= ~CONVC;
496+ vp->tval |= CONVO;
497+ vp->fmt = *fmt;
498+ }
499+ } else {
500+ /* CONVFMT */
501+ if ((vp->tval & CONVO) != 0
502+ || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
503+ update_str_val(vp);
504+ vp->tval &= ~CONVO;
505+ vp->tval |= CONVC;
506+ vp->fmt = *fmt;
507+ }
508+ }
509+ }
510+done:
511+ DPRINTF("getsval %p: %s = \"%s (%p)\", t=%o\n",
512+ (void*)vp, NN(vp->nval), vp->sval, (void*)vp->sval, vp->tval);
513+ return(vp->sval);
514+}
515+
516+char *getsval(Cell *vp) /* get string val of a Cell */
517+{
518+ return get_str_val(vp, CONVFMT);
519+}
520+
521+char *getpssval(Cell *vp) /* get string val of a Cell for print */
522+{
523+ return get_str_val(vp, OFMT);
524+}
525+
526+
527+char *tostring(const char *s) /* make a copy of string s */
528+{
529+ char *p = strdup(s);
530+ if (p == NULL)
531+ FATAL("out of space in tostring on %s", s);
532+ return(p);
533+}
534+
535+char *tostringN(const char *s, size_t n) /* make a copy of string s */
536+{
537+ char *p;
538+
539+ p = (char *) malloc(n);
540+ if (p == NULL)
541+ FATAL("out of space in tostring on %s", s);
542+ strcpy(p, s);
543+ return(p);
544+}
545+
546+Cell *catstr(Cell *a, Cell *b) /* concatenate a and b */
547+{
548+ Cell *c;
549+ char *p;
550+ char *sa = getsval(a);
551+ char *sb = getsval(b);
552+ size_t l = strlen(sa) + strlen(sb) + 1;
553+ p = (char *) malloc(l);
554+ if (p == NULL)
555+ FATAL("out of space concatenating %s and %s", sa, sb);
556+ snprintf(p, l, "%s%s", sa, sb);
557+
558+ l++; // add room for ' '
559+ char *newbuf = (char *) malloc(l);
560+ if (newbuf == NULL)
561+ FATAL("out of space concatenating %s and %s", sa, sb);
562+ // See string() in lex.c; a string "xx" is stored in the symbol
563+ // table as "xx ".
564+ snprintf(newbuf, l, "%s ", p);
565+ c = setsymtab(newbuf, p, 0.0, CON|STR|DONTFREE, symtab);
566+ free(p);
567+ free(newbuf);
568+ return c;
569+}
570+
571+char *qstring(const char *is, int delim) /* collect string up to next delim */
572+{
573+ int c, n;
574+ const uschar *s = (const uschar *) is;
575+ uschar *buf, *bp;
576+
577+ if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL)
578+ FATAL( "out of space in qstring(%s)", s);
579+ for (bp = buf; (c = *s) != delim; s++) {
580+ if (c == '\n')
581+ SYNTAX( "newline in string %.20s...", is );
582+ else if (c != '\\')
583+ *bp++ = c;
584+ else { /* \something */
585+ c = *++s;
586+ if (c == 0) { /* \ at end */
587+ *bp++ = '\\';
588+ break; /* for loop */
589+ }
590+ switch (c) {
591+ case '\\': *bp++ = '\\'; break;
592+ case 'n': *bp++ = '\n'; break;
593+ case 't': *bp++ = '\t'; break;
594+ case 'b': *bp++ = '\b'; break;
595+ case 'f': *bp++ = '\f'; break;
596+ case 'r': *bp++ = '\r'; break;
597+ case 'v': *bp++ = '\v'; break;
598+ case 'a': *bp++ = '\a'; break;
599+ default:
600+ if (!isdigit(c)) {
601+ *bp++ = c;
602+ break;
603+ }
604+ n = c - '0';
605+ if (isdigit(s[1])) {
606+ n = 8 * n + *++s - '0';
607+ if (isdigit(s[1]))
608+ n = 8 * n + *++s - '0';
609+ }
610+ *bp++ = n;
611+ break;
612+ }
613+ }
614+ }
615+ *bp++ = 0;
616+ return (char *) buf;
617+}
618+
619+const char *flags2str(int flags)
620+{
621+ static const struct ftab {
622+ const char *name;
623+ int value;
624+ } flagtab[] = {
625+ { "NUM", NUM },
626+ { "STR", STR },
627+ { "DONTFREE", DONTFREE },
628+ { "CON", CON },
629+ { "ARR", ARR },
630+ { "FCN", FCN },
631+ { "FLD", FLD },
632+ { "REC", REC },
633+ { "CONVC", CONVC },
634+ { "CONVO", CONVO },
635+ { NULL, 0 }
636+ };
637+ static char buf[100];
638+ int i;
639+ char *cp = buf;
640+
641+ for (i = 0; flagtab[i].name != NULL; i++) {
642+ if ((flags & flagtab[i].value) != 0) {
643+ if (cp > buf)
644+ *cp++ = '|';
645+ strcpy(cp, flagtab[i].name);
646+ cp += strlen(cp);
647+ }
648+ }
649+
650+ return buf;
651+}
+20,
-6
1@@ -77,7 +77,7 @@ printgrid(size_t year, int month, int fday, int line)
2 if (trans && !(line == 2 && fday == 3))
3 dom += 11;
4 }
5- if (ltime && year == ltime->tm_year + 1900 && month == ltime->tm_mon)
6+ if (ltime && year == (size_t)(ltime->tm_year + 1900) && month == ltime->tm_mon)
7 today = ltime->tm_mday;
8 for (; d < 7 && dom <= monthlength(year, month, cal); ++d, ++dom) {
9 if (dom == today)
10@@ -98,8 +98,8 @@ drawcal(size_t year, int month, size_t ncols, size_t nmons, int fday)
11 "May", "June", "July", "August",
12 "September", "October", "November", "December" };
13 char *days[] = { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", };
14- size_t m, n, col, cur_year, cur_month, dow;
15- int line, pad;
16+ size_t m, n, col, cur_year, cur_month;
17+ int line, pad, dow;
18 char month_year[sizeof("Su Mo Tu We Th Fr Sa")];
19
20 for (m = 0; m < nmons; ) {
21@@ -141,8 +141,12 @@ drawcal(size_t year, int month, size_t ncols, size_t nmons, int fday)
22 static void
23 usage(void)
24 {
25+#ifdef FEATURE_CAL_EXT
26 eprintf("usage: %s [-1 | -3 | -y | -n num] "
27 "[-s | -m | -f num] [-c num] [[month] year]\n", argv0);
28+#else
29+ eprintf("usage: %s [-y] [[month] year]\n", argv0);
30+#endif
31 }
32
33 int
34@@ -161,10 +165,16 @@ main(int argc, char *argv[])
35 if (!isatty(STDOUT_FILENO))
36 ltime = NULL; /* don't highlight today's date */
37
38+#ifdef FEATURE_CAL_EXT
39 ncols = 3;
40 nmons = 0;
41+#else
42+ ncols = 1;
43+ nmons = 1;
44+#endif
45
46 ARGBEGIN {
47+#ifdef FEATURE_CAL_EXT
48 case '1':
49 nmons = 1;
50 break;
51@@ -176,7 +186,7 @@ main(int argc, char *argv[])
52 }
53 break;
54 case 'c':
55- ncols = estrtonum(EARGF(usage()), 0, MIN(SIZE_MAX, LLONG_MAX));
56+ ncols = estrtonum(EARGF(usage()), 0, MIN((unsigned long long)SIZE_MAX, (unsigned long long)LLONG_MAX));
57 break;
58 case 'f':
59 fday = estrtonum(EARGF(usage()), 0, 6);
60@@ -185,11 +195,12 @@ main(int argc, char *argv[])
61 fday = 1;
62 break;
63 case 'n':
64- nmons = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
65+ nmons = estrtonum(EARGF(usage()), 1, MIN((unsigned long long)SIZE_MAX, (unsigned long long)LLONG_MAX));
66 break;
67 case 's': /* Sunday */
68 fday = 0;
69 break;
70+#endif
71 case 'y':
72 month = 1;
73 nmons = 12;
74@@ -198,6 +209,7 @@ main(int argc, char *argv[])
75 usage();
76 } ARGEND
77
78+#ifdef FEATURE_CAL_EXT
79 if (nmons == 0) {
80 if (argc == 1) {
81 month = 1;
82@@ -206,12 +218,14 @@ main(int argc, char *argv[])
83 nmons = 1;
84 }
85 }
86+#endif
87
88 switch (argc) {
89 case 2:
90 month = estrtonum(argv[0], 1, 12);
91 argv++;
92- case 1: /* fallthrough */
93+ /* fallthrough */
94+ case 1:
95 year = estrtonum(argv[0], 0, INT_MAX);
96 break;
97 case 0:
+2,
-0
1@@ -18,6 +18,8 @@ chgrp(int dirfd, const char *name, struct stat *st, void *data, struct recursor
2 {
3 int flags = 0;
4
5+ (void)data;
6+
7 if ((r->maxdepth == 0 && r->follow == 'P') || (r->follow == 'H' && r->depth) || (hflag && !(r->depth)))
8 flags |= AT_SYMLINK_NOFOLLOW;
9 if (fchownat(dirfd, name, -1, gid, flags) < 0) {
+2,
-0
1@@ -14,6 +14,8 @@ chmodr(int dirfd, const char *name, struct stat *st, void *data, struct recursor
2 {
3 mode_t m;
4
5+ (void)data;
6+
7 m = parsemode(modestr, st->st_mode, mask);
8 if (!S_ISLNK(st->st_mode) && fchmodat(dirfd, name, m, 0) < 0) {
9 weprintf("chmod %s:", r->path);
+2,
-0
1@@ -21,6 +21,8 @@ chownpwgr(int dirfd, const char *name, struct stat *st, void *data, struct recur
2 {
3 int flags = 0;
4
5+ (void)data;
6+
7 if ((r->maxdepth == 0 && r->follow == 'P') || (r->follow == 'H' && r->depth) || (hflag && !(r->depth)))
8 flags |= AT_SYMLINK_NOFOLLOW;
9
+1,
-1
1@@ -71,7 +71,7 @@ cksum(int fd, const char *s)
2 unsigned char buf[BUFSIZ];
3
4 while ((n = read(fd, buf, sizeof(buf))) > 0) {
5- for (i = 0; i < n; i++)
6+ for (i = 0; i < (size_t)n; i++)
7 ck = (ck << 8) ^ crctab[(ck >> 24) ^ buf[i]];
8 len += n;
9 }
+2,
-2
1@@ -193,7 +193,7 @@ main(int argc, char *argv[])
2 continue;
3 ret = 0;
4 }
5- if (ret < ibs) {
6+ if ((size_t)ret < ibs) {
7 ipart++;
8 if (conv & SYNC) {
9 memset(buf + ipos + ret, 0, ibs - ret);
10@@ -220,7 +220,7 @@ main(int argc, char *argv[])
11 eprintf("write:");
12 if (ret == 0)
13 eprintf("write returned 0\n");
14- if (ret < obs)
15+ if ((size_t)ret < obs)
16 opart++;
17 else
18 ofull++;
+1,
-0
1@@ -112,6 +112,7 @@ main(int argc, char *argv[])
2 case 's':
3 case 'i':
4 eprintf("not implemented\n");
5+ break;
6 default:
7 usage();
8 } ARGEND;
+3,
-3
1@@ -98,7 +98,7 @@ du(int dirfd, const char *path, struct stat *st, void *data, struct recursor *r)
2 print:
3 if (!r->depth)
4 printpath(*total, r->path);
5- else if (!sflag && r->depth <= maxdepth && (S_ISDIR(st->st_mode) || aflag))
6+ else if (!sflag && (size_t)r->depth <= maxdepth && (S_ISDIR(st->st_mode) || aflag))
7 printpath(subtotal, r->path);
8 }
9
10@@ -122,7 +122,7 @@ main(int argc, char *argv[])
11 break;
12 case 'd':
13 dflag = 1;
14- maxdepth = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
15+ maxdepth = estrtonum(EARGF(usage()), 0, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
16 break;
17 case 'h':
18 hflag = 1;
19@@ -150,7 +150,7 @@ main(int argc, char *argv[])
20
21 bsize = getenv("BLOCKSIZE");
22 if (bsize)
23- blksize = estrtonum(bsize, 1, MIN(LLONG_MAX, SIZE_MAX));
24+ blksize = estrtonum(bsize, 1, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
25 if (kflag)
26 blksize = 1024;
27
+14,
-3
1@@ -244,7 +244,7 @@ gettxt(int line)
2
3 repeat:
4 chksignals();
5- if (!csize || off < lasto || off - lasto >= csize) {
6+ if (!csize || off < lasto || (size_t)(off - lasto) >= csize) {
7 block = off & ~(CACHESIZ-1);
8 if (lseek(scratch, block, SEEK_SET) < 0 ||
9 (n = read(scratch, buf, CACHESIZ)) < 0) {
10@@ -405,7 +405,7 @@ compile(int delim)
11 bracket = lastre.siz = 0;
12 for (n = 0;; ++n) {
13 c = input();
14- if (c == delim && !bracket || c == '\0') {
15+ if ((c == delim && !bracket) || c == '\0') {
16 break;
17 } else if (c == '\\') {
18 addchar(c, &lastre);
19@@ -450,6 +450,7 @@ match(int num)
20 static int
21 rematch(int num)
22 {
23+ (void)num;
24 regoff_t off = matchs[0].rm_eo;
25 regmatch_t *m;
26 int r;
27@@ -527,6 +528,7 @@ ensureblank(void)
28 case ' ':
29 case '\t':
30 skipblank();
31+ /* fallthrough */
32 case '\0':
33 back(c);
34 break;
35@@ -809,6 +811,7 @@ expandcmd(void)
36 back(c);
37 c = '\\';
38 }
39+ /* fallthrough */
40 default:
41 addchar(c, &cmd);
42 }
43@@ -901,7 +904,7 @@ doread(const char *fname)
44 for (cnt = 0; (len = getline(&s, &n, fp)) > 0; cnt += (size_t)len) {
45 chksignals();
46 if (s[len-1] != '\n') {
47- if (len+1 >= n) {
48+ if ((size_t)len + 1 >= n) {
49 if (n == SIZE_MAX || !(p = realloc(s, ++n)))
50 error("out of memory");
51 s = p;
52@@ -1339,6 +1342,7 @@ repeat:
53 case 'v':
54 case 'V':
55 error("cannot nest global commands");
56+ break;
57 case 'H':
58 if (nlines > 0)
59 goto unexpected;
60@@ -1353,6 +1357,7 @@ repeat:
61 break;
62 case 'w':
63 trunc = 1;
64+ /* fallthrough */
65 case 'W':
66 ensureblank();
67 deflines(nextln(0), lastln);
68@@ -1468,6 +1473,7 @@ repeat:
69 break;
70 case 'x':
71 trunc = 1;
72+ /* fallthrough */
73 case 'X':
74 ensureblank();
75 if (nlines > 0)
76@@ -1475,6 +1481,7 @@ repeat:
77 exstatus = 0;
78 deflines(nextln(0), lastln);
79 dowrite(getfname(cmd), trunc);
80+ /* fallthrough */
81 case 'Q':
82 case 'q':
83 if (nlines > 0)
84@@ -1538,11 +1545,13 @@ chkglobal(void)
85 switch (c = input()) {
86 case 'g':
87 uflag = 0;
88+ /* fallthrough */
89 case 'G':
90 dir = 1;
91 break;
92 case 'v':
93 uflag = 0;
94+ /* fallthrough */
95 case 'V':
96 dir = 0;
97 break;
98@@ -1643,12 +1652,14 @@ usage(void)
99 static void
100 sigintr(int n)
101 {
102+ (void)n;
103 intr = 1;
104 }
105
106 static void
107 sighup(int dummy)
108 {
109+ (void)dummy;
110 hup = 1;
111 }
112
+1,
-1
1@@ -21,7 +21,7 @@ parselist(const char *s)
2 if (*p == '\0')
3 eprintf("empty field in tablist\n");
4 tablist = ereallocarray(tablist, i + 1, sizeof(*tablist));
5- tablist[i] = estrtonum(p, 1, MIN(LLONG_MAX, SIZE_MAX));
6+ tablist[i] = estrtonum(p, 1, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
7 if (i > 0 && tablist[i - 1] >= tablist[i])
8 eprintf("tablist must be ascending\n");
9 }
+4,
-0
1@@ -103,6 +103,8 @@ doop(int *ophead, int *opp, struct val *valhead, struct val *valp)
2 struct val ret = { .str = NULL, .num = 0 }, *a, *b;
3 int op;
4
5+ (void)ophead;
6+
7 /* an operation "a op b" needs an operator and two values */
8 if (opp[-1] == '(')
9 enprintf(2, "syntax error: extra (\n");
10@@ -153,6 +155,8 @@ lex(char *s, struct val *v)
11 int type = VAL;
12 char *ops = "|&=><+-*/%():";
13
14+ (void)v;
15+
16 if (s[0] && strchr(ops, s[0]) && !s[1]) {
17 /* one-char operand */
18 type = s[0];
+391,
-17
1@@ -1,21 +1,22 @@
2 /* See LICENSE file for copyright and license details. */
3+#include "config.h"
4+#include "util.h"
5+
6 #include <dirent.h>
7 #include <errno.h>
8 #include <fnmatch.h>
9 #include <grp.h>
10 #include <libgen.h>
11 #include <pwd.h>
12+#include <regex.h>
13 #include <stdint.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17-#include <time.h>
18-#include <unistd.h>
19-
20 #include <sys/stat.h>
21 #include <sys/wait.h>
22-
23-#include "util.h"
24+#include <time.h>
25+#include <unistd.h>
26
27 /* because putting integers in pointers is undefined by the standard */
28 union extra {
29@@ -116,6 +117,53 @@ static int do_stat(char *path, struct stat *sb, struct findhist *hist);
30
31 /* Primaries */
32 static int pri_name (struct arg *arg);
33+#if FEATURE_FIND_INAME
34+static int pri_iname(struct arg *arg);
35+#endif
36+#if FEATURE_FIND_IPATH
37+static int pri_ipath(struct arg *arg);
38+#endif
39+#if FEATURE_FIND_REGEX
40+static int pri_regex(struct arg *arg);
41+static char **get_regex_arg(char *argv[], union extra *extra);
42+static void free_regex_arg(union extra extra);
43+#endif
44+#if FEATURE_FIND_INUM
45+static int pri_inum(struct arg *arg);
46+static char **get_inum_arg(char *argv[], union extra *extra);
47+#endif
48+#if FEATURE_FIND_SAMEFILE
49+static int pri_samefile(struct arg *arg);
50+static char **get_samefile_arg(char *argv[], union extra *extra);
51+#endif
52+#if FEATURE_FIND_MAXDEPTH
53+static int pri_maxdepth(struct arg *arg);
54+static char **get_maxdepth_arg(char *argv[], union extra *extra);
55+#endif
56+#if FEATURE_FIND_MINDEPTH
57+static int pri_mindepth(struct arg *arg);
58+static char **get_mindepth_arg(char *argv[], union extra *extra);
59+#endif
60+#if FEATURE_FIND_DELETE
61+static int pri_delete(struct arg *arg);
62+static char **get_delete_arg(char *argv[], union extra *extra);
63+#endif
64+#if FEATURE_FIND_QUIT
65+static int pri_quit(struct arg *arg);
66+static char **get_quit_arg(char *argv[], union extra *extra);
67+#endif
68+#if FEATURE_FIND_EMPTY
69+static int pri_empty(struct arg *arg);
70+#endif
71+#if FEATURE_FIND_MMIN
72+static int pri_mmin(struct arg *arg);
73+#endif
74+#if FEATURE_FIND_AMIN
75+static int pri_amin(struct arg *arg);
76+#endif
77+#if FEATURE_FIND_CMIN
78+static int pri_cmin(struct arg *arg);
79+#endif
80 static int pri_path (struct arg *arg);
81 static int pri_nouser (struct arg *arg);
82 static int pri_nogroup(struct arg *arg);
83@@ -133,7 +181,9 @@ static int pri_mtime (struct arg *arg);
84 static int pri_exec (struct arg *arg);
85 static int pri_ok (struct arg *arg);
86 static int pri_print (struct arg *arg);
87+#if FEATURE_FIND_PRINT0
88 static int pri_print0 (struct arg *arg);
89+#endif
90 static int pri_newer (struct arg *arg);
91 static int pri_depth (struct arg *arg);
92
93@@ -174,6 +224,45 @@ static int cmp_lt(int a, int b) { return a < b; }
94
95 /* order from find(1p), may want to alphabetize */
96 static struct pri_info primaries[] = {
97+#if FEATURE_FIND_INAME
98+ { "-iname" , pri_iname , get_name_arg , NULL , 1 },
99+#endif
100+#if FEATURE_FIND_IPATH
101+ { "-ipath" , pri_ipath , get_path_arg , NULL , 1 },
102+#endif
103+#if FEATURE_FIND_REGEX
104+ { "-regex" , pri_regex , get_regex_arg, free_regex_arg, 1 },
105+#endif
106+#if FEATURE_FIND_INUM
107+ { "-inum" , pri_inum , get_inum_arg , NULL , 1 },
108+#endif
109+#if FEATURE_FIND_SAMEFILE
110+ { "-samefile", pri_samefile, get_samefile_arg, free_extra, 1 },
111+#endif
112+#if FEATURE_FIND_MAXDEPTH
113+ { "-maxdepth", pri_maxdepth, get_maxdepth_arg, NULL , 1 },
114+#endif
115+#if FEATURE_FIND_MINDEPTH
116+ { "-mindepth", pri_mindepth, get_mindepth_arg, NULL , 1 },
117+#endif
118+#if FEATURE_FIND_DELETE
119+ { "-delete" , pri_delete , get_delete_arg, NULL , 0 },
120+#endif
121+#if FEATURE_FIND_QUIT
122+ { "-quit" , pri_quit , get_quit_arg , NULL , 0 },
123+#endif
124+#if FEATURE_FIND_MMIN
125+ { "-mmin" , pri_mmin , get_n_arg , free_extra , 1 },
126+#endif
127+#if FEATURE_FIND_AMIN
128+ { "-amin" , pri_amin , get_n_arg , free_extra , 1 },
129+#endif
130+#if FEATURE_FIND_CMIN
131+ { "-cmin" , pri_cmin , get_n_arg , free_extra , 1 },
132+#endif
133+#if FEATURE_FIND_EMPTY
134+ { "-empty" , pri_empty , NULL , NULL , 1 },
135+#endif
136 { "-name" , pri_name , get_name_arg , NULL , 1 },
137 { "-path" , pri_path , get_path_arg , NULL , 1 },
138 { "-nouser" , pri_nouser , NULL , NULL , 1 },
139@@ -192,7 +281,9 @@ static struct pri_info primaries[] = {
140 { "-exec" , pri_exec , get_exec_arg , free_exec_arg, 1 },
141 { "-ok" , pri_ok , get_ok_arg , free_ok_arg , 1 },
142 { "-print" , pri_print , get_print_arg, NULL , 0 },
143+#if FEATURE_FIND_PRINT0
144 { "-print0" , pri_print0 , get_print_arg, NULL , 0 },
145+#endif
146 { "-newer" , pri_newer , get_newer_arg, NULL , 1 },
147 { "-depth" , pri_depth , get_depth_arg, NULL , 0 },
148
149@@ -227,6 +318,9 @@ static struct {
150 char prune; /* hit -prune */
151 char xdev ; /* -xdev, prune directories on different devices */
152 char print; /* whether we will need -print when parsing */
153+ char quit ; /* quit execution immediately */
154+ long maxdepth; /* max depth of recursion */
155+ long mindepth; /* min depth of recursion */
156 } gflags;
157
158 /*
159@@ -246,14 +340,19 @@ spawn(char *argv[])
160 switch((pid = fork())) {
161 case -1:
162 eprintf("fork:");
163+ break;
164 case 0:
165 execvp(*argv, argv);
166 weprintf("exec %s failed:", *argv);
167 _exit(1);
168 }
169
170- /* FIXME: proper course of action for waitpid() on EINTR? */
171- waitpid(pid, &status, 0);
172+ while (waitpid(pid, &status, 0) < 0) {
173+ if (errno != EINTR) {
174+ status = -1;
175+ break;
176+ }
177+ }
178 return status;
179 }
180
181@@ -310,12 +409,14 @@ pri_nogroup(struct arg *arg)
182 static int
183 pri_xdev(struct arg *arg)
184 {
185+ (void)arg;
186 return 1;
187 }
188
189 static int
190 pri_prune(struct arg *arg)
191 {
192+ (void)arg;
193 return gflags.prune = 1;
194 }
195
196@@ -454,7 +555,7 @@ pri_ok(struct arg *arg)
197 * byte? */
198 ;
199
200- if (feof(stdin)) /* FIXME: ferror()? */
201+ if (feof(stdin) || ferror(stdin))
202 clearerr(stdin);
203
204 if (reply != 'y' && reply != 'Y')
205@@ -476,6 +577,7 @@ pri_print(struct arg *arg)
206 return 1;
207 }
208
209+#if FEATURE_FIND_PRINT0
210 static int
211 pri_print0(struct arg *arg)
212 {
213@@ -483,6 +585,7 @@ pri_print0(struct arg *arg)
214 eprintf("fwrite failed:");
215 return 1;
216 }
217+#endif
218
219 /* FIXME: ignoring nanoseconds */
220 static int
221@@ -494,6 +597,7 @@ pri_newer(struct arg *arg)
222 static int
223 pri_depth(struct arg *arg)
224 {
225+ (void)arg;
226 return 1;
227 }
228
229@@ -519,6 +623,7 @@ get_path_arg(char *argv[], union extra *extra)
230 static char **
231 get_xdev_arg(char *argv[], union extra *extra)
232 {
233+ (void)extra;
234 gflags.xdev = 1;
235 return argv;
236 }
237@@ -683,6 +788,7 @@ get_ok_arg(char *argv[], union extra *extra)
238 static char **
239 get_print_arg(char *argv[], union extra *extra)
240 {
241+ (void)extra;
242 gflags.print = 0;
243 return argv;
244 }
245@@ -703,6 +809,7 @@ get_newer_arg(char *argv[], union extra *extra)
246 static char **
247 get_depth_arg(char *argv[], union extra *extra)
248 {
249+ (void)extra;
250 gflags.depth = 1;
251 return argv;
252 }
253@@ -959,6 +1066,250 @@ eval(struct tok *tok, struct arg *arg)
254 /* evaluate path, if it's a directory iterate through directory entries and
255 * recurse
256 */
257+#if FEATURE_FIND_INAME
258+static int
259+pri_iname(struct arg *arg)
260+{
261+ int ret;
262+ char *path;
263+
264+ path = estrdup(arg->path);
265+ ret = !fnmatch((char *)arg->extra.p, basename(path), FNM_CASEFOLD);
266+ free(path);
267+
268+ return ret;
269+}
270+#endif
271+
272+#if FEATURE_FIND_IPATH
273+static int
274+pri_ipath(struct arg *arg)
275+{
276+ return !fnmatch((char *)arg->extra.p, arg->path, FNM_CASEFOLD);
277+}
278+#endif
279+
280+#if FEATURE_FIND_REGEX
281+static int
282+pri_regex(struct arg *arg)
283+{
284+ regex_t *re = arg->extra.p;
285+ regmatch_t match;
286+ if (regexec(re, arg->path, 1, &match, 0) == 0) {
287+ return match.rm_so == 0 && (size_t)match.rm_eo == strlen(arg->path);
288+ }
289+ return 0;
290+}
291+
292+static char **
293+get_regex_arg(char *argv[], union extra *extra)
294+{
295+ regex_t *re = emalloc(sizeof(*re));
296+ eregcomp(re, *argv, 0);
297+ extra->p = re;
298+ return argv;
299+}
300+
301+static void
302+free_regex_arg(union extra extra)
303+{
304+ regex_t *re = extra.p;
305+ regfree(re);
306+ free(re);
307+}
308+#endif
309+
310+#if FEATURE_FIND_INUM
311+static int
312+pri_inum(struct arg *arg)
313+{
314+ ino_t ino = (ino_t)arg->extra.i;
315+ return arg->st->st_ino == ino;
316+}
317+
318+static char **
319+get_inum_arg(char *argv[], union extra *extra)
320+{
321+ char *end;
322+ extra->i = strtol(*argv, &end, 10);
323+ if (end == *argv || *end)
324+ eprintf("bad number '%s'\n", *argv);
325+ return argv;
326+}
327+#endif
328+
329+#if FEATURE_FIND_SAMEFILE
330+struct SameFileArg {
331+ ino_t ino;
332+ dev_t dev;
333+};
334+
335+static int
336+pri_samefile(struct arg *arg)
337+{
338+ struct SameFileArg *s = arg->extra.p;
339+ return arg->st->st_ino == s->ino && arg->st->st_dev == s->dev;
340+}
341+
342+static char **
343+get_samefile_arg(char *argv[], union extra *extra)
344+{
345+ struct stat st;
346+ struct SameFileArg *s = emalloc(sizeof(*s));
347+ if (do_stat(*argv, &st, NULL))
348+ eprintf("failed to stat '%s':", *argv);
349+ s->ino = st.st_ino;
350+ s->dev = st.st_dev;
351+ extra->p = s;
352+ return argv;
353+}
354+#endif
355+
356+#if FEATURE_FIND_MAXDEPTH
357+static int
358+pri_maxdepth(struct arg *arg)
359+{
360+ (void)arg;
361+ return 1;
362+}
363+
364+static char **
365+get_maxdepth_arg(char *argv[], union extra *extra)
366+{
367+ (void)extra;
368+ char *end;
369+ gflags.maxdepth = strtol(*argv, &end, 10);
370+ if (end == *argv || *end || gflags.maxdepth < 0)
371+ eprintf("bad number '%s'\n", *argv);
372+ return argv;
373+}
374+#endif
375+
376+#if FEATURE_FIND_MINDEPTH
377+static int
378+pri_mindepth(struct arg *arg)
379+{
380+ (void)arg;
381+ return 1;
382+}
383+
384+static char **
385+get_mindepth_arg(char *argv[], union extra *extra)
386+{
387+ (void)extra;
388+ char *end;
389+ gflags.mindepth = strtol(*argv, &end, 10);
390+ if (end == *argv || *end || gflags.mindepth < 0)
391+ eprintf("bad number '%s'\n", *argv);
392+ return argv;
393+}
394+#endif
395+
396+#if FEATURE_FIND_DELETE
397+static int
398+pri_delete(struct arg *arg)
399+{
400+ if (remove(arg->path) < 0) {
401+ weprintf("remove %s failed:", arg->path);
402+ gflags.ret = 1;
403+ return 0;
404+ }
405+ return 1;
406+}
407+
408+static char **
409+get_delete_arg(char *argv[], union extra *extra)
410+{
411+ (void)extra;
412+ gflags.depth = 1;
413+ gflags.print = 0;
414+ return argv;
415+}
416+#endif
417+
418+#if FEATURE_FIND_QUIT
419+static int
420+pri_quit(struct arg *arg)
421+{
422+ (void)arg;
423+ gflags.quit = 1;
424+ return 1;
425+}
426+
427+static char **
428+get_quit_arg(char *argv[], union extra *extra)
429+{
430+ (void)extra;
431+ gflags.print = 0;
432+ return argv;
433+}
434+#endif
435+
436+#if FEATURE_FIND_EMPTY
437+static int
438+pri_empty(struct arg *arg)
439+{
440+ DIR *dir;
441+ struct dirent *de;
442+ int empty = 1;
443+
444+ if (S_ISREG(arg->st->st_mode)) {
445+ return arg->st->st_size == 0;
446+ } else if (S_ISDIR(arg->st->st_mode)) {
447+ dir = opendir(arg->path);
448+ if (!dir)
449+ return 0;
450+ while ((de = readdir(dir))) {
451+ if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
452+ empty = 0;
453+ break;
454+ }
455+ }
456+ closedir(dir);
457+ return empty;
458+ }
459+ return 0;
460+}
461+#endif
462+
463+#if FEATURE_FIND_MMIN
464+static int
465+pri_mmin(struct arg *arg)
466+{
467+ struct narg *n = arg->extra.p;
468+ return n->cmp((start.tv_sec - arg->st->st_mtime) / 60, n->n);
469+}
470+#endif
471+
472+#if FEATURE_FIND_AMIN
473+static int
474+pri_amin(struct arg *arg)
475+{
476+ struct narg *n = arg->extra.p;
477+ return n->cmp((start.tv_sec - arg->st->st_atime) / 60, n->n);
478+}
479+#endif
480+
481+#if FEATURE_FIND_CMIN
482+static int
483+pri_cmin(struct arg *arg)
484+{
485+ struct narg *n = arg->extra.p;
486+ return n->cmp((start.tv_sec - arg->st->st_ctime) / 60, n->n);
487+}
488+#endif
489+
490+static int
491+get_depth(struct findhist *hist)
492+{
493+ int d = 0;
494+ while (hist) {
495+ d++;
496+ hist = hist->next;
497+ }
498+ return d;
499+}
500+
501 static void
502 find(char *path, struct findhist *hist)
503 {
504@@ -969,6 +1320,10 @@ find(char *path, struct findhist *hist)
505 size_t namelen, pathcap = 0, len;
506 struct arg arg = { path, &st, { NULL } };
507 char *p, *pathbuf = NULL;
508+ int depth = get_depth(hist);
509+
510+ if (gflags.quit)
511+ return;
512
513 len = strlen(path) + 2; /* \0 and '/' */
514
515@@ -980,13 +1335,27 @@ find(char *path, struct findhist *hist)
516
517 gflags.prune = 0;
518
519- /* don't eval now iff we will hit the eval at the bottom which means
520- * 1. we are a directory 2. we have -depth 3. we don't have -xdev or we are
521- * on same device (so most of the time we eval here) */
522- if (!S_ISDIR(st.st_mode) ||
523- !gflags.depth ||
524- (gflags.xdev && hist && st.st_dev != hist->dev))
525- eval(root, &arg);
526+ if (gflags.maxdepth >= 0 && depth > gflags.maxdepth)
527+ return;
528+
529+ if (gflags.mindepth < 0 || depth >= gflags.mindepth) {
530+ /* don't eval now iff we will hit the eval at the bottom which means
531+ * 1. we are a directory 2. we have -depth 3. we don't have -xdev or we are
532+ * on same device (so most of the time we eval here) */
533+ if (!S_ISDIR(st.st_mode) ||
534+ !gflags.depth ||
535+ (gflags.xdev && hist && st.st_dev != hist->dev))
536+ eval(root, &arg);
537+ }
538+
539+ if (gflags.maxdepth >= 0 && depth >= gflags.maxdepth) {
540+ if (gflags.depth && (gflags.mindepth < 0 || depth >= gflags.mindepth)) {
541+ if (S_ISDIR(st.st_mode) &&
542+ (!gflags.xdev || !hist || st.st_dev == hist->dev))
543+ eval(root, &arg);
544+ }
545+ return;
546+ }
547
548 if (!S_ISDIR(st.st_mode) ||
549 gflags.prune ||
550@@ -1009,12 +1378,14 @@ find(char *path, struct findhist *hist)
551 weprintf("failed to opendir %s:", path);
552 gflags.ret = 1;
553 /* should we just ignore this since we hit an error? */
554- if (gflags.depth)
555+ if (gflags.depth && (gflags.mindepth < 0 || depth >= gflags.mindepth))
556 eval(root, &arg);
557 return;
558 }
559
560 while (errno = 0, (de = readdir(dir))) {
561+ if (gflags.quit)
562+ break;
563 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
564 continue;
565 namelen = strlen(de->d_name);
566@@ -1037,7 +1408,7 @@ find(char *path, struct findhist *hist)
567 }
568 closedir(dir);
569
570- if (gflags.depth)
571+ if (gflags.depth && (gflags.mindepth < 0 || depth >= gflags.mindepth))
572 eval(root, &arg);
573 }
574
575@@ -1054,6 +1425,9 @@ main(int argc, char **argv)
576 int npaths;
577 struct tok *t;
578
579+ gflags.maxdepth = -1;
580+ gflags.mindepth = -1;
581+
582 ARGBEGIN {
583 case 'H':
584 gflags.h = 1;
+1,
-1
1@@ -96,7 +96,7 @@ main(int argc, char *argv[])
2 sflag = 1;
3 break;
4 case 'w':
5- width = estrtonum(EARGF(usage()), 1, MIN(LLONG_MAX, SIZE_MAX));
6+ width = estrtonum(EARGF(usage()), 1, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
7 break;
8 ARGNUM:
9 if (!(width = ARGNUMF()))
+1,
-1
1@@ -25,7 +25,7 @@ main(int argc, char *argv[])
2 {
3 size_t len;
4 long res;
5- int i;
6+ size_t i;
7 char *str;
8
9 ARGBEGIN {
+126,
-13
1@@ -1,13 +1,13 @@
2-/* See LICENSE file for copyright and license details. */
3+#include "config.h"
4+#include "queue.h"
5+#include "util.h"
6+
7 #include <regex.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <strings.h>
12
13-#include "queue.h"
14-#include "util.h"
15-
16 enum { Match = 0, NoMatch = 1, Error = 2 };
17
18 static void addpattern(const char *);
19@@ -27,6 +27,13 @@ static int wflag;
20 static int xflag;
21 static int many;
22 static int mode;
23+#if FEATURE_GREP_CONTEXT
24+static long Aflag = 0;
25+static long Bflag = 0;
26+#endif
27+#if FEATURE_GREP_MAX_COUNT
28+static long mval = -1;
29+#endif
30
31 struct pattern {
32 regex_t preg;
33@@ -75,6 +82,18 @@ addpatternfile(FILE *fp)
34 enprintf(Error, "read error:");
35 }
36
37+#if FEATURE_GREP_CONTEXT
38+static void
39+print_line(const char *str, const char *line, long line_no, char sep)
40+{
41+ if (!hflag && (many || Hflag))
42+ printf("%s%c", str, sep);
43+ if (mode == 'n')
44+ printf("%ld%c", line_no, sep);
45+ puts(line);
46+}
47+#endif
48+
49 static int
50 grep(FILE *fp, const char *str)
51 {
52@@ -84,9 +103,24 @@ grep(FILE *fp, const char *str)
53 long c = 0, n;
54 struct pattern *pnode;
55 int match, result = NoMatch;
56+#if FEATURE_GREP_MAX_COUNT
57+ long matches = 0;
58+#endif
59+#if FEATURE_GREP_CONTEXT
60+ struct context_line {
61+ char *str;
62+ long line_no;
63+ } *before_buf = NULL;
64+ size_t before_head = 0, before_count = 0, i = 0, idx = 0;
65+ long after_left = 0;
66+ long last_printed_line = 0;
67+
68+ if (Bflag > 0 && !(mode == 'c' || mode == 'l' || mode == 'q'))
69+ before_buf = ecalloc(Bflag, sizeof(*before_buf));
70+#endif
71
72 for (n = 1; (len = getline(&buf, &size, fp)) > 0; n++) {
73- /* Remove the trailing newline if one is present. */
74+ /* remove the trailing newline if one is present */
75 if (buf[len - 1] == '\n')
76 buf[len - 1] = '\0';
77 match = 0;
78@@ -112,6 +146,9 @@ grep(FILE *fp, const char *str)
79 }
80 if (match != vflag) {
81 result = Match;
82+#if FEATURE_GREP_MAX_COUNT
83+ matches++;
84+#endif
85 switch (mode) {
86 case 'c':
87 c++;
88@@ -122,18 +159,69 @@ grep(FILE *fp, const char *str)
89 case 'q':
90 exit(Match);
91 default:
92- if (!hflag && (many || Hflag))
93- printf("%s:", str);
94- if (mode == 'n')
95- printf("%ld:", n);
96- puts(buf);
97+#if FEATURE_GREP_CONTEXT
98+ if (Aflag > 0 || Bflag > 0) {
99+ if (last_printed_line > 0 && n > last_printed_line + 1)
100+ puts("--");
101+ for (i = 0; i < before_count; i++) {
102+ idx = (before_head - before_count + i + Bflag) % Bflag;
103+ print_line(str, before_buf[idx].str, before_buf[idx].line_no, '-');
104+ free(before_buf[idx].str);
105+ before_buf[idx].str = NULL;
106+ }
107+ before_count = 0;
108+ before_head = 0;
109+ print_line(str, buf, n, ':');
110+ after_left = Aflag;
111+ last_printed_line = n;
112+ } else {
113+#endif
114+ if (!hflag && (many || Hflag))
115+ printf("%s:", str);
116+ if (mode == 'n')
117+ printf("%ld:", n);
118+ puts(buf);
119+#if FEATURE_GREP_CONTEXT
120+ }
121+#endif
122 break;
123 }
124+#if FEATURE_GREP_MAX_COUNT
125+ if (mval >= 0 && matches >= mval)
126+ goto end;
127+#endif
128+ }
129+#if FEATURE_GREP_CONTEXT
130+ else if (Aflag > 0 || Bflag > 0) {
131+ if (mode != 'c' && mode != 'l' && mode != 'q') {
132+ if (after_left > 0) {
133+ print_line(str, buf, n, '-');
134+ after_left--;
135+ last_printed_line = n;
136+ }
137+ if (Bflag > 0) {
138+ if (before_count == (size_t)Bflag)
139+ free(before_buf[before_head].str);
140+ before_buf[before_head].str = estrdup(buf);
141+ before_buf[before_head].line_no = n;
142+ before_head = (before_head + 1) % Bflag;
143+ if (before_count < (size_t)Bflag)
144+ before_count++;
145+ }
146+ }
147 }
148+#endif
149 }
150 if (mode == 'c')
151 printf("%ld\n", c);
152 end:
153+#if FEATURE_GREP_CONTEXT
154+ if (before_buf) {
155+ for (i = 0; i < (size_t)Bflag; i++)
156+ free(before_buf[i].str);
157+ free(before_buf);
158+ }
159+#endif
160 if (ferror(fp)) {
161 weprintf("%s: read error:", str);
162 result = Error;
163@@ -144,8 +232,14 @@ end:
164 static void
165 usage(void)
166 {
167- enprintf(Error, "usage: %s [-EFHchilnqsvwx] [-e pattern] [-f file] "
168- "[pattern] [file ...]\n", argv0);
169+ enprintf(Error, "usage: %s [-EFHchilnqsvwx]"
170+#if FEATURE_GREP_CONTEXT
171+ " [-A num] [-B num] [-C num]"
172+#endif
173+#if FEATURE_GREP_MAX_COUNT
174+ " [-m num]"
175+#endif
176+ " [-e pattern] [-f file] [pattern] [file ...]\n", argv0);
177 }
178
179 int
180@@ -159,6 +253,25 @@ main(int argc, char *argv[])
181 SLIST_INIT(&phead);
182
183 ARGBEGIN {
184+#if FEATURE_GREP_CONTEXT
185+ case 'A':
186+ Aflag = estrtonum(EARGF(usage()), 0, LONG_MAX);
187+ break;
188+ case 'B':
189+ Bflag = estrtonum(EARGF(usage()), 0, LONG_MAX);
190+ break;
191+ case 'C':
192+ Aflag = Bflag = estrtonum(EARGF(usage()), 0, LONG_MAX);
193+ break;
194+ ARGNUM:
195+ Aflag = Bflag = ARGNUMF();
196+ break;
197+#endif
198+#if FEATURE_GREP_MAX_COUNT
199+ case 'm':
200+ mval = estrtonum(EARGF(usage()), 0, LONG_MAX);
201+ break;
202+#endif
203 case 'E':
204 Eflag = 1;
205 Fflag = 0;
206@@ -234,7 +347,7 @@ main(int argc, char *argv[])
207 }
208
209 if (!Fflag)
210- /* Compile regex for all search patterns */
211+ /* compile regex for all search patterns */
212 SLIST_FOREACH(pnode, &phead, entry)
213 enregcomp(Error, &pnode->preg, pnode->pattern, flags);
214 many = (argc > 1);
+1,
-1
1@@ -37,7 +37,7 @@ main(int argc, char *argv[])
2
3 ARGBEGIN {
4 case 'n':
5- n = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
6+ n = estrtonum(EARGF(usage()), 0, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
7 break;
8 ARGNUM:
9 n = ARGNUMF();
+3,
-3
1@@ -322,7 +322,7 @@ makespec(char *s)
2 fldno = 0;
3 } else if ((s[0] == '1' || s[0] == '2') && s[1] == '.') {
4 fileno = s[0] - '0';
5- fldno = estrtonum(&s[2], 1, MIN(LLONG_MAX, SIZE_MAX)) - 1;
6+ fldno = estrtonum(&s[2], 1, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX)) - 1;
7 } else {
8 eprintf("%s: invalid format\n", s);
9 }
10@@ -459,10 +459,10 @@ main(int argc, char *argv[])
11
12 ARGBEGIN {
13 case '1':
14- jf[0] = estrtonum(EARGF(usage()), 1, MIN(LLONG_MAX, SIZE_MAX));
15+ jf[0] = estrtonum(EARGF(usage()), 1, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
16 break;
17 case '2':
18- jf[1] = estrtonum(EARGF(usage()), 1, MIN(LLONG_MAX, SIZE_MAX));
19+ jf[1] = estrtonum(EARGF(usage()), 1, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
20 break;
21 case 'a':
22 fno = EARGF(usage());
+21,
-34
1@@ -1,4 +1,7 @@
2 /* See LICENSE file for copyright and license details. */
3+#include "sig.h"
4+#include "util.h"
5+
6 #include <sys/wait.h>
7
8 #include <ctype.h>
9@@ -8,45 +11,28 @@
10 #include <string.h>
11 #include <strings.h>
12
13-#include "util.h"
14-
15-struct {
16- const char *name;
17- const int sig;
18-} sigs[] = {
19- { "0", 0 },
20-#define SIG(n) { #n, SIG##n }
21- SIG(ABRT), SIG(ALRM), SIG(BUS), SIG(CHLD), SIG(CONT), SIG(FPE), SIG(HUP),
22- SIG(ILL), SIG(INT), SIG(KILL), SIG(PIPE), SIG(QUIT), SIG(SEGV), SIG(STOP),
23- SIG(TERM), SIG(TRAP), SIG(TSTP), SIG(TTIN), SIG(TTOU), SIG(USR1), SIG(USR2),
24- SIG(URG),
25-#undef SIG
26-};
27-
28-const char *
29-sig2name(const int sig)
30+static const char *
31+sig2name(int sig)
32 {
33- size_t i;
34-
35- for (i = 0; i < LEN(sigs); i++)
36- if (sigs[i].sig == sig)
37- return sigs[i].name;
38- eprintf("%d: bad signal number\n", sig);
39+ static char name[SIG2STR_MAX];
40
41- return NULL; /* not reached */
42+ if (sig == 0)
43+ return "0";
44+ if (sig2str(sig, name) < 0)
45+ eprintf("%d: bad signal number\n", sig);
46+ return name;
47 }
48
49-int
50+static int
51 name2sig(const char *name)
52 {
53- size_t i;
54-
55- for (i = 0; i < LEN(sigs); i++)
56- if (!strcasecmp(sigs[i].name, name))
57- return sigs[i].sig;
58- eprintf("%s: bad signal name\n", name);
59+ int sig;
60
61- return -1; /* not reached */
62+ if (strcmp(name, "0") == 0)
63+ return 0;
64+ if (str2sig(name, &sig) < 0)
65+ eprintf("%s: bad signal name\n", name);
66+ return sig;
67 }
68
69 static void
70@@ -75,8 +61,9 @@ main(int argc, char *argv[])
71 goto longopt;
72 argc--, argv++;
73 if (!argc) {
74- for (i = 0; i < LEN(sigs); i++)
75- puts(sigs[i].name);
76+ for (i = 1; i < (size_t)sys_nsig; i++)
77+ if (sys_signame[i])
78+ puts(sys_signame[i]);
79 } else if (argc == 1) {
80 sig = estrtonum(*argv, 0, INT_MAX);
81 if (sig > 128)
+3,
-2
1@@ -448,8 +448,9 @@ main(int argc, char *argv[])
2 } ARGEND
3
4 switch (argc) {
5- case 0: /* fallthrough */
6+ case 0:
7 *--argv = ".", ++argc;
8+ /* fallthrough */
9 case 1:
10 mkent(&ent, argv[0], 1, Hflag || Lflag);
11 ls("", &ent, (!dflag && S_ISDIR(ent.mode)) ||
12@@ -458,7 +459,7 @@ main(int argc, char *argv[])
13
14 break;
15 default:
16- for (i = ds = fs = 0, fents = dents = NULL; i < argc; ++i) {
17+ for (i = ds = fs = 0, fents = dents = NULL; i < (size_t)argc; ++i) {
18 mkent(&ent, argv[i], 1, Hflag || Lflag);
19
20 if ((!dflag && S_ISDIR(ent.mode)) ||
+6,
-4
1@@ -48,7 +48,7 @@ hash(char *name)
2 int c;
3 unsigned h = 5381;
4
5- while (c = *name++)
6+ while ((c = *name++))
7 h = h*33 ^ c;
8
9 return h;
10@@ -253,7 +253,7 @@ parseargv(char **argv, char ***targets, int where, int export)
11 static void
12 parsemakeflags(void)
13 {
14- int c, n;
15+ int n;
16 char *s, *flags, **arr;
17
18 if ((flags = getenv("MAKEFLAGS")) == NULL)
19@@ -293,7 +293,7 @@ parsemakefiles(char **argv)
20
21 hasmake = 0;
22 for ( ; *argv && **argv == '-'; ++argv) {
23- for (s = *argv; c = *s; ++s) {
24+ for (s = *argv; (c = *s); ++s) {
25 if (hasargs(c))
26 arg = getarg(&s, &argv);
27
28@@ -328,7 +328,7 @@ enadebug(char *argv[])
29
30 for ( ; *argv && **argv == '-'; ++argv) {
31 p = *argv;
32- for (++p; c = *p; ++p) {
33+ for (++p; (c = *p); ++p) {
34 if (hasargs(c))
35 getarg(&p, &argv);
36 if (c == 'd')
37@@ -342,6 +342,8 @@ main(int argc, char *argv[])
38 {
39 char *arg0, **targets;
40
41+ (void)argc;
42+
43 signal(SIGINT, sighandler);
44 signal(SIGHUP, sighandler);
45 signal(SIGTERM, sighandler);
+4,
-4
1@@ -70,7 +70,7 @@ static Macro *
2 lookup(char *name)
3 {
4 Macro *mp;
5- int h = hash(name) & TABSIZ-1;
6+ int h = hash(name) & (TABSIZ-1);
7
8 for (mp = htab[h]; mp && strcmp(mp->name, name); mp = mp->next)
9 ;
10@@ -305,7 +305,6 @@ trim(char *s)
11 static void
12 include(char *s)
13 {
14- int len;
15 FILE *fp;
16 char *fil, *t;
17
18@@ -471,8 +470,8 @@ expandmacro(char *name)
19 static void
20 replace(char *line, char *repl, char *to)
21 {
22- int siz, at, len, replsiz, tosiz, sep, pos;
23- char *oline, *s, *cur, *buf;
24+ int siz, at, len, replsiz, tosiz, pos;
25+ char *oline, *cur, *buf;
26
27 debug("replacing '%s', with '%s' to '%s'", line, repl, to);
28 oline = line;
29@@ -804,6 +803,7 @@ repeat:
30 case '#':
31 comment();
32 c = '\n';
33+ /* fallthrough */
34 case ';':
35 case ':':
36 case '=':
+9,
-24
1@@ -36,7 +36,7 @@ static Target *
2 lookup(char *name)
3 {
4 Target *tp;
5- int h = hash(name) & TABSIZ-1;
6+ int h = hash(name) & (TABSIZ-1);
7
8 for (tp = htab[h]; tp && strcmp(tp->name, name); tp = tp->next)
9 ;
10@@ -89,31 +89,16 @@ cleanup(Target *tp)
11 static int
12 depends(char *target, char *dep)
13 {
14- int i;
15 Target **p, *tp = lookup(target);
16
17 for (p = tp->deps; p && *p; ++p) {
18- if (strcmp((*p)->name, target) == 0)
19+ if (strcmp((*p)->name, dep) == 0)
20 return 1;
21 }
22
23 return 0;
24 }
25
26-static int
27-is_suffix(char *s)
28-{
29- int n;
30-
31- if (s[0] != '.')
32- return 0;
33-
34- for (n = 0; s = strchr(s, '.'); n++)
35- s++;
36-
37- return n == 2;
38-}
39-
40 void
41 addtarget(char *target, int ndeps)
42 {
43@@ -152,7 +137,6 @@ addtarget(char *target, int ndeps)
44 void
45 adddep(char *target, char *dep)
46 {
47- int i;
48 size_t siz;
49 Target **p, *tp = lookup(target);
50
51@@ -213,9 +197,10 @@ static int
52 execline(Target *tp, char *line, int ignore, int silence)
53 {
54 char *s, *t;
55- Target *p, **q;
56 int r, at, plus, minus, l;
57
58+ (void)tp;
59+
60 debug("executing '%s'", line);
61
62 at = plus = minus = 0;
63@@ -398,7 +383,7 @@ inference(Target *tp, int force)
64 to = strrchr(tp->name, '.');
65 if (to && !enabled(to))
66 return NULL;
67- tolen = to ? to - tp->name : strlen(tp->name);
68+ tolen = to ? (int)(to - tp->name) : (int)strlen(tp->name);
69
70 if (!to)
71 to = "";
72@@ -413,7 +398,7 @@ inference(Target *tp, int force)
73 "%s%s",
74 from, to);
75
76- if (r < 0 || r >= sizeof(buf))
77+ if (r < 0 || (size_t)r >= sizeof(buf))
78 error("suffixes too long %s %s", from, to);
79
80 q = lookup(buf);
81@@ -425,7 +410,7 @@ inference(Target *tp, int force)
82 "%*.*s%s",
83 tolen, tolen, tp->name, from);
84
85- if (r < 0 || r >= sizeof(fname)) {
86+ if (r < 0 || (size_t)r >= sizeof(fname)) {
87 error("prerequisite name too long %s %s",
88 tp->name, from);
89 }
90@@ -492,7 +477,7 @@ update(Target *tp)
91 static int
92 rebuild(Target *tp, int *buildp)
93 {
94- Target **p, *q;;
95+ Target **p, *q;
96 int r, need, build, err, def;
97
98 debug("checking rebuild of %s", tp->name);
99@@ -565,7 +550,7 @@ rebuild(Target *tp, int *buildp)
100 int
101 build(char *name)
102 {
103- int build, r;
104+ int build;
105
106 if (!name) {
107 if (!deftarget) {
+4,
-3
1@@ -128,6 +128,7 @@ main(int argc, char *argv[])
2 switch (utflen((d = EARGF(usage())))) {
3 case 0:
4 eprintf("empty logical page delimiter\n");
5+ break;
6 case 1:
7 s = strlen(d);
8 delim = emalloc(s + 1 + 1);
9@@ -151,10 +152,10 @@ main(int argc, char *argv[])
10 type[2] = getlinetype(EARGF(usage()), preg + 2);
11 break;
12 case 'i':
13- incr = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
14+ incr = estrtonum(EARGF(usage()), 0, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
15 break;
16 case 'l':
17- blines = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
18+ blines = estrtonum(EARGF(usage()), 0, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
19 break;
20 case 'n':
21 formattype = EARGF(usage());
22@@ -181,7 +182,7 @@ main(int argc, char *argv[])
23 seplen = unescape(sep);
24 break;
25 case 'v':
26- startnum = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
27+ startnum = estrtonum(EARGF(usage()), 0, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
28 break;
29 case 'w':
30 width = estrtonum(EARGF(usage()), 1, INT_MAX);
+26,
-17
1@@ -1,4 +1,7 @@
2 /* See LICENSE file for copyright and license details. */
3+#include "queue.h"
4+#include "util.h"
5+
6 #include <ctype.h>
7 #include <fcntl.h>
8 #include <stdint.h>
9@@ -7,9 +10,6 @@
10 #include <string.h>
11 #include <unistd.h>
12
13-#include "queue.h"
14-#include "util.h"
15-
16 struct type {
17 unsigned char format;
18 unsigned int len;
19@@ -141,7 +141,7 @@ od(int fd, char *fname, int last)
20 ssize_t n;
21
22 while (skip - addr > 0) {
23- n = read(fd, buf, MIN(skip - addr, sizeof(buf)));
24+ n = read(fd, buf, MIN((size_t)(skip - addr), sizeof(buf)));
25 if (n < 0)
26 weprintf("read %s:", fname);
27 if (n <= 0)
28@@ -153,10 +153,10 @@ od(int fd, char *fname, int last)
29
30 for (;;) {
31 if (max >= 0)
32- size = MIN(max - (addr - skip), size);
33+ size = MIN((size_t)(max - (addr - skip)), size);
34 if ((n = read(fd, buf, size)) <= 0)
35 break;
36- for (i = 0; i < n; i++, addr++) {
37+ for (i = 0; i < (size_t)n; i++, addr++) {
38 line[lineoff++] = buf[i];
39 if (lineoff == linelen) {
40 printline(line, lineoff, addr - lineoff + 1);
41@@ -203,17 +203,19 @@ addtype(char format, int len)
42 static void
43 usage(void)
44 {
45- eprintf("usage: %s [-bdosvx] [-A addressformat] [-E | -e] [-j skip] "
46- "[-t outputformat] [file ...]\n", argv0);
47+ eprintf("usage: %s [-bdosvx] [-A addressformat] "
48+#if FEATURE_OD_ENDIAN
49+ "[-E | -e] "
50+#endif
51+ "[-j skip] [-t outputformat] [file ...]\n", argv0);
52 }
53
54 int
55 main(int argc, char *argv[])
56 {
57- int fd;
58 struct type *t;
59- int ret = 0, len;
60- char *s;
61+ char *s, *end;
62+ int fd, ret = 0, len, fmt_char;
63
64 big_endian = (*(uint16_t *)"\0\xff" == 0xff);
65
66@@ -230,10 +232,12 @@ main(int argc, char *argv[])
67 case 'd':
68 addtype('u', 2);
69 break;
70+#if FEATURE_OD_ENDIAN
71 case 'E':
72 case 'e':
73 big_endian = (ARGC() == 'E');
74 break;
75+#endif
76 case 'j':
77 if ((skip = parseoffset(EARGF(usage()))) < 0)
78 usage();
79@@ -260,28 +264,33 @@ main(int argc, char *argv[])
80 case 'o':
81 case 'u':
82 case 'x':
83- /* todo: allow multiple digits */
84- if (*(s+1) > '0' && *(s+1) <= '9') {
85- len = *(s+1) - '0';
86+ fmt_char = *s;
87+ if (isdigit((unsigned char)*(s + 1))) {
88+ len = strtol(s + 1, &end, 10);
89+ s = end - 1;
90 } else {
91- switch (*(s+1)) {
92+ switch (*(s + 1)) {
93 case 'C':
94 len = sizeof(char);
95+ s++;
96 break;
97 case 'S':
98 len = sizeof(short);
99+ s++;
100 break;
101 case 'I':
102 len = sizeof(int);
103+ s++;
104 break;
105 case 'L':
106 len = sizeof(long);
107+ s++;
108 break;
109 default:
110 len = sizeof(int);
111 }
112 }
113- addtype(*s, len);
114+ addtype(fmt_char, len);
115 break;
116 default:
117 usage();
118@@ -289,7 +298,7 @@ main(int argc, char *argv[])
119 }
120 break;
121 case 'v':
122- /* always set - use uniq(1) to handle duplicate lines */
123+ /* always set, use uniq(1) to handle duplicate lines */
124 break;
125 case 'x':
126 addtype('x', 2);
+8,
-8
1@@ -14,7 +14,8 @@ static void
2 sequential(struct fdescr *dsc, int fdescrlen, Rune *delim, size_t delimlen)
3 {
4 Rune c, last;
5- size_t i, d;
6+ int i;
7+ size_t d;
8
9 for (i = 0; i < fdescrlen; i++) {
10 d = 0;
11@@ -41,20 +42,19 @@ static void
12 parallel(struct fdescr *dsc, int fdescrlen, Rune *delim, size_t delimlen)
13 {
14 Rune c, d;
15- size_t i, m;
16- ssize_t last;
17+ int i, m, last;
18
19 nextline:
20 last = -1;
21
22 for (i = 0; i < fdescrlen; i++) {
23- d = delim[i % delimlen];
24+ d = delim[(size_t)i % delimlen];
25 c = 0;
26
27 while (efgetrune(&c, dsc[i].fp, dsc[i].name)) {
28 for (m = last + 1; m < i; m++) {
29- if (delim[m % delimlen] != '\0')
30- efputrune(&delim[m % delimlen], stdout, "<stdout>");
31+ if (delim[(size_t)m % delimlen] != '\0')
32+ efputrune(&delim[(size_t)m % delimlen], stdout, "<stdout>");
33 }
34 last = i;
35 if (c == '\n') {
36@@ -89,8 +89,8 @@ main(int argc, char *argv[])
37 {
38 struct fdescr *dsc;
39 Rune *delim_rune = NULL;
40- size_t delim_runelen, i, delim_bytelen = 1;
41- int seq = 0, ret = 0;
42+ size_t delim_runelen, delim_bytelen = 1;
43+ int seq = 0, ret = 0, i;
44 char *delim = "\t";
45
46 ARGBEGIN {
+2535,
-0
1@@ -0,0 +1,2535 @@
2+/* taken from: https://github.com/michaelforney/pax */
3+#ifndef _GNU_SOURCE
4+#define _GNU_SOURCE /* needed for major/minor (non-posix) */
5+#endif
6+
7+#include "arg.h"
8+
9+#include <assert.h>
10+#include <cpio.h>
11+#include <ctype.h>
12+#include <dirent.h>
13+#include <errno.h>
14+#include <fcntl.h>
15+#include <fnmatch.h>
16+#include <grp.h>
17+#include <limits.h>
18+#include <pwd.h>
19+#include <regex.h>
20+#include <spawn.h>
21+#include <stdarg.h>
22+#include <stdint.h>
23+#include <stdio.h>
24+#include <stdlib.h>
25+#include <string.h>
26+#include <sys/stat.h>
27+#include <sys/sysmacros.h>
28+#include <sys/types.h>
29+#include <sys/uio.h>
30+#include <sys/wait.h>
31+#include <tar.h>
32+#include <time.h>
33+#include <unistd.h>
34+
35+#ifndef O_SEARCH /* not present on some bsds */
36+#define O_SEARCH 0
37+#endif
38+
39+#if __APPLE__ /* macos lacks st_*tim from posix.1-2008 */
40+#define st_atim st_atimespec
41+#define st_ctim st_ctimespec
42+#define st_mtim st_mtimespec
43+#endif
44+
45+#ifndef HAVE_REALLOCARRAY
46+static void *
47+pax_reallocarray(void *p, size_t n, size_t m)
48+{
49+ if (m && n > SIZE_MAX / m) {
50+ errno = ENOMEM;
51+ return NULL;
52+ }
53+ return realloc(p, n * m);
54+}
55+#undef reallocarray
56+#define reallocarray pax_reallocarray
57+#endif
58+
59+#ifndef HAVE_PIPE2
60+static int
61+pax_pipe2(int fd[2], int flag)
62+{
63+ assert((flag | O_CLOEXEC) == O_CLOEXEC);
64+ if (pipe(fd) != 0)
65+ return -1;
66+ if (flag & O_CLOEXEC && (fcntl(fd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(fd[1], F_SETFD, FD_CLOEXEC) != 0))
67+ return -1;
68+ return 0;
69+}
70+#undef pipe2
71+#define pipe2 pax_pipe2
72+#endif
73+
74+#define LEN(a) (sizeof (a) / sizeof *(a))
75+#define ROUNDUP(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
76+#define MAXTIME 077777777777
77+#define MAXSIZE 077777777777
78+#define MAXUGID 07777777
79+
80+enum mode {
81+ LIST,
82+ READ,
83+ WRITE,
84+ COPY,
85+};
86+
87+enum format {
88+ CPIO,
89+ PAX,
90+ USTAR,
91+ GNUTAR,
92+ V7,
93+};
94+
95+enum field {
96+ ATIME = 1<<0,
97+ CTIME = 1<<1,
98+ GID = 1<<2,
99+ GNAME = 1<<3,
100+ LINKPATH = 1<<4,
101+ MODE = 1<<5,
102+ MTIME = 1<<6,
103+ PATH = 1<<7,
104+ SIZE = 1<<8,
105+ UID = 1<<9,
106+ UNAME = 1<<10,
107+};
108+
109+struct keyword {
110+ const char *name;
111+ enum field field;
112+};
113+
114+struct strbuf {
115+ char *str;
116+ size_t len, cap;
117+};
118+
119+struct header {
120+ /* keywords present in this header */
121+ enum field fields;
122+ /* keywords ignored because they were overridden by an option */
123+ enum field delete;
124+
125+ char type;
126+
127+ char *path;
128+ size_t pathlen;
129+ dev_t dev;
130+ ino_t ino;
131+ mode_t mode;
132+ uid_t uid;
133+ gid_t gid;
134+ nlink_t nlink;
135+ dev_t rdev;
136+ off_t size;
137+ struct timespec atime, mtime, ctime;
138+ char *link;
139+ size_t linklen;
140+ char *uname;
141+ char *gname;
142+
143+ struct strbuf pathbuf;
144+ struct strbuf linkbuf;
145+ struct strbuf unamebuf;
146+ struct strbuf gnamebuf;
147+
148+ /* tar-specific, pre-calculated split point between name and prefix */
149+ char *slash;
150+ /* read this data instead of stdin */
151+ char *data;
152+ /* source file path and flags for hard link (-l flag) */
153+ char *file;
154+ struct timespec fileatime;
155+ int flag;
156+};
157+
158+struct account {
159+ char *name;
160+ enum field type;
161+ uid_t uid;
162+ gid_t gid;
163+};
164+
165+struct bufio {
166+ int fd, err;
167+ off_t off;
168+ char buf[64 * 1024];
169+ char *pos, *end;
170+};
171+
172+struct replstr {
173+ regex_t old;
174+ char *new;
175+ int global;
176+ int print;
177+ int symlink;
178+ struct replstr *next;
179+};
180+
181+struct file {
182+ size_t namelen;
183+ size_t pathlen;
184+ dev_t dev;
185+ struct file *next;
186+ char name[];
187+};
188+
189+struct filelist {
190+ FILE *input;
191+ struct file *pending;
192+};
193+
194+typedef int readfn(struct bufio *, struct header *);
195+typedef void writefn(FILE *, struct header *);
196+
197+static int exitstatus;
198+static int aflag;
199+static int cflag;
200+static int dflag;
201+static int iflag;
202+static int kflag;
203+static int lflag;
204+static int nflag;
205+static int tflag;
206+static int uflag;
207+static int vflag;
208+static int Xflag;
209+static int follow;
210+static int preserve = ATIME | MTIME;
211+static const struct keyword keywords[] = {
212+ {"atime", ATIME},
213+ {"ctime", CTIME},
214+ {"gid", GID},
215+ {"gname", GNAME},
216+ {"linkpath", LINKPATH},
217+ {"mtime", MTIME},
218+ {"path", PATH},
219+ {"size", SIZE},
220+ {"uid", UID},
221+ {"uname", UNAME},
222+};
223+static struct {
224+ enum field delete;
225+ int linkdata;
226+ const char *listopt;
227+ const char *exthdrname;
228+ const char *globexthdrname;
229+ const char *invalid;
230+ int times;
231+} opt;
232+static struct header exthdr, globexthdr;
233+static struct replstr *replstr;
234+static time_t curtime;
235+static char **pats;
236+static size_t patslen;
237+static int *patsused;
238+static struct filelist files;
239+static struct bufio bioin;
240+static char *dest = "";
241+static int destfd = AT_FDCWD;
242+
243+static void
244+fatal(const char *fmt, ...)
245+{
246+ va_list ap;
247+
248+ if (fmt) {
249+ va_start(ap, fmt);
250+ vfprintf(stderr, fmt, ap);
251+ va_end(ap);
252+ if (fmt[0] && fmt[strlen(fmt) - 1] == ':') {
253+ fputc(' ', stderr);
254+ perror(NULL);
255+ } else {
256+ fputc('\n', stderr);
257+ }
258+ } else {
259+ perror(NULL);
260+ }
261+ exit(1);
262+}
263+
264+static char *
265+sbufalloc(struct strbuf *b, size_t n, size_t a)
266+{
267+ char *s;
268+
269+ if (n > b->cap - b->len) {
270+ if (n > SIZE_MAX - a || n + a > SIZE_MAX - b->len)
271+ fatal("path is too long");
272+ b->cap = ROUNDUP(n, a);
273+ s = malloc(b->cap);
274+ if (!s)
275+ fatal(NULL);
276+ if (b->len)
277+ memcpy(s, b->str, b->len);
278+ free(b->str);
279+ b->str = s;
280+ }
281+ return b->str + b->len;
282+}
283+
284+static int
285+sbuffmtv(struct strbuf *b, size_t a, const char *fmt, va_list ap)
286+{
287+ va_list aptmp;
288+ int n;
289+
290+ va_copy(aptmp, ap);
291+ n = vsnprintf(b->str ? b->str + b->len : NULL, b->cap - b->len, fmt, aptmp);
292+ va_end(aptmp);
293+ if (n < 0)
294+ fatal("vsnprintf:");
295+ if ((size_t)n >= b->cap - b->len) {
296+ sbufalloc(b, (size_t)n + 1, a);
297+ n = vsnprintf(b->str + b->len, b->cap - b->len, fmt, ap);
298+ if (n < 0)
299+ fatal("vsnprintf:");
300+ if ((size_t)n >= b->cap - b->len)
301+ fatal("vsnprintf: formatted size changed");
302+ }
303+ b->len += n;
304+ return n;
305+}
306+
307+static int
308+sbuffmt(struct strbuf *b, size_t a, const char *fmt, ...)
309+{
310+ va_list ap;
311+ int n;
312+
313+ va_start(ap, fmt);
314+ n = sbuffmtv(b, a, fmt, ap);
315+ va_end(ap);
316+ return n;
317+}
318+
319+static void
320+sbufcat(struct strbuf *b, const char *s, size_t n, size_t a)
321+{
322+ char *d;
323+
324+ d = sbufalloc(b, n + 1, a);
325+ memcpy(d, s, n);
326+ d[n] = 0;
327+ b->len += n;
328+}
329+
330+static void
331+bioinit(struct bufio *f, int fd)
332+{
333+ f->fd = fd;
334+ f->pos = f->end = f->buf;
335+ f->off = 0;
336+}
337+
338+static size_t
339+bioread(struct bufio *f, void *p, size_t n)
340+{
341+ size_t l;
342+ unsigned char *d;
343+ struct iovec iov[2];
344+ ssize_t r;
345+
346+ d = p;
347+ if (f->pos != f->end) {
348+ l = f->end - f->pos;
349+ if (n < l)
350+ l = n;
351+ memcpy(d, f->pos, l);
352+ f->pos += l;
353+ n -= l;
354+ d += l;
355+ }
356+ iov[1].iov_base = f->buf;
357+ iov[1].iov_len = sizeof f->buf;
358+ for (; n > 0; n -= r, d += r) {
359+ iov[0].iov_base = d;
360+ iov[0].iov_len = n;
361+ r = readv(f->fd, iov, 2);
362+ if (r < 0)
363+ f->err = errno;
364+ if (r <= 0)
365+ break;
366+ if ((size_t)r >= n) {
367+ f->pos = f->buf;
368+ f->end = f->buf + (r - n);
369+ r = n;
370+ }
371+ }
372+ l = d - (unsigned char *)p;
373+ f->off += l;
374+ return l;
375+}
376+
377+static int
378+bioskip(struct bufio *f, off_t n)
379+{
380+ static int seekfail;
381+ size_t l;
382+ ssize_t r;
383+
384+ if (f->pos != f->end) {
385+ l = f->end - f->pos;
386+ if (n < (off_t)l) {
387+ f->pos += n;
388+ return 0;
389+ }
390+ n -= l;
391+ f->pos = f->end = f->buf;
392+ }
393+ if (!seekfail) {
394+ if (n == 0 || lseek(f->fd, n, SEEK_CUR) >= 0)
395+ return 0;
396+ seekfail = 1;
397+ }
398+ for (; n > 0; n -= r) {
399+ l = sizeof f->buf;
400+ if (n < (off_t)l)
401+ l = n;
402+ r = read(f->fd, f->buf, l);
403+ if (r <= 0)
404+ return -1;
405+ }
406+ return 0;
407+}
408+
409+static void
410+copyblock(char *b, struct bufio *r, size_t nr, FILE *w, size_t nw)
411+{
412+ if (bioread(r, b, nr) != nr) {
413+ if (r->err)
414+ fatal("read: %s", strerror(r->err));
415+ fatal("archive truncated");
416+ }
417+ if (nw > nr)
418+ memset(b + nr, 0, nw - nr);
419+ if (nw && fwrite(b, 1, nw, w) != nw)
420+ fatal("write:");
421+}
422+
423+/* nr and nw must differ by at most 8192 */
424+static void
425+copy(struct bufio *r, off_t nr, FILE *w, off_t nw)
426+{
427+ char b[8192];
428+
429+ assert(nr - nw <= (off_t)sizeof b || nw - nr <= (off_t)sizeof b);
430+ for (; nr > (off_t)sizeof b && nw > (off_t)sizeof b; nr -= (off_t)sizeof b, nw -= (off_t)sizeof b)
431+ copyblock(b, r, sizeof b, w, sizeof b);
432+ copyblock(b, r, nr, w, nw);
433+}
434+
435+static struct account *
436+findaccount(const char *name, uid_t uid, gid_t gid)
437+{
438+ static struct account *accts;
439+ static size_t acctslen;
440+ struct account *a;
441+
442+ for (a = accts; a < accts + acctslen; ++a) {
443+ if ((uid == (uid_t)-1 || uid == a->uid) && (gid == (gid_t)-1 || gid == a->gid)
444+ && (!name || (a->name && strcmp(a->name, name) == 0)))
445+ return a;
446+ }
447+ if ((acctslen & (acctslen - 1)) == 0) {
448+ accts = reallocarray(accts, acctslen ? acctslen * 2 : 16, sizeof *accts);
449+ if (!accts)
450+ fatal(NULL);
451+ }
452+ a = &accts[acctslen++];
453+ a->name = NULL;
454+ a->type = 0;
455+ a->uid = -1;
456+ a->gid = -1;
457+ if (name) {
458+ a->name = strdup(name);
459+ if (!a->name)
460+ fatal(NULL);
461+ }
462+ return a;
463+}
464+
465+static uid_t
466+unametouid(const char *uname, uid_t fallback)
467+{
468+ struct account *a;
469+ struct passwd *pw;
470+
471+ if (!*uname)
472+ return fallback;
473+ a = findaccount(uname, (uid_t)-1, (gid_t)-1);
474+ if (~a->type & UID) {
475+ a->type |= UID;
476+ pw = getpwnam(uname);
477+ a->uid = pw ? pw->pw_uid : (uid_t)-1;
478+ }
479+ return a->uid != (uid_t)-1 ? a->uid : fallback;
480+}
481+
482+static gid_t
483+gnametogid(const char *gname, gid_t fallback)
484+{
485+ struct account *a;
486+ struct group *gr;
487+
488+ if (!*gname)
489+ return fallback;
490+ a = findaccount(gname, (uid_t)-1, (gid_t)-1);
491+ if (~a->type & GID) {
492+ a->type |= GID;
493+ gr = getgrnam(gname);
494+ a->gid = gr ? gr->gr_gid : (gid_t)-1;
495+ }
496+ return a->gid != (gid_t)-1 ? a->gid : fallback;
497+}
498+
499+static char *
500+uidtouname(uid_t uid, char *fallback)
501+{
502+ struct account *a;
503+ struct passwd *pw;
504+
505+ a = findaccount(NULL, uid, (gid_t)-1);
506+ if (~a->type & UID) {
507+ a->type |= UID;
508+ a->uid = uid;
509+ assert(!a->name);
510+ pw = getpwuid(uid);
511+ if (pw) {
512+ a->name = strdup(pw->pw_name);
513+ if (!a->name)
514+ fatal(NULL);
515+ }
516+ }
517+ return a->name ? a->name : fallback;
518+}
519+
520+static char *
521+gidtogname(gid_t gid, char *fallback)
522+{
523+ struct account *a;
524+ struct group *gr;
525+
526+ a = findaccount(NULL, (uid_t)-1, gid);
527+ if (~a->type & GID) {
528+ a->type |= GID;
529+ a->gid = gid;
530+ assert(!a->name);
531+ gr = getgrgid(gid);
532+ if (gr) {
533+ a->name = strdup(gr->gr_name);
534+ if (!a->name)
535+ fatal(NULL);
536+ }
537+ }
538+ return a->name ? a->name : fallback;
539+}
540+
541+static unsigned long long
542+octnum(const char *str, size_t len)
543+{
544+ const char *end;
545+ unsigned c;
546+ unsigned long long n;
547+
548+ n = 0;
549+ end = str + len;
550+ /* some archives have leading spaces, so skip them */
551+ for (; str != end && *str == ' '; ++str)
552+ ;
553+ for (; str != end; ++str) {
554+ c = *str;
555+ if (c == ' ' || c == '\0')
556+ break;
557+ c -= '0';
558+ if (c > 7)
559+ fatal("invalid number field");
560+ n = n * 8 + c;
561+ }
562+ return n;
563+}
564+
565+static unsigned long long
566+decnum(const char *str, size_t len, char **pos)
567+{
568+ const char *end;
569+ unsigned c;
570+ unsigned long long n;
571+
572+ n = 0;
573+ end = str + len;
574+ for (; str != end; ++str) {
575+ c = *str - '0';
576+ if (c > 9)
577+ break;
578+ n = n * 10 + c;
579+ }
580+ if (pos)
581+ *pos = (char *)str;
582+ return n;
583+}
584+
585+static int
586+readustar(struct bufio *f, struct header *h)
587+{
588+ static char buf[512];
589+ static off_t end;
590+ size_t namelen, prefixlen, linklen;
591+ unsigned long sum;
592+ int i;
593+ enum format format;
594+
595+ assert(bioin.off <= end);
596+ if (bioskip(f, end - bioin.off) != 0 || bioread(f, buf, sizeof buf) != sizeof buf) {
597+ if (f->err)
598+ fatal("read: %s", strerror(f->err));
599+ fatal("archive truncated");
600+ }
601+ sum = 0;
602+ for (i = 0; i < 512; ++i)
603+ sum += ((unsigned char *)buf)[i];
604+ if (sum == 0)
605+ return 0;
606+ for (i = 148; i < 156; ++i)
607+ sum += ' ' - ((unsigned char *)buf)[i];
608+ if (sum != octnum(buf + 148, 8))
609+ fatal("invalid tar header: bad checksum");
610+ if (memcmp(buf + 257, "ustar\0" "00", 8) == 0)
611+ format = USTAR;
612+ else if (memcmp(buf + 257, "ustar ", 8) == 0)
613+ format = GNUTAR;
614+ else
615+ format = V7;
616+ h->fields = PATH | UID | GID | SIZE;
617+ namelen = strnlen(buf, 100);
618+ prefixlen = format == USTAR ? strnlen(buf + 345, 155) : 0;
619+ if (namelen == 100 || prefixlen > 0) {
620+ h->pathbuf.len = 0;
621+ if (prefixlen > 0) {
622+ sbufcat(&h->pathbuf, buf + 345, prefixlen, 1024);
623+ sbufcat(&h->pathbuf, "/", 1, 1024);
624+ }
625+ sbufcat(&h->pathbuf, buf, namelen, 1024);
626+ h->path = h->pathbuf.str;
627+ h->pathlen = h->pathbuf.len;
628+ } else {
629+ h->path = buf;
630+ h->pathlen = namelen;
631+ }
632+ h->dev = 0;
633+ h->ino = 0;
634+ h->mode = octnum(buf + 100, 8);
635+ h->uid = octnum(buf + 108, 8);
636+ h->gid = octnum(buf + 116, 8);
637+ h->nlink = 1;
638+ h->size = octnum(buf + 124, 12);
639+ end = bioin.off + ROUNDUP(h->size, 512);
640+ h->mtime = (struct timespec){.tv_sec = octnum(buf + 136, 12)};
641+ if (format == GNUTAR) {
642+ h->fields |= ATIME | CTIME;
643+ h->atime = (struct timespec){.tv_sec = octnum(buf + 345, 12)};
644+ h->ctime = (struct timespec){.tv_sec = octnum(buf + 357, 12)};
645+ }
646+ h->type = buf[156];
647+ if (h->type == AREGTYPE)
648+ h->type = REGTYPE;
649+
650+ linklen = strnlen(buf + 157, 100);
651+ if (linklen > 0)
652+ h->fields |= LINKPATH;
653+ if (linklen == 100) {
654+ h->linkbuf.len = 0;
655+ sbufcat(&h->linkbuf, buf + 157, 100, 1024);
656+ h->link = h->linkbuf.str;
657+ h->linklen = h->linkbuf.len;
658+ } else {
659+ h->link = buf + 157;
660+ }
661+ h->linklen = linklen;
662+ if (format == V7) {
663+ h->uname = "";
664+ h->gname = "";
665+ } else {
666+ h->fields |= UNAME | GNAME;
667+ h->uname = buf + 265;
668+ if (!memchr(h->uname, '\0', 32))
669+ fatal("uname is not NUL-terminated");
670+ h->gname = buf + 297;
671+ if (!memchr(h->gname, '\0', 32))
672+ fatal("gname is not NUL-terminated");
673+ if (h->type == CHRTYPE || h->type == BLKTYPE) {
674+ unsigned major, minor;
675+
676+ major = octnum(buf + 329, 8);
677+ minor = octnum(buf + 337, 8);
678+ h->rdev = makedev(major, minor);
679+ }
680+ }
681+ return 1;
682+}
683+
684+static void
685+parsetime(struct timespec *ts, const char *field, const char *str, size_t len)
686+{
687+ const char *end = str + len;
688+ char *pos;
689+ unsigned long long subsec;
690+ size_t sublen;
691+
692+ ts->tv_sec = decnum(str, len, &pos);
693+ if (*pos == '.') {
694+ str = ++pos;
695+ subsec = decnum(str, end - str, &pos);
696+ for (sublen = pos - str; sublen < 9; ++sublen)
697+ subsec *= 10;
698+ ts->tv_nsec = subsec % 1000000000;
699+ }
700+ if (pos != end)
701+ fatal("invalid extended header: bad %s", field);
702+}
703+
704+static void
705+extkeyval(struct header *h, const char *key, const char *val, size_t vallen)
706+{
707+ enum field field;
708+ char *end;
709+ const struct keyword *kw;
710+
711+ field = 0;
712+ for (kw = keywords; kw != keywords + LEN(keywords); ++kw) {
713+ if (strcmp(key, kw->name) == 0) {
714+ field = kw->field;
715+ break;
716+ }
717+ }
718+ if (!field) {
719+ if (strcmp(key, "charset") == 0) {
720+ } else if (strcmp(key, "comment") == 0) {
721+ /* ignore */
722+ } else if (strcmp(key, "hdrcharset") == 0) {
723+ } else if (strncmp(key, "realtime.", 9) == 0) {
724+ } else if (strncmp(key, "security.", 9) == 0) {
725+ } else {
726+ fprintf(stderr, "ignoring unknown keyword '%s'\n", key);
727+ }
728+ return;
729+ }
730+ if ((h->delete | opt.delete) & field)
731+ return;
732+
733+ switch (field) {
734+ case ATIME:
735+ parsetime(&h->atime, "atime", val, vallen);
736+ break;
737+ case CTIME:
738+ parsetime(&h->ctime, "ctime", val, vallen);
739+ break;
740+ case GID:
741+ h->gid = decnum(val, vallen, &end);
742+ if (end != val + vallen)
743+ fatal("invalid extended header: bad gid");
744+ break;
745+ case GNAME:
746+ h->gnamebuf.len = 0;
747+ sbufcat(&h->gnamebuf, val, vallen, 256);
748+ h->gname = h->gnamebuf.str;
749+ break;
750+ case LINKPATH:
751+ h->linkbuf.len = 0;
752+ sbufcat(&h->linkbuf, val, vallen, 1024);
753+ h->link = h->linkbuf.str;
754+ h->linklen = h->linkbuf.len;
755+ break;
756+ case MTIME:
757+ parsetime(&h->mtime, "mtime", val, vallen);
758+ break;
759+ case PATH:
760+ h->pathbuf.len = 0;
761+ sbufcat(&h->pathbuf, val, vallen, 1024);
762+ h->path = h->pathbuf.str;
763+ h->pathlen = h->pathbuf.len;
764+ break;
765+ case SIZE:
766+ h->size = decnum(val, vallen, &end);
767+ if (end != val + vallen)
768+ fatal("invalid extended header: bad size");
769+ break;
770+ case UID:
771+ h->uid = decnum(val, vallen, &end);
772+ if (end != val + vallen)
773+ fatal("invalid extended header: bad uid");
774+ break;
775+ case UNAME:
776+ h->unamebuf.len = 0;
777+ sbufcat(&h->unamebuf, val, vallen, 256);
778+ h->uname = h->unamebuf.str;
779+ break;
780+ default:
781+ return;
782+ }
783+ h->fields |= field;
784+}
785+
786+static void
787+readexthdr(struct bufio *f, struct header *h, off_t len)
788+{
789+ static struct strbuf buf;
790+ size_t reclen, vallen;
791+ char *rec, *end, *key, *val;
792+
793+ if (len > (off_t)SIZE_MAX)
794+ fatal("extended header is too large");
795+ buf.len = 0;
796+ sbufalloc(&buf, (size_t)len, 8192);
797+ if (bioread(f, buf.str, (size_t)len) != (size_t)len) {
798+ if (f->err)
799+ fatal("read: %s", strerror(f->err));
800+ fatal("archive truncated");
801+ }
802+ rec = buf.str;
803+ while (len > 0) {
804+ end = memchr(rec, '\n', len);
805+ if (!end)
806+ fatal("invalid extended header: record is missing newline");
807+ *end = '\0';
808+ reclen = decnum(rec, (size_t)(end - rec), &key);
809+ if (*key != ' ' || reclen != (unsigned long long)(end - rec + 1))
810+ fatal("invalid extended header: invalid record");
811+ ++key;
812+ val = strchr(key, '=');
813+ if (!val)
814+ fatal("invalid extended header: record has no '='");
815+ *val++ = '\0';
816+ vallen = end - val;
817+ extkeyval(h, key, val, vallen);
818+ len -= reclen;
819+ rec += reclen;
820+ }
821+}
822+
823+static void
824+readgnuhdr(struct bufio *f, struct strbuf *b, off_t len)
825+{
826+ if (len > (off_t)(SIZE_MAX - 1))
827+ fatal("GNU header is too large");
828+ b->len = 0;
829+ sbufalloc(b, (size_t)(len + 1), 1024);
830+ if (bioread(f, b->str, (size_t)len) != (size_t)len) {
831+ if (f->err)
832+ fatal("read: %s", strerror(f->err));
833+ fatal("archive truncated");
834+ }
835+ b->str[len] = '\0';
836+ b->len = len;
837+}
838+
839+static int
840+readpax(struct bufio *f, struct header *h)
841+{
842+ exthdr.fields = exthdr.delete;
843+ while (readustar(f, h)) {
844+ switch (h->type) {
845+ case 'g':
846+ readexthdr(f, &globexthdr, h->size);
847+ break;
848+ case 'x':
849+ readexthdr(f, &exthdr, h->size);
850+ break;
851+ case 'L':
852+ if ((exthdr.delete | opt.delete) & PATH)
853+ break;
854+ readgnuhdr(f, &exthdr.pathbuf, h->size);
855+ exthdr.path = exthdr.pathbuf.str;
856+ exthdr.pathlen = exthdr.pathbuf.len;
857+ exthdr.fields |= PATH;
858+ break;
859+ case 'K':
860+ if ((exthdr.delete | opt.delete) & LINKPATH)
861+ break;
862+ readgnuhdr(f, &exthdr.linkbuf, h->size);
863+ exthdr.link = exthdr.linkbuf.str;
864+ exthdr.linklen = exthdr.linkbuf.len;
865+ exthdr.fields |= LINKPATH;
866+ break;
867+ default:
868+ return 1;
869+ }
870+ }
871+ return 0;
872+}
873+
874+static int
875+readcpio(struct bufio *f, struct header *h)
876+{
877+ static off_t end;
878+ unsigned long type;
879+ char buf[76];
880+
881+ if (bioskip(f, end - bioin.off) != 0 || bioread(f, buf, sizeof buf) != sizeof buf) {
882+ if (f->err)
883+ fatal("read: %s", strerror(f->err));
884+ fatal("archive truncated");
885+ }
886+ if (memcmp(buf, "070707", 6) != 0)
887+ fatal("invalid cpio header: bad magic");
888+ h->pathlen = octnum(buf + 59, 6);
889+ if (h->pathlen == 0)
890+ fatal("invalid cpio header: c_namesize is 0");
891+ h->pathbuf.len = 0;
892+ sbufalloc(&h->pathbuf, h->pathlen, 1024);
893+ h->path = h->pathbuf.str;
894+ if (bioread(f, h->path, h->pathlen) != h->pathlen) {
895+ if (f->err)
896+ fatal("read: %s", strerror(f->err));
897+ fatal("archive truncated");
898+ }
899+ if (h->path[--h->pathlen] != '\0')
900+ fatal("invalid cpio header: name is not NUL-terminated");
901+ if (strcmp(h->path, "TRAILER!!!") == 0)
902+ return 0;
903+
904+ h->fields = PATH | MODE | UID | GID | MTIME | SIZE;
905+ h->dev = octnum(buf + 6, 6);
906+ h->ino = octnum(buf + 12, 6);
907+ type = octnum(buf + 18, 6);
908+ h->mode = type & 07777;
909+ type &= ~07777;
910+ switch (type) {
911+ case C_ISDIR: h->type = DIRTYPE; break;
912+ case C_ISFIFO: h->type = FIFOTYPE; break;
913+ case C_ISREG: h->type = REGTYPE; break;
914+ case C_ISLNK: h->type = SYMTYPE; break;
915+ case C_ISBLK: h->type = BLKTYPE; break;
916+ case C_ISCHR: h->type = CHRTYPE; break;
917+ default: fatal("invalid cpio header: invalid or unsupported file type: %#o", type);
918+ }
919+ h->uid = octnum(buf + 24, 6);
920+ h->gid = octnum(buf + 30, 6);
921+ h->nlink = octnum(buf + 36, 6);
922+ h->rdev = octnum(buf + 42, 6);
923+ h->mtime = (struct timespec){.tv_sec = octnum(buf + 48, 11)};
924+ h->size = octnum(buf + 65, 11);
925+ h->uname = "";
926+ h->gname = "";
927+ if (h->type == SYMTYPE) {
928+ if (h->size > (off_t)(SIZE_MAX - 1))
929+ fatal("symlink target is too long");
930+ h->linklen = h->size;
931+ h->linkbuf.len = 0;
932+ h->link = sbufalloc(&h->linkbuf, h->linklen + 1, 1024);
933+ if (bioread(f, h->link, h->linklen) != h->linklen) {
934+ if (f->err)
935+ fatal("read: %s", strerror(f->err));
936+ fatal("archive truncated");
937+ }
938+ h->link[h->linklen] = '\0';
939+ h->size = 0;
940+ h->fields |= LINKPATH;
941+ } else {
942+ h->link = "";
943+ h->linklen = 0;
944+ }
945+ end = bioin.off + h->size;
946+ return 1;
947+}
948+
949+static int
950+decompress(const char *algo, int fd, pid_t *pid)
951+{
952+ extern char **environ;
953+ posix_spawn_file_actions_t fa;
954+ int p[2], err;
955+ char *argv[3];
956+
957+ if (!algo)
958+ return fd;
959+ argv[0] = (char *)algo;
960+ argv[1] = "-dc";
961+ argv[2] = NULL;
962+ if (pipe2(p, O_CLOEXEC) != 0)
963+ fatal("pipe2:");
964+ err = posix_spawn_file_actions_init(&fa);
965+ if (err)
966+ fatal("posix_spawn_file_actions_init: %s", strerror(errno));
967+ err = posix_spawn_file_actions_adddup2(&fa, fd, 0);
968+ if (err)
969+ fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno));
970+ err = posix_spawn_file_actions_adddup2(&fa, p[1], 1);
971+ if (err)
972+ fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno));
973+ err = posix_spawnp(pid, algo, &fa, NULL, argv, environ);
974+ if (err)
975+ fatal("posix_spawnp %s: %s", algo, strerror(errno));
976+ close(p[1]);
977+ return p[0];
978+}
979+
980+static readfn *
981+detectformat(struct bufio *f, const char *algo, pid_t *pid)
982+{
983+ size_t l, i;
984+ ssize_t n;
985+ unsigned char *b;
986+
987+again:
988+ f->fd = decompress(algo, f->fd, pid);
989+ b = (unsigned char *)f->buf;
990+ for (l = 0; l < 512; l += n) {
991+ n = read(f->fd, b + l, 512 - l);
992+ if (n < 0)
993+ fatal("read:");
994+ if (n == 0)
995+ break;
996+ }
997+ f->pos = f->buf;
998+ f->end = f->buf + l;
999+ if (l == 512) {
1000+ unsigned long sum, hdrsum;
1001+
1002+ sum = 0;
1003+ for (i = 0; i < 512; ++i)
1004+ sum += b[i];
1005+ if (sum == 0)
1006+ return readpax;
1007+ hdrsum = 0;
1008+ for (i = 148; i < 156; ++i) {
1009+ sum += ' ' - b[i];
1010+ if (b[i] >= '0' && b[i] <= '9')
1011+ hdrsum = hdrsum * 8 + (b[i] - '0');
1012+ }
1013+ if (sum == hdrsum)
1014+ return readpax;
1015+ }
1016+ if (l >= 76) {
1017+ if (memcmp(b, "070707", 6) == 0)
1018+ return readcpio;
1019+ }
1020+ if (!algo) {
1021+ static const struct command {
1022+ char algo[6];
1023+ unsigned char magiclen;
1024+ unsigned char magic[6];
1025+ } cmds[] = {
1026+ {"gzip", 2, {0x1F, 0x8B}},
1027+ {"bzip2", 2, {'B', 'Z'}},
1028+ {"xz", 6, {0xFD, '7', 'z', 'X', 'Z', 0x00}},
1029+ {"zstd", 4, {0x28, 0xB5, 0x2F, 0xFD}},
1030+ {"lzip", 4, {'L', 'Z', 'I', 'P'}},
1031+ };
1032+ const struct command *c;
1033+
1034+ for (c = cmds; c < cmds + LEN(cmds); ++c) {
1035+ if (l >= c->magiclen && memcmp(b, c->magic, c->magiclen) == 0) {
1036+ if (lseek(f->fd, 0, SEEK_SET) != 0)
1037+ fatal("compression detection requires seekable input");
1038+ algo = c->algo;
1039+ goto again;
1040+ }
1041+ }
1042+ }
1043+ return NULL;
1044+}
1045+
1046+static FILE *
1047+compress(const char *algo, const char *name, pid_t *pid)
1048+{
1049+ extern char **environ;
1050+ FILE *f;
1051+ int fd, p[2], err;
1052+ posix_spawn_file_actions_t fa;
1053+ char *argv[3];
1054+
1055+ if (!algo) {
1056+ if (name && !freopen(name, aflag ? "r+" : "w", stdout))
1057+ fatal("open %s:");
1058+ if (aflag && name) {
1059+ if (fseek(stdout, 0, SEEK_END) != 0)
1060+ fatal("fseek %s:");
1061+ }
1062+ return stdout;
1063+ }
1064+ argv[0] = (char *)algo;
1065+ argv[1] = "-c";
1066+ argv[2] = NULL;
1067+ if (name) {
1068+ fd = open(name, O_WRONLY | O_CREAT, 0666);
1069+ if (fd < 0)
1070+ fatal("open %s:");
1071+ } else {
1072+ fd = 1;
1073+ }
1074+ if (pipe2(p, O_CLOEXEC) != 0)
1075+ fatal("pipe2:");
1076+ f = fdopen(p[1], "w");
1077+ if (!f)
1078+ fatal("fdopen:");
1079+ err = posix_spawn_file_actions_init(&fa);
1080+ if (err)
1081+ fatal("posix_spawn_file_actions_init: %s", strerror(errno));
1082+ err = posix_spawn_file_actions_adddup2(&fa, p[0], 0);
1083+ if (err)
1084+ fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno));
1085+ err = posix_spawn_file_actions_adddup2(&fa, fd, 1);
1086+ if (err)
1087+ fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno));
1088+ err = posix_spawnp(pid, algo, &fa, NULL, argv, environ);
1089+ if (err)
1090+ fatal("posix_spawnp %s: %s", algo, strerror(errno));
1091+ close(fd);
1092+ close(p[0]);
1093+ return f;
1094+}
1095+
1096+static char *
1097+splitname(char *name, size_t namelen)
1098+{
1099+ char *slash;
1100+
1101+ if (namelen > 256)
1102+ return NULL;
1103+ slash = memchr(name + namelen - 100, '/', 100);
1104+ if (!slash || slash - name > 155)
1105+ return NULL;
1106+ return slash;
1107+}
1108+
1109+static void
1110+openfile(struct header *h)
1111+{
1112+ int fd;
1113+
1114+ if (h->file) {
1115+ fd = open(h->file, O_RDONLY);
1116+ if (fd < 0)
1117+ fatal("open %s:", h->file);
1118+ bioinit(&bioin, fd);
1119+ }
1120+}
1121+
1122+static void
1123+closefile(struct header *h)
1124+{
1125+ if (h->file) {
1126+ if (tflag)
1127+ futimens(bioin.fd, (struct timespec[2]){h->fileatime, {.tv_nsec = UTIME_OMIT}});
1128+ close(bioin.fd);
1129+ }
1130+}
1131+
1132+static void
1133+closeustar(FILE *f)
1134+{
1135+ char pad[512];
1136+
1137+ memset(pad, 0, 512);
1138+ if (fwrite(pad, 512, 1, f) != 1)
1139+ fatal("write:");
1140+ if (fwrite(pad, 512, 1, f) != 1)
1141+ fatal("write:");
1142+}
1143+
1144+static void
1145+writeustar(FILE *f, struct header *h)
1146+{
1147+ char buf[512], *slash, tmp[32];
1148+ unsigned long sum;
1149+ int i;
1150+
1151+ if (!h) {
1152+ closeustar(f);
1153+ return;
1154+ }
1155+ slash = h->slash;
1156+ if (!slash && h->pathlen > 100) {
1157+ slash = splitname(h->path, h->pathlen);
1158+ if (!slash)
1159+ fatal("path is too long: %s\n", h->path);
1160+ }
1161+ if (slash) {
1162+ size_t len;
1163+
1164+ strncpy(buf, slash + 1, 100);
1165+ len = slash - h->path;
1166+ memcpy(buf + 345, h->path, len);
1167+ memset(buf + 345 + len, 0, 155 - len);
1168+ } else {
1169+ strncpy(buf, h->path, 100);
1170+ memset(buf + 345, 0, 155);
1171+ }
1172+ if (h->mode > 07777777)
1173+ fatal("mode is too large: %ju", (uintmax_t)h->mode);
1174+ snprintf(tmp, sizeof(tmp), "%.7lo", (unsigned long)h->mode & 07777777);
1175+ memcpy(buf + 100, tmp, 8);
1176+ if (h->uid > MAXUGID)
1177+ fatal("uid is too large: %ju", (uintmax_t)h->uid);
1178+ snprintf(tmp, sizeof(tmp), "%.7lo", (unsigned long)h->uid & 07777777);
1179+ memcpy(buf + 108, tmp, 8);
1180+ if (h->gid > MAXUGID)
1181+ fatal("gid is too large: %ju", (uintmax_t)h->gid);
1182+ snprintf(tmp, sizeof(tmp), "%.7lo", (unsigned long)h->gid & 07777777);
1183+ memcpy(buf + 116, tmp, 8);
1184+ if (h->size < 0 || h->size > MAXSIZE)
1185+ fatal("size is too large: %ju", (uintmax_t)h->size);
1186+ snprintf(buf + 124, 12, "%.11llo", (unsigned long long)h->size);
1187+ if (h->mtime.tv_sec < 0 || h->mtime.tv_sec > MAXTIME)
1188+ fatal("mtime is too large: %ju", (uintmax_t)h->mtime.tv_sec);
1189+ snprintf(buf + 136, 12, "%.11llo", (unsigned long long)h->mtime.tv_sec);
1190+ memset(buf + 148, ' ', 8);
1191+ buf[156] = h->type;
1192+ if (h->linklen > 100)
1193+ fatal("link name is too long: %s\n", h->link);
1194+ strncpy(buf + 157, h->link, 100);
1195+ memcpy(buf + 257, "ustar", 6);
1196+ memcpy(buf + 263, "00", 2);
1197+ if (strlen(h->uname) > 31)
1198+ fatal("user name is too long: %s\n", h->uname);
1199+ strncpy(buf + 265, h->uname, 32);
1200+ if (strlen(h->gname) > 31)
1201+ fatal("group name is too long: %s\n", h->gname);
1202+ strncpy(buf + 297, h->gname, 32);
1203+ if (major(h->rdev) > 07777777)
1204+ fatal("device major is too large: %ju\n", (uintmax_t)major(h->rdev));
1205+ snprintf(tmp, sizeof(tmp), "%.7lo", (unsigned long)major(h->rdev) & 07777777);
1206+ memcpy(buf + 329, tmp, 8);
1207+ if (minor(h->rdev) > 07777777)
1208+ fatal("device minor is too large: %ju\n", (uintmax_t)minor(h->rdev));
1209+ snprintf(tmp, sizeof(tmp), "%.7lo", (unsigned long)minor(h->rdev) & 07777777);
1210+ memcpy(buf + 337, tmp, 8);
1211+ memset(buf + 500, 0, 12);
1212+ sum = 0;
1213+ for (i = 0; i < 512; ++i)
1214+ sum += ((unsigned char *)buf)[i];
1215+ snprintf(tmp, sizeof(tmp), "%.7lo", sum & 07777777);
1216+ memcpy(buf + 148, tmp, 8);
1217+ if (fwrite(buf, 512, 1, f) != 1)
1218+ fatal("write:");
1219+ if (h->data) {
1220+ size_t pad;
1221+
1222+ if (fwrite(h->data, 1, (size_t)h->size, f) != (size_t)h->size)
1223+ fatal("write:");
1224+ pad = (size_t)(ROUNDUP(h->size, 512) - h->size);
1225+ memset(bioin.buf, 0, pad);
1226+ if (fwrite(bioin.buf, 1, pad, f) != pad)
1227+ fatal("write:");
1228+ } else if (h->size > 0) {
1229+ openfile(h);
1230+ copy(&bioin, h->size, f, ROUNDUP(h->size, 512));
1231+ closefile(h);
1232+ }
1233+}
1234+
1235+static void
1236+writerec(struct strbuf *ext, const char *fmt, ...)
1237+{
1238+ static struct strbuf buf;
1239+ va_list ap;
1240+ int d, n, m, l;
1241+
1242+ buf.len = 0;
1243+ va_start(ap, fmt);
1244+ l = sbuffmtv(&buf, 256, fmt, ap);
1245+ va_end(ap);
1246+
1247+ d = 0;
1248+ m = 1;
1249+ for (n = l; n > 0; n /= 10) {
1250+ m *= 10;
1251+ ++d;
1252+ }
1253+ n = d + 1 + l + 1;
1254+ if (n >= m)
1255+ ++n;
1256+ sbuffmt(ext, 256, "%d %.*s\n", n, l, buf.str);
1257+}
1258+
1259+static void
1260+writetimerec(struct strbuf *ext, char *kw, struct timespec *ts)
1261+{
1262+ if (ts->tv_nsec != 0)
1263+ writerec(ext, "%s=%ju.%.9ld", kw, (uintmax_t)ts->tv_sec, ts->tv_nsec % 1000000000);
1264+ else
1265+ writerec(ext, "%s=%ju", kw, (uintmax_t)ts->tv_sec);
1266+}
1267+
1268+static void
1269+writeexthdr(FILE *f, int type, struct header *h)
1270+{
1271+ static struct strbuf ext;
1272+ struct header exthdr;
1273+
1274+ ext.len = 0;
1275+ if (h->fields & PATH)
1276+ writerec(&ext, "path=%s", h->path);
1277+ if (h->fields & UID)
1278+ writerec(&ext, "uid=%ju", (uintmax_t)h->uid);
1279+ if (h->fields & GID)
1280+ writerec(&ext, "gid=%ju", (uintmax_t)h->gid);
1281+ if (h->fields & SIZE)
1282+ writerec(&ext, "size=%ju", (uintmax_t)h->size);
1283+ if (h->fields & MTIME)
1284+ writetimerec(&ext, "mtime", &h->mtime);
1285+ if (h->fields & ATIME)
1286+ writetimerec(&ext, "atime", &h->atime);
1287+ if (h->fields & CTIME)
1288+ writetimerec(&ext, "ctime", &h->ctime);
1289+ if (h->fields & UNAME)
1290+ writerec(&ext, "uname=%s", h->uname);
1291+ if (h->fields & GNAME)
1292+ writerec(&ext, "gname=%s", h->gname);
1293+ if (ext.len > 0) {
1294+ memset(&exthdr, 0, sizeof exthdr);
1295+ exthdr.path = "pax_extended_header";
1296+ exthdr.pathlen = 20;
1297+ exthdr.mode = 0600;
1298+ exthdr.link = "";
1299+ exthdr.uname = "";
1300+ exthdr.gname = "";
1301+ exthdr.size = ext.len;
1302+ exthdr.type = type;
1303+ exthdr.data = ext.str;
1304+ writeustar(f, &exthdr);
1305+ }
1306+}
1307+
1308+static void
1309+mergehdr(struct header *dst, struct header *src, enum field fields)
1310+{
1311+ fields &= src->fields;
1312+ if (fields & PATH) {
1313+ dst->path = src->path;
1314+ dst->pathlen = src->pathlen;
1315+ }
1316+ if (fields & UID)
1317+ dst->uid = src->uid;
1318+ if (fields & GID)
1319+ dst->gid = src->gid;
1320+ if (fields & SIZE)
1321+ dst->size = src->size;
1322+ if (fields & MTIME)
1323+ dst->mtime = src->mtime;
1324+ if (fields & ATIME)
1325+ dst->atime = src->atime;
1326+ if (fields & CTIME)
1327+ dst->ctime = src->ctime;
1328+ if (fields & UNAME)
1329+ dst->uname = src->uname;
1330+ if (fields & GNAME)
1331+ dst->gname = src->gname;
1332+ if (fields & LINKPATH) {
1333+ dst->link = src->link;
1334+ dst->linklen = src->linklen;
1335+ }
1336+ dst->fields |= fields;
1337+}
1338+
1339+static void
1340+writepax(FILE *f, struct header *h)
1341+{
1342+ enum field fields;
1343+
1344+ if (!h) {
1345+ closeustar(f);
1346+ return;
1347+ }
1348+ if (vflag)
1349+ fprintf(stderr, "%s\n", h->path);
1350+ fields = 0;
1351+ if (h->pathlen > 100) {
1352+ h->slash = splitname(h->path, h->pathlen);
1353+ if (!h->slash)
1354+ fields |= PATH;
1355+ }
1356+ if (h->uid > MAXUGID)
1357+ fields |= UID;
1358+ if (h->gid > MAXUGID)
1359+ fields |= GID;
1360+ if (h->size > MAXSIZE)
1361+ fields |= SIZE;
1362+ if (h->mtime.tv_sec > MAXTIME || h->mtime.tv_nsec != 0)
1363+ fields |= MTIME;
1364+ if (opt.times)
1365+ fields |= ATIME | CTIME;
1366+ if (strlen(h->uname) > 31)
1367+ fields |= UNAME;
1368+ if (strlen(h->gname) > 31)
1369+ fields |= GNAME;
1370+ if (h->linklen > 100)
1371+ fields |= LINKPATH;
1372+ fields &= ~(exthdr.fields | opt.delete);
1373+ mergehdr(&exthdr, h, fields);
1374+ writeexthdr(f, 'x', &exthdr);
1375+
1376+ /* reset fields merged into extended header */
1377+ if (fields & PATH)
1378+ h->path = "", h->pathlen = 0;
1379+ if (fields & UID)
1380+ h->uid = 0;
1381+ if (fields & GID)
1382+ h->gid = 0;
1383+ if (fields & SIZE)
1384+ h->size = 0;
1385+ if (fields & MTIME) {
1386+ if (h->mtime.tv_sec > MAXTIME)
1387+ h->mtime.tv_sec = MAXTIME;
1388+ h->mtime.tv_nsec = 0;
1389+ }
1390+ if (fields & ATIME) {
1391+ if (h->atime.tv_sec > MAXTIME)
1392+ h->atime.tv_sec = MAXTIME;
1393+ h->atime.tv_nsec = 0;
1394+ }
1395+ if (fields & CTIME) {
1396+ if (h->ctime.tv_sec > MAXTIME)
1397+ h->ctime.tv_sec = MAXTIME;
1398+ h->ctime.tv_nsec = 0;
1399+ }
1400+ if (fields & UNAME)
1401+ h->uname = "";
1402+ if (fields & GNAME)
1403+ h->gname = "";
1404+ if (fields & LINKPATH)
1405+ h->link = "", h->linklen = 0;
1406+ h->fields &= ~fields;
1407+ writeustar(f, h);
1408+}
1409+
1410+static void
1411+writecpio(FILE *f, struct header *h)
1412+{
1413+ static unsigned long ino;
1414+ char buf[77];
1415+ unsigned long mode;
1416+ uintmax_t size;
1417+ size_t namesize;
1418+ int len;
1419+
1420+ if (!h) {
1421+ memcpy(buf, "070707", 6);
1422+ memset(buf + 6, '0', 70);
1423+ memcpy(buf + 59, "000013", 6);
1424+ if (fwrite(buf, 1, 76, f) != 76)
1425+ fatal("write:");
1426+ if (fwrite("TRAILER!!!", 1, 11, f) != 11)
1427+ fatal("write:");
1428+ return;
1429+ }
1430+ if (vflag)
1431+ fprintf(stderr, "%s\n", h->path);
1432+ mode = h->mode;
1433+ switch (h->type) {
1434+ case DIRTYPE: mode |= S_IFDIR; break;
1435+ case FIFOTYPE: mode |= S_IFIFO; break;
1436+ case REGTYPE: mode |= S_IFREG; break;
1437+ case SYMTYPE: mode |= S_IFLNK; break;
1438+ case BLKTYPE: mode |= S_IFBLK; break;
1439+ case CHRTYPE: mode |= S_IFCHR; break;
1440+ default: fatal("unknown or unsupported header type");
1441+ }
1442+ if (h->dev > 0777777)
1443+ fatal("device is too large: %ju", (uintmax_t)h->dev);
1444+ if (++ino > 0777777)
1445+ fatal("inode is too large: %lu", ino);
1446+ if (mode > 0777777)
1447+ fatal("mode is too large: %lu", mode);
1448+ if (h->uid > MAXUGID)
1449+ fatal("uid is too large: %ju", (uintmax_t)h->uid);
1450+ if (h->gid > MAXUGID)
1451+ fatal("gid is too large: %ju", (uintmax_t)h->gid);
1452+ if (h->nlink > 0777777)
1453+ fatal("nlink is too large: %ju", (uintmax_t)h->nlink);
1454+ if (h->rdev > 0777777)
1455+ fatal("device is too large: %ju", (uintmax_t)h->rdev);
1456+ if (h->mtime.tv_sec > MAXTIME)
1457+ fatal("mtime is too large: %ju", (uintmax_t)h->mtime.tv_sec);
1458+ namesize = h->pathlen;
1459+ if (namesize > 0 && h->path[namesize - 1] == '/')
1460+ --namesize;
1461+ if (namesize > 077777777777 - 1)
1462+ fatal("path is too large: %ju", (uintmax_t)h->pathlen + 1);
1463+ size = h->type == SYMTYPE ? (uintmax_t)h->linklen : (uintmax_t)h->size;
1464+ if (size > MAXSIZE)
1465+ fatal("size is too large: %ju", h->size);
1466+ len = snprintf(buf, sizeof buf, "070707%.6lo%.6lo%.6lo%.6lo%.6lo%.6lo%.6lo%.11llo%.6lo%.11jo",
1467+ (unsigned long)h->dev, ino, mode,
1468+ (unsigned long)h->uid, (unsigned long)h->gid,
1469+ (unsigned long)h->nlink, (unsigned long)h->rdev,
1470+ (unsigned long long)h->mtime.tv_sec, (unsigned long)namesize + 1, size);
1471+ assert(len == 76);
1472+ if (fwrite(buf, 1, 76, f) != 76)
1473+ fatal("write:");
1474+ if (fwrite(h->path, 1, namesize, f) != namesize || fputc('\0', f) == EOF)
1475+ fatal("write:");
1476+ switch (h->type) {
1477+ case SYMTYPE:
1478+ if (fwrite(h->link, 1, h->linklen, f) != h->linklen)
1479+ fatal("write:");
1480+ break;
1481+ case REGTYPE:
1482+ openfile(h);
1483+ copy(&bioin, h->size, f, h->size);
1484+ closefile(h);
1485+ break;
1486+ default:
1487+ break;
1488+ }
1489+}
1490+
1491+static void
1492+filepush(struct filelist *files, const char *name, size_t pathlen, dev_t dev)
1493+{
1494+ struct file *f;
1495+ size_t namelen;
1496+
1497+ namelen = strlen(name);
1498+ f = malloc(sizeof *f + namelen + 1);
1499+ if (!f)
1500+ fatal(NULL);
1501+ memcpy(f->name, name, namelen + 1);
1502+ f->namelen = namelen;
1503+ f->pathlen = pathlen;
1504+ f->dev = dev;
1505+ f->next = files->pending;
1506+ files->pending = f;
1507+}
1508+
1509+static int
1510+readfile(struct bufio *f, struct header *h)
1511+{
1512+ /* use our own path buffer, since we use it for traversal */
1513+ static struct strbuf path;
1514+ struct stat st;
1515+ int flag;
1516+ DIR *dir;
1517+ struct dirent *d;
1518+ ssize_t ret;
1519+ dev_t dev;
1520+
1521+ (void)f;
1522+
1523+next:
1524+ flag = follow == 'L' ? 0 : AT_SYMLINK_NOFOLLOW;
1525+ if (files.pending) {
1526+ struct file *f;
1527+
1528+ f = files.pending;
1529+ files.pending = f->next;
1530+ assert(f->pathlen <= path.len);
1531+ path.len = f->pathlen;
1532+ sbufcat(&path, f->name, f->namelen, 1024);
1533+ if (follow == 'H' && f->pathlen > 0)
1534+ flag &= ~AT_SYMLINK_NOFOLLOW;
1535+ dev = f->dev;
1536+ free(f);
1537+ } else {
1538+ if (!files.input)
1539+ return 0;
1540+ ret = getline(&path.str, &path.cap, files.input);
1541+ if (ret < 0) {
1542+ if (ferror(files.input))
1543+ fatal("getline:");
1544+ return 0;
1545+ }
1546+ if (ret > 0 && path.str[ret - 1] == '\n')
1547+ path.str[--ret] = '\0';
1548+ path.len = ret;
1549+ dev = 0;
1550+ }
1551+
1552+ if (fstatat(AT_FDCWD, path.str, &st, flag) != 0)
1553+ fatal("stat %s:", path.str);
1554+ if (Xflag && dev && st.st_dev != dev)
1555+ goto next;
1556+ if (S_ISDIR(st.st_mode) && path.str[path.len - 1] != '/')
1557+ sbufcat(&path, "/", 1, 1024);
1558+ h->fields = PATH | UID | GID | ATIME | MTIME | CTIME;
1559+ h->path = path.str;
1560+ h->pathlen = path.len;
1561+ h->dev = st.st_dev;
1562+ h->ino = st.st_ino;
1563+ h->mode = st.st_mode & ~S_IFMT;
1564+ h->uid = st.st_uid;
1565+ h->gid = st.st_gid;
1566+ h->nlink = st.st_nlink;
1567+ h->rdev = 0;
1568+ h->size = 0;
1569+ h->atime = st.st_atim;
1570+ h->mtime = st.st_mtim;
1571+ h->ctime = st.st_ctim;
1572+ h->uname = uidtouname(st.st_uid, "");
1573+ h->gname = gidtogname(st.st_gid, "");
1574+ h->link = "";
1575+ h->linklen = 0;
1576+ h->slash = NULL;
1577+ h->data = NULL;
1578+ h->file = h->path;
1579+ h->fileatime = st.st_atim;
1580+ h->flag = flag;
1581+ switch (st.st_mode & S_IFMT) {
1582+ case S_IFREG:
1583+ h->type = REGTYPE;
1584+ h->size = st.st_size;
1585+ break;
1586+ case S_IFLNK:
1587+ h->type = SYMTYPE;
1588+ h->linkbuf.len = 0;
1589+ sbufalloc(&h->linkbuf, 1024, 1024);
1590+ for (;;) {
1591+ ret = readlink(h->path, h->linkbuf.str, h->linkbuf.cap - 1);
1592+ if (ret < 0)
1593+ fatal("readlink %s:", h->path);
1594+ if ((size_t)ret < h->linkbuf.cap)
1595+ break;
1596+ if (h->linkbuf.cap > (size_t)SSIZE_MAX / 2)
1597+ fatal("symlink target is too long");
1598+ sbufalloc(&h->linkbuf, h->linkbuf.cap * 2, 1024);
1599+ }
1600+ h->linkbuf.str[ret] = '\0';
1601+ h->linkbuf.len = ret;
1602+ h->link = h->linkbuf.str;
1603+ h->linklen = h->linkbuf.len;
1604+ break;
1605+ case S_IFCHR:
1606+ h->type = CHRTYPE;
1607+ h->rdev = st.st_rdev;
1608+ break;
1609+ case S_IFBLK:
1610+ h->type = BLKTYPE;
1611+ h->rdev = st.st_rdev;
1612+ break;
1613+ case S_IFDIR:
1614+ h->type = DIRTYPE;
1615+ dir = opendir(h->path);
1616+ if (!dir)
1617+ fatal("opendir %s:", h->path);
1618+ for (;;) {
1619+ errno = 0;
1620+ d = readdir(dir);
1621+ if (!d)
1622+ break;
1623+ if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0)
1624+ continue;
1625+ filepush(&files, d->d_name, path.len, st.st_dev);
1626+ }
1627+ if (errno != 0)
1628+ fatal("readdir %s:", h->path);
1629+ closedir(dir);
1630+ break;
1631+ case S_IFIFO:
1632+ h->type = FIFOTYPE;
1633+ break;
1634+ }
1635+ return 1;
1636+}
1637+
1638+static void
1639+usage(void)
1640+{
1641+ fprintf(stderr, "usage: pax\n");
1642+ exit(2);
1643+}
1644+
1645+static void
1646+print_listopt(struct header *h)
1647+{
1648+ const char *s;
1649+ size_t len;
1650+
1651+ s = opt.listopt;
1652+ while (*s) {
1653+ if (*s == '%') {
1654+ s++;
1655+ if (*s == '%') {
1656+ putchar('%');
1657+ s++;
1658+ } else {
1659+ len = 0;
1660+ while (s[len] && isalnum((unsigned char)s[len]))
1661+ len++;
1662+ if (len > 0) {
1663+ if (strncmp(s, "path", len) == 0 && len == 4)
1664+ printf("%s", h->path);
1665+ else if (strncmp(s, "size", len) == 0 && len == 4)
1666+ printf("%ju", (uintmax_t)h->size);
1667+ else if (strncmp(s, "uid", len) == 0 && len == 3)
1668+ printf("%ju", (uintmax_t)h->uid);
1669+ else if (strncmp(s, "gid", len) == 0 && len == 3)
1670+ printf("%ju", (uintmax_t)h->gid);
1671+ else if (strncmp(s, "uname", len) == 0 && len == 5)
1672+ printf("%s", h->uname);
1673+ else if (strncmp(s, "gname", len) == 0 && len == 5)
1674+ printf("%s", h->gname);
1675+ else if (strncmp(s, "mode", len) == 0 && len == 4)
1676+ printf("%04o", (unsigned int)(h->mode & 07777));
1677+ else if (strncmp(s, "mtime", len) == 0 && len == 5)
1678+ printf("%ju", (uintmax_t)h->mtime.tv_sec);
1679+ else if (strncmp(s, "atime", len) == 0 && len == 5)
1680+ printf("%ju", (uintmax_t)h->atime.tv_sec);
1681+ else if (strncmp(s, "ctime", len) == 0 && len == 5)
1682+ printf("%ju", (uintmax_t)h->ctime.tv_sec);
1683+ else if (strncmp(s, "linkpath", len) == 0 && len == 8)
1684+ printf("%s", h->link);
1685+ else {
1686+ putchar('%');
1687+ fwrite(s, 1, len, stdout);
1688+ }
1689+ s += len;
1690+ } else {
1691+ putchar('%');
1692+ }
1693+ }
1694+ } else if (*s == '\\') {
1695+ s++;
1696+ if (*s == 'n') {
1697+ putchar('\n');
1698+ s++;
1699+ } else if (*s == 't') {
1700+ putchar('\t');
1701+ s++;
1702+ } else if (*s == '\\') {
1703+ putchar('\\');
1704+ s++;
1705+ } else if (*s) {
1706+ putchar(*s);
1707+ s++;
1708+ }
1709+ } else {
1710+ putchar(*s);
1711+ s++;
1712+ }
1713+ }
1714+ putchar('\n');
1715+}
1716+
1717+static void
1718+parseopts(char *s)
1719+{
1720+ char *key, *val, *end, *d;
1721+ int ext;
1722+
1723+ for (;;) {
1724+ s += strspn(s, " \t\n\v\f\r");
1725+ if (!*s)
1726+ break;
1727+ key = s;
1728+ while (*s && *s != ',' && *s != '=')
1729+ ++s;
1730+ val = NULL;
1731+ end = NULL, ext = 0; /* silence gcc uninitialized warning */
1732+ if (*s == '=') {
1733+ ext = s > key && s[-1] == ':';
1734+ s[-ext] = '\0';
1735+ val = ++s;
1736+ for (d = s; *s && *s != ','; ++s, ++d) {
1737+ if (*s == '\\')
1738+ ++s;
1739+ if (d < s)
1740+ *d = *s;
1741+ }
1742+ end = d;
1743+ }
1744+ if (*s == ',')
1745+ *s++ = '\0';
1746+ if (strcmp(key, "linkdata") == 0) {
1747+ if (val)
1748+ fatal("option 'linkdata' must not have a value");
1749+ opt.linkdata = 1;
1750+ } else if (strcmp(key, "times") == 0) {
1751+ if (val)
1752+ fatal("option 'times' must not have a value");
1753+ opt.times = 1;
1754+ } else if (!val) {
1755+ fatal("option '%s' must have a value", key);
1756+ } else if (strcmp(key, "delete") == 0) {
1757+ const struct keyword *kw;
1758+
1759+ for (kw = keywords; kw != keywords + LEN(keywords); ++kw) {
1760+ switch (fnmatch(val, kw->name, 0)) {
1761+ case 0: opt.delete |= kw->field; break;
1762+ case FNM_NOMATCH: break;
1763+ default: fatal("fnmatch error");
1764+ }
1765+ }
1766+ } else if (strcmp(key, "exthdr.name") == 0) {
1767+ opt.exthdrname = val;
1768+ } else if (strcmp(key, "globexthdr.name") == 0) {
1769+ opt.globexthdrname = val;
1770+ } else if (strcmp(key, "invalid") == 0) {
1771+ if (strcmp(val, "bypass") != 0 && strcmp(val, "rename") != 0 &&
1772+ strcmp(val, "UTF-8") != 0 && strcmp(val, "write") != 0) {
1773+ fatal("invalid action '%s' for option 'invalid'", val);
1774+ }
1775+ opt.invalid = val;
1776+ } else if (strcmp(key, "listopt") == 0) {
1777+ opt.listopt = val;
1778+ } else {
1779+ extkeyval(ext ? &exthdr : &globexthdr, key, val, end - val);
1780+ }
1781+ }
1782+}
1783+
1784+static void
1785+listhdr(FILE *f, struct header *h)
1786+{
1787+ char mode[11], time[13], info[23];
1788+ char unamebuf[(sizeof(uid_t) * CHAR_BIT + 2) / 3 + 1];
1789+ char gnamebuf[(sizeof(gid_t) * CHAR_BIT + 2) / 3 + 1];
1790+ const char *uname, *gname, *timefmt;
1791+ struct tm *tm;
1792+
1793+ if (!h)
1794+ return;
1795+ (void)f;
1796+ if (opt.listopt) {
1797+ print_listopt(h);
1798+ return;
1799+ }
1800+ if (!vflag) {
1801+ printf("%s\n", h->path);
1802+ return;
1803+ }
1804+ memset(mode, '-', sizeof mode - 1);
1805+ mode[10] = '\0';
1806+ switch (h->type) {
1807+ case SYMTYPE: mode[0] = 'l'; break;
1808+ case CHRTYPE: mode[0] = 'c'; break;
1809+ case BLKTYPE: mode[0] = 'b'; break;
1810+ case DIRTYPE: mode[0] = 'd'; break;
1811+ case FIFOTYPE: mode[0] = 'p'; break;
1812+ }
1813+ if (h->mode & S_IRUSR) mode[1] = 'r';
1814+ if (h->mode & S_IWUSR) mode[2] = 'w';
1815+ if (h->mode & S_IXUSR) mode[3] = 'x';
1816+ if (h->mode & S_IRGRP) mode[4] = 'r';
1817+ if (h->mode & S_IWGRP) mode[5] = 'w';
1818+ if (h->mode & S_IXGRP) mode[6] = 'x';
1819+ if (h->mode & S_IROTH) mode[7] = 'r';
1820+ if (h->mode & S_IWOTH) mode[8] = 'w';
1821+ if (h->mode & S_IXOTH) mode[9] = 'x';
1822+ if (h->mode & S_ISUID) mode[3] = mode[3] == 'x' ? 's' : 'S';
1823+ if (h->mode & S_ISGID) mode[3] = mode[6] == 'x' ? 's' : 'S';
1824+ if (h->mode & S_ISVTX) mode[9] = mode[9] == 'x' ? 't' : 'T';
1825+ uname = h->uname;
1826+ if (!uname[0]) {
1827+ snprintf(unamebuf, sizeof unamebuf, "%ju", (uintmax_t)h->uid);
1828+ uname = unamebuf;
1829+ }
1830+ gname = h->gname;
1831+ if (!gname[0]) {
1832+ snprintf(gnamebuf, sizeof gnamebuf, "%ju", (uintmax_t)h->gid);
1833+ gname = gnamebuf;
1834+ }
1835+ timefmt = h->mtime.tv_sec + 15780000 < curtime || h->mtime.tv_sec > curtime
1836+ ? "%b %e %Y" : "%b %e %H:%M";
1837+ tm = localtime(&h->mtime.tv_sec);
1838+ if (!tm)
1839+ fatal("localtime:");
1840+ strftime(time, sizeof time, timefmt, tm);
1841+ if (h->type == CHRTYPE || h->type == BLKTYPE)
1842+ snprintf(info, sizeof info, "%u, %u", major(h->rdev), minor(h->rdev));
1843+ else
1844+ snprintf(info, sizeof info, "%ju", (uintmax_t)h->size);
1845+ printf("%s %2ju %-8s %-8s %9s %s %s", mode, (uintmax_t)h->nlink, uname, gname, info, time, h->path);
1846+ switch (h->type) {
1847+ case LNKTYPE: printf(" == %s", h->link); break;
1848+ case SYMTYPE: printf(" -> %s", h->link); break;
1849+ }
1850+ putchar('\n');
1851+}
1852+
1853+static void
1854+mkdirp(int fd, char *name, size_t len)
1855+{
1856+ char *p;
1857+
1858+ if (len == 0)
1859+ return;
1860+ for (p = name + 1; p < name + len - 1; ++p) {
1861+ if (*p != '/')
1862+ continue;
1863+ *p = 0;
1864+ if (mkdirat(fd, name, 0777) != 0 && errno != EEXIST)
1865+ fatal("mkdir %s:", name);
1866+ *p = '/';
1867+ }
1868+}
1869+
1870+static void
1871+writefile(FILE *unused, struct header *h)
1872+{
1873+ FILE *f;
1874+ int fd, retry, flags;
1875+ struct stat st;
1876+ mode_t mode;
1877+
1878+ (void)unused;
1879+ if (!h)
1880+ return;
1881+ if (uflag && fstatat(destfd, h->path, &st, 0) == 0) {
1882+ if (h->mtime.tv_sec < st.st_mtime || (h->mtime.tv_sec == st.st_mtime
1883+ && h->mtime.tv_nsec < st.st_mtim.tv_nsec))
1884+ return;
1885+ }
1886+ if (vflag)
1887+ fprintf(stderr, "%s\n", h->path);
1888+ if (lflag && h->file && h->type != DIRTYPE) {
1889+ if (linkat(AT_FDCWD, h->file, destfd, h->path, h->flag) == 0)
1890+ return;
1891+ }
1892+ retry = 1;
1893+ if (0) {
1894+ retry:
1895+ retry = 0;
1896+ mkdirp(destfd, h->path, h->pathlen);
1897+ }
1898+ mode = h->mode & ~(S_ISUID | S_ISGID);
1899+ switch (h->type) {
1900+ case REGTYPE:
1901+ flags = O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC;
1902+ if (kflag)
1903+ flags |= O_EXCL;
1904+ fd = openat(destfd, h->path, flags, mode);
1905+ if (fd < 0) {
1906+ if (retry && errno == ENOENT)
1907+ goto retry;
1908+ fatal("open %s%s:", dest, h->path);
1909+ }
1910+ f = fdopen(fd, "w");
1911+ if (!f)
1912+ fatal("open %s:", h->path);
1913+ openfile(h);
1914+ copy(&bioin, h->size, f, h->size);
1915+ closefile(h);
1916+ fclose(f);
1917+ break;
1918+ case LNKTYPE:
1919+ if (linkat(destfd, h->link, destfd, h->path, 0) != 0) {
1920+ if (retry && errno == ENOENT)
1921+ goto retry;
1922+ fatal("link %s%s:", dest, h->path);
1923+ }
1924+ break;
1925+ case SYMTYPE:
1926+ if (symlinkat(h->link, destfd, h->path) != 0) {
1927+ if (retry && errno == ENOENT)
1928+ goto retry;
1929+ fatal("symlink %s%s:", dest, h->path);
1930+ }
1931+ break;
1932+ case CHRTYPE:
1933+ case BLKTYPE:
1934+ mode |= h->type == CHRTYPE ? S_IFCHR : S_IFBLK;
1935+ if (mknodat(destfd, h->path, mode, h->rdev) != 0) {
1936+ if (retry && errno == ENOENT)
1937+ goto retry;
1938+ fatal("mknod %s%s:", dest, h->path);
1939+ }
1940+ break;
1941+ case DIRTYPE:
1942+ if (mkdirat(destfd, h->path, mode) != 0) {
1943+ if (retry && errno == ENOENT)
1944+ goto retry;
1945+ if (errno == EEXIST) {
1946+ if (fstatat(destfd, h->path, &st, 0) == 0 && S_ISDIR(st.st_mode))
1947+ break;
1948+ errno = EEXIST;
1949+ }
1950+ fatal("mkdir %s%s:", dest, h->path);
1951+ }
1952+ break;
1953+ case FIFOTYPE:
1954+ if (mkfifoat(destfd, h->path, mode) != 0) {
1955+ if (retry && errno == ENOENT)
1956+ goto retry;
1957+ if (errno == EEXIST) {
1958+ if (fstatat(destfd, h->path, &st, 0) == 0 && S_ISFIFO(st.st_mode))
1959+ break;
1960+ errno = EEXIST;
1961+ }
1962+ fatal("mkfifo %s%s:", dest, h->path);
1963+ }
1964+ break;
1965+ }
1966+ if (preserve & (ATIME | MTIME)) {
1967+ struct timespec ts[2];
1968+
1969+ ts[0] = preserve & ATIME ? h->atime : (struct timespec){.tv_nsec = UTIME_OMIT};
1970+ ts[1] = preserve & MTIME ? h->mtime : (struct timespec){.tv_nsec = UTIME_OMIT};
1971+ if (utimensat(destfd, h->path, ts, AT_SYMLINK_NOFOLLOW) != 0) {
1972+ fprintf(stderr, "utimens %s%s: %s\n", dest, h->path, strerror(errno));
1973+ exitstatus = 1;
1974+ }
1975+ }
1976+ if (preserve & (UID | GID)) {
1977+ uid_t uid;
1978+ gid_t gid;
1979+
1980+ uid = preserve & UID ? unametouid(h->uname, h->uid) : (uid_t)-1;
1981+ gid = preserve & GID ? gnametogid(h->gname, h->gid) : (gid_t)-1;
1982+ if (fchownat(destfd, h->path, uid, gid, 0) != 0) {
1983+ fprintf(stderr, "chown %s%s: %s\n", dest, h->path, strerror(errno));
1984+ exitstatus = 1;
1985+ } else {
1986+ /* add back setuid/setgid bits if we preserved the uid/gid */
1987+ mode = h->mode;
1988+ }
1989+ }
1990+ if (preserve & MODE && h->type != SYMTYPE) {
1991+ if (fchmodat(destfd, h->path, mode, 0) != 0) {
1992+ fprintf(stderr, "chmod %s%s: %s\n", dest, h->path, strerror(errno));
1993+ exitstatus = 1;
1994+ }
1995+ }
1996+}
1997+
1998+static int
1999+match(struct header *h)
2000+{
2001+ static struct dir {
2002+ char *path;
2003+ size_t pathlen;
2004+ } *dirs;
2005+ static size_t dirslen;
2006+ size_t i;
2007+
2008+ if (patslen == 0)
2009+ return 1;
2010+ if (!dflag) {
2011+ struct dir *d;
2012+
2013+ for (d = dirs; d < dirs + dirslen; ++d) {
2014+ if (h->pathlen >= d->pathlen && memcmp(h->path, d->path, d->pathlen) == 0)
2015+ return !cflag;
2016+ }
2017+ }
2018+ for (i = 0; i < patslen; ++i) {
2019+ if (nflag && patsused[i])
2020+ continue;
2021+ switch (fnmatch(pats[i], h->path, FNM_PATHNAME | FNM_PERIOD)) {
2022+ case 0:
2023+ patsused[i] = 1;
2024+ if (!dflag && h->type == DIRTYPE) {
2025+ struct dir *d;
2026+
2027+ if ((dirslen & (dirslen - 1)) == 0) {
2028+ dirs = reallocarray(dirs, dirslen ? dirslen * 2 : 32, sizeof *dirs);
2029+ if (!dirs)
2030+ fatal(NULL);
2031+ }
2032+ d = &dirs[dirslen++];
2033+ d->pathlen = h->pathlen;
2034+ d->path = malloc(d->pathlen + 1);
2035+ if (!d->path)
2036+ fatal(NULL);
2037+ memcpy(d->path, h->path, h->pathlen);
2038+ /* add trailing slash if not already present */
2039+ if (d->path[d->pathlen - 1] != '/')
2040+ d->path[d->pathlen++] = '/';
2041+ }
2042+ return !cflag;
2043+ case FNM_NOMATCH:
2044+ break;
2045+ default:
2046+ fatal("fnmatch error");
2047+ }
2048+ }
2049+ return cflag;
2050+}
2051+
2052+static void
2053+parsereplstr(char *str)
2054+{
2055+ static struct replstr **end = &replstr;
2056+ struct replstr *r;
2057+ char *old, *new, delim;
2058+ int err;
2059+
2060+ delim = str[0];
2061+ if (!delim)
2062+ usage();
2063+ old = str + 1;
2064+ str = strchr(old, delim);
2065+ if (!str)
2066+ usage();
2067+ *str = 0;
2068+ new = str + 1;
2069+ str = strchr(new, delim);
2070+ if (!str)
2071+ usage();
2072+ *str = 0;
2073+
2074+ r = malloc(sizeof *r);
2075+ if (!r)
2076+ fatal(NULL);
2077+ r->next = NULL;
2078+ r->global = 0;
2079+ r->print = 0;
2080+ r->symlink = 0;
2081+ for (;;) {
2082+ switch (*++str) {
2083+ case 'g': r->global = 1; break;
2084+ case 'p': r->print = 1; break;
2085+ case 's': r->symlink = 0; break;
2086+ case 'S': r->symlink = 1; break;
2087+ case 0: goto done;
2088+ }
2089+ }
2090+done:
2091+ err = regcomp(&r->old, old, REG_NEWLINE);
2092+ if (err != 0) {
2093+ char errbuf[256];
2094+
2095+ regerror(err, &r->old, errbuf, sizeof errbuf);
2096+ fatal("invalid regular expression: %s", errbuf);
2097+ }
2098+ r->new = new;
2099+ *end = r;
2100+ end = &r->next;
2101+}
2102+
2103+static int
2104+applyrepl(struct replstr *r, struct strbuf *b, const char *old, size_t oldlen)
2105+{
2106+ regmatch_t match[10];
2107+ size_t i, n, l;
2108+ const char *s, *p;
2109+ char *d;
2110+ int flags;
2111+
2112+ flags = 0;
2113+ b->len = 0;
2114+ p = old;
2115+ while (regexec(&r->old, p, LEN(match), match, flags) == 0) {
2116+ n = match[0].rm_so;
2117+ for (s = r->new; *s; ++s) {
2118+ switch (*s) {
2119+ case '&': i = 0; break;
2120+ case '\\': i = *++s - '0'; break;
2121+ default: i = -1; break;
2122+ }
2123+ n += i <= 9 ? match[i].rm_eo - match[i].rm_so : 1;
2124+ }
2125+ d = sbufalloc(b, n + 1, 1024);
2126+ b->len += n;
2127+ memcpy(d, p, match[0].rm_so);
2128+ d += match[0].rm_so;
2129+ for (s = r->new; *s; ++s) {
2130+ switch (*s) {
2131+ case '&': i = 0; break;
2132+ case '\\': i = *++s - '0'; break;
2133+ default: i = -1; break;
2134+ }
2135+ if (i <= 9) {
2136+ l = match[i].rm_eo - match[i].rm_so;
2137+ memcpy(d, p + match[i].rm_so, l);
2138+ d += l;
2139+ } else {
2140+ *d++ = *s;
2141+ }
2142+ }
2143+ flags |= REG_NOTBOL;
2144+ p += match[0].rm_eo;
2145+ if (!r->global)
2146+ break;
2147+ }
2148+ if (flags == 0)
2149+ return 0;
2150+ sbufcat(b, p, oldlen - (p - old), 1024);
2151+ if (r->print)
2152+ fprintf(stderr, "%s >> %s\n", old, b->str);
2153+ return 1;
2154+}
2155+
2156+static void
2157+replace(struct header *h)
2158+{
2159+ static struct strbuf path, link;
2160+ struct replstr *r;
2161+
2162+ for (r = replstr; r; r = r->next) {
2163+ if (applyrepl(r, &path, h->path, h->pathlen)) {
2164+ h->path = path.str;
2165+ h->pathlen = path.len;
2166+ break;
2167+ }
2168+ }
2169+ if (h->type != LNKTYPE && h->type != SYMTYPE)
2170+ return;
2171+ for (r = replstr; r; r = r->next) {
2172+ if (h->type == SYMTYPE && !r->symlink)
2173+ continue;
2174+ if (applyrepl(r, &link, h->link, h->linklen)) {
2175+ h->link = link.str;
2176+ h->linklen = link.len;
2177+ break;
2178+ }
2179+ }
2180+}
2181+
2182+static int
2183+is_invalid_name(const char *name)
2184+{
2185+ unsigned char c;
2186+
2187+ if (!name || !*name)
2188+ return 1;
2189+ for (; *name; name++) {
2190+ c = (unsigned char)*name;
2191+ if (c < 32 || c >= 127)
2192+ return 1;
2193+ }
2194+ return 0;
2195+}
2196+
2197+static void
2198+interactiverename(struct header *h)
2199+{
2200+ static FILE *ttyfp = NULL;
2201+ static char ttybuf[1024];
2202+ char *res;
2203+
2204+ if (!iflag)
2205+ return;
2206+
2207+ if (!ttyfp) {
2208+ ttyfp = fopen("/dev/tty", "r+");
2209+ if (!ttyfp)
2210+ ttyfp = stdin;
2211+ }
2212+
2213+ fprintf(stderr, "rename %s? ", h->path);
2214+ fflush(stderr);
2215+
2216+ if (ttyfp == stdin)
2217+ res = fgets(ttybuf, sizeof(ttybuf), stdin);
2218+ else
2219+ res = fgets(ttybuf, sizeof(ttybuf), ttyfp);
2220+
2221+ if (!res) {
2222+ h->path = "";
2223+ h->pathlen = 0;
2224+ return;
2225+ }
2226+
2227+ ttybuf[strcspn(ttybuf, "\n")] = '\0';
2228+
2229+ if (ttybuf[0] == '\0')
2230+ return;
2231+
2232+ if (strcmp(ttybuf, ".") == 0) {
2233+ h->path = "";
2234+ h->pathlen = 0;
2235+ return;
2236+ }
2237+
2238+ h->pathbuf.len = 0;
2239+ sbufcat(&h->pathbuf, ttybuf, strlen(ttybuf), 1024);
2240+ h->path = h->pathbuf.str;
2241+ h->pathlen = h->pathbuf.len;
2242+}
2243+
2244+static void
2245+checkinvalid(struct header *h)
2246+{
2247+ int saved_iflag;
2248+
2249+ if (!opt.invalid)
2250+ return;
2251+ if (is_invalid_name(h->path)) {
2252+ if (strcmp(opt.invalid, "bypass") == 0) {
2253+ h->path = "";
2254+ h->pathlen = 0;
2255+ } else if (strcmp(opt.invalid, "rename") == 0) {
2256+ saved_iflag = iflag;
2257+ iflag = 1;
2258+ interactiverename(h);
2259+ iflag = saved_iflag;
2260+ }
2261+ }
2262+}
2263+
2264+static off_t
2265+locate_tar_end(const char *filename)
2266+{
2267+ char buf[512];
2268+ off_t offset = 0;
2269+ int zero_blocks = 0;
2270+ int fd, i, is_zero;
2271+ ssize_t r;
2272+
2273+ fd = open(filename, O_RDONLY);
2274+ if (fd < 0) {
2275+ if (errno == ENOENT)
2276+ return 0;
2277+ fatal("open %s for append check:", filename);
2278+ }
2279+
2280+ while ((r = read(fd, buf, 512)) == 512) {
2281+ is_zero = 1;
2282+ for (i = 0; i < 512; i++) {
2283+ if (buf[i] != 0) {
2284+ is_zero = 0;
2285+ break;
2286+ }
2287+ }
2288+ if (is_zero) {
2289+ zero_blocks++;
2290+ if (zero_blocks == 2) {
2291+ close(fd);
2292+ return offset;
2293+ }
2294+ } else {
2295+ zero_blocks = 0;
2296+ }
2297+ offset += 512;
2298+ }
2299+
2300+ close(fd);
2301+ return offset;
2302+}
2303+
2304+static void
2305+handle_append(const char *filename, const char *algo, const char *format)
2306+{
2307+ off_t offset;
2308+ int fd;
2309+
2310+ if (!aflag)
2311+ return;
2312+ if (algo)
2313+ fatal("cannot append to compressed archives");
2314+ if (strcmp(format, "ustar") != 0 && strcmp(format, "pax") != 0)
2315+ fatal("append is only supported for ustar and pax formats");
2316+
2317+ if (filename) {
2318+ offset = locate_tar_end(filename);
2319+ if (offset > 0) {
2320+ fd = open(filename, O_RDWR);
2321+ if (fd >= 0) {
2322+ if (ftruncate(fd, offset) != 0)
2323+ fatal("ftruncate %s for append:", filename);
2324+ close(fd);
2325+ }
2326+ }
2327+ }
2328+}
2329+
2330+int
2331+main(int argc, char *argv[])
2332+{
2333+ const char *name = NULL, *arg, *format = "pax";
2334+ const char *algo = NULL;
2335+ enum mode mode = LIST;
2336+ struct header hdr;
2337+ readfn *readhdr = NULL;
2338+ writefn *writehdr = listhdr;
2339+ FILE *out = NULL;
2340+ pid_t pid = -1;
2341+ int i;
2342+ size_t l;
2343+
2344+ ARGBEGIN {
2345+ case 'a':
2346+ aflag = 1;
2347+ break;
2348+ case 'b':
2349+ EARGF(usage());
2350+ break;
2351+ case 'c':
2352+ cflag = 1;
2353+ break;
2354+ case 'd':
2355+ dflag = 1;
2356+ break;
2357+ case 'f':
2358+ name = EARGF(usage());
2359+ break;
2360+ case 'H':
2361+ follow = 'H';
2362+ break;
2363+ case 'i':
2364+ iflag = 1;
2365+ break;
2366+ case 'j':
2367+ algo = "bzip2";
2368+ break;
2369+ case 'J':
2370+ algo = "xz";
2371+ break;
2372+ case 'k':
2373+ kflag = 1;
2374+ break;
2375+ case 'l':
2376+ lflag = 1;
2377+ break;
2378+ case 'L':
2379+ follow = 'L';
2380+ break;
2381+ case 'n':
2382+ nflag = 1;
2383+ break;
2384+ case 'o':
2385+ parseopts(EARGF(usage()));
2386+ break;
2387+ case 'p':
2388+ for (arg = EARGF(usage()); *arg; ++arg) {
2389+ switch (*arg) {
2390+ case 'a': preserve &= ~ATIME; break;
2391+ case 'e': preserve = ~0; break;
2392+ case 'm': preserve &= ~MTIME; break;
2393+ case 'o': preserve |= UID | GID; break;
2394+ case 'p': preserve |= MODE; break;
2395+ default: fatal("unknown -p option");
2396+ }
2397+ }
2398+ break;
2399+ case 'r':
2400+ mode |= READ;
2401+ break;
2402+ case 's':
2403+ parsereplstr(EARGF(usage()));
2404+ break;
2405+ case 't':
2406+ tflag = 1;
2407+ break;
2408+ case 'u':
2409+ uflag = 1;
2410+ break;
2411+ case 'v':
2412+ vflag = 1;
2413+ break;
2414+ case 'w':
2415+ mode |= WRITE;
2416+ break;
2417+ case 'x':
2418+ format = EARGF(usage());
2419+ break;
2420+ case 'X':
2421+ Xflag = 1;
2422+ break;
2423+ case 'z':
2424+ algo = "gzip";
2425+ break;
2426+ default:
2427+ usage();
2428+ } ARGEND;
2429+
2430+ curtime = time(NULL);
2431+ if (curtime == (time_t)-1)
2432+ fatal("time:");
2433+ exthdr.fields &= ~opt.delete;
2434+ exthdr.delete = exthdr.fields;
2435+ globexthdr.fields &= ~opt.delete;
2436+ globexthdr.delete = globexthdr.fields;
2437+ if ((exthdr.fields | globexthdr.fields | opt.delete) & SIZE)
2438+ fatal("field 'size' cannot be overridden or deleted");
2439+
2440+ switch (mode) {
2441+ case READ:
2442+ writehdr = writefile;
2443+ /* fallthrough */
2444+ case LIST:
2445+ if (name && strcmp(name, "-") != 0) {
2446+ bioin.fd = open(name, O_RDONLY);
2447+ if (bioin.fd < 0)
2448+ fatal("open %s:", name);
2449+ }
2450+ readhdr = detectformat(&bioin, algo, &pid);
2451+ if (!readhdr)
2452+ fatal("could not detect archive format");
2453+ if (argc) {
2454+ pats = argv;
2455+ patslen = argc;
2456+ patsused = calloc(1, argc);
2457+ if (!patsused)
2458+ fatal(NULL);
2459+ }
2460+ break;
2461+ case WRITE:
2462+ if (name && strcmp(name, "-") == 0)
2463+ name = NULL;
2464+ handle_append(name, algo, format);
2465+ out = compress(algo, name, &pid);
2466+ if (strcmp(format, "ustar") == 0) {
2467+ writehdr = writeustar;
2468+ } else if (strcmp(format, "pax") == 0) {
2469+ writehdr = writepax;
2470+ if (globexthdr.fields)
2471+ writeexthdr(stdout, 'g', &globexthdr);
2472+ } else if (strcmp(format, "cpio") == 0) {
2473+ writehdr = writecpio;
2474+ } else {
2475+ fatal("unsupported archive format '%s'", format);
2476+ }
2477+ break;
2478+ case COPY:
2479+ if (name || argc == 0)
2480+ usage();
2481+ l = strlen(argv[--argc]);
2482+ dest = malloc(l + 2);
2483+ if (!dest)
2484+ fatal(NULL);
2485+ memcpy(dest, argv[argc], l);
2486+ memcpy(dest + l, "/", 2);
2487+ destfd = open(dest, O_SEARCH|O_DIRECTORY);
2488+ if (destfd < 0)
2489+ fatal("open %s:", dest);
2490+ writehdr = writefile;
2491+ break;
2492+ }
2493+ if (mode & WRITE) {
2494+ readhdr = readfile;
2495+ bioin.fd = -1;
2496+ for (i = 0; i < argc; ++i)
2497+ filepush(&files, argv[i], 0, 0);
2498+ if (argc == 0)
2499+ files.input = stdin;
2500+ }
2501+
2502+ memset(&hdr, 0, sizeof hdr);
2503+ while (readhdr(&bioin, &hdr)) {
2504+ mergehdr(&hdr, &exthdr, ~0);
2505+ mergehdr(&hdr, &globexthdr, ~exthdr.fields);
2506+ if (match(&hdr)) {
2507+ replace(&hdr);
2508+ checkinvalid(&hdr);
2509+ interactiverename(&hdr);
2510+ if (*hdr.path)
2511+ writehdr(out, &hdr);
2512+ }
2513+ }
2514+ writehdr(out, NULL);
2515+ if (out) {
2516+ if (fflush(out) != 0)
2517+ fatal("write:");
2518+ fclose(out);
2519+ }
2520+ for (i = 0; i < (int)patslen; ++i) {
2521+ if (!patsused[i])
2522+ fatal("pattern not matched: %s", pats[i]);
2523+ }
2524+
2525+ if (pid != -1) {
2526+ int st;
2527+
2528+ if (waitpid(pid, &st, 0) == -1)
2529+ fatal("waitpid:");
2530+ if (WIFEXITED(st) && WEXITSTATUS(st) != 0)
2531+ fatal("child exited with status %d", WEXITSTATUS(st));
2532+ if (WIFSIGNALED(st))
2533+ fatal("child terminated by signal %d", WTERMSIG(st));
2534+ }
2535+ return exitstatus;
2536+}
+1,
-1
1@@ -47,7 +47,7 @@ unescape_pct(char *s, char *is_pct)
2 for (q = 0, m = 3; m && is_odigit(*r); m--, r++)
3 q = q * 8 + (*r - '0');
4 is_pct[w - s] = 0;
5- *w++ = MIN(q, 255);
6+ *w++ = MIN(q, (size_t)255);
7 } else if (*r == 'x' && isxdigit(r[1])) {
8 r++;
9 for (q = 0, m = 2; m && isxdigit(*r); m--, r++)
+1,
-1
1@@ -32,7 +32,7 @@ psout(struct procstat *ps)
2 {
3 struct procstatus pstatus;
4 char cmdline[BUFSIZ], *cmd;
5- char buf[BUFSIZ];
6+ char buf[sizeof(cmdline) + sizeof(ps->comm) + 1024];
7 char ttystr[TTY_NAME_MAX], *myttystr;
8 int tty_maj, tty_min;
9 uid_t myeuid;
+1,
-2
1@@ -25,7 +25,7 @@ forbidden(char *path, struct stat *root)
2 for (s = t; s > path && s[-1] != '/'; --s)
3 ;
4 n = t - s;
5- if (n == 1 && *s == '.' || n == 2 && s[0] == '.' && s[1] == '.') {
6+ if ((n == 1 && *s == '.') || (n == 2 && s[0] == '.' && s[1] == '.')) {
7 if (!w1)
8 weprintf("\".\" and \"..\" may not be removed\n");
9 w1 = 1;
10@@ -47,7 +47,6 @@ forbidden(char *path, struct stat *root)
11 int
12 main(int argc, char *argv[])
13 {
14- char *s;
15 struct stat st;
16 struct recursor r = { .fn = rm, .maxdepth = 1, .follow = 'P' };
17
+157,
-6
1@@ -7,14 +7,18 @@
2 * POSIX says don't flush on N when out of input, but GNU and busybox do.
3 */
4
5+#include "config.h"
6+#include "utf.h"
7+#include "util.h"
8+
9 #include <ctype.h>
10 #include <errno.h>
11+#include <libgen.h>
12 #include <regex.h>
13 #include <stdlib.h>
14 #include <string.h>
15-
16-#include "utf.h"
17-#include "util.h"
18+#include <sys/stat.h>
19+#include <unistd.h>
20
21 /* Types */
22
23@@ -353,6 +357,32 @@ leprintf(char *s)
24 }
25
26 /* FIXME: write usage message */
27+#if FEATURE_SED_INPLACE
28+static int iflag = 0;
29+static char *backup_suffix = NULL;
30+
31+static int
32+create_temp_file(const char *orig_path, char **temp_path)
33+{
34+ char *dir, *dircopy, *tmpl;
35+ int fd;
36+
37+ dircopy = estrdup(orig_path);
38+ dir = dirname(dircopy);
39+ tmpl = emalloc(strlen(dir) + 16);
40+ sprintf(tmpl, "%s/sedtmpXXXXXX", dir);
41+ free(dircopy);
42+
43+ fd = mkstemp(tmpl);
44+ if (fd < 0) {
45+ free(tmpl);
46+ return -1;
47+ }
48+ *temp_path = tmpl;
49+ return fd;
50+}
51+#endif
52+
53 static void
54 usage(void)
55 {
56@@ -1463,7 +1493,9 @@ cmd_s(Cmd *c)
57 strnacat(&genbuf, p, len);
58 p += len;
59 switch (*p) {
60- default: leprintf("this shouldn't be possible");
61+ default:
62+ leprintf("this shouldn't be possible");
63+ break;
64 case '\0':
65 /* we're at the end, back up one so the ++p will put us on
66 * the null byte to break out of the loop */
67@@ -1594,6 +1626,7 @@ cmd_y(Cmd *c)
68 static void
69 cmd_colon(Cmd *c)
70 {
71+ (void)c;
72 }
73
74 static void
75@@ -1620,12 +1653,14 @@ cmd_lbrace(Cmd *c)
76 static void
77 cmd_rbrace(Cmd *c)
78 {
79+ (void)c;
80 }
81
82 /* not actually a sed function, but acts like one, put in last spot of script */
83 static void
84 cmd_last(Cmd *c)
85 {
86+ (void)c;
87 if (!gflags.n)
88 check_puts(patt.str, stdout);
89 do_writes();
90@@ -1713,6 +1748,17 @@ main(int argc, char *argv[])
91 compile(arg, 1);
92 script = 1;
93 break;
94+#if FEATURE_SED_INPLACE
95+ case 'i':
96+ iflag = 1;
97+ if (argv[0][1] != '\0') {
98+ backup_suffix = &argv[0][1];
99+ brk_ = 1;
100+ } else {
101+ backup_suffix = "";
102+ }
103+ break;
104+#endif
105 default : usage();
106 } ARGEND
107
108@@ -1729,8 +1775,113 @@ main(int argc, char *argv[])
109 pc = prog + pcap - 1;
110 pc->fninfo = &(Fninfo){ cmd_last, NULL, NULL, 0 };
111
112- files = argv;
113- run();
114+#if FEATURE_SED_INPLACE
115+ if (iflag) {
116+ char *single_file[2] = { NULL, NULL };
117+ char **orig_files = argv;
118+ int i;
119+
120+ if (!*orig_files)
121+ eprintf("no input files\n");
122+
123+ for (i = 0; orig_files[i]; i++) {
124+ char *temp_path = NULL;
125+ int temp_fd;
126+ int real_stdout;
127+ struct stat st;
128+ Cmd *c;
129+
130+ if (strcmp(orig_files[i], "-") == 0) {
131+ weprintf("cannot edit stdin in-place\n");
132+ ret = 1;
133+ continue;
134+ }
135+
136+ if (stat(orig_files[i], &st) < 0) {
137+ weprintf("stat %s:", orig_files[i]);
138+ ret = 1;
139+ continue;
140+ }
141+
142+ temp_fd = create_temp_file(orig_files[i], &temp_path);
143+ if (temp_fd < 0) {
144+ weprintf("create_temp_file:");
145+ ret = 1;
146+ continue;
147+ }
148+
149+ real_stdout = dup(1);
150+ if (real_stdout < 0) {
151+ weprintf("dup stdout:");
152+ close(temp_fd);
153+ free(temp_path);
154+ ret = 1;
155+ continue;
156+ }
157+ if (dup2(temp_fd, 1) < 0) {
158+ weprintf("dup2 stdout:");
159+ close(temp_fd);
160+ close(real_stdout);
161+ free(temp_path);
162+ ret = 1;
163+ continue;
164+ }
165+ close(temp_fd);
166+
167+ single_file[0] = orig_files[i];
168+ files = single_file;
169+
170+ /* reset state for next file */
171+ lineno = 0;
172+ gflags.halt = 0;
173+ stracpy(&hold, "");
174+ stracpy(&patt, "");
175+ writes.size = 0;
176+ for (c = prog; c->fninfo->fn != cmd_last; c++) {
177+ c->in_match = 0;
178+ }
179+
180+ run();
181+
182+ fflush(stdout);
183+ dup2(real_stdout, 1);
184+ close(real_stdout);
185+
186+ if (backup_suffix && *backup_suffix) {
187+ char *backup_path = emalloc(strlen(orig_files[i]) + strlen(backup_suffix) + 1);
188+ sprintf(backup_path, "%s%s", orig_files[i], backup_suffix);
189+ if (rename(orig_files[i], backup_path) < 0) {
190+ weprintf("rename %s to %s:", orig_files[i], backup_path);
191+ unlink(temp_path);
192+ free(backup_path);
193+ free(temp_path);
194+ ret = 1;
195+ continue;
196+ }
197+ free(backup_path);
198+ } else {
199+ unlink(orig_files[i]);
200+ }
201+
202+ if (rename(temp_path, orig_files[i]) < 0) {
203+ weprintf("rename %s to %s:", temp_path, orig_files[i]);
204+ unlink(temp_path);
205+ free(temp_path);
206+ ret = 1;
207+ continue;
208+ }
209+
210+ chmod(orig_files[i], st.st_mode);
211+ chown(orig_files[i], st.st_uid, st.st_gid);
212+
213+ free(temp_path);
214+ }
215+ } else
216+#endif
217+ {
218+ files = argv;
219+ run();
220+ }
221
222 ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
223
+272,
-0
1@@ -0,0 +1,272 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#include <stdlib.h>
37+#include "shell.h"
38+#include "output.h"
39+#include "error.h"
40+#include "memalloc.h"
41+#include "mystring.h"
42+#include "alias.h"
43+#include "options.h"
44+#include "builtins.h"
45+
46+#define ATABSIZE 39
47+
48+static struct alias *atab[ATABSIZE];
49+static int aliases;
50+
51+static void setalias(const char *, const char *);
52+static int unalias(const char *);
53+static size_t hashalias(const char *);
54+
55+static
56+void
57+setalias(const char *name, const char *val)
58+{
59+ struct alias *ap, **app;
60+
61+ unalias(name);
62+ app = &atab[hashalias(name)];
63+ INTOFF;
64+ ap = ckmalloc(sizeof (struct alias));
65+ ap->name = savestr(name);
66+ ap->val = savestr(val);
67+ ap->flag = 0;
68+ ap->next = *app;
69+ *app = ap;
70+ aliases++;
71+ INTON;
72+}
73+
74+static void
75+freealias(struct alias *ap)
76+{
77+ ckfree(ap->name);
78+ ckfree(ap->val);
79+ ckfree(ap);
80+}
81+
82+static int
83+unalias(const char *name)
84+{
85+ struct alias *ap, **app;
86+
87+ app = &atab[hashalias(name)];
88+
89+ for (ap = *app; ap; app = &(ap->next), ap = ap->next) {
90+ if (equal(name, ap->name)) {
91+ /*
92+ * if the alias is currently in use (i.e. its
93+ * buffer is being used by the input routine) we
94+ * just null out the name instead of freeing it.
95+ * We could clear it out later, but this situation
96+ * is so rare that it hardly seems worth it.
97+ */
98+ if (ap->flag & ALIASINUSE)
99+ *ap->name = '\0';
100+ else {
101+ INTOFF;
102+ *app = ap->next;
103+ freealias(ap);
104+ INTON;
105+ }
106+ aliases--;
107+ return (0);
108+ }
109+ }
110+
111+ return (1);
112+}
113+
114+static void
115+rmaliases(void)
116+{
117+ struct alias *ap, **app;
118+ int i;
119+
120+ INTOFF;
121+ for (i = 0; i < ATABSIZE; i++) {
122+ app = &atab[i];
123+ while (*app) {
124+ ap = *app;
125+ if (ap->flag & ALIASINUSE) {
126+ *ap->name = '\0';
127+ app = &(*app)->next;
128+ } else {
129+ *app = ap->next;
130+ freealias(ap);
131+ }
132+ }
133+ }
134+ aliases = 0;
135+ INTON;
136+}
137+
138+struct alias *
139+lookupalias(const char *name, int check)
140+{
141+ struct alias *ap;
142+
143+ if (aliases == 0)
144+ return (NULL);
145+ for (ap = atab[hashalias(name)]; ap; ap = ap->next) {
146+ if (equal(name, ap->name)) {
147+ if (check && (ap->flag & ALIASINUSE))
148+ return (NULL);
149+ return (ap);
150+ }
151+ }
152+
153+ return (NULL);
154+}
155+
156+static int
157+comparealiases(const void *p1, const void *p2)
158+{
159+ const struct alias *const *a1 = p1;
160+ const struct alias *const *a2 = p2;
161+
162+ return strcmp((*a1)->name, (*a2)->name);
163+}
164+
165+static void
166+printalias(const struct alias *a)
167+{
168+ out1fmt("%s=", a->name);
169+ out1qstr(a->val);
170+ out1c('\n');
171+}
172+
173+static void
174+printaliases(void)
175+{
176+ int i, j;
177+ struct alias **sorted, *ap;
178+
179+ INTOFF;
180+ sorted = ckmalloc(aliases * sizeof(*sorted));
181+ j = 0;
182+ for (i = 0; i < ATABSIZE; i++)
183+ for (ap = atab[i]; ap; ap = ap->next)
184+ if (*ap->name != '\0')
185+ sorted[j++] = ap;
186+ qsort(sorted, aliases, sizeof(*sorted), comparealiases);
187+ for (i = 0; i < aliases; i++) {
188+ printalias(sorted[i]);
189+ if (int_pending())
190+ break;
191+ }
192+ ckfree(sorted);
193+ INTON;
194+}
195+
196+int
197+aliascmd(int argc __unused, char **argv __unused)
198+{
199+ char *n, *v;
200+ int ret = 0;
201+ struct alias *ap;
202+
203+ nextopt("");
204+
205+ if (*argptr == NULL) {
206+ printaliases();
207+ return (0);
208+ }
209+ while ((n = *argptr++) != NULL) {
210+ if (n[0] == '\0') {
211+ warning("'': not found");
212+ ret = 1;
213+ continue;
214+ }
215+ if ((v = strchr(n+1, '=')) == NULL) /* n+1: funny ksh stuff */
216+ if ((ap = lookupalias(n, 0)) == NULL) {
217+ warning("%s: not found", n);
218+ ret = 1;
219+ } else
220+ printalias(ap);
221+ else {
222+ *v++ = '\0';
223+ setalias(n, v);
224+ }
225+ }
226+
227+ return (ret);
228+}
229+
230+int
231+unaliascmd(int argc __unused, char **argv __unused)
232+{
233+ int i;
234+
235+ while ((i = nextopt("a")) != '\0') {
236+ if (i == 'a') {
237+ rmaliases();
238+ return (0);
239+ }
240+ }
241+ for (i = 0; *argptr; argptr++)
242+ i |= unalias(*argptr);
243+
244+ return (i);
245+}
246+
247+static size_t
248+hashalias(const char *p)
249+{
250+ unsigned int hashval;
251+
252+ hashval = (unsigned char)*p << 4;
253+ while (*p)
254+ hashval+= *p++;
255+ return (hashval % ATABSIZE);
256+}
257+
258+const struct alias *
259+iteralias(const struct alias *index)
260+{
261+ size_t i = 0;
262+
263+ if (index != NULL) {
264+ if (index->next != NULL)
265+ return (index->next);
266+ i = hashalias(index->name) + 1;
267+ }
268+ for (; i < ATABSIZE; i++)
269+ if (atab[i] != NULL)
270+ return (atab[i]);
271+
272+ return (NULL);
273+}
+45,
-0
1@@ -0,0 +1,45 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#define ALIASINUSE 1
37+
38+struct alias {
39+ struct alias *next;
40+ char *name;
41+ char *val;
42+ int flag;
43+};
44+
45+struct alias *lookupalias(const char *, int);
46+const struct alias *iteralias(const struct alias *);
+36,
-0
1@@ -0,0 +1,36 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1995
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * Redistribution and use in source and binary forms, with or without
9+ * modification, are permitted provided that the following conditions
10+ * are met:
11+ * 1. Redistributions of source code must retain the above copyright
12+ * notice, this list of conditions and the following disclaimer.
13+ * 2. Redistributions in binary form must reproduce the above copyright
14+ * notice, this list of conditions and the following disclaimer in the
15+ * documentation and/or other materials provided with the distribution.
16+ * 3. Neither the name of the University nor the names of its contributors
17+ * may be used to endorse or promote products derived from this software
18+ * without specific prior written permission.
19+ *
20+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30+ * SUCH DAMAGE.
31+ */
32+
33+#include "shell.h"
34+
35+#define DIGITS(var) (3 + (2 + CHAR_BIT * sizeof((var))) / 3)
36+
37+arith_t arith(const char *);
+387,
-0
1@@ -0,0 +1,387 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1993
6+ * The Regents of the University of California. All rights reserved.
7+ * Copyright (c) 2007
8+ * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved.
9+ *
10+ * This code is derived from software contributed to Berkeley by
11+ * Kenneth Almquist.
12+ *
13+ * Redistribution and use in source and binary forms, with or without
14+ * modification, are permitted provided that the following conditions
15+ * are met:
16+ * 1. Redistributions of source code must retain the above copyright
17+ * notice, this list of conditions and the following disclaimer.
18+ * 2. Redistributions in binary form must reproduce the above copyright
19+ * notice, this list of conditions and the following disclaimer in the
20+ * documentation and/or other materials provided with the distribution.
21+ * 3. Neither the name of the University nor the names of its contributors
22+ * may be used to endorse or promote products derived from this software
23+ * without specific prior written permission.
24+ *
25+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35+ * SUCH DAMAGE.
36+ */
37+
38+#if defined(__has_include)
39+#if __has_include(<sys/cdefs.h>)
40+#include <sys/cdefs.h>
41+#endif
42+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
43+#include <sys/cdefs.h>
44+#endif
45+#include <limits.h>
46+#include <errno.h>
47+#include <inttypes.h>
48+#include <stdlib.h>
49+#include <stdio.h>
50+#include "arith.h"
51+#include "arith_yacc.h"
52+#include "expand.h"
53+#include "shell.h"
54+#include "error.h"
55+#include "memalloc.h"
56+#include "output.h"
57+#include "options.h"
58+#include "var.h"
59+
60+#if ARITH_BOR + 11 != ARITH_BORASS || ARITH_ASS + 11 != ARITH_EQ
61+#error Arithmetic tokens are out of order.
62+#endif
63+
64+static const char *arith_startbuf;
65+
66+const char *arith_buf;
67+union yystype yylval;
68+
69+static int last_token;
70+
71+#define ARITH_PRECEDENCE(op, prec) [op - ARITH_BINOP_MIN] = prec
72+
73+static const char prec[ARITH_BINOP_MAX - ARITH_BINOP_MIN] = {
74+ ARITH_PRECEDENCE(ARITH_MUL, 0),
75+ ARITH_PRECEDENCE(ARITH_DIV, 0),
76+ ARITH_PRECEDENCE(ARITH_REM, 0),
77+ ARITH_PRECEDENCE(ARITH_ADD, 1),
78+ ARITH_PRECEDENCE(ARITH_SUB, 1),
79+ ARITH_PRECEDENCE(ARITH_LSHIFT, 2),
80+ ARITH_PRECEDENCE(ARITH_RSHIFT, 2),
81+ ARITH_PRECEDENCE(ARITH_LT, 3),
82+ ARITH_PRECEDENCE(ARITH_LE, 3),
83+ ARITH_PRECEDENCE(ARITH_GT, 3),
84+ ARITH_PRECEDENCE(ARITH_GE, 3),
85+ ARITH_PRECEDENCE(ARITH_EQ, 4),
86+ ARITH_PRECEDENCE(ARITH_NE, 4),
87+ ARITH_PRECEDENCE(ARITH_BAND, 5),
88+ ARITH_PRECEDENCE(ARITH_BXOR, 6),
89+ ARITH_PRECEDENCE(ARITH_BOR, 7),
90+};
91+
92+#define ARITH_MAX_PREC 8
93+
94+int letcmd(int, char **);
95+
96+static __dead2 void yyerror(const char *s)
97+{
98+ error("arithmetic expression: %s: \"%s\"", s, arith_startbuf);
99+ /* NOTREACHED */
100+}
101+
102+static arith_t arith_lookupvarint(char *varname)
103+{
104+ const char *str;
105+ char *p;
106+ arith_t result;
107+
108+ str = lookupvar(varname);
109+ if (uflag && str == NULL)
110+ yyerror("variable not set");
111+ if (str == NULL || *str == '\0')
112+ str = "0";
113+ errno = 0;
114+ result = strtoarith_t(str, &p);
115+ if (errno != 0 || *p != '\0')
116+ yyerror("variable conversion error");
117+ return result;
118+}
119+
120+static inline int arith_prec(int op)
121+{
122+ return prec[op - ARITH_BINOP_MIN];
123+}
124+
125+static inline int higher_prec(int op1, int op2)
126+{
127+ return arith_prec(op1) < arith_prec(op2);
128+}
129+
130+static arith_t do_binop(int op, arith_t a, arith_t b)
131+{
132+
133+ switch (op) {
134+ default:
135+ case ARITH_REM:
136+ case ARITH_DIV:
137+ if (!b)
138+ yyerror("division by zero");
139+ if (a == ARITH_MIN && b == -1)
140+ yyerror("divide error");
141+ return op == ARITH_REM ? a % b : a / b;
142+ case ARITH_MUL:
143+ return (uintmax_t)a * (uintmax_t)b;
144+ case ARITH_ADD:
145+ return (uintmax_t)a + (uintmax_t)b;
146+ case ARITH_SUB:
147+ return (uintmax_t)a - (uintmax_t)b;
148+ case ARITH_LSHIFT:
149+ return (uintmax_t)a << (b & (sizeof(uintmax_t) * CHAR_BIT - 1));
150+ case ARITH_RSHIFT:
151+ return a >> (b & (sizeof(uintmax_t) * CHAR_BIT - 1));
152+ case ARITH_LT:
153+ return a < b;
154+ case ARITH_LE:
155+ return a <= b;
156+ case ARITH_GT:
157+ return a > b;
158+ case ARITH_GE:
159+ return a >= b;
160+ case ARITH_EQ:
161+ return a == b;
162+ case ARITH_NE:
163+ return a != b;
164+ case ARITH_BAND:
165+ return a & b;
166+ case ARITH_BXOR:
167+ return a ^ b;
168+ case ARITH_BOR:
169+ return a | b;
170+ }
171+}
172+
173+static arith_t assignment(int var, int noeval);
174+
175+static arith_t primary(int token, union yystype *val, int op, int noeval)
176+{
177+ arith_t result;
178+
179+again:
180+ switch (token) {
181+ case ARITH_LPAREN:
182+ result = assignment(op, noeval);
183+ if (last_token != ARITH_RPAREN)
184+ yyerror("expecting ')'");
185+ last_token = yylex();
186+ return result;
187+ case ARITH_NUM:
188+ last_token = op;
189+ return val->val;
190+ case ARITH_VAR:
191+ last_token = op;
192+ return noeval ? val->val : arith_lookupvarint(val->name);
193+ case ARITH_ADD:
194+ token = op;
195+ *val = yylval;
196+ op = yylex();
197+ goto again;
198+ case ARITH_SUB:
199+ *val = yylval;
200+ return -primary(op, val, yylex(), noeval);
201+ case ARITH_NOT:
202+ *val = yylval;
203+ return !primary(op, val, yylex(), noeval);
204+ case ARITH_BNOT:
205+ *val = yylval;
206+ return ~primary(op, val, yylex(), noeval);
207+ default:
208+ yyerror("expecting primary");
209+ }
210+}
211+
212+static arith_t binop2(arith_t a, int op, int precedence, int noeval)
213+{
214+ for (;;) {
215+ union yystype val;
216+ arith_t b;
217+ int op2;
218+ int token;
219+
220+ token = yylex();
221+ val = yylval;
222+
223+ b = primary(token, &val, yylex(), noeval);
224+
225+ op2 = last_token;
226+ if (op2 >= ARITH_BINOP_MIN && op2 < ARITH_BINOP_MAX &&
227+ higher_prec(op2, op)) {
228+ b = binop2(b, op2, arith_prec(op), noeval);
229+ op2 = last_token;
230+ }
231+
232+ a = noeval ? b : do_binop(op, a, b);
233+
234+ if (op2 < ARITH_BINOP_MIN || op2 >= ARITH_BINOP_MAX ||
235+ arith_prec(op2) >= precedence)
236+ return a;
237+
238+ op = op2;
239+ }
240+}
241+
242+static arith_t binop(int token, union yystype *val, int op, int noeval)
243+{
244+ arith_t a = primary(token, val, op, noeval);
245+
246+ op = last_token;
247+ if (op < ARITH_BINOP_MIN || op >= ARITH_BINOP_MAX)
248+ return a;
249+
250+ return binop2(a, op, ARITH_MAX_PREC, noeval);
251+}
252+
253+static arith_t and(int token, union yystype *val, int op, int noeval)
254+{
255+ arith_t a = binop(token, val, op, noeval);
256+ arith_t b;
257+
258+ op = last_token;
259+ if (op != ARITH_AND)
260+ return a;
261+
262+ token = yylex();
263+ *val = yylval;
264+
265+ b = and(token, val, yylex(), noeval | !a);
266+
267+ return a && b;
268+}
269+
270+static arith_t or(int token, union yystype *val, int op, int noeval)
271+{
272+ arith_t a = and(token, val, op, noeval);
273+ arith_t b;
274+
275+ op = last_token;
276+ if (op != ARITH_OR)
277+ return a;
278+
279+ token = yylex();
280+ *val = yylval;
281+
282+ b = or(token, val, yylex(), noeval | !!a);
283+
284+ return a || b;
285+}
286+
287+static arith_t cond(int token, union yystype *val, int op, int noeval)
288+{
289+ arith_t a = or(token, val, op, noeval);
290+ arith_t b;
291+ arith_t c;
292+
293+ if (last_token != ARITH_QMARK)
294+ return a;
295+
296+ b = assignment(yylex(), noeval | !a);
297+
298+ if (last_token != ARITH_COLON)
299+ yyerror("expecting ':'");
300+
301+ token = yylex();
302+ *val = yylval;
303+
304+ c = cond(token, val, yylex(), noeval | !!a);
305+
306+ return a ? b : c;
307+}
308+
309+static arith_t assignment(int var, int noeval)
310+{
311+ union yystype val = yylval;
312+ int op = yylex();
313+ arith_t result;
314+ char sresult[DIGITS(result) + 1];
315+
316+ if (var != ARITH_VAR)
317+ return cond(var, &val, op, noeval);
318+
319+ if (op != ARITH_ASS && (op < ARITH_ASS_MIN || op >= ARITH_ASS_MAX))
320+ return cond(var, &val, op, noeval);
321+
322+ result = assignment(yylex(), noeval);
323+ if (noeval)
324+ return result;
325+
326+ if (op != ARITH_ASS)
327+ result = do_binop(op - 11, arith_lookupvarint(val.name), result);
328+ snprintf(sresult, sizeof(sresult), ARITH_FORMAT_STR, result);
329+ setvar(val.name, sresult, 0);
330+ return result;
331+}
332+
333+arith_t arith(const char *s)
334+{
335+ struct stackmark smark;
336+ arith_t result;
337+
338+ setstackmark(&smark);
339+
340+ arith_buf = arith_startbuf = s;
341+
342+ result = assignment(yylex(), 0);
343+
344+ if (last_token)
345+ yyerror("expecting EOF");
346+
347+ popstackmark(&smark);
348+
349+ return result;
350+}
351+
352+/*
353+ * The exp(1) builtin.
354+ */
355+int
356+letcmd(int argc, char **argv)
357+{
358+ const char *p;
359+ char *concat;
360+ char **ap;
361+ arith_t i;
362+
363+ if (argc > 1) {
364+ p = argv[1];
365+ if (argc > 2) {
366+ /*
367+ * Concatenate arguments.
368+ */
369+ STARTSTACKSTR(concat);
370+ ap = argv + 2;
371+ for (;;) {
372+ while (*p)
373+ STPUTC(*p++, concat);
374+ if ((p = *ap++) == NULL)
375+ break;
376+ STPUTC(' ', concat);
377+ }
378+ STPUTC('\0', concat);
379+ p = grabstackstr(concat);
380+ }
381+ } else
382+ p = "";
383+
384+ i = arith(p);
385+
386+ out1fmt(ARITH_FORMAT_STR "\n", i);
387+ return !i;
388+}
+94,
-0
1@@ -0,0 +1,94 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1993
6+ * The Regents of the University of California. All rights reserved.
7+ * Copyright (c) 2007
8+ * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved.
9+ *
10+ * This code is derived from software contributed to Berkeley by
11+ * Kenneth Almquist.
12+ *
13+ * Redistribution and use in source and binary forms, with or without
14+ * modification, are permitted provided that the following conditions
15+ * are met:
16+ * 1. Redistributions of source code must retain the above copyright
17+ * notice, this list of conditions and the following disclaimer.
18+ * 2. Redistributions in binary form must reproduce the above copyright
19+ * notice, this list of conditions and the following disclaimer in the
20+ * documentation and/or other materials provided with the distribution.
21+ * 3. Neither the name of the University nor the names of its contributors
22+ * may be used to endorse or promote products derived from this software
23+ * without specific prior written permission.
24+ *
25+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35+ * SUCH DAMAGE.
36+ */
37+
38+#define ARITH_ASS 1
39+
40+#define ARITH_OR 2
41+#define ARITH_AND 3
42+#define ARITH_BAD 4
43+#define ARITH_NUM 5
44+#define ARITH_VAR 6
45+#define ARITH_NOT 7
46+
47+#define ARITH_BINOP_MIN 8
48+#define ARITH_LE 8
49+#define ARITH_GE 9
50+#define ARITH_LT 10
51+#define ARITH_GT 11
52+#define ARITH_EQ 12
53+#define ARITH_REM 13
54+#define ARITH_BAND 14
55+#define ARITH_LSHIFT 15
56+#define ARITH_RSHIFT 16
57+#define ARITH_MUL 17
58+#define ARITH_ADD 18
59+#define ARITH_BOR 19
60+#define ARITH_SUB 20
61+#define ARITH_BXOR 21
62+#define ARITH_DIV 22
63+#define ARITH_NE 23
64+#define ARITH_BINOP_MAX 24
65+
66+#define ARITH_ASS_MIN 24
67+#define ARITH_REMASS 24
68+#define ARITH_BANDASS 25
69+#define ARITH_LSHIFTASS 26
70+#define ARITH_RSHIFTASS 27
71+#define ARITH_MULASS 28
72+#define ARITH_ADDASS 29
73+#define ARITH_BORASS 30
74+#define ARITH_SUBASS 31
75+#define ARITH_BXORASS 32
76+#define ARITH_DIVASS 33
77+#define ARITH_ASS_MAX 34
78+
79+#define ARITH_LPAREN 34
80+#define ARITH_RPAREN 35
81+#define ARITH_BNOT 36
82+#define ARITH_QMARK 37
83+#define ARITH_COLON 38
84+
85+extern const char *arith_buf;
86+
87+union yystype {
88+ arith_t val;
89+ char *name;
90+};
91+
92+extern union yystype yylval;
93+
94+arith_t strtoarith_t(const char *restrict nptr, char **restrict endptr);
95+int yylex(void);
+282,
-0
1@@ -0,0 +1,282 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 2002
6+ * Herbert Xu.
7+ * Copyright (c) 1993
8+ * The Regents of the University of California. All rights reserved.
9+ *
10+ * This code is derived from software contributed to Berkeley by
11+ * Kenneth Almquist.
12+ *
13+ * Redistribution and use in source and binary forms, with or without
14+ * modification, are permitted provided that the following conditions
15+ * are met:
16+ * 1. Redistributions of source code must retain the above copyright
17+ * notice, this list of conditions and the following disclaimer.
18+ * 2. Redistributions in binary form must reproduce the above copyright
19+ * notice, this list of conditions and the following disclaimer in the
20+ * documentation and/or other materials provided with the distribution.
21+ * 3. Neither the name of the University nor the names of its contributors
22+ * may be used to endorse or promote products derived from this software
23+ * without specific prior written permission.
24+ *
25+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35+ * SUCH DAMAGE.
36+ */
37+
38+#if defined(__has_include)
39+#if __has_include(<sys/cdefs.h>)
40+#include <sys/cdefs.h>
41+#endif
42+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
43+#include <sys/cdefs.h>
44+#endif
45+#include <ctype.h>
46+#include <errno.h>
47+#include <inttypes.h>
48+#include <stdlib.h>
49+#include <string.h>
50+#include "shell.h"
51+#include "arith_yacc.h"
52+#include "expand.h"
53+#include "error.h"
54+#include "memalloc.h"
55+#include "parser.h"
56+#include "syntax.h"
57+
58+#if ARITH_BOR + 11 != ARITH_BORASS || ARITH_ASS + 11 != ARITH_EQ
59+#error Arithmetic tokens are out of order.
60+#endif
61+
62+arith_t
63+strtoarith_t(const char *restrict nptr, char **restrict endptr)
64+{
65+ arith_t val;
66+
67+ while (isspace((unsigned char)*nptr))
68+ nptr++;
69+ switch (*nptr) {
70+ case '-':
71+ return strtoimax(nptr, endptr, 0);
72+ case '0':
73+ return (arith_t)strtoumax(nptr, endptr, 0);
74+ default:
75+ val = (arith_t)strtoumax(nptr, endptr, 0);
76+ if (val >= 0)
77+ return val;
78+ else if (val == ARITH_MIN) {
79+ errno = ERANGE;
80+ return ARITH_MIN;
81+ } else {
82+ errno = ERANGE;
83+ return ARITH_MAX;
84+ }
85+ }
86+}
87+
88+int
89+yylex(void)
90+{
91+ int value;
92+ const char *buf = arith_buf;
93+ char *end;
94+ const char *p;
95+
96+ for (;;) {
97+ value = *buf;
98+ switch (value) {
99+ case ' ':
100+ case '\t':
101+ case '\n':
102+ buf++;
103+ continue;
104+ default:
105+ return ARITH_BAD;
106+ case '0':
107+ case '1':
108+ case '2':
109+ case '3':
110+ case '4':
111+ case '5':
112+ case '6':
113+ case '7':
114+ case '8':
115+ case '9':
116+ yylval.val = strtoarith_t(buf, &end);
117+ arith_buf = end;
118+ return ARITH_NUM;
119+ case 'A':
120+ case 'B':
121+ case 'C':
122+ case 'D':
123+ case 'E':
124+ case 'F':
125+ case 'G':
126+ case 'H':
127+ case 'I':
128+ case 'J':
129+ case 'K':
130+ case 'L':
131+ case 'M':
132+ case 'N':
133+ case 'O':
134+ case 'P':
135+ case 'Q':
136+ case 'R':
137+ case 'S':
138+ case 'T':
139+ case 'U':
140+ case 'V':
141+ case 'W':
142+ case 'X':
143+ case 'Y':
144+ case 'Z':
145+ case '_':
146+ case 'a':
147+ case 'b':
148+ case 'c':
149+ case 'd':
150+ case 'e':
151+ case 'f':
152+ case 'g':
153+ case 'h':
154+ case 'i':
155+ case 'j':
156+ case 'k':
157+ case 'l':
158+ case 'm':
159+ case 'n':
160+ case 'o':
161+ case 'p':
162+ case 'q':
163+ case 'r':
164+ case 's':
165+ case 't':
166+ case 'u':
167+ case 'v':
168+ case 'w':
169+ case 'x':
170+ case 'y':
171+ case 'z':
172+ p = buf;
173+ while (buf++, is_in_name(*buf))
174+ ;
175+ yylval.name = stalloc(buf - p + 1);
176+ memcpy(yylval.name, p, buf - p);
177+ yylval.name[buf - p] = '\0';
178+ value = ARITH_VAR;
179+ goto out;
180+ case '=':
181+ value += ARITH_ASS - '=';
182+checkeq:
183+ buf++;
184+checkeqcur:
185+ if (*buf != '=')
186+ goto out;
187+ value += 11;
188+ break;
189+ case '>':
190+ switch (*++buf) {
191+ case '=':
192+ value += ARITH_GE - '>';
193+ break;
194+ case '>':
195+ value += ARITH_RSHIFT - '>';
196+ goto checkeq;
197+ default:
198+ value += ARITH_GT - '>';
199+ goto out;
200+ }
201+ break;
202+ case '<':
203+ switch (*++buf) {
204+ case '=':
205+ value += ARITH_LE - '<';
206+ break;
207+ case '<':
208+ value += ARITH_LSHIFT - '<';
209+ goto checkeq;
210+ default:
211+ value += ARITH_LT - '<';
212+ goto out;
213+ }
214+ break;
215+ case '|':
216+ if (*++buf != '|') {
217+ value += ARITH_BOR - '|';
218+ goto checkeqcur;
219+ }
220+ value += ARITH_OR - '|';
221+ break;
222+ case '&':
223+ if (*++buf != '&') {
224+ value += ARITH_BAND - '&';
225+ goto checkeqcur;
226+ }
227+ value += ARITH_AND - '&';
228+ break;
229+ case '!':
230+ if (*++buf != '=') {
231+ value += ARITH_NOT - '!';
232+ goto out;
233+ }
234+ value += ARITH_NE - '!';
235+ break;
236+ case 0:
237+ goto out;
238+ case '(':
239+ value += ARITH_LPAREN - '(';
240+ break;
241+ case ')':
242+ value += ARITH_RPAREN - ')';
243+ break;
244+ case '*':
245+ value += ARITH_MUL - '*';
246+ goto checkeq;
247+ case '/':
248+ value += ARITH_DIV - '/';
249+ goto checkeq;
250+ case '%':
251+ value += ARITH_REM - '%';
252+ goto checkeq;
253+ case '+':
254+ if (buf[1] == '+')
255+ return ARITH_BAD;
256+ value += ARITH_ADD - '+';
257+ goto checkeq;
258+ case '-':
259+ if (buf[1] == '-')
260+ return ARITH_BAD;
261+ value += ARITH_SUB - '-';
262+ goto checkeq;
263+ case '~':
264+ value += ARITH_BNOT - '~';
265+ break;
266+ case '^':
267+ value += ARITH_BXOR - '^';
268+ goto checkeq;
269+ case '?':
270+ value += ARITH_QMARK - '?';
271+ break;
272+ case ':':
273+ value += ARITH_COLON - ':';
274+ break;
275+ }
276+ break;
277+ }
278+
279+ buf++;
280+out:
281+ arith_buf = buf;
282+ return value;
283+}
+78,
-0
1@@ -0,0 +1,78 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+/*
37+ * This file is included by programs which are optionally built into the
38+ * shell. If SHELL is defined, we try to map the standard UNIX library
39+ * routines to ash routines using defines.
40+ */
41+
42+#include "shell.h"
43+#include "mystring.h"
44+#ifdef SHELL
45+#include "error.h"
46+#include "output.h"
47+#include "builtins.h"
48+#define FILE struct output
49+#undef stdout
50+#define stdout out1
51+#undef stderr
52+#define stderr out2
53+#define printf out1fmt
54+#undef putc
55+#define putc(c, file) outc(c, file)
56+#undef putchar
57+#define putchar(c) out1c(c)
58+#define fprintf outfmt
59+#define fputs outstr
60+#define fwrite(ptr, size, nmemb, file) outbin(ptr, (size) * (nmemb), file)
61+#define fflush flushout
62+#define INITARGS(argv)
63+#define warnx warning
64+#define warn(fmt, ...) warning(fmt ": %s", __VA_ARGS__, strerror(errno))
65+#define errx(exitstatus, ...) error(__VA_ARGS__)
66+
67+#else
68+#undef NULL
69+#include <stdio.h>
70+#undef main
71+#define INITARGS(argv) if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else
72+#endif
73+
74+#include <unistd.h>
75+
76+pointer stalloc(int);
77+int killjob(const char *, int);
78+
79+extern char *commandname;
+91,
-0
1@@ -0,0 +1,91 @@
2+#!/bin/sh -
3+
4+#-
5+# Copyright (c) 1991, 1993
6+# The Regents of the University of California. All rights reserved.
7+#
8+# This code is derived from software contributed to Berkeley by
9+# Kenneth Almquist.
10+#
11+# Redistribution and use in source and binary forms, with or without
12+# modification, are permitted provided that the following conditions
13+# are met:
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.
22+#
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
33+# SUCH DAMAGE.
34+
35+#
36+# This file lists all the builtin commands. The first column is the name
37+# of a C routine.
38+# The -j flag specifies that this command is to be excluded from systems
39+# without job control.
40+# The -n flag specifies that this command can safely be run in the same
41+# process when it is the only command in a command substitution. Some
42+# commands have special logic defined in safe_builtin().
43+# The -s flag specifies that this is a POSIX 'special built-in' command.
44+# The rest of the line specifies the command name or names used to run the
45+# command. The entry for bltincmd, which is run when the user does not specify
46+# a command, must come first.
47+#
48+# NOTE: bltincmd must come first!
49+
50+bltincmd -n builtin
51+aliascmd alias
52+bgcmd -j bg
53+bindcmd bind #FEATURE_SH_HISTEDIT
54+breakcmd -s break -s continue
55+cdcmd cd chdir
56+commandcmd -n command
57+dotcmd -s .
58+echocmd -n echo
59+evalcmd -s eval
60+execcmd -s exec
61+exitcmd -s exit
62+letcmd let #FEATURE_SH_LET
63+exportcmd -s export -s readonly
64+#exprcmd expr
65+falsecmd -n false
66+fgcmd -j fg
67+freebsd_wordexpcmd freebsd_wordexp #FEATURE_SH_WORDEXP
68+getoptscmd getopts
69+hashcmd hash
70+histcmd fc #FEATURE_SH_HISTEDIT
71+jobidcmd -n jobid
72+jobscmd -n jobs
73+killcmd -n kill
74+localcmd local #FEATURE_SH_LOCAL
75+printfcmd -n printf
76+pwdcmd -n pwd
77+readcmd read
78+returncmd -s return
79+setcmd -s set
80+setvarcmd setvar #FEATURE_SH_SETVAR
81+shiftcmd -s shift
82+testcmd -n test [
83+timescmd -n -s times
84+trapcmd -s trap
85+truecmd -n -s : true
86+typecmd -n type
87+ulimitcmd ulimit #FEATURE_SH_ULIMIT
88+umaskcmd umask
89+unaliascmd unalias
90+unsetcmd -s unset
91+waitcmd wait
92+wordexpcmd wordexp #FEATURE_SH_WORDEXP
+423,
-0
1@@ -0,0 +1,423 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#include <sys/types.h>
37+#include <sys/stat.h>
38+#include <stdlib.h>
39+#include <string.h>
40+#include <unistd.h>
41+#include <errno.h>
42+#include <limits.h>
43+
44+/*
45+ * The cd and pwd commands.
46+ */
47+
48+#include "shell.h"
49+#include "var.h"
50+#include "nodes.h" /* for jobs.h */
51+#include "jobs.h"
52+#include "options.h"
53+#include "output.h"
54+#include "memalloc.h"
55+#include "error.h"
56+#include "exec.h"
57+#include "redir.h"
58+#include "mystring.h"
59+#include "show.h"
60+#include "cd.h"
61+#include "builtins.h"
62+
63+static int cdlogical(char *);
64+static int cdphysical(char *);
65+static int docd(char *, int, int);
66+static char *getcomponent(char **);
67+static char *findcwd(char *);
68+static void updatepwd(char *);
69+static char *getpwd(void);
70+static char *getpwd2(void);
71+
72+static char *curdir = NULL; /* current working directory */
73+
74+int
75+cdcmd(int argc __unused, char **argv __unused)
76+{
77+ const char *dest;
78+ const char *path;
79+ char *p;
80+ struct stat statb;
81+ int ch, phys, print = 0, getcwderr = 0;
82+ int rc;
83+ int errno1 = ENOENT;
84+
85+ phys = Pflag;
86+ while ((ch = nextopt("eLP")) != '\0') {
87+ switch (ch) {
88+ case 'e':
89+ getcwderr = 1;
90+ break;
91+ case 'L':
92+ phys = 0;
93+ break;
94+ case 'P':
95+ phys = 1;
96+ break;
97+ }
98+ }
99+
100+ if (*argptr != NULL && argptr[1] != NULL)
101+ error("too many arguments");
102+
103+ if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
104+ error("HOME not set");
105+ if (dest[0] == '-' && dest[1] == '\0') {
106+ dest = bltinlookup("OLDPWD", 1);
107+ if (dest == NULL)
108+ error("OLDPWD not set");
109+ print = 1;
110+ }
111+ if (dest[0] == '/' ||
112+ (dest[0] == '.' && (dest[1] == '/' || dest[1] == '\0')) ||
113+ (dest[0] == '.' && dest[1] == '.' && (dest[2] == '/' || dest[2] == '\0')) ||
114+ (path = bltinlookup("CDPATH", 1)) == NULL)
115+ path = "";
116+ while ((p = padvance(&path, NULL, dest)) != NULL) {
117+ if (stat(p, &statb) < 0) {
118+ if (errno != ENOENT)
119+ errno1 = errno;
120+ } else if (!S_ISDIR(statb.st_mode))
121+ errno1 = ENOTDIR;
122+ else {
123+ if (!print) {
124+ /*
125+ * XXX - rethink
126+ */
127+ if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
128+ print = strcmp(p + 2, dest);
129+ else
130+ print = strcmp(p, dest);
131+ }
132+ rc = docd(p, print, phys);
133+ if (rc >= 0)
134+ return getcwderr ? rc : 0;
135+ if (errno != ENOENT)
136+ errno1 = errno;
137+ }
138+ }
139+ error("%s: %s", dest, strerror(errno1));
140+ /*NOTREACHED*/
141+ return 0;
142+}
143+
144+
145+/*
146+ * Actually change the directory. In an interactive shell, print the
147+ * directory name if "print" is nonzero.
148+ */
149+static int
150+docd(char *dest, int print, int phys)
151+{
152+ int rc;
153+
154+ TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, phys));
155+
156+ /* If logical cd fails, fall back to physical. */
157+ if ((phys || (rc = cdlogical(dest)) < 0) && (rc = cdphysical(dest)) < 0)
158+ return (-1);
159+
160+ if (print && iflag && curdir) {
161+ out1fmt("%s\n", curdir);
162+ /*
163+ * Ignore write errors to preserve the invariant that the
164+ * current directory is changed iff the exit status is 0
165+ * (or 1 if -e was given and the full pathname could not be
166+ * determined).
167+ */
168+ flushout(out1);
169+ outclearerror(out1);
170+ }
171+
172+ return (rc);
173+}
174+
175+static int
176+cdlogical(char *dest)
177+{
178+ char *p;
179+ char *q;
180+ char *component;
181+ char *path;
182+ struct stat statb;
183+ int first;
184+ int badstat;
185+
186+ /*
187+ * Check each component of the path. If we find a symlink or
188+ * something we can't stat, clear curdir to force a getcwd()
189+ * next time we get the value of the current directory.
190+ */
191+ badstat = 0;
192+ path = stsavestr(dest);
193+ STARTSTACKSTR(p);
194+ if (*dest == '/') {
195+ STPUTC('/', p);
196+ path++;
197+ }
198+ first = 1;
199+ while ((q = getcomponent(&path)) != NULL) {
200+ if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
201+ continue;
202+ if (! first)
203+ STPUTC('/', p);
204+ first = 0;
205+ component = q;
206+ STPUTS(q, p);
207+ if (equal(component, ".."))
208+ continue;
209+ STACKSTRNUL(p);
210+ if (lstat(stackblock(), &statb) < 0) {
211+ badstat = 1;
212+ break;
213+ }
214+ }
215+
216+ INTOFF;
217+ if ((p = findcwd(badstat ? NULL : dest)) == NULL || chdir(p) < 0) {
218+ INTON;
219+ return (-1);
220+ }
221+ updatepwd(p);
222+ INTON;
223+ return (0);
224+}
225+
226+static int
227+cdphysical(char *dest)
228+{
229+ char *p;
230+ int rc = 0;
231+
232+ INTOFF;
233+ if (chdir(dest) < 0) {
234+ INTON;
235+ return (-1);
236+ }
237+ p = findcwd(NULL);
238+ if (p == NULL) {
239+ warning("warning: failed to get name of current directory");
240+ rc = 1;
241+ }
242+ updatepwd(p);
243+ INTON;
244+ return (rc);
245+}
246+
247+/*
248+ * Get the next component of the path name pointed to by *path.
249+ * This routine overwrites *path and the string pointed to by it.
250+ */
251+static char *
252+getcomponent(char **path)
253+{
254+ char *p;
255+ char *start;
256+
257+ if ((p = *path) == NULL)
258+ return NULL;
259+ start = *path;
260+ while (*p != '/' && *p != '\0')
261+ p++;
262+ if (*p == '\0') {
263+ *path = NULL;
264+ } else {
265+ *p++ = '\0';
266+ *path = p;
267+ }
268+ return start;
269+}
270+
271+
272+static char *
273+findcwd(char *dir)
274+{
275+ char *new;
276+ char *p;
277+ char *path;
278+
279+ /*
280+ * If our argument is NULL, we don't know the current directory
281+ * any more because we traversed a symbolic link or something
282+ * we couldn't stat().
283+ */
284+ if (dir == NULL || curdir == NULL)
285+ return getpwd2();
286+ path = stsavestr(dir);
287+ STARTSTACKSTR(new);
288+ if (*dir != '/') {
289+ STPUTS(curdir, new);
290+ if (STTOPC(new) == '/')
291+ STUNPUTC(new);
292+ }
293+ while ((p = getcomponent(&path)) != NULL) {
294+ if (equal(p, "..")) {
295+ while (new > stackblock() && (STUNPUTC(new), *new) != '/');
296+ } else if (*p != '\0' && ! equal(p, ".")) {
297+ STPUTC('/', new);
298+ STPUTS(p, new);
299+ }
300+ }
301+ if (new == stackblock())
302+ STPUTC('/', new);
303+ STACKSTRNUL(new);
304+ return stackblock();
305+}
306+
307+/*
308+ * Update curdir (the name of the current directory) in response to a
309+ * cd command. We also call hashcd to let the routines in exec.c know
310+ * that the current directory has changed.
311+ */
312+static void
313+updatepwd(char *dir)
314+{
315+ char *prevdir;
316+
317+ hashcd(); /* update command hash table */
318+
319+ setvar("PWD", dir, VEXPORT);
320+ setvar("OLDPWD", curdir, VEXPORT);
321+ prevdir = curdir;
322+ curdir = dir ? savestr(dir) : NULL;
323+ ckfree(prevdir);
324+}
325+
326+int
327+pwdcmd(int argc __unused, char **argv __unused)
328+{
329+ char *p;
330+ int ch, phys;
331+
332+ phys = Pflag;
333+ while ((ch = nextopt("LP")) != '\0') {
334+ switch (ch) {
335+ case 'L':
336+ phys = 0;
337+ break;
338+ case 'P':
339+ phys = 1;
340+ break;
341+ }
342+ }
343+
344+ if (*argptr != NULL)
345+ error("too many arguments");
346+
347+ if (!phys && getpwd()) {
348+ out1fmt("%s\n", curdir);
349+ } else {
350+ if ((p = getpwd2()) == NULL)
351+ error(".: %s", strerror(errno));
352+ out1fmt("%s\n", p);
353+ }
354+
355+ return 0;
356+}
357+
358+/*
359+ * Get the current directory and cache the result in curdir.
360+ */
361+static char *
362+getpwd(void)
363+{
364+ char *p;
365+
366+ if (curdir)
367+ return curdir;
368+
369+ p = getpwd2();
370+ if (p != NULL) {
371+ INTOFF;
372+ curdir = savestr(p);
373+ INTON;
374+ }
375+
376+ return curdir;
377+}
378+
379+#define MAXPWD 256
380+
381+/*
382+ * Return the current directory.
383+ */
384+static char *
385+getpwd2(void)
386+{
387+ char *pwd;
388+ int i;
389+
390+ for (i = MAXPWD;; i *= 2) {
391+ pwd = stalloc(i);
392+ if (getcwd(pwd, i) != NULL)
393+ return pwd;
394+ stunalloc(pwd);
395+ if (errno != ERANGE)
396+ break;
397+ }
398+
399+ return NULL;
400+}
401+
402+/*
403+ * Initialize PWD in a new shell.
404+ * If the shell is interactive, we need to warn if this fails.
405+ */
406+void
407+pwd_init(int warn)
408+{
409+ char *pwd;
410+ struct stat stdot, stpwd;
411+
412+ pwd = lookupvar("PWD");
413+ if (pwd && *pwd == '/' && stat(".", &stdot) != -1 &&
414+ stat(pwd, &stpwd) != -1 &&
415+ stdot.st_dev == stpwd.st_dev &&
416+ stdot.st_ino == stpwd.st_ino) {
417+ if (curdir)
418+ ckfree(curdir);
419+ curdir = savestr(pwd);
420+ }
421+ if (getpwd() == NULL && warn)
422+ out2fmt_flush("sh: cannot determine working directory\n");
423+ setvar("PWD", curdir, VEXPORT);
424+}
+32,
-0
1@@ -0,0 +1,32 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1995
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * Redistribution and use in source and binary forms, with or without
9+ * modification, are permitted provided that the following conditions
10+ * are met:
11+ * 1. Redistributions of source code must retain the above copyright
12+ * notice, this list of conditions and the following disclaimer.
13+ * 2. Redistributions in binary form must reproduce the above copyright
14+ * notice, this list of conditions and the following disclaimer in the
15+ * documentation and/or other materials provided with the distribution.
16+ * 3. Neither the name of the University nor the names of its contributors
17+ * may be used to endorse or promote products derived from this software
18+ * without specific prior written permission.
19+ *
20+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30+ * SUCH DAMAGE.
31+ */
32+
33+void pwd_init(int);
+104,
-0
1@@ -0,0 +1,104 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#include "bltin.h"
37+
38+/*
39+ * Echo command.
40+ */
41+
42+#define main echocmd
43+
44+/* #define eflag 1 */
45+
46+int
47+main(int argc, char *argv[])
48+{
49+ char **ap;
50+ char *p;
51+ char c;
52+ int count;
53+ int nflag = 0;
54+#ifndef eflag
55+ int eflag = 0;
56+#endif
57+
58+ ap = argv;
59+ if (argc)
60+ ap++;
61+ if ((p = *ap) != NULL) {
62+ if (equal(p, "-n")) {
63+ nflag++;
64+ ap++;
65+ } else if (equal(p, "-e")) {
66+#ifndef eflag
67+ eflag++;
68+#endif
69+ ap++;
70+ }
71+ }
72+ while ((p = *ap++) != NULL) {
73+ while ((c = *p++) != '\0') {
74+ if (c == '\\' && eflag) {
75+ switch (*p++) {
76+ case 'a': c = '\a'; break;
77+ case 'b': c = '\b'; break;
78+ case 'c': return 0; /* exit */
79+ case 'e': c = '\033'; break;
80+ case 'f': c = '\f'; break;
81+ case 'n': c = '\n'; break;
82+ case 'r': c = '\r'; break;
83+ case 't': c = '\t'; break;
84+ case 'v': c = '\v'; break;
85+ case '\\': break; /* c = '\\' */
86+ case '0':
87+ c = 0;
88+ count = 3;
89+ while (--count >= 0 && (unsigned)(*p - '0') < 8)
90+ c = (c << 3) + (*p++ - '0');
91+ break;
92+ default:
93+ p--;
94+ break;
95+ }
96+ }
97+ putchar(c);
98+ }
99+ if (*ap)
100+ putchar(' ');
101+ }
102+ if (! nflag)
103+ putchar('\n');
104+ return 0;
105+}
+196,
-0
1@@ -0,0 +1,196 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+/*
37+ * Errors and exceptions.
38+ */
39+
40+#include "shell.h"
41+#include "eval.h"
42+#include "main.h"
43+#include "options.h"
44+#include "output.h"
45+#include "error.h"
46+#include "nodes.h" /* show.h needs nodes.h */
47+#include "show.h"
48+#include "trap.h"
49+#include <signal.h>
50+#include <stdlib.h>
51+#include <unistd.h>
52+#include <errno.h>
53+
54+
55+/*
56+ * Code to handle exceptions in C.
57+ */
58+
59+struct jmploc *handler;
60+volatile sig_atomic_t exception;
61+volatile sig_atomic_t suppressint;
62+volatile sig_atomic_t intpending;
63+
64+
65+static void verrorwithstatus(int, const char *, va_list) __printf0like(2, 0) __dead2;
66+
67+/*
68+ * Called to raise an exception. Since C doesn't include exceptions, we
69+ * just do a longjmp to the exception handler. The type of exception is
70+ * stored in the global variable "exception".
71+ *
72+ * Interrupts are disabled; they should be re-enabled when the exception is
73+ * caught.
74+ */
75+
76+void
77+exraise(int e)
78+{
79+ INTOFF;
80+ if (handler == NULL)
81+ abort();
82+ exception = e;
83+ longjmp(handler->loc, 1);
84+}
85+
86+
87+/*
88+ * Called from trap.c when a SIGINT is received and not suppressed, or when
89+ * an interrupt is pending and interrupts are re-enabled using INTON.
90+ * (If the user specifies that SIGINT is to be trapped or ignored using the
91+ * trap builtin, then this routine is not called.) Suppressint is nonzero
92+ * when interrupts are held using the INTOFF macro. If SIGINTs are not
93+ * suppressed and the shell is not a root shell, then we want to be
94+ * terminated if we get here, as if we were terminated directly by a SIGINT.
95+ * Arrange for this here.
96+ */
97+
98+void
99+onint(void)
100+{
101+ sigset_t sigs;
102+
103+ intpending = 0;
104+ sigemptyset(&sigs);
105+ sigprocmask(SIG_SETMASK, &sigs, NULL);
106+
107+ /*
108+ * This doesn't seem to be needed, since main() emits a newline.
109+ */
110+#if 0
111+ if (tcgetpgrp(0) == getpid())
112+ write(STDERR_FILENO, "\n", 1);
113+#endif
114+ if (rootshell && iflag)
115+ exraise(EXINT);
116+ else {
117+ signal(SIGINT, SIG_DFL);
118+ kill(getpid(), SIGINT);
119+ _exit(128 + SIGINT);
120+ }
121+}
122+
123+
124+static void
125+vwarning(const char *msg, va_list ap)
126+{
127+ if (commandname)
128+ outfmt(out2, "%s: ", commandname);
129+ else if (arg0)
130+ outfmt(out2, "%s: ", arg0);
131+ doformat(out2, msg, ap);
132+ out2fmt_flush("\n");
133+}
134+
135+
136+void
137+warning(const char *msg, ...)
138+{
139+ va_list ap;
140+ va_start(ap, msg);
141+ vwarning(msg, ap);
142+ va_end(ap);
143+}
144+
145+
146+/*
147+ * Exverror is called to raise the error exception. If the first argument
148+ * is not NULL then error prints an error message using printf style
149+ * formatting. It then raises the error exception.
150+ */
151+static void
152+verrorwithstatus(int status, const char *msg, va_list ap)
153+{
154+ /*
155+ * An interrupt trumps an error. Certain places catch error
156+ * exceptions or transform them to a plain nonzero exit code
157+ * in child processes, and if an error exception can be handled,
158+ * an interrupt can be handled as well.
159+ *
160+ * exraise() will disable interrupts for the exception handler.
161+ */
162+ FORCEINTON;
163+
164+#ifdef DEBUG
165+ if (msg)
166+ TRACE(("verrorwithstatus(%d, \"%s\") pid=%d\n",
167+ status, msg, getpid()));
168+ else
169+ TRACE(("verrorwithstatus(%d, NULL) pid=%d\n",
170+ status, getpid()));
171+#endif
172+ if (msg)
173+ vwarning(msg, ap);
174+ flushall();
175+ exitstatus = status;
176+ exraise(EXERROR);
177+}
178+
179+
180+void
181+error(const char *msg, ...)
182+{
183+ va_list ap;
184+ va_start(ap, msg);
185+ verrorwithstatus(2, msg, ap);
186+ va_end(ap);
187+}
188+
189+
190+void
191+errorwithstatus(int status, const char *msg, ...)
192+{
193+ va_list ap;
194+ va_start(ap, msg);
195+ verrorwithstatus(status, msg, ap);
196+ va_end(ap);
197+}
+95,
-0
1@@ -0,0 +1,95 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+/*
37+ * We enclose jmp_buf in a structure so that we can declare pointers to
38+ * jump locations. The global variable handler contains the location to
39+ * jump to when an exception occurs, and the global variable exception
40+ * contains a code identifying the exception. To implement nested
41+ * exception handlers, the user should save the value of handler on entry
42+ * to an inner scope, set handler to point to a jmploc structure for the
43+ * inner scope, and restore handler on exit from the scope.
44+ */
45+
46+#include <setjmp.h>
47+#include <signal.h>
48+
49+struct jmploc {
50+ jmp_buf loc;
51+};
52+
53+extern struct jmploc *handler;
54+extern volatile sig_atomic_t exception;
55+
56+/* exceptions */
57+#define EXINT 0 /* SIGINT received */
58+#define EXERROR 1 /* a generic error with exitstatus */
59+#define EXEXIT 2 /* call exitshell(exitstatus) */
60+
61+
62+/*
63+ * These macros allow the user to suspend the handling of interrupt signals
64+ * over a period of time. This is similar to SIGHOLD to or sigblock, but
65+ * much more efficient and portable. (But hacking the kernel is so much
66+ * more fun than worrying about efficiency and portability. :-))
67+ */
68+
69+extern volatile sig_atomic_t suppressint;
70+extern volatile sig_atomic_t intpending;
71+
72+#define INTOFF suppressint++
73+#define INTON { if (--suppressint == 0 && intpending) onint(); }
74+#define is_int_on() suppressint
75+#define SETINTON(s) do { suppressint = (s); if (suppressint == 0 && intpending) onint(); } while (0)
76+#define FORCEINTON {suppressint = 0; if (intpending) onint();}
77+#define SET_PENDING_INT intpending = 1
78+#define CLEAR_PENDING_INT intpending = 0
79+#define int_pending() intpending
80+
81+void exraise(int) __dead2;
82+void onint(void) __dead2;
83+void warning(const char *, ...) __printflike(1, 2);
84+void error(const char *, ...) __printf0like(1, 2) __dead2;
85+void errorwithstatus(int, const char *, ...) __printf0like(2, 3) __dead2;
86+
87+
88+/*
89+ * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
90+ * so we use _setjmp instead.
91+ */
92+
93+#undef setjmp
94+#define setjmp(jmploc) _setjmp(jmploc)
95+#undef longjmp
96+#define longjmp(jmploc, val) _longjmp(jmploc, val)
+1423,
-0
1@@ -0,0 +1,1423 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#include <paths.h>
37+#include <signal.h>
38+#include <stdlib.h>
39+#include <unistd.h>
40+#include <sys/resource.h>
41+#include <errno.h>
42+
43+/*
44+ * Evaluate a command.
45+ */
46+
47+#include "shell.h"
48+#include "nodes.h"
49+#include "syntax.h"
50+#include "expand.h"
51+#include "parser.h"
52+#include "jobs.h"
53+#include "eval.h"
54+#include "builtins.h"
55+#include "options.h"
56+#include "exec.h"
57+#include "redir.h"
58+#include "input.h"
59+#include "output.h"
60+#include "trap.h"
61+#include "var.h"
62+#include "memalloc.h"
63+#include "error.h"
64+#include "show.h"
65+#include "mystring.h"
66+#ifndef NO_HISTORY
67+#include "myhistedit.h"
68+#endif
69+
70+
71+int evalskip; /* set if we are skipping commands */
72+int skipcount; /* number of levels to skip */
73+static int loopnest; /* current loop nesting level */
74+int funcnest; /* depth of function calls */
75+static int builtin_flags; /* evalcommand flags for builtins */
76+
77+
78+char *commandname;
79+struct arglist *cmdenviron;
80+int exitstatus; /* exit status of last command */
81+int oexitstatus; /* saved exit status */
82+
83+
84+static void evalloop(union node *, int);
85+static void evalfor(union node *, int);
86+static union node *evalcase(union node *);
87+static void evalsubshell(union node *, int);
88+static void evalredir(union node *, int);
89+static void exphere(union node *, struct arglist *);
90+static void expredir(union node *);
91+static void evalpipe(union node *);
92+static int is_valid_fast_cmdsubst(union node *n);
93+static void evalcommand(union node *, int, struct backcmd *);
94+static void prehash(union node *);
95+
96+
97+/*
98+ * Called to reset things after an exception.
99+ */
100+
101+void
102+reseteval(void)
103+{
104+ evalskip = 0;
105+ loopnest = 0;
106+}
107+
108+
109+/*
110+ * The eval command.
111+ */
112+
113+int
114+evalcmd(int argc, char **argv)
115+{
116+ char *p;
117+ char *concat;
118+ char **ap;
119+
120+ if (argc > 1) {
121+ p = argv[1];
122+ if (argc > 2) {
123+ STARTSTACKSTR(concat);
124+ ap = argv + 2;
125+ for (;;) {
126+ STPUTS(p, concat);
127+ if ((p = *ap++) == NULL)
128+ break;
129+ STPUTC(' ', concat);
130+ }
131+ STPUTC('\0', concat);
132+ p = grabstackstr(concat);
133+ }
134+ evalstring(p, builtin_flags);
135+ } else
136+ exitstatus = 0;
137+ return exitstatus;
138+}
139+
140+
141+/*
142+ * Execute a command or commands contained in a string.
143+ */
144+
145+void
146+evalstring(const char *s, int flags)
147+{
148+ union node *n;
149+ struct stackmark smark;
150+ int flags_exit;
151+ int any;
152+
153+ flags_exit = flags & EV_EXIT;
154+ flags &= ~EV_EXIT;
155+ any = 0;
156+ setstackmark(&smark);
157+ setinputstring(s);
158+ while ((n = parsecmd(0)) != NEOF) {
159+ if (n != NULL && !nflag) {
160+ if (flags_exit && preadateof())
161+ evaltree(n, flags | EV_EXIT);
162+ else
163+ evaltree(n, flags);
164+ any = 1;
165+ if (evalskip)
166+ break;
167+ }
168+ popstackmark(&smark);
169+ setstackmark(&smark);
170+ }
171+ popfile();
172+ popstackmark(&smark);
173+ if (!any)
174+ exitstatus = 0;
175+ if (flags_exit)
176+ exraise(EXEXIT);
177+}
178+
179+
180+/*
181+ * Evaluate a parse tree. The value is left in the global variable
182+ * exitstatus.
183+ */
184+
185+void
186+evaltree(union node *n, int flags)
187+{
188+ int do_etest;
189+ union node *next;
190+ struct stackmark smark;
191+
192+ setstackmark(&smark);
193+ do_etest = 0;
194+ if (n == NULL) {
195+ TRACE(("evaltree(NULL) called\n"));
196+ exitstatus = 0;
197+ goto out;
198+ }
199+ do {
200+ next = NULL;
201+#ifndef NO_HISTORY
202+ displayhist = 1; /* show history substitutions done with fc */
203+#endif
204+ TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type));
205+ switch (n->type) {
206+ case NSEMI:
207+ evaltree(n->nbinary.ch1, flags & ~EV_EXIT);
208+ if (evalskip)
209+ goto out;
210+ next = n->nbinary.ch2;
211+ break;
212+ case NAND:
213+ evaltree(n->nbinary.ch1, EV_TESTED);
214+ if (evalskip || exitstatus != 0) {
215+ goto out;
216+ }
217+ next = n->nbinary.ch2;
218+ break;
219+ case NOR:
220+ evaltree(n->nbinary.ch1, EV_TESTED);
221+ if (evalskip || exitstatus == 0)
222+ goto out;
223+ next = n->nbinary.ch2;
224+ break;
225+ case NREDIR:
226+ evalredir(n, flags);
227+ break;
228+ case NSUBSHELL:
229+ evalsubshell(n, flags);
230+ do_etest = !(flags & EV_TESTED);
231+ break;
232+ case NBACKGND:
233+ evalsubshell(n, flags);
234+ break;
235+ case NIF: {
236+ evaltree(n->nif.test, EV_TESTED);
237+ if (evalskip)
238+ goto out;
239+ if (exitstatus == 0)
240+ next = n->nif.ifpart;
241+ else if (n->nif.elsepart)
242+ next = n->nif.elsepart;
243+ else
244+ exitstatus = 0;
245+ break;
246+ }
247+ case NWHILE:
248+ case NUNTIL:
249+ evalloop(n, flags & ~EV_EXIT);
250+ break;
251+ case NFOR:
252+ evalfor(n, flags & ~EV_EXIT);
253+ break;
254+ case NCASE:
255+ next = evalcase(n);
256+ break;
257+ case NCLIST:
258+ next = n->nclist.body;
259+ break;
260+ case NCLISTFALLTHRU:
261+ if (n->nclist.body) {
262+ evaltree(n->nclist.body, flags & ~EV_EXIT);
263+ if (evalskip)
264+ goto out;
265+ }
266+ next = n->nclist.next;
267+ break;
268+ case NDEFUN:
269+ defun(n->narg.text, n->narg.next);
270+ exitstatus = 0;
271+ break;
272+ case NNOT:
273+ evaltree(n->nnot.com, EV_TESTED);
274+ if (evalskip)
275+ goto out;
276+ exitstatus = !exitstatus;
277+ break;
278+
279+ case NPIPE:
280+ evalpipe(n);
281+ do_etest = !(flags & EV_TESTED);
282+ break;
283+ case NCMD:
284+ evalcommand(n, flags, (struct backcmd *)NULL);
285+ do_etest = !(flags & EV_TESTED);
286+ break;
287+ default:
288+ out1fmt("Node type = %d\n", n->type);
289+ flushout(&output);
290+ break;
291+ }
292+ n = next;
293+ popstackmark(&smark);
294+ setstackmark(&smark);
295+ } while (n != NULL);
296+out:
297+ popstackmark(&smark);
298+ if (pendingsig)
299+ dotrap();
300+ if (eflag && exitstatus != 0 && do_etest)
301+ exitshell(exitstatus);
302+ if (flags & EV_EXIT)
303+ exraise(EXEXIT);
304+}
305+
306+
307+static void
308+evalloop(union node *n, int flags)
309+{
310+ int status;
311+
312+ loopnest++;
313+ status = 0;
314+ for (;;) {
315+ if (!evalskip)
316+ evaltree(n->nbinary.ch1, EV_TESTED);
317+ if (evalskip) {
318+ if (evalskip == SKIPCONT && --skipcount <= 0) {
319+ evalskip = 0;
320+ continue;
321+ }
322+ if (evalskip == SKIPBREAK && --skipcount <= 0)
323+ evalskip = 0;
324+ if (evalskip == SKIPRETURN)
325+ status = exitstatus;
326+ break;
327+ }
328+ if (n->type == NWHILE) {
329+ if (exitstatus != 0)
330+ break;
331+ } else {
332+ if (exitstatus == 0)
333+ break;
334+ }
335+ evaltree(n->nbinary.ch2, flags);
336+ status = exitstatus;
337+ }
338+ loopnest--;
339+ exitstatus = status;
340+}
341+
342+
343+
344+static void
345+evalfor(union node *n, int flags)
346+{
347+ struct arglist arglist;
348+ union node *argp;
349+ int i;
350+ int status;
351+
352+ emptyarglist(&arglist);
353+ for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
354+ oexitstatus = exitstatus;
355+ expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
356+ }
357+
358+ loopnest++;
359+ status = 0;
360+ for (i = 0; i < arglist.count; i++) {
361+ setvar(n->nfor.var, arglist.args[i], 0);
362+ evaltree(n->nfor.body, flags);
363+ status = exitstatus;
364+ if (evalskip) {
365+ if (evalskip == SKIPCONT && --skipcount <= 0) {
366+ evalskip = 0;
367+ continue;
368+ }
369+ if (evalskip == SKIPBREAK && --skipcount <= 0)
370+ evalskip = 0;
371+ break;
372+ }
373+ }
374+ loopnest--;
375+ exitstatus = status;
376+}
377+
378+
379+/*
380+ * Evaluate a case statement, returning the selected tree.
381+ *
382+ * The exit status needs care to get right.
383+ */
384+
385+static union node *
386+evalcase(union node *n)
387+{
388+ union node *cp;
389+ union node *patp;
390+ struct arglist arglist;
391+
392+ emptyarglist(&arglist);
393+ oexitstatus = exitstatus;
394+ expandarg(n->ncase.expr, &arglist, EXP_TILDE);
395+ for (cp = n->ncase.cases ; cp ; cp = cp->nclist.next) {
396+ for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
397+ if (casematch(patp, arglist.args[0])) {
398+ while (cp->nclist.next &&
399+ cp->type == NCLISTFALLTHRU &&
400+ cp->nclist.body == NULL)
401+ cp = cp->nclist.next;
402+ if (cp->nclist.next &&
403+ cp->type == NCLISTFALLTHRU)
404+ return (cp);
405+ if (cp->nclist.body == NULL)
406+ exitstatus = 0;
407+ return (cp->nclist.body);
408+ }
409+ }
410+ }
411+ exitstatus = 0;
412+ return (NULL);
413+}
414+
415+
416+
417+/*
418+ * Kick off a subshell to evaluate a tree.
419+ */
420+
421+static void
422+evalsubshell(union node *n, int flags)
423+{
424+ struct job *jp;
425+ int backgnd = (n->type == NBACKGND);
426+
427+ oexitstatus = exitstatus;
428+ expredir(n->nredir.redirect);
429+ if ((!backgnd && flags & EV_EXIT && !have_traps()) ||
430+ forkshell(jp = makejob(n, 1), n, backgnd) == 0) {
431+ if (backgnd)
432+ flags &=~ EV_TESTED;
433+ redirect(n->nredir.redirect, 0);
434+ evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
435+ } else if (! backgnd) {
436+ INTOFF;
437+ exitstatus = waitforjob(jp, (int *)NULL);
438+ INTON;
439+ } else
440+ exitstatus = 0;
441+}
442+
443+
444+/*
445+ * Evaluate a redirected compound command.
446+ */
447+
448+static void
449+evalredir(union node *n, int flags)
450+{
451+ struct jmploc jmploc;
452+ struct jmploc *savehandler;
453+ volatile int in_redirect = 1;
454+
455+ oexitstatus = exitstatus;
456+ expredir(n->nredir.redirect);
457+ savehandler = handler;
458+ if (setjmp(jmploc.loc)) {
459+ int e;
460+
461+ handler = savehandler;
462+ e = exception;
463+ popredir();
464+ if (e == EXERROR && in_redirect) {
465+ FORCEINTON;
466+ return;
467+ }
468+ longjmp(handler->loc, 1);
469+ } else {
470+ INTOFF;
471+ handler = &jmploc;
472+ redirect(n->nredir.redirect, REDIR_PUSH);
473+ in_redirect = 0;
474+ INTON;
475+ evaltree(n->nredir.n, flags);
476+ }
477+ INTOFF;
478+ handler = savehandler;
479+ popredir();
480+ INTON;
481+}
482+
483+
484+static void
485+exphere(union node *redir, struct arglist *fn)
486+{
487+ struct jmploc jmploc;
488+ struct jmploc *savehandler;
489+ struct localvar *savelocalvars;
490+ int need_longjmp = 0;
491+ unsigned char saveoptreset;
492+
493+ redir->nhere.expdoc = "";
494+ savelocalvars = localvars;
495+ localvars = NULL;
496+ saveoptreset = shellparam.reset;
497+ forcelocal++;
498+ savehandler = handler;
499+ if (setjmp(jmploc.loc))
500+ need_longjmp = exception != EXERROR;
501+ else {
502+ handler = &jmploc;
503+ expandarg(redir->nhere.doc, fn, 0);
504+ redir->nhere.expdoc = fn->args[0];
505+ INTOFF;
506+ }
507+ handler = savehandler;
508+ forcelocal--;
509+ poplocalvars();
510+ localvars = savelocalvars;
511+ shellparam.reset = saveoptreset;
512+ if (need_longjmp)
513+ longjmp(handler->loc, 1);
514+ INTON;
515+}
516+
517+
518+/*
519+ * Compute the names of the files in a redirection list.
520+ */
521+
522+static void
523+expredir(union node *n)
524+{
525+ union node *redir;
526+
527+ for (redir = n ; redir ; redir = redir->nfile.next) {
528+ struct arglist fn;
529+ emptyarglist(&fn);
530+ switch (redir->type) {
531+ case NFROM:
532+ case NTO:
533+ case NFROMTO:
534+ case NAPPEND:
535+ case NCLOBBER:
536+ expandarg(redir->nfile.fname, &fn, EXP_TILDE);
537+ redir->nfile.expfname = fn.args[0];
538+ break;
539+ case NFROMFD:
540+ case NTOFD:
541+ if (redir->ndup.vname) {
542+ expandarg(redir->ndup.vname, &fn, EXP_TILDE);
543+ fixredir(redir, fn.args[0], 1);
544+ }
545+ break;
546+ case NXHERE:
547+ exphere(redir, &fn);
548+ break;
549+ }
550+ }
551+}
552+
553+
554+
555+/*
556+ * Evaluate a pipeline. All the processes in the pipeline are children
557+ * of the process creating the pipeline. (This differs from some versions
558+ * of the shell, which make the last process in a pipeline the parent
559+ * of all the rest.)
560+ */
561+
562+static void
563+evalpipe(union node *n)
564+{
565+ struct job *jp;
566+ struct nodelist *lp;
567+ int pipelen;
568+ int prevfd;
569+ int pip[2];
570+
571+ TRACE(("evalpipe(%p) called\n", (void *)n));
572+ pipelen = 0;
573+ for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
574+ pipelen++;
575+ INTOFF;
576+ jp = makejob(n, pipelen);
577+ prevfd = -1;
578+ for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
579+ prehash(lp->n);
580+ pip[1] = -1;
581+ if (lp->next) {
582+ if (pipe(pip) < 0) {
583+ if (prevfd >= 0)
584+ close(prevfd);
585+ error("Pipe call failed: %s", strerror(errno));
586+ }
587+ }
588+ if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
589+ INTON;
590+ if (prevfd > 0) {
591+ dup2(prevfd, 0);
592+ close(prevfd);
593+ }
594+ if (pip[1] >= 0) {
595+ if (!(prevfd >= 0 && pip[0] == 0))
596+ close(pip[0]);
597+ if (pip[1] != 1) {
598+ dup2(pip[1], 1);
599+ close(pip[1]);
600+ }
601+ }
602+ evaltree(lp->n, EV_EXIT);
603+ }
604+ if (prevfd >= 0)
605+ close(prevfd);
606+ prevfd = pip[0];
607+ if (pip[1] != -1)
608+ close(pip[1]);
609+ }
610+ INTON;
611+ if (n->npipe.backgnd == 0) {
612+ INTOFF;
613+ exitstatus = waitforjob(jp, (int *)NULL);
614+ TRACE(("evalpipe: job done exit status %d\n", exitstatus));
615+ INTON;
616+ } else
617+ exitstatus = 0;
618+}
619+
620+
621+
622+static int
623+is_valid_fast_cmdsubst(union node *n)
624+{
625+
626+ return (n->type == NCMD);
627+}
628+
629+/*
630+ * Execute a command inside back quotes. If it's a builtin command, we
631+ * want to save its output in a block obtained from malloc. Otherwise
632+ * we fork off a subprocess and get the output of the command via a pipe.
633+ * Should be called with interrupts off.
634+ */
635+
636+void
637+evalbackcmd(union node *n, struct backcmd *result)
638+{
639+ int pip[2];
640+ struct job *jp;
641+ struct stackmark smark;
642+ struct jmploc jmploc;
643+ struct jmploc *savehandler;
644+ struct localvar *savelocalvars;
645+ unsigned char saveoptreset;
646+
647+ result->fd = -1;
648+ result->buf = NULL;
649+ result->nleft = 0;
650+ result->jp = NULL;
651+ if (n == NULL) {
652+ exitstatus = 0;
653+ return;
654+ }
655+ setstackmark(&smark);
656+ exitstatus = oexitstatus;
657+ if (is_valid_fast_cmdsubst(n)) {
658+ savelocalvars = localvars;
659+ localvars = NULL;
660+ saveoptreset = shellparam.reset;
661+ forcelocal++;
662+ savehandler = handler;
663+ if (setjmp(jmploc.loc)) {
664+ if (exception == EXERROR)
665+ /* nothing */;
666+ else if (exception != 0) {
667+ handler = savehandler;
668+ forcelocal--;
669+ poplocalvars();
670+ localvars = savelocalvars;
671+ shellparam.reset = saveoptreset;
672+ longjmp(handler->loc, 1);
673+ }
674+ } else {
675+ handler = &jmploc;
676+ evalcommand(n, EV_BACKCMD, result);
677+ }
678+ handler = savehandler;
679+ forcelocal--;
680+ poplocalvars();
681+ localvars = savelocalvars;
682+ shellparam.reset = saveoptreset;
683+ } else {
684+ if (pipe(pip) < 0)
685+ error("Pipe call failed: %s", strerror(errno));
686+ jp = makejob(n, 1);
687+ if (forkshell(jp, n, FORK_NOJOB) == 0) {
688+ FORCEINTON;
689+ close(pip[0]);
690+ if (pip[1] != 1) {
691+ dup2(pip[1], 1);
692+ close(pip[1]);
693+ }
694+ evaltree(n, EV_EXIT);
695+ }
696+ close(pip[1]);
697+ result->fd = pip[0];
698+ result->jp = jp;
699+ }
700+ popstackmark(&smark);
701+ TRACE(("evalbackcmd done: fd=%d buf=%p nleft=%d jp=%p\n",
702+ result->fd, result->buf, result->nleft, result->jp));
703+}
704+
705+static int
706+mustexpandto(const char *argtext, const char *mask)
707+{
708+ for (;;) {
709+ if (*argtext == CTLQUOTEMARK || *argtext == CTLQUOTEEND) {
710+ argtext++;
711+ continue;
712+ }
713+ if (*argtext == CTLESC)
714+ argtext++;
715+ else if (BASESYNTAX[(int)*argtext] == CCTL)
716+ return (0);
717+ if (*argtext != *mask)
718+ return (0);
719+ if (*argtext == '\0')
720+ return (1);
721+ argtext++;
722+ mask++;
723+ }
724+}
725+
726+static int
727+isdeclarationcmd(struct narg *arg)
728+{
729+ int have_command = 0;
730+
731+ if (arg == NULL)
732+ return (0);
733+ while (mustexpandto(arg->text, "command")) {
734+ have_command = 1;
735+ arg = &arg->next->narg;
736+ if (arg == NULL)
737+ return (0);
738+ /*
739+ * To also allow "command -p" and "command --" as part of
740+ * a declaration command, add code here.
741+ * We do not do this, as ksh does not do it either and it
742+ * is not required by POSIX.
743+ */
744+ }
745+ return (mustexpandto(arg->text, "export") ||
746+ mustexpandto(arg->text, "readonly") ||
747+ (mustexpandto(arg->text, "local") &&
748+ (have_command || !isfunc("local"))));
749+}
750+
751+static void
752+xtracecommand(struct arglist *varlist, int argc, char **argv)
753+{
754+ char sep = 0;
755+ const char *text, *p, *ps4;
756+ int i;
757+
758+ ps4 = expandstr(ps4val());
759+ out2str(ps4 != NULL ? ps4 : ps4val());
760+ for (i = 0; i < varlist->count; i++) {
761+ text = varlist->args[i];
762+ if (sep != 0)
763+ out2c(' ');
764+ p = strchr(text, '=');
765+ if (p != NULL) {
766+ p++;
767+ outbin(text, p - text, out2);
768+ out2qstr(p);
769+ } else
770+ out2qstr(text);
771+ sep = ' ';
772+ }
773+ for (i = 0; i < argc; i++) {
774+ text = argv[i];
775+ if (sep != 0)
776+ out2c(' ');
777+ out2qstr(text);
778+ sep = ' ';
779+ }
780+ out2c('\n');
781+ flushout(&errout);
782+}
783+
784+/*
785+ * Check if a builtin can safely be executed in the same process,
786+ * even though it should be in a subshell (command substitution).
787+ * Note that jobid, jobs, times and trap can show information not
788+ * available in a child process; this is deliberate.
789+ * The arguments should already have been expanded.
790+ */
791+static int
792+safe_builtin(int idx, int argc, char **argv)
793+{
794+ /* Generated from builtins.def. */
795+ if (safe_builtin_always(idx))
796+ return (1);
797+ if (idx == EXPORTCMD || idx == TRAPCMD || idx == ULIMITCMD ||
798+ idx == UMASKCMD)
799+ return (argc <= 1 || (argc == 2 && argv[1][0] == '-'));
800+ if (idx == SETCMD)
801+ return (argc <= 1 || (argc == 2 && (argv[1][0] == '-' ||
802+ argv[1][0] == '+') && argv[1][1] == 'o' &&
803+ argv[1][2] == '\0'));
804+ return (0);
805+}
806+
807+/*
808+ * Perform redirections, then execute a simple command with vfork.
809+ * This cannot be used for command substitutions for two reasons:
810+ * - Redirections might cause the error message for later redirections or for
811+ * an unknown command to be sent to the pipe (to be substituted), and this
812+ * might cause a deadlock if the message is too long.
813+ * - The assignment of the pipe needs to come before instead of after the
814+ * redirections.
815+ */
816+static int
817+redirected_vforkexecshell(struct job *jp, union node *redir, char **argv,
818+ char **envp, const char *path, int idx)
819+{
820+ struct jmploc jmploc;
821+ struct jmploc *savehandler;
822+ volatile int in_redirect = 1;
823+
824+ savehandler = handler;
825+ if (setjmp(jmploc.loc)) {
826+ int e;
827+
828+ handler = savehandler;
829+ e = exception;
830+ popredir();
831+ if (e == EXERROR && in_redirect) {
832+ FORCEINTON;
833+ return 0;
834+ }
835+ longjmp(handler->loc, 1);
836+ } else {
837+ INTOFF;
838+ handler = &jmploc;
839+ redirect(redir, REDIR_PUSH);
840+ in_redirect = 0;
841+ INTON;
842+ vforkexecshell(jp, argv, envp, path, idx, NULL);
843+ }
844+ INTOFF;
845+ handler = savehandler;
846+ popredir();
847+ INTON;
848+ return 1;
849+}
850+
851+/*
852+ * Execute a simple command.
853+ * Note: This may or may not return if (flags & EV_EXIT).
854+ */
855+
856+static void
857+evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
858+{
859+ union node *argp;
860+ struct arglist arglist;
861+ struct arglist varlist;
862+ char **argv;
863+ int argc;
864+ char **envp;
865+ int varflag;
866+ int mode;
867+ int pip[2];
868+ struct cmdentry cmdentry;
869+ struct job *jp;
870+ struct jmploc jmploc;
871+ struct jmploc *savehandler;
872+ char *savecmdname;
873+ struct shparam saveparam;
874+ struct localvar *savelocalvars;
875+ struct parsefile *savetopfile;
876+ volatile int e;
877+ char *lastarg;
878+ int signaled;
879+ int do_clearcmdentry;
880+ const char *path = pathval();
881+ int i;
882+
883+ /* First expand the arguments. */
884+ TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags));
885+ emptyarglist(&arglist);
886+ emptyarglist(&varlist);
887+ varflag = 1;
888+ jp = NULL;
889+ do_clearcmdentry = 0;
890+ oexitstatus = exitstatus;
891+ exitstatus = 0;
892+ /* Add one slot at the beginning for tryexec(). */
893+ appendarglist(&arglist, nullstr);
894+ for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
895+ if (varflag && isassignment(argp->narg.text)) {
896+ expandarg(argp, varflag == 1 ? &varlist : &arglist,
897+ EXP_VARTILDE);
898+ continue;
899+ } else if (varflag == 1)
900+ varflag = isdeclarationcmd(&argp->narg) ? 2 : 0;
901+ expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
902+ }
903+ appendarglist(&arglist, nullstr);
904+ expredir(cmd->ncmd.redirect);
905+ argc = arglist.count - 2;
906+ argv = &arglist.args[1];
907+
908+ argv[argc] = NULL;
909+ lastarg = NULL;
910+ if (iflag && funcnest == 0 && argc > 0)
911+ lastarg = argv[argc - 1];
912+
913+ /* Print the command if xflag is set. */
914+ if (xflag)
915+ xtracecommand(&varlist, argc, argv);
916+
917+ /* Now locate the command. */
918+ if (argc == 0) {
919+ /* Variable assignment(s) without command */
920+ cmdentry.cmdtype = CMDBUILTIN;
921+ cmdentry.u.index = BLTINCMD;
922+ cmdentry.special = 0;
923+ } else {
924+ static const char PATH[] = "PATH=";
925+ int cmd_flags = 0, bltinonly = 0;
926+
927+ /*
928+ * Modify the command lookup path, if a PATH= assignment
929+ * is present
930+ */
931+ for (i = 0; i < varlist.count; i++)
932+ if (strncmp(varlist.args[i], PATH, sizeof(PATH) - 1) == 0) {
933+ path = varlist.args[i] + sizeof(PATH) - 1;
934+ /*
935+ * On `PATH=... command`, we need to make
936+ * sure that the command isn't using the
937+ * non-updated hash table of the outer PATH
938+ * setting and we need to make sure that
939+ * the hash table isn't filled with items
940+ * from the temporary setting.
941+ *
942+ * It would be better to forbid using and
943+ * updating the table while this command
944+ * runs, by the command finding mechanism
945+ * is heavily integrated with hash handling,
946+ * so we just delete the hash before and after
947+ * the command runs. Partly deleting like
948+ * changepatch() does doesn't seem worth the
949+ * booking effort, since most such runs add
950+ * directories in front of the new PATH.
951+ */
952+ clearcmdentry();
953+ do_clearcmdentry = 1;
954+ }
955+
956+ for (;;) {
957+ if (bltinonly) {
958+ cmdentry.u.index = find_builtin(*argv, &cmdentry.special);
959+ if (cmdentry.u.index < 0) {
960+ cmdentry.u.index = BLTINCMD;
961+ argv--;
962+ argc++;
963+ break;
964+ }
965+ } else
966+ find_command(argv[0], &cmdentry, cmd_flags, path);
967+ /* implement the bltin and command builtins here */
968+ if (cmdentry.cmdtype != CMDBUILTIN)
969+ break;
970+ if (cmdentry.u.index == BLTINCMD) {
971+ if (argc == 1)
972+ break;
973+ argv++;
974+ argc--;
975+ bltinonly = 1;
976+ } else if (cmdentry.u.index == COMMANDCMD) {
977+ if (argc == 1)
978+ break;
979+ if (!strcmp(argv[1], "-p")) {
980+ if (argc == 2)
981+ break;
982+ if (argv[2][0] == '-') {
983+ if (strcmp(argv[2], "--"))
984+ break;
985+ if (argc == 3)
986+ break;
987+ argv += 3;
988+ argc -= 3;
989+ } else {
990+ argv += 2;
991+ argc -= 2;
992+ }
993+ path = _PATH_STDPATH;
994+ clearcmdentry();
995+ do_clearcmdentry = 1;
996+ } else if (!strcmp(argv[1], "--")) {
997+ if (argc == 2)
998+ break;
999+ argv += 2;
1000+ argc -= 2;
1001+ } else if (argv[1][0] == '-')
1002+ break;
1003+ else {
1004+ argv++;
1005+ argc--;
1006+ }
1007+ cmd_flags |= DO_NOFUNC;
1008+ bltinonly = 0;
1009+ } else
1010+ break;
1011+ }
1012+ /*
1013+ * Special builtins lose their special properties when
1014+ * called via 'command'.
1015+ */
1016+ if (cmd_flags & DO_NOFUNC)
1017+ cmdentry.special = 0;
1018+ }
1019+
1020+ /* Fork off a child process if necessary. */
1021+ if (((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN)
1022+ && ((flags & EV_EXIT) == 0 || have_traps()))
1023+ || ((flags & EV_BACKCMD) != 0
1024+ && (cmdentry.cmdtype != CMDBUILTIN ||
1025+ !safe_builtin(cmdentry.u.index, argc, argv)))) {
1026+ jp = makejob(cmd, 1);
1027+ mode = FORK_FG;
1028+ if (flags & EV_BACKCMD) {
1029+ mode = FORK_NOJOB;
1030+ if (pipe(pip) < 0)
1031+ error("Pipe call failed: %s", strerror(errno));
1032+ }
1033+ if (cmdentry.cmdtype == CMDNORMAL &&
1034+ (cmd->ncmd.redirect == NULL || (flags & EV_BACKCMD) == 0) &&
1035+ varlist.count == 0 &&
1036+ (mode == FORK_FG || mode == FORK_NOJOB) &&
1037+ !disvforkset() && !iflag && !mflag) {
1038+ if (cmd->ncmd.redirect != NULL) {
1039+ if (redirected_vforkexecshell(jp,
1040+ cmd->ncmd.redirect,
1041+ argv, environment(), path,
1042+ cmdentry.u.index))
1043+ goto parent;
1044+ else
1045+ goto out;
1046+ } else
1047+ vforkexecshell(jp, argv, environment(), path,
1048+ cmdentry.u.index,
1049+ flags & EV_BACKCMD ? pip : NULL);
1050+ goto parent;
1051+ }
1052+ if (forkshell(jp, cmd, mode) != 0)
1053+ goto parent; /* at end of routine */
1054+ if (flags & EV_BACKCMD) {
1055+ FORCEINTON;
1056+ close(pip[0]);
1057+ if (pip[1] != 1) {
1058+ dup2(pip[1], 1);
1059+ close(pip[1]);
1060+ }
1061+ flags &= ~EV_BACKCMD;
1062+ }
1063+ flags |= EV_EXIT;
1064+ }
1065+
1066+ /* This is the child process if a fork occurred. */
1067+ /* Execute the command. */
1068+ if (cmdentry.cmdtype == CMDFUNCTION) {
1069+#ifdef DEBUG
1070+ trputs("Shell function: "); trargs(argv);
1071+#endif
1072+ saveparam = shellparam;
1073+ shellparam.malloc = 0;
1074+ shellparam.reset = 1;
1075+ shellparam.nparam = argc - 1;
1076+ shellparam.p = argv + 1;
1077+ shellparam.optp = NULL;
1078+ shellparam.optnext = NULL;
1079+ INTOFF;
1080+ savelocalvars = localvars;
1081+ localvars = NULL;
1082+ reffunc(cmdentry.u.func);
1083+ savehandler = handler;
1084+ if (setjmp(jmploc.loc)) {
1085+ popredir();
1086+ unreffunc(cmdentry.u.func);
1087+ poplocalvars();
1088+ localvars = savelocalvars;
1089+ freeparam(&shellparam);
1090+ shellparam = saveparam;
1091+ funcnest--;
1092+ handler = savehandler;
1093+ longjmp(handler->loc, 1);
1094+ }
1095+ handler = &jmploc;
1096+ funcnest++;
1097+ redirect(cmd->ncmd.redirect, REDIR_PUSH);
1098+ INTON;
1099+ for (i = 0; i < varlist.count; i++)
1100+ mklocal(varlist.args[i]);
1101+ exitstatus = oexitstatus;
1102+ evaltree(getfuncnode(cmdentry.u.func),
1103+ flags & (EV_TESTED | EV_EXIT));
1104+ INTOFF;
1105+ unreffunc(cmdentry.u.func);
1106+ poplocalvars();
1107+ localvars = savelocalvars;
1108+ freeparam(&shellparam);
1109+ shellparam = saveparam;
1110+ handler = savehandler;
1111+ funcnest--;
1112+ popredir();
1113+ INTON;
1114+ if (evalskip == SKIPRETURN) {
1115+ evalskip = 0;
1116+ skipcount = 0;
1117+ }
1118+ if (jp)
1119+ exitshell(exitstatus);
1120+ } else if (cmdentry.cmdtype == CMDBUILTIN) {
1121+#ifdef DEBUG
1122+ trputs("builtin command: "); trargs(argv);
1123+#endif
1124+ mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
1125+ if (flags == EV_BACKCMD) {
1126+ memout.nextc = memout.buf;
1127+ mode |= REDIR_BACKQ;
1128+ }
1129+ savecmdname = commandname;
1130+ savetopfile = getcurrentfile();
1131+ cmdenviron = &varlist;
1132+ e = -1;
1133+ savehandler = handler;
1134+ if (setjmp(jmploc.loc)) {
1135+ e = exception;
1136+ if (e == EXINT)
1137+ exitstatus = SIGINT+128;
1138+ goto cmddone;
1139+ }
1140+ handler = &jmploc;
1141+ redirect(cmd->ncmd.redirect, mode);
1142+ outclearerror(out1);
1143+ /*
1144+ * If there is no command word, redirection errors should
1145+ * not be fatal but assignment errors should.
1146+ */
1147+ if (argc == 0)
1148+ cmdentry.special = 1;
1149+ listsetvar(cmdenviron, cmdentry.special ? 0 : VNOSET);
1150+ if (argc > 0)
1151+ bltinsetlocale();
1152+ commandname = argv[0];
1153+ argptr = argv + 1;
1154+ nextopt_optptr = NULL; /* initialize nextopt */
1155+ builtin_flags = flags;
1156+ exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
1157+ flushall();
1158+ if (outiserror(out1)) {
1159+ warning("write error on stdout");
1160+ if (exitstatus == 0 || exitstatus == 1)
1161+ exitstatus = 2;
1162+ }
1163+cmddone:
1164+ if (argc > 0)
1165+ bltinunsetlocale();
1166+ cmdenviron = NULL;
1167+ out1 = &output;
1168+ out2 = &errout;
1169+ freestdout();
1170+ handler = savehandler;
1171+ commandname = savecmdname;
1172+ if (jp)
1173+ exitshell(exitstatus);
1174+ if (flags == EV_BACKCMD) {
1175+ backcmd->buf = memout.buf;
1176+ backcmd->nleft = memout.buf != NULL ?
1177+ memout.nextc - memout.buf : 0;
1178+ memout.buf = NULL;
1179+ memout.nextc = NULL;
1180+ memout.bufend = NULL;
1181+ memout.bufsize = 64;
1182+ }
1183+ if (cmdentry.u.index != EXECCMD)
1184+ popredir();
1185+ if (e != -1) {
1186+ if (e != EXERROR || cmdentry.special)
1187+ exraise(e);
1188+ popfilesupto(savetopfile);
1189+ if (flags != EV_BACKCMD)
1190+ FORCEINTON;
1191+ }
1192+ } else {
1193+#ifdef DEBUG
1194+ trputs("normal command: "); trargs(argv);
1195+#endif
1196+ redirect(cmd->ncmd.redirect, 0);
1197+ for (i = 0; i < varlist.count; i++)
1198+ setvareq(varlist.args[i], VEXPORT|VSTACK);
1199+ envp = environment();
1200+ shellexec(argv, envp, path, cmdentry.u.index);
1201+ /*NOTREACHED*/
1202+ }
1203+ goto out;
1204+
1205+parent: /* parent process gets here (if we forked) */
1206+ if (mode == FORK_FG) { /* argument to fork */
1207+ INTOFF;
1208+ exitstatus = waitforjob(jp, &signaled);
1209+ INTON;
1210+ if (iflag && loopnest > 0 && signaled) {
1211+ evalskip = SKIPBREAK;
1212+ skipcount = loopnest;
1213+ }
1214+ } else if (mode == FORK_NOJOB) {
1215+ backcmd->fd = pip[0];
1216+ close(pip[1]);
1217+ backcmd->jp = jp;
1218+ }
1219+
1220+out:
1221+ if (lastarg)
1222+ setvar("_", lastarg, 0);
1223+ if (do_clearcmdentry)
1224+ clearcmdentry();
1225+}
1226+
1227+
1228+
1229+/*
1230+ * Search for a command. This is called before we fork so that the
1231+ * location of the command will be available in the parent as well as
1232+ * the child. The check for "goodname" is an overly conservative
1233+ * check that the name will not be subject to expansion.
1234+ */
1235+
1236+static void
1237+prehash(union node *n)
1238+{
1239+ struct cmdentry entry;
1240+
1241+ if (n && n->type == NCMD && n->ncmd.args)
1242+ if (goodname(n->ncmd.args->narg.text))
1243+ find_command(n->ncmd.args->narg.text, &entry, 0,
1244+ pathval());
1245+}
1246+
1247+
1248+
1249+/*
1250+ * Builtin commands. Builtin commands whose functions are closely
1251+ * tied to evaluation are implemented here.
1252+ */
1253+
1254+/*
1255+ * No command given, a bltin command with no arguments, or a bltin command
1256+ * with an invalid name.
1257+ */
1258+
1259+int
1260+bltincmd(int argc, char **argv)
1261+{
1262+ if (argc > 1) {
1263+ out2fmt_flush("%s: not found\n", argv[1]);
1264+ return 127;
1265+ }
1266+ /*
1267+ * Preserve exitstatus of a previous possible command substitution
1268+ * as POSIX mandates
1269+ */
1270+ return exitstatus;
1271+}
1272+
1273+
1274+/*
1275+ * Handle break and continue commands. Break, continue, and return are
1276+ * all handled by setting the evalskip flag. The evaluation routines
1277+ * above all check this flag, and if it is set they start skipping
1278+ * commands rather than executing them. The variable skipcount is
1279+ * the number of loops to break/continue, or the number of function
1280+ * levels to return. (The latter is always 1.) It should probably
1281+ * be an error to break out of more loops than exist, but it isn't
1282+ * in the standard shell so we don't make it one here.
1283+ */
1284+
1285+int
1286+breakcmd(int argc, char **argv)
1287+{
1288+ long n;
1289+ char *end;
1290+
1291+ if (argc > 1) {
1292+ /* Allow arbitrarily large numbers. */
1293+ n = strtol(argv[1], &end, 10);
1294+ if (!is_digit(argv[1][0]) || *end != '\0')
1295+ error("Illegal number: %s", argv[1]);
1296+ } else
1297+ n = 1;
1298+ if (n > loopnest)
1299+ n = loopnest;
1300+ if (n > 0) {
1301+ evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
1302+ skipcount = n;
1303+ }
1304+ return 0;
1305+}
1306+
1307+/*
1308+ * The `command' command.
1309+ */
1310+int
1311+commandcmd(int argc __unused, char **argv __unused)
1312+{
1313+ const char *path;
1314+ int ch;
1315+ int cmd = -1;
1316+
1317+ path = bltinlookup("PATH", 1);
1318+
1319+ while ((ch = nextopt("pvV")) != '\0') {
1320+ switch (ch) {
1321+ case 'p':
1322+ path = _PATH_STDPATH;
1323+ break;
1324+ case 'v':
1325+ cmd = TYPECMD_SMALLV;
1326+ break;
1327+ case 'V':
1328+ cmd = TYPECMD_BIGV;
1329+ break;
1330+ }
1331+ }
1332+
1333+ if (cmd != -1) {
1334+ if (*argptr == NULL || argptr[1] != NULL)
1335+ error("wrong number of arguments");
1336+ return typecmd_impl(2, argptr - 1, cmd, path);
1337+ }
1338+ if (*argptr != NULL)
1339+ error("commandcmd bad call");
1340+
1341+ /*
1342+ * Do nothing successfully if no command was specified;
1343+ * ksh also does this.
1344+ */
1345+ return 0;
1346+}
1347+
1348+
1349+/*
1350+ * The return command.
1351+ */
1352+
1353+int
1354+returncmd(int argc, char **argv)
1355+{
1356+ int ret = argc > 1 ? number(argv[1]) : oexitstatus;
1357+
1358+ evalskip = SKIPRETURN;
1359+ skipcount = 1;
1360+ return ret;
1361+}
1362+
1363+
1364+int
1365+falsecmd(int argc __unused, char **argv __unused)
1366+{
1367+ return 1;
1368+}
1369+
1370+
1371+int
1372+truecmd(int argc __unused, char **argv __unused)
1373+{
1374+ return 0;
1375+}
1376+
1377+
1378+int
1379+execcmd(int argc, char **argv)
1380+{
1381+ int i;
1382+
1383+ /*
1384+ * Because we have historically not supported any options,
1385+ * only treat "--" specially.
1386+ */
1387+ if (argc > 1 && strcmp(argv[1], "--") == 0)
1388+ argc--, argv++;
1389+ if (argc > 1) {
1390+ iflag = 0; /* exit on error */
1391+ mflag = 0;
1392+ optschanged();
1393+ for (i = 0; i < cmdenviron->count; i++)
1394+ setvareq(cmdenviron->args[i], VEXPORT|VSTACK);
1395+ shellexec(argv + 1, environment(), pathval(), 0);
1396+
1397+ }
1398+ return 0;
1399+}
1400+
1401+
1402+int
1403+timescmd(int argc __unused, char **argv __unused)
1404+{
1405+ struct rusage ru;
1406+ long shumins, shsmins, chumins, chsmins;
1407+ double shusecs, shssecs, chusecs, chssecs;
1408+
1409+ if (getrusage(RUSAGE_SELF, &ru) < 0)
1410+ return 1;
1411+ shumins = ru.ru_utime.tv_sec / 60;
1412+ shusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
1413+ shsmins = ru.ru_stime.tv_sec / 60;
1414+ shssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
1415+ if (getrusage(RUSAGE_CHILDREN, &ru) < 0)
1416+ return 1;
1417+ chumins = ru.ru_utime.tv_sec / 60;
1418+ chusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
1419+ chsmins = ru.ru_stime.tv_sec / 60;
1420+ chssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
1421+ out1fmt("%ldm%.3fs %ldm%.3fs\n%ldm%.3fs %ldm%.3fs\n", shumins,
1422+ shusecs, shsmins, shssecs, chumins, chusecs, chsmins, chssecs);
1423+ return 0;
1424+}
+69,
-0
1@@ -0,0 +1,69 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+extern char *commandname; /* currently executing command */
37+extern int exitstatus; /* exit status of last command */
38+extern int oexitstatus; /* saved exit status */
39+extern struct arglist *cmdenviron; /* environment for builtin command */
40+
41+
42+struct backcmd { /* result of evalbackcmd */
43+ int fd; /* file descriptor to read from */
44+ char *buf; /* buffer */
45+ int nleft; /* number of chars in buffer */
46+ struct job *jp; /* job structure for command */
47+};
48+
49+void reseteval(void);
50+
51+/* flags in argument to evaltree/evalstring */
52+#define EV_EXIT 01 /* exit after evaluating tree */
53+#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
54+#define EV_BACKCMD 04 /* command executing within back quotes */
55+
56+void evalstring(const char *, int);
57+union node; /* BLETCH for ansi C */
58+void evaltree(union node *, int);
59+void evalbackcmd(union node *, struct backcmd *);
60+
61+/* in_function returns nonzero if we are currently evaluating a function */
62+#define in_function() funcnest
63+extern int funcnest;
64+extern int evalskip;
65+extern int skipcount;
66+
67+/* reasons for skipping commands (see comment on breakcmd routine) */
68+#define SKIPBREAK 1
69+#define SKIPCONT 2
70+#define SKIPRETURN 3
+848,
-0
1@@ -0,0 +1,848 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#include <sys/types.h>
37+#include <sys/stat.h>
38+#include <unistd.h>
39+#include <fcntl.h>
40+#include <errno.h>
41+#include <paths.h>
42+#include <stdlib.h>
43+
44+/*
45+ * When commands are first encountered, they are entered in a hash table.
46+ * This ensures that a full path search will not have to be done for them
47+ * on each invocation.
48+ *
49+ * We should investigate converting to a linear search, even though that
50+ * would make the command name "hash" a misnomer.
51+ */
52+
53+#include "shell.h"
54+#include "main.h"
55+#include "nodes.h"
56+#include "parser.h"
57+#include "redir.h"
58+#include "eval.h"
59+#include "exec.h"
60+#include "builtins.h"
61+#include "var.h"
62+#include "options.h"
63+#include "input.h"
64+#include "output.h"
65+#include "syntax.h"
66+#include "memalloc.h"
67+#include "error.h"
68+#include "mystring.h"
69+#include "show.h"
70+#include "jobs.h"
71+#include "alias.h"
72+
73+
74+#define CMDTABLESIZE 31 /* should be prime */
75+
76+
77+
78+struct tblentry {
79+ struct tblentry *next; /* next entry in hash chain */
80+ union param param; /* definition of builtin function */
81+ int special; /* flag for special builtin commands */
82+ signed char cmdtype; /* index identifying command */
83+ char cmdname[]; /* name of command */
84+};
85+
86+
87+static struct tblentry *cmdtable[CMDTABLESIZE];
88+static int cmdtable_cd = 0; /* cmdtable contains cd-dependent entries */
89+
90+
91+static void tryexec(char *, char **, char **);
92+static void printentry(struct tblentry *, int);
93+static struct tblentry *cmdlookup(const char *, int);
94+static void delete_cmd_entry(void);
95+static void addcmdentry(const char *, struct cmdentry *);
96+
97+
98+
99+/*
100+ * Exec a program. Never returns. If you change this routine, you may
101+ * have to change the find_command routine as well.
102+ *
103+ * The argv array may be changed and element argv[-1] should be writable.
104+ */
105+
106+void
107+shellexec(char **argv, char **envp, const char *path, int idx)
108+{
109+ char *cmdname;
110+ const char *opt;
111+ int e;
112+
113+ if (strchr(argv[0], '/') != NULL) {
114+ tryexec(argv[0], argv, envp);
115+ e = errno;
116+ } else {
117+ e = ENOENT;
118+ while ((cmdname = padvance(&path, &opt, argv[0])) != NULL) {
119+ if (--idx < 0 && opt == NULL) {
120+ tryexec(cmdname, argv, envp);
121+ if (errno != ENOENT && errno != ENOTDIR)
122+ e = errno;
123+ if (e == ENOEXEC)
124+ break;
125+ }
126+ stunalloc(cmdname);
127+ }
128+ }
129+
130+ /* Map to POSIX errors */
131+ if (e == ENOENT || e == ENOTDIR)
132+ errorwithstatus(127, "%s: not found", argv[0]);
133+ else
134+ errorwithstatus(126, "%s: %s", argv[0], strerror(e));
135+}
136+
137+
138+static int
139+isbinary(const char *data, size_t len)
140+{
141+ const char *nul, *p;
142+ int hasletter;
143+
144+ nul = memchr(data, '\0', len);
145+ if (nul == NULL)
146+ return 0;
147+ /*
148+ * POSIX says we shall allow execution if the initial part intended
149+ * to be parsed by the shell consists of characters and does not
150+ * contain the NUL character. This allows concatenating a shell
151+ * script (ending with exec or exit) and a binary payload.
152+ *
153+ * In order to reject common binary files such as PNG images, check
154+ * that there is a lowercase letter or expansion before the last
155+ * newline before the NUL character, in addition to the check for
156+ * the newline character suggested by POSIX.
157+ */
158+ hasletter = 0;
159+ for (p = data; *p != '\0'; p++) {
160+ if ((*p >= 'a' && *p <= 'z') || *p == '$' || *p == '`')
161+ hasletter = 1;
162+ if (hasletter && *p == '\n')
163+ return 0;
164+ }
165+ return 1;
166+}
167+
168+
169+static void
170+tryexec(char *cmd, char **argv, char **envp)
171+{
172+ int e, in;
173+ ssize_t n;
174+ char buf[256];
175+
176+ execve(cmd, argv, envp);
177+ e = errno;
178+ if (e == ENOEXEC) {
179+ INTOFF;
180+ in = open(cmd, O_RDONLY | O_NONBLOCK);
181+ if (in != -1) {
182+ n = pread(in, buf, sizeof buf, 0);
183+ close(in);
184+ if (n > 0 && isbinary(buf, n)) {
185+ errno = ENOEXEC;
186+ return;
187+ }
188+ }
189+ *argv = cmd;
190+ *--argv = __DECONST(char *, _PATH_BSHELL);
191+ execve(_PATH_BSHELL, argv, envp);
192+ }
193+ errno = e;
194+}
195+
196+/*
197+ * Do a path search. The variable path (passed by reference) should be
198+ * set to the start of the path before the first call; padvance will update
199+ * this value as it proceeds. Successive calls to padvance will return
200+ * the possible path expansions in sequence. If popt is not NULL, options
201+ * are processed: if an option (indicated by a percent sign) appears in
202+ * the path entry then *popt will be set to point to it; else *popt will be
203+ * set to NULL. If popt is NULL, percent signs are not special.
204+ */
205+
206+char *
207+padvance(const char **path, const char **popt, const char *name)
208+{
209+ const char *p, *start;
210+ char *q;
211+ size_t len, namelen;
212+
213+ if (*path == NULL)
214+ return NULL;
215+ start = *path;
216+ if (popt != NULL)
217+ for (p = start; *p && *p != ':' && *p != '%'; p++)
218+ ; /* nothing */
219+ else
220+ for (p = start; *p && *p != ':'; p++)
221+ ; /* nothing */
222+ namelen = strlen(name);
223+ len = p - start + namelen + 2; /* "2" is for '/' and '\0' */
224+ STARTSTACKSTR(q);
225+ CHECKSTRSPACE(len, q);
226+ if (p != start) {
227+ memcpy(q, start, p - start);
228+ q += p - start;
229+ *q++ = '/';
230+ }
231+ memcpy(q, name, namelen + 1);
232+ if (popt != NULL) {
233+ if (*p == '%') {
234+ *popt = ++p;
235+ while (*p && *p != ':') p++;
236+ } else
237+ *popt = NULL;
238+ }
239+ if (*p == ':')
240+ *path = p + 1;
241+ else
242+ *path = NULL;
243+ return stalloc(len);
244+}
245+
246+
247+
248+/*** Command hashing code ***/
249+
250+
251+int
252+hashcmd(int argc __unused, char **argv __unused)
253+{
254+ struct tblentry **pp;
255+ struct tblentry *cmdp;
256+ int c;
257+ int verbose;
258+ struct cmdentry entry;
259+ char *name;
260+ int errors;
261+
262+ errors = 0;
263+ verbose = 0;
264+ while ((c = nextopt("rv")) != '\0') {
265+ if (c == 'r') {
266+ clearcmdentry();
267+ } else if (c == 'v') {
268+ verbose++;
269+ }
270+ }
271+ if (*argptr == NULL) {
272+ for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
273+ for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
274+ if (cmdp->cmdtype == CMDNORMAL)
275+ printentry(cmdp, verbose);
276+ }
277+ }
278+ return 0;
279+ }
280+ while ((name = *argptr) != NULL) {
281+ if ((cmdp = cmdlookup(name, 0)) != NULL
282+ && cmdp->cmdtype == CMDNORMAL)
283+ delete_cmd_entry();
284+ find_command(name, &entry, DO_ERR, pathval());
285+ if (entry.cmdtype == CMDUNKNOWN)
286+ errors = 1;
287+ else if (verbose) {
288+ cmdp = cmdlookup(name, 0);
289+ if (cmdp != NULL)
290+ printentry(cmdp, verbose);
291+ else {
292+ outfmt(out2, "%s: not found\n", name);
293+ errors = 1;
294+ }
295+ flushall();
296+ }
297+ argptr++;
298+ }
299+ return errors;
300+}
301+
302+
303+static void
304+printentry(struct tblentry *cmdp, int verbose)
305+{
306+ int idx;
307+ const char *path, *opt;
308+ char *name;
309+
310+ if (cmdp->cmdtype == CMDNORMAL) {
311+ idx = cmdp->param.index;
312+ path = pathval();
313+ do {
314+ name = padvance(&path, &opt, cmdp->cmdname);
315+ stunalloc(name);
316+ } while (--idx >= 0);
317+ out1str(name);
318+ } else if (cmdp->cmdtype == CMDBUILTIN) {
319+ out1fmt("builtin %s", cmdp->cmdname);
320+ } else if (cmdp->cmdtype == CMDFUNCTION) {
321+ out1fmt("function %s", cmdp->cmdname);
322+ if (verbose) {
323+ INTOFF;
324+ name = commandtext(getfuncnode(cmdp->param.func));
325+ out1c(' ');
326+ out1str(name);
327+ ckfree(name);
328+ INTON;
329+ }
330+#ifdef DEBUG
331+ } else {
332+ error("internal error: cmdtype %d", cmdp->cmdtype);
333+#endif
334+ }
335+ out1c('\n');
336+}
337+
338+
339+
340+/*
341+ * Resolve a command name. If you change this routine, you may have to
342+ * change the shellexec routine as well.
343+ */
344+
345+void
346+find_command(const char *name, struct cmdentry *entry, int act,
347+ const char *path)
348+{
349+ struct tblentry *cmdp, loc_cmd;
350+ int idx;
351+ const char *opt;
352+ char *fullname;
353+ struct stat statb;
354+ int e;
355+ int i;
356+ int spec;
357+ int cd;
358+
359+ /* If name contains a slash, don't use the hash table */
360+ if (strchr(name, '/') != NULL) {
361+ entry->cmdtype = CMDNORMAL;
362+ entry->u.index = 0;
363+ entry->special = 0;
364+ return;
365+ }
366+
367+ cd = 0;
368+
369+ /* If name is in the table, we're done */
370+ if ((cmdp = cmdlookup(name, 0)) != NULL) {
371+ if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC)
372+ cmdp = NULL;
373+ else
374+ goto success;
375+ }
376+
377+ /* Check for builtin next */
378+ if ((i = find_builtin(name, &spec)) >= 0) {
379+ INTOFF;
380+ cmdp = cmdlookup(name, 1);
381+ if (cmdp->cmdtype == CMDFUNCTION)
382+ cmdp = &loc_cmd;
383+ cmdp->cmdtype = CMDBUILTIN;
384+ cmdp->param.index = i;
385+ cmdp->special = spec;
386+ INTON;
387+ goto success;
388+ }
389+
390+ /* We have to search path. */
391+
392+ e = ENOENT;
393+ idx = -1;
394+ for (;(fullname = padvance(&path, &opt, name)) != NULL;
395+ stunalloc(fullname)) {
396+ idx++;
397+ if (opt) {
398+ if (strncmp(opt, "func", 4) == 0) {
399+ /* handled below */
400+ } else {
401+ continue; /* ignore unimplemented options */
402+ }
403+ }
404+ if (fullname[0] != '/')
405+ cd = 1;
406+ if (stat(fullname, &statb) < 0) {
407+ if (errno != ENOENT && errno != ENOTDIR)
408+ e = errno;
409+ continue;
410+ }
411+ e = EACCES; /* if we fail, this will be the error */
412+ if (!S_ISREG(statb.st_mode))
413+ continue;
414+ if (opt) { /* this is a %func directory */
415+ readcmdfile(fullname, -1 /* verify */);
416+ if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
417+ error("%s not defined in %s", name, fullname);
418+ stunalloc(fullname);
419+ goto success;
420+ }
421+#ifdef notdef
422+ if (statb.st_uid == geteuid()) {
423+ if ((statb.st_mode & 0100) == 0)
424+ goto loop;
425+ } else if (statb.st_gid == getegid()) {
426+ if ((statb.st_mode & 010) == 0)
427+ goto loop;
428+ } else {
429+ if ((statb.st_mode & 01) == 0)
430+ goto loop;
431+ }
432+#endif
433+ TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
434+ INTOFF;
435+ stunalloc(fullname);
436+ cmdp = cmdlookup(name, 1);
437+ if (cmdp->cmdtype == CMDFUNCTION)
438+ cmdp = &loc_cmd;
439+ cmdp->cmdtype = CMDNORMAL;
440+ cmdp->param.index = idx;
441+ cmdp->special = 0;
442+ INTON;
443+ goto success;
444+ }
445+
446+ if (act & DO_ERR) {
447+ if (e == ENOENT || e == ENOTDIR)
448+ outfmt(out2, "%s: not found\n", name);
449+ else
450+ outfmt(out2, "%s: %s\n", name, strerror(e));
451+ }
452+ entry->cmdtype = CMDUNKNOWN;
453+ entry->u.index = 0;
454+ entry->special = 0;
455+ return;
456+
457+success:
458+ if (cd)
459+ cmdtable_cd = 1;
460+ entry->cmdtype = cmdp->cmdtype;
461+ entry->u = cmdp->param;
462+ entry->special = cmdp->special;
463+}
464+
465+
466+
467+/*
468+ * Search the table of builtin commands.
469+ */
470+
471+int
472+find_builtin(const char *name, int *special)
473+{
474+ const unsigned char *bp;
475+ size_t len;
476+
477+ len = strlen(name);
478+ for (bp = builtincmd ; *bp ; bp += 2 + bp[0]) {
479+ if (bp[0] == len && memcmp(bp + 2, name, len) == 0) {
480+ *special = (bp[1] & BUILTIN_SPECIAL) != 0;
481+ return bp[1] & ~BUILTIN_SPECIAL;
482+ }
483+ }
484+ return -1;
485+}
486+
487+
488+
489+/*
490+ * Called when a cd is done. If any entry in cmdtable depends on the current
491+ * directory, simply clear cmdtable completely.
492+ */
493+
494+void
495+hashcd(void)
496+{
497+ if (cmdtable_cd)
498+ clearcmdentry();
499+}
500+
501+
502+
503+/*
504+ * Called before PATH is changed. The argument is the new value of PATH;
505+ * pathval() still returns the old value at this point. Called with
506+ * interrupts off.
507+ */
508+
509+void
510+changepath(const char *newval __unused)
511+{
512+ clearcmdentry();
513+}
514+
515+
516+/*
517+ * Clear out cached utility locations.
518+ */
519+
520+void
521+clearcmdentry(void)
522+{
523+ struct tblentry **tblp;
524+ struct tblentry **pp;
525+ struct tblentry *cmdp;
526+
527+ INTOFF;
528+ for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
529+ pp = tblp;
530+ while ((cmdp = *pp) != NULL) {
531+ if (cmdp->cmdtype == CMDNORMAL) {
532+ *pp = cmdp->next;
533+ ckfree(cmdp);
534+ } else {
535+ pp = &cmdp->next;
536+ }
537+ }
538+ }
539+ cmdtable_cd = 0;
540+ INTON;
541+}
542+
543+
544+static unsigned int
545+hashname(const char *p)
546+{
547+ unsigned int hashval;
548+
549+ hashval = (unsigned char)*p << 4;
550+ while (*p)
551+ hashval += *p++;
552+
553+ return (hashval % CMDTABLESIZE);
554+}
555+
556+
557+/*
558+ * Locate a command in the command hash table. If "add" is nonzero,
559+ * add the command to the table if it is not already present. The
560+ * variable "lastcmdentry" is set to point to the address of the link
561+ * pointing to the entry, so that delete_cmd_entry can delete the
562+ * entry.
563+ */
564+
565+static struct tblentry **lastcmdentry;
566+
567+
568+static struct tblentry *
569+cmdlookup(const char *name, int add)
570+{
571+ struct tblentry *cmdp;
572+ struct tblentry **pp;
573+ size_t len;
574+
575+ pp = &cmdtable[hashname(name)];
576+ for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
577+ if (equal(cmdp->cmdname, name))
578+ break;
579+ pp = &cmdp->next;
580+ }
581+ if (add && cmdp == NULL) {
582+ INTOFF;
583+ len = strlen(name);
584+ cmdp = *pp = ckmalloc(sizeof (struct tblentry) + len + 1);
585+ cmdp->next = NULL;
586+ cmdp->cmdtype = CMDUNKNOWN;
587+ memcpy(cmdp->cmdname, name, len + 1);
588+ INTON;
589+ }
590+ lastcmdentry = pp;
591+ return cmdp;
592+}
593+
594+const void *
595+itercmd(const void *entry, struct cmdentry *result)
596+{
597+ const struct tblentry *e = entry;
598+ size_t i = 0;
599+
600+ if (e != NULL) {
601+ if (e->next != NULL) {
602+ e = e->next;
603+ goto success;
604+ }
605+ i = hashname(e->cmdname) + 1;
606+ }
607+ for (; i < CMDTABLESIZE; i++)
608+ if ((e = cmdtable[i]) != NULL)
609+ goto success;
610+
611+ return (NULL);
612+success:
613+ result->cmdtype = e->cmdtype;
614+ result->cmdname = e->cmdname;
615+
616+ return (e);
617+}
618+
619+/*
620+ * Delete the command entry returned on the last lookup.
621+ */
622+
623+static void
624+delete_cmd_entry(void)
625+{
626+ struct tblentry *cmdp;
627+
628+ INTOFF;
629+ cmdp = *lastcmdentry;
630+ *lastcmdentry = cmdp->next;
631+ ckfree(cmdp);
632+ INTON;
633+}
634+
635+
636+
637+/*
638+ * Add a new command entry, replacing any existing command entry for
639+ * the same name.
640+ */
641+
642+static void
643+addcmdentry(const char *name, struct cmdentry *entry)
644+{
645+ struct tblentry *cmdp;
646+
647+ INTOFF;
648+ cmdp = cmdlookup(name, 1);
649+ if (cmdp->cmdtype == CMDFUNCTION) {
650+ unreffunc(cmdp->param.func);
651+ }
652+ cmdp->cmdtype = entry->cmdtype;
653+ cmdp->param = entry->u;
654+ cmdp->special = entry->special;
655+ INTON;
656+}
657+
658+
659+/*
660+ * Define a shell function.
661+ */
662+
663+void
664+defun(const char *name, union node *func)
665+{
666+ struct cmdentry entry;
667+
668+ INTOFF;
669+ entry.cmdtype = CMDFUNCTION;
670+ entry.u.func = copyfunc(func);
671+ entry.special = 0;
672+ addcmdentry(name, &entry);
673+ INTON;
674+}
675+
676+
677+/*
678+ * Delete a function if it exists.
679+ * Called with interrupts off.
680+ */
681+
682+int
683+unsetfunc(const char *name)
684+{
685+ struct tblentry *cmdp;
686+
687+ if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
688+ unreffunc(cmdp->param.func);
689+ delete_cmd_entry();
690+ return (0);
691+ }
692+ return (0);
693+}
694+
695+
696+/*
697+ * Check if a function by a certain name exists.
698+ */
699+int
700+isfunc(const char *name)
701+{
702+ struct tblentry *cmdp;
703+ cmdp = cmdlookup(name, 0);
704+ return (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION);
705+}
706+
707+
708+static void
709+print_absolute_path(const char *name)
710+{
711+ const char *pwd;
712+
713+ if (*name != '/' && (pwd = lookupvar("PWD")) != NULL && *pwd != '\0') {
714+ out1str(pwd);
715+ if (strcmp(pwd, "/") != 0)
716+ outcslow('/', out1);
717+ }
718+ out1str(name);
719+ outcslow('\n', out1);
720+}
721+
722+
723+/*
724+ * Shared code for the following builtin commands:
725+ * type, command -v, command -V
726+ */
727+
728+int
729+typecmd_impl(int argc, char **argv, int cmd, const char *path)
730+{
731+ struct cmdentry entry;
732+ struct tblentry *cmdp;
733+ const char *const *pp;
734+ struct alias *ap;
735+ int i;
736+ int error1 = 0;
737+
738+ if (path != pathval())
739+ clearcmdentry();
740+
741+ for (i = 1; i < argc; i++) {
742+ /* First look at the keywords */
743+ for (pp = parsekwd; *pp; pp++)
744+ if (**pp == *argv[i] && equal(*pp, argv[i]))
745+ break;
746+
747+ if (*pp) {
748+ if (cmd == TYPECMD_SMALLV)
749+ out1fmt("%s\n", argv[i]);
750+ else
751+ out1fmt("%s is a shell keyword\n", argv[i]);
752+ continue;
753+ }
754+
755+ /* Then look at the aliases */
756+ if ((ap = lookupalias(argv[i], 1)) != NULL) {
757+ if (cmd == TYPECMD_SMALLV) {
758+ out1fmt("alias %s=", argv[i]);
759+ out1qstr(ap->val);
760+ outcslow('\n', out1);
761+ } else
762+ out1fmt("%s is an alias for %s\n", argv[i],
763+ ap->val);
764+ continue;
765+ }
766+
767+ /* Then check if it is a tracked alias */
768+ if ((cmdp = cmdlookup(argv[i], 0)) != NULL) {
769+ entry.cmdtype = cmdp->cmdtype;
770+ entry.u = cmdp->param;
771+ entry.special = cmdp->special;
772+ }
773+ else {
774+ /* Finally use brute force */
775+ find_command(argv[i], &entry, 0, path);
776+ }
777+
778+ switch (entry.cmdtype) {
779+ case CMDNORMAL: {
780+ if (strchr(argv[i], '/') == NULL) {
781+ const char *path2 = path;
782+ const char *opt2;
783+ char *name;
784+ int j = entry.u.index;
785+ do {
786+ name = padvance(&path2, &opt2, argv[i]);
787+ stunalloc(name);
788+ } while (--j >= 0);
789+ if (cmd != TYPECMD_SMALLV)
790+ out1fmt("%s is%s ", argv[i],
791+ (cmdp && cmd == TYPECMD_TYPE) ?
792+ " a tracked alias for" : "");
793+ print_absolute_path(name);
794+ } else {
795+ if (eaccess(argv[i], X_OK) == 0) {
796+ if (cmd != TYPECMD_SMALLV)
797+ out1fmt("%s is ", argv[i]);
798+ print_absolute_path(argv[i]);
799+ } else {
800+ if (cmd != TYPECMD_SMALLV)
801+ outfmt(out2, "%s: %s\n",
802+ argv[i], strerror(errno));
803+ error1 |= 127;
804+ }
805+ }
806+ break;
807+ }
808+ case CMDFUNCTION:
809+ if (cmd == TYPECMD_SMALLV)
810+ out1fmt("%s\n", argv[i]);
811+ else
812+ out1fmt("%s is a shell function\n", argv[i]);
813+ break;
814+
815+ case CMDBUILTIN:
816+ if (cmd == TYPECMD_SMALLV)
817+ out1fmt("%s\n", argv[i]);
818+ else if (entry.special)
819+ out1fmt("%s is a special shell builtin\n",
820+ argv[i]);
821+ else
822+ out1fmt("%s is a shell builtin\n", argv[i]);
823+ break;
824+
825+ default:
826+ if (cmd != TYPECMD_SMALLV)
827+ outfmt(out2, "%s: not found\n", argv[i]);
828+ error1 |= 127;
829+ break;
830+ }
831+ }
832+
833+ if (path != pathval())
834+ clearcmdentry();
835+
836+ return error1;
837+}
838+
839+/*
840+ * Locate and print what a word is...
841+ */
842+
843+int
844+typecmd(int argc, char **argv)
845+{
846+ if (argc > 2 && strcmp(argv[1], "--") == 0)
847+ argc--, argv++;
848+ return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1));
849+}
+75,
-0
1@@ -0,0 +1,75 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+/* values of cmdtype */
37+#define CMDUNKNOWN -1 /* no entry in table for command */
38+#define CMDNORMAL 0 /* command is an executable program */
39+#define CMDBUILTIN 1 /* command is a shell builtin */
40+#define CMDFUNCTION 2 /* command is a shell function */
41+
42+/* values for typecmd_impl's third parameter */
43+enum {
44+ TYPECMD_SMALLV, /* command -v */
45+ TYPECMD_BIGV, /* command -V */
46+ TYPECMD_TYPE /* type */
47+};
48+
49+union node;
50+struct cmdentry {
51+ int cmdtype;
52+ union param {
53+ int index;
54+ struct funcdef *func;
55+ } u;
56+ int special;
57+ const char *cmdname;
58+};
59+
60+
61+/* action to find_command() */
62+#define DO_ERR 0x01 /* prints errors */
63+#define DO_NOFUNC 0x02 /* don't return shell functions, for command */
64+
65+void shellexec(char **, char **, const char *, int) __dead2;
66+char *padvance(const char **, const char **, const char *);
67+void find_command(const char *, struct cmdentry *, int, const char *);
68+int find_builtin(const char *, int *);
69+void hashcd(void);
70+void changepath(const char *);
71+void defun(const char *, union node *);
72+int unsetfunc(const char *);
73+int isfunc(const char *);
74+int typecmd_impl(int, char **, int, const char *);
75+void clearcmdentry(void);
76+const void *itercmd(const void *, struct cmdentry *);
+1547,
-0
1@@ -0,0 +1,1547 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ * Copyright (c) 1997-2005
8+ * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved.
9+ * Copyright (c) 2010-2015
10+ * Jilles Tjoelker <jilles@stack.nl>. All rights reserved.
11+ *
12+ * This code is derived from software contributed to Berkeley by
13+ * Kenneth Almquist.
14+ *
15+ * Redistribution and use in source and binary forms, with or without
16+ * modification, are permitted provided that the following conditions
17+ * are met:
18+ * 1. Redistributions of source code must retain the above copyright
19+ * notice, this list of conditions and the following disclaimer.
20+ * 2. Redistributions in binary form must reproduce the above copyright
21+ * notice, this list of conditions and the following disclaimer in the
22+ * documentation and/or other materials provided with the distribution.
23+ * 3. Neither the name of the University nor the names of its contributors
24+ * may be used to endorse or promote products derived from this software
25+ * without specific prior written permission.
26+ *
27+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37+ * SUCH DAMAGE.
38+ */
39+
40+#include <sys/types.h>
41+#include <sys/time.h>
42+#include <sys/stat.h>
43+#include <dirent.h>
44+#include <errno.h>
45+#include <inttypes.h>
46+#include <limits.h>
47+#include <pwd.h>
48+#include <stdio.h>
49+#include <stdlib.h>
50+#include <string.h>
51+#include <unistd.h>
52+#include <wchar.h>
53+#include <wctype.h>
54+
55+/*
56+ * Routines to expand arguments to commands. We have to deal with
57+ * backquotes, shell variables, and file metacharacters.
58+ */
59+
60+#include "shell.h"
61+#include "main.h"
62+#include "nodes.h"
63+#include "eval.h"
64+#include "expand.h"
65+#include "syntax.h"
66+#include "parser.h"
67+#include "jobs.h"
68+#include "options.h"
69+#include "var.h"
70+#include "input.h"
71+#include "output.h"
72+#include "memalloc.h"
73+#include "error.h"
74+#include "mystring.h"
75+#include "arith.h"
76+#include "show.h"
77+#include "builtins.h"
78+
79+enum wordstate { WORD_IDLE, WORD_WS_DELIMITED, WORD_QUOTEMARK };
80+
81+struct worddest {
82+ struct arglist *list;
83+ enum wordstate state;
84+};
85+
86+static char *expdest; /* output of current string */
87+
88+static const char *argstr(const char *, struct nodelist **restrict, int,
89+ struct worddest *);
90+static const char *exptilde(const char *, int);
91+static const char *expari(const char *, struct nodelist **restrict, int,
92+ struct worddest *);
93+static void expbackq(union node *, int, int, struct worddest *);
94+static const char *subevalvar_trim(const char *, struct nodelist **restrict,
95+ int, int, int);
96+static const char *subevalvar_misc(const char *, struct nodelist **restrict,
97+ const char *, int, int, int);
98+static const char *evalvar(const char *, struct nodelist **restrict, int,
99+ struct worddest *);
100+static int varisset(const char *, int);
101+static void strtodest(const char *, int, int, int, struct worddest *);
102+static void reprocess(int, int, int, int, struct worddest *);
103+static void varvalue(const char *, int, int, int, struct worddest *);
104+static void expandmeta(char *, struct arglist *);
105+static void expmeta(char *, char *, struct arglist *);
106+static int expsortcmp(const void *, const void *);
107+static int patmatch(const char *, const char *);
108+static void cvtnum(int, char *);
109+static int collate_range_cmp(wchar_t, wchar_t);
110+
111+void
112+emptyarglist(struct arglist *list)
113+{
114+
115+ list->args = list->smallarg;
116+ list->count = 0;
117+ list->capacity = sizeof(list->smallarg) / sizeof(list->smallarg[0]);
118+}
119+
120+void
121+appendarglist(struct arglist *list, char *str)
122+{
123+ char **newargs;
124+ int newcapacity;
125+
126+ if (list->count >= list->capacity) {
127+ newcapacity = list->capacity * 2;
128+ if (newcapacity < 16)
129+ newcapacity = 16;
130+ if (newcapacity > INT_MAX / (int)sizeof(newargs[0]))
131+ error("Too many entries in arglist");
132+ newargs = stalloc(newcapacity * sizeof(newargs[0]));
133+ memcpy(newargs, list->args, list->count * sizeof(newargs[0]));
134+ list->args = newargs;
135+ list->capacity = newcapacity;
136+ }
137+ list->args[list->count++] = str;
138+}
139+
140+static int
141+collate_range_cmp(wchar_t c1, wchar_t c2)
142+{
143+ wchar_t s1[2], s2[2];
144+
145+ s1[0] = c1;
146+ s1[1] = L'\0';
147+ s2[0] = c2;
148+ s2[1] = L'\0';
149+ return (wcscoll(s1, s2));
150+}
151+
152+static char *
153+stputs_quotes(const char *data, const char *syntax, char *p)
154+{
155+ while (*data) {
156+ CHECKSTRSPACE(2, p);
157+ if (syntax[(int)*data] == CCTL)
158+ USTPUTC(CTLESC, p);
159+ USTPUTC(*data++, p);
160+ }
161+ return (p);
162+}
163+#define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p)
164+
165+static char *
166+nextword(char c, int flag, char *p, struct worddest *dst)
167+{
168+ int is_ws;
169+
170+ is_ws = c == '\t' || c == '\n' || c == ' ';
171+ if (p != stackblock() || (is_ws ? dst->state == WORD_QUOTEMARK :
172+ dst->state != WORD_WS_DELIMITED) || c == '\0') {
173+ STPUTC('\0', p);
174+ if (flag & EXP_GLOB)
175+ expandmeta(grabstackstr(p), dst->list);
176+ else
177+ appendarglist(dst->list, grabstackstr(p));
178+ dst->state = is_ws ? WORD_WS_DELIMITED : WORD_IDLE;
179+ } else if (!is_ws && dst->state == WORD_WS_DELIMITED)
180+ dst->state = WORD_IDLE;
181+ /* Reserve space while the stack string is empty. */
182+ appendarglist(dst->list, NULL);
183+ dst->list->count--;
184+ STARTSTACKSTR(p);
185+ return p;
186+}
187+#define NEXTWORD(c, flag, p, dstlist) p = nextword(c, flag, p, dstlist)
188+
189+static char *
190+stputs_split(const char *data, const char *syntax, int flag, char *p,
191+ struct worddest *dst)
192+{
193+ const char *ifs;
194+ char c;
195+
196+ ifs = ifsset() ? ifsval() : " \t\n";
197+ while (*data) {
198+ CHECKSTRSPACE(2, p);
199+ c = *data++;
200+ if (strchr(ifs, c) != NULL) {
201+ NEXTWORD(c, flag, p, dst);
202+ continue;
203+ }
204+ if (flag & EXP_GLOB && syntax[(int)c] == CCTL)
205+ USTPUTC(CTLESC, p);
206+ USTPUTC(c, p);
207+ }
208+ return (p);
209+}
210+#define STPUTS_SPLIT(data, syntax, flag, p, dst) p = stputs_split((data), syntax, flag, p, dst)
211+
212+/*
213+ * Perform expansions on an argument, placing the resulting list of arguments
214+ * in arglist. Parameter expansion, command substitution and arithmetic
215+ * expansion are always performed; additional expansions can be requested
216+ * via flag (EXP_*).
217+ * The result is left in the stack string.
218+ * When arglist is NULL, perform here document expansion.
219+ *
220+ * When doing something that may cause this to be re-entered, make sure
221+ * the stack string is empty via grabstackstr() and do not assume expdest
222+ * remains valid.
223+ */
224+void
225+expandarg(union node *arg, struct arglist *arglist, int flag)
226+{
227+ struct worddest exparg;
228+ struct nodelist *argbackq;
229+
230+ if (fflag)
231+ flag &= ~EXP_GLOB;
232+ argbackq = arg->narg.backquote;
233+ exparg.list = arglist;
234+ exparg.state = WORD_IDLE;
235+ STARTSTACKSTR(expdest);
236+ argstr(arg->narg.text, &argbackq, flag, &exparg);
237+ if (arglist == NULL) {
238+ STACKSTRNUL(expdest);
239+ return; /* here document expanded */
240+ }
241+ if ((flag & EXP_SPLIT) == 0 || expdest != stackblock() ||
242+ exparg.state == WORD_QUOTEMARK) {
243+ STPUTC('\0', expdest);
244+ if (flag & EXP_SPLIT) {
245+ if (flag & EXP_GLOB)
246+ expandmeta(grabstackstr(expdest), exparg.list);
247+ else
248+ appendarglist(exparg.list, grabstackstr(expdest));
249+ }
250+ }
251+ if ((flag & EXP_SPLIT) == 0)
252+ appendarglist(arglist, grabstackstr(expdest));
253+}
254+
255+
256+
257+/*
258+ * Perform parameter expansion, command substitution and arithmetic
259+ * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE.
260+ * Processing ends at a CTLENDVAR or CTLENDARI character as well as '\0'.
261+ * This is used to expand word in ${var+word} etc.
262+ * If EXP_GLOB or EXP_CASE are set, keep and/or generate CTLESC
263+ * characters to allow for further processing.
264+ *
265+ * If EXP_SPLIT is set, dst receives any complete words produced.
266+ */
267+static const char *
268+argstr(const char *p, struct nodelist **restrict argbackq, int flag,
269+ struct worddest *dst)
270+{
271+ char c;
272+ int quotes = flag & (EXP_GLOB | EXP_CASE); /* do CTLESC */
273+ int firsteq = 1;
274+ int split_lit;
275+ int lit_quoted;
276+
277+ split_lit = flag & EXP_SPLIT_LIT;
278+ lit_quoted = flag & EXP_LIT_QUOTED;
279+ flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED);
280+ if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
281+ p = exptilde(p, flag);
282+ for (;;) {
283+ CHECKSTRSPACE(2, expdest);
284+ switch (c = *p++) {
285+ case '\0':
286+ return (p - 1);
287+ case CTLENDVAR:
288+ case CTLENDARI:
289+ return (p);
290+ case CTLQUOTEMARK:
291+ lit_quoted = 1;
292+ /* "$@" syntax adherence hack */
293+ if (p[0] == CTLVAR && (p[1] & VSQUOTE) != 0 &&
294+ p[2] == '@' && p[3] == '=')
295+ break;
296+ if ((flag & EXP_SPLIT) != 0 && expdest == stackblock())
297+ dst->state = WORD_QUOTEMARK;
298+ break;
299+ case CTLQUOTEEND:
300+ lit_quoted = 0;
301+ break;
302+ case CTLESC:
303+ c = *p++;
304+ if (split_lit && !lit_quoted &&
305+ strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) {
306+ NEXTWORD(c, flag, expdest, dst);
307+ break;
308+ }
309+ if (quotes)
310+ USTPUTC(CTLESC, expdest);
311+ USTPUTC(c, expdest);
312+ break;
313+ case CTLVAR:
314+ p = evalvar(p, argbackq, flag, dst);
315+ break;
316+ case CTLBACKQ:
317+ case CTLBACKQ|CTLQUOTE:
318+ expbackq((*argbackq)->n, c & CTLQUOTE, flag, dst);
319+ *argbackq = (*argbackq)->next;
320+ break;
321+ case CTLARI:
322+ p = expari(p, argbackq, flag, dst);
323+ break;
324+ case ':':
325+ case '=':
326+ /*
327+ * sort of a hack - expand tildes in variable
328+ * assignments (after the first '=' and after ':'s).
329+ */
330+ if (split_lit && !lit_quoted &&
331+ strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) {
332+ NEXTWORD(c, flag, expdest, dst);
333+ break;
334+ }
335+ USTPUTC(c, expdest);
336+ if (flag & EXP_VARTILDE && *p == '~' &&
337+ (c != '=' || firsteq)) {
338+ if (c == '=')
339+ firsteq = 0;
340+ p = exptilde(p, flag);
341+ }
342+ break;
343+ default:
344+ if (split_lit && !lit_quoted &&
345+ strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) {
346+ NEXTWORD(c, flag, expdest, dst);
347+ break;
348+ }
349+ USTPUTC(c, expdest);
350+ }
351+ }
352+}
353+
354+/*
355+ * Perform tilde expansion, placing the result in the stack string and
356+ * returning the next position in the input string to process.
357+ */
358+static const char *
359+exptilde(const char *p, int flag)
360+{
361+ char c;
362+ const char *startp = p;
363+ const char *user;
364+ struct passwd *pw;
365+ char *home;
366+ int len;
367+
368+ for (;;) {
369+ c = *p;
370+ switch(c) {
371+ case CTLESC: /* This means CTL* are always considered quoted. */
372+ case CTLVAR:
373+ case CTLBACKQ:
374+ case CTLBACKQ | CTLQUOTE:
375+ case CTLARI:
376+ case CTLENDARI:
377+ case CTLQUOTEMARK:
378+ return (startp);
379+ case ':':
380+ if ((flag & EXP_VARTILDE) == 0)
381+ break;
382+ /* FALLTHROUGH */
383+ case '\0':
384+ case '/':
385+ case CTLENDVAR:
386+ len = p - startp - 1;
387+ STPUTBIN(startp + 1, len, expdest);
388+ STACKSTRNUL(expdest);
389+ user = expdest - len;
390+ if (*user == '\0') {
391+ home = lookupvar("HOME");
392+ } else {
393+ pw = getpwnam(user);
394+ home = pw != NULL ? pw->pw_dir : NULL;
395+ }
396+ STADJUST(-len, expdest);
397+ if (home == NULL || *home == '\0')
398+ return (startp);
399+ strtodest(home, flag, VSNORMAL, 1, NULL);
400+ return (p);
401+ }
402+ p++;
403+ }
404+}
405+
406+
407+/*
408+ * Expand arithmetic expression.
409+ */
410+static const char *
411+expari(const char *p, struct nodelist **restrict argbackq, int flag,
412+ struct worddest *dst)
413+{
414+ char *q, *start;
415+ arith_t result;
416+ int begoff;
417+ int quoted;
418+ int adj;
419+
420+ quoted = *p++ == '"';
421+ begoff = expdest - stackblock();
422+ p = argstr(p, argbackq, 0, NULL);
423+ STPUTC('\0', expdest);
424+ start = stackblock() + begoff;
425+
426+ q = grabstackstr(expdest);
427+ result = arith(start);
428+ ungrabstackstr(q, expdest);
429+
430+ start = stackblock() + begoff;
431+ adj = start - expdest;
432+ STADJUST(adj, expdest);
433+
434+ CHECKSTRSPACE((int)(DIGITS(result) + 1), expdest);
435+ fmtstr(expdest, DIGITS(result), ARITH_FORMAT_STR, result);
436+ adj = strlen(expdest);
437+ STADJUST(adj, expdest);
438+ /*
439+ * If this is quoted, a '-' must not indicate a range in [...].
440+ * If this is not quoted, splitting may occur.
441+ */
442+ if (quoted ?
443+ result < 0 && begoff > 1 && flag & (EXP_GLOB | EXP_CASE) :
444+ flag & EXP_SPLIT)
445+ reprocess(expdest - adj - stackblock(), flag, VSNORMAL, quoted,
446+ dst);
447+ return p;
448+}
449+
450+
451+/*
452+ * Perform command substitution.
453+ */
454+static void
455+expbackq(union node *cmd, int quoted, int flag, struct worddest *dst)
456+{
457+ struct backcmd in;
458+ int i;
459+ char buf[128];
460+ char *p;
461+ char *dest = expdest;
462+ char lastc;
463+ char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
464+ int quotes = flag & (EXP_GLOB | EXP_CASE);
465+ size_t nnl;
466+ const char *ifs;
467+ int startloc;
468+
469+ INTOFF;
470+ p = grabstackstr(dest);
471+ evalbackcmd(cmd, &in);
472+ ungrabstackstr(p, dest);
473+
474+ p = in.buf;
475+ startloc = dest - stackblock();
476+ nnl = 0;
477+ if (!quoted && flag & EXP_SPLIT)
478+ ifs = ifsset() ? ifsval() : " \t\n";
479+ else
480+ ifs = "";
481+ /* Remove trailing newlines */
482+ for (;;) {
483+ if (--in.nleft < 0) {
484+ if (in.fd < 0)
485+ break;
486+ while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR)
487+ ;
488+ TRACE(("expbackq: read returns %d\n", i));
489+ if (i <= 0)
490+ break;
491+ p = buf;
492+ in.nleft = i - 1;
493+ }
494+ lastc = *p++;
495+ if (lastc == '\0')
496+ continue;
497+ if (nnl > 0 && lastc != '\n') {
498+ NEXTWORD('\n', flag, dest, dst);
499+ nnl = 0;
500+ }
501+ if (strchr(ifs, lastc) != NULL) {
502+ if (lastc == '\n')
503+ nnl++;
504+ else
505+ NEXTWORD(lastc, flag, dest, dst);
506+ } else {
507+ CHECKSTRSPACE(2, dest);
508+ if (quotes && syntax[(int)lastc] == CCTL)
509+ USTPUTC(CTLESC, dest);
510+ USTPUTC(lastc, dest);
511+ }
512+ }
513+ while (dest > stackblock() + startloc && STTOPC(dest) == '\n')
514+ STUNPUTC(dest);
515+
516+ if (in.fd >= 0)
517+ close(in.fd);
518+ if (in.buf)
519+ ckfree(in.buf);
520+ if (in.jp) {
521+ p = grabstackstr(dest);
522+ exitstatus = waitforjob(in.jp, (int *)NULL);
523+ ungrabstackstr(p, dest);
524+ }
525+ TRACE(("expbackq: done\n"));
526+ expdest = dest;
527+ INTON;
528+}
529+
530+
531+
532+static void
533+recordleft(const char *str, const char *loc, char *startp)
534+{
535+ int amount;
536+
537+ amount = ((str - 1) - (loc - startp)) - expdest;
538+ STADJUST(amount, expdest);
539+ while (loc != str - 1)
540+ *startp++ = *loc++;
541+}
542+
543+static const char *
544+subevalvar_trim(const char *p, struct nodelist **restrict argbackq, int strloc,
545+ int subtype, int startloc)
546+{
547+ char *startp;
548+ char *loc = NULL;
549+ char *str;
550+ int c = 0;
551+ int amount;
552+
553+ p = argstr(p, argbackq, EXP_CASE | EXP_TILDE, NULL);
554+ STACKSTRNUL(expdest);
555+ startp = stackblock() + startloc;
556+ str = stackblock() + strloc;
557+
558+ switch (subtype) {
559+ case VSTRIMLEFT:
560+ for (loc = startp; loc < str; loc++) {
561+ c = *loc;
562+ *loc = '\0';
563+ if (patmatch(str, startp)) {
564+ *loc = c;
565+ recordleft(str, loc, startp);
566+ return p;
567+ }
568+ *loc = c;
569+ }
570+ break;
571+
572+ case VSTRIMLEFTMAX:
573+ for (loc = str - 1; loc >= startp;) {
574+ c = *loc;
575+ *loc = '\0';
576+ if (patmatch(str, startp)) {
577+ *loc = c;
578+ recordleft(str, loc, startp);
579+ return p;
580+ }
581+ *loc = c;
582+ loc--;
583+ }
584+ break;
585+
586+ case VSTRIMRIGHT:
587+ for (loc = str - 1; loc >= startp;) {
588+ if (patmatch(str, loc)) {
589+ amount = loc - expdest;
590+ STADJUST(amount, expdest);
591+ return p;
592+ }
593+ loc--;
594+ }
595+ break;
596+
597+ case VSTRIMRIGHTMAX:
598+ for (loc = startp; loc < str - 1; loc++) {
599+ if (patmatch(str, loc)) {
600+ amount = loc - expdest;
601+ STADJUST(amount, expdest);
602+ return p;
603+ }
604+ }
605+ break;
606+
607+
608+ default:
609+ abort();
610+ }
611+ amount = (expdest - stackblock() - strloc) + 1;
612+ STADJUST(-amount, expdest);
613+ return p;
614+}
615+
616+
617+static const char *
618+subevalvar_misc(const char *p, struct nodelist **restrict argbackq,
619+ const char *var, int subtype, int startloc, int varflags)
620+{
621+ const char *end;
622+ char *startp;
623+ int amount;
624+
625+ end = argstr(p, argbackq, EXP_TILDE, NULL);
626+ STACKSTRNUL(expdest);
627+ startp = stackblock() + startloc;
628+
629+ switch (subtype) {
630+ case VSASSIGN:
631+ setvar(var, startp, 0);
632+ amount = startp - expdest;
633+ STADJUST(amount, expdest);
634+ return end;
635+
636+ case VSQUESTION:
637+ if (*p != CTLENDVAR) {
638+ outfmt(out2, "%s\n", startp);
639+ error((char *)NULL);
640+ }
641+ error("%.*s: parameter %snot set", (int)(p - var - 1),
642+ var, (varflags & VSNUL) ? "null or " : "");
643+
644+ default:
645+ abort();
646+ }
647+}
648+
649+
650+/*
651+ * Expand a variable, and return a pointer to the next character in the
652+ * input string.
653+ */
654+
655+static const char *
656+evalvar(const char *p, struct nodelist **restrict argbackq, int flag,
657+ struct worddest *dst)
658+{
659+ int subtype;
660+ int varflags;
661+ const char *var;
662+ const char *val;
663+ int patloc;
664+ int c;
665+ int set;
666+ int special;
667+ int startloc;
668+ int varlen;
669+ int varlenb;
670+ char buf[21];
671+
672+ varflags = (unsigned char)*p++;
673+ subtype = varflags & VSTYPE;
674+ var = p;
675+ special = 0;
676+ if (! is_name(*p))
677+ special = 1;
678+ p = strchr(p, '=') + 1;
679+ if (varflags & VSLINENO) {
680+ set = 1;
681+ special = 1;
682+ val = NULL;
683+ } else if (special) {
684+ set = varisset(var, varflags & VSNUL);
685+ val = NULL;
686+ } else {
687+ val = bltinlookup(var, 1);
688+ if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
689+ val = NULL;
690+ set = 0;
691+ } else
692+ set = 1;
693+ }
694+ varlen = 0;
695+ startloc = expdest - stackblock();
696+ if (!set && uflag && *var != '@' && *var != '*') {
697+ switch (subtype) {
698+ case VSNORMAL:
699+ case VSTRIMLEFT:
700+ case VSTRIMLEFTMAX:
701+ case VSTRIMRIGHT:
702+ case VSTRIMRIGHTMAX:
703+ case VSLENGTH:
704+ error("%.*s: parameter not set", (int)(p - var - 1),
705+ var);
706+ }
707+ }
708+ if (set && subtype != VSPLUS) {
709+ /* insert the value of the variable */
710+ if (special) {
711+ if (varflags & VSLINENO) {
712+ if (p - var > (ptrdiff_t)sizeof(buf))
713+ abort();
714+ memcpy(buf, var, p - var - 1);
715+ buf[p - var - 1] = '\0';
716+ strtodest(buf, flag, subtype,
717+ varflags & VSQUOTE, dst);
718+ } else
719+ varvalue(var, varflags & VSQUOTE, subtype, flag,
720+ dst);
721+ if (subtype == VSLENGTH) {
722+ varlenb = expdest - stackblock() - startloc;
723+ varlen = varlenb;
724+ if (localeisutf8) {
725+ val = stackblock() + startloc;
726+ for (;val != expdest; val++)
727+ if ((*val & 0xC0) == 0x80)
728+ varlen--;
729+ }
730+ STADJUST(-varlenb, expdest);
731+ }
732+ } else {
733+ if (subtype == VSLENGTH) {
734+ for (;*val; val++)
735+ if (!localeisutf8 ||
736+ (*val & 0xC0) != 0x80)
737+ varlen++;
738+ }
739+ else
740+ strtodest(val, flag, subtype,
741+ varflags & VSQUOTE, dst);
742+ }
743+ }
744+
745+ if (subtype == VSPLUS)
746+ set = ! set;
747+
748+ switch (subtype) {
749+ case VSLENGTH:
750+ cvtnum(varlen, buf);
751+ strtodest(buf, flag, VSNORMAL, varflags & VSQUOTE, dst);
752+ break;
753+
754+ case VSNORMAL:
755+ return p;
756+
757+ case VSPLUS:
758+ case VSMINUS:
759+ if (!set) {
760+ return argstr(p, argbackq,
761+ flag | (flag & EXP_SPLIT ? EXP_SPLIT_LIT : 0) |
762+ (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0), dst);
763+ }
764+ break;
765+
766+ case VSTRIMLEFT:
767+ case VSTRIMLEFTMAX:
768+ case VSTRIMRIGHT:
769+ case VSTRIMRIGHTMAX:
770+ if (!set)
771+ break;
772+ /*
773+ * Terminate the string and start recording the pattern
774+ * right after it
775+ */
776+ STPUTC('\0', expdest);
777+ patloc = expdest - stackblock();
778+ p = subevalvar_trim(p, argbackq, patloc, subtype, startloc);
779+ reprocess(startloc, flag, VSNORMAL, varflags & VSQUOTE, dst);
780+ if (flag & EXP_SPLIT && *var == '@' && varflags & VSQUOTE)
781+ dst->state = WORD_QUOTEMARK;
782+ return p;
783+
784+ case VSASSIGN:
785+ case VSQUESTION:
786+ if (!set) {
787+ p = subevalvar_misc(p, argbackq, var, subtype,
788+ startloc, varflags);
789+ /* assert(subtype == VSASSIGN); */
790+ val = lookupvar(var);
791+ strtodest(val, flag, subtype, varflags & VSQUOTE, dst);
792+ return p;
793+ }
794+ break;
795+
796+ case VSERROR:
797+ c = p - var - 1;
798+ error("${%.*s%s}: Bad substitution", c, var,
799+ (c > 0 && *p != CTLENDVAR) ? "..." : "");
800+
801+ default:
802+ abort();
803+ }
804+
805+ { /* skip to end of alternative */
806+ int nesting = 1;
807+ for (;;) {
808+ if ((c = *p++) == CTLESC)
809+ p++;
810+ else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE))
811+ *argbackq = (*argbackq)->next;
812+ else if (c == CTLVAR) {
813+ if ((*p++ & VSTYPE) != VSNORMAL)
814+ nesting++;
815+ } else if (c == CTLENDVAR) {
816+ if (--nesting == 0)
817+ break;
818+ }
819+ }
820+ }
821+ return p;
822+}
823+
824+
825+
826+/*
827+ * Test whether a special or positional parameter is set.
828+ */
829+
830+static int
831+varisset(const char *name, int nulok)
832+{
833+
834+ if (*name == '!')
835+ return backgndpidset();
836+ else if (*name == '@' || *name == '*') {
837+ if (*shellparam.p == NULL)
838+ return 0;
839+
840+ if (nulok) {
841+ char **av;
842+
843+ for (av = shellparam.p; *av; av++)
844+ if (**av != '\0')
845+ return 1;
846+ return 0;
847+ }
848+ } else if (is_digit(*name)) {
849+ char *ap;
850+ long num;
851+
852+ errno = 0;
853+ num = strtol(name, NULL, 10);
854+ if (errno != 0 || num > shellparam.nparam)
855+ return 0;
856+
857+ if (num == 0)
858+ ap = arg0;
859+ else
860+ ap = shellparam.p[num - 1];
861+
862+ if (nulok && (ap == NULL || *ap == '\0'))
863+ return 0;
864+ }
865+ return 1;
866+}
867+
868+static void
869+strtodest(const char *p, int flag, int subtype, int quoted,
870+ struct worddest *dst)
871+{
872+ if (subtype == VSLENGTH || subtype == VSTRIMLEFT ||
873+ subtype == VSTRIMLEFTMAX || subtype == VSTRIMRIGHT ||
874+ subtype == VSTRIMRIGHTMAX)
875+ STPUTS(p, expdest);
876+ else if (flag & EXP_SPLIT && !quoted && dst != NULL)
877+ STPUTS_SPLIT(p, BASESYNTAX, flag, expdest, dst);
878+ else if (flag & (EXP_GLOB | EXP_CASE))
879+ STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest);
880+ else
881+ STPUTS(p, expdest);
882+}
883+
884+static void
885+reprocess(int startloc, int flag, int subtype, int quoted,
886+ struct worddest *dst)
887+{
888+ static char *buf = NULL;
889+ static size_t buflen = 0;
890+ char *startp;
891+ size_t len, zpos, zlen;
892+
893+ startp = stackblock() + startloc;
894+ len = expdest - startp;
895+ if (len >= SIZE_MAX / 2 || len > PTRDIFF_MAX)
896+ abort();
897+ INTOFF;
898+ if (len >= buflen) {
899+ ckfree(buf);
900+ buf = NULL;
901+ }
902+ if (buflen < 128)
903+ buflen = 128;
904+ while (len >= buflen)
905+ buflen <<= 1;
906+ if (buf == NULL)
907+ buf = ckmalloc(buflen);
908+ INTON;
909+ memcpy(buf, startp, len);
910+ buf[len] = '\0';
911+ STADJUST(-(ptrdiff_t)len, expdest);
912+ for (zpos = 0;;) {
913+ zlen = strlen(buf + zpos);
914+ strtodest(buf + zpos, flag, subtype, quoted, dst);
915+ zpos += zlen + 1;
916+ if (zpos == len + 1)
917+ break;
918+ if (flag & EXP_SPLIT && (quoted || (zlen > 0 && zpos < len)))
919+ NEXTWORD('\0', flag, expdest, dst);
920+ }
921+}
922+
923+/*
924+ * Add the value of a special or positional parameter to the stack string.
925+ */
926+
927+static void
928+varvalue(const char *name, int quoted, int subtype, int flag,
929+ struct worddest *dst)
930+{
931+ int num;
932+ char *p;
933+ int i;
934+ int splitlater;
935+ char sep[2];
936+ char **ap;
937+ char buf[(NSHORTOPTS > 10 ? NSHORTOPTS : 10) + 1];
938+
939+ if (subtype == VSLENGTH)
940+ flag &= ~EXP_FULL;
941+ splitlater = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX ||
942+ subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX;
943+
944+ switch (*name) {
945+ case '$':
946+ num = rootpid;
947+ break;
948+ case '?':
949+ num = oexitstatus;
950+ break;
951+ case '#':
952+ num = shellparam.nparam;
953+ break;
954+ case '!':
955+ num = backgndpidval();
956+ break;
957+ case '-':
958+ p = buf;
959+ for (i = 0 ; i < NSHORTOPTS ; i++) {
960+ if (optval[i])
961+ *p++ = optletter[i];
962+ }
963+ *p = '\0';
964+ strtodest(buf, flag, subtype, quoted, dst);
965+ return;
966+ case '@':
967+ if (flag & EXP_SPLIT && quoted) {
968+ for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
969+ strtodest(p, flag, subtype, quoted, dst);
970+ if (*ap) {
971+ if (splitlater)
972+ STPUTC('\0', expdest);
973+ else
974+ NEXTWORD('\0', flag, expdest,
975+ dst);
976+ }
977+ }
978+ if (shellparam.nparam > 0)
979+ dst->state = WORD_QUOTEMARK;
980+ return;
981+ }
982+ /* FALLTHROUGH */
983+ case '*':
984+ if (ifsset())
985+ sep[0] = ifsval()[0];
986+ else
987+ sep[0] = ' ';
988+ sep[1] = '\0';
989+ for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
990+ strtodest(p, flag, subtype, quoted, dst);
991+ if (!*ap)
992+ break;
993+ if (sep[0])
994+ strtodest(sep, flag, subtype, quoted, dst);
995+ else if (flag & EXP_SPLIT && !quoted && **ap != '\0') {
996+ if (splitlater)
997+ STPUTC('\0', expdest);
998+ else
999+ NEXTWORD('\0', flag, expdest, dst);
1000+ }
1001+ }
1002+ return;
1003+ default:
1004+ if (is_digit(*name)) {
1005+ num = atoi(name);
1006+ if (num == 0)
1007+ p = arg0;
1008+ else if (num > 0 && num <= shellparam.nparam)
1009+ p = shellparam.p[num - 1];
1010+ else
1011+ return;
1012+ strtodest(p, flag, subtype, quoted, dst);
1013+ }
1014+ return;
1015+ }
1016+ cvtnum(num, buf);
1017+ strtodest(buf, flag, subtype, quoted, dst);
1018+}
1019+
1020+
1021+
1022+static char expdir[PATH_MAX];
1023+#define expdir_end (expdir + sizeof(expdir))
1024+
1025+/*
1026+ * Perform pathname generation and remove control characters.
1027+ * At this point, the only control characters should be CTLESC.
1028+ * The results are stored in the list dstlist.
1029+ */
1030+static void
1031+expandmeta(char *pattern, struct arglist *dstlist)
1032+{
1033+ char *p;
1034+ int firstmatch;
1035+ char c;
1036+
1037+ firstmatch = dstlist->count;
1038+ p = pattern;
1039+ for (; (c = *p) != '\0'; p++) {
1040+ /* fast check for meta chars */
1041+ if (c == '*' || c == '?' || c == '[') {
1042+ INTOFF;
1043+ expmeta(expdir, pattern, dstlist);
1044+ INTON;
1045+ break;
1046+ }
1047+ }
1048+ if (dstlist->count == firstmatch) {
1049+ /*
1050+ * no matches
1051+ */
1052+ rmescapes(pattern);
1053+ appendarglist(dstlist, pattern);
1054+ } else {
1055+ qsort(&dstlist->args[firstmatch],
1056+ dstlist->count - firstmatch,
1057+ sizeof(dstlist->args[0]), expsortcmp);
1058+ }
1059+}
1060+
1061+
1062+/*
1063+ * Do metacharacter (i.e. *, ?, [...]) expansion.
1064+ */
1065+
1066+static void
1067+expmeta(char *enddir, char *name, struct arglist *arglist)
1068+{
1069+ const char *p;
1070+ const char *q;
1071+ const char *start;
1072+ char *endname;
1073+ int metaflag;
1074+ struct stat statb;
1075+ DIR *dirp;
1076+ struct dirent *dp;
1077+ int atend;
1078+ int matchdot;
1079+ int esc;
1080+ int namlen;
1081+
1082+ metaflag = 0;
1083+ start = name;
1084+ for (p = name; esc = 0, *p; p += esc + 1) {
1085+ if (*p == '*' || *p == '?')
1086+ metaflag = 1;
1087+ else if (*p == '[') {
1088+ q = p + 1;
1089+ if (*q == '!' || *q == '^')
1090+ q++;
1091+ for (;;) {
1092+ if (*q == CTLESC)
1093+ q++;
1094+ if (*q == '/' || *q == '\0')
1095+ break;
1096+ if (*++q == ']') {
1097+ metaflag = 1;
1098+ break;
1099+ }
1100+ }
1101+ } else if (*p == '\0')
1102+ break;
1103+ else {
1104+ if (*p == CTLESC)
1105+ esc++;
1106+ if (p[esc] == '/') {
1107+ if (metaflag)
1108+ break;
1109+ start = p + esc + 1;
1110+ }
1111+ }
1112+ }
1113+ if (metaflag == 0) { /* we've reached the end of the file name */
1114+ if (enddir != expdir)
1115+ metaflag++;
1116+ for (p = name ; ; p++) {
1117+ if (*p == CTLESC)
1118+ p++;
1119+ *enddir++ = *p;
1120+ if (*p == '\0')
1121+ break;
1122+ if (enddir == expdir_end)
1123+ return;
1124+ }
1125+ if (metaflag == 0 || lstat(expdir, &statb) >= 0)
1126+ appendarglist(arglist, stsavestr(expdir));
1127+ return;
1128+ }
1129+ endname = name + (p - name);
1130+ if (start != name) {
1131+ p = name;
1132+ while (p < start) {
1133+ if (*p == CTLESC)
1134+ p++;
1135+ *enddir++ = *p++;
1136+ if (enddir == expdir_end)
1137+ return;
1138+ }
1139+ }
1140+ if (enddir == expdir) {
1141+ p = ".";
1142+ } else if (enddir == expdir + 1 && *expdir == '/') {
1143+ p = "/";
1144+ } else {
1145+ p = expdir;
1146+ enddir[-1] = '\0';
1147+ }
1148+ if ((dirp = opendir(p)) == NULL)
1149+ return;
1150+ if (enddir != expdir)
1151+ enddir[-1] = '/';
1152+ if (*endname == 0) {
1153+ atend = 1;
1154+ } else {
1155+ atend = 0;
1156+ *endname = '\0';
1157+ endname += esc + 1;
1158+ }
1159+ matchdot = 0;
1160+ p = start;
1161+ if (*p == CTLESC)
1162+ p++;
1163+ if (*p == '.')
1164+ matchdot++;
1165+ while (! int_pending() && (dp = readdir(dirp)) != NULL) {
1166+ if (dp->d_name[0] == '.' && ! matchdot)
1167+ continue;
1168+ if (patmatch(start, dp->d_name)) {
1169+ namlen = strlen(dp->d_name);
1170+ if (enddir + namlen + 1 > expdir_end)
1171+ continue;
1172+ memcpy(enddir, dp->d_name, namlen + 1);
1173+ if (atend)
1174+ appendarglist(arglist, stsavestr(expdir));
1175+ else {
1176+ if (dp->d_type != DT_UNKNOWN &&
1177+ dp->d_type != DT_DIR &&
1178+ dp->d_type != DT_LNK)
1179+ continue;
1180+ if (enddir + namlen + 2 > expdir_end)
1181+ continue;
1182+ enddir[namlen] = '/';
1183+ enddir[namlen + 1] = '\0';
1184+ expmeta(enddir + namlen + 1, endname, arglist);
1185+ }
1186+ }
1187+ }
1188+ closedir(dirp);
1189+ if (! atend)
1190+ endname[-esc - 1] = esc ? CTLESC : '/';
1191+}
1192+
1193+
1194+static int
1195+expsortcmp(const void *p1, const void *p2)
1196+{
1197+ const char *s1 = *(const char * const *)p1;
1198+ const char *s2 = *(const char * const *)p2;
1199+
1200+ return (strcoll(s1, s2));
1201+}
1202+
1203+
1204+
1205+static wchar_t
1206+get_wc(const char **p)
1207+{
1208+ wchar_t c;
1209+ int chrlen;
1210+
1211+ chrlen = mbtowc(&c, *p, 4);
1212+ if (chrlen == 0)
1213+ return 0;
1214+ else if (chrlen == -1)
1215+ c = 0;
1216+ else
1217+ *p += chrlen;
1218+ return c;
1219+}
1220+
1221+
1222+/*
1223+ * See if a character matches a character class, starting at the first colon
1224+ * of "[:class:]".
1225+ * If a valid character class is recognized, a pointer to the next character
1226+ * after the final closing bracket is stored into *end, otherwise a null
1227+ * pointer is stored into *end.
1228+ */
1229+static int
1230+match_charclass(const char *p, wchar_t chr, const char **end)
1231+{
1232+ char name[20];
1233+ const char *nameend;
1234+ wctype_t cclass;
1235+
1236+ *end = NULL;
1237+ p++;
1238+ nameend = strstr(p, ":]");
1239+ if (nameend == NULL || (size_t)(nameend - p) >= sizeof(name) ||
1240+ nameend == p)
1241+ return 0;
1242+ memcpy(name, p, nameend - p);
1243+ name[nameend - p] = '\0';
1244+ *end = nameend + 2;
1245+ cclass = wctype(name);
1246+ /* An unknown class matches nothing but is valid nevertheless. */
1247+ if (cclass == 0)
1248+ return 0;
1249+ return iswctype(chr, cclass);
1250+}
1251+
1252+
1253+/*
1254+ * Returns true if the pattern matches the string.
1255+ */
1256+
1257+static int
1258+patmatch(const char *pattern, const char *string)
1259+{
1260+ const char *p, *q, *end;
1261+ const char *bt_p, *bt_q;
1262+ char c;
1263+ wchar_t wc, wc2;
1264+
1265+ p = pattern;
1266+ q = string;
1267+ bt_p = NULL;
1268+ bt_q = NULL;
1269+ for (;;) {
1270+ switch (c = *p++) {
1271+ case '\0':
1272+ if (*q != '\0')
1273+ goto backtrack;
1274+ return 1;
1275+ case CTLESC:
1276+ if (*q++ != *p++)
1277+ goto backtrack;
1278+ break;
1279+ case '?':
1280+ if (*q == '\0')
1281+ return 0;
1282+ if (localeisutf8) {
1283+ wc = get_wc(&q);
1284+ /*
1285+ * A '?' does not match invalid UTF-8 but a
1286+ * '*' does, so backtrack.
1287+ */
1288+ if (wc == 0)
1289+ goto backtrack;
1290+ } else
1291+ q++;
1292+ break;
1293+ case '*':
1294+ c = *p;
1295+ while (c == '*')
1296+ c = *++p;
1297+ /*
1298+ * If the pattern ends here, we know the string
1299+ * matches without needing to look at the rest of it.
1300+ */
1301+ if (c == '\0')
1302+ return 1;
1303+ /*
1304+ * First try the shortest match for the '*' that
1305+ * could work. We can forget any earlier '*' since
1306+ * there is no way having it match more characters
1307+ * can help us, given that we are already here.
1308+ */
1309+ bt_p = p;
1310+ bt_q = q;
1311+ break;
1312+ case '[': {
1313+ const char *savep, *saveq;
1314+ int invert, found;
1315+ wchar_t chr;
1316+
1317+ savep = p, saveq = q;
1318+ invert = 0;
1319+ if (*p == '!' || *p == '^') {
1320+ invert++;
1321+ p++;
1322+ }
1323+ found = 0;
1324+ if (*q == '\0')
1325+ return 0;
1326+ if (localeisutf8) {
1327+ chr = get_wc(&q);
1328+ if (chr == 0)
1329+ goto backtrack;
1330+ } else
1331+ chr = (unsigned char)*q++;
1332+ c = *p++;
1333+ do {
1334+ if (c == '\0') {
1335+ p = savep, q = saveq;
1336+ c = '[';
1337+ goto dft;
1338+ }
1339+ if (c == '[' && *p == ':') {
1340+ found |= match_charclass(p, chr, &end);
1341+ if (end != NULL) {
1342+ p = end;
1343+ continue;
1344+ }
1345+ }
1346+ if (c == CTLESC)
1347+ c = *p++;
1348+ if (localeisutf8 && c & 0x80) {
1349+ p--;
1350+ wc = get_wc(&p);
1351+ if (wc == 0) /* bad utf-8 */
1352+ return 0;
1353+ } else
1354+ wc = (unsigned char)c;
1355+ if (*p == '-' && p[1] != ']') {
1356+ p++;
1357+ if (*p == CTLESC)
1358+ p++;
1359+ if (localeisutf8) {
1360+ wc2 = get_wc(&p);
1361+ if (wc2 == 0) /* bad utf-8 */
1362+ return 0;
1363+ } else
1364+ wc2 = (unsigned char)*p++;
1365+ if ( collate_range_cmp(chr, wc) >= 0
1366+ && collate_range_cmp(chr, wc2) <= 0
1367+ )
1368+ found = 1;
1369+ } else {
1370+ if (chr == wc)
1371+ found = 1;
1372+ }
1373+ } while ((c = *p++) != ']');
1374+ if (found == invert)
1375+ goto backtrack;
1376+ break;
1377+ }
1378+dft: default:
1379+ if (*q == '\0')
1380+ return 0;
1381+ if (*q++ == c)
1382+ break;
1383+backtrack:
1384+ /*
1385+ * If we have a mismatch (other than hitting the end
1386+ * of the string), go back to the last '*' seen and
1387+ * have it match one additional character.
1388+ */
1389+ if (bt_p == NULL)
1390+ return 0;
1391+ if (*bt_q == '\0')
1392+ return 0;
1393+ bt_q++;
1394+ p = bt_p;
1395+ q = bt_q;
1396+ break;
1397+ }
1398+ }
1399+}
1400+
1401+
1402+
1403+/*
1404+ * Remove any CTLESC and CTLQUOTEMARK characters from a string.
1405+ */
1406+
1407+void
1408+rmescapes(char *str)
1409+{
1410+ char *p, *q;
1411+
1412+ p = str;
1413+ while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) {
1414+ if (*p++ == '\0')
1415+ return;
1416+ }
1417+ q = p;
1418+ while (*p) {
1419+ if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) {
1420+ p++;
1421+ continue;
1422+ }
1423+ if (*p == CTLESC)
1424+ p++;
1425+ *q++ = *p++;
1426+ }
1427+ *q = '\0';
1428+}
1429+
1430+
1431+
1432+/*
1433+ * See if a pattern matches in a case statement.
1434+ */
1435+
1436+int
1437+casematch(union node *pattern, const char *val)
1438+{
1439+ struct stackmark smark;
1440+ struct nodelist *argbackq;
1441+ int result;
1442+ char *p;
1443+
1444+ setstackmark(&smark);
1445+ argbackq = pattern->narg.backquote;
1446+ STARTSTACKSTR(expdest);
1447+ argstr(pattern->narg.text, &argbackq, EXP_TILDE | EXP_CASE, NULL);
1448+ STPUTC('\0', expdest);
1449+ p = grabstackstr(expdest);
1450+ result = patmatch(p, val);
1451+ popstackmark(&smark);
1452+ return result;
1453+}
1454+
1455+/*
1456+ * Our own itoa().
1457+ */
1458+
1459+static void
1460+cvtnum(int num, char *buf)
1461+{
1462+ char temp[32];
1463+ int neg = num < 0;
1464+ char *p = temp + 31;
1465+
1466+ temp[31] = '\0';
1467+
1468+ do {
1469+ *--p = num % 10 + '0';
1470+ } while ((num /= 10) != 0);
1471+
1472+ if (neg)
1473+ *--p = '-';
1474+
1475+ memcpy(buf, p, temp + 32 - p);
1476+}
1477+
1478+/*
1479+ * Do most of the work for wordexp(3).
1480+ */
1481+
1482+int
1483+wordexpcmd(int argc, char **argv)
1484+{
1485+ size_t len;
1486+ int i;
1487+
1488+ out1fmt("%08x", argc - 1);
1489+ for (i = 1, len = 0; i < argc; i++)
1490+ len += strlen(argv[i]);
1491+ out1fmt("%08x", (int)len);
1492+ for (i = 1; i < argc; i++)
1493+ outbin(argv[i], strlen(argv[i]) + 1, out1);
1494+ return (0);
1495+}
1496+
1497+/*
1498+ * Do most of the work for wordexp(3), new version.
1499+ */
1500+
1501+int
1502+freebsd_wordexpcmd(int argc __unused, char **argv __unused)
1503+{
1504+ struct arglist arglist;
1505+ union node *args, *n;
1506+ size_t len;
1507+ int ch;
1508+ int protected = 0;
1509+ int fd = -1;
1510+ int i;
1511+
1512+ while ((ch = nextopt("f:p")) != '\0') {
1513+ switch (ch) {
1514+ case 'f':
1515+ fd = number(shoptarg);
1516+ break;
1517+ case 'p':
1518+ protected = 1;
1519+ break;
1520+ }
1521+ }
1522+ if (*argptr != NULL)
1523+ error("wrong number of arguments");
1524+ if (fd < 0)
1525+ error("missing fd");
1526+ INTOFF;
1527+ setinputfd(fd, 1);
1528+ INTON;
1529+ args = parsewordexp();
1530+ popfile(); /* will also close fd */
1531+ if (protected)
1532+ for (n = args; n != NULL; n = n->narg.next) {
1533+ if (n->narg.backquote != NULL) {
1534+ outcslow('C', out1);
1535+ error("command substitution disabled");
1536+ }
1537+ }
1538+ outcslow(' ', out1);
1539+ emptyarglist(&arglist);
1540+ for (n = args; n != NULL; n = n->narg.next)
1541+ expandarg(n, &arglist, EXP_FULL | EXP_TILDE);
1542+ for (i = 0, len = 0; i < arglist.count; i++)
1543+ len += strlen(arglist.args[i]);
1544+ out1fmt("%016x %016zx", arglist.count, len);
1545+ for (i = 0; i < arglist.count; i++)
1546+ outbin(arglist.args[i], strlen(arglist.args[i]) + 1, out1);
1547+ return (0);
1548+}
+61,
-0
1@@ -0,0 +1,61 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+struct arglist {
37+ char **args;
38+ int count;
39+ int capacity;
40+ char *smallarg[1];
41+};
42+
43+/*
44+ * expandarg() flags
45+ */
46+#define EXP_SPLIT 0x1 /* perform word splitting */
47+#define EXP_TILDE 0x2 /* do normal tilde expansion */
48+#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
49+#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
50+#define EXP_SPLIT_LIT 0x20 /* IFS split literal text ${v+-a b c} */
51+#define EXP_LIT_QUOTED 0x40 /* for EXP_SPLIT_LIT, start off quoted */
52+#define EXP_GLOB 0x80 /* perform file globbing */
53+
54+#define EXP_FULL (EXP_SPLIT | EXP_GLOB)
55+
56+
57+void emptyarglist(struct arglist *);
58+void appendarglist(struct arglist *, char *);
59+union node;
60+void expandarg(union node *, struct arglist *, int);
61+void rmescapes(char *);
62+int casematch(union node *, const char *);
+802,
-0
1@@ -0,0 +1,802 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#if !FEATURE_SH_HISTEDIT
37+#ifndef NO_HISTORY
38+#define NO_HISTORY
39+#endif
40+#endif
41+
42+#include "shell.h"
43+#include "alias.h"
44+#include "builtins.h"
45+#include "error.h"
46+#include "eval.h"
47+#include "exec.h"
48+#include "main.h"
49+#include "memalloc.h"
50+#include "mystring.h"
51+#include "options.h"
52+#include "output.h"
53+#include "parser.h"
54+#include "var.h"
55+
56+#ifndef NO_HISTORY
57+#include "myhistedit.h"
58+
59+#include <sys/param.h>
60+#include <sys/stat.h>
61+
62+#include <dirent.h>
63+#include <errno.h>
64+#include <fcntl.h>
65+#include <limits.h>
66+#include <paths.h>
67+#include <stdio.h>
68+#include <stdlib.h>
69+#include <unistd.h>
70+
71+#define MAXHISTLOOPS 4 /* max recursions through fc */
72+#define DEFEDITOR "ed" /* default editor *should* be $EDITOR */
73+
74+History *hist; /* history cookie */
75+EditLine *el; /* editline cookie */
76+int displayhist;
77+static int savehist;
78+static FILE *el_in, *el_out;
79+static int in_command_completion;
80+
81+static char *fc_replace(const char *, char *, char *);
82+static int not_fcnumber(const char *);
83+static int str_to_event(const char *, int);
84+static int comparator(const void *, const void *, void *);
85+static char **sh_matches(const char *, int, int);
86+static const char *append_char_function(const char *);
87+static unsigned char sh_complete(EditLine *, int);
88+
89+static const char *
90+get_histfile(void)
91+{
92+ const char *histfile;
93+
94+ /* don't try to save if the history size is 0 */
95+ if (hist == NULL || !strcmp(histsizeval(), "0"))
96+ return (NULL);
97+ histfile = expandstr("${HISTFILE-${HOME-}/.sh_history}");
98+
99+ if (histfile[0] == '\0')
100+ return (NULL);
101+ return (histfile);
102+}
103+
104+void
105+histsave(void)
106+{
107+ HistEvent he;
108+ char *histtmpname = NULL;
109+ const char *histfile;
110+ int fd;
111+ FILE *f;
112+
113+ if (!savehist || (histfile = get_histfile()) == NULL)
114+ return;
115+ INTOFF;
116+ asprintf(&histtmpname, "%s.XXXXXXXXXX", histfile);
117+ if (histtmpname == NULL) {
118+ INTON;
119+ return;
120+ }
121+ fd = mkstemp(histtmpname);
122+ if (fd == -1 || (f = fdopen(fd, "w")) == NULL) {
123+ free(histtmpname);
124+ INTON;
125+ return;
126+ }
127+ if (history(hist, &he, H_SAVE_FP, f) < 1 ||
128+ rename(histtmpname, histfile) == -1)
129+ unlink(histtmpname);
130+ fclose(f);
131+ free(histtmpname);
132+ INTON;
133+
134+}
135+
136+void
137+histload(void)
138+{
139+ const char *histfile;
140+ HistEvent he;
141+
142+ if ((histfile = get_histfile()) == NULL)
143+ return;
144+ errno = 0;
145+ if (history(hist, &he, H_LOAD, histfile) != -1 || errno == ENOENT)
146+ savehist = 1;
147+}
148+
149+/*
150+ * Set history and editing status. Called whenever the status may
151+ * have changed (figures out what to do).
152+ */
153+void
154+histedit(void)
155+{
156+
157+#define editing (Eflag || Vflag)
158+
159+ if (iflag) {
160+ if (!hist) {
161+ /*
162+ * turn history on
163+ */
164+ INTOFF;
165+ hist = history_init();
166+ INTON;
167+
168+ if (hist != NULL)
169+ sethistsize(histsizeval());
170+ else
171+ out2fmt_flush("sh: can't initialize history\n");
172+ }
173+ if (editing && !el && isatty(0)) { /* && isatty(2) ??? */
174+ /*
175+ * turn editing on
176+ */
177+ char *term;
178+
179+ INTOFF;
180+ if (el_in == NULL)
181+ el_in = fdopen(0, "r");
182+ if (el_out == NULL)
183+ el_out = fdopen(2, "w");
184+ if (el_in == NULL || el_out == NULL)
185+ goto bad;
186+ term = lookupvar("TERM");
187+ if (term)
188+ setenv("TERM", term, 1);
189+ else
190+ unsetenv("TERM");
191+ el = el_init(arg0, el_in, el_out, el_out);
192+ if (el != NULL) {
193+ if (hist)
194+ el_set(el, EL_HIST, history, hist);
195+ el_set(el, EL_PROMPT_ESC, getprompt, '\001');
196+ el_set(el, EL_ADDFN, "sh-complete",
197+ "Filename completion",
198+ sh_complete);
199+ } else {
200+bad:
201+ out2fmt_flush("sh: can't initialize editing\n");
202+ }
203+ INTON;
204+ } else if (!editing && el) {
205+ INTOFF;
206+ el_end(el);
207+ el = NULL;
208+ INTON;
209+ }
210+ if (el) {
211+ if (Vflag)
212+ el_set(el, EL_EDITOR, "vi");
213+ else if (Eflag) {
214+ el_set(el, EL_EDITOR, "emacs");
215+ }
216+ el_set(el, EL_BIND, "^I", "sh-complete", NULL);
217+ el_source(el, NULL);
218+ }
219+ } else {
220+ INTOFF;
221+ if (el) { /* no editing if not interactive */
222+ el_end(el);
223+ el = NULL;
224+ }
225+ if (hist) {
226+ history_end(hist);
227+ hist = NULL;
228+ }
229+ INTON;
230+ }
231+}
232+
233+
234+void
235+sethistsize(const char *hs)
236+{
237+ int histsize;
238+ HistEvent he;
239+
240+ if (hist != NULL) {
241+ if (hs == NULL || !is_number(hs))
242+ histsize = 128;
243+ else
244+ histsize = atoi(hs);
245+ history(hist, &he, H_SETSIZE, histsize);
246+ history(hist, &he, H_SETUNIQUE, 1);
247+ }
248+}
249+
250+void
251+setterm(const char *term)
252+{
253+ if (rootshell && el != NULL && term != NULL)
254+ el_set(el, EL_TERMINAL, term);
255+}
256+
257+int
258+histcmd(int argc, char **argv __unused)
259+{
260+ const char *editor = NULL;
261+ HistEvent he;
262+ int lflg = 0, nflg = 0, rflg = 0, sflg = 0;
263+ int i, retval;
264+ const char *firststr, *laststr;
265+ int first, last, direction;
266+ char *pat = NULL, *repl = NULL;
267+ static int active = 0;
268+ struct jmploc jmploc;
269+ struct jmploc *savehandler;
270+ char editfilestr[PATH_MAX];
271+ char *volatile editfile;
272+ FILE *efp = NULL;
273+ int oldhistnum;
274+
275+ if (hist == NULL)
276+ error("history not active");
277+
278+ if (argc == 1)
279+ error("missing history argument");
280+
281+ while (not_fcnumber(*argptr))
282+ do {
283+ switch (nextopt("e:lnrs")) {
284+ case 'e':
285+ editor = shoptarg;
286+ break;
287+ case 'l':
288+ lflg = 1;
289+ break;
290+ case 'n':
291+ nflg = 1;
292+ break;
293+ case 'r':
294+ rflg = 1;
295+ break;
296+ case 's':
297+ sflg = 1;
298+ break;
299+ case '\0':
300+ goto operands;
301+ }
302+ } while (nextopt_optptr != NULL);
303+operands:
304+ savehandler = handler;
305+ /*
306+ * If executing...
307+ */
308+ if (lflg == 0 || editor || sflg) {
309+ lflg = 0; /* ignore */
310+ editfile = NULL;
311+ /*
312+ * Catch interrupts to reset active counter and
313+ * cleanup temp files.
314+ */
315+ if (setjmp(jmploc.loc)) {
316+ active = 0;
317+ if (editfile)
318+ unlink(editfile);
319+ handler = savehandler;
320+ longjmp(handler->loc, 1);
321+ }
322+ handler = &jmploc;
323+ if (++active > MAXHISTLOOPS) {
324+ active = 0;
325+ displayhist = 0;
326+ error("called recursively too many times");
327+ }
328+ /*
329+ * Set editor.
330+ */
331+ if (sflg == 0) {
332+ if (editor == NULL &&
333+ (editor = bltinlookup("FCEDIT", 1)) == NULL &&
334+ (editor = bltinlookup("EDITOR", 1)) == NULL)
335+ editor = DEFEDITOR;
336+ if (editor[0] == '-' && editor[1] == '\0') {
337+ sflg = 1; /* no edit */
338+ editor = NULL;
339+ }
340+ }
341+ }
342+
343+ /*
344+ * If executing, parse [old=new] now
345+ */
346+ if (lflg == 0 && *argptr != NULL &&
347+ ((repl = strchr(*argptr, '=')) != NULL)) {
348+ pat = *argptr;
349+ *repl++ = '\0';
350+ argptr++;
351+ }
352+ /*
353+ * determine [first] and [last]
354+ */
355+ if (*argptr == NULL) {
356+ firststr = lflg ? "-16" : "-1";
357+ laststr = "-1";
358+ } else if (argptr[1] == NULL) {
359+ firststr = argptr[0];
360+ laststr = lflg ? "-1" : argptr[0];
361+ } else if (argptr[2] == NULL) {
362+ firststr = argptr[0];
363+ laststr = argptr[1];
364+ } else
365+ error("too many arguments");
366+ /*
367+ * Turn into event numbers.
368+ */
369+ first = str_to_event(firststr, 0);
370+ last = str_to_event(laststr, 1);
371+
372+ if (rflg) {
373+ i = last;
374+ last = first;
375+ first = i;
376+ }
377+ /*
378+ * XXX - this should not depend on the event numbers
379+ * always increasing. Add sequence numbers or offset
380+ * to the history element in next (diskbased) release.
381+ */
382+ direction = first < last ? H_PREV : H_NEXT;
383+
384+ /*
385+ * If editing, grab a temp file.
386+ */
387+ if (editor) {
388+ int fd;
389+ INTOFF; /* easier */
390+ sprintf(editfilestr, "%s/_shXXXXXX", _PATH_TMP);
391+ if ((fd = mkstemp(editfilestr)) < 0)
392+ error("can't create temporary file %s", editfile);
393+ editfile = editfilestr;
394+ if ((efp = fdopen(fd, "w")) == NULL) {
395+ close(fd);
396+ error("Out of space");
397+ }
398+ }
399+
400+ /*
401+ * Loop through selected history events. If listing or executing,
402+ * do it now. Otherwise, put into temp file and call the editor
403+ * after.
404+ *
405+ * The history interface needs rethinking, as the following
406+ * convolutions will demonstrate.
407+ */
408+ history(hist, &he, H_FIRST);
409+ retval = history(hist, &he, H_NEXT_EVENT, first);
410+ for (;retval != -1; retval = history(hist, &he, direction)) {
411+ if (lflg) {
412+ if (!nflg)
413+ out1fmt("%5d ", he.num);
414+ out1str(he.str);
415+ } else {
416+ const char *s = pat ?
417+ fc_replace(he.str, pat, repl) : he.str;
418+
419+ if (sflg) {
420+ if (displayhist) {
421+ out2str(s);
422+ flushout(out2);
423+ }
424+ evalstring(s, 0);
425+ if (displayhist && hist) {
426+ /*
427+ * XXX what about recursive and
428+ * relative histnums.
429+ */
430+ oldhistnum = he.num;
431+ history(hist, &he, H_ENTER, s);
432+ /*
433+ * XXX H_ENTER moves the internal
434+ * cursor, set it back to the current
435+ * entry.
436+ */
437+ history(hist, &he,
438+ H_NEXT_EVENT, oldhistnum);
439+ }
440+ } else
441+ fputs(s, efp);
442+ }
443+ /*
444+ * At end? (if we were to lose last, we'd sure be
445+ * messed up).
446+ */
447+ if (he.num == last)
448+ break;
449+ }
450+ if (editor) {
451+ char *editcmd;
452+
453+ fclose(efp);
454+ INTON;
455+ editcmd = stalloc(strlen(editor) + strlen(editfile) + 2);
456+ sprintf(editcmd, "%s %s", editor, editfile);
457+ evalstring(editcmd, 0); /* XXX - should use no JC command */
458+ readcmdfile(editfile, 0 /* verify */); /* XXX - should read back - quick tst */
459+ unlink(editfile);
460+ }
461+
462+ if (lflg == 0 && active > 0)
463+ --active;
464+ if (displayhist)
465+ displayhist = 0;
466+ handler = savehandler;
467+ return 0;
468+}
469+
470+static char *
471+fc_replace(const char *s, char *p, char *r)
472+{
473+ char *dest;
474+ int plen = strlen(p);
475+
476+ STARTSTACKSTR(dest);
477+ while (*s) {
478+ if (*s == *p && strncmp(s, p, plen) == 0) {
479+ STPUTS(r, dest);
480+ s += plen;
481+ *p = '\0'; /* so no more matches */
482+ } else
483+ STPUTC(*s++, dest);
484+ }
485+ STPUTC('\0', dest);
486+ dest = grabstackstr(dest);
487+
488+ return (dest);
489+}
490+
491+static int
492+not_fcnumber(const char *s)
493+{
494+ if (s == NULL)
495+ return (0);
496+ if (*s == '-')
497+ s++;
498+ return (!is_number(s));
499+}
500+
501+static int
502+str_to_event(const char *str, int last)
503+{
504+ HistEvent he;
505+ const char *s = str;
506+ int relative = 0;
507+ int i, retval;
508+
509+ retval = history(hist, &he, H_FIRST);
510+ switch (*s) {
511+ case '-':
512+ relative = 1;
513+ /*FALLTHROUGH*/
514+ case '+':
515+ s++;
516+ }
517+ if (is_number(s)) {
518+ i = atoi(s);
519+ if (relative) {
520+ while (retval != -1 && i--) {
521+ retval = history(hist, &he, H_NEXT);
522+ }
523+ if (retval == -1)
524+ retval = history(hist, &he, H_LAST);
525+ } else {
526+ retval = history(hist, &he, H_NEXT_EVENT, i);
527+ if (retval == -1) {
528+ /*
529+ * the notion of first and last is
530+ * backwards to that of the history package
531+ */
532+ retval = history(hist, &he, last ? H_FIRST : H_LAST);
533+ }
534+ }
535+ if (retval == -1)
536+ error("history number %s not found (internal error)",
537+ str);
538+ } else {
539+ /*
540+ * pattern
541+ */
542+ retval = history(hist, &he, H_PREV_STR, str);
543+ if (retval == -1)
544+ error("history pattern not found: %s", str);
545+ }
546+ return (he.num);
547+}
548+
549+int
550+bindcmd(int argc, char **argv)
551+{
552+ int ret;
553+ FILE *old;
554+ FILE *out;
555+
556+ if (el == NULL)
557+ error("line editing is disabled");
558+
559+ INTOFF;
560+
561+ out = out1fp();
562+ if (out == NULL)
563+ error("Out of space");
564+
565+ el_get(el, EL_GETFP, 1, &old);
566+ el_set(el, EL_SETFP, 1, out);
567+
568+ ret = el_parse(el, argc, __DECONST(const char **, argv));
569+
570+ el_set(el, EL_SETFP, 1, old);
571+
572+ fclose(out);
573+
574+ if (argc > 1 && argv[1][0] == '-' &&
575+ memchr("ve", argv[1][1], 2) != NULL) {
576+ Vflag = argv[1][1] == 'v';
577+ Eflag = !Vflag;
578+ histedit();
579+ }
580+
581+ INTON;
582+
583+ return ret;
584+}
585+
586+/*
587+ * Comparator function for qsort(). The use of curpos here is to skip
588+ * characters that we already know to compare equal (common prefix).
589+ */
590+static int
591+comparator(const void *a, const void *b, void *thunk)
592+{
593+ size_t curpos = (intptr_t)thunk;
594+
595+ return (strcmp(*(char *const *)a + curpos,
596+ *(char *const *)b + curpos));
597+}
598+
599+static char
600+**add_match(char **matches, size_t i, size_t *size, char *match_copy)
601+{
602+ if (match_copy == NULL)
603+ return (NULL);
604+ matches[i] = match_copy;
605+ if (i >= *size - 1) {
606+ *size *= 2;
607+ matches = reallocarray(matches, *size, sizeof(matches[0]));
608+ }
609+
610+ return (matches);
611+}
612+
613+/*
614+ * This function is passed to libedit's fn_complete2(). The library will use
615+ * it instead of its standard function that finds matching files in current
616+ * directory. If we're at the start of the line, we want to look for
617+ * available commands from all paths in $PATH.
618+ */
619+static char
620+**sh_matches(const char *text, int start, int end)
621+{
622+ char *free_path = NULL, *path;
623+ const char *dirname;
624+ char **matches = NULL, **rmatches;
625+ size_t i = 0, size = 16, uniq;
626+ size_t curpos = end - start, lcstring = -1;
627+ struct cmdentry e;
628+
629+ in_command_completion = 0;
630+ if (start > 0 || memchr("/.~", text[0], 3) != NULL)
631+ return (NULL);
632+ in_command_completion = 1;
633+ if ((free_path = path = strdup(pathval())) == NULL)
634+ goto out;
635+ if ((matches = malloc(size * sizeof(matches[0]))) == NULL)
636+ goto out;
637+ while ((dirname = strsep(&path, ":")) != NULL) {
638+ struct dirent *entry;
639+ DIR *dir;
640+ int dfd;
641+
642+ dir = opendir(dirname[0] == '\0' ? "." : dirname);
643+ if (dir == NULL)
644+ continue;
645+ if ((dfd = dirfd(dir)) == -1) {
646+ closedir(dir);
647+ continue;
648+ }
649+ while ((entry = readdir(dir)) != NULL) {
650+ struct stat statb;
651+
652+ if (strncmp(entry->d_name, text, curpos) != 0)
653+ continue;
654+ if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK) {
655+ if (fstatat(dfd, entry->d_name, &statb, 0) == -1)
656+ continue;
657+ if (!S_ISREG(statb.st_mode))
658+ continue;
659+ } else if (entry->d_type != DT_REG)
660+ continue;
661+ rmatches = add_match(matches, ++i, &size,
662+ strdup(entry->d_name));
663+ if (rmatches == NULL) {
664+ closedir(dir);
665+ goto out;
666+ }
667+ matches = rmatches;
668+ }
669+ closedir(dir);
670+ }
671+ for (const unsigned char *bp = builtincmd; *bp != 0; bp += 2 + bp[0]) {
672+ if (curpos > bp[0] || memcmp(bp + 2, text, curpos) != 0)
673+ continue;
674+ rmatches = add_match(matches, ++i, &size, strndup(bp + 2, bp[0]));
675+ if (rmatches == NULL)
676+ goto out;
677+ matches = rmatches;
678+ }
679+ for (const struct alias *ap = NULL; (ap = iteralias(ap)) != NULL;) {
680+ if (strncmp(ap->name, text, curpos) != 0)
681+ continue;
682+ rmatches = add_match(matches, ++i, &size, strdup(ap->name));
683+ if (rmatches == NULL)
684+ goto out;
685+ matches = rmatches;
686+ }
687+ for (const void *a = NULL; (a = itercmd(a, &e)) != NULL;) {
688+ if (e.cmdtype != CMDFUNCTION)
689+ continue;
690+ if (strncmp(e.cmdname, text, curpos) != 0)
691+ continue;
692+ rmatches = add_match(matches, ++i, &size, strdup(e.cmdname));
693+ if (rmatches == NULL)
694+ goto out;
695+ matches = rmatches;
696+ }
697+out:
698+ free(free_path);
699+ if (i == 0) {
700+ free(matches);
701+ return (NULL);
702+ }
703+ uniq = 1;
704+ if (i > 1) {
705+ qsort_s(matches + 1, i, sizeof(matches[0]), comparator,
706+ (void *)(intptr_t)curpos);
707+ for (size_t k = 2; k <= i; k++) {
708+ const char *l = matches[uniq] + curpos;
709+ const char *r = matches[k] + curpos;
710+ size_t common = 0;
711+
712+ while (*l != '\0' && *r != '\0' && *l == *r)
713+ (void)l++, r++, common++;
714+ if (common < lcstring)
715+ lcstring = common;
716+ if (*l == *r)
717+ free(matches[k]);
718+ else
719+ matches[++uniq] = matches[k];
720+ }
721+ }
722+ matches[uniq + 1] = NULL;
723+ /*
724+ * matches[0] is special: it's not a real matching file name but
725+ * a common prefix for all matching names. It can't be null, unlike
726+ * any other element of the array. When strings matches[0] and
727+ * matches[1] compare equal and matches[2] is null that means to
728+ * libedit that there is only a single match. It will then replace
729+ * user input with possibly escaped string in matches[0] which is the
730+ * reason to copy the full name of the only match.
731+ */
732+ if (uniq == 1)
733+ matches[0] = strdup(matches[1]);
734+ else if (lcstring != (size_t)-1)
735+ matches[0] = strndup(matches[1], curpos + lcstring);
736+ else
737+ matches[0] = strdup(text);
738+ if (matches[0] == NULL) {
739+ for (size_t k = 1; k <= uniq; k++)
740+ free(matches[k]);
741+ free(matches);
742+ return (NULL);
743+ }
744+ return (matches);
745+}
746+
747+/*
748+ * If we don't specify this function as app_func in the call to fn_complete2,
749+ * libedit will use the default one, which adds a " " to plain files and
750+ * a "/" to directories regardless of whether it's a command name or a plain
751+ * path (relative or absolute). We never want to add "/" to commands.
752+ *
753+ * For example, after I did "mkdir rmdir", "rmdi" would be autocompleted to
754+ * "rmdir/" instead of "rmdir ".
755+ */
756+static const char *
757+append_char_function(const char *name)
758+{
759+ struct stat stbuf;
760+ char *expname = name[0] == '~' ? fn_tilde_expand(name) : NULL;
761+ const char *rs;
762+
763+ if (!in_command_completion &&
764+ stat(expname ? expname : name, &stbuf) == 0 &&
765+ S_ISDIR(stbuf.st_mode))
766+ rs = "/";
767+ else
768+ rs = " ";
769+ free(expname);
770+ return (rs);
771+}
772+
773+/*
774+ * This is passed to el_set(el, EL_ADDFN, ...) so that it's possible to
775+ * bind a key (tab by default) to execute the function.
776+ */
777+unsigned char
778+sh_complete(EditLine *sel, int ch __unused)
779+{
780+ return (unsigned char)fn_complete2(sel, NULL, sh_matches,
781+ L" \t\n\"\\'`@$><=;|&{(", NULL, append_char_function,
782+ (size_t)100, NULL, &((int) {0}), NULL, NULL, FN_QUOTE_MATCH);
783+}
784+
785+#else
786+
787+int
788+histcmd(int argc __unused, char **argv __unused)
789+{
790+
791+ error("not compiled with history support");
792+ /*NOTREACHED*/
793+ return (0);
794+}
795+
796+int
797+bindcmd(int argc __unused, char **argv __unused)
798+{
799+
800+ error("not compiled with line editing support");
801+ return (0);
802+}
803+#endif
+522,
-0
1@@ -0,0 +1,522 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#include <stdio.h> /* defines BUFSIZ */
37+#include <fcntl.h>
38+#include <errno.h>
39+#include <unistd.h>
40+#include <stdlib.h>
41+#include <string.h>
42+
43+/*
44+ * This file implements the input routines used by the parser.
45+ */
46+
47+#include "shell.h"
48+#include "redir.h"
49+#include "syntax.h"
50+#include "input.h"
51+#include "output.h"
52+#include "options.h"
53+#include "memalloc.h"
54+#include "error.h"
55+#include "alias.h"
56+#include "parser.h"
57+#ifndef NO_HISTORY
58+#include "myhistedit.h"
59+#endif
60+#include "trap.h"
61+
62+#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
63+
64+struct strpush {
65+ struct strpush *prev; /* preceding string on stack */
66+ const char *prevstring;
67+ int prevnleft;
68+ int prevlleft;
69+ struct alias *ap; /* if push was associated with an alias */
70+};
71+
72+/*
73+ * The parsefile structure pointed to by the global variable parsefile
74+ * contains information about the current file being read.
75+ */
76+
77+struct parsefile {
78+ struct parsefile *prev; /* preceding file on stack */
79+ int linno; /* current line */
80+ int fd; /* file descriptor (or -1 if string) */
81+ int nleft; /* number of chars left in this line */
82+ int lleft; /* number of lines left in this buffer */
83+ const char *nextc; /* next char in buffer */
84+ char *buf; /* input buffer */
85+ size_t bufsize; /* input buffer size */
86+ struct strpush *strpush; /* for pushing strings at this level */
87+ struct strpush basestrpush; /* so pushing one is fast */
88+};
89+
90+
91+int plinno = 1; /* input line number */
92+int parsenleft; /* copy of parsefile->nleft */
93+static int parselleft; /* copy of parsefile->lleft */
94+const char *parsenextc; /* copy of parsefile->nextc */
95+static char basebuf[BUFSIZ + 1];/* buffer for top level input file */
96+static struct parsefile basepf = { /* top level input file */
97+ .nextc = basebuf,
98+ .buf = basebuf,
99+ .bufsize = sizeof(basebuf),
100+};
101+static struct parsefile *parsefile = &basepf; /* current input file */
102+int whichprompt; /* 1 == PS1, 2 == PS2 */
103+
104+static void pushfile(void);
105+static int preadfd(void);
106+static void popstring(void);
107+
108+void
109+resetinput(void)
110+{
111+ popallfiles();
112+ parselleft = parsenleft = 0; /* clear input buffer */
113+}
114+
115+
116+
117+/*
118+ * Read a character from the script, returning PEOF on end of file.
119+ * Nul characters in the input are silently discarded.
120+ */
121+
122+int
123+pgetc(void)
124+{
125+ return pgetc_macro();
126+}
127+
128+
129+static int
130+preadfd(void)
131+{
132+ int nr;
133+
134+ retry:
135+#ifndef NO_HISTORY
136+ if (parsefile->fd == 0 && el) {
137+ const char *line;
138+
139+ el_resize(el);
140+ line = el_gets(el, &nr);
141+ if (nr > 0 && parsefile->bufsize < (size_t)nr + 1) {
142+ size_t bufsize;
143+
144+ INTOFF;
145+ if (parsefile->buf != basebuf) {
146+ ckfree(parsefile->buf);
147+ parsefile->buf = NULL;
148+ parsefile->bufsize = 0;
149+ }
150+ bufsize = (size_t)nr + BUFSIZ + 1;
151+ bufsize -= bufsize % BUFSIZ;
152+ parsefile->buf = ckmalloc(bufsize);
153+ parsefile->bufsize = bufsize;
154+ INTON;
155+ }
156+ if (nr > 0 && line != NULL)
157+ memcpy(parsefile->buf, line, nr);
158+ else
159+ nr = nr ? -1 : 0;
160+ } else
161+#endif
162+ nr = read(parsefile->fd, parsefile->buf, parsefile->bufsize - 1);
163+
164+ if (nr < 0)
165+ switch (errno) {
166+ int flags;
167+
168+ case EINTR:
169+ goto retry;
170+ case EWOULDBLOCK:
171+ if (parsefile->fd != 0)
172+ break;
173+ if ((flags = fcntl(0, F_GETFL, 0)) < 0)
174+ break;
175+ if (!(flags & O_NONBLOCK))
176+ break;
177+ if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) < 0)
178+ break;
179+ out2fmt_flush("sh: turning off NDELAY mode\n");
180+ goto retry;
181+ }
182+ else if (nr > 0)
183+ parsefile->buf[nr] = '\0';
184+ else
185+ nr = -1;
186+
187+ parsenextc = parsefile->buf;
188+ return nr;
189+}
190+
191+/*
192+ * Refill the input buffer and return the next input character:
193+ *
194+ * 1) If a string was pushed back on the input, pop it;
195+ * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
196+ * from a string so we can't refill the buffer, return EOF.
197+ * 3) If there is more in this buffer, use it else call read to fill it.
198+ * 4) Process input up to the next newline, deleting nul characters.
199+ */
200+
201+int
202+preadbuffer(void)
203+{
204+ const char *end;
205+ char *q, *r;
206+ char savec;
207+
208+ while (parsefile->strpush) {
209+ /*
210+ * Add a space to the end of an alias to ensure that the
211+ * alias remains in use while parsing its last word.
212+ * This avoids alias recursions.
213+ */
214+ if (parsenleft == -1 && parsefile->strpush->ap != NULL)
215+ return ' ';
216+ popstring();
217+ if (--parsenleft >= 0)
218+ return (*parsenextc++);
219+ }
220+ if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
221+ return PEOF;
222+
223+ again:
224+ if (parselleft <= 0 && (parselleft = preadfd()) == -1) {
225+ parselleft = parsenleft = EOF_NLEFT;
226+ return (PEOF);
227+ }
228+ end = parsenextc + parselleft;
229+ q = strchrnul(parsenextc, '\n');
230+ if (*q == '\0' && q != end) {
231+ /* delete nul characters */
232+ for (r = q++; q != end; q++)
233+ if (*q != '\0')
234+ *r++ = *q;
235+ *r = '\0';
236+ parselleft = r - parsenextc;
237+ goto again;
238+ }
239+ if (*q == '\0') {
240+ parsenleft = parselleft;
241+ parselleft = 0;
242+ } else /* *q == '\n' */ {
243+ q++;
244+ parsenleft = q - parsenextc;
245+ parselleft -= parsenleft;
246+ }
247+ parsenleft--;
248+
249+ savec = *q;
250+ *q = '\0';
251+
252+#ifndef NO_HISTORY
253+ if (parsefile->fd == 0 && hist &&
254+ parsenextc[strspn(parsenextc, " \t\n")] != '\0') {
255+ HistEvent he;
256+ INTOFF;
257+ history(hist, &he, whichprompt == 1 ? H_ENTER : H_ADD,
258+ parsenextc);
259+ INTON;
260+ }
261+#endif
262+
263+ if (vflag) {
264+ out2str(parsenextc);
265+ flushout(out2);
266+ }
267+
268+ *q = savec;
269+
270+ return *parsenextc++;
271+}
272+
273+/*
274+ * Returns if we are certain we are at EOF. Does not cause any more input
275+ * to be read from the outside world.
276+ */
277+
278+int
279+preadateof(void)
280+{
281+ if (parsenleft > 0)
282+ return 0;
283+ if (parsefile->strpush)
284+ return 0;
285+ if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
286+ return 1;
287+ return 0;
288+}
289+
290+/*
291+ * Undo the last call to pgetc. Only one character may be pushed back.
292+ * PEOF may be pushed back.
293+ */
294+
295+void
296+pungetc(void)
297+{
298+ parsenleft++;
299+ parsenextc--;
300+}
301+
302+/*
303+ * Push a string back onto the input at this current parsefile level.
304+ * We handle aliases this way.
305+ */
306+void
307+pushstring(const char *s, int len, struct alias *ap)
308+{
309+ struct strpush *sp;
310+
311+ INTOFF;
312+/*out2fmt_flush("*** calling pushstring: %s, %d\n", s, len);*/
313+ if (parsefile->strpush) {
314+ sp = ckmalloc(sizeof(struct strpush));
315+ sp->prev = parsefile->strpush;
316+ parsefile->strpush = sp;
317+ } else
318+ sp = parsefile->strpush = &(parsefile->basestrpush);
319+ sp->prevstring = parsenextc;
320+ sp->prevnleft = parsenleft;
321+ sp->prevlleft = parselleft;
322+ sp->ap = ap;
323+ if (ap)
324+ ap->flag |= ALIASINUSE;
325+ parsenextc = s;
326+ parsenleft = len;
327+ INTON;
328+}
329+
330+static void
331+popstring(void)
332+{
333+ struct strpush *sp = parsefile->strpush;
334+
335+ INTOFF;
336+ if (sp->ap) {
337+ if (parsenextc != sp->ap->val &&
338+ (parsenextc[-1] == ' ' || parsenextc[-1] == '\t'))
339+ forcealias();
340+ sp->ap->flag &= ~ALIASINUSE;
341+ }
342+ parsenextc = sp->prevstring;
343+ parsenleft = sp->prevnleft;
344+ parselleft = sp->prevlleft;
345+/*out2fmt_flush("*** calling popstring: restoring to '%s'\n", parsenextc);*/
346+ parsefile->strpush = sp->prev;
347+ if (sp != &(parsefile->basestrpush))
348+ ckfree(sp);
349+ INTON;
350+}
351+
352+/*
353+ * Set the input to take input from a file. If push is set, push the
354+ * old input onto the stack first.
355+ * About verify:
356+ * -1: Obey verifyflag
357+ * 0: Do not verify
358+ * 1: Do verify
359+ */
360+
361+void
362+setinputfile(const char *fname, int push, int verify)
363+{
364+ int e;
365+ int fd;
366+ int fd2;
367+ int oflags = O_RDONLY | O_CLOEXEC;
368+
369+ if (verify == 1 || (verify == -1 && verifyflag))
370+ oflags |= O_VERIFY;
371+
372+ INTOFF;
373+ if ((fd = open(fname, oflags)) < 0) {
374+ e = errno;
375+ errorwithstatus(e == ENOENT || e == ENOTDIR ? 127 : 126,
376+ "cannot open %s: %s", fname, strerror(e));
377+ }
378+ if (fd < 10) {
379+ fd2 = fcntl(fd, F_DUPFD_CLOEXEC, 10);
380+ close(fd);
381+ if (fd2 < 0)
382+ error("Out of file descriptors");
383+ fd = fd2;
384+ }
385+ setinputfd(fd, push);
386+ INTON;
387+}
388+
389+
390+/*
391+ * Like setinputfile, but takes an open file descriptor (which should have
392+ * its FD_CLOEXEC flag already set). Call this with interrupts off.
393+ */
394+
395+void
396+setinputfd(int fd, int push)
397+{
398+ if (push)
399+ pushfile();
400+ if (parsefile->fd > 0)
401+ close(parsefile->fd);
402+ parsefile->fd = fd;
403+ if (parsefile->buf == NULL) {
404+ parsefile->buf = ckmalloc(BUFSIZ + 1);
405+ parsefile->bufsize = BUFSIZ + 1;
406+ }
407+ parselleft = parsenleft = 0;
408+ plinno = 1;
409+}
410+
411+
412+/*
413+ * Like setinputfile, but takes input from a string.
414+ */
415+
416+void
417+setinputstring(const char *string)
418+{
419+ INTOFF;
420+ pushfile();
421+ parsenextc = string;
422+ parselleft = parsenleft = strlen(string);
423+ plinno = 1;
424+ INTON;
425+}
426+
427+
428+
429+/*
430+ * To handle the "." command, a stack of input files is used. Pushfile
431+ * adds a new entry to the stack and popfile restores the previous level.
432+ */
433+
434+static void
435+pushfile(void)
436+{
437+ struct parsefile *pf;
438+
439+ pf = (struct parsefile *)ckmalloc(sizeof(struct parsefile));
440+ *pf = (struct parsefile){ .prev = parsefile, .fd = -1 };
441+ parsefile->nleft = parsenleft;
442+ parsefile->lleft = parselleft;
443+ parsefile->nextc = parsenextc;
444+ parsefile->linno = plinno;
445+ parsefile = pf;
446+}
447+
448+
449+void
450+popfile(void)
451+{
452+ struct parsefile *pf = parsefile;
453+
454+ INTOFF;
455+ if (pf->fd >= 0)
456+ close(pf->fd);
457+ if (pf->buf)
458+ ckfree(pf->buf);
459+ while (pf->strpush)
460+ popstring();
461+ parsefile = pf->prev;
462+ ckfree(pf);
463+ parsenleft = parsefile->nleft;
464+ parselleft = parsefile->lleft;
465+ parsenextc = parsefile->nextc;
466+ plinno = parsefile->linno;
467+ INTON;
468+}
469+
470+
471+/*
472+ * Return current file (to go back to it later using popfilesupto()).
473+ */
474+
475+struct parsefile *
476+getcurrentfile(void)
477+{
478+ return parsefile;
479+}
480+
481+
482+/*
483+ * Pop files until the given file is on top again. Useful for regular
484+ * builtins that read shell commands from files or strings.
485+ * If the given file is not an active file, an error is raised.
486+ */
487+
488+void
489+popfilesupto(struct parsefile *file)
490+{
491+ while (parsefile != file && parsefile != &basepf)
492+ popfile();
493+ if (parsefile != file)
494+ error("popfilesupto() misused");
495+}
496+
497+/*
498+ * Return to top level.
499+ */
500+
501+void
502+popallfiles(void)
503+{
504+ while (parsefile != &basepf)
505+ popfile();
506+}
507+
508+
509+
510+/*
511+ * Close the file(s) that the shell is reading commands from. Called
512+ * after a fork is done.
513+ */
514+
515+void
516+closescript(void)
517+{
518+ popallfiles();
519+ if (parsefile->fd > 0) {
520+ close(parsefile->fd);
521+ parsefile->fd = 0;
522+ }
523+}
+64,
-0
1@@ -0,0 +1,64 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+/* PEOF (the end of file marker) is defined in syntax.h */
37+
38+/*
39+ * The input line number. Input.c just defines this variable, and saves
40+ * and restores it when files are pushed and popped. The user of this
41+ * package must set its value.
42+ */
43+extern int plinno;
44+extern int parsenleft; /* number of characters left in input buffer */
45+extern const char *parsenextc; /* next character in input buffer */
46+
47+struct alias;
48+struct parsefile;
49+
50+void resetinput(void);
51+int pgetc(void);
52+int preadbuffer(void);
53+int preadateof(void);
54+void pungetc(void);
55+void pushstring(const char *, int, struct alias *);
56+void setinputfile(const char *, int, int);
57+void setinputfd(int, int);
58+void setinputstring(const char *);
59+void popfile(void);
60+struct parsefile *getcurrentfile(void);
61+void popfilesupto(struct parsefile *);
62+void popallfiles(void);
63+void closescript(void);
64+
65+#define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer())
+1578,
-0
1@@ -0,0 +1,1578 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#include <sys/ioctl.h>
37+#include <sys/param.h>
38+#include <sys/resource.h>
39+#include <sys/time.h>
40+#include <sys/wait.h>
41+#include <errno.h>
42+#include <fcntl.h>
43+#include <paths.h>
44+#include <signal.h>
45+#include <stddef.h>
46+#include <stdlib.h>
47+#include <unistd.h>
48+
49+#include "shell.h"
50+#if JOBS
51+#include <termios.h>
52+#undef CEOF /* syntax.h redefines this */
53+#endif
54+#include "redir.h"
55+#include "exec.h"
56+#include "show.h"
57+#include "main.h"
58+#include "parser.h"
59+#include "nodes.h"
60+#include "jobs.h"
61+#include "options.h"
62+#include "trap.h"
63+#include "syntax.h"
64+#include "input.h"
65+#include "output.h"
66+#include "memalloc.h"
67+#include "error.h"
68+#include "mystring.h"
69+#include "var.h"
70+#include "builtins.h"
71+#include "eval.h"
72+
73+
74+/*
75+ * A job structure contains information about a job. A job is either a
76+ * single process or a set of processes contained in a pipeline. In the
77+ * latter case, pidlist will be non-NULL, and will point to a -1 terminated
78+ * array of pids.
79+ */
80+
81+struct procstat {
82+ pid_t pid; /* process id */
83+ int status; /* status flags (defined above) */
84+ char *cmd; /* text of command being run */
85+};
86+
87+
88+/* states */
89+#define JOBSTOPPED 1 /* all procs are stopped */
90+#define JOBDONE 2 /* all procs are completed */
91+
92+
93+struct job {
94+ struct procstat ps0; /* status of process */
95+ struct procstat *ps; /* status or processes when more than one */
96+ short nprocs; /* number of processes */
97+ pid_t pgrp; /* process group of this job */
98+ char state; /* true if job is finished */
99+ char used; /* true if this entry is in use */
100+ char changed; /* true if status has changed */
101+ char foreground; /* true if running in the foreground */
102+ char remembered; /* true if $! referenced */
103+ char pipefail; /* pass any non-zero status */
104+#if JOBS
105+ char jobctl; /* job running under job control */
106+ struct job *next; /* job used after this one */
107+#endif
108+};
109+
110+
111+static struct job *jobtab; /* array of jobs */
112+static int njobs; /* size of array */
113+static pid_t backgndpid = -1; /* pid of last background process */
114+static struct job *bgjob = NULL; /* last background process */
115+#if JOBS
116+static struct job *jobmru; /* most recently used job list */
117+static pid_t initialpgrp; /* pgrp of shell on invocation */
118+#endif
119+static int ttyfd = -1;
120+
121+/* mode flags for dowait */
122+#define DOWAIT_BLOCK 0x1 /* wait until a child exits */
123+#define DOWAIT_SIG 0x2 /* if DOWAIT_BLOCK, abort on signal */
124+#define DOWAIT_SIG_TRAP 0x4 /* if DOWAIT_SIG, abort on trapped signal only */
125+
126+#if JOBS
127+static void restartjob(struct job *);
128+#endif
129+static void freejob(struct job *);
130+static int waitcmdloop(struct job *);
131+static struct job *getjob_nonotfound(const char *);
132+static struct job *getjob(const char *);
133+pid_t killjob(const char *, int);
134+static pid_t dowait(int, struct job *);
135+static void checkzombies(void);
136+static void cmdtxt(union node *);
137+static void cmdputs(const char *);
138+#if JOBS
139+static void setcurjob(struct job *);
140+static void deljob(struct job *);
141+static struct job *getcurjob(struct job *);
142+#endif
143+static int getjobstatus(const struct job *);
144+static void printjobcmd(struct job *);
145+static void showjob(struct job *, int);
146+
147+
148+/*
149+ * Turn job control on and off.
150+ */
151+
152+static int jobctl;
153+
154+#if JOBS
155+static void
156+jobctl_notty(void)
157+{
158+ if (ttyfd >= 0) {
159+ close(ttyfd);
160+ ttyfd = -1;
161+ }
162+ if (!iflag) {
163+ setsignal(SIGTSTP);
164+ setsignal(SIGTTOU);
165+ setsignal(SIGTTIN);
166+ jobctl = 1;
167+ return;
168+ }
169+ out2fmt_flush("sh: can't access tty; job control turned off\n");
170+ mflag = 0;
171+}
172+
173+void
174+setjobctl(int on)
175+{
176+ int i;
177+
178+ if (on == jobctl || rootshell == 0)
179+ return;
180+ if (on) {
181+ if (ttyfd != -1)
182+ close(ttyfd);
183+ if ((ttyfd = open(_PATH_TTY, O_RDWR | O_CLOEXEC)) < 0) {
184+ i = 0;
185+ while (i <= 2 && !isatty(i))
186+ i++;
187+ if (i > 2 ||
188+ (ttyfd = fcntl(i, F_DUPFD_CLOEXEC, 10)) < 0) {
189+ jobctl_notty();
190+ return;
191+ }
192+ }
193+ if (ttyfd < 10) {
194+ /*
195+ * Keep our TTY file descriptor out of the way of
196+ * the user's redirections.
197+ */
198+ if ((i = fcntl(ttyfd, F_DUPFD_CLOEXEC, 10)) < 0) {
199+ jobctl_notty();
200+ return;
201+ }
202+ close(ttyfd);
203+ ttyfd = i;
204+ }
205+ do { /* while we are in the background */
206+ initialpgrp = tcgetpgrp(ttyfd);
207+ if (initialpgrp < 0) {
208+ jobctl_notty();
209+ return;
210+ }
211+ if (initialpgrp != getpgrp()) {
212+ if (!iflag) {
213+ initialpgrp = -1;
214+ jobctl_notty();
215+ return;
216+ }
217+ kill(0, SIGTTIN);
218+ continue;
219+ }
220+ } while (0);
221+ setsignal(SIGTSTP);
222+ setsignal(SIGTTOU);
223+ setsignal(SIGTTIN);
224+ setpgid(0, rootpid);
225+ tcsetpgrp(ttyfd, rootpid);
226+ } else { /* turning job control off */
227+ setpgid(0, initialpgrp);
228+ if (ttyfd >= 0) {
229+ tcsetpgrp(ttyfd, initialpgrp);
230+ close(ttyfd);
231+ ttyfd = -1;
232+ }
233+ setsignal(SIGTSTP);
234+ setsignal(SIGTTOU);
235+ setsignal(SIGTTIN);
236+ }
237+ jobctl = on;
238+}
239+#endif
240+
241+
242+#if JOBS
243+int
244+fgcmd(int argc __unused, char **argv __unused)
245+{
246+ struct job *jp;
247+ pid_t pgrp;
248+ int status;
249+
250+ nextopt("");
251+ jp = getjob(*argptr);
252+ if (jp->jobctl == 0)
253+ error("job not created under job control");
254+ printjobcmd(jp);
255+ flushout(&output);
256+ pgrp = jp->ps[0].pid;
257+ if (ttyfd >= 0)
258+ tcsetpgrp(ttyfd, pgrp);
259+ restartjob(jp);
260+ jp->foreground = 1;
261+ INTOFF;
262+ status = waitforjob(jp, (int *)NULL);
263+ INTON;
264+ return status;
265+}
266+
267+
268+int
269+bgcmd(int argc __unused, char **argv __unused)
270+{
271+ struct job *jp;
272+
273+ nextopt("");
274+ do {
275+ jp = getjob(*argptr);
276+ if (jp->jobctl == 0)
277+ error("job not created under job control");
278+ if (jp->state == JOBDONE)
279+ continue;
280+ restartjob(jp);
281+ jp->foreground = 0;
282+ out1fmt("[%td] ", jp - jobtab + 1);
283+ printjobcmd(jp);
284+ } while (*argptr != NULL && *++argptr != NULL);
285+ return 0;
286+}
287+
288+
289+static void
290+restartjob(struct job *jp)
291+{
292+ struct procstat *ps;
293+ int i;
294+
295+ if (jp->state == JOBDONE)
296+ return;
297+ setcurjob(jp);
298+ INTOFF;
299+ kill(-jp->ps[0].pid, SIGCONT);
300+ for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
301+ if (WIFSTOPPED(ps->status)) {
302+ ps->status = -1;
303+ jp->state = 0;
304+ }
305+ }
306+ INTON;
307+}
308+#endif
309+
310+
311+int
312+jobscmd(int argc __unused, char *argv[] __unused)
313+{
314+ char *id;
315+ int ch, mode;
316+
317+ mode = SHOWJOBS_DEFAULT;
318+ while ((ch = nextopt("lps")) != '\0') {
319+ switch (ch) {
320+ case 'l':
321+ mode = SHOWJOBS_VERBOSE;
322+ break;
323+ case 'p':
324+ mode = SHOWJOBS_PGIDS;
325+ break;
326+ case 's':
327+ mode = SHOWJOBS_PIDS;
328+ break;
329+ }
330+ }
331+
332+ if (*argptr == NULL)
333+ showjobs(0, mode);
334+ else
335+ while ((id = *argptr++) != NULL)
336+ showjob(getjob(id), mode);
337+
338+ return (0);
339+}
340+
341+static int getjobstatus(const struct job *jp)
342+{
343+ int i, status;
344+
345+ if (!jp->pipefail)
346+ return (jp->ps[jp->nprocs - 1].status);
347+ for (i = jp->nprocs - 1; i >= 0; i--) {
348+ status = jp->ps[i].status;
349+ if (status != 0)
350+ return (status);
351+ }
352+ return (0);
353+}
354+
355+static void
356+printjobcmd(struct job *jp)
357+{
358+ struct procstat *ps;
359+ int i;
360+
361+ for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
362+ out1str(ps->cmd);
363+ if (i > 0)
364+ out1str(" | ");
365+ }
366+ out1c('\n');
367+}
368+
369+static void
370+showjob(struct job *jp, int mode)
371+{
372+ char s[64];
373+ char statebuf[16];
374+ const char *statestr, *coredump;
375+ struct procstat *ps;
376+ struct job *j;
377+ int col, curr, i, jobno, prev, procno, status;
378+ char c;
379+
380+ procno = (mode == SHOWJOBS_PGIDS) ? 1 : jp->nprocs;
381+ jobno = jp - jobtab + 1;
382+ curr = prev = 0;
383+#if JOBS
384+ if ((j = getcurjob(NULL)) != NULL) {
385+ curr = j - jobtab + 1;
386+ if ((j = getcurjob(j)) != NULL)
387+ prev = j - jobtab + 1;
388+ }
389+#endif
390+ coredump = "";
391+ status = getjobstatus(jp);
392+ if (jp->state == 0) {
393+ statestr = "Running";
394+#if JOBS
395+ } else if (jp->state == JOBSTOPPED) {
396+ ps = jp->ps + jp->nprocs - 1;
397+ while (!WIFSTOPPED(ps->status) && ps > jp->ps)
398+ ps--;
399+ if (WIFSTOPPED(ps->status))
400+ i = WSTOPSIG(ps->status);
401+ else
402+ i = -1;
403+ statestr = strsignal(i);
404+ if (statestr == NULL)
405+ statestr = "Suspended";
406+#endif
407+ } else if (WIFEXITED(status)) {
408+ if (WEXITSTATUS(status) == 0)
409+ statestr = "Done";
410+ else {
411+ fmtstr(statebuf, sizeof(statebuf), "Done(%d)",
412+ WEXITSTATUS(status));
413+ statestr = statebuf;
414+ }
415+ } else {
416+ i = WTERMSIG(status);
417+ statestr = strsignal(i);
418+ if (statestr == NULL)
419+ statestr = "Unknown signal";
420+ if (WCOREDUMP(status))
421+ coredump = " (core dumped)";
422+ }
423+
424+ for (ps = jp->ps ; procno > 0 ; ps++, procno--) { /* for each process */
425+ if (mode == SHOWJOBS_PIDS || mode == SHOWJOBS_PGIDS) {
426+ out1fmt("%d\n", (int)ps->pid);
427+ continue;
428+ }
429+ if (mode != SHOWJOBS_VERBOSE && ps != jp->ps)
430+ continue;
431+ if (jobno == curr && ps == jp->ps)
432+ c = '+';
433+ else if (jobno == prev && ps == jp->ps)
434+ c = '-';
435+ else
436+ c = ' ';
437+ if (ps == jp->ps)
438+ fmtstr(s, 64, "[%d] %c ", jobno, c);
439+ else
440+ fmtstr(s, 64, " %c ", c);
441+ out1str(s);
442+ col = strlen(s);
443+ if (mode == SHOWJOBS_VERBOSE) {
444+ fmtstr(s, 64, "%d ", (int)ps->pid);
445+ out1str(s);
446+ col += strlen(s);
447+ }
448+ if (ps == jp->ps) {
449+ out1str(statestr);
450+ out1str(coredump);
451+ col += strlen(statestr) + strlen(coredump);
452+ }
453+ do {
454+ out1c(' ');
455+ col++;
456+ } while (col < 30);
457+ if (mode == SHOWJOBS_VERBOSE) {
458+ out1str(ps->cmd);
459+ out1c('\n');
460+ } else
461+ printjobcmd(jp);
462+ }
463+}
464+
465+/*
466+ * Print a list of jobs. If "change" is nonzero, only print jobs whose
467+ * statuses have changed since the last call to showjobs.
468+ *
469+ * If the shell is interrupted in the process of creating a job, the
470+ * result may be a job structure containing zero processes. Such structures
471+ * will be freed here.
472+ */
473+
474+void
475+showjobs(int change, int mode)
476+{
477+ int jobno;
478+ struct job *jp;
479+
480+ TRACE(("showjobs(%d) called\n", change));
481+ checkzombies();
482+ for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
483+ if (! jp->used)
484+ continue;
485+ if (jp->nprocs == 0) {
486+ freejob(jp);
487+ continue;
488+ }
489+ if (change && ! jp->changed)
490+ continue;
491+ showjob(jp, mode);
492+ if (mode == SHOWJOBS_DEFAULT || mode == SHOWJOBS_VERBOSE) {
493+ jp->changed = 0;
494+ /* Hack: discard jobs for which $! has not been
495+ * referenced in interactive mode when they terminate.
496+ */
497+ if (jp->state == JOBDONE && !jp->remembered &&
498+ (iflag || jp != bgjob)) {
499+ freejob(jp);
500+ }
501+ }
502+ }
503+}
504+
505+
506+/*
507+ * Mark a job structure as unused.
508+ */
509+
510+static void
511+freejob(struct job *jp)
512+{
513+ struct procstat *ps;
514+ int i;
515+
516+ INTOFF;
517+ if (bgjob == jp)
518+ bgjob = NULL;
519+ for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
520+ if (ps->cmd != nullstr)
521+ ckfree(ps->cmd);
522+ }
523+ if (jp->ps != &jp->ps0)
524+ ckfree(jp->ps);
525+ jp->used = 0;
526+#if JOBS
527+ deljob(jp);
528+#endif
529+ INTON;
530+}
531+
532+
533+
534+int
535+waitcmd(int argc __unused, char **argv __unused)
536+{
537+ struct job *job;
538+ int retval;
539+
540+ nextopt("");
541+ if (*argptr == NULL)
542+ return (waitcmdloop(NULL));
543+
544+ do {
545+ job = getjob_nonotfound(*argptr);
546+ if (job == NULL)
547+ retval = 127;
548+ else
549+ retval = waitcmdloop(job);
550+ argptr++;
551+ } while (*argptr != NULL);
552+
553+ return (retval);
554+}
555+
556+static int
557+waitcmdloop(struct job *job)
558+{
559+ int status, retval, sig;
560+ struct job *jp;
561+
562+ /*
563+ * Loop until a process is terminated or stopped, or a SIGINT is
564+ * received.
565+ */
566+
567+ do {
568+ if (job != NULL) {
569+ if (job->state == JOBDONE) {
570+ status = getjobstatus(job);
571+ if (WIFEXITED(status))
572+ retval = WEXITSTATUS(status);
573+ else
574+ retval = WTERMSIG(status) + 128;
575+ if (! iflag || ! job->changed)
576+ freejob(job);
577+ else {
578+ job->remembered = 0;
579+ deljob(job);
580+ if (job == bgjob)
581+ bgjob = NULL;
582+ }
583+ return retval;
584+ }
585+ } else {
586+ if (njobs == 0)
587+ return 0;
588+ for (jp = jobtab ; jp < jobtab + njobs; jp++)
589+ if (jp->used && jp->state == JOBDONE) {
590+ if (! iflag || ! jp->changed)
591+ freejob(jp);
592+ else {
593+ jp->remembered = 0;
594+ if (jp == bgjob)
595+ bgjob = NULL;
596+ }
597+ }
598+ for (jp = jobtab ; ; jp++) {
599+ if (jp >= jobtab + njobs) { /* no running procs */
600+ return 0;
601+ }
602+ if (jp->used && jp->state == 0)
603+ break;
604+ }
605+ }
606+ } while (dowait(DOWAIT_BLOCK | DOWAIT_SIG, job) != -1);
607+
608+ sig = pendingsig_waitcmd;
609+ pendingsig_waitcmd = 0;
610+ return sig + 128;
611+}
612+
613+
614+
615+int
616+jobidcmd(int argc __unused, char **argv __unused)
617+{
618+ struct job *jp;
619+ int i;
620+
621+ nextopt("");
622+ jp = getjob(*argptr);
623+ for (i = 0 ; i < jp->nprocs ; ) {
624+ out1fmt("%d", (int)jp->ps[i].pid);
625+ out1c(++i < jp->nprocs? ' ' : '\n');
626+ }
627+ return 0;
628+}
629+
630+
631+
632+/*
633+ * Convert a job name to a job structure.
634+ */
635+
636+static struct job *
637+getjob_nonotfound(const char *name)
638+{
639+ int jobno;
640+ struct job *found, *jp;
641+ size_t namelen;
642+ pid_t pid;
643+ int i;
644+
645+ if (name == NULL) {
646+#if JOBS
647+ name = "%+";
648+#else
649+ error("No current job");
650+#endif
651+ }
652+ if (name[0] == '%') {
653+ if (is_digit(name[1])) {
654+ jobno = number(name + 1);
655+ if (jobno > 0 && jobno <= njobs
656+ && jobtab[jobno - 1].used != 0)
657+ return &jobtab[jobno - 1];
658+#if JOBS
659+ } else if ((name[1] == '%' || name[1] == '+') &&
660+ name[2] == '\0') {
661+ if ((jp = getcurjob(NULL)) == NULL)
662+ error("No current job");
663+ return (jp);
664+ } else if (name[1] == '-' && name[2] == '\0') {
665+ if ((jp = getcurjob(NULL)) == NULL ||
666+ (jp = getcurjob(jp)) == NULL)
667+ error("No previous job");
668+ return (jp);
669+#endif
670+ } else if (name[1] == '?') {
671+ found = NULL;
672+ for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
673+ if (jp->used && jp->nprocs > 0
674+ && strstr(jp->ps[0].cmd, name + 2) != NULL) {
675+ if (found)
676+ error("%s: ambiguous", name);
677+ found = jp;
678+ }
679+ }
680+ if (found != NULL)
681+ return (found);
682+ } else {
683+ namelen = strlen(name);
684+ found = NULL;
685+ for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
686+ if (jp->used && jp->nprocs > 0
687+ && strncmp(jp->ps[0].cmd, name + 1,
688+ namelen - 1) == 0) {
689+ if (found)
690+ error("%s: ambiguous", name);
691+ found = jp;
692+ }
693+ }
694+ if (found)
695+ return found;
696+ }
697+ } else if (is_number(name)) {
698+ pid = (pid_t)number(name);
699+ for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
700+ if (jp->used && jp->nprocs > 0
701+ && jp->ps[jp->nprocs - 1].pid == pid)
702+ return jp;
703+ }
704+ }
705+ return NULL;
706+}
707+
708+
709+static struct job *
710+getjob(const char *name)
711+{
712+ struct job *jp;
713+
714+ jp = getjob_nonotfound(name);
715+ if (jp == NULL)
716+ error("No such job: %s", name);
717+ return (jp);
718+}
719+
720+
721+int
722+killjob(const char *name, int sig)
723+{
724+ struct job *jp;
725+ int i, ret;
726+
727+ jp = getjob(name);
728+ if (jp->state == JOBDONE)
729+ return 0;
730+ if (jp->jobctl)
731+ return kill(-jp->ps[0].pid, sig);
732+ ret = -1;
733+ errno = ESRCH;
734+ for (i = 0; i < jp->nprocs; i++)
735+ if (jp->ps[i].status == -1 || WIFSTOPPED(jp->ps[i].status)) {
736+ if (kill(jp->ps[i].pid, sig) == 0)
737+ ret = 0;
738+ } else
739+ ret = 0;
740+ return ret;
741+}
742+
743+/*
744+ * Return a new job structure,
745+ */
746+
747+struct job *
748+makejob(union node *node __unused, int nprocs)
749+{
750+ int i;
751+ struct job *jp;
752+
753+ for (i = njobs, jp = jobtab ; ; jp++) {
754+ if (--i < 0) {
755+ INTOFF;
756+ if (njobs == 0) {
757+ jobtab = ckmalloc(4 * sizeof jobtab[0]);
758+#if JOBS
759+ jobmru = NULL;
760+#endif
761+ } else {
762+ jp = ckmalloc((njobs + 4) * sizeof jobtab[0]);
763+ memcpy(jp, jobtab, njobs * sizeof jp[0]);
764+#if JOBS
765+ /* Relocate `next' pointers and list head */
766+ if (jobmru != NULL)
767+ jobmru = &jp[jobmru - jobtab];
768+ for (i = 0; i < njobs; i++)
769+ if (jp[i].next != NULL)
770+ jp[i].next = &jp[jp[i].next -
771+ jobtab];
772+#endif
773+ if (bgjob != NULL)
774+ bgjob = &jp[bgjob - jobtab];
775+ /* Relocate `ps' pointers */
776+ for (i = 0; i < njobs; i++)
777+ if (jp[i].ps == &jobtab[i].ps0)
778+ jp[i].ps = &jp[i].ps0;
779+ ckfree(jobtab);
780+ jobtab = jp;
781+ }
782+ jp = jobtab + njobs;
783+ for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0)
784+ ;
785+ INTON;
786+ break;
787+ }
788+ if (jp->used == 0)
789+ break;
790+ }
791+ INTOFF;
792+ jp->state = 0;
793+ jp->used = 1;
794+ jp->changed = 0;
795+ jp->nprocs = 0;
796+ jp->foreground = 0;
797+ jp->remembered = 0;
798+ jp->pipefail = pipefailflag;
799+#if JOBS
800+ jp->jobctl = jobctl;
801+ jp->next = NULL;
802+#endif
803+ if (nprocs > 1) {
804+ jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
805+ } else {
806+ jp->ps = &jp->ps0;
807+ }
808+ INTON;
809+ TRACE(("makejob(%p, %d) returns %%%td\n", (void *)node, nprocs,
810+ jp - jobtab + 1));
811+ return jp;
812+}
813+
814+#if JOBS
815+static void
816+setcurjob(struct job *cj)
817+{
818+ struct job *jp, *prev;
819+
820+ for (prev = NULL, jp = jobmru; jp != NULL; prev = jp, jp = jp->next) {
821+ if (jp == cj) {
822+ if (prev != NULL)
823+ prev->next = jp->next;
824+ else
825+ jobmru = jp->next;
826+ jp->next = jobmru;
827+ jobmru = cj;
828+ return;
829+ }
830+ }
831+ cj->next = jobmru;
832+ jobmru = cj;
833+}
834+
835+static void
836+deljob(struct job *j)
837+{
838+ struct job *jp, *prev;
839+
840+ for (prev = NULL, jp = jobmru; jp != NULL; prev = jp, jp = jp->next) {
841+ if (jp == j) {
842+ if (prev != NULL)
843+ prev->next = jp->next;
844+ else
845+ jobmru = jp->next;
846+ return;
847+ }
848+ }
849+}
850+
851+/*
852+ * Return the most recently used job that isn't `nj', and preferably one
853+ * that is stopped.
854+ */
855+static struct job *
856+getcurjob(struct job *nj)
857+{
858+ struct job *jp;
859+
860+ /* Try to find a stopped one.. */
861+ for (jp = jobmru; jp != NULL; jp = jp->next)
862+ if (jp->used && jp != nj && jp->state == JOBSTOPPED)
863+ return (jp);
864+ /* Otherwise the most recently used job that isn't `nj' */
865+ for (jp = jobmru; jp != NULL; jp = jp->next)
866+ if (jp->used && jp != nj)
867+ return (jp);
868+
869+ return (NULL);
870+}
871+
872+#endif
873+
874+/*
875+ * Fork of a subshell. If we are doing job control, give the subshell its
876+ * own process group. Jp is a job structure that the job is to be added to.
877+ * N is the command that will be evaluated by the child. Both jp and n may
878+ * be NULL. The mode parameter can be one of the following:
879+ * FORK_FG - Fork off a foreground process.
880+ * FORK_BG - Fork off a background process.
881+ * FORK_NOJOB - Like FORK_FG, but don't give the process its own
882+ * process group even if job control is on.
883+ *
884+ * When job control is turned off, background processes have their standard
885+ * input redirected to /dev/null (except for the second and later processes
886+ * in a pipeline).
887+ */
888+
889+pid_t
890+forkshell(struct job *jp, union node *n, int mode)
891+{
892+ pid_t pid;
893+ pid_t pgrp;
894+
895+ TRACE(("forkshell(%%%td, %p, %d) called\n", jp - jobtab, (void *)n,
896+ mode));
897+ INTOFF;
898+ if (mode == FORK_BG && (jp == NULL || jp->nprocs == 0))
899+ checkzombies();
900+ flushall();
901+ pid = fork();
902+ if (pid == -1) {
903+ TRACE(("Fork failed, errno=%d\n", errno));
904+ INTON;
905+ error("Cannot fork: %s", strerror(errno));
906+ }
907+ if (pid == 0) {
908+ struct job *p;
909+ int wasroot;
910+ int i;
911+
912+ TRACE(("Child shell %d\n", (int)getpid()));
913+ wasroot = rootshell;
914+ rootshell = 0;
915+ handler = &main_handler;
916+ closescript();
917+ INTON;
918+ forcelocal = 0;
919+ clear_traps();
920+#if JOBS
921+ jobctl = 0; /* do job control only in root shell */
922+ if (wasroot && mode != FORK_NOJOB && mflag) {
923+ if (jp == NULL || jp->nprocs == 0)
924+ pgrp = getpid();
925+ else
926+ pgrp = jp->ps[0].pid;
927+ if (setpgid(0, pgrp) == 0 && mode == FORK_FG &&
928+ ttyfd >= 0) {
929+ /*
930+ * Each process in a pipeline must have the tty
931+ * pgrp set before running its code.
932+ * Only for pipelines of three or more processes
933+ * could this be reduced to two calls.
934+ */
935+ if (tcsetpgrp(ttyfd, pgrp) < 0)
936+ error("tcsetpgrp failed, errno=%d", errno);
937+ }
938+ setsignal(SIGTSTP);
939+ setsignal(SIGTTOU);
940+ } else if (mode == FORK_BG) {
941+ ignoresig(SIGINT);
942+ ignoresig(SIGQUIT);
943+ if ((jp == NULL || jp->nprocs == 0) &&
944+ ! fd0_redirected_p ()) {
945+ close(0);
946+ if (open(_PATH_DEVNULL, O_RDONLY) != 0)
947+ error("cannot open %s: %s",
948+ _PATH_DEVNULL, strerror(errno));
949+ }
950+ }
951+#else
952+ if (mode == FORK_BG) {
953+ ignoresig(SIGINT);
954+ ignoresig(SIGQUIT);
955+ if ((jp == NULL || jp->nprocs == 0) &&
956+ ! fd0_redirected_p ()) {
957+ close(0);
958+ if (open(_PATH_DEVNULL, O_RDONLY) != 0)
959+ error("cannot open %s: %s",
960+ _PATH_DEVNULL, strerror(errno));
961+ }
962+ }
963+#endif
964+ INTOFF;
965+ for (i = njobs, p = jobtab ; --i >= 0 ; p++)
966+ if (p->used)
967+ freejob(p);
968+ INTON;
969+ if (wasroot && iflag) {
970+ setsignal(SIGINT);
971+ setsignal(SIGQUIT);
972+ setsignal(SIGTERM);
973+ }
974+ return pid;
975+ }
976+ if (rootshell && mode != FORK_NOJOB && mflag) {
977+ if (jp == NULL || jp->nprocs == 0)
978+ pgrp = pid;
979+ else
980+ pgrp = jp->ps[0].pid;
981+ setpgid(pid, pgrp);
982+ }
983+ if (mode == FORK_BG) {
984+ if (bgjob != NULL && bgjob->state == JOBDONE &&
985+ !bgjob->remembered && !iflag)
986+ freejob(bgjob);
987+ backgndpid = pid; /* set $! */
988+ bgjob = jp;
989+ }
990+ if (jp) {
991+ struct procstat *ps = &jp->ps[jp->nprocs++];
992+ ps->pid = pid;
993+ ps->status = -1;
994+ ps->cmd = nullstr;
995+ if (iflag && rootshell && n)
996+ ps->cmd = commandtext(n);
997+ jp->foreground = mode == FORK_FG;
998+#if JOBS
999+ setcurjob(jp);
1000+#endif
1001+ }
1002+ INTON;
1003+ TRACE(("In parent shell: child = %d\n", (int)pid));
1004+ return pid;
1005+}
1006+
1007+
1008+pid_t
1009+vforkexecshell(struct job *jp, char **argv, char **envp, const char *path, int idx, int pip[2])
1010+{
1011+ pid_t pid;
1012+ struct jmploc jmploc;
1013+ struct jmploc *savehandler;
1014+ int inton;
1015+
1016+ TRACE(("vforkexecshell(%%%td, %s, %p) called\n", jp - jobtab, argv[0],
1017+ (void *)pip));
1018+ inton = is_int_on();
1019+ INTOFF;
1020+ flushall();
1021+ savehandler = handler;
1022+ pid = vfork();
1023+ if (pid == -1) {
1024+ TRACE(("Vfork failed, errno=%d\n", errno));
1025+ INTON;
1026+ error("Cannot fork: %s", strerror(errno));
1027+ }
1028+ if (pid == 0) {
1029+ TRACE(("Child shell %d\n", (int)getpid()));
1030+ if (setjmp(jmploc.loc))
1031+ _exit(exitstatus);
1032+ if (pip != NULL) {
1033+ close(pip[0]);
1034+ if (pip[1] != 1) {
1035+ dup2(pip[1], 1);
1036+ close(pip[1]);
1037+ }
1038+ }
1039+ handler = &jmploc;
1040+ shellexec(argv, envp, path, idx);
1041+ }
1042+ handler = savehandler;
1043+ if (jp) {
1044+ struct procstat *ps = &jp->ps[jp->nprocs++];
1045+ ps->pid = pid;
1046+ ps->status = -1;
1047+ ps->cmd = nullstr;
1048+ jp->foreground = 1;
1049+#if JOBS
1050+ setcurjob(jp);
1051+#endif
1052+ }
1053+ SETINTON(inton);
1054+ TRACE(("In parent shell: child = %d\n", (int)pid));
1055+ return pid;
1056+}
1057+
1058+
1059+/*
1060+ * Wait for job to finish.
1061+ *
1062+ * Under job control we have the problem that while a child process is
1063+ * running interrupts generated by the user are sent to the child but not
1064+ * to the shell. This means that an infinite loop started by an inter-
1065+ * active user may be hard to kill. With job control turned off, an
1066+ * interactive user may place an interactive program inside a loop. If
1067+ * the interactive program catches interrupts, the user doesn't want
1068+ * these interrupts to also abort the loop. The approach we take here
1069+ * is to have the shell ignore interrupt signals while waiting for a
1070+ * foreground process to terminate, and then send itself an interrupt
1071+ * signal if the child process was terminated by an interrupt signal.
1072+ * Unfortunately, some programs want to do a bit of cleanup and then
1073+ * exit on interrupt; unless these processes terminate themselves by
1074+ * sending a signal to themselves (instead of calling exit) they will
1075+ * confuse this approach.
1076+ */
1077+
1078+int
1079+waitforjob(struct job *jp, int *signaled)
1080+{
1081+#if JOBS
1082+ int propagate_int = jp->jobctl && jp->foreground;
1083+#endif
1084+ int jobindex;
1085+ int status;
1086+ int st;
1087+
1088+ INTOFF;
1089+ TRACE(("waitforjob(%%%td) called\n", jp - jobtab + 1));
1090+ while (jp->state == 0)
1091+ if (dowait(DOWAIT_BLOCK | (Tflag ? DOWAIT_SIG |
1092+ DOWAIT_SIG_TRAP : 0), jp) == -1) {
1093+ jobindex = jp - jobtab;
1094+ dotrap();
1095+ jp = jobtab + jobindex;
1096+ }
1097+#if JOBS
1098+ if (jp->jobctl) {
1099+ if (ttyfd >= 0 && tcsetpgrp(ttyfd, rootpid) < 0)
1100+ error("tcsetpgrp failed, errno=%d\n", errno);
1101+ }
1102+ if (jp->state == JOBSTOPPED)
1103+ setcurjob(jp);
1104+#endif
1105+ status = getjobstatus(jp);
1106+ if (signaled != NULL)
1107+ *signaled = WIFSIGNALED(status);
1108+ /* convert to 8 bits */
1109+ if (WIFEXITED(status))
1110+ st = WEXITSTATUS(status);
1111+#if JOBS
1112+ else if (WIFSTOPPED(status))
1113+ st = WSTOPSIG(status) + 128;
1114+#endif
1115+ else
1116+ st = WTERMSIG(status) + 128;
1117+ if (! JOBS || jp->state == JOBDONE)
1118+ freejob(jp);
1119+ if (int_pending()) {
1120+ if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGINT)
1121+ CLEAR_PENDING_INT;
1122+ }
1123+#if JOBS
1124+ else if (rootshell && propagate_int &&
1125+ WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
1126+ kill(getpid(), SIGINT);
1127+#endif
1128+ INTON;
1129+ return st;
1130+}
1131+
1132+
1133+static void
1134+dummy_handler(int sig __unused)
1135+{
1136+}
1137+
1138+/*
1139+ * Wait for a process to terminate.
1140+ */
1141+
1142+static pid_t
1143+dowait(int mode, struct job *job)
1144+{
1145+ struct sigaction sa, osa;
1146+ sigset_t mask, omask;
1147+ pid_t pid;
1148+ int status;
1149+ struct procstat *sp;
1150+ struct job *jp;
1151+ struct job *thisjob;
1152+ const char *sigstr;
1153+ int done;
1154+ int stopped;
1155+ int sig;
1156+ int coredump;
1157+ int wflags;
1158+ int restore_sigchld;
1159+
1160+ TRACE(("dowait(%d, %p) called\n", mode, job));
1161+ restore_sigchld = 0;
1162+ if ((mode & DOWAIT_SIG) != 0) {
1163+ sigfillset(&mask);
1164+ sigprocmask(SIG_BLOCK, &mask, &omask);
1165+ INTOFF;
1166+ if (!issigchldtrapped()) {
1167+ restore_sigchld = 1;
1168+ sa.sa_handler = dummy_handler;
1169+ sa.sa_flags = 0;
1170+ sigemptyset(&sa.sa_mask);
1171+ sigaction(SIGCHLD, &sa, &osa);
1172+ }
1173+ }
1174+ do {
1175+#if JOBS
1176+ if (iflag)
1177+ wflags = WUNTRACED | WCONTINUED;
1178+ else
1179+#endif
1180+ wflags = 0;
1181+ if ((mode & (DOWAIT_BLOCK | DOWAIT_SIG)) != DOWAIT_BLOCK)
1182+ wflags |= WNOHANG;
1183+ pid = wait3(&status, wflags, (struct rusage *)NULL);
1184+ TRACE(("wait returns %d, status=%d\n", (int)pid, status));
1185+ if (pid == 0 && (mode & DOWAIT_SIG) != 0) {
1186+ pid = -1;
1187+ if (((mode & DOWAIT_SIG_TRAP) != 0 ?
1188+ pendingsig : pendingsig_waitcmd) != 0) {
1189+ errno = EINTR;
1190+ break;
1191+ }
1192+ sigsuspend(&omask);
1193+ if (int_pending())
1194+ break;
1195+ }
1196+ } while (pid == -1 && errno == EINTR);
1197+ if (pid == -1 && errno == ECHILD && job != NULL)
1198+ job->state = JOBDONE;
1199+ if ((mode & DOWAIT_SIG) != 0) {
1200+ if (restore_sigchld)
1201+ sigaction(SIGCHLD, &osa, NULL);
1202+ sigprocmask(SIG_SETMASK, &omask, NULL);
1203+ INTON;
1204+ }
1205+ if (pid <= 0)
1206+ return pid;
1207+ INTOFF;
1208+ thisjob = NULL;
1209+ for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
1210+ if (jp->used && jp->nprocs > 0) {
1211+ done = 1;
1212+ stopped = 1;
1213+ for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
1214+ if (sp->pid == -1)
1215+ continue;
1216+ if (sp->pid == pid && (sp->status == -1 ||
1217+ WIFSTOPPED(sp->status))) {
1218+ TRACE(("Changing status of proc %d from 0x%x to 0x%x\n",
1219+ (int)pid, sp->status,
1220+ status));
1221+ if (WIFCONTINUED(status)) {
1222+ sp->status = -1;
1223+ jp->state = 0;
1224+ } else
1225+ sp->status = status;
1226+ thisjob = jp;
1227+ }
1228+ if (sp->status == -1)
1229+ stopped = 0;
1230+ else if (WIFSTOPPED(sp->status))
1231+ done = 0;
1232+ }
1233+ if (stopped) { /* stopped or done */
1234+ int state = done? JOBDONE : JOBSTOPPED;
1235+ if (jp->state != state) {
1236+ TRACE(("Job %td: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
1237+ jp->state = state;
1238+ if (jp != job) {
1239+ if (done && !jp->remembered &&
1240+ !iflag && jp != bgjob)
1241+ freejob(jp);
1242+#if JOBS
1243+ else if (done)
1244+ deljob(jp);
1245+#endif
1246+ }
1247+ }
1248+ }
1249+ }
1250+ }
1251+ INTON;
1252+ if (!thisjob || thisjob->state == 0)
1253+ ;
1254+ else if ((!rootshell || !iflag || thisjob == job) &&
1255+ thisjob->foreground && thisjob->state != JOBSTOPPED) {
1256+ sig = 0;
1257+ coredump = 0;
1258+ for (sp = thisjob->ps; sp < thisjob->ps + thisjob->nprocs; sp++)
1259+ if (WIFSIGNALED(sp->status)) {
1260+ sig = WTERMSIG(sp->status);
1261+ coredump = WCOREDUMP(sp->status);
1262+ }
1263+ if (sig > 0 && sig != SIGINT && sig != SIGPIPE) {
1264+ sigstr = strsignal(sig);
1265+ if (sigstr != NULL)
1266+ out2str(sigstr);
1267+ else
1268+ out2str("Unknown signal");
1269+ if (coredump)
1270+ out2str(" (core dumped)");
1271+ out2c('\n');
1272+ flushout(out2);
1273+ }
1274+ } else {
1275+ TRACE(("Not printing status, rootshell=%d, job=%p\n", rootshell, job));
1276+ thisjob->changed = 1;
1277+ }
1278+ return pid;
1279+}
1280+
1281+
1282+
1283+/*
1284+ * return 1 if there are stopped jobs, otherwise 0
1285+ */
1286+int job_warning = 0;
1287+int
1288+stoppedjobs(void)
1289+{
1290+ int jobno;
1291+ struct job *jp;
1292+
1293+ if (job_warning)
1294+ return (0);
1295+ for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) {
1296+ if (jp->used == 0)
1297+ continue;
1298+ if (jp->state == JOBSTOPPED) {
1299+ out2fmt_flush("You have stopped jobs.\n");
1300+ job_warning = 2;
1301+ return (1);
1302+ }
1303+ }
1304+
1305+ return (0);
1306+}
1307+
1308+
1309+static void
1310+checkzombies(void)
1311+{
1312+ while (njobs > 0 && dowait(0, NULL) > 0)
1313+ ;
1314+}
1315+
1316+
1317+int
1318+backgndpidset(void)
1319+{
1320+ return backgndpid != -1;
1321+}
1322+
1323+
1324+pid_t
1325+backgndpidval(void)
1326+{
1327+ if (bgjob != NULL && !forcelocal)
1328+ bgjob->remembered = 1;
1329+ return backgndpid;
1330+}
1331+
1332+/*
1333+ * Return a string identifying a command (to be printed by the
1334+ * jobs command.
1335+ */
1336+
1337+static char *cmdnextc;
1338+static int cmdnleft;
1339+#define MAXCMDTEXT 200
1340+
1341+char *
1342+commandtext(union node *n)
1343+{
1344+ char *name;
1345+
1346+ cmdnextc = name = ckmalloc(MAXCMDTEXT);
1347+ cmdnleft = MAXCMDTEXT - 4;
1348+ cmdtxt(n);
1349+ *cmdnextc = '\0';
1350+ return name;
1351+}
1352+
1353+
1354+static void
1355+cmdtxtdogroup(union node *n)
1356+{
1357+ cmdputs("; do ");
1358+ cmdtxt(n);
1359+ cmdputs("; done");
1360+}
1361+
1362+
1363+static void
1364+cmdtxtredir(union node *n, const char *op, int deffd)
1365+{
1366+ char s[2];
1367+
1368+ if (n->nfile.fd != deffd) {
1369+ s[0] = n->nfile.fd + '0';
1370+ s[1] = '\0';
1371+ cmdputs(s);
1372+ }
1373+ cmdputs(op);
1374+ if (n->type == NTOFD || n->type == NFROMFD) {
1375+ if (n->ndup.dupfd >= 0)
1376+ s[0] = n->ndup.dupfd + '0';
1377+ else
1378+ s[0] = '-';
1379+ s[1] = '\0';
1380+ cmdputs(s);
1381+ } else {
1382+ cmdtxt(n->nfile.fname);
1383+ }
1384+}
1385+
1386+
1387+static void
1388+cmdtxt(union node *n)
1389+{
1390+ union node *np;
1391+ struct nodelist *lp;
1392+
1393+ if (n == NULL)
1394+ return;
1395+ switch (n->type) {
1396+ case NSEMI:
1397+ cmdtxt(n->nbinary.ch1);
1398+ cmdputs("; ");
1399+ cmdtxt(n->nbinary.ch2);
1400+ break;
1401+ case NAND:
1402+ cmdtxt(n->nbinary.ch1);
1403+ cmdputs(" && ");
1404+ cmdtxt(n->nbinary.ch2);
1405+ break;
1406+ case NOR:
1407+ cmdtxt(n->nbinary.ch1);
1408+ cmdputs(" || ");
1409+ cmdtxt(n->nbinary.ch2);
1410+ break;
1411+ case NPIPE:
1412+ for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
1413+ cmdtxt(lp->n);
1414+ if (lp->next)
1415+ cmdputs(" | ");
1416+ }
1417+ break;
1418+ case NSUBSHELL:
1419+ cmdputs("(");
1420+ cmdtxt(n->nredir.n);
1421+ cmdputs(")");
1422+ break;
1423+ case NREDIR:
1424+ case NBACKGND:
1425+ cmdtxt(n->nredir.n);
1426+ break;
1427+ case NIF:
1428+ cmdputs("if ");
1429+ cmdtxt(n->nif.test);
1430+ cmdputs("; then ");
1431+ cmdtxt(n->nif.ifpart);
1432+ cmdputs("...");
1433+ break;
1434+ case NWHILE:
1435+ cmdputs("while ");
1436+ cmdtxt(n->nbinary.ch1);
1437+ cmdtxtdogroup(n->nbinary.ch2);
1438+ break;
1439+ case NUNTIL:
1440+ cmdputs("until ");
1441+ cmdtxt(n->nbinary.ch1);
1442+ cmdtxtdogroup(n->nbinary.ch2);
1443+ break;
1444+ case NFOR:
1445+ cmdputs("for ");
1446+ cmdputs(n->nfor.var);
1447+ cmdputs(" in ...");
1448+ break;
1449+ case NCASE:
1450+ cmdputs("case ");
1451+ cmdputs(n->ncase.expr->narg.text);
1452+ cmdputs(" in ...");
1453+ break;
1454+ case NDEFUN:
1455+ cmdputs(n->narg.text);
1456+ cmdputs("() ...");
1457+ break;
1458+ case NNOT:
1459+ cmdputs("! ");
1460+ cmdtxt(n->nnot.com);
1461+ break;
1462+ case NCMD:
1463+ for (np = n->ncmd.args ; np ; np = np->narg.next) {
1464+ cmdtxt(np);
1465+ if (np->narg.next)
1466+ cmdputs(" ");
1467+ }
1468+ for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
1469+ cmdputs(" ");
1470+ cmdtxt(np);
1471+ }
1472+ break;
1473+ case NARG:
1474+ cmdputs(n->narg.text);
1475+ break;
1476+ case NTO:
1477+ cmdtxtredir(n, ">", 1);
1478+ break;
1479+ case NAPPEND:
1480+ cmdtxtredir(n, ">>", 1);
1481+ break;
1482+ case NTOFD:
1483+ cmdtxtredir(n, ">&", 1);
1484+ break;
1485+ case NCLOBBER:
1486+ cmdtxtredir(n, ">|", 1);
1487+ break;
1488+ case NFROM:
1489+ cmdtxtredir(n, "<", 0);
1490+ break;
1491+ case NFROMTO:
1492+ cmdtxtredir(n, "<>", 0);
1493+ break;
1494+ case NFROMFD:
1495+ cmdtxtredir(n, "<&", 0);
1496+ break;
1497+ case NHERE:
1498+ case NXHERE:
1499+ cmdputs("<<...");
1500+ break;
1501+ default:
1502+ cmdputs("???");
1503+ break;
1504+ }
1505+}
1506+
1507+
1508+
1509+static void
1510+cmdputs(const char *s)
1511+{
1512+ const char *p;
1513+ char *q;
1514+ char c;
1515+ int subtype = 0;
1516+
1517+ if (cmdnleft <= 0)
1518+ return;
1519+ p = s;
1520+ q = cmdnextc;
1521+ while ((c = *p++) != '\0') {
1522+ if (c == CTLESC)
1523+ *q++ = *p++;
1524+ else if (c == CTLVAR) {
1525+ *q++ = '$';
1526+ if (--cmdnleft > 0)
1527+ *q++ = '{';
1528+ subtype = *p++;
1529+ if ((subtype & VSTYPE) == VSLENGTH && --cmdnleft > 0)
1530+ *q++ = '#';
1531+ } else if (c == '=' && subtype != 0) {
1532+ *q = "}-+?=##%%\0X"[(subtype & VSTYPE) - VSNORMAL];
1533+ if (*q)
1534+ q++;
1535+ else
1536+ cmdnleft++;
1537+ if (((subtype & VSTYPE) == VSTRIMLEFTMAX ||
1538+ (subtype & VSTYPE) == VSTRIMRIGHTMAX) &&
1539+ --cmdnleft > 0)
1540+ *q = q[-1], q++;
1541+ subtype = 0;
1542+ } else if (c == CTLENDVAR) {
1543+ *q++ = '}';
1544+ } else if (c == CTLBACKQ || c == CTLBACKQ+CTLQUOTE) {
1545+ cmdnleft -= 5;
1546+ if (cmdnleft > 0) {
1547+ *q++ = '$';
1548+ *q++ = '(';
1549+ *q++ = '.';
1550+ *q++ = '.';
1551+ *q++ = '.';
1552+ *q++ = ')';
1553+ }
1554+ } else if (c == CTLARI) {
1555+ cmdnleft -= 2;
1556+ if (cmdnleft > 0) {
1557+ *q++ = '$';
1558+ *q++ = '(';
1559+ *q++ = '(';
1560+ }
1561+ p++;
1562+ } else if (c == CTLENDARI) {
1563+ if (--cmdnleft > 0) {
1564+ *q++ = ')';
1565+ *q++ = ')';
1566+ }
1567+ } else if (c == CTLQUOTEMARK || c == CTLQUOTEEND)
1568+ cmdnleft++; /* ignore */
1569+ else
1570+ *q++ = c;
1571+ if (--cmdnleft <= 0) {
1572+ *q++ = '.';
1573+ *q++ = '.';
1574+ *q++ = '.';
1575+ break;
1576+ }
1577+ }
1578+ cmdnextc = q;
1579+}
+66,
-0
1@@ -0,0 +1,66 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
37+#define FORK_FG 0
38+#define FORK_BG 1
39+#define FORK_NOJOB 2
40+
41+#include <signal.h> /* for sig_atomic_t */
42+
43+struct job;
44+
45+enum {
46+ SHOWJOBS_DEFAULT, /* job number, status, command */
47+ SHOWJOBS_VERBOSE, /* job number, PID, status, command */
48+ SHOWJOBS_PIDS, /* PID only */
49+ SHOWJOBS_PGIDS /* PID of the group leader only */
50+};
51+
52+extern int job_warning; /* user was warned about stopped jobs */
53+
54+void setjobctl(int);
55+void showjobs(int, int);
56+struct job *makejob(union node *, int);
57+pid_t forkshell(struct job *, union node *, int);
58+pid_t vforkexecshell(struct job *, char **, char **, const char *, int, int [2]);
59+int waitforjob(struct job *, int *);
60+int stoppedjobs(void);
61+int backgndpidset(void);
62+pid_t backgndpidval(void);
63+char *commandtext(union node *);
64+
65+#if ! JOBS
66+#define setjobctl(on) /* do nothing */
67+#endif
+187,
-0
1@@ -0,0 +1,187 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1988, 1993, 1994
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * Redistribution and use in source and binary forms, with or without
9+ * modification, are permitted provided that the following conditions
10+ * are met:
11+ * 1. Redistributions of source code must retain the above copyright
12+ * notice, this list of conditions and the following disclaimer.
13+ * 2. Redistributions in binary form must reproduce the above copyright
14+ * notice, this list of conditions and the following disclaimer in the
15+ * documentation and/or other materials provided with the distribution.
16+ * 3. Neither the name of the University nor the names of its contributors
17+ * may be used to endorse or promote products derived from this software
18+ * without specific prior written permission.
19+ *
20+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30+ * SUCH DAMAGE.
31+ */
32+/*
33+ * Important: This file is used both as a standalone program /bin/kill and
34+ * as a builtin for /bin/sh (#define SHELL).
35+ */
36+
37+#include "sig.h"
38+
39+#ifdef SHELL
40+#define main killcmd
41+#include "bltin.h"
42+#endif
43+
44+#ifndef __dead2
45+#define __dead2 __attribute__((__noreturn__))
46+#endif
47+
48+#include <ctype.h>
49+#ifndef SHELL
50+#include <err.h>
51+#endif
52+#include <errno.h>
53+#include <signal.h>
54+#include <stdio.h>
55+#include <stdlib.h>
56+#include <string.h>
57+
58+static void nosig(const char *);
59+static void printsignals(FILE *);
60+static void usage(void) __dead2;
61+
62+int
63+main(int argc, char *argv[])
64+{
65+ char signame[SIG2STR_MAX];
66+ long pidl;
67+ pid_t pid;
68+ int errors, numsig, ret;
69+ char *ep;
70+
71+ if (argc < 2)
72+ usage();
73+
74+ numsig = SIGTERM;
75+
76+ argc--, argv++;
77+ if (!strcmp(*argv, "-l")) {
78+ argc--, argv++;
79+ if (argc > 1)
80+ usage();
81+ if (argc == 1) {
82+ if (!isdigit(**argv))
83+ usage();
84+ numsig = strtol(*argv, &ep, 10);
85+ if (!**argv || *ep)
86+ errx(2, "invalid signal number: %s", *argv);
87+ if (numsig >= 128)
88+ numsig -= 128;
89+ if (sig2str(numsig, signame) < 0)
90+ nosig(*argv);
91+ printf("%s\n", signame);
92+ return (0);
93+ }
94+ printsignals(stdout);
95+ return (0);
96+ }
97+
98+ if (!strcmp(*argv, "-s")) {
99+ argc--, argv++;
100+ if (argc < 1) {
101+ warnx("option requires an argument -- s");
102+ usage();
103+ }
104+ if (strcmp(*argv, "0") == 0)
105+ numsig = 0;
106+ else if (str2sig(*argv, &numsig) < 0)
107+ nosig(*argv);
108+ argc--, argv++;
109+ } else if (**argv == '-' && *(*argv + 1) != '-') {
110+ ++*argv;
111+ if (strcmp(*argv, "0") == 0)
112+ numsig = 0;
113+ else if (str2sig(*argv, &numsig) < 0)
114+ nosig(*argv);
115+ argc--, argv++;
116+ }
117+
118+ if (argc > 0 && strncmp(*argv, "--", 2) == 0)
119+ argc--, argv++;
120+
121+ if (argc == 0)
122+ usage();
123+
124+ for (errors = 0; argc; argc--, argv++) {
125+#ifdef SHELL
126+ if (**argv == '%')
127+ ret = killjob(*argv, numsig);
128+ else
129+#endif
130+ {
131+ pidl = strtol(*argv, &ep, 10);
132+ /* Check for overflow of pid_t. */
133+ pid = (pid_t)pidl;
134+ if (!**argv || *ep || pid != pidl)
135+ errx(2, "illegal process id: %s", *argv);
136+ ret = kill(pid, numsig);
137+ }
138+ if (ret == -1) {
139+ warn("%s", *argv);
140+ errors = 1;
141+ }
142+ }
143+
144+ return (errors);
145+}
146+
147+static void
148+nosig(const char *name)
149+{
150+
151+ warnx("unknown signal %s; valid signals:", name);
152+ printsignals(stderr);
153+#ifdef SHELL
154+ error(NULL);
155+#else
156+ exit(2);
157+#endif
158+}
159+
160+static void
161+printsignals(FILE *fp)
162+{
163+ int n;
164+
165+ for (n = 1; n < sys_nsig; n++) {
166+ (void)fprintf(fp, "%s", sys_signame[n]);
167+ if (n == (sys_nsig / 2) || n == (sys_nsig - 1))
168+ (void)fprintf(fp, "\n");
169+ else
170+ (void)fprintf(fp, " ");
171+ }
172+}
173+
174+static void
175+usage(void)
176+{
177+
178+ (void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
179+ "usage: kill [-s signal_name] pid ...",
180+ " kill -l [exit_status]",
181+ " kill -signal_name pid ...",
182+ " kill -signal_number pid ...");
183+#ifdef SHELL
184+ error(NULL);
185+#else
186+ exit(2);
187+#endif
188+}
+113,
-0
1@@ -0,0 +1,113 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+/*
37+ * Routines to check for mail. (Perhaps make part of main.c?)
38+ */
39+
40+#include "shell.h"
41+#include "mail.h"
42+#include "var.h"
43+#include "output.h"
44+#include "memalloc.h"
45+#include "error.h"
46+#include <sys/types.h>
47+#include <sys/stat.h>
48+#include <stdlib.h>
49+
50+
51+#define MAXMBOXES 10
52+
53+
54+static int nmboxes; /* number of mailboxes */
55+static time_t mailtime[MAXMBOXES]; /* times of mailboxes */
56+
57+
58+
59+/*
60+ * Print appropriate message(s) if mail has arrived. If the argument is
61+ * non-zero, then the value of MAIL has changed, so we just update the
62+ * values.
63+ */
64+
65+void
66+chkmail(int silent)
67+{
68+ int i;
69+ char *mpath;
70+ char *p;
71+ char *msg;
72+ struct stackmark smark;
73+ struct stat statb;
74+
75+ if (silent)
76+ nmboxes = 10;
77+ if (nmboxes == 0)
78+ return;
79+ setstackmark(&smark);
80+ mpath = stsavestr(mpathset()? mpathval() : mailval());
81+ for (i = 0 ; i < nmboxes ; i++) {
82+ p = mpath;
83+ if (*p == '\0')
84+ break;
85+ mpath = strchrnul(mpath, ':');
86+ if (*mpath != '\0') {
87+ *mpath++ = '\0';
88+ if (p == mpath - 1)
89+ continue;
90+ }
91+ msg = strchr(p, '%');
92+ if (msg != NULL)
93+ *msg++ = '\0';
94+#ifdef notdef /* this is what the System V shell claims to do (it lies) */
95+ if (stat(p, &statb) < 0)
96+ statb.st_mtime = 0;
97+ if (statb.st_mtime > mailtime[i] && ! silent) {
98+ out2str(msg? msg : "you have mail");
99+ out2c('\n');
100+ }
101+ mailtime[i] = statb.st_mtime;
102+#else /* this is what it should do */
103+ if (stat(p, &statb) < 0)
104+ statb.st_size = 0;
105+ if (statb.st_size > mailtime[i] && ! silent) {
106+ out2str(msg? msg : "you have mail");
107+ out2c('\n');
108+ }
109+ mailtime[i] = statb.st_size;
110+#endif
111+ }
112+ nmboxes = i;
113+ popstackmark(&smark);
114+}
+35,
-0
1@@ -0,0 +1,35 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+void chkmail(int);
+367,
-0
1@@ -0,0 +1,367 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#include <stdio.h>
37+#include <signal.h>
38+#include <sys/stat.h>
39+#include <unistd.h>
40+#include <fcntl.h>
41+#include <locale.h>
42+#include <errno.h>
43+#include <termios.h>
44+#include <paths.h>
45+#include <unistd.h>
46+
47+#include "shell.h"
48+#include "main.h"
49+#include "mail.h"
50+#include "options.h"
51+#include "output.h"
52+#include "parser.h"
53+#include "nodes.h"
54+#include "expand.h"
55+#include "eval.h"
56+#include "jobs.h"
57+#include "input.h"
58+#include "trap.h"
59+#include "var.h"
60+#include "show.h"
61+#include "memalloc.h"
62+#include "error.h"
63+#include "mystring.h"
64+#include "exec.h"
65+#include "cd.h"
66+#include "redir.h"
67+#include "builtins.h"
68+#ifndef NO_HISTORY
69+#include "myhistedit.h"
70+#endif
71+
72+int rootpid;
73+int rootshell;
74+struct jmploc main_handler;
75+int localeisutf8, initial_localeisutf8;
76+
77+static void reset(void);
78+static void cmdloop(int);
79+static void read_profile(const char *);
80+static char *find_dot_file(char *);
81+
82+/*
83+ * Main routine. We initialize things, parse the arguments, execute
84+ * profiles if we're a login shell, and then call cmdloop to execute
85+ * commands. The setjmp call sets up the location to jump to when an
86+ * exception occurs. When an exception occurs the variable "state"
87+ * is used to figure out how far we had gotten.
88+ */
89+
90+int
91+main(int argc, char *argv[])
92+{
93+ /*
94+ * As smark is accessed after a longjmp, it cannot be a local in main().
95+ * The C standard specifies that the values of non-volatile local
96+ * variables are unspecified after a jump if modified between the
97+ * setjmp and longjmp.
98+ */
99+ static struct stackmark smark, smark2;
100+ volatile int state;
101+ char *shinit;
102+ int login;
103+
104+ (void) setlocale(LC_ALL, "");
105+ initcharset();
106+ state = 0;
107+ if (setjmp(main_handler.loc)) {
108+ if (state == 0 || iflag == 0 || ! rootshell ||
109+ exception == EXEXIT)
110+ exitshell(exitstatus);
111+ reset();
112+ if (exception == EXINT)
113+ out2fmt_flush("\n");
114+ popstackmark(&smark);
115+ FORCEINTON; /* enable interrupts */
116+ if (state == 1)
117+ goto state1;
118+ else if (state == 2)
119+ goto state2;
120+ else if (state == 3)
121+ goto state3;
122+ else
123+ goto state4;
124+ }
125+ handler = &main_handler;
126+#ifdef DEBUG
127+ opentrace();
128+ trputs("Shell args: "); trargs(argv);
129+#endif
130+ rootpid = getpid();
131+ if (rootpid == 1) {
132+ /*
133+ * Make sh usable for invocation as interactive init
134+ * substitute with init_path=/bin/sh, by opening
135+ * file descriptors 0, 1, and 2 on /dev/console.
136+ */
137+ if (fcntl(STDIN_FILENO, F_GETFL, NULL) == -1 && errno == EBADF) {
138+ (void)open(_PATH_CONSOLE, O_RDWR);
139+ (void)setsid();
140+ (void)tcsetsid(STDIN_FILENO, rootpid);
141+ }
142+ if (fcntl(STDOUT_FILENO, F_GETFL, NULL) == -1 && errno == EBADF)
143+ (void)dup2(STDIN_FILENO, STDOUT_FILENO);
144+ if (fcntl(STDERR_FILENO, F_GETFL, NULL) == -1 && errno == EBADF)
145+ (void)dup2(STDIN_FILENO, STDERR_FILENO);
146+ }
147+ rootshell = 1;
148+ INTOFF;
149+ initvar();
150+ setstackmark(&smark);
151+ setstackmark(&smark2);
152+ login = procargs(argc, argv);
153+ trap_init();
154+ pwd_init(iflag);
155+ INTON;
156+ if (iflag)
157+ chkmail(1);
158+ if (login) {
159+ state = 1;
160+ read_profile("/etc/profile");
161+state1:
162+ state = 2;
163+ if (privileged == 0)
164+ read_profile("${HOME-}/.profile");
165+ else
166+ read_profile("/etc/suid_profile");
167+ }
168+state2:
169+ state = 3;
170+ if (!privileged && iflag) {
171+ if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
172+ state = 3;
173+ read_profile(shinit);
174+ }
175+ }
176+#ifndef NO_HISTORY
177+ if (iflag)
178+ histload();
179+#endif
180+state3:
181+ state = 4;
182+ popstackmark(&smark2);
183+ if (minusc) {
184+ evalstring(minusc, sflag ? 0 : EV_EXIT);
185+ }
186+state4:
187+ if (sflag || minusc == NULL) {
188+ cmdloop(1);
189+ }
190+ exitshell(exitstatus);
191+ /*NOTREACHED*/
192+ return 0;
193+}
194+
195+static void
196+reset(void)
197+{
198+ reseteval();
199+ resetinput();
200+}
201+
202+/*
203+ * Read and execute commands. "Top" is nonzero for the top level command
204+ * loop; it turns on prompting if the shell is interactive.
205+ */
206+
207+static void
208+cmdloop(int top)
209+{
210+ union node *n;
211+ struct stackmark smark;
212+ int inter;
213+ int numeof = 0;
214+
215+ TRACE(("cmdloop(%d) called\n", top));
216+ setstackmark(&smark);
217+ for (;;) {
218+ if (pendingsig)
219+ dotrap();
220+ inter = 0;
221+ if (iflag && top) {
222+ inter++;
223+ showjobs(1, SHOWJOBS_DEFAULT);
224+ chkmail(0);
225+ flushout(&output);
226+ }
227+ n = parsecmd(inter);
228+ /* showtree(n); DEBUG */
229+ if (n == NEOF) {
230+ if (!top || numeof >= 50)
231+ break;
232+ if (!stoppedjobs()) {
233+ if (!Iflag)
234+ break;
235+ out2fmt_flush("\nUse \"exit\" to leave shell.\n");
236+ }
237+ numeof++;
238+ } else if (n != NULL && nflag == 0) {
239+ job_warning = (job_warning == 2) ? 1 : 0;
240+ numeof = 0;
241+ evaltree(n, 0);
242+ }
243+ popstackmark(&smark);
244+ setstackmark(&smark);
245+ if (evalskip != 0) {
246+ if (evalskip == SKIPRETURN)
247+ evalskip = 0;
248+ break;
249+ }
250+ }
251+ popstackmark(&smark);
252+ if (top && iflag) {
253+ out2c('\n');
254+ flushout(out2);
255+ }
256+}
257+
258+
259+
260+/*
261+ * Read /etc/profile or .profile. Return on error.
262+ */
263+
264+static void
265+read_profile(const char *name)
266+{
267+ int fd;
268+ const char *expandedname;
269+ int oflags = O_RDONLY | O_CLOEXEC;
270+
271+ if (verifyflag)
272+ oflags |= O_VERIFY;
273+
274+ expandedname = expandstr(name);
275+ if (expandedname == NULL)
276+ return;
277+ INTOFF;
278+ if ((fd = open(expandedname, oflags)) >= 0)
279+ setinputfd(fd, 1);
280+ INTON;
281+ if (fd < 0)
282+ return;
283+ cmdloop(0);
284+ popfile();
285+}
286+
287+
288+
289+/*
290+ * Read a file containing shell functions.
291+ */
292+
293+void
294+readcmdfile(const char *name, int verify)
295+{
296+ setinputfile(name, 1, verify);
297+ cmdloop(0);
298+ popfile();
299+}
300+
301+
302+
303+/*
304+ * Take commands from a file. To be compatible we should do a path
305+ * search for the file, which is necessary to find sub-commands.
306+ */
307+
308+
309+static char *
310+find_dot_file(char *basename)
311+{
312+ char *fullname;
313+ const char *opt;
314+ const char *path = pathval();
315+ struct stat statb;
316+
317+ /* don't try this for absolute or relative paths */
318+ if( strchr(basename, '/'))
319+ return basename;
320+
321+ while ((fullname = padvance(&path, &opt, basename)) != NULL) {
322+ if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
323+ /*
324+ * Don't bother freeing here, since it will
325+ * be freed by the caller.
326+ */
327+ return fullname;
328+ }
329+ stunalloc(fullname);
330+ }
331+ return basename;
332+}
333+
334+int
335+dotcmd(int argc, char **argv)
336+{
337+ char *filename, *fullname;
338+
339+ if (argc < 2)
340+ error("missing filename");
341+
342+ exitstatus = 0;
343+
344+ /*
345+ * Because we have historically not supported any options,
346+ * only treat "--" specially.
347+ */
348+ filename = argc > 2 && strcmp(argv[1], "--") == 0 ? argv[2] : argv[1];
349+
350+ fullname = find_dot_file(filename);
351+ setinputfile(fullname, 1, -1 /* verify */);
352+ commandname = fullname;
353+ cmdloop(0);
354+ popfile();
355+ return exitstatus;
356+}
357+
358+
359+int
360+exitcmd(int argc, char **argv)
361+{
362+ if (stoppedjobs())
363+ return 0;
364+ if (argc > 1)
365+ exitshell(number(argv[1]));
366+ else
367+ exitshell_savedstatus();
368+}
+39,
-0
1@@ -0,0 +1,39 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+extern int rootpid; /* pid of main shell */
37+extern int rootshell; /* true if we aren't a child of the main shell */
38+extern struct jmploc main_handler; /* top level exception handler */
39+
40+void readcmdfile(const char *, int);
+345,
-0
1@@ -0,0 +1,345 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#include <sys/param.h>
37+#include "shell.h"
38+#include "output.h"
39+#include "memalloc.h"
40+#include "error.h"
41+#include "mystring.h"
42+#include "expand.h"
43+#include <stdlib.h>
44+#include <unistd.h>
45+
46+static void
47+badalloc(const char *message)
48+{
49+ write(2, message, strlen(message));
50+ abort();
51+}
52+
53+/*
54+ * Like malloc, but returns an error when out of space.
55+ */
56+
57+pointer
58+ckmalloc(size_t nbytes)
59+{
60+ pointer p;
61+
62+ if (!is_int_on())
63+ badalloc("Unsafe ckmalloc() call\n");
64+ p = malloc(nbytes);
65+ if (p == NULL)
66+ error("Out of space");
67+ return p;
68+}
69+
70+
71+/*
72+ * Same for realloc.
73+ */
74+
75+pointer
76+ckrealloc(pointer p, int nbytes)
77+{
78+ if (!is_int_on())
79+ badalloc("Unsafe ckrealloc() call\n");
80+ p = realloc(p, nbytes);
81+ if (p == NULL)
82+ error("Out of space");
83+ return p;
84+}
85+
86+void
87+ckfree(pointer p)
88+{
89+ if (!is_int_on())
90+ badalloc("Unsafe ckfree() call\n");
91+ free(p);
92+}
93+
94+
95+/*
96+ * Make a copy of a string in safe storage.
97+ */
98+
99+char *
100+savestr(const char *s)
101+{
102+ char *p;
103+ size_t len;
104+
105+ len = strlen(s);
106+ p = ckmalloc(len + 1);
107+ memcpy(p, s, len + 1);
108+ return p;
109+}
110+
111+
112+/*
113+ * Parse trees for commands are allocated in lifo order, so we use a stack
114+ * to make this more efficient, and also to avoid all sorts of exception
115+ * handling code to handle interrupts in the middle of a parse.
116+ *
117+ * The size 496 was chosen because with 16-byte alignment the total size
118+ * for the allocated block is 512.
119+ */
120+
121+#define MINSIZE 496 /* minimum size of a block. */
122+
123+
124+struct stack_block {
125+ struct stack_block *prev;
126+ /* Data follows */
127+};
128+#define SPACE(sp) ((char*)(sp) + ALIGN(sizeof(struct stack_block)))
129+
130+static struct stack_block *stackp;
131+char *stacknxt;
132+int stacknleft;
133+char *sstrend;
134+
135+
136+static void
137+stnewblock(int nbytes)
138+{
139+ struct stack_block *sp;
140+ int allocsize;
141+
142+ if (nbytes < MINSIZE)
143+ nbytes = MINSIZE;
144+
145+ allocsize = ALIGN(sizeof(struct stack_block)) + ALIGN(nbytes);
146+
147+ INTOFF;
148+ sp = ckmalloc(allocsize);
149+ sp->prev = stackp;
150+ stacknxt = SPACE(sp);
151+ stacknleft = allocsize - (stacknxt - (char*)sp);
152+ sstrend = stacknxt + stacknleft;
153+ stackp = sp;
154+ INTON;
155+}
156+
157+
158+pointer
159+stalloc(int nbytes)
160+{
161+ char *p;
162+
163+ nbytes = ALIGN(nbytes);
164+ if (nbytes > stacknleft)
165+ stnewblock(nbytes);
166+ p = stacknxt;
167+ stacknxt += nbytes;
168+ stacknleft -= nbytes;
169+ return p;
170+}
171+
172+
173+void
174+stunalloc(pointer p)
175+{
176+ if (p == NULL) { /*DEBUG */
177+ write(STDERR_FILENO, "stunalloc\n", 10);
178+ abort();
179+ }
180+ stacknleft += stacknxt - (char *)p;
181+ stacknxt = p;
182+}
183+
184+
185+char *
186+stsavestr(const char *s)
187+{
188+ char *p;
189+ size_t len;
190+
191+ len = strlen(s);
192+ p = stalloc(len + 1);
193+ memcpy(p, s, len + 1);
194+ return p;
195+}
196+
197+
198+void
199+setstackmark(struct stackmark *mark)
200+{
201+ mark->stackp = stackp;
202+ mark->stacknxt = stacknxt;
203+ mark->stacknleft = stacknleft;
204+ /* Ensure this block stays in place. */
205+ if (stackp != NULL && stacknxt == SPACE(stackp))
206+ stalloc(1);
207+}
208+
209+
210+void
211+popstackmark(struct stackmark *mark)
212+{
213+ struct stack_block *sp;
214+
215+ INTOFF;
216+ while (stackp != mark->stackp) {
217+ sp = stackp;
218+ stackp = sp->prev;
219+ ckfree(sp);
220+ }
221+ stacknxt = mark->stacknxt;
222+ stacknleft = mark->stacknleft;
223+ if (stacknleft != 0)
224+ sstrend = stacknxt + stacknleft;
225+ else
226+ sstrend = stacknxt;
227+ INTON;
228+}
229+
230+
231+/*
232+ * When the parser reads in a string, it wants to stick the string on the
233+ * stack and only adjust the stack pointer when it knows how big the
234+ * string is. Stackblock (defined in stack.h) returns a pointer to a block
235+ * of space on top of the stack and stackblocklen returns the length of
236+ * this block. Growstackblock will grow this space by at least one byte,
237+ * possibly moving it (like realloc). Grabstackblock actually allocates the
238+ * part of the block that has been used.
239+ */
240+
241+static void
242+growstackblock(int min)
243+{
244+ char *p;
245+ int newlen;
246+ char *oldspace;
247+ int oldlen;
248+ struct stack_block *sp;
249+ struct stack_block *oldstackp;
250+
251+ if (min < stacknleft)
252+ min = stacknleft;
253+ if ((unsigned int)min >=
254+ INT_MAX / 2 - ALIGN(sizeof(struct stack_block)))
255+ error("Out of space");
256+ min += stacknleft;
257+ min += ALIGN(sizeof(struct stack_block));
258+ newlen = 512;
259+ while (newlen < min)
260+ newlen <<= 1;
261+ oldspace = stacknxt;
262+ oldlen = stacknleft;
263+
264+ if (stackp != NULL && stacknxt == SPACE(stackp)) {
265+ INTOFF;
266+ oldstackp = stackp;
267+ stackp = oldstackp->prev;
268+ sp = ckrealloc((pointer)oldstackp, newlen);
269+ sp->prev = stackp;
270+ stackp = sp;
271+ stacknxt = SPACE(sp);
272+ stacknleft = newlen - (stacknxt - (char*)sp);
273+ sstrend = stacknxt + stacknleft;
274+ INTON;
275+ } else {
276+ newlen -= ALIGN(sizeof(struct stack_block));
277+ p = stalloc(newlen);
278+ if (oldlen != 0)
279+ memcpy(p, oldspace, oldlen);
280+ stunalloc(p);
281+ }
282+}
283+
284+
285+/*
286+ * The following routines are somewhat easier to use than the above.
287+ * The user declares a variable of type STACKSTR, which may be declared
288+ * to be a register. The macro STARTSTACKSTR initializes things. Then
289+ * the user uses the macro STPUTC to add characters to the string. In
290+ * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
291+ * grown as necessary. When the user is done, she can just leave the
292+ * string there and refer to it using stackblock(). Or she can allocate
293+ * the space for it using grabstackstr(). If it is necessary to allow
294+ * someone else to use the stack temporarily and then continue to grow
295+ * the string, the user should use grabstack to allocate the space, and
296+ * then call ungrabstr(p) to return to the previous mode of operation.
297+ *
298+ * USTPUTC is like STPUTC except that it doesn't check for overflow.
299+ * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
300+ * is space for at least one character.
301+ */
302+
303+static char *
304+growstrstackblock(int n, int min)
305+{
306+ growstackblock(min);
307+ return stackblock() + n;
308+}
309+
310+char *
311+growstackstr(void)
312+{
313+ int len;
314+
315+ len = stackblocksize();
316+ return (growstrstackblock(len, 0));
317+}
318+
319+
320+/*
321+ * Called from CHECKSTRSPACE.
322+ */
323+
324+char *
325+makestrspace(int min, char *p)
326+{
327+ int len;
328+
329+ len = p - stackblock();
330+ return (growstrstackblock(len, min));
331+}
332+
333+
334+char *
335+stputbin(const char *data, size_t len, char *p)
336+{
337+ CHECKSTRSPACE(len, p);
338+ memcpy(p, data, len);
339+ return (p + len);
340+}
341+
342+char *
343+stputs(const char *data, char *p)
344+{
345+ return (stputbin(data, strlen(data), p));
346+}
+85,
-0
1@@ -0,0 +1,85 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#include <string.h>
37+
38+struct stackmark {
39+ struct stack_block *stackp;
40+ char *stacknxt;
41+ int stacknleft;
42+};
43+
44+
45+extern char *stacknxt;
46+extern int stacknleft;
47+extern char *sstrend;
48+
49+pointer ckmalloc(size_t);
50+pointer ckrealloc(pointer, int);
51+void ckfree(pointer);
52+char *savestr(const char *);
53+pointer stalloc(int);
54+void stunalloc(pointer);
55+char *stsavestr(const char *);
56+void setstackmark(struct stackmark *);
57+void popstackmark(struct stackmark *);
58+char *growstackstr(void);
59+char *makestrspace(int, char *);
60+char *stputbin(const char *data, size_t len, char *p);
61+char *stputs(const char *data, char *p);
62+
63+
64+
65+#define stackblock() stacknxt
66+#define stackblocksize() stacknleft
67+#define grabstackblock(n) stalloc(n)
68+#define STARTSTACKSTR(p) p = stackblock()
69+#define STPUTC(c, p) do { if (p == sstrend) p = growstackstr(); *p++ = (c); } while(0)
70+#define CHECKSTRSPACE(n, p) { if ((size_t)(sstrend - p) < n) p = makestrspace(n, p); }
71+#define USTPUTC(c, p) (*p++ = (c))
72+/*
73+ * STACKSTRNUL's use is where we want to be able to turn a stack
74+ * (non-sentinel, character counting string) into a C string,
75+ * and later pretend the NUL is not there.
76+ * Note: Because of STACKSTRNUL's semantics, STACKSTRNUL cannot be used
77+ * on a stack that will grabstackstr()ed.
78+ */
79+#define STACKSTRNUL(p) (p == sstrend ? (p = growstackstr(), *p = '\0') : (*p = '\0'))
80+#define STUNPUTC(p) (--p)
81+#define STTOPC(p) p[-1]
82+#define STADJUST(amount, p) (p += (amount))
83+#define grabstackstr(p) stalloc((char *)p - stackblock())
84+#define ungrabstackstr(s, p) stunalloc((s))
85+#define STPUTBIN(s, len, p) p = stputbin((s), (len), p)
86+#define STPUTS(s, p) p = stputs((s), p)
+642,
-0
1@@ -0,0 +1,642 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+/*
37+ * Miscellaneous builtins.
38+ */
39+
40+#include <sys/types.h>
41+#include <sys/stat.h>
42+#include <sys/time.h>
43+#include <sys/resource.h>
44+
45+#include <errno.h>
46+#include <poll.h>
47+#include <signal.h>
48+#include <stdint.h>
49+#include <stdio.h>
50+#include <stdlib.h>
51+#include <time.h>
52+#include <unistd.h>
53+
54+#include "shell.h"
55+
56+#ifndef timespeccmp
57+#define timespeccmp(tvp, uvp, cmp) \
58+ (((tvp)->tv_sec == (uvp)->tv_sec) ? \
59+ ((tvp)->tv_nsec cmp (uvp)->tv_nsec) : \
60+ ((tvp)->tv_sec cmp (uvp)->tv_sec))
61+#endif
62+#include "options.h"
63+#include "var.h"
64+#include "output.h"
65+#include "memalloc.h"
66+#include "error.h"
67+#include "mystring.h"
68+#include "syntax.h"
69+#include "trap.h"
70+
71+#undef eflag
72+
73+#define READ_BUFLEN 1024
74+struct fdctx {
75+ int fd;
76+ size_t off; /* offset in buf */
77+ size_t buflen;
78+ char *ep; /* tail pointer */
79+ char buf[READ_BUFLEN];
80+};
81+
82+static void fdctx_init(int, struct fdctx *);
83+static void fdctx_destroy(struct fdctx *);
84+static ssize_t fdgetc(struct fdctx *, char *);
85+int readcmd(int, char **);
86+int umaskcmd(int, char **);
87+int ulimitcmd(int, char **);
88+
89+extern mode_t parsemode(const char *str, mode_t mode, mode_t mask);
90+
91+static void
92+fdctx_init(int fd, struct fdctx *fdc)
93+{
94+ off_t cur;
95+
96+ /* Check if fd is seekable. */
97+ cur = lseek(fd, 0, SEEK_CUR);
98+ *fdc = (struct fdctx){
99+ .fd = fd,
100+ .buflen = (cur != -1) ? READ_BUFLEN : 1,
101+ .ep = &fdc->buf[0], /* No data */
102+ };
103+}
104+
105+static ssize_t
106+fdgetc(struct fdctx *fdc, char *c)
107+{
108+ ssize_t nread;
109+
110+ if (&fdc->buf[fdc->off] == fdc->ep) {
111+ nread = read(fdc->fd, fdc->buf, fdc->buflen);
112+ if (nread > 0) {
113+ fdc->off = 0;
114+ fdc->ep = fdc->buf + nread;
115+ } else
116+ return (nread);
117+ }
118+ *c = fdc->buf[fdc->off++];
119+
120+ return (1);
121+}
122+
123+static void
124+fdctx_destroy(struct fdctx *fdc)
125+{
126+ off_t residue;
127+
128+ if (fdc->buflen > 1) {
129+ /*
130+ * Reposition the file offset. Here is the layout of buf:
131+ *
132+ * | off
133+ * v
134+ * |*****************|-------|
135+ * buf ep buf+buflen
136+ * |<- residue ->|
137+ *
138+ * off: current character
139+ * ep: offset just after read(2)
140+ * residue: length for reposition
141+ */
142+ residue = (fdc->ep - fdc->buf) - fdc->off;
143+ if (residue > 0)
144+ (void) lseek(fdc->fd, -residue, SEEK_CUR);
145+ }
146+}
147+
148+/*
149+ * The read builtin. The -r option causes backslashes to be treated like
150+ * ordinary characters.
151+ *
152+ * Note that if IFS=' :' then read x y should work so that:
153+ * 'a b' x='a', y='b'
154+ * ' a b ' x='a', y='b'
155+ * ':b' x='', y='b'
156+ * ':' x='', y=''
157+ * '::' x='', y=''
158+ * ': :' x='', y=''
159+ * ':::' x='', y='::'
160+ * ':b c:' x='', y='b c:'
161+ */
162+
163+int
164+readcmd(int argc __unused, char **argv __unused)
165+{
166+ char **ap;
167+ int backslash;
168+ char c;
169+ int rflag;
170+ char *prompt;
171+ const char *ifs;
172+ char *p;
173+ int startword;
174+ int status;
175+ int i;
176+ int is_ifs;
177+ int saveall = 0;
178+ ptrdiff_t lastnonifs, lastnonifsws;
179+ sigset_t set, oset;
180+ intmax_t number, timeout;
181+ struct timespec tnow, tend, tresid;
182+ struct pollfd pfd;
183+ char *endptr;
184+ ssize_t nread;
185+ int sig;
186+ struct fdctx fdctx;
187+
188+ rflag = 0;
189+ prompt = NULL;
190+ timeout = -1;
191+ while ((i = nextopt("erp:t:")) != '\0') {
192+ switch(i) {
193+ case 'p':
194+ prompt = shoptarg;
195+ break;
196+ case 'e':
197+ break;
198+ case 'r':
199+ rflag = 1;
200+ break;
201+ case 't':
202+ timeout = 0;
203+ do {
204+ number = strtol(shoptarg, &endptr, 0);
205+ if (number < 0 || endptr == shoptarg)
206+ error("timeout value");
207+ switch (*endptr) {
208+ case 's':
209+ endptr++;
210+ break;
211+ case 'h':
212+ number *= 60;
213+ /* FALLTHROUGH */
214+ case 'm':
215+ number *= 60;
216+ endptr++;
217+ break;
218+ }
219+ if (*endptr != '\0' &&
220+ !(*endptr >= '0' && *endptr <= '9'))
221+ error("timeout unit");
222+ timeout += number;
223+ shoptarg = endptr;
224+ } while (*shoptarg != '\0');
225+ break;
226+ }
227+ }
228+ if (prompt && isatty(0)) {
229+ out2str(prompt);
230+ flushall();
231+ }
232+ if (*(ap = argptr) == NULL)
233+ error("arg count");
234+ if ((ifs = bltinlookup("IFS", 1)) == NULL)
235+ ifs = " \t\n";
236+
237+ if (timeout >= 0) {
238+ /*
239+ * Wait for something to become available.
240+ */
241+ pfd.fd = STDIN_FILENO;
242+ pfd.events = POLLIN;
243+ status = sig = 0;
244+ sigfillset(&set);
245+ sigprocmask(SIG_SETMASK, &set, &oset);
246+ if (pendingsig) {
247+ /* caught a signal already */
248+ status = -1;
249+ } else if (timeout == 0) {
250+ status = poll(&pfd, 1, 0);
251+ } else {
252+ clock_gettime(CLOCK_UPTIME, &tnow);
253+ tend = tnow;
254+ tend.tv_sec += timeout;
255+ do {
256+ timespecsub(&tend, &tnow, &tresid);
257+ status = ppoll(&pfd, 1, &tresid, &oset);
258+ if (status >= 0 || pendingsig != 0)
259+ break;
260+ clock_gettime(CLOCK_UPTIME, &tnow);
261+ } while (timespeccmp(&tnow, &tend, <));
262+ }
263+ sigprocmask(SIG_SETMASK, &oset, NULL);
264+ /*
265+ * If there's nothing ready, return an error.
266+ */
267+ if (status <= 0) {
268+ while (*ap != NULL)
269+ setvar(*ap++, "", 0);
270+ sig = pendingsig;
271+ return (128 + (sig != 0 ? sig : SIGALRM));
272+ }
273+ }
274+
275+ status = 0;
276+ startword = 2;
277+ backslash = 0;
278+ STARTSTACKSTR(p);
279+ lastnonifs = lastnonifsws = -1;
280+ fdctx_init(STDIN_FILENO, &fdctx);
281+ for (;;) {
282+ c = 0;
283+ nread = fdgetc(&fdctx, &c);
284+ if (nread == -1) {
285+ if (errno == EINTR) {
286+ sig = pendingsig;
287+ if (sig == 0)
288+ continue;
289+ status = 128 + sig;
290+ break;
291+ }
292+ warning("read error: %s", strerror(errno));
293+ status = 2;
294+ break;
295+ } else if (nread != 1) {
296+ status = 1;
297+ break;
298+ }
299+ if (c == '\0')
300+ continue;
301+ CHECKSTRSPACE(1, p);
302+ if (backslash) {
303+ backslash = 0;
304+ if (c != '\n') {
305+ startword = 0;
306+ lastnonifs = lastnonifsws = p - stackblock();
307+ USTPUTC(c, p);
308+ }
309+ continue;
310+ }
311+ if (!rflag && c == '\\' && !backslash) {
312+ backslash++;
313+ continue;
314+ }
315+ if (c == '\n')
316+ break;
317+ if (strchr(ifs, c))
318+ is_ifs = strchr(" \t\n", c) ? 1 : 2;
319+ else
320+ is_ifs = 0;
321+
322+ if (startword != 0) {
323+ if (is_ifs == 1) {
324+ /* Ignore leading IFS whitespace */
325+ if (saveall)
326+ USTPUTC(c, p);
327+ continue;
328+ }
329+ if (is_ifs == 2 && startword == 1) {
330+ /* Only one non-whitespace IFS per word */
331+ startword = 2;
332+ if (saveall) {
333+ lastnonifsws = p - stackblock();
334+ USTPUTC(c, p);
335+ }
336+ continue;
337+ }
338+ }
339+
340+ if (is_ifs == 0) {
341+ /* append this character to the current variable */
342+ startword = 0;
343+ if (saveall)
344+ /* Not just a spare terminator */
345+ saveall++;
346+ lastnonifs = lastnonifsws = p - stackblock();
347+ USTPUTC(c, p);
348+ continue;
349+ }
350+
351+ /* end of variable... */
352+ startword = is_ifs;
353+
354+ if (ap[1] == NULL) {
355+ /* Last variable needs all IFS chars */
356+ saveall++;
357+ if (is_ifs == 2)
358+ lastnonifsws = p - stackblock();
359+ USTPUTC(c, p);
360+ continue;
361+ }
362+
363+ STACKSTRNUL(p);
364+ setvar(*ap, stackblock(), 0);
365+ ap++;
366+ STARTSTACKSTR(p);
367+ lastnonifs = lastnonifsws = -1;
368+ }
369+ fdctx_destroy(&fdctx);
370+ STACKSTRNUL(p);
371+
372+ /*
373+ * Remove trailing IFS chars: always remove whitespace, don't remove
374+ * non-whitespace unless it was naked
375+ */
376+ if (saveall <= 1)
377+ lastnonifsws = lastnonifs;
378+ stackblock()[lastnonifsws + 1] = '\0';
379+ setvar(*ap, stackblock(), 0);
380+
381+ /* Set any remaining args to "" */
382+ while (*++ap != NULL)
383+ setvar(*ap, "", 0);
384+ return status;
385+}
386+
387+
388+
389+int
390+umaskcmd(int argc __unused, char **argv __unused)
391+{
392+ char *ap;
393+ int mask;
394+ int i;
395+ int symbolic_mode = 0;
396+
397+ while ((i = nextopt("S")) != '\0') {
398+ symbolic_mode = 1;
399+ }
400+
401+ INTOFF;
402+ mask = umask(0);
403+ umask(mask);
404+ INTON;
405+
406+ if ((ap = *argptr) == NULL) {
407+ if (symbolic_mode) {
408+ char u[4], g[4], o[4];
409+
410+ i = 0;
411+ if ((mask & S_IRUSR) == 0)
412+ u[i++] = 'r';
413+ if ((mask & S_IWUSR) == 0)
414+ u[i++] = 'w';
415+ if ((mask & S_IXUSR) == 0)
416+ u[i++] = 'x';
417+ u[i] = '\0';
418+
419+ i = 0;
420+ if ((mask & S_IRGRP) == 0)
421+ g[i++] = 'r';
422+ if ((mask & S_IWGRP) == 0)
423+ g[i++] = 'w';
424+ if ((mask & S_IXGRP) == 0)
425+ g[i++] = 'x';
426+ g[i] = '\0';
427+
428+ i = 0;
429+ if ((mask & S_IROTH) == 0)
430+ o[i++] = 'r';
431+ if ((mask & S_IWOTH) == 0)
432+ o[i++] = 'w';
433+ if ((mask & S_IXOTH) == 0)
434+ o[i++] = 'x';
435+ o[i] = '\0';
436+
437+ out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
438+ } else {
439+ out1fmt("%.4o\n", mask);
440+ }
441+ } else {
442+ if (is_digit(*ap)) {
443+ mask = 0;
444+ do {
445+ if (*ap >= '8' || *ap < '0')
446+ error("Illegal number: %s", *argptr);
447+ mask = (mask << 3) + (*ap - '0');
448+ } while (*++ap != '\0');
449+ umask(mask);
450+ } else {
451+ mode_t newmask;
452+ INTOFF;
453+ newmask = parsemode(ap, ~mask & 0777, mask);
454+ umask(~newmask & 0777);
455+ INTON;
456+ }
457+ }
458+ return 0;
459+}
460+
461+/*
462+ * ulimit builtin
463+ *
464+ * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
465+ * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
466+ * ash by J.T. Conklin.
467+ *
468+ * Public domain.
469+ */
470+
471+struct limits {
472+ const char *name;
473+ const char *units;
474+ int cmd;
475+ short factor; /* multiply by to get rlim_{cur,max} values */
476+ char option;
477+};
478+
479+static const struct limits limits[] = {
480+#ifdef RLIMIT_CPU
481+ { "cpu time", "seconds", RLIMIT_CPU, 1, 't' },
482+#endif
483+#ifdef RLIMIT_FSIZE
484+ { "file size", "512-blocks", RLIMIT_FSIZE, 512, 'f' },
485+#endif
486+#ifdef RLIMIT_DATA
487+ { "data seg size", "kbytes", RLIMIT_DATA, 1024, 'd' },
488+#endif
489+#ifdef RLIMIT_STACK
490+ { "stack size", "kbytes", RLIMIT_STACK, 1024, 's' },
491+#endif
492+#ifdef RLIMIT_CORE
493+ { "core file size", "512-blocks", RLIMIT_CORE, 512, 'c' },
494+#endif
495+#ifdef RLIMIT_RSS
496+ { "max memory size", "kbytes", RLIMIT_RSS, 1024, 'm' },
497+#endif
498+#ifdef RLIMIT_MEMLOCK
499+ { "locked memory", "kbytes", RLIMIT_MEMLOCK, 1024, 'l' },
500+#endif
501+#ifdef RLIMIT_NPROC
502+ { "max user processes", (char *)0, RLIMIT_NPROC, 1, 'u' },
503+#endif
504+#ifdef RLIMIT_NOFILE
505+ { "open files", (char *)0, RLIMIT_NOFILE, 1, 'n' },
506+#endif
507+#ifdef RLIMIT_VMEM
508+ { "virtual mem size", "kbytes", RLIMIT_VMEM, 1024, 'v' },
509+#endif
510+#ifdef RLIMIT_SWAP
511+ { "swap limit", "kbytes", RLIMIT_SWAP, 1024, 'w' },
512+#endif
513+#ifdef RLIMIT_SBSIZE
514+ { "socket buffer size", "bytes", RLIMIT_SBSIZE, 1, 'b' },
515+#endif
516+#ifdef RLIMIT_NPTS
517+ { "pseudo-terminals", (char *)0, RLIMIT_NPTS, 1, 'p' },
518+#endif
519+#ifdef RLIMIT_KQUEUES
520+ { "kqueues", (char *)0, RLIMIT_KQUEUES, 1, 'k' },
521+#endif
522+#ifdef RLIMIT_UMTXP
523+ { "umtx shared locks", (char *)0, RLIMIT_UMTXP, 1, 'o' },
524+#endif
525+#ifdef RLIMIT_PIPEBUF
526+ { "pipebuf", "kbytes", RLIMIT_PIPEBUF, 1024, 'y' },
527+#endif
528+#ifdef RLIMIT_VMM
529+ { "virtual machines", (char *)0, RLIMIT_VMM, 1, 'V' },
530+#endif
531+ { (char *) 0, (char *)0, 0, 0, '\0' }
532+};
533+
534+enum limithow { SOFT = 0x1, HARD = 0x2 };
535+
536+static void
537+printlimit(enum limithow how, const struct rlimit *limit,
538+ const struct limits *l)
539+{
540+ rlim_t val = 0;
541+
542+ if (how & SOFT)
543+ val = limit->rlim_cur;
544+ else if (how & HARD)
545+ val = limit->rlim_max;
546+ if (val == RLIM_INFINITY)
547+ out1str("unlimited\n");
548+ else
549+ {
550+ val /= l->factor;
551+ out1fmt("%jd\n", (intmax_t)val);
552+ }
553+}
554+
555+int
556+ulimitcmd(int argc __unused, char **argv __unused)
557+{
558+ rlim_t val = 0;
559+ enum limithow how = SOFT | HARD;
560+ const struct limits *l;
561+ int set, all = 0;
562+ int optc, what;
563+ struct rlimit limit;
564+
565+ what = 'f';
566+ while ((optc = nextopt("abcdfHklmnopSstuVvwy")) != '\0')
567+ switch (optc) {
568+ case 'H':
569+ how = HARD;
570+ break;
571+ case 'S':
572+ how = SOFT;
573+ break;
574+ case 'a':
575+ all = 1;
576+ break;
577+ default:
578+ what = optc;
579+ }
580+
581+ for (l = limits; l->name && l->option != what; l++)
582+ ;
583+ if (!l->name)
584+ error("internal error (%c)", what);
585+
586+ set = *argptr ? 1 : 0;
587+ if (set) {
588+ char *p = *argptr;
589+
590+ if (all || argptr[1])
591+ error("too many arguments");
592+ if (strcmp(p, "unlimited") == 0)
593+ val = RLIM_INFINITY;
594+ else {
595+ char *end;
596+ uintmax_t uval;
597+
598+ if (*p < '0' || *p > '9')
599+ error("bad number");
600+ errno = 0;
601+ uval = strtoumax(p, &end, 10);
602+ if (errno != 0 || *end != '\0')
603+ error("bad number");
604+ if (uval > UINTMAX_MAX / l->factor)
605+ error("bad number");
606+ uval *= l->factor;
607+ val = (rlim_t)uval;
608+ if ((intmax_t)val < 0 || (uintmax_t)val != uval ||
609+ val == RLIM_INFINITY)
610+ error("bad number");
611+ }
612+ }
613+ if (all) {
614+ for (l = limits; l->name; l++) {
615+ char optbuf[40];
616+ if (getrlimit(l->cmd, &limit) < 0)
617+ error("can't get limit: %s", strerror(errno));
618+
619+ if (l->units)
620+ snprintf(optbuf, sizeof(optbuf),
621+ "(%s, -%c) ", l->units, l->option);
622+ else
623+ snprintf(optbuf, sizeof(optbuf),
624+ "(-%c) ", l->option);
625+ out1fmt("%-18s %18s ", l->name, optbuf);
626+ printlimit(how, &limit, l);
627+ }
628+ return 0;
629+ }
630+
631+ if (getrlimit(l->cmd, &limit) < 0)
632+ error("can't get limit: %s", strerror(errno));
633+ if (set) {
634+ if (how & SOFT)
635+ limit.rlim_cur = val;
636+ if (how & HARD)
637+ limit.rlim_max = val;
638+ if (setrlimit(l->cmd, &limit) < 0)
639+ error("bad limit: %s", strerror(errno));
640+ } else
641+ printlimit(how, &limit, l);
642+ return 0;
643+}
+156,
-0
1@@ -0,0 +1,156 @@
2+#!/bin/sh -
3+
4+#-
5+# Copyright (c) 1991, 1993
6+# The Regents of the University of California. All rights reserved.
7+#
8+# This code is derived from software contributed to Berkeley by
9+# Kenneth Almquist.
10+#
11+# Redistribution and use in source and binary forms, with or without
12+# modification, are permitted provided that the following conditions
13+# are met:
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.
22+#
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
33+# SUCH DAMAGE.
34+
35+temp=`mktemp -t ka.XXXXXX`
36+srcdir=$1
37+havejobs=0
38+if grep '^#define[ ]*JOBS[ ]*1' $srcdir/shell.h > /dev/null
39+then havejobs=1
40+fi
41+
42+feature_histedit=0
43+feature_local=1
44+feature_let=1
45+feature_ulimit=1
46+feature_setvar=1
47+feature_wordexp=1
48+
49+if [ -f ../../config.mk ]; then
50+ feature_histedit=$(grep '^[[:space:]]*FEATURE_SH_HISTEDIT[[:space:]]*=' ../../config.mk | cut -d= -f2 | tr -d '[:space:]')
51+ feature_local=$(grep '^[[:space:]]*FEATURE_SH_LOCAL[[:space:]]*=' ../../config.mk | cut -d= -f2 | tr -d '[:space:]')
52+ feature_let=$(grep '^[[:space:]]*FEATURE_SH_LET[[:space:]]*=' ../../config.mk | cut -d= -f2 | tr -d '[:space:]')
53+ feature_ulimit=$(grep '^[[:space:]]*FEATURE_SH_ULIMIT[[:space:]]*=' ../../config.mk | cut -d= -f2 | tr -d '[:space:]')
54+ feature_setvar=$(grep '^[[:space:]]*FEATURE_SH_SETVAR[[:space:]]*=' ../../config.mk | cut -d= -f2 | tr -d '[:space:]')
55+ feature_wordexp=$(grep '^[[:space:]]*FEATURE_SH_WORDEXP[[:space:]]*=' ../../config.mk | cut -d= -f2 | tr -d '[:space:]')
56+fi
57+
58+exec > builtins.c
59+cat <<\!
60+/*
61+ * This file was generated by the mkbuiltins program.
62+ */
63+
64+#include <stdlib.h>
65+#include "shell.h"
66+#include "builtins.h"
67+
68+!
69+awk '/^[^#]/ {
70+ if ($0 ~ /#FEATURE_SH_HISTEDIT/ && '$feature_histedit' != 1) next;
71+ if ($0 ~ /#FEATURE_SH_LOCAL/ && '$feature_local' != 1) next;
72+ if ($0 ~ /#FEATURE_SH_LET/ && '$feature_let' != 1) next;
73+ if ($0 ~ /#FEATURE_SH_ULIMIT/ && '$feature_ulimit' != 1) next;
74+ if ($0 ~ /#FEATURE_SH_SETVAR/ && '$feature_setvar' != 1) next;
75+ if ($0 ~ /#FEATURE_SH_WORDEXP/ && '$feature_wordexp' != 1) next;
76+
77+ if ('$havejobs' || $2 != "-j") {
78+ sub(/#[A-Z0-9_]*/, "");
79+ print $0
80+ }
81+}' $srcdir/builtins.def | sed 's/-j//' > $temp
82+echo 'int (*const builtinfunc[])(int, char **) = {'
83+awk '/^[^#]/ { printf "\t%s,\n", $1}' $temp
84+echo '};
85+
86+const unsigned char builtincmd[] = {'
87+awk '{ for (i = 2 ; i <= NF ; i++) {
88+ if ($i == "-s") {
89+ spc = 1;
90+ } else if ($i == "-n") {
91+ # Handled later for builtins.h
92+ continue
93+ } else {
94+ printf "\t\"\\%03o\\%03o%s\"\n", length($i), (spc ? 128 : 0) + NR-1, $i
95+ spc = 0;
96+ }
97+ }}' $temp
98+echo '};'
99+
100+exec > builtins.h
101+cat <<\!
102+/*
103+ * This file was generated by the mkbuiltins program.
104+ */
105+
106+!
107+tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ < $temp |
108+ awk '{ printf "#define %s %d\n", $1, NR-1}'
109+echo '
110+#define BUILTIN_SPECIAL 0x80
111+
112+extern int (*const builtinfunc[])(int, char **);
113+extern const unsigned char builtincmd[];
114+'
115+awk '{ printf "int %s(int, char **);\n", $1}' $temp
116+
117+# Build safe_builtin_always()
118+cat <<EOF
119+
120+static inline int
121+safe_builtin_always(int idx)
122+{
123+EOF
124+awk '
125+BEGIN { printed = 0 }
126+{
127+ for (i = 2 ; i <= NF ; i++) {
128+ if ($i == "-s") {
129+ continue
130+ } else if ($i == "-n") {
131+ nofork = 1;
132+ } else {
133+ if (nofork == 0) {
134+ continue
135+ }
136+ if (printed == 1) {
137+ printf " || \n\t "
138+ } else {
139+ printf "\tif ("
140+ }
141+ printf "idx == " toupper($1)
142+ printed = 1
143+ nofork = 0;
144+ # Only need to check each once
145+ break
146+ }
147+ }
148+}' $temp
149+
150+cat << EOF
151+)
152+ return (1);
153+ return(0);
154+}
155+EOF
156+
157+rm -f $temp
+454,
-0
1@@ -0,0 +1,454 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+/*
37+ * This program reads the nodetypes file and nodes.c.pat file. It generates
38+ * the files nodes.h and nodes.c.
39+ */
40+
41+#include <stdio.h>
42+#include <stdlib.h>
43+#include <string.h>
44+#include <errno.h>
45+#include <stdarg.h>
46+
47+#ifndef __printf0like
48+#define __printf0like(n, m) __attribute__((__format__(__printf__, n, m)))
49+#endif
50+#ifndef __dead2
51+#define __dead2 __attribute__((__noreturn__))
52+#endif
53+
54+#define MAXTYPES 50 /* max number of node types */
55+#define MAXFIELDS 20 /* max fields in a structure */
56+#define BUFLEN 100 /* size of character buffers */
57+
58+/* field types */
59+#define T_NODE 1 /* union node *field */
60+#define T_NODELIST 2 /* struct nodelist *field */
61+#define T_STRING 3
62+#define T_INT 4 /* int field */
63+#define T_OTHER 5 /* other */
64+#define T_TEMP 6 /* don't copy this field */
65+
66+
67+struct field { /* a structure field */
68+ char *name; /* name of field */
69+ int type; /* type of field */
70+ char *decl; /* declaration of field */
71+};
72+
73+
74+struct str { /* struct representing a node structure */
75+ char *tag; /* structure tag */
76+ int nfields; /* number of fields in the structure */
77+ struct field field[MAXFIELDS]; /* the fields of the structure */
78+ int done; /* set if fully parsed */
79+};
80+
81+
82+static int ntypes; /* number of node types */
83+static char *nodename[MAXTYPES]; /* names of the nodes */
84+static struct str *nodestr[MAXTYPES]; /* type of structure used by the node */
85+static int nstr; /* number of structures */
86+static struct str str[MAXTYPES]; /* the structures */
87+static struct str *curstr; /* current structure */
88+static char line[1024];
89+static int linno;
90+static char *linep;
91+
92+static void parsenode(void);
93+static void parsefield(void);
94+static void output(char *);
95+static void outsizes(FILE *);
96+static void outfunc(FILE *, int);
97+static void indent(int, FILE *);
98+static int nextfield(char *);
99+static void skipbl(void);
100+static int readline(FILE *);
101+static void error(const char *, ...) __printf0like(1, 2) __dead2;
102+static char *savestr(const char *);
103+
104+
105+int
106+main(int argc, char *argv[])
107+{
108+ FILE *infp;
109+
110+ if (argc != 3)
111+ error("usage: mknodes file");
112+ if ((infp = fopen(argv[1], "r")) == NULL)
113+ error("Can't open %s: %s", argv[1], strerror(errno));
114+ while (readline(infp)) {
115+ if (line[0] == ' ' || line[0] == '\t')
116+ parsefield();
117+ else if (line[0] != '\0')
118+ parsenode();
119+ }
120+ fclose(infp);
121+ output(argv[2]);
122+ exit(0);
123+}
124+
125+
126+
127+static void
128+parsenode(void)
129+{
130+ char name[BUFLEN];
131+ char tag[BUFLEN];
132+ struct str *sp;
133+
134+ if (curstr && curstr->nfields > 0)
135+ curstr->done = 1;
136+ nextfield(name);
137+ if (! nextfield(tag))
138+ error("Tag expected");
139+ if (*linep != '\0')
140+ error("Garbage at end of line");
141+ nodename[ntypes] = savestr(name);
142+ for (sp = str ; sp < str + nstr ; sp++) {
143+ if (strcmp(sp->tag, tag) == 0)
144+ break;
145+ }
146+ if (sp >= str + nstr) {
147+ sp->tag = savestr(tag);
148+ sp->nfields = 0;
149+ curstr = sp;
150+ nstr++;
151+ }
152+ nodestr[ntypes] = sp;
153+ ntypes++;
154+}
155+
156+
157+static void
158+parsefield(void)
159+{
160+ char name[BUFLEN];
161+ char type[BUFLEN];
162+ char decl[2 * BUFLEN];
163+ struct field *fp;
164+
165+ if (curstr == NULL || curstr->done)
166+ error("No current structure to add field to");
167+ if (! nextfield(name))
168+ error("No field name");
169+ if (! nextfield(type))
170+ error("No field type");
171+ fp = &curstr->field[curstr->nfields];
172+ fp->name = savestr(name);
173+ if (strcmp(type, "nodeptr") == 0) {
174+ fp->type = T_NODE;
175+ sprintf(decl, "union node *%s", name);
176+ } else if (strcmp(type, "nodelist") == 0) {
177+ fp->type = T_NODELIST;
178+ sprintf(decl, "struct nodelist *%s", name);
179+ } else if (strcmp(type, "string") == 0) {
180+ fp->type = T_STRING;
181+ sprintf(decl, "char *%s", name);
182+ } else if (strcmp(type, "int") == 0) {
183+ fp->type = T_INT;
184+ sprintf(decl, "int %s", name);
185+ } else if (strcmp(type, "other") == 0) {
186+ fp->type = T_OTHER;
187+ } else if (strcmp(type, "temp") == 0) {
188+ fp->type = T_TEMP;
189+ } else {
190+ error("Unknown type %s", type);
191+ }
192+ if (fp->type == T_OTHER || fp->type == T_TEMP) {
193+ skipbl();
194+ fp->decl = savestr(linep);
195+ } else {
196+ if (*linep)
197+ error("Garbage at end of line");
198+ fp->decl = savestr(decl);
199+ }
200+ curstr->nfields++;
201+}
202+
203+
204+static const char writer[] = "\
205+/*\n\
206+ * This file was generated by the mknodes program.\n\
207+ */\n\
208+\n";
209+
210+static void
211+output(char *file)
212+{
213+ FILE *hfile;
214+ FILE *cfile;
215+ FILE *patfile;
216+ int i;
217+ struct str *sp;
218+ struct field *fp;
219+ char *p;
220+
221+ if ((patfile = fopen(file, "r")) == NULL)
222+ error("Can't open %s: %s", file, strerror(errno));
223+ if ((hfile = fopen("nodes.h", "w")) == NULL)
224+ error("Can't create nodes.h: %s", strerror(errno));
225+ if ((cfile = fopen("nodes.c", "w")) == NULL)
226+ error("Can't create nodes.c");
227+ fputs(writer, hfile);
228+ for (i = 0 ; i < ntypes ; i++)
229+ fprintf(hfile, "#define %s %d\n", nodename[i], i);
230+ fputs("\n\n\n", hfile);
231+ for (sp = str ; sp < &str[nstr] ; sp++) {
232+ fprintf(hfile, "struct %s {\n", sp->tag);
233+ for (i = sp->nfields, fp = sp->field ; --i >= 0 ; fp++) {
234+ fprintf(hfile, " %s;\n", fp->decl);
235+ }
236+ fputs("};\n\n\n", hfile);
237+ }
238+ fputs("union node {\n", hfile);
239+ fprintf(hfile, " int type;\n");
240+ for (sp = str ; sp < &str[nstr] ; sp++) {
241+ fprintf(hfile, " struct %s %s;\n", sp->tag, sp->tag);
242+ }
243+ fputs("};\n\n\n", hfile);
244+ fputs("struct nodelist {\n", hfile);
245+ fputs("\tstruct nodelist *next;\n", hfile);
246+ fputs("\tunion node *n;\n", hfile);
247+ fputs("};\n\n\n", hfile);
248+ fputs("struct funcdef;\n", hfile);
249+ fputs("struct funcdef *copyfunc(union node *);\n", hfile);
250+ fputs("union node *getfuncnode(struct funcdef *);\n", hfile);
251+ fputs("void reffunc(struct funcdef *);\n", hfile);
252+ fputs("void unreffunc(struct funcdef *);\n", hfile);
253+ if (ferror(hfile))
254+ error("Can't write to nodes.h");
255+ if (fclose(hfile))
256+ error("Can't close nodes.h");
257+
258+ fputs(writer, cfile);
259+ while (fgets(line, sizeof line, patfile) != NULL) {
260+ for (p = line ; *p == ' ' || *p == '\t' ; p++);
261+ if (strcmp(p, "%SIZES\n") == 0)
262+ outsizes(cfile);
263+ else if (strcmp(p, "%CALCSIZE\n") == 0)
264+ outfunc(cfile, 1);
265+ else if (strcmp(p, "%COPY\n") == 0)
266+ outfunc(cfile, 0);
267+ else
268+ fputs(line, cfile);
269+ }
270+ fclose(patfile);
271+ if (ferror(cfile))
272+ error("Can't write to nodes.c");
273+ if (fclose(cfile))
274+ error("Can't close nodes.c");
275+}
276+
277+
278+
279+static void
280+outsizes(FILE *cfile)
281+{
282+ int i;
283+
284+ fprintf(cfile, "static const short nodesize[%d] = {\n", ntypes);
285+ for (i = 0 ; i < ntypes ; i++) {
286+ fprintf(cfile, " ALIGN(sizeof (struct %s)),\n", nodestr[i]->tag);
287+ }
288+ fprintf(cfile, "};\n");
289+}
290+
291+
292+static void
293+outfunc(FILE *cfile, int calcsize)
294+{
295+ struct str *sp;
296+ struct field *fp;
297+ int i;
298+
299+ fputs(" if (n == NULL)\n", cfile);
300+ if (calcsize)
301+ fputs(" return;\n", cfile);
302+ else
303+ fputs(" return NULL;\n", cfile);
304+ if (calcsize)
305+ fputs(" result->blocksize += nodesize[n->type];\n", cfile);
306+ else {
307+ fputs(" new = state->block;\n", cfile);
308+ fputs(" state->block = (char *)state->block + nodesize[n->type];\n", cfile);
309+ }
310+ fputs(" switch (n->type) {\n", cfile);
311+ for (sp = str ; sp < &str[nstr] ; sp++) {
312+ for (i = 0 ; i < ntypes ; i++) {
313+ if (nodestr[i] == sp)
314+ fprintf(cfile, " case %s:\n", nodename[i]);
315+ }
316+ for (i = sp->nfields ; --i >= 1 ; ) {
317+ fp = &sp->field[i];
318+ switch (fp->type) {
319+ case T_NODE:
320+ if (calcsize) {
321+ indent(12, cfile);
322+ fprintf(cfile, "calcsize(n->%s.%s, result);\n",
323+ sp->tag, fp->name);
324+ } else {
325+ indent(12, cfile);
326+ fprintf(cfile, "new->%s.%s = copynode(n->%s.%s, state);\n",
327+ sp->tag, fp->name, sp->tag, fp->name);
328+ }
329+ break;
330+ case T_NODELIST:
331+ if (calcsize) {
332+ indent(12, cfile);
333+ fprintf(cfile, "sizenodelist(n->%s.%s, result);\n",
334+ sp->tag, fp->name);
335+ } else {
336+ indent(12, cfile);
337+ fprintf(cfile, "new->%s.%s = copynodelist(n->%s.%s, state);\n",
338+ sp->tag, fp->name, sp->tag, fp->name);
339+ }
340+ break;
341+ case T_STRING:
342+ if (calcsize) {
343+ indent(12, cfile);
344+ fprintf(cfile, "result->stringsize += strlen(n->%s.%s) + 1;\n",
345+ sp->tag, fp->name);
346+ } else {
347+ indent(12, cfile);
348+ fprintf(cfile, "new->%s.%s = nodesavestr(n->%s.%s, state);\n",
349+ sp->tag, fp->name, sp->tag, fp->name);
350+ }
351+ break;
352+ case T_INT:
353+ case T_OTHER:
354+ if (! calcsize) {
355+ indent(12, cfile);
356+ fprintf(cfile, "new->%s.%s = n->%s.%s;\n",
357+ sp->tag, fp->name, sp->tag, fp->name);
358+ }
359+ break;
360+ }
361+ }
362+ indent(12, cfile);
363+ fputs("break;\n", cfile);
364+ }
365+ fputs(" };\n", cfile);
366+ if (! calcsize)
367+ fputs(" new->type = n->type;\n", cfile);
368+}
369+
370+
371+static void
372+indent(int amount, FILE *fp)
373+{
374+ while (amount >= 8) {
375+ putc('\t', fp);
376+ amount -= 8;
377+ }
378+ while (--amount >= 0) {
379+ putc(' ', fp);
380+ }
381+}
382+
383+
384+static int
385+nextfield(char *buf)
386+{
387+ char *p, *q;
388+
389+ p = linep;
390+ while (*p == ' ' || *p == '\t')
391+ p++;
392+ q = buf;
393+ while (*p != ' ' && *p != '\t' && *p != '\0')
394+ *q++ = *p++;
395+ *q = '\0';
396+ linep = p;
397+ return (q > buf);
398+}
399+
400+
401+static void
402+skipbl(void)
403+{
404+ while (*linep == ' ' || *linep == '\t')
405+ linep++;
406+}
407+
408+
409+static int
410+readline(FILE *infp)
411+{
412+ char *p;
413+
414+ if (fgets(line, 1024, infp) == NULL)
415+ return 0;
416+ for (p = line ; *p != '#' && *p != '\n' && *p != '\0' ; p++);
417+ while (p > line && (p[-1] == ' ' || p[-1] == '\t'))
418+ p--;
419+ *p = '\0';
420+ linep = line;
421+ linno++;
422+ if (p - line > BUFLEN)
423+ error("Line too long");
424+ return 1;
425+}
426+
427+
428+
429+static void
430+error(const char *msg, ...)
431+{
432+ va_list va;
433+ va_start(va, msg);
434+
435+ (void) fprintf(stderr, "line %d: ", linno);
436+ (void) vfprintf(stderr, msg, va);
437+ (void) fputc('\n', stderr);
438+
439+ va_end(va);
440+
441+ exit(2);
442+}
443+
444+
445+
446+static char *
447+savestr(const char *s)
448+{
449+ char *p;
450+
451+ if ((p = malloc(strlen(s) + 1)) == NULL)
452+ error("Out of space");
453+ (void) strcpy(p, s);
454+ return p;
455+}
+321,
-0
1@@ -0,0 +1,321 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+/*
37+ * This program creates syntax.h and syntax.c.
38+ */
39+
40+#include <stdio.h>
41+#include <stdlib.h>
42+#include <string.h>
43+#include "parser.h"
44+
45+#ifndef __unused
46+#define __unused __attribute__((__unused__))
47+#endif
48+
49+
50+struct synclass {
51+ const char *name;
52+ const char *comment;
53+};
54+
55+/* Syntax classes */
56+static const struct synclass synclass[] = {
57+ { "CWORD", "character is nothing special" },
58+ { "CNL", "newline character" },
59+ { "CQNL", "newline character in quotes" },
60+ { "CBACK", "a backslash character" },
61+ { "CSBACK", "a backslash character in single quotes" },
62+ { "CSQUOTE", "single quote" },
63+ { "CDQUOTE", "double quote" },
64+ { "CENDQUOTE", "a terminating quote" },
65+ { "CBQUOTE", "backwards single quote" },
66+ { "CVAR", "a dollar sign" },
67+ { "CENDVAR", "a '}' character" },
68+ { "CLP", "a left paren in arithmetic" },
69+ { "CRP", "a right paren in arithmetic" },
70+ { "CEOF", "end of file" },
71+ { "CCTL", "like CWORD, except it must be escaped" },
72+ { "CSPCL", "these terminate a word" },
73+ { "CIGN", "character should be ignored" },
74+ { NULL, NULL }
75+};
76+
77+
78+/*
79+ * Syntax classes for is_ functions. Warning: if you add new classes
80+ * you may have to change the definition of the is_in_name macro.
81+ */
82+static const struct synclass is_entry[] = {
83+ { "ISDIGIT", "a digit" },
84+ { "ISUPPER", "an upper case letter" },
85+ { "ISLOWER", "a lower case letter" },
86+ { "ISUNDER", "an underscore" },
87+ { "ISSPECL", "the name of a special parameter" },
88+ { NULL, NULL }
89+};
90+
91+static const char writer[] = "\
92+/*\n\
93+ * This file was generated by the mksyntax program.\n\
94+ */\n\
95+\n";
96+
97+
98+static FILE *cfile;
99+static FILE *hfile;
100+
101+static void add_default(void);
102+static void finish(void);
103+static void init(const char *);
104+static void add(const char *, const char *);
105+static void output_type_macros(void);
106+
107+int
108+main(int argc __unused, char **argv __unused)
109+{
110+ int i;
111+ char buf[80];
112+ int pos;
113+
114+ /* Create output files */
115+ if ((cfile = fopen("syntax.c", "w")) == NULL) {
116+ perror("syntax.c");
117+ exit(2);
118+ }
119+ if ((hfile = fopen("syntax.h", "w")) == NULL) {
120+ perror("syntax.h");
121+ exit(2);
122+ }
123+ fputs(writer, hfile);
124+ fputs(writer, cfile);
125+
126+ fputs("#include <limits.h>\n\n", hfile);
127+
128+ /* Generate the #define statements in the header file */
129+ fputs("/* Syntax classes */\n", hfile);
130+ for (i = 0 ; synclass[i].name ; i++) {
131+ sprintf(buf, "#define %s %d", synclass[i].name, i);
132+ fputs(buf, hfile);
133+ for (pos = strlen(buf) ; pos < 32 ; pos = (pos + 8) & ~07)
134+ putc('\t', hfile);
135+ fprintf(hfile, "/* %s */\n", synclass[i].comment);
136+ }
137+ putc('\n', hfile);
138+ fputs("/* Syntax classes for is_ functions */\n", hfile);
139+ for (i = 0 ; is_entry[i].name ; i++) {
140+ sprintf(buf, "#define %s %#o", is_entry[i].name, 1 << i);
141+ fputs(buf, hfile);
142+ for (pos = strlen(buf) ; pos < 32 ; pos = (pos + 8) & ~07)
143+ putc('\t', hfile);
144+ fprintf(hfile, "/* %s */\n", is_entry[i].comment);
145+ }
146+ putc('\n', hfile);
147+ fputs("#define SYNBASE (1 - CHAR_MIN)\n", hfile);
148+ fputs("#define PEOF -SYNBASE\n\n", hfile);
149+ putc('\n', hfile);
150+ fputs("#define BASESYNTAX (basesyntax + SYNBASE)\n", hfile);
151+ fputs("#define DQSYNTAX (dqsyntax + SYNBASE)\n", hfile);
152+ fputs("#define SQSYNTAX (sqsyntax + SYNBASE)\n", hfile);
153+ fputs("#define ARISYNTAX (arisyntax + SYNBASE)\n", hfile);
154+ putc('\n', hfile);
155+ output_type_macros(); /* is_digit, etc. */
156+ putc('\n', hfile);
157+
158+ /* Generate the syntax tables. */
159+ fputs("#include \"parser.h\"\n", cfile);
160+ fputs("#include \"shell.h\"\n", cfile);
161+ fputs("#include \"syntax.h\"\n\n", cfile);
162+
163+ fputs("/* syntax table used when not in quotes */\n", cfile);
164+ init("basesyntax");
165+ add_default();
166+ add("\n", "CNL");
167+ add("\\", "CBACK");
168+ add("'", "CSQUOTE");
169+ add("\"", "CDQUOTE");
170+ add("`", "CBQUOTE");
171+ add("$", "CVAR");
172+ add("}", "CENDVAR");
173+ add("<>();&| \t", "CSPCL");
174+ finish();
175+
176+ fputs("\n/* syntax table used when in double quotes */\n", cfile);
177+ init("dqsyntax");
178+ add_default();
179+ add("\n", "CQNL");
180+ add("\\", "CBACK");
181+ add("\"", "CENDQUOTE");
182+ add("`", "CBQUOTE");
183+ add("$", "CVAR");
184+ add("}", "CENDVAR");
185+ /* ':/' for tilde expansion, '-^]' for [a\-x] pattern ranges */
186+ add("!*?[]=~:/-^", "CCTL");
187+ finish();
188+
189+ fputs("\n/* syntax table used when in single quotes */\n", cfile);
190+ init("sqsyntax");
191+ add_default();
192+ add("\n", "CQNL");
193+ add("\\", "CSBACK");
194+ add("'", "CENDQUOTE");
195+ /* ':/' for tilde expansion, '-^]' for [a\-x] pattern ranges */
196+ add("!*?[]=~:/-^", "CCTL");
197+ finish();
198+
199+ fputs("\n/* syntax table used when in arithmetic */\n", cfile);
200+ init("arisyntax");
201+ add_default();
202+ add("\n", "CQNL");
203+ add("\\", "CBACK");
204+ add("`", "CBQUOTE");
205+ add("\"", "CIGN");
206+ add("$", "CVAR");
207+ add("}", "CENDVAR");
208+ add("(", "CLP");
209+ add(")", "CRP");
210+ finish();
211+
212+ fputs("\n/* character classification table */\n", cfile);
213+ init("is_type");
214+ add("0123456789", "ISDIGIT");
215+ add("abcdefghijklmnopqrstuvwxyz", "ISLOWER");
216+ add("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "ISUPPER");
217+ add("_", "ISUNDER");
218+ add("#?$!-*@", "ISSPECL");
219+ finish();
220+
221+ exit(0);
222+}
223+
224+
225+/*
226+ * Output the header and declaration of a syntax table.
227+ */
228+
229+static void
230+init(const char *name)
231+{
232+ fprintf(hfile, "extern const char %s[];\n", name);
233+ fprintf(cfile, "const char %s[SYNBASE + CHAR_MAX + 1] = {\n", name);
234+}
235+
236+
237+static void
238+add_one(const char *key, const char *type)
239+{
240+ fprintf(cfile, "\t[SYNBASE + %s] = %s,\n", key, type);
241+}
242+
243+
244+/*
245+ * Add default values to the syntax table.
246+ */
247+
248+static void
249+add_default(void)
250+{
251+ add_one("PEOF", "CEOF");
252+ add_one("CTLESC", "CCTL");
253+ add_one("CTLVAR", "CCTL");
254+ add_one("CTLENDVAR", "CCTL");
255+ add_one("CTLBACKQ", "CCTL");
256+ add_one("CTLBACKQ + CTLQUOTE", "CCTL");
257+ add_one("CTLARI", "CCTL");
258+ add_one("CTLENDARI", "CCTL");
259+ add_one("CTLQUOTEMARK", "CCTL");
260+ add_one("CTLQUOTEEND", "CCTL");
261+}
262+
263+
264+/*
265+ * Output the footer of a syntax table.
266+ */
267+
268+static void
269+finish(void)
270+{
271+ fputs("};\n", cfile);
272+}
273+
274+
275+/*
276+ * Add entries to the syntax table.
277+ */
278+
279+static void
280+add(const char *p, const char *type)
281+{
282+ for (; *p; ++p) {
283+ char c = *p;
284+ switch (c) {
285+ case '\t': c = 't'; break;
286+ case '\n': c = 'n'; break;
287+ case '\'': c = '\''; break;
288+ case '\\': c = '\\'; break;
289+
290+ default:
291+ fprintf(cfile, "\t[SYNBASE + '%c'] = %s,\n", c, type);
292+ continue;
293+ }
294+ fprintf(cfile, "\t[SYNBASE + '\\%c'] = %s,\n", c, type);
295+ }
296+}
297+
298+
299+/*
300+ * Output character classification macros (e.g. is_digit). If digits are
301+ * contiguous, we can test for them quickly.
302+ */
303+
304+static const char *macro[] = {
305+ "#define is_digit(c)\t((unsigned int)((c) - '0') <= 9)",
306+ "#define is_eof(c)\t((c) == PEOF)",
307+ "#define is_alpha(c)\t((is_type+SYNBASE)[(int)c] & (ISUPPER|ISLOWER))",
308+ "#define is_name(c)\t((is_type+SYNBASE)[(int)c] & (ISUPPER|ISLOWER|ISUNDER))",
309+ "#define is_in_name(c)\t((is_type+SYNBASE)[(int)c] & (ISUPPER|ISLOWER|ISUNDER|ISDIGIT))",
310+ "#define is_special(c)\t((is_type+SYNBASE)[(int)c] & (ISSPECL|ISDIGIT))",
311+ "#define digit_val(c)\t((c) - '0')",
312+ NULL
313+};
314+
315+static void
316+output_type_macros(void)
317+{
318+ const char **pp;
319+
320+ for (pp = macro ; *pp ; pp++)
321+ fprintf(hfile, "%s\n", *pp);
322+}
+90,
-0
1@@ -0,0 +1,90 @@
2+#!/bin/sh -
3+
4+#-
5+# Copyright (c) 1991, 1993
6+# The Regents of the University of California. All rights reserved.
7+#
8+# This code is derived from software contributed to Berkeley by
9+# Kenneth Almquist.
10+#
11+# Redistribution and use in source and binary forms, with or without
12+# modification, are permitted provided that the following conditions
13+# are met:
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.
22+#
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
33+# SUCH DAMAGE.
34+
35+# The following is a list of tokens. The second column is nonzero if the
36+# token marks the end of a list. The third column is the name to print in
37+# error messages.
38+
39+temp=`mktemp -t ka.XXXXXX`
40+cat > $temp <<\!
41+TEOF 1 end of file
42+TNL 0 newline
43+TSEMI 0 ";"
44+TBACKGND 0 "&"
45+TAND 0 "&&"
46+TOR 0 "||"
47+TPIPE 0 "|"
48+TLP 0 "("
49+TRP 1 ")"
50+TENDCASE 1 ";;"
51+TFALLTHRU 1 ";&"
52+TREDIR 0 redirection
53+TWORD 0 word
54+TIF 0 "if"
55+TTHEN 1 "then"
56+TELSE 1 "else"
57+TELIF 1 "elif"
58+TFI 1 "fi"
59+TWHILE 0 "while"
60+TUNTIL 0 "until"
61+TFOR 0 "for"
62+TDO 1 "do"
63+TDONE 1 "done"
64+TBEGIN 0 "{"
65+TEND 1 "}"
66+TCASE 0 "case"
67+TESAC 1 "esac"
68+TNOT 0 "!"
69+!
70+nl=`wc -l $temp`
71+exec > token.h
72+awk '{print "#define " $1 " " NR-1}' $temp
73+echo '
74+/* Array indicating which tokens mark the end of a list */
75+static const char tokendlist[] = {'
76+awk '{print "\t" $2 ","}' $temp
77+echo '};
78+
79+static const char *const tokname[] = {'
80+sed -e 's/"/\\"/g' \
81+ -e 's/[^ ]*[ ][ ]*[^ ]*[ ][ ]*\(.*\)/ "\1",/' \
82+ $temp
83+echo '};
84+'
85+sed 's/"//g' $temp | awk '
86+/TIF/{print "#define KWDOFFSET " NR-1; print ""; print "const char *const parsekwd[] = {"}
87+/TIF/,/neverfound/{print " \"" $3 "\","}'
88+echo ' 0
89+};'
90+
91+rm $temp
+37,
-0
1@@ -0,0 +1,37 @@
2+#ifndef MYHISTEDIT_H_
3+#define MYHISTEDIT_H_
4+
5+#if FEATURE_SH_HISTEDIT
6+
7+#include <histedit.h>
8+#include <filecomplete.h>
9+
10+extern History *hist;
11+extern EditLine *el;
12+
13+void histedit(void);
14+void sethistsize(const char *);
15+void setterm(const char *);
16+void histload(void);
17+void histsave(void);
18+
19+#else
20+
21+/* stubbed histedit definitions */
22+typedef void History;
23+typedef void EditLine;
24+
25+extern History *hist;
26+extern EditLine *el;
27+
28+#define histedit() ((void)0)
29+#define sethistsize(s) ((void)0)
30+#define setterm(t) ((void)0)
31+#define histload() ((void)0)
32+#define histsave() ((void)0)
33+
34+#endif
35+
36+extern int displayhist;
37+
38+#endif /* !MYHISTEDIT_H_ */
+92,
-0
1@@ -0,0 +1,92 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+/*
37+ * String functions.
38+ *
39+ * equal(s1, s2) Return true if strings are equal.
40+ * number(s) Convert a string of digits to an integer.
41+ * is_number(s) Return true if s is a string of digits.
42+ */
43+
44+#include <stdlib.h>
45+#include "shell.h"
46+#include "syntax.h"
47+#include "error.h"
48+#include "mystring.h"
49+
50+
51+char nullstr[1]; /* zero length string */
52+
53+/*
54+ * equal - #defined in mystring.h
55+ */
56+
57+
58+/*
59+ * Convert a string of digits to an integer, printing an error message on
60+ * failure.
61+ */
62+
63+int
64+number(const char *s)
65+{
66+ if (! is_number(s))
67+ error("Illegal number: %s", s);
68+ return atoi(s);
69+}
70+
71+
72+
73+/*
74+ * Check for a valid number. This should be elsewhere.
75+ */
76+
77+int
78+is_number(const char *p)
79+{
80+ const char *q;
81+
82+ if (*p == '\0')
83+ return 0;
84+ while (*p == '0')
85+ p++;
86+ for (q = p; *q != '\0'; q++)
87+ if (! is_digit(*q))
88+ return 0;
89+ if (q - p > 10 ||
90+ (q - p == 10 && memcmp(p, "2147483647", 10) > 0))
91+ return 0;
92+ return 1;
93+}
+40,
-0
1@@ -0,0 +1,40 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#include <string.h>
37+
38+int number(const char *);
39+int is_number(const char *);
40+
41+#define equal(s1, s2) (strcmp(s1, s2) == 0)
+190,
-0
1@@ -0,0 +1,190 @@
2+/*-
3+ * Copyright (c) 1991, 1993
4+ * The Regents of the University of California. All rights reserved.
5+ *
6+ * This code is derived from software contributed to Berkeley by
7+ * Kenneth Almquist.
8+ *
9+ * Redistribution and use in source and binary forms, with or without
10+ * modification, are permitted provided that the following conditions
11+ * are met:
12+ * 1. Redistributions of source code must retain the above copyright
13+ * notice, this list of conditions and the following disclaimer.
14+ * 2. Redistributions in binary form must reproduce the above copyright
15+ * notice, this list of conditions and the following disclaimer in the
16+ * documentation and/or other materials provided with the distribution.
17+ * 3. Neither the name of the University nor the names of its contributors
18+ * may be used to endorse or promote products derived from this software
19+ * without specific prior written permission.
20+ *
21+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31+ * SUCH DAMAGE.
32+ */
33+
34+#include <sys/param.h>
35+#include <stdlib.h>
36+#include <stddef.h>
37+/*
38+ * Routine for dealing with parsed shell commands.
39+ */
40+
41+#include "shell.h"
42+#include "nodes.h"
43+#include "memalloc.h"
44+#include "mystring.h"
45+
46+
47+struct nodesize {
48+ int blocksize; /* size of structures in function */
49+ int stringsize; /* size of strings in node */
50+};
51+
52+struct nodecopystate {
53+ pointer block; /* block to allocate function from */
54+ char *string; /* block to allocate strings from */
55+};
56+
57+%SIZES
58+
59+
60+static void calcsize(union node *, struct nodesize *);
61+static void sizenodelist(struct nodelist *, struct nodesize *);
62+static union node *copynode(union node *, struct nodecopystate *);
63+static struct nodelist *copynodelist(struct nodelist *, struct nodecopystate *);
64+static char *nodesavestr(const char *, struct nodecopystate *);
65+
66+
67+struct funcdef {
68+ unsigned int refcount;
69+ union node n;
70+};
71+
72+/*
73+ * Make a copy of a parse tree.
74+ */
75+
76+struct funcdef *
77+copyfunc(union node *n)
78+{
79+ struct nodesize sz;
80+ struct nodecopystate st;
81+ struct funcdef *fn;
82+
83+ if (n == NULL)
84+ return NULL;
85+ sz.blocksize = offsetof(struct funcdef, n);
86+ sz.stringsize = 0;
87+ calcsize(n, &sz);
88+ fn = ckmalloc(sz.blocksize + sz.stringsize);
89+ fn->refcount = 1;
90+ st.block = (char *)fn + offsetof(struct funcdef, n);
91+ st.string = (char *)fn + sz.blocksize;
92+ copynode(n, &st);
93+ return fn;
94+}
95+
96+
97+union node *
98+getfuncnode(struct funcdef *fn)
99+{
100+ return fn == NULL ? NULL : &fn->n;
101+}
102+
103+
104+static void
105+calcsize(union node *n, struct nodesize *result)
106+{
107+ %CALCSIZE
108+}
109+
110+
111+
112+static void
113+sizenodelist(struct nodelist *lp, struct nodesize *result)
114+{
115+ while (lp) {
116+ result->blocksize += ALIGN(sizeof(struct nodelist));
117+ calcsize(lp->n, result);
118+ lp = lp->next;
119+ }
120+}
121+
122+
123+
124+static union node *
125+copynode(union node *n, struct nodecopystate *state)
126+{
127+ union node *new;
128+
129+ %COPY
130+ return new;
131+}
132+
133+
134+static struct nodelist *
135+copynodelist(struct nodelist *lp, struct nodecopystate *state)
136+{
137+ struct nodelist *start;
138+ struct nodelist **lpp;
139+
140+ lpp = &start;
141+ while (lp) {
142+ *lpp = state->block;
143+ state->block = (char *)state->block +
144+ ALIGN(sizeof(struct nodelist));
145+ (*lpp)->n = copynode(lp->n, state);
146+ lp = lp->next;
147+ lpp = &(*lpp)->next;
148+ }
149+ *lpp = NULL;
150+ return start;
151+}
152+
153+
154+
155+static char *
156+nodesavestr(const char *s, struct nodecopystate *state)
157+{
158+ const char *p = s;
159+ char *q = state->string;
160+ char *rtn = state->string;
161+
162+ while ((*q++ = *p++) != '\0')
163+ continue;
164+ state->string = q;
165+ return rtn;
166+}
167+
168+
169+void
170+reffunc(struct funcdef *fn)
171+{
172+ if (fn)
173+ fn->refcount++;
174+}
175+
176+
177+/*
178+ * Decrement the reference count of a function definition, freeing it
179+ * if it falls to 0.
180+ */
181+
182+void
183+unreffunc(struct funcdef *fn)
184+{
185+ if (fn) {
186+ fn->refcount--;
187+ if (fn->refcount > 0)
188+ return;
189+ ckfree(fn);
190+ }
191+}
+142,
-0
1@@ -0,0 +1,142 @@
2+#-
3+# Copyright (c) 1991, 1993
4+# The Regents of the University of California. All rights reserved.
5+#
6+# This code is derived from software contributed to Berkeley by
7+# Kenneth Almquist.
8+#
9+# Redistribution and use in source and binary forms, with or without
10+# modification, are permitted provided that the following conditions
11+# are met:
12+# 1. Redistributions of source code must retain the above copyright
13+# notice, this list of conditions and the following disclaimer.
14+# 2. Redistributions in binary form must reproduce the above copyright
15+# notice, this list of conditions and the following disclaimer in the
16+# documentation and/or other materials provided with the distribution.
17+# 3. Neither the name of the University nor the names of its contributors
18+# may be used to endorse or promote products derived from this software
19+# without specific prior written permission.
20+#
21+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31+# SUCH DAMAGE.
32+
33+# This file describes the nodes used in parse trees. Unindented lines
34+# contain a node type followed by a structure tag. Subsequent indented
35+# lines specify the fields of the structure. Several node types can share
36+# the same structure, in which case the fields of the structure should be
37+# specified only once.
38+#
39+# A field of a structure is described by the name of the field followed
40+# by a type. The currently implemented types are:
41+# nodeptr - a pointer to a node
42+# nodelist - a pointer to a list of nodes
43+# string - a pointer to a nul terminated string
44+# int - an integer
45+# other - any type that can be copied by assignment
46+# temp - a field that doesn't have to be copied when the node is copied
47+# The last two types should be followed by the text of a C declaration for
48+# the field.
49+
50+NSEMI nbinary # two commands separated by a semicolon
51+ type int
52+ ch1 nodeptr # the first child
53+ ch2 nodeptr # the second child
54+
55+NCMD ncmd # a simple command
56+ type int
57+ args nodeptr # the arguments
58+ redirect nodeptr # list of file redirections
59+
60+NPIPE npipe # a pipeline
61+ type int
62+ backgnd int # set to run pipeline in background
63+ cmdlist nodelist # the commands in the pipeline
64+
65+NREDIR nredir # redirection (of a complex command)
66+ type int
67+ n nodeptr # the command
68+ redirect nodeptr # list of file redirections
69+
70+NBACKGND nredir # run command in background
71+NSUBSHELL nredir # run command in a subshell
72+
73+NAND nbinary # the && operator
74+NOR nbinary # the || operator
75+
76+NIF nif # the if statement. Elif clauses are handled
77+ type int # using multiple if nodes.
78+ test nodeptr # if test
79+ ifpart nodeptr # then ifpart
80+ elsepart nodeptr # else elsepart
81+
82+NWHILE nbinary # the while statement. First child is the test
83+NUNTIL nbinary # the until statement
84+
85+NFOR nfor # the for statement
86+ type int
87+ args nodeptr # for var in args
88+ body nodeptr # do body; done
89+ var string # the for variable
90+
91+NCASE ncase # a case statement
92+ type int
93+ expr nodeptr # the word to switch on
94+ cases nodeptr # the list of cases (NCLIST nodes)
95+
96+NCLIST nclist # a case ending with ;;
97+ type int
98+ next nodeptr # the next case in list
99+ pattern nodeptr # list of patterns for this case
100+ body nodeptr # code to execute for this case
101+
102+NCLISTFALLTHRU nclist # a case ending with ;&
103+
104+NDEFUN narg # define a function. The "next" field contains
105+ # the body of the function.
106+
107+NARG narg # represents a word
108+ type int
109+ next nodeptr # next word in list
110+ text string # the text of the word
111+ backquote nodelist # list of commands in back quotes
112+
113+NTO nfile # fd> fname
114+NFROM nfile # fd< fname
115+NFROMTO nfile # fd<> fname
116+NAPPEND nfile # fd>> fname
117+NCLOBBER nfile # fd>| fname
118+ type int
119+ fd int # file descriptor being redirected
120+ next nodeptr # next redirection in list
121+ fname nodeptr # file name, in a NARG node
122+ expfname temp char *expfname # actual file name
123+
124+NTOFD ndup # fd<&dupfd
125+NFROMFD ndup # fd>&dupfd
126+ type int
127+ fd int # file descriptor being redirected
128+ next nodeptr # next redirection in list
129+ dupfd int # file descriptor to duplicate
130+ vname nodeptr # file name if fd>&$var
131+
132+
133+NHERE nhere # fd<<\!
134+NXHERE nhere # fd<<!
135+ type int
136+ fd int # file descriptor being redirected
137+ next nodeptr # next redirection in list
138+ doc nodeptr # input to command (NARG node)
139+ expdoc temp const char *expdoc # actual document (for NXHERE)
140+
141+NNOT nnot # ! command (actually pipeline)
142+ type int
143+ com nodeptr
+597,
-0
1@@ -0,0 +1,597 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#include <signal.h>
37+#include <unistd.h>
38+#include <stdlib.h>
39+
40+#include "shell.h"
41+#define DEFINE_OPTIONS
42+#include "options.h"
43+#undef DEFINE_OPTIONS
44+#include "nodes.h" /* for other header files */
45+#include "eval.h"
46+#include "jobs.h"
47+#include "input.h"
48+#include "output.h"
49+#include "trap.h"
50+#include "var.h"
51+#include "memalloc.h"
52+#include "error.h"
53+#include "mystring.h"
54+#include "builtins.h"
55+#ifndef NO_HISTORY
56+#include "myhistedit.h"
57+#endif
58+
59+char *arg0; /* value of $0 */
60+struct shparam shellparam; /* current positional parameters */
61+char **argptr; /* argument list for builtin commands */
62+char *shoptarg; /* set by nextopt (like getopt) */
63+char *nextopt_optptr; /* used by nextopt */
64+
65+char *minusc; /* argument to -c option */
66+
67+
68+static int options(int);
69+static void minus_o(char *, int);
70+static void setoption(int, int);
71+static void setoptionbyindex(int, int);
72+static void setparam(int, char **);
73+static int getopts(char *, char *, char **, char ***, char **);
74+
75+
76+/*
77+ * Process the shell command line arguments.
78+ */
79+
80+int
81+procargs(int argc, char **argv)
82+{
83+ int i, login;
84+ char *scriptname;
85+
86+ argptr = argv;
87+ login = argptr[0] != NULL && argptr[0][0] == '-';
88+ if (argc > 0)
89+ argptr++;
90+ for (i = 0; i < NOPTS; i++)
91+ optval[i] = 2;
92+ privileged = (getuid() != geteuid() || getgid() != getegid());
93+ login |= options(1);
94+ if (*argptr == NULL && minusc == NULL)
95+ sflag = 1;
96+ if (iflag != 0 && sflag == 1 && isatty(0) && isatty(1)) {
97+ iflag = 1;
98+ if (Eflag == 2)
99+ Eflag = 1;
100+ }
101+ if (mflag == 2)
102+ mflag = iflag;
103+ for (i = 0; i < NOPTS; i++)
104+ if (optval[i] == 2)
105+ optval[i] = 0;
106+ arg0 = argv[0];
107+ if (sflag == 0 && minusc == NULL) {
108+ scriptname = *argptr++;
109+ setinputfile(scriptname, 0, -1 /* verify */);
110+ commandname = arg0 = scriptname;
111+ }
112+ /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
113+ if (argptr && minusc && *argptr)
114+ arg0 = *argptr++;
115+
116+ shellparam.p = argptr;
117+ shellparam.reset = 1;
118+ /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
119+ while (*argptr) {
120+ shellparam.nparam++;
121+ argptr++;
122+ }
123+ optschanged();
124+
125+ return (login);
126+}
127+
128+
129+void
130+optschanged(void)
131+{
132+ setinteractive();
133+#ifndef NO_HISTORY
134+ histedit();
135+#endif
136+ setjobctl(mflag);
137+}
138+
139+/*
140+ * Process shell options. The global variable argptr contains a pointer
141+ * to the argument list; we advance it past the options.
142+ * If cmdline is true, process the shell's argv; otherwise, process arguments
143+ * to the set special builtin.
144+ */
145+
146+static int
147+options(int cmdline)
148+{
149+ char *kp, *p;
150+ int val;
151+ int c;
152+ int login = 0;
153+
154+ if (cmdline)
155+ minusc = NULL;
156+ while ((p = *argptr) != NULL) {
157+ argptr++;
158+ if ((c = *p++) == '-') {
159+ val = 1;
160+ /* A "-" or "--" terminates options */
161+ if (p[0] == '\0')
162+ goto end_options1;
163+ if (p[0] == '-' && p[1] == '\0')
164+ goto end_options2;
165+ /**
166+ * For the benefit of `#!' lines in shell scripts,
167+ * treat a string of '-- *#.*' the same as '--'.
168+ * This is needed so that a script starting with:
169+ * #!/bin/sh -- # -*- perl -*-
170+ * will continue to work after a change is made to
171+ * kern/imgact_shell.c to NOT token-ize the options
172+ * specified on a '#!' line. A bit of a kludge,
173+ * but that trick is recommended in documentation
174+ * for some scripting languages, and we might as
175+ * well continue to support it.
176+ */
177+ if (p[0] == '-') {
178+ kp = p + 1;
179+ while (*kp == ' ' || *kp == '\t')
180+ kp++;
181+ if (*kp == '#' || *kp == '\0')
182+ goto end_options2;
183+ }
184+ } else if (c == '+') {
185+ val = 0;
186+ } else {
187+ argptr--;
188+ break;
189+ }
190+ while ((c = *p++) != '\0') {
191+ if (c == 'c' && cmdline) {
192+ char *q;
193+
194+ q = *argptr++;
195+ if (q == NULL || minusc != NULL)
196+ error("Bad -c option");
197+ minusc = q;
198+ } else if (c == 'l' && cmdline) {
199+ login = 1;
200+ } else if (c == 'o') {
201+ minus_o(*argptr, val);
202+ if (*argptr)
203+ argptr++;
204+ } else
205+ setoption(c, val);
206+ }
207+ }
208+ return (login);
209+
210+ /* When processing `set', a single "-" means turn off -x and -v */
211+end_options1:
212+ if (!cmdline) {
213+ xflag = vflag = 0;
214+ return (login);
215+ }
216+
217+ /*
218+ * When processing `set', a "--" means the remaining arguments
219+ * replace the positional parameters in the active shell. If
220+ * there are no remaining options, then all the positional
221+ * parameters are cleared (equivalent to doing ``shift $#'').
222+ */
223+end_options2:
224+ if (!cmdline) {
225+ if (*argptr == NULL)
226+ setparam(0, argptr);
227+ return (login);
228+ }
229+
230+ /*
231+ * At this point we are processing options given to 'sh' on a command
232+ * line. If an end-of-options marker ("-" or "--") is followed by an
233+ * arg of "#", then skip over all remaining arguments. Some scripting
234+ * languages (e.g.: perl) document that /bin/sh will implement this
235+ * behavior, and they recommend that users take advantage of it to
236+ * solve certain issues that can come up when writing a perl script.
237+ * Yes, this feature is in /bin/sh to help users write perl scripts.
238+ */
239+ p = *argptr;
240+ if (p != NULL && p[0] == '#' && p[1] == '\0') {
241+ while (*argptr != NULL)
242+ argptr++;
243+ /* We need to keep the final argument */
244+ argptr--;
245+ }
246+
247+ return (login);
248+}
249+
250+static void
251+minus_o(char *name, int val)
252+{
253+ int i;
254+ const unsigned char *on;
255+ size_t len;
256+
257+ if (name == NULL) {
258+ if (val) {
259+ /* "Pretty" output. */
260+ out1str("Current option settings\n");
261+ for (i = 0, on = optname; i < NOPTS; i++, on += *on + 1)
262+ out1fmt("%-16.*s%s\n", *on, on + 1,
263+ optval[i] ? "on" : "off");
264+ } else {
265+ /* Output suitable for re-input to shell. */
266+ for (i = 0, on = optname; i < NOPTS; i++, on += *on + 1)
267+ out1fmt("%s %co %.*s%s",
268+ i % 6 == 0 ? "set" : "",
269+ optval[i] ? '-' : '+',
270+ *on, on + 1,
271+ i % 6 == 5 || i == NOPTS - 1 ? "\n" : "");
272+ }
273+ } else {
274+ len = strlen(name);
275+ for (i = 0, on = optname; i < NOPTS; i++, on += *on + 1)
276+ if (*on == len && memcmp(on + 1, name, len) == 0) {
277+ setoptionbyindex(i, val);
278+ return;
279+ }
280+ error("Illegal option -o %s", name);
281+ }
282+}
283+
284+
285+static void
286+setoptionbyindex(int idx, int val)
287+{
288+ if (&optval[idx] == &privileged && !val && privileged) {
289+ if (setgid(getgid()) == -1)
290+ error("setgid");
291+ if (setuid(getuid()) == -1)
292+ error("setuid");
293+ }
294+ optval[idx] = val;
295+ if (val) {
296+ /* #%$ hack for ksh semantics */
297+ if (&optval[idx] == &Vflag)
298+ Eflag = 0;
299+ else if (&optval[idx] == &Eflag)
300+ Vflag = 0;
301+ }
302+}
303+
304+static void
305+setoption(int flag, int val)
306+{
307+ int i;
308+
309+ for (i = 0; i < NSHORTOPTS; i++)
310+ if (optletter[i] == flag) {
311+ setoptionbyindex(i, val);
312+ return;
313+ }
314+ error("Illegal option -%c", flag);
315+}
316+
317+
318+/*
319+ * Set the shell parameters.
320+ */
321+
322+static void
323+setparam(int argc, char **argv)
324+{
325+ char **newparam;
326+ char **ap;
327+
328+ ap = newparam = ckmalloc((argc + 1) * sizeof *ap);
329+ while (*argv) {
330+ *ap++ = savestr(*argv++);
331+ }
332+ *ap = NULL;
333+ freeparam(&shellparam);
334+ shellparam.malloc = 1;
335+ shellparam.nparam = argc;
336+ shellparam.p = newparam;
337+ shellparam.optp = NULL;
338+ shellparam.reset = 1;
339+ shellparam.optnext = NULL;
340+}
341+
342+
343+/*
344+ * Free the list of positional parameters.
345+ */
346+
347+void
348+freeparam(struct shparam *param)
349+{
350+ char **ap;
351+
352+ if (param->malloc) {
353+ for (ap = param->p ; *ap ; ap++)
354+ ckfree(*ap);
355+ ckfree(param->p);
356+ }
357+ if (param->optp) {
358+ for (ap = param->optp ; *ap ; ap++)
359+ ckfree(*ap);
360+ ckfree(param->optp);
361+ }
362+}
363+
364+
365+
366+/*
367+ * The shift builtin command.
368+ */
369+
370+int
371+shiftcmd(int argc, char **argv)
372+{
373+ int i, n;
374+
375+ n = 1;
376+ if (argc > 1)
377+ n = number(argv[1]);
378+ if (n > shellparam.nparam)
379+ return 1;
380+ INTOFF;
381+ shellparam.nparam -= n;
382+ if (shellparam.malloc)
383+ for (i = 0; i < n; i++)
384+ ckfree(shellparam.p[i]);
385+ memmove(shellparam.p, shellparam.p + n,
386+ (shellparam.nparam + 1) * sizeof(shellparam.p[0]));
387+ shellparam.reset = 1;
388+ INTON;
389+ return 0;
390+}
391+
392+
393+
394+/*
395+ * The set builtin command.
396+ */
397+
398+int
399+setcmd(int argc, char **argv)
400+{
401+ if (argc == 1)
402+ return showvarscmd(argc, argv);
403+ INTOFF;
404+ options(0);
405+ optschanged();
406+ if (*argptr != NULL) {
407+ setparam(argc - (argptr - argv), argptr);
408+ }
409+ INTON;
410+ return 0;
411+}
412+
413+
414+void
415+getoptsreset(const char *value)
416+{
417+ while (*value == '0')
418+ value++;
419+ if (strcmp(value, "1") == 0)
420+ shellparam.reset = 1;
421+}
422+
423+/*
424+ * The getopts builtin. Shellparam.optnext points to the next argument
425+ * to be processed. Shellparam.optptr points to the next character to
426+ * be processed in the current argument. If shellparam.optnext is NULL,
427+ * then it's the first time getopts has been called.
428+ */
429+
430+int
431+getoptscmd(int argc, char **argv)
432+{
433+ char **optbase = NULL, **ap;
434+ int i;
435+
436+ if (argc < 3)
437+ error("usage: getopts optstring var [arg]");
438+
439+ if (shellparam.reset == 1) {
440+ INTOFF;
441+ if (shellparam.optp) {
442+ for (ap = shellparam.optp ; *ap ; ap++)
443+ ckfree(*ap);
444+ ckfree(shellparam.optp);
445+ shellparam.optp = NULL;
446+ }
447+ if (argc > 3) {
448+ shellparam.optp = ckmalloc((argc - 2) * sizeof *ap);
449+ memset(shellparam.optp, '\0', (argc - 2) * sizeof *ap);
450+ for (i = 0; i < argc - 3; i++)
451+ shellparam.optp[i] = savestr(argv[i + 3]);
452+ }
453+ INTON;
454+ optbase = argc == 3 ? shellparam.p : shellparam.optp;
455+ shellparam.optnext = optbase;
456+ shellparam.optptr = NULL;
457+ shellparam.reset = 0;
458+ } else
459+ optbase = shellparam.optp ? shellparam.optp : shellparam.p;
460+
461+ return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
462+ &shellparam.optptr);
463+}
464+
465+static int
466+getopts(char *optstr, char *optvar, char **optfirst, char ***optnext,
467+ char **optptr)
468+{
469+ char *p, *q;
470+ char c = '?';
471+ int done = 0;
472+ int ind = 0;
473+ int err = 0;
474+ char s[10];
475+ const char *newoptarg = NULL;
476+
477+ if ((p = *optptr) == NULL || *p == '\0') {
478+ /* Current word is done, advance */
479+ if (*optnext == NULL)
480+ return 1;
481+ p = **optnext;
482+ if (p == NULL || *p != '-' || *++p == '\0') {
483+ atend:
484+ ind = *optnext - optfirst + 1;
485+ *optnext = NULL;
486+ p = NULL;
487+ done = 1;
488+ goto out;
489+ }
490+ (*optnext)++;
491+ if (p[0] == '-' && p[1] == '\0') /* check for "--" */
492+ goto atend;
493+ }
494+
495+ c = *p++;
496+ for (q = optstr; *q != c; ) {
497+ if (*q == '\0') {
498+ if (optstr[0] == ':') {
499+ s[0] = c;
500+ s[1] = '\0';
501+ newoptarg = s;
502+ }
503+ else
504+ out2fmt_flush("Illegal option -%c\n", c);
505+ c = '?';
506+ goto out;
507+ }
508+ if (*++q == ':')
509+ q++;
510+ }
511+
512+ if (*++q == ':') {
513+ if (*p == '\0' && (p = **optnext) == NULL) {
514+ if (optstr[0] == ':') {
515+ s[0] = c;
516+ s[1] = '\0';
517+ newoptarg = s;
518+ c = ':';
519+ }
520+ else {
521+ out2fmt_flush("No arg for -%c option\n", c);
522+ c = '?';
523+ }
524+ goto out;
525+ }
526+
527+ if (p == **optnext)
528+ (*optnext)++;
529+ newoptarg = p;
530+ p = NULL;
531+ }
532+
533+out:
534+ if (*optnext != NULL)
535+ ind = *optnext - optfirst + 1;
536+ *optptr = p;
537+ if (newoptarg != NULL)
538+ err |= setvarsafe("OPTARG", newoptarg, 0);
539+ else {
540+ INTOFF;
541+ err |= unsetvar("OPTARG");
542+ INTON;
543+ }
544+ fmtstr(s, sizeof(s), "%d", ind);
545+ err |= setvarsafe("OPTIND", s, VNOFUNC);
546+ s[0] = c;
547+ s[1] = '\0';
548+ err |= setvarsafe(optvar, s, 0);
549+ if (err) {
550+ *optnext = NULL;
551+ *optptr = NULL;
552+ flushall();
553+ exraise(EXERROR);
554+ }
555+ return done;
556+}
557+
558+/*
559+ * Standard option processing (a la getopt) for builtin routines. The
560+ * only argument that is passed to nextopt is the option string; the
561+ * other arguments are unnecessary. It returns the option, or '\0' on
562+ * end of input.
563+ */
564+
565+int
566+nextopt(const char *optstring)
567+{
568+ char *p;
569+ const char *q;
570+ char c;
571+
572+ if ((p = nextopt_optptr) == NULL || *p == '\0') {
573+ p = *argptr;
574+ if (p == NULL || *p != '-' || *++p == '\0')
575+ return '\0';
576+ argptr++;
577+ if (p[0] == '-' && p[1] == '\0') /* check for "--" */
578+ return '\0';
579+ }
580+ c = *p++;
581+ for (q = optstring ; *q != c ; ) {
582+ if (*q == '\0')
583+ error("Illegal option -%c", c);
584+ if (*++q == ':')
585+ q++;
586+ }
587+ if (*++q == ':') {
588+ if (*p == '\0' && (p = *argptr++) == NULL)
589+ error("No arg for -%c option", c);
590+ shoptarg = p;
591+ p = NULL;
592+ }
593+ if (p != NULL && *p != '\0')
594+ nextopt_optptr = p;
595+ else
596+ nextopt_optptr = NULL;
597+ return c;
598+}
+116,
-0
1@@ -0,0 +1,116 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+struct shparam {
37+ int nparam; /* # of positional parameters (without $0) */
38+ unsigned char malloc; /* if parameter list dynamically allocated */
39+ unsigned char reset; /* if getopts has been reset */
40+ char **p; /* parameter list */
41+ char **optp; /* parameter list for getopts */
42+ char **optnext; /* next parameter to be processed by getopts */
43+ char *optptr; /* used by getopts */
44+};
45+
46+
47+
48+#define eflag optval[0]
49+#define fflag optval[1]
50+#define Iflag optval[2]
51+#define iflag optval[3]
52+#define mflag optval[4]
53+#define nflag optval[5]
54+#define sflag optval[6]
55+#define xflag optval[7]
56+#define vflag optval[8]
57+#define Vflag optval[9]
58+#define Eflag optval[10]
59+#define Cflag optval[11]
60+#define aflag optval[12]
61+#define bflag optval[13]
62+#define uflag optval[14]
63+#define privileged optval[15]
64+#define Tflag optval[16]
65+#define Pflag optval[17]
66+#define hflag optval[18]
67+#define nologflag optval[19]
68+#define pipefailflag optval[20]
69+#define verifyflag optval[21]
70+
71+#define NSHORTOPTS 19
72+#define NOPTS 22
73+
74+extern char optval[NOPTS];
75+extern const char optletter[NSHORTOPTS];
76+#ifdef DEFINE_OPTIONS
77+char optval[NOPTS];
78+const char optletter[NSHORTOPTS] __nonstring = "efIimnsxvVECabupTPh";
79+static const unsigned char optname[] =
80+ "\007errexit"
81+ "\006noglob"
82+ "\011ignoreeof"
83+ "\013interactive"
84+ "\007monitor"
85+ "\006noexec"
86+ "\005stdin"
87+ "\006xtrace"
88+ "\007verbose"
89+ "\002vi"
90+ "\005emacs"
91+ "\011noclobber"
92+ "\011allexport"
93+ "\006notify"
94+ "\007nounset"
95+ "\012privileged"
96+ "\012trapsasync"
97+ "\010physical"
98+ "\010trackall"
99+ "\005nolog"
100+ "\010pipefail"
101+ "\006verify"
102+;
103+#endif
104+
105+
106+extern char *minusc; /* argument to -c option */
107+extern char *arg0; /* $0 */
108+extern struct shparam shellparam; /* $@ */
109+extern char **argptr; /* argument list for builtin commands */
110+extern char *shoptarg; /* set by nextopt */
111+extern char *nextopt_optptr; /* used by nextopt */
112+
113+int procargs(int, char **);
114+void optschanged(void);
115+void freeparam(struct shparam *);
116+int nextopt(const char *);
117+void getoptsreset(const char *);
+415,
-0
1@@ -0,0 +1,415 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+/*
37+ * Shell output routines. We use our own output routines because:
38+ * When a builtin command is interrupted we have to discard
39+ * any pending output.
40+ * When a builtin command appears in back quotes, we want to
41+ * save the output of the command in a region obtained
42+ * via malloc, rather than doing a fork and reading the
43+ * output of the command via a pipe.
44+ */
45+
46+#include <stdio.h> /* defines BUFSIZ */
47+#include <string.h>
48+#include <stdarg.h>
49+#include <errno.h>
50+#include <unistd.h>
51+#include <stdlib.h>
52+#include <wchar.h>
53+#include <wctype.h>
54+
55+#if defined(__linux__) || defined(__CYGWIN__)
56+struct fwopen_cookie {
57+ void *cookie;
58+ int (*writefn)(void *, const char *, int);
59+};
60+
61+static ssize_t
62+compat_fwopen_write(void *cookie, const char *buf, size_t size)
63+{
64+ struct fwopen_cookie *c = cookie;
65+ return c->writefn(c->cookie, buf, (int)size);
66+}
67+
68+static int
69+compat_fwopen_close(void *cookie)
70+{
71+ free(cookie);
72+ return 0;
73+}
74+
75+static FILE *
76+fwopen(void *cookie, int (*writefn)(void *, const char *, int))
77+{
78+ struct fwopen_cookie *c;
79+ cookie_io_functions_t io_funcs = {
80+ .read = NULL,
81+ .write = compat_fwopen_write,
82+ .seek = NULL,
83+ .close = compat_fwopen_close
84+ };
85+ FILE *fp;
86+
87+ c = malloc(sizeof(struct fwopen_cookie));
88+ if (c == NULL)
89+ return NULL;
90+ c->cookie = cookie;
91+ c->writefn = writefn;
92+
93+ fp = fopencookie(c, "w", io_funcs);
94+ if (fp == NULL) {
95+ free(c);
96+ return NULL;
97+ }
98+ return fp;
99+}
100+#endif
101+
102+#include "shell.h"
103+#include "syntax.h"
104+#include "output.h"
105+#include "memalloc.h"
106+#include "error.h"
107+#include "var.h"
108+
109+
110+#define OUTBUFSIZ BUFSIZ
111+#define MEM_OUT -2 /* output to dynamically allocated memory */
112+#define OUTPUT_ERR 01 /* error occurred on output */
113+
114+static int doformat_wr(void *, const char *, int);
115+
116+struct output output = {NULL, NULL, NULL, OUTBUFSIZ, 1, 0};
117+struct output errout = {NULL, NULL, NULL, 256, 2, 0};
118+struct output memout = {NULL, NULL, NULL, 64, MEM_OUT, 0};
119+struct output *out1 = &output;
120+struct output *out2 = &errout;
121+
122+void
123+outcslow(int c, struct output *file)
124+{
125+ outc(c, file);
126+}
127+
128+void
129+out1str(const char *p)
130+{
131+ outstr(p, out1);
132+}
133+
134+void
135+out1qstr(const char *p)
136+{
137+ outqstr(p, out1);
138+}
139+
140+void
141+out2str(const char *p)
142+{
143+ outstr(p, out2);
144+}
145+
146+void
147+out2qstr(const char *p)
148+{
149+ outqstr(p, out2);
150+}
151+
152+void
153+outstr(const char *p, struct output *file)
154+{
155+ outbin(p, strlen(p), file);
156+}
157+
158+static void
159+byteseq(int ch, struct output *file)
160+{
161+ char seq[4];
162+
163+ seq[0] = '\\';
164+ seq[1] = (ch >> 6 & 0x3) + '0';
165+ seq[2] = (ch >> 3 & 0x7) + '0';
166+ seq[3] = (ch & 0x7) + '0';
167+ outbin(seq, 4, file);
168+}
169+
170+static void
171+outdqstr(const char *p, struct output *file)
172+{
173+ const char *end;
174+ mbstate_t mbs;
175+ size_t clen;
176+ wchar_t wc;
177+
178+ memset(&mbs, '\0', sizeof(mbs));
179+ end = p + strlen(p);
180+ outstr("$'", file);
181+ while ((clen = mbrtowc(&wc, p, end - p + 1, &mbs)) != 0) {
182+ if (clen == (size_t)-2) {
183+ while (p < end)
184+ byteseq(*p++, file);
185+ break;
186+ }
187+ if (clen == (size_t)-1) {
188+ memset(&mbs, '\0', sizeof(mbs));
189+ byteseq(*p++, file);
190+ continue;
191+ }
192+ if (wc == L'\n')
193+ outcslow('\n', file), p++;
194+ else if (wc == L'\r')
195+ outstr("\\r", file), p++;
196+ else if (wc == L'\t')
197+ outstr("\\t", file), p++;
198+ else if (!iswprint(wc)) {
199+ for (; clen > 0; clen--)
200+ byteseq(*p++, file);
201+ } else {
202+ if (wc == L'\'' || wc == L'\\')
203+ outcslow('\\', file);
204+ outbin(p, clen, file);
205+ p += clen;
206+ }
207+ }
208+ outcslow('\'', file);
209+}
210+
211+/* Like outstr(), but quote for re-input into the shell. */
212+void
213+outqstr(const char *p, struct output *file)
214+{
215+ int i;
216+
217+ if (p[0] == '\0') {
218+ outstr("''", file);
219+ return;
220+ }
221+ for (i = 0; p[i] != '\0'; i++) {
222+ if ((p[i] > '\0' && p[i] < ' ' && p[i] != '\n') ||
223+ (p[i] & 0x80) != 0 || p[i] == '\'') {
224+ outdqstr(p, file);
225+ return;
226+ }
227+ }
228+
229+ if (p[strcspn(p, "|&;<>()$`\\\" \n*?[~#=")] == '\0' ||
230+ strcmp(p, "[") == 0) {
231+ outstr(p, file);
232+ return;
233+ }
234+
235+ outcslow('\'', file);
236+ outstr(p, file);
237+ outcslow('\'', file);
238+}
239+
240+void
241+outbin(const void *data, size_t len, struct output *file)
242+{
243+ const char *p;
244+
245+ p = data;
246+ while (len-- > 0)
247+ outc(*p++, file);
248+}
249+
250+void
251+emptyoutbuf(struct output *dest)
252+{
253+ int offset, newsize;
254+
255+ if (dest->buf == NULL) {
256+ INTOFF;
257+ dest->buf = ckmalloc(dest->bufsize);
258+ dest->nextc = dest->buf;
259+ dest->bufend = dest->buf + dest->bufsize;
260+ INTON;
261+ } else if (dest->fd == MEM_OUT) {
262+ offset = dest->nextc - dest->buf;
263+ newsize = dest->bufsize << 1;
264+ INTOFF;
265+ dest->buf = ckrealloc(dest->buf, newsize);
266+ dest->bufsize = newsize;
267+ dest->bufend = dest->buf + newsize;
268+ dest->nextc = dest->buf + offset;
269+ INTON;
270+ } else {
271+ flushout(dest);
272+ }
273+}
274+
275+
276+void
277+flushall(void)
278+{
279+ flushout(&output);
280+ flushout(&errout);
281+}
282+
283+
284+void
285+flushout(struct output *dest)
286+{
287+
288+ if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
289+ return;
290+ if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
291+ dest->flags |= OUTPUT_ERR;
292+ dest->nextc = dest->buf;
293+}
294+
295+
296+void
297+freestdout(void)
298+{
299+ output.nextc = output.buf;
300+}
301+
302+
303+int
304+outiserror(struct output *file)
305+{
306+ return (file->flags & OUTPUT_ERR);
307+}
308+
309+
310+void
311+outclearerror(struct output *file)
312+{
313+ file->flags &= ~OUTPUT_ERR;
314+}
315+
316+
317+void
318+outfmt(struct output *file, const char *fmt, ...)
319+{
320+ va_list ap;
321+
322+ va_start(ap, fmt);
323+ doformat(file, fmt, ap);
324+ va_end(ap);
325+}
326+
327+
328+void
329+out1fmt(const char *fmt, ...)
330+{
331+ va_list ap;
332+
333+ va_start(ap, fmt);
334+ doformat(out1, fmt, ap);
335+ va_end(ap);
336+}
337+
338+void
339+out2fmt_flush(const char *fmt, ...)
340+{
341+ va_list ap;
342+
343+ va_start(ap, fmt);
344+ doformat(out2, fmt, ap);
345+ va_end(ap);
346+ flushout(out2);
347+}
348+
349+void
350+fmtstr(char *outbuf, int length, const char *fmt, ...)
351+{
352+ va_list ap;
353+
354+ INTOFF;
355+ va_start(ap, fmt);
356+ vsnprintf(outbuf, length, fmt, ap);
357+ va_end(ap);
358+ INTON;
359+}
360+
361+static int
362+doformat_wr(void *cookie, const char *buf, int len)
363+{
364+ struct output *o;
365+
366+ o = (struct output *)cookie;
367+ outbin(buf, len, o);
368+
369+ return (len);
370+}
371+
372+void
373+doformat(struct output *dest, const char *f, va_list ap)
374+{
375+ FILE *fp;
376+
377+ if ((fp = fwopen(dest, doformat_wr)) != NULL) {
378+ vfprintf(fp, f, ap);
379+ fclose(fp);
380+ }
381+}
382+
383+FILE *
384+out1fp(void)
385+{
386+ return fwopen(out1, doformat_wr);
387+}
388+
389+/*
390+ * Version of write which resumes after a signal is caught.
391+ */
392+
393+int
394+xwrite(int fd, const char *buf, int nbytes)
395+{
396+ int ntry;
397+ int i;
398+ int n;
399+
400+ n = nbytes;
401+ ntry = 0;
402+ for (;;) {
403+ i = write(fd, buf, n);
404+ if (i > 0) {
405+ if ((n -= i) <= 0)
406+ return nbytes;
407+ buf += i;
408+ ntry = 0;
409+ } else if (i == 0) {
410+ if (++ntry > 10)
411+ return nbytes - n;
412+ } else if (errno != EINTR) {
413+ return -1;
414+ }
415+ }
416+}
+84,
-0
1@@ -0,0 +1,84 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#ifndef OUTPUT_INCL
37+
38+#include <stdarg.h>
39+#include <stddef.h>
40+#include <stdio.h>
41+
42+struct output {
43+ char *nextc;
44+ char *bufend;
45+ char *buf;
46+ int bufsize;
47+ short fd;
48+ short flags;
49+};
50+
51+extern struct output output; /* to fd 1 */
52+extern struct output errout; /* to fd 2 */
53+extern struct output memout;
54+extern struct output *out1; /* &memout if backquote, otherwise &output */
55+extern struct output *out2; /* &memout if backquote with 2>&1, otherwise
56+ &errout */
57+
58+void outcslow(int, struct output *);
59+void out1str(const char *);
60+void out1qstr(const char *);
61+void out2str(const char *);
62+void out2qstr(const char *);
63+void outstr(const char *, struct output *);
64+void outqstr(const char *, struct output *);
65+void outbin(const void *, size_t, struct output *);
66+void emptyoutbuf(struct output *);
67+void flushall(void);
68+void flushout(struct output *);
69+void freestdout(void);
70+int outiserror(struct output *);
71+void outclearerror(struct output *);
72+void outfmt(struct output *, const char *, ...) __printflike(2, 3);
73+void out1fmt(const char *, ...) __printflike(1, 2);
74+void out2fmt_flush(const char *, ...) __printflike(1, 2);
75+void fmtstr(char *, int, const char *, ...) __printflike(3, 4);
76+void doformat(struct output *, const char *, va_list) __printflike(2, 0);
77+FILE *out1fp(void);
78+int xwrite(int, const char *, int);
79+
80+#define outc(c, file) ((file)->nextc == (file)->bufend ? (emptyoutbuf(file), *(file)->nextc++ = (c)) : (*(file)->nextc++ = (c)))
81+#define out1c(c) outc(c, out1);
82+#define out2c(c) outcslow(c, out2);
83+
84+#define OUTPUT_INCL
85+#endif
+2404,
-0
1@@ -0,0 +1,2404 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#include <sys/param.h>
37+#include <pwd.h>
38+#include <stdlib.h>
39+#include <unistd.h>
40+#include <stdio.h>
41+#include <time.h>
42+
43+#include "shell.h"
44+
45+#undef strlcpy
46+#define strlcpy xstrlcpy
47+size_t xstrlcpy(char *, const char *, size_t);
48+
49+#include "parser.h"
50+#include "nodes.h"
51+#include "expand.h" /* defines rmescapes() */
52+#include "syntax.h"
53+#include "options.h"
54+#include "input.h"
55+#include "output.h"
56+#include "var.h"
57+#include "error.h"
58+#include "memalloc.h"
59+#include "mystring.h"
60+#include "alias.h"
61+#include "show.h"
62+#include "eval.h"
63+#include "exec.h" /* to check for special builtins */
64+#include "main.h"
65+#include "jobs.h"
66+#ifndef NO_HISTORY
67+#include "myhistedit.h"
68+#endif
69+
70+/*
71+ * Shell command parser.
72+ */
73+
74+#define PROMPTLEN 192
75+
76+/* values of checkkwd variable */
77+#define CHKALIAS 0x1
78+#define CHKKWD 0x2
79+#define CHKNL 0x4
80+
81+/* values returned by readtoken */
82+#include "token.h"
83+
84+
85+
86+struct heredoc {
87+ struct heredoc *next; /* next here document in list */
88+ union node *here; /* redirection node */
89+ char *eofmark; /* string indicating end of input */
90+ int striptabs; /* if set, strip leading tabs */
91+};
92+
93+struct parser_temp {
94+ struct parser_temp *next;
95+ void *data;
96+};
97+
98+
99+static struct heredoc *heredoclist; /* list of here documents to read */
100+static int doprompt; /* if set, prompt the user */
101+static int needprompt; /* true if interactive and at start of line */
102+static int lasttoken; /* last token read */
103+static int tokpushback; /* last token pushed back */
104+static char *wordtext; /* text of last word returned by readtoken */
105+static int checkkwd;
106+static struct nodelist *backquotelist;
107+static union node *redirnode;
108+static struct heredoc *heredoc;
109+static int quoteflag; /* set if (part of) last token was quoted */
110+static int startlinno; /* line # where last token started */
111+static int funclinno; /* line # where the current function started */
112+static struct parser_temp *parser_temp;
113+
114+#define NOEOFMARK ((const char *)&heredoclist)
115+
116+
117+static union node *list(int);
118+static union node *andor(void);
119+static union node *pipeline(void);
120+static union node *command(void);
121+static union node *simplecmd(union node **, union node *);
122+static union node *makename(void);
123+static union node *makebinary(int type, union node *n1, union node *n2);
124+static void parsefname(void);
125+static void parseheredoc(void);
126+static int peektoken(void);
127+static int readtoken(void);
128+static int xxreadtoken(void);
129+static int readtoken1(int, const char *, const char *, int);
130+static int noexpand(char *);
131+static void consumetoken(int);
132+static void synexpect(int) __dead2;
133+static void synerror(const char *) __dead2;
134+static void setprompt(int);
135+static int pgetc_linecont(void);
136+static void getusername(char *, size_t);
137+
138+
139+static void *
140+parser_temp_alloc(size_t len)
141+{
142+ struct parser_temp *t;
143+
144+ INTOFF;
145+ t = ckmalloc(sizeof(*t));
146+ t->data = NULL;
147+ t->next = parser_temp;
148+ parser_temp = t;
149+ t->data = ckmalloc(len);
150+ INTON;
151+ return t->data;
152+}
153+
154+
155+static void *
156+parser_temp_realloc(void *ptr, size_t len)
157+{
158+ struct parser_temp *t;
159+
160+ INTOFF;
161+ t = parser_temp;
162+ if (ptr != t->data)
163+ error("bug: parser_temp_realloc misused");
164+ t->data = ckrealloc(t->data, len);
165+ INTON;
166+ return t->data;
167+}
168+
169+
170+static void
171+parser_temp_free_upto(void *ptr)
172+{
173+ struct parser_temp *t;
174+ int done = 0;
175+
176+ INTOFF;
177+ while (parser_temp != NULL && !done) {
178+ t = parser_temp;
179+ parser_temp = t->next;
180+ done = t->data == ptr;
181+ ckfree(t->data);
182+ ckfree(t);
183+ }
184+ INTON;
185+ if (!done)
186+ error("bug: parser_temp_free_upto misused");
187+}
188+
189+
190+static void
191+parser_temp_free_all(void)
192+{
193+ struct parser_temp *t;
194+
195+ INTOFF;
196+ while (parser_temp != NULL) {
197+ t = parser_temp;
198+ parser_temp = t->next;
199+ ckfree(t->data);
200+ ckfree(t);
201+ }
202+ INTON;
203+}
204+
205+
206+/*
207+ * Read and parse a command. Returns NEOF on end of file. (NULL is a
208+ * valid parse tree indicating a blank line.)
209+ */
210+
211+union node *
212+parsecmd(int interact)
213+{
214+ int t;
215+
216+ /* This assumes the parser is not re-entered,
217+ * which could happen if we add command substitution on PS1/PS2.
218+ */
219+ parser_temp_free_all();
220+ heredoclist = NULL;
221+
222+ tokpushback = 0;
223+ checkkwd = 0;
224+ doprompt = interact;
225+ if (doprompt)
226+ setprompt(1);
227+ else
228+ setprompt(0);
229+ needprompt = 0;
230+ t = readtoken();
231+ if (t == TEOF)
232+ return NEOF;
233+ if (t == TNL)
234+ return NULL;
235+ tokpushback++;
236+ return list(1);
237+}
238+
239+
240+/*
241+ * Read and parse words for wordexp.
242+ * Returns a list of NARG nodes; NULL if there are no words.
243+ */
244+union node *
245+parsewordexp(void)
246+{
247+ union node *n, *first = NULL, **pnext;
248+ int t;
249+
250+ /* This assumes the parser is not re-entered,
251+ * which could happen if we add command substitution on PS1/PS2.
252+ */
253+ parser_temp_free_all();
254+ heredoclist = NULL;
255+
256+ tokpushback = 0;
257+ checkkwd = 0;
258+ doprompt = 0;
259+ setprompt(0);
260+ needprompt = 0;
261+ pnext = &first;
262+ while ((t = readtoken()) != TEOF) {
263+ if (t != TWORD)
264+ synexpect(TWORD);
265+ n = makename();
266+ *pnext = n;
267+ pnext = &n->narg.next;
268+ }
269+ return first;
270+}
271+
272+
273+static union node *
274+list(int nlflag)
275+{
276+ union node *ntop, *n1, *n2, *n3;
277+ int tok;
278+
279+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
280+ if (!nlflag && tokendlist[peektoken()])
281+ return NULL;
282+ ntop = n1 = NULL;
283+ for (;;) {
284+ n2 = andor();
285+ tok = readtoken();
286+ if (tok == TBACKGND) {
287+ if (n2 != NULL && n2->type == NPIPE) {
288+ n2->npipe.backgnd = 1;
289+ } else if (n2 != NULL && n2->type == NREDIR) {
290+ n2->type = NBACKGND;
291+ } else {
292+ n3 = (union node *)stalloc(sizeof (struct nredir));
293+ n3->type = NBACKGND;
294+ n3->nredir.n = n2;
295+ n3->nredir.redirect = NULL;
296+ n2 = n3;
297+ }
298+ }
299+ if (ntop == NULL)
300+ ntop = n2;
301+ else if (n1 == NULL) {
302+ n1 = makebinary(NSEMI, ntop, n2);
303+ ntop = n1;
304+ }
305+ else {
306+ n3 = makebinary(NSEMI, n1->nbinary.ch2, n2);
307+ n1->nbinary.ch2 = n3;
308+ n1 = n3;
309+ }
310+ switch (tok) {
311+ case TBACKGND:
312+ case TSEMI:
313+ tok = readtoken();
314+ /* FALLTHROUGH */
315+ case TNL:
316+ if (tok == TNL) {
317+ parseheredoc();
318+ if (nlflag)
319+ return ntop;
320+ } else if (tok == TEOF && nlflag) {
321+ parseheredoc();
322+ return ntop;
323+ } else {
324+ tokpushback++;
325+ }
326+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
327+ if (!nlflag && tokendlist[peektoken()])
328+ return ntop;
329+ break;
330+ case TEOF:
331+ if (heredoclist)
332+ parseheredoc();
333+ else
334+ pungetc(); /* push back EOF on input */
335+ return ntop;
336+ default:
337+ if (nlflag)
338+ synexpect(-1);
339+ tokpushback++;
340+ return ntop;
341+ }
342+ }
343+}
344+
345+
346+
347+static union node *
348+andor(void)
349+{
350+ union node *n;
351+ int t;
352+
353+ n = pipeline();
354+ for (;;) {
355+ if ((t = readtoken()) == TAND) {
356+ t = NAND;
357+ } else if (t == TOR) {
358+ t = NOR;
359+ } else {
360+ tokpushback++;
361+ return n;
362+ }
363+ n = makebinary(t, n, pipeline());
364+ }
365+}
366+
367+
368+
369+static union node *
370+pipeline(void)
371+{
372+ union node *n1, *n2, *pipenode;
373+ struct nodelist *lp, *prev;
374+ int negate, t;
375+
376+ negate = 0;
377+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
378+ TRACE(("pipeline: entered\n"));
379+ while (readtoken() == TNOT)
380+ negate = !negate;
381+ tokpushback++;
382+ n1 = command();
383+ if (readtoken() == TPIPE) {
384+ pipenode = (union node *)stalloc(sizeof (struct npipe));
385+ pipenode->type = NPIPE;
386+ pipenode->npipe.backgnd = 0;
387+ lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
388+ pipenode->npipe.cmdlist = lp;
389+ lp->n = n1;
390+ do {
391+ prev = lp;
392+ lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
393+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
394+ t = readtoken();
395+ tokpushback++;
396+ if (t == TNOT)
397+ lp->n = pipeline();
398+ else
399+ lp->n = command();
400+ prev->next = lp;
401+ } while (readtoken() == TPIPE);
402+ lp->next = NULL;
403+ n1 = pipenode;
404+ }
405+ tokpushback++;
406+ if (negate) {
407+ n2 = (union node *)stalloc(sizeof (struct nnot));
408+ n2->type = NNOT;
409+ n2->nnot.com = n1;
410+ return n2;
411+ } else
412+ return n1;
413+}
414+
415+
416+
417+static union node *
418+command(void)
419+{
420+ union node *n1, *n2;
421+ union node *ap, **app;
422+ union node *cp, **cpp;
423+ union node *redir, **rpp;
424+ int t;
425+ int is_subshell;
426+
427+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
428+ is_subshell = 0;
429+ redir = NULL;
430+ n1 = NULL;
431+ rpp = &redir;
432+
433+ /* Check for redirection which may precede command */
434+ while (readtoken() == TREDIR) {
435+ *rpp = n2 = redirnode;
436+ rpp = &n2->nfile.next;
437+ parsefname();
438+ }
439+ tokpushback++;
440+
441+ switch (readtoken()) {
442+ case TIF:
443+ n1 = (union node *)stalloc(sizeof (struct nif));
444+ n1->type = NIF;
445+ if ((n1->nif.test = list(0)) == NULL)
446+ synexpect(-1);
447+ consumetoken(TTHEN);
448+ n1->nif.ifpart = list(0);
449+ n2 = n1;
450+ while (readtoken() == TELIF) {
451+ n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
452+ n2 = n2->nif.elsepart;
453+ n2->type = NIF;
454+ if ((n2->nif.test = list(0)) == NULL)
455+ synexpect(-1);
456+ consumetoken(TTHEN);
457+ n2->nif.ifpart = list(0);
458+ }
459+ if (lasttoken == TELSE)
460+ n2->nif.elsepart = list(0);
461+ else {
462+ n2->nif.elsepart = NULL;
463+ tokpushback++;
464+ }
465+ consumetoken(TFI);
466+ checkkwd = CHKKWD | CHKALIAS;
467+ break;
468+ case TWHILE:
469+ case TUNTIL:
470+ t = lasttoken;
471+ if ((n1 = list(0)) == NULL)
472+ synexpect(-1);
473+ consumetoken(TDO);
474+ n1 = makebinary((t == TWHILE)? NWHILE : NUNTIL, n1, list(0));
475+ consumetoken(TDONE);
476+ checkkwd = CHKKWD | CHKALIAS;
477+ break;
478+ case TFOR:
479+ if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
480+ synerror("Bad for loop variable");
481+ n1 = (union node *)stalloc(sizeof (struct nfor));
482+ n1->type = NFOR;
483+ n1->nfor.var = wordtext;
484+ checkkwd = CHKNL;
485+ if (readtoken() == TWORD && !quoteflag &&
486+ equal(wordtext, "in")) {
487+ app = ≈
488+ while (readtoken() == TWORD) {
489+ n2 = makename();
490+ *app = n2;
491+ app = &n2->narg.next;
492+ }
493+ *app = NULL;
494+ n1->nfor.args = ap;
495+ if (lasttoken == TNL)
496+ tokpushback++;
497+ else if (lasttoken != TSEMI)
498+ synexpect(-1);
499+ } else {
500+ static char argvars[5] = {
501+ CTLVAR, (char)(VSNORMAL|VSQUOTE), '@', '=', '\0'
502+ };
503+ n2 = (union node *)stalloc(sizeof (struct narg));
504+ n2->type = NARG;
505+ n2->narg.text = argvars;
506+ n2->narg.backquote = NULL;
507+ n2->narg.next = NULL;
508+ n1->nfor.args = n2;
509+ /*
510+ * Newline or semicolon here is optional (but note
511+ * that the original Bourne shell only allowed NL).
512+ */
513+ if (lasttoken != TSEMI)
514+ tokpushback++;
515+ }
516+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
517+ if ((t = readtoken()) == TDO)
518+ t = TDONE;
519+ else if (t == TBEGIN)
520+ t = TEND;
521+ else
522+ synexpect(-1);
523+ n1->nfor.body = list(0);
524+ consumetoken(t);
525+ checkkwd = CHKKWD | CHKALIAS;
526+ break;
527+ case TCASE:
528+ n1 = (union node *)stalloc(sizeof (struct ncase));
529+ n1->type = NCASE;
530+ consumetoken(TWORD);
531+ n1->ncase.expr = makename();
532+ checkkwd = CHKNL;
533+ if (readtoken() != TWORD || ! equal(wordtext, "in"))
534+ synerror("expecting \"in\"");
535+ cpp = &n1->ncase.cases;
536+ checkkwd = CHKNL | CHKKWD, readtoken();
537+ while (lasttoken != TESAC) {
538+ *cpp = cp = (union node *)stalloc(sizeof (struct nclist));
539+ cp->type = NCLIST;
540+ app = &cp->nclist.pattern;
541+ if (lasttoken == TLP)
542+ readtoken();
543+ for (;;) {
544+ *app = ap = makename();
545+ checkkwd = CHKNL | CHKKWD;
546+ if (readtoken() != TPIPE)
547+ break;
548+ app = &ap->narg.next;
549+ readtoken();
550+ }
551+ ap->narg.next = NULL;
552+ if (lasttoken != TRP)
553+ synexpect(TRP);
554+ cp->nclist.body = list(0);
555+
556+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
557+ if ((t = readtoken()) != TESAC) {
558+ if (t == TENDCASE)
559+ ;
560+ else if (t == TFALLTHRU)
561+ cp->type = NCLISTFALLTHRU;
562+ else
563+ synexpect(TENDCASE);
564+ checkkwd = CHKNL | CHKKWD, readtoken();
565+ }
566+ cpp = &cp->nclist.next;
567+ }
568+ *cpp = NULL;
569+ checkkwd = CHKKWD | CHKALIAS;
570+ break;
571+ case TLP:
572+ n1 = (union node *)stalloc(sizeof (struct nredir));
573+ n1->type = NSUBSHELL;
574+ n1->nredir.n = list(0);
575+ n1->nredir.redirect = NULL;
576+ consumetoken(TRP);
577+ checkkwd = CHKKWD | CHKALIAS;
578+ is_subshell = 1;
579+ break;
580+ case TBEGIN:
581+ n1 = list(0);
582+ consumetoken(TEND);
583+ checkkwd = CHKKWD | CHKALIAS;
584+ break;
585+ /* A simple command must have at least one redirection or word. */
586+ case TBACKGND:
587+ case TSEMI:
588+ case TAND:
589+ case TOR:
590+ case TPIPE:
591+ case TENDCASE:
592+ case TFALLTHRU:
593+ case TEOF:
594+ case TNL:
595+ case TRP:
596+ if (!redir)
597+ synexpect(-1);
598+ /* fallthrough */
599+ case TWORD:
600+ tokpushback++;
601+ n1 = simplecmd(rpp, redir);
602+ return n1;
603+ default:
604+ synexpect(-1);
605+ }
606+
607+ /* Now check for redirection which may follow command */
608+ while (readtoken() == TREDIR) {
609+ *rpp = n2 = redirnode;
610+ rpp = &n2->nfile.next;
611+ parsefname();
612+ }
613+ tokpushback++;
614+ *rpp = NULL;
615+ if (redir) {
616+ if (!is_subshell) {
617+ n2 = (union node *)stalloc(sizeof (struct nredir));
618+ n2->type = NREDIR;
619+ n2->nredir.n = n1;
620+ n1 = n2;
621+ }
622+ n1->nredir.redirect = redir;
623+ }
624+
625+ return n1;
626+}
627+
628+
629+static union node *
630+simplecmd(union node **rpp, union node *redir)
631+{
632+ union node *args, **app;
633+ union node **orig_rpp = rpp;
634+ union node *n = NULL;
635+ int special;
636+ int savecheckkwd;
637+
638+ /* If we don't have any redirections already, then we must reset */
639+ /* rpp to be the address of the local redir variable. */
640+ if (redir == NULL)
641+ rpp = &redir;
642+
643+ args = NULL;
644+ app = &args;
645+ /*
646+ * We save the incoming value, because we need this for shell
647+ * functions. There can not be a redirect or an argument between
648+ * the function name and the open parenthesis.
649+ */
650+ orig_rpp = rpp;
651+
652+ savecheckkwd = CHKALIAS;
653+
654+ for (;;) {
655+ checkkwd = savecheckkwd;
656+ if (readtoken() == TWORD) {
657+ n = makename();
658+ *app = n;
659+ app = &n->narg.next;
660+ if (savecheckkwd != 0 && !isassignment(wordtext))
661+ savecheckkwd = 0;
662+ } else if (lasttoken == TREDIR) {
663+ *rpp = n = redirnode;
664+ rpp = &n->nfile.next;
665+ parsefname(); /* read name of redirection file */
666+ } else if (lasttoken == TLP && app == &args->narg.next
667+ && rpp == orig_rpp) {
668+ /* We have a function */
669+ consumetoken(TRP);
670+ funclinno = plinno;
671+ /*
672+ * - Require plain text.
673+ * - Functions with '/' cannot be called.
674+ * - Reject name=().
675+ * - Reject ksh extended glob patterns.
676+ */
677+ if (!noexpand(n->narg.text) || quoteflag ||
678+ strchr(n->narg.text, '/') ||
679+ strchr("!%*+-=?@}~",
680+ n->narg.text[strlen(n->narg.text) - 1]))
681+ synerror("Bad function name");
682+ rmescapes(n->narg.text);
683+ if (find_builtin(n->narg.text, &special) >= 0 &&
684+ special)
685+ synerror("Cannot override a special builtin with a function");
686+ n->type = NDEFUN;
687+ n->narg.next = command();
688+ funclinno = 0;
689+ return n;
690+ } else {
691+ tokpushback++;
692+ break;
693+ }
694+ }
695+ *app = NULL;
696+ *rpp = NULL;
697+ n = (union node *)stalloc(sizeof (struct ncmd));
698+ n->type = NCMD;
699+ n->ncmd.args = args;
700+ n->ncmd.redirect = redir;
701+ return n;
702+}
703+
704+static union node *
705+makename(void)
706+{
707+ union node *n;
708+
709+ n = (union node *)stalloc(sizeof (struct narg));
710+ n->type = NARG;
711+ n->narg.next = NULL;
712+ n->narg.text = wordtext;
713+ n->narg.backquote = backquotelist;
714+ return n;
715+}
716+
717+static union node *
718+makebinary(int type, union node *n1, union node *n2)
719+{
720+ union node *n;
721+
722+ n = (union node *)stalloc(sizeof (struct nbinary));
723+ n->type = type;
724+ n->nbinary.ch1 = n1;
725+ n->nbinary.ch2 = n2;
726+ return (n);
727+}
728+
729+void
730+forcealias(void)
731+{
732+ checkkwd |= CHKALIAS;
733+}
734+
735+void
736+fixredir(union node *n, const char *text, int err)
737+{
738+ TRACE(("Fix redir %s %d\n", text, err));
739+ if (!err)
740+ n->ndup.vname = NULL;
741+
742+ if (is_digit(text[0]) && text[1] == '\0')
743+ n->ndup.dupfd = digit_val(text[0]);
744+ else if (text[0] == '-' && text[1] == '\0')
745+ n->ndup.dupfd = -1;
746+ else {
747+
748+ if (err)
749+ synerror("Bad fd number");
750+ else
751+ n->ndup.vname = makename();
752+ }
753+}
754+
755+
756+static void
757+parsefname(void)
758+{
759+ union node *n = redirnode;
760+
761+ consumetoken(TWORD);
762+ if (n->type == NHERE) {
763+ struct heredoc *here = heredoc;
764+ struct heredoc *p;
765+
766+ if (quoteflag == 0)
767+ n->type = NXHERE;
768+ TRACE(("Here document %d\n", n->type));
769+ if (here->striptabs) {
770+ while (*wordtext == '\t')
771+ wordtext++;
772+ }
773+ if (! noexpand(wordtext))
774+ synerror("Illegal eof marker for << redirection");
775+ rmescapes(wordtext);
776+ here->eofmark = wordtext;
777+ here->next = NULL;
778+ if (heredoclist == NULL)
779+ heredoclist = here;
780+ else {
781+ for (p = heredoclist ; p->next ; p = p->next);
782+ p->next = here;
783+ }
784+ } else if (n->type == NTOFD || n->type == NFROMFD) {
785+ fixredir(n, wordtext, 0);
786+ } else {
787+ n->nfile.fname = makename();
788+ }
789+}
790+
791+
792+/*
793+ * Input any here documents.
794+ */
795+
796+static void
797+parseheredoc(void)
798+{
799+ struct heredoc *here;
800+ union node *n;
801+
802+ while (heredoclist) {
803+ here = heredoclist;
804+ heredoclist = here->next;
805+ if (needprompt) {
806+ setprompt(2);
807+ needprompt = 0;
808+ }
809+ readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
810+ here->eofmark, here->striptabs);
811+ n = makename();
812+ here->here->nhere.doc = n;
813+ }
814+}
815+
816+static int
817+peektoken(void)
818+{
819+ int t;
820+
821+ t = readtoken();
822+ tokpushback++;
823+ return (t);
824+}
825+
826+static int
827+readtoken(void)
828+{
829+ int t;
830+ struct alias *ap;
831+#ifdef DEBUG
832+ int alreadyseen = tokpushback;
833+#endif
834+
835+ top:
836+ t = xxreadtoken();
837+
838+ /*
839+ * eat newlines
840+ */
841+ if (checkkwd & CHKNL) {
842+ while (t == TNL) {
843+ parseheredoc();
844+ t = xxreadtoken();
845+ }
846+ }
847+
848+ /*
849+ * check for keywords and aliases
850+ */
851+ if (t == TWORD && !quoteflag)
852+ {
853+ const char * const *pp;
854+
855+ if (checkkwd & CHKKWD)
856+ for (pp = parsekwd; *pp; pp++) {
857+ if (**pp == *wordtext && equal(*pp, wordtext))
858+ {
859+ lasttoken = t = pp - parsekwd + KWDOFFSET;
860+ TRACE(("keyword %s recognized\n", tokname[t]));
861+ goto out;
862+ }
863+ }
864+ if (checkkwd & CHKALIAS &&
865+ (ap = lookupalias(wordtext, 1)) != NULL) {
866+ pushstring(ap->val, strlen(ap->val), ap);
867+ goto top;
868+ }
869+ }
870+out:
871+ if (t != TNOT)
872+ checkkwd = 0;
873+
874+#ifdef DEBUG
875+ if (!alreadyseen)
876+ TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
877+ else
878+ TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
879+#endif
880+ return (t);
881+}
882+
883+
884+/*
885+ * Read the next input token.
886+ * If the token is a word, we set backquotelist to the list of cmds in
887+ * backquotes. We set quoteflag to true if any part of the word was
888+ * quoted.
889+ * If the token is TREDIR, then we set redirnode to a structure containing
890+ * the redirection.
891+ * In all cases, the variable startlinno is set to the number of the line
892+ * on which the token starts.
893+ *
894+ * [Change comment: here documents and internal procedures]
895+ * [Readtoken shouldn't have any arguments. Perhaps we should make the
896+ * word parsing code into a separate routine. In this case, readtoken
897+ * doesn't need to have any internal procedures, but parseword does.
898+ * We could also make parseoperator in essence the main routine, and
899+ * have parseword (readtoken1?) handle both words and redirection.]
900+ */
901+
902+#define RETURN(token) return lasttoken = token
903+
904+static int
905+xxreadtoken(void)
906+{
907+ int c;
908+
909+ if (tokpushback) {
910+ tokpushback = 0;
911+ return lasttoken;
912+ }
913+ if (needprompt) {
914+ setprompt(2);
915+ needprompt = 0;
916+ }
917+ startlinno = plinno;
918+ for (;;) { /* until token or start of word found */
919+ c = pgetc_macro();
920+ switch (c) {
921+ case ' ': case '\t':
922+ continue;
923+ case '#':
924+ while ((c = pgetc()) != '\n' && c != PEOF);
925+ pungetc();
926+ continue;
927+ case '\\':
928+ if (pgetc() == '\n') {
929+ startlinno = ++plinno;
930+ if (doprompt)
931+ setprompt(2);
932+ else
933+ setprompt(0);
934+ continue;
935+ }
936+ pungetc();
937+ /* FALLTHROUGH */
938+ default:
939+ return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
940+ case '\n':
941+ plinno++;
942+ needprompt = doprompt;
943+ RETURN(TNL);
944+ case PEOF:
945+ RETURN(TEOF);
946+ case '&':
947+ if (pgetc_linecont() == '&')
948+ RETURN(TAND);
949+ pungetc();
950+ RETURN(TBACKGND);
951+ case '|':
952+ if (pgetc_linecont() == '|')
953+ RETURN(TOR);
954+ pungetc();
955+ RETURN(TPIPE);
956+ case ';':
957+ c = pgetc_linecont();
958+ if (c == ';')
959+ RETURN(TENDCASE);
960+ else if (c == '&')
961+ RETURN(TFALLTHRU);
962+ pungetc();
963+ RETURN(TSEMI);
964+ case '(':
965+ RETURN(TLP);
966+ case ')':
967+ RETURN(TRP);
968+ }
969+ }
970+#undef RETURN
971+}
972+
973+
974+#define MAXNEST_static 8
975+struct tokenstate
976+{
977+ const char *syntax; /* *SYNTAX */
978+ int parenlevel; /* levels of parentheses in arithmetic */
979+ enum tokenstate_category
980+ {
981+ TSTATE_TOP,
982+ TSTATE_VAR_OLD, /* ${var+-=?}, inherits dquotes */
983+ TSTATE_VAR_NEW, /* other ${var...}, own dquote state */
984+ TSTATE_ARITH
985+ } category;
986+};
987+
988+
989+/*
990+ * Check to see whether we are at the end of the here document. When this
991+ * is called, c is set to the first character of the next input line. If
992+ * we are at the end of the here document, this routine sets the c to PEOF.
993+ * The new value of c is returned.
994+ */
995+
996+static int
997+checkend(int c, const char *eofmark, int striptabs)
998+{
999+ if (striptabs) {
1000+ while (c == '\t')
1001+ c = pgetc();
1002+ }
1003+ if (c == *eofmark) {
1004+ int c2;
1005+ const char *q;
1006+
1007+ for (q = eofmark + 1; c2 = pgetc(), *q != '\0' && c2 == *q; q++)
1008+ ;
1009+ if ((c2 == PEOF || c2 == '\n') && *q == '\0') {
1010+ c = PEOF;
1011+ if (c2 == '\n') {
1012+ plinno++;
1013+ needprompt = doprompt;
1014+ }
1015+ } else {
1016+ pungetc();
1017+ pushstring(eofmark + 1, q - (eofmark + 1), NULL);
1018+ }
1019+ } else if (c == '\n' && *eofmark == '\0') {
1020+ c = PEOF;
1021+ plinno++;
1022+ needprompt = doprompt;
1023+ }
1024+ return (c);
1025+}
1026+
1027+
1028+/*
1029+ * Parse a redirection operator. The variable "out" points to a string
1030+ * specifying the fd to be redirected. The variable "c" contains the
1031+ * first character of the redirection operator.
1032+ */
1033+
1034+static void
1035+parseredir(char *out, int c)
1036+{
1037+ char fd = *out;
1038+ union node *np;
1039+
1040+ np = (union node *)stalloc(sizeof (struct nfile));
1041+ if (c == '>') {
1042+ np->nfile.fd = 1;
1043+ c = pgetc_linecont();
1044+ if (c == '>')
1045+ np->type = NAPPEND;
1046+ else if (c == '&')
1047+ np->type = NTOFD;
1048+ else if (c == '|')
1049+ np->type = NCLOBBER;
1050+ else {
1051+ np->type = NTO;
1052+ pungetc();
1053+ }
1054+ } else { /* c == '<' */
1055+ np->nfile.fd = 0;
1056+ c = pgetc_linecont();
1057+ if (c == '<') {
1058+ if (sizeof (struct nfile) != sizeof (struct nhere)) {
1059+ np = (union node *)stalloc(sizeof (struct nhere));
1060+ np->nfile.fd = 0;
1061+ }
1062+ np->type = NHERE;
1063+ heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
1064+ heredoc->here = np;
1065+ if ((c = pgetc_linecont()) == '-') {
1066+ heredoc->striptabs = 1;
1067+ } else {
1068+ heredoc->striptabs = 0;
1069+ pungetc();
1070+ }
1071+ } else if (c == '&')
1072+ np->type = NFROMFD;
1073+ else if (c == '>')
1074+ np->type = NFROMTO;
1075+ else {
1076+ np->type = NFROM;
1077+ pungetc();
1078+ }
1079+ }
1080+ if (fd != '\0')
1081+ np->nfile.fd = digit_val(fd);
1082+ redirnode = np;
1083+}
1084+
1085+/*
1086+ * Called to parse command substitutions.
1087+ */
1088+
1089+static char *
1090+parsebackq(char *out, struct nodelist **pbqlist,
1091+ int oldstyle, int dblquote, int quoted)
1092+{
1093+ struct nodelist **nlpp;
1094+ union node *n;
1095+ char *volatile str;
1096+ struct jmploc jmploc;
1097+ struct jmploc *const savehandler = handler;
1098+ size_t savelen;
1099+ int saveprompt;
1100+ const int bq_startlinno = plinno;
1101+ char *volatile ostr = NULL;
1102+ struct parsefile *const savetopfile = getcurrentfile();
1103+ struct heredoc *const saveheredoclist = heredoclist;
1104+ struct heredoc *here;
1105+
1106+ str = NULL;
1107+ if (setjmp(jmploc.loc)) {
1108+ popfilesupto(savetopfile);
1109+ if (str)
1110+ ckfree(str);
1111+ if (ostr)
1112+ ckfree(ostr);
1113+ heredoclist = saveheredoclist;
1114+ handler = savehandler;
1115+ if (exception == EXERROR) {
1116+ startlinno = bq_startlinno;
1117+ synerror("Error in command substitution");
1118+ }
1119+ longjmp(handler->loc, 1);
1120+ }
1121+ INTOFF;
1122+ savelen = out - stackblock();
1123+ if (savelen > 0) {
1124+ str = ckmalloc(savelen);
1125+ memcpy(str, stackblock(), savelen);
1126+ }
1127+ handler = &jmploc;
1128+ heredoclist = NULL;
1129+ INTON;
1130+ if (oldstyle) {
1131+ /* We must read until the closing backquote, giving special
1132+ treatment to some slashes, and then push the string and
1133+ reread it as input, interpreting it normally. */
1134+ char *oout;
1135+ int c;
1136+ int olen;
1137+
1138+
1139+ STARTSTACKSTR(oout);
1140+ for (;;) {
1141+ if (needprompt) {
1142+ setprompt(2);
1143+ needprompt = 0;
1144+ }
1145+ CHECKSTRSPACE(2, oout);
1146+ c = pgetc_linecont();
1147+ if (c == '`')
1148+ break;
1149+ switch (c) {
1150+ case '\\':
1151+ c = pgetc();
1152+ if (c != '\\' && c != '`' && c != '$'
1153+ && (!dblquote || c != '"'))
1154+ USTPUTC('\\', oout);
1155+ break;
1156+
1157+ case '\n':
1158+ plinno++;
1159+ needprompt = doprompt;
1160+ break;
1161+
1162+ case PEOF:
1163+ startlinno = plinno;
1164+ synerror("EOF in backquote substitution");
1165+ break;
1166+
1167+ default:
1168+ break;
1169+ }
1170+ USTPUTC(c, oout);
1171+ }
1172+ USTPUTC('\0', oout);
1173+ olen = oout - stackblock();
1174+ INTOFF;
1175+ ostr = ckmalloc(olen);
1176+ memcpy(ostr, stackblock(), olen);
1177+ setinputstring(ostr);
1178+ INTON;
1179+ }
1180+ nlpp = pbqlist;
1181+ while (*nlpp)
1182+ nlpp = &(*nlpp)->next;
1183+ *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
1184+ (*nlpp)->next = NULL;
1185+
1186+ if (oldstyle) {
1187+ saveprompt = doprompt;
1188+ doprompt = 0;
1189+ }
1190+
1191+ n = list(0);
1192+
1193+ if (oldstyle) {
1194+ if (peektoken() != TEOF)
1195+ synexpect(-1);
1196+ doprompt = saveprompt;
1197+ } else
1198+ consumetoken(TRP);
1199+
1200+ (*nlpp)->n = n;
1201+ if (oldstyle) {
1202+ /*
1203+ * Start reading from old file again, ignoring any pushed back
1204+ * tokens left from the backquote parsing
1205+ */
1206+ popfile();
1207+ tokpushback = 0;
1208+ }
1209+ STARTSTACKSTR(out);
1210+ CHECKSTRSPACE(savelen + 1, out);
1211+ INTOFF;
1212+ if (str) {
1213+ memcpy(out, str, savelen);
1214+ STADJUST(savelen, out);
1215+ ckfree(str);
1216+ str = NULL;
1217+ }
1218+ if (ostr) {
1219+ ckfree(ostr);
1220+ ostr = NULL;
1221+ }
1222+ here = saveheredoclist;
1223+ if (here != NULL) {
1224+ while (here->next != NULL)
1225+ here = here->next;
1226+ here->next = heredoclist;
1227+ heredoclist = saveheredoclist;
1228+ }
1229+ handler = savehandler;
1230+ INTON;
1231+ if (quoted)
1232+ USTPUTC(CTLBACKQ | CTLQUOTE, out);
1233+ else
1234+ USTPUTC(CTLBACKQ, out);
1235+ return out;
1236+}
1237+
1238+
1239+/*
1240+ * Called to parse a backslash escape sequence inside $'...'.
1241+ * The backslash has already been read.
1242+ */
1243+static char *
1244+readcstyleesc(char *out)
1245+{
1246+ int c, vc, i, n;
1247+ unsigned int v;
1248+
1249+ c = pgetc();
1250+ switch (c) {
1251+ case '\0':
1252+ synerror("Unterminated quoted string");
1253+ case '\n':
1254+ plinno++;
1255+ if (doprompt)
1256+ setprompt(2);
1257+ else
1258+ setprompt(0);
1259+ return out;
1260+ case '\\':
1261+ case '\'':
1262+ case '"':
1263+ v = c;
1264+ break;
1265+ case 'a': v = '\a'; break;
1266+ case 'b': v = '\b'; break;
1267+ case 'e': v = '\033'; break;
1268+ case 'f': v = '\f'; break;
1269+ case 'n': v = '\n'; break;
1270+ case 'r': v = '\r'; break;
1271+ case 't': v = '\t'; break;
1272+ case 'v': v = '\v'; break;
1273+ case 'x':
1274+ v = 0;
1275+ for (;;) {
1276+ c = pgetc();
1277+ if (c >= '0' && c <= '9')
1278+ v = (v << 4) + c - '0';
1279+ else if (c >= 'A' && c <= 'F')
1280+ v = (v << 4) + c - 'A' + 10;
1281+ else if (c >= 'a' && c <= 'f')
1282+ v = (v << 4) + c - 'a' + 10;
1283+ else
1284+ break;
1285+ }
1286+ pungetc();
1287+ break;
1288+ case '0': case '1': case '2': case '3':
1289+ case '4': case '5': case '6': case '7':
1290+ v = c - '0';
1291+ c = pgetc();
1292+ if (c >= '0' && c <= '7') {
1293+ v <<= 3;
1294+ v += c - '0';
1295+ c = pgetc();
1296+ if (c >= '0' && c <= '7') {
1297+ v <<= 3;
1298+ v += c - '0';
1299+ } else
1300+ pungetc();
1301+ } else
1302+ pungetc();
1303+ break;
1304+ case 'c':
1305+ c = pgetc();
1306+ if (c < 0x3f || c > 0x7a || c == 0x60)
1307+ synerror("Bad escape sequence");
1308+ if (c == '\\' && pgetc() != '\\')
1309+ synerror("Bad escape sequence");
1310+ if (c == '?')
1311+ v = 127;
1312+ else
1313+ v = c & 0x1f;
1314+ break;
1315+ case 'u':
1316+ case 'U':
1317+ n = c == 'U' ? 8 : 4;
1318+ v = 0;
1319+ for (i = 0; i < n; i++) {
1320+ c = pgetc();
1321+ if (c >= '0' && c <= '9')
1322+ v = (v << 4) + c - '0';
1323+ else if (c >= 'A' && c <= 'F')
1324+ v = (v << 4) + c - 'A' + 10;
1325+ else if (c >= 'a' && c <= 'f')
1326+ v = (v << 4) + c - 'a' + 10;
1327+ else
1328+ synerror("Bad escape sequence");
1329+ }
1330+ if (v == 0 || (v >= 0xd800 && v <= 0xdfff))
1331+ synerror("Bad escape sequence");
1332+ /* We really need iconv here. */
1333+ if (initial_localeisutf8 && v > 127) {
1334+ CHECKSTRSPACE(4, out);
1335+ /*
1336+ * We cannot use wctomb() as the locale may have
1337+ * changed.
1338+ */
1339+ if (v <= 0x7ff) {
1340+ USTPUTC(0xc0 | v >> 6, out);
1341+ USTPUTC(0x80 | (v & 0x3f), out);
1342+ return out;
1343+ } else if (v <= 0xffff) {
1344+ USTPUTC(0xe0 | v >> 12, out);
1345+ USTPUTC(0x80 | ((v >> 6) & 0x3f), out);
1346+ USTPUTC(0x80 | (v & 0x3f), out);
1347+ return out;
1348+ } else if (v <= 0x10ffff) {
1349+ USTPUTC(0xf0 | v >> 18, out);
1350+ USTPUTC(0x80 | ((v >> 12) & 0x3f), out);
1351+ USTPUTC(0x80 | ((v >> 6) & 0x3f), out);
1352+ USTPUTC(0x80 | (v & 0x3f), out);
1353+ return out;
1354+ }
1355+ }
1356+ if (v > 127)
1357+ v = '?';
1358+ break;
1359+ default:
1360+ synerror("Bad escape sequence");
1361+ }
1362+ vc = (char)v;
1363+ /*
1364+ * We can't handle NUL bytes.
1365+ * POSIX says we should skip till the closing quote.
1366+ */
1367+ if (vc == '\0') {
1368+ while ((c = pgetc()) != '\'') {
1369+ if (c == '\\')
1370+ c = pgetc();
1371+ if (c == PEOF)
1372+ synerror("Unterminated quoted string");
1373+ if (c == '\n') {
1374+ plinno++;
1375+ if (doprompt)
1376+ setprompt(2);
1377+ else
1378+ setprompt(0);
1379+ }
1380+ }
1381+ pungetc();
1382+ return out;
1383+ }
1384+ if (SQSYNTAX[vc] == CCTL)
1385+ USTPUTC(CTLESC, out);
1386+ USTPUTC(vc, out);
1387+ return out;
1388+}
1389+
1390+
1391+/*
1392+ * If eofmark is NULL, read a word or a redirection symbol. If eofmark
1393+ * is not NULL, read a here document. In the latter case, eofmark is the
1394+ * word which marks the end of the document and striptabs is true if
1395+ * leading tabs should be stripped from the document. The argument firstc
1396+ * is the first character of the input token or document.
1397+ *
1398+ * Because C does not have internal subroutines, I have simulated them
1399+ * using goto's to implement the subroutine linkage. The following macros
1400+ * will run code that appears at the end of readtoken1.
1401+ */
1402+
1403+#define PARSESUB() {goto parsesub; parsesub_return:;}
1404+#define PARSEARITH() {goto parsearith; parsearith_return:;}
1405+
1406+static int
1407+readtoken1(int firstc, char const *initialsyntax, const char *eofmark,
1408+ int striptabs)
1409+{
1410+ int c = firstc;
1411+ char *out;
1412+ int len;
1413+ struct nodelist *bqlist;
1414+ int quotef;
1415+ int newvarnest;
1416+ int level;
1417+ int synentry;
1418+ struct tokenstate state_static[MAXNEST_static];
1419+ int maxnest = MAXNEST_static;
1420+ struct tokenstate *state = state_static;
1421+ int sqiscstyle = 0;
1422+
1423+ startlinno = plinno;
1424+ quotef = 0;
1425+ bqlist = NULL;
1426+ newvarnest = 0;
1427+ level = 0;
1428+ state[level].syntax = initialsyntax;
1429+ state[level].parenlevel = 0;
1430+ state[level].category = TSTATE_TOP;
1431+
1432+ STARTSTACKSTR(out);
1433+ loop: { /* for each line, until end of word */
1434+ if (eofmark && eofmark != NOEOFMARK)
1435+ /* set c to PEOF if at end of here document */
1436+ c = checkend(c, eofmark, striptabs);
1437+ for (;;) { /* until end of line or end of word */
1438+ CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
1439+
1440+ synentry = state[level].syntax[c];
1441+
1442+ switch(synentry) {
1443+ case CNL: /* '\n' */
1444+ if (level == 0)
1445+ goto endword; /* exit outer loop */
1446+ /* FALLTHROUGH */
1447+ case CQNL:
1448+ USTPUTC(c, out);
1449+ plinno++;
1450+ if (doprompt)
1451+ setprompt(2);
1452+ else
1453+ setprompt(0);
1454+ c = pgetc();
1455+ goto loop; /* continue outer loop */
1456+ case CSBACK:
1457+ if (sqiscstyle) {
1458+ out = readcstyleesc(out);
1459+ break;
1460+ }
1461+ /* FALLTHROUGH */
1462+ case CWORD:
1463+ USTPUTC(c, out);
1464+ break;
1465+ case CCTL:
1466+ if (eofmark == NULL || initialsyntax != SQSYNTAX)
1467+ USTPUTC(CTLESC, out);
1468+ USTPUTC(c, out);
1469+ break;
1470+ case CBACK: /* backslash */
1471+ c = pgetc();
1472+ if (c == PEOF) {
1473+ USTPUTC('\\', out);
1474+ pungetc();
1475+ } else if (c == '\n') {
1476+ plinno++;
1477+ if (doprompt)
1478+ setprompt(2);
1479+ else
1480+ setprompt(0);
1481+ } else {
1482+ if (state[level].syntax == DQSYNTAX &&
1483+ c != '\\' && c != '`' && c != '$' &&
1484+ (c != '"' || (eofmark != NULL &&
1485+ newvarnest == 0)) &&
1486+ (c != '}' || state[level].category != TSTATE_VAR_OLD))
1487+ USTPUTC('\\', out);
1488+ if ((eofmark == NULL ||
1489+ newvarnest > 0) &&
1490+ state[level].syntax == BASESYNTAX)
1491+ USTPUTC(CTLQUOTEMARK, out);
1492+ if (SQSYNTAX[c] == CCTL)
1493+ USTPUTC(CTLESC, out);
1494+ USTPUTC(c, out);
1495+ if ((eofmark == NULL ||
1496+ newvarnest > 0) &&
1497+ state[level].syntax == BASESYNTAX &&
1498+ state[level].category == TSTATE_VAR_OLD)
1499+ USTPUTC(CTLQUOTEEND, out);
1500+ quotef++;
1501+ }
1502+ break;
1503+ case CSQUOTE:
1504+ USTPUTC(CTLQUOTEMARK, out);
1505+ state[level].syntax = SQSYNTAX;
1506+ sqiscstyle = 0;
1507+ break;
1508+ case CDQUOTE:
1509+ USTPUTC(CTLQUOTEMARK, out);
1510+ state[level].syntax = DQSYNTAX;
1511+ break;
1512+ case CENDQUOTE:
1513+ if (eofmark != NULL && newvarnest == 0)
1514+ USTPUTC(c, out);
1515+ else {
1516+ if (state[level].category == TSTATE_VAR_OLD)
1517+ USTPUTC(CTLQUOTEEND, out);
1518+ state[level].syntax = BASESYNTAX;
1519+ quotef++;
1520+ }
1521+ break;
1522+ case CVAR: /* '$' */
1523+ PARSESUB(); /* parse substitution */
1524+ break;
1525+ case CENDVAR: /* '}' */
1526+ if (level > 0 &&
1527+ ((state[level].category == TSTATE_VAR_OLD &&
1528+ state[level].syntax ==
1529+ state[level - 1].syntax) ||
1530+ (state[level].category == TSTATE_VAR_NEW &&
1531+ state[level].syntax == BASESYNTAX))) {
1532+ if (state[level].category == TSTATE_VAR_NEW)
1533+ newvarnest--;
1534+ level--;
1535+ USTPUTC(CTLENDVAR, out);
1536+ } else {
1537+ USTPUTC(c, out);
1538+ }
1539+ break;
1540+ case CLP: /* '(' in arithmetic */
1541+ state[level].parenlevel++;
1542+ USTPUTC(c, out);
1543+ break;
1544+ case CRP: /* ')' in arithmetic */
1545+ if (state[level].parenlevel > 0) {
1546+ USTPUTC(c, out);
1547+ --state[level].parenlevel;
1548+ } else {
1549+ if (pgetc_linecont() == ')') {
1550+ if (level > 0 &&
1551+ state[level].category == TSTATE_ARITH) {
1552+ level--;
1553+ USTPUTC(CTLENDARI, out);
1554+ } else
1555+ USTPUTC(')', out);
1556+ } else {
1557+ /*
1558+ * unbalanced parens
1559+ * (don't 2nd guess - no error)
1560+ */
1561+ pungetc();
1562+ USTPUTC(')', out);
1563+ }
1564+ }
1565+ break;
1566+ case CBQUOTE: /* '`' */
1567+ out = parsebackq(out, &bqlist, 1,
1568+ state[level].syntax == DQSYNTAX &&
1569+ (eofmark == NULL || newvarnest > 0),
1570+ state[level].syntax == DQSYNTAX || state[level].syntax == ARISYNTAX);
1571+ break;
1572+ case CEOF:
1573+ goto endword; /* exit outer loop */
1574+ case CIGN:
1575+ break;
1576+ default:
1577+ if (level == 0)
1578+ goto endword; /* exit outer loop */
1579+ USTPUTC(c, out);
1580+ }
1581+ c = pgetc_macro();
1582+ }
1583+ }
1584+endword:
1585+ if (state[level].syntax == ARISYNTAX)
1586+ synerror("Missing '))'");
1587+ if (state[level].syntax != BASESYNTAX && eofmark == NULL)
1588+ synerror("Unterminated quoted string");
1589+ if (state[level].category == TSTATE_VAR_OLD ||
1590+ state[level].category == TSTATE_VAR_NEW) {
1591+ startlinno = plinno;
1592+ synerror("Missing '}'");
1593+ }
1594+ if (state != state_static)
1595+ parser_temp_free_upto(state);
1596+ USTPUTC('\0', out);
1597+ len = out - stackblock();
1598+ out = stackblock();
1599+ if (eofmark == NULL) {
1600+ if ((c == '>' || c == '<')
1601+ && quotef == 0
1602+ && len <= 2
1603+ && (*out == '\0' || is_digit(*out))) {
1604+ parseredir(out, c);
1605+ return lasttoken = TREDIR;
1606+ } else {
1607+ pungetc();
1608+ }
1609+ }
1610+ quoteflag = quotef;
1611+ backquotelist = bqlist;
1612+ grabstackblock(len);
1613+ wordtext = out;
1614+ return lasttoken = TWORD;
1615+/* end of readtoken routine */
1616+
1617+
1618+/*
1619+ * Parse a substitution. At this point, we have read the dollar sign
1620+ * and nothing else.
1621+ */
1622+
1623+parsesub: {
1624+ int subtype;
1625+ int typeloc;
1626+ int flags;
1627+ char *p;
1628+ static const char types[] = "}-+?=";
1629+ int linno;
1630+ int length;
1631+ int c1;
1632+
1633+ c = pgetc_linecont();
1634+ if (c == '(') { /* $(command) or $((arith)) */
1635+ if (pgetc_linecont() == '(') {
1636+ PARSEARITH();
1637+ } else {
1638+ pungetc();
1639+ out = parsebackq(out, &bqlist, 0,
1640+ state[level].syntax == DQSYNTAX &&
1641+ (eofmark == NULL || newvarnest > 0),
1642+ state[level].syntax == DQSYNTAX ||
1643+ state[level].syntax == ARISYNTAX);
1644+ }
1645+ } else if (c == '{' || is_name(c) || is_special(c)) {
1646+ USTPUTC(CTLVAR, out);
1647+ typeloc = out - stackblock();
1648+ USTPUTC(VSNORMAL, out);
1649+ subtype = VSNORMAL;
1650+ flags = 0;
1651+ if (c == '{') {
1652+ c = pgetc_linecont();
1653+ subtype = 0;
1654+ }
1655+varname:
1656+ if (!is_eof(c) && is_name(c)) {
1657+ length = 0;
1658+ do {
1659+ STPUTC(c, out);
1660+ c = pgetc_linecont();
1661+ length++;
1662+ } while (!is_eof(c) && is_in_name(c));
1663+ if (length == 6 &&
1664+ strncmp(out - length, "LINENO", length) == 0) {
1665+ /* Replace the variable name with the
1666+ * current line number. */
1667+ STADJUST(-6, out);
1668+ CHECKSTRSPACE(11, out);
1669+ linno = plinno;
1670+ if (funclinno != 0)
1671+ linno -= funclinno - 1;
1672+ length = snprintf(out, 11, "%d", linno);
1673+ if (length > 10)
1674+ length = 10;
1675+ out += length;
1676+ flags |= VSLINENO;
1677+ }
1678+ } else if (is_digit(c)) {
1679+ if (subtype != VSNORMAL) {
1680+ do {
1681+ STPUTC(c, out);
1682+ c = pgetc_linecont();
1683+ } while (is_digit(c));
1684+ } else {
1685+ USTPUTC(c, out);
1686+ c = pgetc_linecont();
1687+ }
1688+ } else if (is_special(c)) {
1689+ c1 = c;
1690+ c = pgetc_linecont();
1691+ if (subtype == 0 && c1 == '#') {
1692+ subtype = VSLENGTH;
1693+ if (strchr(types, c) == NULL && c != ':' &&
1694+ c != '#' && c != '%')
1695+ goto varname;
1696+ c1 = c;
1697+ c = pgetc_linecont();
1698+ if (c1 != '}' && c == '}') {
1699+ pungetc();
1700+ c = c1;
1701+ goto varname;
1702+ }
1703+ pungetc();
1704+ c = c1;
1705+ c1 = '#';
1706+ subtype = 0;
1707+ }
1708+ USTPUTC(c1, out);
1709+ } else {
1710+ subtype = VSERROR;
1711+ if (c == '}')
1712+ pungetc();
1713+ else if (c == '\n' || c == PEOF)
1714+ synerror("Unexpected end of line in substitution");
1715+ else if (BASESYNTAX[c] != CCTL)
1716+ USTPUTC(c, out);
1717+ }
1718+ if (subtype == 0) {
1719+ switch (c) {
1720+ case ':':
1721+ flags |= VSNUL;
1722+ c = pgetc_linecont();
1723+ /*FALLTHROUGH*/
1724+ default:
1725+ p = strchr(types, c);
1726+ if (p == NULL) {
1727+ if (c == '\n' || c == PEOF)
1728+ synerror("Unexpected end of line in substitution");
1729+ if (flags == VSNUL)
1730+ STPUTC(':', out);
1731+ if (BASESYNTAX[c] != CCTL)
1732+ STPUTC(c, out);
1733+ subtype = VSERROR;
1734+ } else
1735+ subtype = p - types + VSNORMAL;
1736+ break;
1737+ case '%':
1738+ case '#':
1739+ {
1740+ int cc = c;
1741+ subtype = c == '#' ? VSTRIMLEFT :
1742+ VSTRIMRIGHT;
1743+ c = pgetc_linecont();
1744+ if (c == cc)
1745+ subtype++;
1746+ else
1747+ pungetc();
1748+ break;
1749+ }
1750+ }
1751+ } else if (subtype != VSERROR) {
1752+ if (subtype == VSLENGTH && c != '}')
1753+ subtype = VSERROR;
1754+ pungetc();
1755+ }
1756+ STPUTC('=', out);
1757+ if (state[level].syntax == DQSYNTAX ||
1758+ state[level].syntax == ARISYNTAX)
1759+ flags |= VSQUOTE;
1760+ *(stackblock() + typeloc) = subtype | flags;
1761+ if (subtype != VSNORMAL) {
1762+ if (level + 1 >= maxnest) {
1763+ maxnest *= 2;
1764+ if (state == state_static) {
1765+ state = parser_temp_alloc(
1766+ maxnest * sizeof(*state));
1767+ memcpy(state, state_static,
1768+ MAXNEST_static * sizeof(*state));
1769+ } else
1770+ state = parser_temp_realloc(state,
1771+ maxnest * sizeof(*state));
1772+ }
1773+ level++;
1774+ state[level].parenlevel = 0;
1775+ if (subtype == VSMINUS || subtype == VSPLUS ||
1776+ subtype == VSQUESTION || subtype == VSASSIGN) {
1777+ /*
1778+ * For operators that were in the Bourne shell,
1779+ * inherit the double-quote state.
1780+ */
1781+ state[level].syntax = state[level - 1].syntax;
1782+ state[level].category = TSTATE_VAR_OLD;
1783+ } else {
1784+ /*
1785+ * The other operators take a pattern,
1786+ * so go to BASESYNTAX.
1787+ * Also, ' and " are now special, even
1788+ * in here documents.
1789+ */
1790+ state[level].syntax = BASESYNTAX;
1791+ state[level].category = TSTATE_VAR_NEW;
1792+ newvarnest++;
1793+ }
1794+ }
1795+ } else if (c == '\'' && state[level].syntax == BASESYNTAX) {
1796+ /* $'cstylequotes' */
1797+ USTPUTC(CTLQUOTEMARK, out);
1798+ state[level].syntax = SQSYNTAX;
1799+ sqiscstyle = 1;
1800+ } else {
1801+ USTPUTC('$', out);
1802+ pungetc();
1803+ }
1804+ goto parsesub_return;
1805+}
1806+
1807+
1808+/*
1809+ * Parse an arithmetic expansion (indicate start of one and set state)
1810+ */
1811+parsearith: {
1812+
1813+ if (level + 1 >= maxnest) {
1814+ maxnest *= 2;
1815+ if (state == state_static) {
1816+ state = parser_temp_alloc(
1817+ maxnest * sizeof(*state));
1818+ memcpy(state, state_static,
1819+ MAXNEST_static * sizeof(*state));
1820+ } else
1821+ state = parser_temp_realloc(state,
1822+ maxnest * sizeof(*state));
1823+ }
1824+ level++;
1825+ state[level].syntax = ARISYNTAX;
1826+ state[level].parenlevel = 0;
1827+ state[level].category = TSTATE_ARITH;
1828+ USTPUTC(CTLARI, out);
1829+ if (state[level - 1].syntax == DQSYNTAX)
1830+ USTPUTC('"',out);
1831+ else
1832+ USTPUTC(' ',out);
1833+ goto parsearith_return;
1834+}
1835+
1836+} /* end of readtoken */
1837+
1838+
1839+/*
1840+ * Returns true if the text contains nothing to expand (no dollar signs
1841+ * or backquotes).
1842+ */
1843+
1844+static int
1845+noexpand(char *text)
1846+{
1847+ char *p;
1848+ char c;
1849+
1850+ p = text;
1851+ while ((c = *p++) != '\0') {
1852+ if ( c == CTLQUOTEMARK)
1853+ continue;
1854+ if (c == CTLESC)
1855+ p++;
1856+ else if (BASESYNTAX[(int)c] == CCTL)
1857+ return 0;
1858+ }
1859+ return 1;
1860+}
1861+
1862+
1863+/*
1864+ * Return true if the argument is a legal variable name (a letter or
1865+ * underscore followed by zero or more letters, underscores, and digits).
1866+ */
1867+
1868+int
1869+goodname(const char *name)
1870+{
1871+ const char *p;
1872+
1873+ p = name;
1874+ if (! is_name(*p))
1875+ return 0;
1876+ while (*++p) {
1877+ if (! is_in_name(*p))
1878+ return 0;
1879+ }
1880+ return 1;
1881+}
1882+
1883+
1884+int
1885+isassignment(const char *p)
1886+{
1887+ if (!is_name(*p))
1888+ return 0;
1889+ p++;
1890+ for (;;) {
1891+ if (*p == '=')
1892+ return 1;
1893+ else if (!is_in_name(*p))
1894+ return 0;
1895+ p++;
1896+ }
1897+}
1898+
1899+
1900+static void
1901+consumetoken(int token)
1902+{
1903+ if (readtoken() != token)
1904+ synexpect(token);
1905+}
1906+
1907+
1908+/*
1909+ * Called when an unexpected token is read during the parse. The argument
1910+ * is the token that is expected, or -1 if more than one type of token can
1911+ * occur at this point.
1912+ */
1913+
1914+static void
1915+synexpect(int token)
1916+{
1917+ char msg[64];
1918+
1919+ if (token >= 0) {
1920+ fmtstr(msg, 64, "%s unexpected (expecting %s)",
1921+ tokname[lasttoken], tokname[token]);
1922+ } else {
1923+ fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
1924+ }
1925+ synerror(msg);
1926+}
1927+
1928+
1929+static void
1930+synerror(const char *msg)
1931+{
1932+ if (commandname)
1933+ outfmt(out2, "%s: %d: ", commandname, startlinno);
1934+ else if (arg0)
1935+ outfmt(out2, "%s: ", arg0);
1936+ outfmt(out2, "Syntax error: %s\n", msg);
1937+ error((char *)NULL);
1938+}
1939+
1940+static void
1941+setprompt(int which)
1942+{
1943+ whichprompt = which;
1944+ if (which == 0)
1945+ return;
1946+
1947+#ifndef NO_HISTORY
1948+ if (!el)
1949+#endif
1950+ {
1951+ out2str(getprompt(NULL));
1952+ flushout(out2);
1953+ }
1954+}
1955+
1956+static int
1957+pgetc_linecont(void)
1958+{
1959+ int c;
1960+
1961+ while ((c = pgetc_macro()) == '\\') {
1962+ c = pgetc();
1963+ if (c == '\n') {
1964+ plinno++;
1965+ if (doprompt)
1966+ setprompt(2);
1967+ else
1968+ setprompt(0);
1969+ } else {
1970+ pungetc();
1971+ /* Allow the backslash to be pushed back. */
1972+ pushstring("\\", 1, NULL);
1973+ return (pgetc());
1974+ }
1975+ }
1976+ return (c);
1977+}
1978+
1979+
1980+static struct passwd *
1981+getpwlogin(void)
1982+{
1983+ const char *login;
1984+
1985+ login = getlogin();
1986+ if (login == NULL)
1987+ return (NULL);
1988+
1989+ return (getpwnam(login));
1990+}
1991+
1992+
1993+static void
1994+getusername(char *name, size_t namelen)
1995+{
1996+ static char cached_name[MAXLOGNAME];
1997+ struct passwd *pw;
1998+ uid_t euid;
1999+
2000+ if (cached_name[0] == '\0') {
2001+ euid = geteuid();
2002+
2003+ /*
2004+ * Handle the case when there is more than one
2005+ * login with the same UID, or when the login
2006+ * returned by getlogin(2) does no longer match
2007+ * the current UID.
2008+ */
2009+ pw = getpwlogin();
2010+ if (pw == NULL || pw->pw_uid != euid)
2011+ pw = getpwuid(euid);
2012+
2013+ if (pw != NULL) {
2014+ strlcpy(cached_name, pw->pw_name,
2015+ sizeof(cached_name));
2016+ } else {
2017+ snprintf(cached_name, sizeof(cached_name),
2018+ "%u", euid);
2019+ }
2020+ }
2021+
2022+ strlcpy(name, cached_name, namelen);
2023+}
2024+
2025+
2026+/*
2027+ * called by editline -- any expansions to the prompt
2028+ * should be added here.
2029+ */
2030+char *
2031+getprompt(void *unused __unused)
2032+{
2033+ static char ps[PROMPTLEN];
2034+ const char *fmt;
2035+ const char *home;
2036+ const char *pwd;
2037+ size_t homelen;
2038+ int i, trim;
2039+ static char internal_error[] = "??";
2040+
2041+ /*
2042+ * Select prompt format.
2043+ */
2044+ switch (whichprompt) {
2045+ case 0:
2046+ fmt = "";
2047+ break;
2048+ case 1:
2049+ fmt = ps1val();
2050+ break;
2051+ case 2:
2052+ fmt = ps2val();
2053+ break;
2054+ default:
2055+ return internal_error;
2056+ }
2057+
2058+ /*
2059+ * Format prompt string.
2060+ */
2061+ for (i = 0; (i < PROMPTLEN - 1) && (*fmt != '\0'); i++, fmt++) {
2062+ if (*fmt == '$') {
2063+ const char *varname_start, *varname_end, *value;
2064+ char varname[256];
2065+ int namelen, braced = 0;
2066+
2067+ fmt++; /* Skip the '$' */
2068+
2069+ /* Check for ${VAR} syntax */
2070+ if (*fmt == '{') {
2071+ braced = 1;
2072+ fmt++;
2073+ }
2074+
2075+ varname_start = fmt;
2076+
2077+ /* Extract variable name */
2078+ if (is_digit(*fmt)) {
2079+ /* Positional parameter: $0, $1, etc. */
2080+ fmt++;
2081+ varname_end = fmt;
2082+ } else if (is_special(*fmt)) {
2083+ /* Special parameter: $?, $!, $$, etc. */
2084+ fmt++;
2085+ varname_end = fmt;
2086+ } else if (is_name(*fmt)) {
2087+ /* Regular variable name */
2088+ do
2089+ fmt++;
2090+ while (is_in_name(*fmt));
2091+ varname_end = fmt;
2092+ } else {
2093+ /*
2094+ * Not a valid variable reference.
2095+ * Output literal '$'.
2096+ */
2097+ ps[i] = '$';
2098+ if (braced && i < PROMPTLEN - 2)
2099+ ps[++i] = '{';
2100+ fmt = varname_start - 1;
2101+ continue;
2102+ }
2103+
2104+ namelen = varname_end - varname_start;
2105+ if (namelen == 0 || namelen >= (int)sizeof(varname)) {
2106+ /* Invalid or too long, output literal */
2107+ ps[i] = '$';
2108+ fmt = varname_start - 1;
2109+ continue;
2110+ }
2111+
2112+ /* Copy variable name */
2113+ memcpy(varname, varname_start, namelen);
2114+ varname[namelen] = '\0';
2115+
2116+ /* Handle closing brace for ${VAR} */
2117+ if (braced) {
2118+ if (*fmt == '}') {
2119+ fmt++;
2120+ } else {
2121+ /* Missing closing brace, treat as literal */
2122+ ps[i] = '$';
2123+ if (i < PROMPTLEN - 2)
2124+ ps[++i] = '{';
2125+ fmt = varname_start - 1;
2126+ continue;
2127+ }
2128+ }
2129+
2130+ /* Look up the variable */
2131+ if (namelen == 1 && is_digit(*varname)) {
2132+ /* Positional parameters - check digits FIRST */
2133+ int num = *varname - '0';
2134+ if (num == 0)
2135+ value = arg0 ? arg0 : "";
2136+ else if (num > 0 && num <= shellparam.nparam)
2137+ value = shellparam.p[num - 1];
2138+ else
2139+ value = "";
2140+ } else if (namelen == 1 && is_special(*varname)) {
2141+ /* Special parameters */
2142+ char valbuf[20];
2143+ int num;
2144+
2145+ switch (*varname) {
2146+ case '$':
2147+ num = rootpid;
2148+ break;
2149+ case '?':
2150+ num = exitstatus;
2151+ break;
2152+ case '#':
2153+ num = shellparam.nparam;
2154+ break;
2155+ case '!':
2156+ num = backgndpidval();
2157+ break;
2158+ default:
2159+ num = 0;
2160+ break;
2161+ }
2162+ snprintf(valbuf, sizeof(valbuf), "%d", num);
2163+ value = valbuf;
2164+ } else {
2165+ /* Regular variables */
2166+ value = lookupvar(varname);
2167+ if (value == NULL)
2168+ value = "";
2169+ }
2170+
2171+ /* Copy value to output, respecting buffer size */
2172+ while (*value != '\0' && i < PROMPTLEN - 1) {
2173+ ps[i++] = *value++;
2174+ }
2175+
2176+ /*
2177+ * Adjust fmt and i for the loop increment.
2178+ * fmt will be incremented by the for loop,
2179+ * so position it one before where we want.
2180+ */
2181+ fmt--;
2182+ i--;
2183+ continue;
2184+ } else if (*fmt != '\\') {
2185+ ps[i] = *fmt;
2186+ continue;
2187+ }
2188+
2189+ switch (*++fmt) {
2190+
2191+ /*
2192+ * Non-printing sequence begin and end.
2193+ */
2194+ case '[':
2195+ case ']':
2196+ ps[i] = '\001';
2197+ break;
2198+
2199+ /*
2200+ * Literal \ and some ASCII characters:
2201+ * \a BEL
2202+ * \e ESC
2203+ * \r CR
2204+ */
2205+ case '\\':
2206+ case 'a':
2207+ case 'e':
2208+ case 'r':
2209+ if (*fmt == 'a')
2210+ ps[i] = '\007';
2211+ else if (*fmt == 'e')
2212+ ps[i] = '\033';
2213+ else if (*fmt == 'r')
2214+ ps[i] = '\r';
2215+ else
2216+ ps[i] = '\\';
2217+ break;
2218+
2219+ /*
2220+ * CRLF sequence
2221+ */
2222+ case 'n':
2223+ if (i < PROMPTLEN - 3) {
2224+ ps[i++] = '\r';
2225+ ps[i] = '\n';
2226+ }
2227+ break;
2228+
2229+ /*
2230+ * Print the current time as per provided strftime format.
2231+ */
2232+ case 'D': {
2233+ char tfmt[128] = "%X"; /* \D{} means %X. */
2234+ struct tm *now;
2235+
2236+ if (fmt[1] != '{') {
2237+ /*
2238+ * "\D" but not "\D{", so treat the '\'
2239+ * literally and rewind fmt to treat 'D'
2240+ * literally next iteration.
2241+ */
2242+ ps[i] = '\\';
2243+ fmt--;
2244+ break;
2245+ }
2246+ fmt += 2; /* Consume "D{". */
2247+ if (fmt[0] != '}') {
2248+ char *end;
2249+
2250+ end = memccpy(tfmt, fmt, '}', sizeof(tfmt));
2251+ if (end == NULL) {
2252+ /*
2253+ * Format too long or no '}', so
2254+ * ignore "\D{" altogether.
2255+ * The loop will do i++, but nothing
2256+ * was written to ps, so do i-- here.
2257+ * Rewind fmt for similar reason.
2258+ */
2259+ i--;
2260+ fmt--;
2261+ break;
2262+ }
2263+ *--end = '\0'; /* Ignore the copy of '}'. */
2264+ fmt += end - tfmt;
2265+ }
2266+ now = localtime(&(time_t){time(NULL)});
2267+ i += strftime(&ps[i], PROMPTLEN - i - 1, tfmt, now);
2268+ i--; /* The loop will do i++. */
2269+ break;
2270+ }
2271+
2272+ /*
2273+ * Hostname.
2274+ *
2275+ * \h specifies just the local hostname,
2276+ * \H specifies fully-qualified hostname.
2277+ */
2278+ case 'h':
2279+ case 'H':
2280+ ps[i] = '\0';
2281+ gethostname(&ps[i], PROMPTLEN - i - 1);
2282+ ps[PROMPTLEN - 1] = '\0';
2283+ /* Skip to end of hostname. */
2284+ trim = (*fmt == 'h') ? '.' : '\0';
2285+ while ((ps[i] != '\0') && (ps[i] != trim))
2286+ i++;
2287+ --i;
2288+ break;
2289+
2290+ /*
2291+ * User name.
2292+ */
2293+ case 'u':
2294+ ps[i] = '\0';
2295+ getusername(&ps[i], PROMPTLEN - i);
2296+ /* Skip to end of username. */
2297+ while (ps[i + 1] != '\0')
2298+ i++;
2299+ break;
2300+
2301+ /*
2302+ * Working directory.
2303+ *
2304+ * \W specifies just the final component,
2305+ * \w specifies the entire path.
2306+ */
2307+ case 'W':
2308+ case 'w':
2309+ pwd = lookupvar("PWD");
2310+ if (pwd == NULL || *pwd == '\0')
2311+ pwd = "?";
2312+ if (*fmt == 'W' &&
2313+ *pwd == '/' && pwd[1] != '\0')
2314+ strlcpy(&ps[i], strrchr(pwd, '/') + 1,
2315+ PROMPTLEN - i);
2316+ else {
2317+ home = lookupvar("HOME");
2318+ if (home != NULL)
2319+ homelen = strlen(home);
2320+ if (home != NULL &&
2321+ strcmp(home, "/") != 0 &&
2322+ strncmp(pwd, home, homelen) == 0 &&
2323+ (pwd[homelen] == '/' ||
2324+ pwd[homelen] == '\0')) {
2325+ strlcpy(&ps[i], "~",
2326+ PROMPTLEN - i);
2327+ strlcpy(&ps[i + 1],
2328+ pwd + homelen,
2329+ PROMPTLEN - i - 1);
2330+ } else {
2331+ strlcpy(&ps[i], pwd, PROMPTLEN - i);
2332+ }
2333+ }
2334+ /* Skip to end of path. */
2335+ while (ps[i + 1] != '\0')
2336+ i++;
2337+ break;
2338+
2339+ /*
2340+ * Superuser status.
2341+ *
2342+ * '$' for normal users, '#' for root.
2343+ */
2344+ case '$':
2345+ ps[i] = (geteuid() != 0) ? '$' : '#';
2346+ break;
2347+
2348+ /*
2349+ * Emit unrecognized formats verbatim.
2350+ */
2351+ default:
2352+ ps[i] = '\\';
2353+ if (i < PROMPTLEN - 2)
2354+ ps[++i] = *fmt;
2355+ break;
2356+ }
2357+
2358+ }
2359+ ps[i] = '\0';
2360+ return (ps);
2361+}
2362+
2363+
2364+const char *
2365+expandstr(const char *ps)
2366+{
2367+ union node n;
2368+ struct jmploc jmploc;
2369+ struct jmploc *const savehandler = handler;
2370+ const int saveprompt = doprompt;
2371+ struct parsefile *const savetopfile = getcurrentfile();
2372+ struct parser_temp *const saveparser_temp = parser_temp;
2373+ const char *result = NULL;
2374+
2375+ if (!setjmp(jmploc.loc)) {
2376+ handler = &jmploc;
2377+ parser_temp = NULL;
2378+ setinputstring(ps);
2379+ doprompt = 0;
2380+ readtoken1(pgetc(), DQSYNTAX, NOEOFMARK, 0);
2381+ if (backquotelist != NULL)
2382+ error("Command substitution not allowed here");
2383+
2384+ n.narg.type = NARG;
2385+ n.narg.next = NULL;
2386+ n.narg.text = wordtext;
2387+ n.narg.backquote = backquotelist;
2388+
2389+ expandarg(&n, NULL, 0);
2390+ result = stackblock();
2391+ INTOFF;
2392+ }
2393+ handler = savehandler;
2394+ doprompt = saveprompt;
2395+ popfilesupto(savetopfile);
2396+ if (parser_temp != saveparser_temp) {
2397+ parser_temp_free_all();
2398+ parser_temp = saveparser_temp;
2399+ }
2400+ if (result != NULL) {
2401+ INTON;
2402+ } else if (exception == EXINT)
2403+ raise(SIGINT);
2404+ return result;
2405+}
+84,
-0
1@@ -0,0 +1,84 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+/* control characters in argument strings */
37+#define CTLESC '\300'
38+#define CTLVAR '\301'
39+#define CTLENDVAR '\371'
40+#define CTLBACKQ '\372'
41+#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
42+/* CTLBACKQ | CTLQUOTE == '\373' */
43+#define CTLARI '\374'
44+#define CTLENDARI '\375'
45+#define CTLQUOTEMARK '\376'
46+#define CTLQUOTEEND '\377' /* only for ${v+-...} */
47+
48+/* variable substitution byte (follows CTLVAR) */
49+#define VSTYPE 0x0f /* type of variable substitution */
50+#define VSNUL 0x10 /* colon--treat the empty string as unset */
51+#define VSLINENO 0x20 /* expansion of $LINENO, the line number \
52+ follows immediately */
53+#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
54+
55+/* values of VSTYPE field */
56+#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
57+#define VSMINUS 0x2 /* ${var-text} */
58+#define VSPLUS 0x3 /* ${var+text} */
59+#define VSQUESTION 0x4 /* ${var?message} */
60+#define VSASSIGN 0x5 /* ${var=text} */
61+#define VSTRIMLEFT 0x6 /* ${var#pattern} */
62+#define VSTRIMLEFTMAX 0x7 /* ${var##pattern} */
63+#define VSTRIMRIGHT 0x8 /* ${var%pattern} */
64+#define VSTRIMRIGHTMAX 0x9 /* ${var%%pattern} */
65+#define VSLENGTH 0xa /* ${#var} */
66+#define VSERROR 0xb /* Syntax error, issue when expanded */
67+
68+
69+/*
70+ * NEOF is returned by parsecmd when it encounters an end of file. It
71+ * must be distinct from NULL.
72+ */
73+#define NEOF ((union node *)-1)
74+extern int whichprompt; /* 1 == PS1, 2 == PS2 */
75+extern const char *const parsekwd[];
76+
77+
78+union node *parsecmd(int);
79+union node *parsewordexp(void);
80+void forcealias(void);
81+void fixredir(union node *, const char *, int);
82+int goodname(const char *);
83+int isassignment(const char *);
84+char *getprompt(void *);
85+const char *expandstr(const char *);
+672,
-0
1@@ -0,0 +1,672 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
6+ * Copyright 2014 Garrett D'Amore <garrett@damore.org>
7+ * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
8+ * Copyright (c) 1989, 1993
9+ * The Regents of the University of California. All rights reserved.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+/*
36+ * Important: This file is used both as a standalone program /usr/bin/printf
37+ * and as a builtin for /bin/sh (#define SHELL).
38+ */
39+
40+#include <sys/types.h>
41+
42+#include <ctype.h>
43+#include <err.h>
44+#include <errno.h>
45+#include <inttypes.h>
46+#include <limits.h>
47+#include <locale.h>
48+#include <stdio.h>
49+#include <stdlib.h>
50+#include <string.h>
51+#include <unistd.h>
52+#include <wchar.h>
53+
54+#ifdef SHELL
55+#define main printfcmd
56+#include "bltin.h"
57+#include "options.h"
58+#endif
59+
60+#define PF(f, func) do { \
61+ if (havewidth) \
62+ if (haveprec) \
63+ (void)printf(f, fieldwidth, precision, func); \
64+ else \
65+ (void)printf(f, fieldwidth, func); \
66+ else if (haveprec) \
67+ (void)printf(f, precision, func); \
68+ else \
69+ (void)printf(f, func); \
70+} while (0)
71+
72+static int asciicode(void);
73+static char *printf_doformat(char *, int *);
74+static int escape(char *, int, size_t *);
75+static int getchr(void);
76+static int getfloating(long double *, int);
77+static int getint(int *);
78+static int getnum(intmax_t *, uintmax_t *, int);
79+static const char
80+ *getstr(void);
81+static char *mknum(char *, char);
82+static void usage(void);
83+
84+static const char digits[] = "0123456789";
85+
86+static char end_fmt[1];
87+
88+static int myargc;
89+static char **myargv;
90+static char **gargv;
91+static char **maxargv;
92+
93+int
94+main(int argc, char *argv[])
95+{
96+ size_t len;
97+ int end, rval;
98+ char *format, *fmt, *start;
99+#ifndef SHELL
100+ int ch;
101+
102+ (void) setlocale(LC_ALL, "");
103+#endif
104+
105+#ifdef SHELL
106+ nextopt("");
107+ argc -= argptr - argv;
108+ argv = argptr;
109+#else
110+ while ((ch = getopt(argc, argv, "")) != -1)
111+ switch (ch) {
112+ case '?':
113+ default:
114+ usage();
115+ return (1);
116+ }
117+ argc -= optind;
118+ argv += optind;
119+#endif
120+
121+ if (argc < 1) {
122+ usage();
123+ return (1);
124+ }
125+
126+#ifdef SHELL
127+ INTOFF;
128+#endif
129+ /*
130+ * Basic algorithm is to scan the format string for conversion
131+ * specifications -- once one is found, find out if the field
132+ * width or precision is a '*'; if it is, gather up value. Note,
133+ * format strings are reused as necessary to use up the provided
134+ * arguments, arguments of zero/null string are provided to use
135+ * up the format string.
136+ */
137+ fmt = format = *argv;
138+ escape(fmt, 1, &len); /* backslash interpretation */
139+ rval = end = 0;
140+ gargv = ++argv;
141+
142+ for (;;) {
143+ maxargv = gargv;
144+
145+ myargv = gargv;
146+ for (myargc = 0; gargv[myargc]; myargc++)
147+ /* nop */;
148+ start = fmt;
149+ while (fmt < format + len) {
150+ if (fmt[0] == '%') {
151+ fwrite(start, 1, fmt - start, stdout);
152+ if (fmt[1] == '%') {
153+ /* %% prints a % */
154+ putchar('%');
155+ fmt += 2;
156+ } else {
157+ fmt = printf_doformat(fmt, &rval);
158+ if (fmt == NULL || fmt == end_fmt) {
159+#ifdef SHELL
160+ INTON;
161+#endif
162+ return (fmt == NULL ? 1 : rval);
163+ }
164+ end = 0;
165+ }
166+ start = fmt;
167+ } else
168+ fmt++;
169+ if (gargv > maxargv)
170+ maxargv = gargv;
171+ }
172+ gargv = maxargv;
173+
174+ if (end == 1) {
175+ warnx("missing format character");
176+#ifdef SHELL
177+ INTON;
178+#endif
179+ return (1);
180+ }
181+ fwrite(start, 1, fmt - start, stdout);
182+ if (!*gargv) {
183+#ifdef SHELL
184+ INTON;
185+#endif
186+ return (rval);
187+ }
188+ /* Restart at the beginning of the format string. */
189+ fmt = format;
190+ end = 1;
191+ }
192+ /* NOTREACHED */
193+}
194+
195+
196+static char *
197+printf_doformat(char *fmt, int *rval)
198+{
199+ static const char skip1[] = "#'-+ 0";
200+ int fieldwidth, haveprec, havewidth, mod_ldbl, precision;
201+ char convch, nextch;
202+ char start[strlen(fmt) + 1];
203+ char **fargv;
204+ char *dptr;
205+ int l;
206+
207+ dptr = start;
208+ *dptr++ = '%';
209+ *dptr = 0;
210+
211+ fmt++;
212+
213+ /* look for "n$" field index specifier */
214+ l = strspn(fmt, digits);
215+ if ((l > 0) && (fmt[l] == '$')) {
216+ int idx = atoi(fmt);
217+ if (idx <= myargc) {
218+ gargv = &myargv[idx - 1];
219+ } else {
220+ gargv = &myargv[myargc];
221+ }
222+ if (gargv > maxargv)
223+ maxargv = gargv;
224+ fmt += l + 1;
225+
226+ /* save format argument */
227+ fargv = gargv;
228+ } else {
229+ fargv = NULL;
230+ }
231+
232+ /* skip to field width */
233+ while (*fmt && strchr(skip1, *fmt) != NULL) {
234+ *dptr++ = *fmt++;
235+ *dptr = 0;
236+ }
237+
238+ if (*fmt == '*') {
239+
240+ fmt++;
241+ l = strspn(fmt, digits);
242+ if ((l > 0) && (fmt[l] == '$')) {
243+ int idx = atoi(fmt);
244+ if (fargv == NULL) {
245+ warnx("incomplete use of n$");
246+ return (NULL);
247+ }
248+ if (idx <= myargc) {
249+ gargv = &myargv[idx - 1];
250+ } else {
251+ gargv = &myargv[myargc];
252+ }
253+ fmt += l + 1;
254+ } else if (fargv != NULL) {
255+ warnx("incomplete use of n$");
256+ return (NULL);
257+ }
258+
259+ if (getint(&fieldwidth))
260+ return (NULL);
261+ if (gargv > maxargv)
262+ maxargv = gargv;
263+ havewidth = 1;
264+
265+ *dptr++ = '*';
266+ *dptr = 0;
267+ } else {
268+ havewidth = 0;
269+
270+ /* skip to possible '.', get following precision */
271+ while (isdigit(*fmt)) {
272+ *dptr++ = *fmt++;
273+ *dptr = 0;
274+ }
275+ }
276+
277+ if (*fmt == '.') {
278+ /* precision present? */
279+ fmt++;
280+ *dptr++ = '.';
281+
282+ if (*fmt == '*') {
283+
284+ fmt++;
285+ l = strspn(fmt, digits);
286+ if ((l > 0) && (fmt[l] == '$')) {
287+ int idx = atoi(fmt);
288+ if (fargv == NULL) {
289+ warnx("incomplete use of n$");
290+ return (NULL);
291+ }
292+ if (idx <= myargc) {
293+ gargv = &myargv[idx - 1];
294+ } else {
295+ gargv = &myargv[myargc];
296+ }
297+ fmt += l + 1;
298+ } else if (fargv != NULL) {
299+ warnx("incomplete use of n$");
300+ return (NULL);
301+ }
302+
303+ if (getint(&precision))
304+ return (NULL);
305+ if (gargv > maxargv)
306+ maxargv = gargv;
307+ haveprec = 1;
308+ *dptr++ = '*';
309+ *dptr = 0;
310+ } else {
311+ haveprec = 0;
312+
313+ /* skip to conversion char */
314+ while (isdigit(*fmt)) {
315+ *dptr++ = *fmt++;
316+ *dptr = 0;
317+ }
318+ }
319+ } else
320+ haveprec = 0;
321+ if (!*fmt) {
322+ warnx("missing format character");
323+ return (NULL);
324+ }
325+ *dptr++ = *fmt;
326+ *dptr = 0;
327+
328+ /*
329+ * Look for a length modifier. POSIX doesn't have these, so
330+ * we only support them for floating-point conversions, which
331+ * are extensions. This is useful because the L modifier can
332+ * be used to gain extra range and precision, while omitting
333+ * it is more likely to produce consistent results on different
334+ * architectures. This is not so important for integers
335+ * because overflow is the only bad thing that can happen to
336+ * them, but consider the command printf %a 1.1
337+ */
338+ if (*fmt == 'L') {
339+ mod_ldbl = 1;
340+ fmt++;
341+ if (!strchr("aAeEfFgG", *fmt)) {
342+ warnx("bad modifier L for %%%c", *fmt);
343+ return (NULL);
344+ }
345+ } else {
346+ mod_ldbl = 0;
347+ }
348+
349+ /* save the current arg offset, and set to the format arg */
350+ if (fargv != NULL) {
351+ gargv = fargv;
352+ }
353+
354+ convch = *fmt;
355+ nextch = *++fmt;
356+
357+ *fmt = '\0';
358+ switch (convch) {
359+ case 'b': {
360+ size_t len;
361+ char *p;
362+ int getout;
363+
364+ /* Convert "b" to "s" for output. */
365+ start[strlen(start) - 1] = 's';
366+ if ((p = strdup(getstr())) == NULL) {
367+ warnx("%s", strerror(ENOMEM));
368+ return (NULL);
369+ }
370+ getout = escape(p, 0, &len);
371+ PF(start, p);
372+ /* Restore format for next loop. */
373+
374+ free(p);
375+ if (getout)
376+ return (end_fmt);
377+ break;
378+ }
379+ case 'c': {
380+ char p;
381+
382+ p = getchr();
383+ if (p != '\0')
384+ PF(start, p);
385+ break;
386+ }
387+ case 's': {
388+ const char *p;
389+
390+ p = getstr();
391+ PF(start, p);
392+ break;
393+ }
394+ case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
395+ char *f;
396+ intmax_t val;
397+ uintmax_t uval;
398+ int signedconv;
399+
400+ signedconv = (convch == 'd' || convch == 'i');
401+ if ((f = mknum(start, convch)) == NULL)
402+ return (NULL);
403+ if (getnum(&val, &uval, signedconv))
404+ *rval = 1;
405+ if (signedconv)
406+ PF(f, val);
407+ else
408+ PF(f, uval);
409+ break;
410+ }
411+ case 'e': case 'E':
412+ case 'f': case 'F':
413+ case 'g': case 'G':
414+ case 'a': case 'A': {
415+ long double p;
416+
417+ if (getfloating(&p, mod_ldbl))
418+ *rval = 1;
419+ if (mod_ldbl)
420+ PF(start, p);
421+ else
422+ PF(start, (double)p);
423+ break;
424+ }
425+ default:
426+ warnx("illegal format character %c", convch);
427+ return (NULL);
428+ }
429+ *fmt = nextch;
430+ /* return the gargv to the next element */
431+ return (fmt);
432+}
433+
434+static char *
435+mknum(char *str, char ch)
436+{
437+ static char *copy;
438+ static size_t copy_size;
439+ char *newcopy;
440+ size_t len, newlen;
441+
442+ len = strlen(str) + 2;
443+ if (len > copy_size) {
444+ newlen = ((len + 1023) >> 10) << 10;
445+ if ((newcopy = realloc(copy, newlen)) == NULL) {
446+ warnx("%s", strerror(ENOMEM));
447+ return (NULL);
448+ }
449+ copy = newcopy;
450+ copy_size = newlen;
451+ }
452+
453+ memmove(copy, str, len - 3);
454+ copy[len - 3] = 'j';
455+ copy[len - 2] = ch;
456+ copy[len - 1] = '\0';
457+ return (copy);
458+}
459+
460+static int
461+escape(char *fmt, int percent, size_t *len)
462+{
463+ char *save, *store, c;
464+ int value;
465+
466+ for (save = store = fmt; ((c = *fmt) != 0); ++fmt, ++store) {
467+ if (c != '\\') {
468+ *store = c;
469+ continue;
470+ }
471+ switch (*++fmt) {
472+ case '\0': /* EOS, user error */
473+ *store = '\\';
474+ *++store = '\0';
475+ *len = store - save;
476+ return (0);
477+ case '\\': /* backslash */
478+ case '\'': /* single quote */
479+ *store = *fmt;
480+ break;
481+ case 'a': /* bell/alert */
482+ *store = '\a';
483+ break;
484+ case 'b': /* backspace */
485+ *store = '\b';
486+ break;
487+ case 'c':
488+ if (!percent) {
489+ *store = '\0';
490+ *len = store - save;
491+ return (1);
492+ }
493+ *store = 'c';
494+ break;
495+ case 'f': /* form-feed */
496+ *store = '\f';
497+ break;
498+ case 'n': /* newline */
499+ *store = '\n';
500+ break;
501+ case 'r': /* carriage-return */
502+ *store = '\r';
503+ break;
504+ case 't': /* horizontal tab */
505+ *store = '\t';
506+ break;
507+ case 'v': /* vertical tab */
508+ *store = '\v';
509+ break;
510+ /* octal constant */
511+ case '0': case '1': case '2': case '3':
512+ case '4': case '5': case '6': case '7':
513+ c = (!percent && *fmt == '0') ? 4 : 3;
514+ for (value = 0;
515+ c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
516+ value <<= 3;
517+ value += *fmt - '0';
518+ }
519+ --fmt;
520+ if (percent && value == '%') {
521+ *store++ = '%';
522+ *store = '%';
523+ } else
524+ *store = (char)value;
525+ break;
526+ default:
527+ *store = *fmt;
528+ break;
529+ }
530+ }
531+ *store = '\0';
532+ *len = store - save;
533+ return (0);
534+}
535+
536+static int
537+getchr(void)
538+{
539+ if (!*gargv)
540+ return ('\0');
541+ return ((int)**gargv++);
542+}
543+
544+static const char *
545+getstr(void)
546+{
547+ if (!*gargv)
548+ return ("");
549+ return (*gargv++);
550+}
551+
552+static int
553+getint(int *ip)
554+{
555+ intmax_t val;
556+ uintmax_t uval;
557+ int rval;
558+
559+ if (getnum(&val, &uval, 1))
560+ return (1);
561+ rval = 0;
562+ if (val < INT_MIN || val > INT_MAX) {
563+ warnx("%s: %s", *gargv, strerror(ERANGE));
564+ rval = 1;
565+ }
566+ *ip = (int)val;
567+ return (rval);
568+}
569+
570+static int
571+getnum(intmax_t *ip, uintmax_t *uip, int signedconv)
572+{
573+ char *ep;
574+ int rval;
575+
576+ if (!*gargv) {
577+ *ip = *uip = 0;
578+ return (0);
579+ }
580+ if (**gargv == '"' || **gargv == '\'') {
581+ if (signedconv)
582+ *ip = asciicode();
583+ else
584+ *uip = asciicode();
585+ return (0);
586+ }
587+ rval = 0;
588+ errno = 0;
589+ if (signedconv)
590+ *ip = strtoimax(*gargv, &ep, 0);
591+ else
592+ *uip = strtoumax(*gargv, &ep, 0);
593+ if (ep == *gargv) {
594+ warnx("%s: expected numeric value", *gargv);
595+ rval = 1;
596+ }
597+ else if (*ep != '\0') {
598+ warnx("%s: not completely converted", *gargv);
599+ rval = 1;
600+ }
601+ if (errno == ERANGE) {
602+ warnx("%s: %s", *gargv, strerror(ERANGE));
603+ rval = 1;
604+ }
605+ ++gargv;
606+ return (rval);
607+}
608+
609+static int
610+getfloating(long double *dp, int mod_ldbl)
611+{
612+ char *ep;
613+ int rval;
614+
615+ if (!*gargv) {
616+ *dp = 0.0;
617+ return (0);
618+ }
619+ if (**gargv == '"' || **gargv == '\'') {
620+ *dp = asciicode();
621+ return (0);
622+ }
623+ rval = 0;
624+ errno = 0;
625+ if (mod_ldbl)
626+ *dp = strtold(*gargv, &ep);
627+ else
628+ *dp = strtod(*gargv, &ep);
629+ if (ep == *gargv) {
630+ warnx("%s: expected numeric value", *gargv);
631+ rval = 1;
632+ } else if (*ep != '\0') {
633+ warnx("%s: not completely converted", *gargv);
634+ rval = 1;
635+ }
636+ if (errno == ERANGE) {
637+ warnx("%s: %s", *gargv, strerror(ERANGE));
638+ rval = 1;
639+ }
640+ ++gargv;
641+ return (rval);
642+}
643+
644+static int
645+asciicode(void)
646+{
647+ int ch;
648+ wchar_t wch;
649+ mbstate_t mbs;
650+
651+ ch = (unsigned char)**gargv;
652+ if (ch == '\'' || ch == '"') {
653+ memset(&mbs, 0, sizeof(mbs));
654+ switch (mbrtowc(&wch, *gargv + 1, MB_LEN_MAX, &mbs)) {
655+ case (size_t)-2:
656+ case (size_t)-1:
657+ wch = (unsigned char)gargv[0][1];
658+ break;
659+ case 0:
660+ wch = 0;
661+ break;
662+ }
663+ ch = wch;
664+ }
665+ ++gargv;
666+ return (ch);
667+}
668+
669+static void
670+usage(void)
671+{
672+ (void)fprintf(stderr, "usage: printf format [arguments ...]\n");
673+}
+357,
-0
1@@ -0,0 +1,357 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#include <sys/types.h>
37+#include <sys/stat.h>
38+#include <signal.h>
39+#include <string.h>
40+#include <fcntl.h>
41+#include <errno.h>
42+#include <unistd.h>
43+#include <stdlib.h>
44+
45+/*
46+ * Code for dealing with input/output redirection.
47+ */
48+
49+#include "shell.h"
50+#include "nodes.h"
51+#include "jobs.h"
52+#include "expand.h"
53+#include "redir.h"
54+#include "output.h"
55+#include "memalloc.h"
56+#include "error.h"
57+#include "options.h"
58+
59+
60+#define EMPTY -2 /* marks an unused slot in redirtab */
61+#define CLOSED -1 /* fd was not open before redir */
62+
63+
64+struct redirtab {
65+ struct redirtab *next;
66+ int renamed[10];
67+ int fd0_redirected;
68+ unsigned int empty_redirs;
69+};
70+
71+
72+static struct redirtab *redirlist;
73+
74+/*
75+ * We keep track of whether or not fd0 has been redirected. This is for
76+ * background commands, where we want to redirect fd0 to /dev/null only
77+ * if it hasn't already been redirected.
78+ */
79+static int fd0_redirected = 0;
80+
81+/* Number of redirtabs that have not been allocated. */
82+static unsigned int empty_redirs = 0;
83+
84+static void openredirect(union node *, char[10]);
85+static int openhere(union node *);
86+
87+
88+/*
89+ * Process a list of redirection commands. If the REDIR_PUSH flag is set,
90+ * old file descriptors are stashed away so that the redirection can be
91+ * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
92+ * standard output, and the standard error if it becomes a duplicate of
93+ * stdout, is saved in memory.
94+ *
95+ * We suppress interrupts so that we won't leave open file
96+ * descriptors around. Because the signal handler remains
97+ * installed and we do not use system call restart, interrupts
98+ * will still abort blocking opens such as fifos (they will fail
99+ * with EINTR). There is, however, a race condition if an interrupt
100+ * arrives after INTOFF and before open blocks.
101+ */
102+
103+void
104+redirect(union node *redir, int flags)
105+{
106+ union node *n;
107+ struct redirtab *sv = NULL;
108+ int i;
109+ int fd;
110+ char memory[10]; /* file descriptors to write to memory */
111+
112+ INTOFF;
113+ for (i = 10 ; --i >= 0 ; )
114+ memory[i] = 0;
115+ memory[1] = flags & REDIR_BACKQ;
116+ if (flags & REDIR_PUSH) {
117+ empty_redirs++;
118+ if (redir != NULL) {
119+ sv = ckmalloc(sizeof (struct redirtab));
120+ for (i = 0 ; i < 10 ; i++)
121+ sv->renamed[i] = EMPTY;
122+ sv->fd0_redirected = fd0_redirected;
123+ sv->empty_redirs = empty_redirs - 1;
124+ sv->next = redirlist;
125+ redirlist = sv;
126+ empty_redirs = 0;
127+ }
128+ }
129+ for (n = redir ; n ; n = n->nfile.next) {
130+ fd = n->nfile.fd;
131+ if (fd == 0)
132+ fd0_redirected = 1;
133+ if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
134+ n->ndup.dupfd == fd)
135+ continue; /* redirect from/to same file descriptor */
136+
137+ if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) {
138+ INTOFF;
139+ if ((i = fcntl(fd, F_DUPFD_CLOEXEC, 10)) == -1) {
140+ switch (errno) {
141+ case EBADF:
142+ i = CLOSED;
143+ break;
144+ default:
145+ INTON;
146+ error("%d: %s", fd, strerror(errno));
147+ break;
148+ }
149+ }
150+ sv->renamed[fd] = i;
151+ INTON;
152+ }
153+ openredirect(n, memory);
154+ INTON;
155+ INTOFF;
156+ }
157+ if (memory[1])
158+ out1 = &memout;
159+ if (memory[2])
160+ out2 = &memout;
161+ INTON;
162+}
163+
164+
165+static void
166+openredirect(union node *redir, char memory[10])
167+{
168+ struct stat sb;
169+ int fd = redir->nfile.fd;
170+ const char *fname;
171+ int f;
172+ int e;
173+
174+ memory[fd] = 0;
175+ switch (redir->nfile.type) {
176+ case NFROM:
177+ fname = redir->nfile.expfname;
178+ if ((f = open(fname, O_RDONLY)) < 0)
179+ error("cannot open %s: %s", fname, strerror(errno));
180+ break;
181+ case NFROMTO:
182+ fname = redir->nfile.expfname;
183+ if ((f = open(fname, O_RDWR|O_CREAT, 0666)) < 0)
184+ error("cannot create %s: %s", fname, strerror(errno));
185+ break;
186+ case NTO:
187+ if (Cflag) {
188+ fname = redir->nfile.expfname;
189+ if (stat(fname, &sb) == -1) {
190+ if ((f = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0)
191+ error("cannot create %s: %s", fname, strerror(errno));
192+ } else if (!S_ISREG(sb.st_mode)) {
193+ if ((f = open(fname, O_WRONLY, 0666)) < 0)
194+ error("cannot create %s: %s", fname, strerror(errno));
195+ if (fstat(f, &sb) != -1 && S_ISREG(sb.st_mode)) {
196+ close(f);
197+ error("cannot create %s: %s", fname,
198+ strerror(EEXIST));
199+ }
200+ } else
201+ error("cannot create %s: %s", fname,
202+ strerror(EEXIST));
203+ break;
204+ }
205+ /* FALLTHROUGH */
206+ case NCLOBBER:
207+ fname = redir->nfile.expfname;
208+ if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
209+ error("cannot create %s: %s", fname, strerror(errno));
210+ break;
211+ case NAPPEND:
212+ fname = redir->nfile.expfname;
213+ if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
214+ error("cannot create %s: %s", fname, strerror(errno));
215+ break;
216+ case NTOFD:
217+ case NFROMFD:
218+ if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
219+ if (memory[redir->ndup.dupfd])
220+ memory[fd] = 1;
221+ else {
222+ if (dup2(redir->ndup.dupfd, fd) < 0)
223+ error("%d: %s", redir->ndup.dupfd,
224+ strerror(errno));
225+ }
226+ } else {
227+ close(fd);
228+ }
229+ return;
230+ case NHERE:
231+ case NXHERE:
232+ f = openhere(redir);
233+ break;
234+ default:
235+ abort();
236+ }
237+ if (f != fd) {
238+ if (dup2(f, fd) == -1) {
239+ e = errno;
240+ close(f);
241+ error("%d: %s", fd, strerror(e));
242+ }
243+ close(f);
244+ }
245+}
246+
247+
248+/*
249+ * Handle here documents. Normally we fork off a process to write the
250+ * data to a pipe. If the document is short, we can stuff the data in
251+ * the pipe without forking.
252+ */
253+
254+static int
255+openhere(union node *redir)
256+{
257+ const char *p;
258+ int pip[2];
259+ size_t len = 0;
260+ int flags;
261+ ssize_t written = 0;
262+
263+ if (pipe(pip) < 0)
264+ error("Pipe call failed: %s", strerror(errno));
265+
266+ if (redir->type == NXHERE)
267+ p = redir->nhere.expdoc;
268+ else
269+ p = redir->nhere.doc->narg.text;
270+ len = strlen(p);
271+ if (len == 0)
272+ goto out;
273+ flags = fcntl(pip[1], F_GETFL, 0);
274+ if (flags != -1 && fcntl(pip[1], F_SETFL, flags | O_NONBLOCK) != -1) {
275+ written = write(pip[1], p, len);
276+ if (written < 0)
277+ written = 0;
278+ if ((size_t)written == len)
279+ goto out;
280+ fcntl(pip[1], F_SETFL, flags);
281+ }
282+
283+ if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
284+ close(pip[0]);
285+ signal(SIGINT, SIG_IGN);
286+ signal(SIGQUIT, SIG_IGN);
287+ signal(SIGHUP, SIG_IGN);
288+ signal(SIGTSTP, SIG_IGN);
289+ signal(SIGPIPE, SIG_DFL);
290+ xwrite(pip[1], p + written, len - written);
291+ _exit(0);
292+ }
293+out:
294+ close(pip[1]);
295+ return pip[0];
296+}
297+
298+
299+
300+/*
301+ * Undo the effects of the last redirection.
302+ */
303+
304+void
305+popredir(void)
306+{
307+ struct redirtab *rp = redirlist;
308+ int i;
309+
310+ INTOFF;
311+ if (empty_redirs > 0) {
312+ empty_redirs--;
313+ INTON;
314+ return;
315+ }
316+ for (i = 0 ; i < 10 ; i++) {
317+ if (rp->renamed[i] != EMPTY) {
318+ if (rp->renamed[i] >= 0) {
319+ dup2(rp->renamed[i], i);
320+ close(rp->renamed[i]);
321+ } else {
322+ close(i);
323+ }
324+ }
325+ }
326+ fd0_redirected = rp->fd0_redirected;
327+ empty_redirs = rp->empty_redirs;
328+ redirlist = rp->next;
329+ ckfree(rp);
330+ INTON;
331+}
332+
333+/* Return true if fd 0 has already been redirected at least once. */
334+int
335+fd0_redirected_p(void)
336+{
337+ return fd0_redirected != 0;
338+}
339+
340+/*
341+ * Discard all saved file descriptors.
342+ */
343+
344+void
345+clearredir(void)
346+{
347+ struct redirtab *rp;
348+ int i;
349+
350+ for (rp = redirlist ; rp ; rp = rp->next) {
351+ for (i = 0 ; i < 10 ; i++) {
352+ if (rp->renamed[i] >= 0) {
353+ close(rp->renamed[i]);
354+ }
355+ rp->renamed[i] = EMPTY;
356+ }
357+ }
358+}
+43,
-0
1@@ -0,0 +1,43 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+/* flags passed to redirect */
37+#define REDIR_PUSH 01 /* save previous values of file descriptors */
38+#define REDIR_BACKQ 02 /* save the command output in memory */
39+
40+union node;
41+void redirect(union node *, int);
42+void popredir(void);
43+int fd0_redirected_p(void);
44+void clearredir(void);
+170,
-0
1@@ -0,0 +1,170 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#ifndef SHELL_H_
37+#define SHELL_H_
38+
39+#if !defined(FEATURE_SH_HISTEDIT) || !FEATURE_SH_HISTEDIT
40+#ifndef NO_HISTORY
41+#define NO_HISTORY
42+#endif
43+#endif
44+
45+#include "sig.h"
46+
47+#include <fcntl.h>
48+#include <inttypes.h>
49+#include <stdint.h>
50+#include <sys/ioctl.h>
51+#include <sys/stat.h>
52+#include <sys/types.h>
53+#include <unistd.h>
54+
55+#ifndef tcsetsid
56+#if defined(__linux__) || defined(__CYGWIN__)
57+#define tcsetsid(fd, pid) ioctl((fd), TIOCSCTTY, 0)
58+#endif
59+#endif
60+
61+#ifndef ALIGN
62+#define ALIGNBYTES (sizeof(void *) - 1)
63+#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) & ~ALIGNBYTES)
64+#endif
65+
66+#ifndef __dead2
67+#define __dead2 __attribute__((__noreturn__))
68+#endif
69+#ifndef __unused
70+#define __unused __attribute__((__unused__))
71+#endif
72+#ifndef __nonstring
73+#if defined(__has_attribute)
74+#if __has_attribute(__nonstring__)
75+#define __nonstring __attribute__((__nonstring__))
76+#elif __has_attribute(nonstring)
77+#define __nonstring __attribute__((nonstring))
78+#endif
79+#endif
80+#endif
81+#ifndef __nonstring
82+#define __nonstring
83+#endif
84+#ifndef __printf0like
85+#define __printf0like(n, m) __attribute__((__format__(__printf__, n, m)))
86+#endif
87+#ifndef __printflike
88+#define __printflike(n, m) __attribute__((__format__(__printf__, n, m)))
89+#endif
90+
91+#ifndef O_VERIFY
92+#define O_VERIFY 0
93+#endif
94+
95+#ifndef CLOCK_UPTIME
96+#define CLOCK_UPTIME CLOCK_MONOTONIC
97+#endif
98+
99+#ifndef timespecsub
100+#define timespecsub(a, b, result) \
101+ do { \
102+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
103+ (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \
104+ if ((result)->tv_nsec < 0) { \
105+ --(result)->tv_sec; \
106+ (result)->tv_nsec += 1000000000L; \
107+ } \
108+ } while (0)
109+#endif
110+
111+#ifndef MAXLOGNAME
112+#ifdef LOGIN_NAME_MAX
113+#define MAXLOGNAME LOGIN_NAME_MAX
114+#else
115+#define MAXLOGNAME 32
116+#endif
117+#endif
118+
119+#ifndef __DECONST
120+#define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var))
121+#endif
122+
123+#ifndef eaccess
124+#ifdef AT_EACCESS
125+#define eaccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_EACCESS)
126+#else
127+#define eaccess(path, mode) access((path), (mode))
128+#endif
129+#endif
130+
131+/*
132+ * The follow should be set to reflect the type of system you have:
133+ * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
134+ * define DEBUG=1 to compile in debugging (set global "debug" to turn on)
135+ * define DEBUG=2 to compile in and turn on debugging.
136+ *
137+ * When debugging is on, debugging info will be written to ./trace and
138+ * a quit signal will generate a core dump.
139+ */
140+
141+
142+#define JOBS 1
143+/* #define DEBUG 1 */
144+
145+/*
146+ * Type of used arithmetic. SUSv3 requires us to have at least signed long.
147+ */
148+typedef intmax_t arith_t;
149+#define ARITH_FORMAT_STR "%" PRIdMAX
150+#define ARITH_MIN INTMAX_MIN
151+#define ARITH_MAX INTMAX_MAX
152+
153+typedef void *pointer;
154+
155+#if defined(__has_include)
156+#if __has_include(<sys/cdefs.h>)
157+#include <sys/cdefs.h>
158+#endif
159+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
160+#include <sys/cdefs.h>
161+#endif
162+
163+extern char nullstr[1]; /* null string */
164+
165+#ifdef DEBUG
166+#define TRACE(param) sh_trace param
167+#else
168+#define TRACE(param)
169+#endif
170+
171+#endif /* !SHELL_H_ */
+401,
-0
1@@ -0,0 +1,401 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#include <fcntl.h>
37+#include <stdio.h>
38+#include <stdlib.h>
39+#include <stdarg.h>
40+#include <errno.h>
41+
42+#include "shell.h"
43+#include "parser.h"
44+#include "nodes.h"
45+#include "mystring.h"
46+#include "show.h"
47+
48+
49+#ifdef DEBUG
50+static void shtree(union node *, int, char *, FILE*);
51+static void shcmd(union node *, FILE *);
52+static void sharg(union node *, FILE *);
53+static void indent(int, char *, FILE *);
54+static void trstring(char *);
55+
56+
57+void
58+showtree(union node *n)
59+{
60+ trputs("showtree called\n");
61+ shtree(n, 1, NULL, stdout);
62+}
63+
64+
65+static void
66+shtree(union node *n, int ind, char *pfx, FILE *fp)
67+{
68+ struct nodelist *lp;
69+ const char *s;
70+
71+ if (n == NULL)
72+ return;
73+
74+ indent(ind, pfx, fp);
75+ switch(n->type) {
76+ case NSEMI:
77+ s = "; ";
78+ goto binop;
79+ case NAND:
80+ s = " && ";
81+ goto binop;
82+ case NOR:
83+ s = " || ";
84+ binop:
85+ shtree(n->nbinary.ch1, ind, NULL, fp);
86+ /* if (ind < 0) */
87+ fputs(s, fp);
88+ shtree(n->nbinary.ch2, ind, NULL, fp);
89+ break;
90+ case NCMD:
91+ shcmd(n, fp);
92+ if (ind >= 0)
93+ putc('\n', fp);
94+ break;
95+ case NPIPE:
96+ for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
97+ shcmd(lp->n, fp);
98+ if (lp->next)
99+ fputs(" | ", fp);
100+ }
101+ if (n->npipe.backgnd)
102+ fputs(" &", fp);
103+ if (ind >= 0)
104+ putc('\n', fp);
105+ break;
106+ default:
107+ fprintf(fp, "<node type %d>", n->type);
108+ if (ind >= 0)
109+ putc('\n', fp);
110+ break;
111+ }
112+}
113+
114+
115+
116+static void
117+shcmd(union node *cmd, FILE *fp)
118+{
119+ union node *np;
120+ int first;
121+ const char *s;
122+ int dftfd;
123+
124+ first = 1;
125+ for (np = cmd->ncmd.args ; np ; np = np->narg.next) {
126+ if (! first)
127+ putchar(' ');
128+ sharg(np, fp);
129+ first = 0;
130+ }
131+ for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) {
132+ if (! first)
133+ putchar(' ');
134+ switch (np->nfile.type) {
135+ case NTO: s = ">"; dftfd = 1; break;
136+ case NAPPEND: s = ">>"; dftfd = 1; break;
137+ case NTOFD: s = ">&"; dftfd = 1; break;
138+ case NCLOBBER: s = ">|"; dftfd = 1; break;
139+ case NFROM: s = "<"; dftfd = 0; break;
140+ case NFROMTO: s = "<>"; dftfd = 0; break;
141+ case NFROMFD: s = "<&"; dftfd = 0; break;
142+ case NHERE: s = "<<"; dftfd = 0; break;
143+ case NXHERE: s = "<<"; dftfd = 0; break;
144+ default: s = "*error*"; dftfd = 0; break;
145+ }
146+ if (np->nfile.fd != dftfd)
147+ fprintf(fp, "%d", np->nfile.fd);
148+ fputs(s, fp);
149+ if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
150+ if (np->ndup.dupfd >= 0)
151+ fprintf(fp, "%d", np->ndup.dupfd);
152+ else
153+ fprintf(fp, "-");
154+ } else if (np->nfile.type == NHERE) {
155+ fprintf(fp, "HERE");
156+ } else if (np->nfile.type == NXHERE) {
157+ fprintf(fp, "XHERE");
158+ } else {
159+ sharg(np->nfile.fname, fp);
160+ }
161+ first = 0;
162+ }
163+}
164+
165+
166+
167+static void
168+sharg(union node *arg, FILE *fp)
169+{
170+ char *p;
171+ struct nodelist *bqlist;
172+ int subtype;
173+
174+ if (arg->type != NARG) {
175+ printf("<node type %d>\n", arg->type);
176+ fflush(stdout);
177+ abort();
178+ }
179+ bqlist = arg->narg.backquote;
180+ for (p = arg->narg.text ; *p ; p++) {
181+ switch (*p) {
182+ case CTLESC:
183+ putc(*++p, fp);
184+ break;
185+ case CTLVAR:
186+ putc('$', fp);
187+ putc('{', fp);
188+ subtype = *++p;
189+ if (subtype == VSLENGTH)
190+ putc('#', fp);
191+
192+ while (*p != '=')
193+ putc(*p++, fp);
194+
195+ if (subtype & VSNUL)
196+ putc(':', fp);
197+
198+ switch (subtype & VSTYPE) {
199+ case VSNORMAL:
200+ putc('}', fp);
201+ break;
202+ case VSMINUS:
203+ putc('-', fp);
204+ break;
205+ case VSPLUS:
206+ putc('+', fp);
207+ break;
208+ case VSQUESTION:
209+ putc('?', fp);
210+ break;
211+ case VSASSIGN:
212+ putc('=', fp);
213+ break;
214+ case VSTRIMLEFT:
215+ putc('#', fp);
216+ break;
217+ case VSTRIMLEFTMAX:
218+ putc('#', fp);
219+ putc('#', fp);
220+ break;
221+ case VSTRIMRIGHT:
222+ putc('%', fp);
223+ break;
224+ case VSTRIMRIGHTMAX:
225+ putc('%', fp);
226+ putc('%', fp);
227+ break;
228+ case VSLENGTH:
229+ break;
230+ default:
231+ printf("<subtype %d>", subtype);
232+ }
233+ break;
234+ case CTLENDVAR:
235+ putc('}', fp);
236+ break;
237+ case CTLBACKQ:
238+ case CTLBACKQ|CTLQUOTE:
239+ putc('$', fp);
240+ putc('(', fp);
241+ shtree(bqlist->n, -1, NULL, fp);
242+ putc(')', fp);
243+ break;
244+ default:
245+ putc(*p, fp);
246+ break;
247+ }
248+ }
249+}
250+
251+
252+static void
253+indent(int amount, char *pfx, FILE *fp)
254+{
255+ int i;
256+
257+ for (i = 0 ; i < amount ; i++) {
258+ if (pfx && i == amount - 1)
259+ fputs(pfx, fp);
260+ putc('\t', fp);
261+ }
262+}
263+
264+
265+/*
266+ * Debugging stuff.
267+ */
268+
269+
270+static FILE *tracefile;
271+#if DEBUG >= 2
272+int debug = 1;
273+#else
274+int debug = 0;
275+#endif
276+
277+
278+void
279+trputc(int c)
280+{
281+ if (tracefile == NULL)
282+ return;
283+ putc(c, tracefile);
284+ if (c == '\n')
285+ fflush(tracefile);
286+}
287+
288+
289+void
290+sh_trace(const char *fmt, ...)
291+{
292+ va_list va;
293+ va_start(va, fmt);
294+ if (tracefile != NULL) {
295+ (void) vfprintf(tracefile, fmt, va);
296+ if (strchr(fmt, '\n'))
297+ (void) fflush(tracefile);
298+ }
299+ va_end(va);
300+}
301+
302+
303+void
304+trputs(const char *s)
305+{
306+ if (tracefile == NULL)
307+ return;
308+ fputs(s, tracefile);
309+ if (strchr(s, '\n'))
310+ fflush(tracefile);
311+}
312+
313+
314+static void
315+trstring(char *s)
316+{
317+ char *p;
318+ char c;
319+
320+ if (tracefile == NULL)
321+ return;
322+ putc('"', tracefile);
323+ for (p = s ; *p ; p++) {
324+ switch (*p) {
325+ case '\n': c = 'n'; goto backslash;
326+ case '\t': c = 't'; goto backslash;
327+ case '\r': c = 'r'; goto backslash;
328+ case '"': c = '"'; goto backslash;
329+ case '\\': c = '\\'; goto backslash;
330+ case CTLESC: c = 'e'; goto backslash;
331+ case CTLVAR: c = 'v'; goto backslash;
332+ case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
333+ case CTLBACKQ: c = 'q'; goto backslash;
334+ case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
335+backslash: putc('\\', tracefile);
336+ putc(c, tracefile);
337+ break;
338+ default:
339+ if (*p >= ' ' && *p <= '~')
340+ putc(*p, tracefile);
341+ else {
342+ putc('\\', tracefile);
343+ putc(*p >> 6 & 03, tracefile);
344+ putc(*p >> 3 & 07, tracefile);
345+ putc(*p & 07, tracefile);
346+ }
347+ break;
348+ }
349+ }
350+ putc('"', tracefile);
351+}
352+
353+
354+void
355+trargs(char **ap)
356+{
357+ if (tracefile == NULL)
358+ return;
359+ while (*ap) {
360+ trstring(*ap++);
361+ if (*ap)
362+ putc(' ', tracefile);
363+ else
364+ putc('\n', tracefile);
365+ }
366+ fflush(tracefile);
367+}
368+
369+
370+void
371+opentrace(void)
372+{
373+ char s[100];
374+ int flags;
375+
376+ if (!debug)
377+ return;
378+#ifdef not_this_way
379+ {
380+ char *p;
381+ if ((p = getenv("HOME")) == NULL) {
382+ if (geteuid() == 0)
383+ p = "/";
384+ else
385+ p = "/tmp";
386+ }
387+ strcpy(s, p);
388+ strcat(s, "/trace");
389+ }
390+#else
391+ strcpy(s, "./trace");
392+#endif /* not_this_way */
393+ if ((tracefile = fopen(s, "a")) == NULL) {
394+ fprintf(stderr, "Can't open %s: %s\n", s, strerror(errno));
395+ return;
396+ }
397+ if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0)
398+ fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
399+ fputs("\nTracing started.\n", tracefile);
400+ fflush(tracefile);
401+}
402+#endif /* DEBUG */
+41,
-0
1@@ -0,0 +1,41 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1995
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * Redistribution and use in source and binary forms, with or without
9+ * modification, are permitted provided that the following conditions
10+ * are met:
11+ * 1. Redistributions of source code must retain the above copyright
12+ * notice, this list of conditions and the following disclaimer.
13+ * 2. Redistributions in binary form must reproduce the above copyright
14+ * notice, this list of conditions and the following disclaimer in the
15+ * documentation and/or other materials provided with the distribution.
16+ * 3. Neither the name of the University nor the names of its contributors
17+ * may be used to endorse or promote products derived from this software
18+ * without specific prior written permission.
19+ *
20+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30+ * SUCH DAMAGE.
31+ */
32+
33+void showtree(union node *);
34+#ifdef DEBUG
35+void sh_trace(const char *, ...) __printflike(1, 2);
36+void trargs(char **);
37+void trputc(int);
38+void trputs(const char *);
39+void opentrace(void);
40+
41+extern int debug;
42+#endif
+652,
-0
1@@ -0,0 +1,652 @@
2+/* $NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $ */
3+
4+/*-
5+ * test(1); version 7-like -- author Erik Baalbergen
6+ * modified by Eric Gisin to be used as built-in.
7+ * modified by Arnold Robbins to add SVR3 compatibility
8+ * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
9+ * modified by J.T. Conklin for NetBSD.
10+ *
11+ * This program is in the Public Domain.
12+ */
13+/*
14+ * Important: This file is used both as a standalone program /bin/test and
15+ * as a builtin for /bin/sh (#define SHELL).
16+ */
17+
18+#ifndef SHELL
19+#ifndef __dead2
20+#define __dead2 __attribute__((__noreturn__))
21+#endif
22+#ifndef __printf0like
23+#define __printf0like(n, m) __attribute__((__format__(__printf__, n, m)))
24+#endif
25+#ifndef __nonstring
26+#if defined(__has_attribute)
27+#if __has_attribute(__nonstring__)
28+#define __nonstring __attribute__((__nonstring__))
29+#elif __has_attribute(nonstring)
30+#define __nonstring __attribute__((nonstring))
31+#endif
32+#endif
33+#endif
34+#ifndef __nonstring
35+#define __nonstring
36+#endif
37+#ifndef eaccess
38+#ifdef AT_EACCESS
39+#define eaccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_EACCESS)
40+#else
41+#define eaccess(path, mode) access((path), (mode))
42+#endif
43+#endif
44+#endif
45+
46+#include <sys/types.h>
47+#include <sys/stat.h>
48+
49+#include <ctype.h>
50+#include <err.h>
51+#include <errno.h>
52+#include <inttypes.h>
53+#include <stdarg.h>
54+#include <stdlib.h>
55+#include <string.h>
56+#include <unistd.h>
57+
58+#ifdef SHELL
59+#define main testcmd
60+#include "bltin.h"
61+#else
62+#include <locale.h>
63+
64+static void error(const char *, ...) __dead2 __printf0like(1, 2);
65+
66+static void
67+error(const char *msg, ...)
68+{
69+ va_list ap;
70+ va_start(ap, msg);
71+ verrx(2, msg, ap);
72+ /*NOTREACHED*/
73+ va_end(ap);
74+}
75+#endif
76+
77+/* test(1) accepts the following grammar:
78+ oexpr ::= aexpr | aexpr "-o" oexpr ;
79+ aexpr ::= nexpr | nexpr "-a" aexpr ;
80+ nexpr ::= primary | "!" primary
81+ primary ::= unary-operator operand
82+ | operand binary-operator operand
83+ | operand
84+ | "(" oexpr ")"
85+ ;
86+ unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
87+ "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
88+
89+ binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
90+ "-nt"|"-ot"|"-ef";
91+ operand ::= <any legal UNIX file name>
92+*/
93+
94+enum token_types {
95+ UNOP = 0x100,
96+ BINOP = 0x200,
97+ BUNOP = 0x300,
98+ BBINOP = 0x400,
99+ PAREN = 0x500
100+};
101+
102+enum token {
103+ EOI,
104+ OPERAND,
105+ FILRD = UNOP + 1,
106+ FILWR,
107+ FILEX,
108+ FILEXIST,
109+ FILREG,
110+ FILDIR,
111+ FILCDEV,
112+ FILBDEV,
113+ FILFIFO,
114+ FILSOCK,
115+ FILSYM,
116+ FILGZ,
117+ FILTT,
118+ FILSUID,
119+ FILSGID,
120+ FILSTCK,
121+ STREZ,
122+ STRNZ,
123+ FILUID,
124+ FILGID,
125+ FILNT = BINOP + 1,
126+ FILOT,
127+ FILEQ,
128+ STREQ,
129+ STRNE,
130+ STRLT,
131+ STRGT,
132+ INTEQ,
133+ INTNE,
134+ INTGE,
135+ INTGT,
136+ INTLE,
137+ INTLT,
138+ UNOT = BUNOP + 1,
139+ BAND = BBINOP + 1,
140+ BOR,
141+ LPAREN = PAREN + 1,
142+ RPAREN
143+};
144+
145+#define TOKEN_TYPE(token) ((token) & 0xff00)
146+
147+static const struct t_op {
148+ char op_text[2] __nonstring;
149+ short op_num;
150+} ops1[] = {
151+ {"=", STREQ},
152+ {"<", STRLT},
153+ {">", STRGT},
154+ {"!", UNOT},
155+ {"(", LPAREN},
156+ {")", RPAREN},
157+}, opsm1[] = {
158+ {"r", FILRD},
159+ {"w", FILWR},
160+ {"x", FILEX},
161+ {"e", FILEXIST},
162+ {"f", FILREG},
163+ {"d", FILDIR},
164+ {"c", FILCDEV},
165+ {"b", FILBDEV},
166+ {"p", FILFIFO},
167+ {"u", FILSUID},
168+ {"g", FILSGID},
169+ {"k", FILSTCK},
170+ {"s", FILGZ},
171+ {"t", FILTT},
172+ {"z", STREZ},
173+ {"n", STRNZ},
174+ {"h", FILSYM}, /* for backwards compat */
175+ {"O", FILUID},
176+ {"G", FILGID},
177+ {"L", FILSYM},
178+ {"S", FILSOCK},
179+ {"a", BAND},
180+ {"o", BOR},
181+}, ops2[] = {
182+ {"==", STREQ},
183+ {"!=", STRNE},
184+}, opsm2[] = {
185+ {"eq", INTEQ},
186+ {"ne", INTNE},
187+ {"ge", INTGE},
188+ {"gt", INTGT},
189+ {"le", INTLE},
190+ {"lt", INTLT},
191+ {"nt", FILNT},
192+ {"ot", FILOT},
193+ {"ef", FILEQ},
194+};
195+
196+static int nargc;
197+static char **t_wp;
198+static int parenlevel;
199+
200+static int aexpr(enum token);
201+static int binop(enum token);
202+static int equalf(const char *, const char *);
203+static int filstat(char *, enum token);
204+static int getn(const char *);
205+static intmax_t getq(const char *);
206+static int intcmp(const char *, const char *);
207+static int isunopoperand(void);
208+static int islparenoperand(void);
209+static int isrparenoperand(void);
210+static int newerf(const char *, const char *);
211+static int nexpr(enum token);
212+static int oexpr(enum token);
213+static int olderf(const char *, const char *);
214+static int primary(enum token);
215+static void syntax(const char *, const char *);
216+static enum token t_lex(char *);
217+
218+int
219+main(int argc, char **argv)
220+{
221+ int res;
222+ char *p;
223+
224+ if ((p = strrchr(argv[0], '/')) == NULL)
225+ p = argv[0];
226+ else
227+ p++;
228+ if (strcmp(p, "[") == 0) {
229+ if (strcmp(argv[--argc], "]") != 0)
230+ error("missing ']'");
231+ argv[argc] = NULL;
232+ }
233+
234+ /* no expression => false */
235+ if (--argc <= 0)
236+ return 1;
237+
238+#ifndef SHELL
239+ (void)setlocale(LC_CTYPE, "");
240+#endif
241+ nargc = argc;
242+ t_wp = &argv[1];
243+ parenlevel = 0;
244+ if (nargc == 4 && strcmp(*t_wp, "!") == 0) {
245+ /* Things like ! "" -o x do not fit in the normal grammar. */
246+ --nargc;
247+ ++t_wp;
248+ res = oexpr(t_lex(*t_wp));
249+ } else
250+ res = !oexpr(t_lex(*t_wp));
251+
252+ if (--nargc > 0)
253+ syntax(*t_wp, "unexpected operator");
254+
255+ return res;
256+}
257+
258+static void
259+syntax(const char *op, const char *msg)
260+{
261+
262+ if (op && *op)
263+ error("%s: %s", op, msg);
264+ else
265+ error("%s", msg);
266+}
267+
268+static int
269+oexpr(enum token n)
270+{
271+ int res;
272+
273+ res = aexpr(n);
274+ if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BOR)
275+ return oexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ||
276+ res;
277+ t_wp--;
278+ nargc++;
279+ return res;
280+}
281+
282+static int
283+aexpr(enum token n)
284+{
285+ int res;
286+
287+ res = nexpr(n);
288+ if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BAND)
289+ return aexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) &&
290+ res;
291+ t_wp--;
292+ nargc++;
293+ return res;
294+}
295+
296+static int
297+nexpr(enum token n)
298+{
299+ if (n == UNOT)
300+ return !nexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL));
301+ return primary(n);
302+}
303+
304+static int
305+primary(enum token n)
306+{
307+ enum token nn;
308+ int res;
309+
310+ if (n == EOI)
311+ return 0; /* missing expression */
312+ if (n == LPAREN) {
313+ parenlevel++;
314+ if ((nn = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ==
315+ RPAREN) {
316+ parenlevel--;
317+ return 0; /* missing expression */
318+ }
319+ res = oexpr(nn);
320+ if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) != RPAREN)
321+ syntax(NULL, "closing paren expected");
322+ parenlevel--;
323+ return res;
324+ }
325+ if (TOKEN_TYPE(n) == UNOP) {
326+ /* unary expression */
327+ if (--nargc == 0)
328+ syntax(NULL, "argument expected"); /* impossible */
329+ switch (n) {
330+ case STREZ:
331+ return strlen(*++t_wp) == 0;
332+ case STRNZ:
333+ return strlen(*++t_wp) != 0;
334+ case FILTT:
335+ return isatty(getn(*++t_wp));
336+ default:
337+ return filstat(*++t_wp, n);
338+ }
339+ }
340+
341+ nn = t_lex(nargc > 0 ? t_wp[1] : NULL);
342+ if (TOKEN_TYPE(nn) == BINOP)
343+ return binop(nn);
344+
345+ return strlen(*t_wp) > 0;
346+}
347+
348+static int
349+binop(enum token n)
350+{
351+ const char *opnd1, *op, *opnd2;
352+
353+ opnd1 = *t_wp;
354+ op = nargc > 0 ? (--nargc, *++t_wp) : NULL;
355+
356+ if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL)
357+ syntax(op, "argument expected");
358+
359+ switch (n) {
360+ case STREQ:
361+ return strcmp(opnd1, opnd2) == 0;
362+ case STRNE:
363+ return strcmp(opnd1, opnd2) != 0;
364+ case STRLT:
365+ return strcmp(opnd1, opnd2) < 0;
366+ case STRGT:
367+ return strcmp(opnd1, opnd2) > 0;
368+ case INTEQ:
369+ return intcmp(opnd1, opnd2) == 0;
370+ case INTNE:
371+ return intcmp(opnd1, opnd2) != 0;
372+ case INTGE:
373+ return intcmp(opnd1, opnd2) >= 0;
374+ case INTGT:
375+ return intcmp(opnd1, opnd2) > 0;
376+ case INTLE:
377+ return intcmp(opnd1, opnd2) <= 0;
378+ case INTLT:
379+ return intcmp(opnd1, opnd2) < 0;
380+ case FILNT:
381+ return newerf (opnd1, opnd2);
382+ case FILOT:
383+ return olderf (opnd1, opnd2);
384+ case FILEQ:
385+ return equalf (opnd1, opnd2);
386+ default:
387+ abort();
388+ /* NOTREACHED */
389+ }
390+}
391+
392+static int
393+filstat(char *nm, enum token mode)
394+{
395+ struct stat s;
396+
397+ if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
398+ return 0;
399+
400+ switch (mode) {
401+ case FILRD:
402+ return (eaccess(nm, R_OK) == 0);
403+ case FILWR:
404+ return (eaccess(nm, W_OK) == 0);
405+ case FILEX:
406+ /* XXX work around eaccess(2) false positives for superuser */
407+ if (eaccess(nm, X_OK) != 0)
408+ return 0;
409+ if (S_ISDIR(s.st_mode) || geteuid() != 0)
410+ return 1;
411+ return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
412+ case FILEXIST:
413+ return (eaccess(nm, F_OK) == 0);
414+ case FILREG:
415+ return S_ISREG(s.st_mode);
416+ case FILDIR:
417+ return S_ISDIR(s.st_mode);
418+ case FILCDEV:
419+ return S_ISCHR(s.st_mode);
420+ case FILBDEV:
421+ return S_ISBLK(s.st_mode);
422+ case FILFIFO:
423+ return S_ISFIFO(s.st_mode);
424+ case FILSOCK:
425+ return S_ISSOCK(s.st_mode);
426+ case FILSYM:
427+ return S_ISLNK(s.st_mode);
428+ case FILSUID:
429+ return (s.st_mode & S_ISUID) != 0;
430+ case FILSGID:
431+ return (s.st_mode & S_ISGID) != 0;
432+ case FILSTCK:
433+ return (s.st_mode & S_ISVTX) != 0;
434+ case FILGZ:
435+ return s.st_size > (off_t)0;
436+ case FILUID:
437+ return s.st_uid == geteuid();
438+ case FILGID:
439+ return s.st_gid == getegid();
440+ default:
441+ return 1;
442+ }
443+}
444+
445+static int
446+find_op_1char(const struct t_op *op, const struct t_op *end, const char *s)
447+{
448+ char c;
449+
450+ c = s[0];
451+ while (op != end) {
452+ if (c == *op->op_text)
453+ return op->op_num;
454+ op++;
455+ }
456+ return OPERAND;
457+}
458+
459+static int
460+find_op_2char(const struct t_op *op, const struct t_op *end, const char *s)
461+{
462+ while (op != end) {
463+ if (s[0] == op->op_text[0] && s[1] == op->op_text[1])
464+ return op->op_num;
465+ op++;
466+ }
467+ return OPERAND;
468+}
469+
470+static int
471+find_op(const char *s)
472+{
473+ if (s[0] == '\0')
474+ return OPERAND;
475+ else if (s[1] == '\0')
476+ return find_op_1char(ops1, (&ops1)[1], s);
477+ else if (s[2] == '\0')
478+ return s[0] == '-' ? find_op_1char(opsm1, (&opsm1)[1], s + 1) :
479+ find_op_2char(ops2, (&ops2)[1], s);
480+ else if (s[3] == '\0')
481+ return s[0] == '-' ? find_op_2char(opsm2, (&opsm2)[1], s + 1) :
482+ OPERAND;
483+ else
484+ return OPERAND;
485+}
486+
487+static enum token
488+t_lex(char *s)
489+{
490+ int num;
491+
492+ if (s == NULL) {
493+ return EOI;
494+ }
495+ num = find_op(s);
496+ if (((TOKEN_TYPE(num) == UNOP || TOKEN_TYPE(num) == BUNOP)
497+ && isunopoperand()) ||
498+ (num == LPAREN && islparenoperand()) ||
499+ (num == RPAREN && isrparenoperand()))
500+ return OPERAND;
501+ return num;
502+}
503+
504+static int
505+isunopoperand(void)
506+{
507+ char *s;
508+ char *t;
509+ int num;
510+
511+ if (nargc == 1)
512+ return 1;
513+ s = *(t_wp + 1);
514+ if (nargc == 2)
515+ return parenlevel == 1 && strcmp(s, ")") == 0;
516+ t = *(t_wp + 2);
517+ num = find_op(s);
518+ return TOKEN_TYPE(num) == BINOP &&
519+ (parenlevel == 0 || t[0] != ')' || t[1] != '\0');
520+}
521+
522+static int
523+islparenoperand(void)
524+{
525+ char *s;
526+ int num;
527+
528+ if (nargc == 1)
529+ return 1;
530+ s = *(t_wp + 1);
531+ if (nargc == 2)
532+ return parenlevel == 1 && strcmp(s, ")") == 0;
533+ if (nargc != 3)
534+ return 0;
535+ num = find_op(s);
536+ return TOKEN_TYPE(num) == BINOP;
537+}
538+
539+static int
540+isrparenoperand(void)
541+{
542+ char *s;
543+
544+ if (nargc == 1)
545+ return 0;
546+ s = *(t_wp + 1);
547+ if (nargc == 2)
548+ return parenlevel == 1 && strcmp(s, ")") == 0;
549+ return 0;
550+}
551+
552+/* atoi with error detection */
553+static int
554+getn(const char *s)
555+{
556+ char *p;
557+ long r;
558+
559+ errno = 0;
560+ r = strtol(s, &p, 10);
561+
562+ if (s == p)
563+ error("%s: bad number", s);
564+
565+ if (errno != 0)
566+ error((errno == EINVAL) ? "%s: bad number" :
567+ "%s: out of range", s);
568+
569+ while (isspace((unsigned char)*p))
570+ p++;
571+
572+ if (*p)
573+ error("%s: bad number", s);
574+
575+ return (int) r;
576+}
577+
578+/* atoi with error detection and 64 bit range */
579+static intmax_t
580+getq(const char *s)
581+{
582+ char *p;
583+ intmax_t r;
584+
585+ errno = 0;
586+ r = strtoimax(s, &p, 10);
587+
588+ if (s == p)
589+ error("%s: bad number", s);
590+
591+ if (errno != 0)
592+ error((errno == EINVAL) ? "%s: bad number" :
593+ "%s: out of range", s);
594+
595+ while (isspace((unsigned char)*p))
596+ p++;
597+
598+ if (*p)
599+ error("%s: bad number", s);
600+
601+ return r;
602+}
603+
604+static int
605+intcmp (const char *s1, const char *s2)
606+{
607+ intmax_t q1, q2;
608+
609+
610+ q1 = getq(s1);
611+ q2 = getq(s2);
612+
613+ if (q1 > q2)
614+ return 1;
615+
616+ if (q1 < q2)
617+ return -1;
618+
619+ return 0;
620+}
621+
622+static int
623+newerf (const char *f1, const char *f2)
624+{
625+ struct stat b1, b2;
626+
627+ if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0)
628+ return 0;
629+
630+ if (b1.st_mtim.tv_sec > b2.st_mtim.tv_sec)
631+ return 1;
632+ if (b1.st_mtim.tv_sec < b2.st_mtim.tv_sec)
633+ return 0;
634+
635+ return (b1.st_mtim.tv_nsec > b2.st_mtim.tv_nsec);
636+}
637+
638+static int
639+olderf (const char *f1, const char *f2)
640+{
641+ return (newerf(f2, f1));
642+}
643+
644+static int
645+equalf (const char *f1, const char *f2)
646+{
647+ struct stat b1, b2;
648+
649+ return (stat (f1, &b1) == 0 &&
650+ stat (f2, &b2) == 0 &&
651+ b1.st_dev == b2.st_dev &&
652+ b1.st_ino == b2.st_ino);
653+}
+546,
-0
1@@ -0,0 +1,546 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#include "shell.h"
37+#include "main.h"
38+#include "nodes.h" /* for other headers */
39+#include "eval.h"
40+#include "jobs.h"
41+#include "show.h"
42+#include "options.h"
43+#include "syntax.h"
44+#include "output.h"
45+#include "memalloc.h"
46+#include "error.h"
47+#include "trap.h"
48+#include "mystring.h"
49+#include "builtins.h"
50+#ifndef NO_HISTORY
51+#include "myhistedit.h"
52+#endif
53+
54+#include <signal.h>
55+#include <stdlib.h>
56+#include <unistd.h>
57+
58+/*
59+ * Sigmode records the current value of the signal handlers for the various
60+ * modes. A value of zero means that the current handler is not known.
61+ * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
62+ */
63+
64+#define S_DFL 1 /* default signal handling (SIG_DFL) */
65+#define S_CATCH 2 /* signal is caught */
66+#define S_IGN 3 /* signal is ignored (SIG_IGN) */
67+#define S_HARD_IGN 4 /* signal is ignored permanently */
68+#define S_RESET 5 /* temporary - to reset a hard ignored sig */
69+
70+
71+static char sigmode[NSIG]; /* current value of signal */
72+volatile sig_atomic_t pendingsig; /* indicates some signal received */
73+volatile sig_atomic_t pendingsig_waitcmd; /* indicates wait builtin should be interrupted */
74+static int in_dotrap; /* do we execute in a trap handler? */
75+static char *volatile trap[NSIG]; /* trap handler commands */
76+static volatile sig_atomic_t gotsig[NSIG];
77+ /* indicates specified signal received */
78+static int ignore_sigchld; /* Used while handling SIGCHLD traps. */
79+static int last_trapsig;
80+
81+static int exiting; /* exitshell() has been called */
82+static int exiting_exitstatus; /* value passed to exitshell() */
83+
84+static int getsigaction(int, sig_t *);
85+
86+
87+/*
88+ * Map a string to a signal number.
89+ *
90+ * Note: the signal number may exceed NSIG.
91+ */
92+static int
93+sigstring_to_signum(char *sig)
94+{
95+
96+ if (is_number(sig)) {
97+ int signo;
98+
99+ signo = atoi(sig);
100+ return ((signo >= 0 && signo < NSIG) ? signo : (-1));
101+ } else if (strcasecmp(sig, "EXIT") == 0) {
102+ return (0);
103+ } else {
104+ int n;
105+
106+ if (strncasecmp(sig, "SIG", 3) == 0)
107+ sig += 3;
108+ for (n = 1; n < sys_nsig; n++)
109+ if (sys_signame[n] &&
110+ strcasecmp(sys_signame[n], sig) == 0)
111+ return (n);
112+ }
113+ return (-1);
114+}
115+
116+
117+/*
118+ * Print a list of valid signal names.
119+ */
120+static void
121+printsignals(void)
122+{
123+ int n, outlen;
124+
125+ outlen = 0;
126+ for (n = 1; n < sys_nsig; n++) {
127+ if (sys_signame[n]) {
128+ out1fmt("%s", sys_signame[n]);
129+ outlen += strlen(sys_signame[n]);
130+ } else {
131+ out1fmt("%d", n);
132+ outlen += 3; /* good enough */
133+ }
134+ ++outlen;
135+ if (outlen > 71 || n == sys_nsig - 1) {
136+ out1str("\n");
137+ outlen = 0;
138+ } else {
139+ out1c(' ');
140+ }
141+ }
142+}
143+
144+
145+/*
146+ * The trap builtin.
147+ */
148+int
149+trapcmd(int argc __unused, char **argv)
150+{
151+ char *action;
152+ int signo;
153+ int errors = 0;
154+ int i;
155+
156+ while ((i = nextopt("l")) != '\0') {
157+ switch (i) {
158+ case 'l':
159+ printsignals();
160+ return (0);
161+ }
162+ }
163+ argv = argptr;
164+
165+ if (*argv == NULL) {
166+ for (signo = 0 ; signo < sys_nsig ; signo++) {
167+ if (signo < NSIG && trap[signo] != NULL) {
168+ out1str("trap -- ");
169+ out1qstr(trap[signo]);
170+ if (signo == 0) {
171+ out1str(" EXIT\n");
172+ } else if (sys_signame[signo]) {
173+ out1fmt(" %s\n", sys_signame[signo]);
174+ } else {
175+ out1fmt(" %d\n", signo);
176+ }
177+ }
178+ }
179+ return 0;
180+ }
181+ action = NULL;
182+ if (*argv && !is_number(*argv)) {
183+ if (strcmp(*argv, "-") == 0)
184+ argv++;
185+ else {
186+ action = *argv;
187+ argv++;
188+ }
189+ }
190+ for (; *argv; argv++) {
191+ if ((signo = sigstring_to_signum(*argv)) == -1) {
192+ warning("bad signal %s", *argv);
193+ errors = 1;
194+ continue;
195+ }
196+ INTOFF;
197+ if (action)
198+ action = savestr(action);
199+ if (trap[signo])
200+ ckfree(trap[signo]);
201+ trap[signo] = action;
202+ if (signo != 0)
203+ setsignal(signo);
204+ INTON;
205+ }
206+ return errors;
207+}
208+
209+
210+/*
211+ * Clear traps on a fork.
212+ */
213+void
214+clear_traps(void)
215+{
216+ char *volatile *tp;
217+
218+ for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) {
219+ if (*tp && **tp) { /* trap not NULL or SIG_IGN */
220+ INTOFF;
221+ ckfree(*tp);
222+ *tp = NULL;
223+ if (tp != &trap[0])
224+ setsignal(tp - trap);
225+ INTON;
226+ }
227+ }
228+}
229+
230+
231+/*
232+ * Check if we have any traps enabled.
233+ */
234+int
235+have_traps(void)
236+{
237+ char *volatile *tp;
238+
239+ for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) {
240+ if (*tp && **tp) /* trap not NULL or SIG_IGN */
241+ return 1;
242+ }
243+ return 0;
244+}
245+
246+/*
247+ * Set the signal handler for the specified signal. The routine figures
248+ * out what it should be set to.
249+ */
250+void
251+setsignal(int signo)
252+{
253+ int action;
254+ sig_t sigact = SIG_DFL;
255+ struct sigaction sa;
256+ char *t;
257+
258+ if ((t = trap[signo]) == NULL)
259+ action = S_DFL;
260+ else if (*t != '\0')
261+ action = S_CATCH;
262+ else
263+ action = S_IGN;
264+ if (action == S_DFL) {
265+ switch (signo) {
266+ case SIGINT:
267+ action = S_CATCH;
268+ break;
269+ case SIGQUIT:
270+#ifdef DEBUG
271+ if (debug)
272+ break;
273+#endif
274+ action = S_CATCH;
275+ break;
276+ case SIGTERM:
277+ if (rootshell && iflag)
278+ action = S_IGN;
279+ break;
280+#if JOBS
281+ case SIGTSTP:
282+ case SIGTTOU:
283+ if (rootshell && mflag)
284+ action = S_IGN;
285+ break;
286+#endif
287+ }
288+ }
289+
290+ t = &sigmode[signo];
291+ if (*t == 0) {
292+ /*
293+ * current setting unknown
294+ */
295+ if (!getsigaction(signo, &sigact)) {
296+ /*
297+ * Pretend it worked; maybe we should give a warning
298+ * here, but other shells don't. We don't alter
299+ * sigmode, so that we retry every time.
300+ */
301+ return;
302+ }
303+ if (sigact == SIG_IGN) {
304+ if (mflag && (signo == SIGTSTP ||
305+ signo == SIGTTIN || signo == SIGTTOU)) {
306+ *t = S_IGN; /* don't hard ignore these */
307+ } else
308+ *t = S_HARD_IGN;
309+ } else {
310+ *t = S_RESET; /* force to be set */
311+ }
312+ }
313+ if (*t == S_HARD_IGN || *t == action)
314+ return;
315+ switch (action) {
316+ case S_DFL: sigact = SIG_DFL; break;
317+ case S_CATCH: sigact = onsig; break;
318+ case S_IGN: sigact = SIG_IGN; break;
319+ }
320+ *t = action;
321+ sa.sa_handler = sigact;
322+ sa.sa_flags = 0;
323+ sigemptyset(&sa.sa_mask);
324+ sigaction(signo, &sa, NULL);
325+}
326+
327+
328+/*
329+ * Return the current setting for sig w/o changing it.
330+ */
331+static int
332+getsigaction(int signo, sig_t *sigact)
333+{
334+ struct sigaction sa;
335+
336+ if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
337+ return 0;
338+ *sigact = (sig_t) sa.sa_handler;
339+ return 1;
340+}
341+
342+
343+/*
344+ * Ignore a signal.
345+ */
346+void
347+ignoresig(int signo)
348+{
349+
350+ if (sigmode[signo] == 0)
351+ setsignal(signo);
352+ if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) {
353+ signal(signo, SIG_IGN);
354+ sigmode[signo] = S_IGN;
355+ }
356+}
357+
358+
359+int
360+issigchldtrapped(void)
361+{
362+
363+ return (trap[SIGCHLD] != NULL && *trap[SIGCHLD] != '\0');
364+}
365+
366+
367+/*
368+ * Signal handler.
369+ */
370+void
371+onsig(int signo)
372+{
373+
374+ if (signo == SIGINT && trap[SIGINT] == NULL) {
375+ if (suppressint)
376+ SET_PENDING_INT;
377+ else
378+ onint();
379+ return;
380+ }
381+
382+ /* If we are currently in a wait builtin, prepare to break it */
383+ if (signo == SIGINT || signo == SIGQUIT)
384+ pendingsig_waitcmd = signo;
385+
386+ if (trap[signo] != NULL && trap[signo][0] != '\0' &&
387+ (signo != SIGCHLD || !ignore_sigchld)) {
388+ gotsig[signo] = 1;
389+ pendingsig = signo;
390+ pendingsig_waitcmd = signo;
391+ }
392+}
393+
394+
395+/*
396+ * Called to execute a trap. Perhaps we should avoid entering new trap
397+ * handlers while we are executing a trap handler.
398+ */
399+void
400+dotrap(void)
401+{
402+ struct stackmark smark;
403+ int i;
404+ int savestatus, prev_evalskip, prev_skipcount;
405+
406+ in_dotrap++;
407+ for (;;) {
408+ pendingsig = 0;
409+ pendingsig_waitcmd = 0;
410+ for (i = 1; i < NSIG; i++) {
411+ if (gotsig[i]) {
412+ gotsig[i] = 0;
413+ if (trap[i]) {
414+ /*
415+ * Ignore SIGCHLD to avoid infinite
416+ * recursion if the trap action does
417+ * a fork.
418+ */
419+ if (i == SIGCHLD)
420+ ignore_sigchld++;
421+
422+ /*
423+ * Backup current evalskip
424+ * state and reset it before
425+ * executing a trap, so that the
426+ * trap is not disturbed by an
427+ * ongoing break/continue/return
428+ * statement.
429+ */
430+ prev_evalskip = evalskip;
431+ prev_skipcount = skipcount;
432+ evalskip = 0;
433+
434+ last_trapsig = i;
435+ savestatus = exitstatus;
436+ setstackmark(&smark);
437+ evalstring(stsavestr(trap[i]), 0);
438+ popstackmark(&smark);
439+
440+ /*
441+ * If such a command was not
442+ * already in progress, allow a
443+ * break/continue/return in the
444+ * trap action to have an effect
445+ * outside of it.
446+ */
447+ if (evalskip == 0 ||
448+ prev_evalskip != 0) {
449+ evalskip = prev_evalskip;
450+ skipcount = prev_skipcount;
451+ exitstatus = savestatus;
452+ }
453+
454+ if (i == SIGCHLD)
455+ ignore_sigchld--;
456+ }
457+ break;
458+ }
459+ }
460+ if (i >= NSIG)
461+ break;
462+ }
463+ in_dotrap--;
464+}
465+
466+
467+void
468+trap_init(void)
469+{
470+ setsignal(SIGINT);
471+ setsignal(SIGQUIT);
472+}
473+
474+
475+/*
476+ * Controls whether the shell is interactive or not based on iflag.
477+ */
478+void
479+setinteractive(void)
480+{
481+ setsignal(SIGTERM);
482+}
483+
484+
485+/*
486+ * Called to exit the shell.
487+ */
488+void
489+exitshell(int status)
490+{
491+ TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
492+ exiting = 1;
493+ exiting_exitstatus = status;
494+ exitshell_savedstatus();
495+}
496+
497+void
498+exitshell_savedstatus(void)
499+{
500+ struct jmploc loc1, loc2;
501+ char *p;
502+ int sig = 0;
503+ sigset_t sigs;
504+
505+ if (!exiting) {
506+ if (in_dotrap && last_trapsig) {
507+ sig = last_trapsig;
508+ exiting_exitstatus = sig + 128;
509+ } else
510+ exiting_exitstatus = oexitstatus;
511+ }
512+ exitstatus = oexitstatus = exiting_exitstatus;
513+ if (!setjmp(loc1.loc)) {
514+ handler = &loc1;
515+ if ((p = trap[0]) != NULL && *p != '\0') {
516+ /*
517+ * Reset evalskip, or the trap on EXIT could be
518+ * interrupted if the last command was a "return".
519+ */
520+ evalskip = 0;
521+ trap[0] = NULL;
522+ FORCEINTON;
523+ evalstring(p, 0);
524+ }
525+ }
526+ if (!setjmp(loc2.loc)) {
527+ handler = &loc2; /* probably unnecessary */
528+ FORCEINTON;
529+ flushall();
530+#if JOBS
531+ setjobctl(0);
532+#endif
533+#ifndef NO_HISTORY
534+ histsave();
535+#endif
536+ }
537+ if (sig != 0 && sig != SIGSTOP && sig != SIGTSTP && sig != SIGTTIN &&
538+ sig != SIGTTOU) {
539+ signal(sig, SIG_DFL);
540+ sigemptyset(&sigs);
541+ sigaddset(&sigs, sig);
542+ sigprocmask(SIG_UNBLOCK, &sigs, NULL);
543+ kill(getpid(), sig);
544+ /* If the default action is to ignore, fall back to _exit(). */
545+ }
546+ _exit(exiting_exitstatus);
547+}
+48,
-0
1@@ -0,0 +1,48 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+extern volatile sig_atomic_t pendingsig;
37+extern volatile sig_atomic_t pendingsig_waitcmd;
38+
39+void clear_traps(void);
40+int have_traps(void);
41+void setsignal(int);
42+void ignoresig(int);
43+int issigchldtrapped(void);
44+void onsig(int);
45+void dotrap(void);
46+void trap_init(void);
47+void setinteractive(void);
48+void exitshell(int) __dead2;
49+void exitshell_savedstatus(void) __dead2;
+961,
-0
1@@ -0,0 +1,961 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+#include "shell.h"
37+#include "output.h"
38+#include "expand.h"
39+#include "nodes.h" /* for other headers */
40+#include "eval.h" /* defines cmdenviron */
41+#include "exec.h"
42+#include "syntax.h"
43+#include "options.h"
44+#include "mail.h"
45+#include "var.h"
46+#include "memalloc.h"
47+#include "error.h"
48+#include "mystring.h"
49+#include "parser.h"
50+#include "builtins.h"
51+#ifndef NO_HISTORY
52+#include "myhistedit.h"
53+#endif
54+
55+#include <langinfo.h>
56+#include <locale.h>
57+#include <paths.h>
58+#include <stdlib.h>
59+#include <unistd.h>
60+
61+/*
62+ * Shell variables.
63+ */
64+
65+#ifndef VTABSIZE
66+#define VTABSIZE 39
67+#endif
68+
69+
70+struct varinit {
71+ struct var *var;
72+ int flags;
73+ const char *text;
74+ void (*func)(const char *);
75+};
76+
77+
78+#ifndef NO_HISTORY
79+struct var vhistsize;
80+struct var vterm;
81+#endif
82+struct var vifs;
83+struct var vmail;
84+struct var vmpath;
85+struct var vpath;
86+struct var vps1;
87+struct var vps2;
88+struct var vps4;
89+static struct var voptind;
90+struct var vdisvfork;
91+
92+struct localvar *localvars;
93+int forcelocal;
94+
95+static const struct varinit varinit[] = {
96+#ifndef NO_HISTORY
97+ { &vhistsize, VUNSET, "HISTSIZE=",
98+ sethistsize },
99+#endif
100+ { &vifs, 0, "IFS= \t\n",
101+ NULL },
102+ { &vmail, VUNSET, "MAIL=",
103+ NULL },
104+ { &vmpath, VUNSET, "MAILPATH=",
105+ NULL },
106+ { &vpath, 0, "PATH=" _PATH_DEFPATH,
107+ changepath },
108+ /*
109+ * vps1 depends on uid
110+ */
111+ { &vps2, 0, "PS2=> ",
112+ NULL },
113+ { &vps4, 0, "PS4=+ ",
114+ NULL },
115+#ifndef NO_HISTORY
116+ { &vterm, VUNSET, "TERM=",
117+ setterm },
118+#endif
119+ { &voptind, 0, "OPTIND=1",
120+ getoptsreset },
121+ { &vdisvfork, VUNSET, "SH_DISABLE_VFORK=",
122+ NULL },
123+ { NULL, 0, NULL,
124+ NULL }
125+};
126+
127+static struct var *vartab[VTABSIZE];
128+
129+static const char *const locale_names[7] = {
130+ "LC_COLLATE", "LC_CTYPE", "LC_MONETARY",
131+ "LC_NUMERIC", "LC_TIME", "LC_MESSAGES", NULL
132+};
133+static const int locale_categories[7] = {
134+ LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES, 0
135+};
136+
137+static int varequal(const char *, const char *);
138+static struct var *find_var(const char *, struct var ***, int *);
139+static int localevar(const char *);
140+static void setvareq_const(const char *s, int flags);
141+
142+extern char **environ;
143+
144+/*
145+ * This routine initializes the builtin variables and imports the environment.
146+ * It is called when the shell is initialized.
147+ */
148+
149+void
150+initvar(void)
151+{
152+ char ppid[20];
153+ const struct varinit *ip;
154+ struct var *vp;
155+ struct var **vpp;
156+ char **envp;
157+
158+ for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
159+ if (find_var(ip->text, &vpp, &vp->name_len) != NULL)
160+ continue;
161+ vp->next = *vpp;
162+ *vpp = vp;
163+ vp->text = __DECONST(char *, ip->text);
164+ vp->flags = ip->flags | VSTRFIXED | VTEXTFIXED;
165+ vp->func = ip->func;
166+ }
167+ /*
168+ * PS1 depends on uid
169+ */
170+ if (find_var("PS1", &vpp, &vps1.name_len) == NULL) {
171+ vps1.next = *vpp;
172+ *vpp = &vps1;
173+ vps1.text = __DECONST(char *, geteuid() ? "PS1=$ " : "PS1=# ");
174+ vps1.flags = VSTRFIXED|VTEXTFIXED;
175+ }
176+ fmtstr(ppid, sizeof(ppid), "%d", (int)getppid());
177+ setvarsafe("PPID", ppid, 0);
178+ for (envp = environ ; *envp ; envp++) {
179+ if (strchr(*envp, '=')) {
180+ setvareq(*envp, VEXPORT|VTEXTFIXED);
181+ }
182+ }
183+ setvareq_const("OPTIND=1", 0);
184+ setvareq_const("IFS= \t\n", 0);
185+}
186+
187+/*
188+ * Safe version of setvar, returns 1 on success 0 on failure.
189+ */
190+
191+int
192+setvarsafe(const char *name, const char *val, int flags)
193+{
194+ struct jmploc jmploc;
195+ struct jmploc *const savehandler = handler;
196+ int err = 0;
197+ int inton;
198+
199+ inton = is_int_on();
200+ if (setjmp(jmploc.loc))
201+ err = 1;
202+ else {
203+ handler = &jmploc;
204+ setvar(name, val, flags);
205+ }
206+ handler = savehandler;
207+ SETINTON(inton);
208+ return err;
209+}
210+
211+/*
212+ * Set the value of a variable. The flags argument is stored with the
213+ * flags of the variable. If val is NULL, the variable is unset.
214+ */
215+
216+void
217+setvar(const char *name, const char *val, int flags)
218+{
219+ const char *p;
220+ size_t len;
221+ size_t namelen;
222+ size_t vallen;
223+ char *nameeq;
224+ int isbad;
225+
226+ isbad = 0;
227+ p = name;
228+ if (! is_name(*p))
229+ isbad = 1;
230+ p++;
231+ for (;;) {
232+ if (! is_in_name(*p)) {
233+ if (*p == '\0' || *p == '=')
234+ break;
235+ isbad = 1;
236+ }
237+ p++;
238+ }
239+ namelen = p - name;
240+ if (isbad)
241+ error("%.*s: bad variable name", (int)namelen, name);
242+ len = namelen + 2; /* 2 is space for '=' and '\0' */
243+ if (val == NULL) {
244+ flags |= VUNSET;
245+ vallen = 0;
246+ } else {
247+ vallen = strlen(val);
248+ len += vallen;
249+ }
250+ INTOFF;
251+ nameeq = ckmalloc(len);
252+ memcpy(nameeq, name, namelen);
253+ nameeq[namelen] = '=';
254+ if (val)
255+ memcpy(nameeq + namelen + 1, val, vallen + 1);
256+ else
257+ nameeq[namelen + 1] = '\0';
258+ setvareq(nameeq, flags);
259+ INTON;
260+}
261+
262+static int
263+localevar(const char *s)
264+{
265+ const char *const *ss;
266+
267+ if (*s != 'L')
268+ return 0;
269+ if (varequal(s + 1, "ANG"))
270+ return 1;
271+ if (strncmp(s + 1, "C_", 2) != 0)
272+ return 0;
273+ if (varequal(s + 3, "ALL"))
274+ return 1;
275+ for (ss = locale_names; *ss ; ss++)
276+ if (varequal(s + 3, *ss + 3))
277+ return 1;
278+ return 0;
279+}
280+
281+
282+/*
283+ * Sets/unsets an environment variable from a pointer that may actually be a
284+ * pointer into environ where the string should not be manipulated.
285+ */
286+static void
287+change_env(const char *s, int set)
288+{
289+ char *eqp;
290+ char *ss;
291+
292+ INTOFF;
293+ ss = savestr(s);
294+ if ((eqp = strchr(ss, '=')) != NULL)
295+ *eqp = '\0';
296+ if (set && eqp != NULL)
297+ (void) setenv(ss, eqp + 1, 1);
298+ else
299+ (void) unsetenv(ss);
300+ ckfree(ss);
301+ INTON;
302+
303+ return;
304+}
305+
306+
307+/*
308+ * Same as setvar except that the variable and value are passed in
309+ * the first argument as name=value. Since the first argument will
310+ * be actually stored in the table, it should not be a string that
311+ * will go away.
312+ */
313+
314+void
315+setvareq(char *s, int flags)
316+{
317+ struct var *vp, **vpp;
318+ int nlen;
319+
320+ if (aflag)
321+ flags |= VEXPORT;
322+ if (forcelocal && !(flags & (VNOSET | VNOLOCAL)))
323+ mklocal(s);
324+ vp = find_var(s, &vpp, &nlen);
325+ if (vp != NULL) {
326+ if (vp->flags & VREADONLY) {
327+ if ((flags & (VTEXTFIXED|VSTACK)) == 0)
328+ ckfree(s);
329+ error("%.*s: is read only", vp->name_len, vp->text);
330+ }
331+ if (flags & VNOSET) {
332+ if ((flags & (VTEXTFIXED|VSTACK)) == 0)
333+ ckfree(s);
334+ return;
335+ }
336+ INTOFF;
337+
338+ if (vp->func && (flags & VNOFUNC) == 0)
339+ (*vp->func)(s + vp->name_len + 1);
340+
341+ if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
342+ ckfree(vp->text);
343+
344+ vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
345+ vp->flags |= flags;
346+ vp->text = s;
347+
348+ /*
349+ * We could roll this to a function, to handle it as
350+ * a regular variable function callback, but why bother?
351+ *
352+ * Note: this assumes iflag is not set to 1 initially.
353+ * As part of initvar(), this is called before arguments
354+ * are looked at.
355+ */
356+ if ((vp == &vmpath || (vp == &vmail && ! mpathset())) &&
357+ iflag == 1)
358+ chkmail(1);
359+ if ((vp->flags & VEXPORT) && localevar(s)) {
360+ change_env(s, 1);
361+ (void) setlocale(LC_ALL, "");
362+ updatecharset();
363+ }
364+ INTON;
365+ return;
366+ }
367+ /* not found */
368+ if (flags & VNOSET) {
369+ if ((flags & (VTEXTFIXED|VSTACK)) == 0)
370+ ckfree(s);
371+ return;
372+ }
373+ INTOFF;
374+ vp = ckmalloc(sizeof (*vp));
375+ vp->flags = flags;
376+ vp->text = s;
377+ vp->name_len = nlen;
378+ vp->next = *vpp;
379+ vp->func = NULL;
380+ *vpp = vp;
381+ if ((vp->flags & VEXPORT) && localevar(s)) {
382+ change_env(s, 1);
383+ (void) setlocale(LC_ALL, "");
384+ updatecharset();
385+ }
386+ INTON;
387+}
388+
389+
390+static void
391+setvareq_const(const char *s, int flags)
392+{
393+ setvareq(__DECONST(char *, s), flags | VTEXTFIXED);
394+}
395+
396+
397+/*
398+ * Process a linked list of variable assignments.
399+ */
400+
401+void
402+listsetvar(struct arglist *list, int flags)
403+{
404+ int i;
405+
406+ INTOFF;
407+ for (i = 0; i < list->count; i++)
408+ setvareq(savestr(list->args[i]), flags);
409+ INTON;
410+}
411+
412+
413+
414+/*
415+ * Find the value of a variable. Returns NULL if not set.
416+ */
417+
418+char *
419+lookupvar(const char *name)
420+{
421+ struct var *v;
422+
423+ v = find_var(name, NULL, NULL);
424+ if (v == NULL || v->flags & VUNSET)
425+ return NULL;
426+ return v->text + v->name_len + 1;
427+}
428+
429+
430+
431+/*
432+ * Search the environment of a builtin command. If the second argument
433+ * is nonzero, return the value of a variable even if it hasn't been
434+ * exported.
435+ */
436+
437+char *
438+bltinlookup(const char *name, int doall)
439+{
440+ struct var *v;
441+ char *result;
442+ int i;
443+
444+ result = NULL;
445+ if (cmdenviron) for (i = 0; i < cmdenviron->count; i++) {
446+ if (varequal(cmdenviron->args[i], name))
447+ result = strchr(cmdenviron->args[i], '=') + 1;
448+ }
449+ if (result != NULL)
450+ return result;
451+
452+ v = find_var(name, NULL, NULL);
453+ if (v == NULL || v->flags & VUNSET ||
454+ (!doall && (v->flags & VEXPORT) == 0))
455+ return NULL;
456+ return v->text + v->name_len + 1;
457+}
458+
459+
460+/*
461+ * Set up locale for a builtin (LANG/LC_* assignments).
462+ */
463+void
464+bltinsetlocale(void)
465+{
466+ int act = 0;
467+ char *loc, *locdef;
468+ int i;
469+
470+ if (cmdenviron) for (i = 0; i < cmdenviron->count; i++) {
471+ if (localevar(cmdenviron->args[i])) {
472+ act = 1;
473+ break;
474+ }
475+ }
476+ if (!act)
477+ return;
478+ loc = bltinlookup("LC_ALL", 0);
479+ INTOFF;
480+ if (loc != NULL) {
481+ setlocale(LC_ALL, loc);
482+ INTON;
483+ updatecharset();
484+ return;
485+ }
486+ locdef = bltinlookup("LANG", 0);
487+ for (i = 0; locale_names[i] != NULL; i++) {
488+ loc = bltinlookup(locale_names[i], 0);
489+ if (loc == NULL)
490+ loc = locdef;
491+ if (loc != NULL)
492+ setlocale(locale_categories[i], loc);
493+ }
494+ INTON;
495+ updatecharset();
496+}
497+
498+/*
499+ * Undo the effect of bltinlocaleset().
500+ */
501+void
502+bltinunsetlocale(void)
503+{
504+ int i;
505+
506+ INTOFF;
507+ if (cmdenviron) for (i = 0; i < cmdenviron->count; i++) {
508+ if (localevar(cmdenviron->args[i])) {
509+ setlocale(LC_ALL, "");
510+ updatecharset();
511+ break;
512+ }
513+ }
514+ INTON;
515+}
516+
517+/*
518+ * Update the localeisutf8 flag.
519+ */
520+void
521+updatecharset(void)
522+{
523+ char *charset;
524+
525+ charset = nl_langinfo(CODESET);
526+ localeisutf8 = !strcmp(charset, "UTF-8");
527+}
528+
529+void
530+initcharset(void)
531+{
532+ updatecharset();
533+ initial_localeisutf8 = localeisutf8;
534+}
535+
536+/*
537+ * Generate a list of exported variables. This routine is used to construct
538+ * the third argument to execve when executing a program.
539+ */
540+
541+char **
542+environment(void)
543+{
544+ int nenv;
545+ struct var **vpp;
546+ struct var *vp;
547+ char **env, **ep;
548+
549+ nenv = 0;
550+ for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
551+ for (vp = *vpp ; vp ; vp = vp->next)
552+ if ((vp->flags & (VEXPORT|VUNSET)) == VEXPORT)
553+ nenv++;
554+ }
555+ ep = env = stalloc((nenv + 1) * sizeof *env);
556+ for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
557+ for (vp = *vpp ; vp ; vp = vp->next)
558+ if ((vp->flags & (VEXPORT|VUNSET)) == VEXPORT)
559+ *ep++ = vp->text;
560+ }
561+ *ep = NULL;
562+ return env;
563+}
564+
565+
566+static int
567+var_compare(const void *a, const void *b)
568+{
569+ const char *const *sa, *const *sb;
570+
571+ sa = a;
572+ sb = b;
573+ /*
574+ * This compares two var=value strings which creates a different
575+ * order from what you would probably expect. POSIX is somewhat
576+ * ambiguous on what should be sorted exactly.
577+ */
578+ return strcoll(*sa, *sb);
579+}
580+
581+
582+/*
583+ * Command to list all variables which are set. This is invoked from the
584+ * set command when it is called without any options or operands.
585+ */
586+
587+int
588+showvarscmd(int argc __unused, char **argv __unused)
589+{
590+ struct var **vpp;
591+ struct var *vp;
592+ const char *s;
593+ const char **vars;
594+ int i, n;
595+
596+ /*
597+ * POSIX requires us to sort the variables.
598+ */
599+ n = 0;
600+ for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
601+ for (vp = *vpp; vp; vp = vp->next) {
602+ if (!(vp->flags & VUNSET))
603+ n++;
604+ }
605+ }
606+
607+ INTOFF;
608+ vars = ckmalloc(n * sizeof(*vars));
609+ i = 0;
610+ for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
611+ for (vp = *vpp; vp; vp = vp->next) {
612+ if (!(vp->flags & VUNSET))
613+ vars[i++] = vp->text;
614+ }
615+ }
616+
617+ qsort(vars, n, sizeof(*vars), var_compare);
618+ for (i = 0; i < n; i++) {
619+ /*
620+ * Skip improper variable names so the output remains usable as
621+ * shell input.
622+ */
623+ if (!isassignment(vars[i]))
624+ continue;
625+ s = strchr(vars[i], '=');
626+ s++;
627+ outbin(vars[i], s - vars[i], out1);
628+ out1qstr(s);
629+ out1c('\n');
630+ }
631+ ckfree(vars);
632+ INTON;
633+
634+ return 0;
635+}
636+
637+
638+
639+/*
640+ * The export and readonly commands.
641+ */
642+
643+int
644+exportcmd(int argc __unused, char **argv)
645+{
646+ struct var **vpp;
647+ struct var *vp;
648+ char **ap;
649+ char *name;
650+ char *p;
651+ char *cmdname;
652+ int ch, values;
653+ int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
654+
655+ cmdname = argv[0];
656+ values = 0;
657+ while ((ch = nextopt("p")) != '\0') {
658+ switch (ch) {
659+ case 'p':
660+ values = 1;
661+ break;
662+ }
663+ }
664+
665+ if (values && *argptr != NULL)
666+ error("-p requires no arguments");
667+ if (*argptr != NULL) {
668+ for (ap = argptr; (name = *ap) != NULL; ap++) {
669+ if ((p = strchr(name, '=')) != NULL) {
670+ p++;
671+ } else {
672+ vp = find_var(name, NULL, NULL);
673+ if (vp != NULL) {
674+ vp->flags |= flag;
675+ if ((vp->flags & VEXPORT) && localevar(vp->text)) {
676+ change_env(vp->text, 1);
677+ (void) setlocale(LC_ALL, "");
678+ updatecharset();
679+ }
680+ continue;
681+ }
682+ }
683+ setvar(name, p, flag);
684+ }
685+ } else {
686+ for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
687+ for (vp = *vpp ; vp ; vp = vp->next) {
688+ if (vp->flags & flag) {
689+ if (values) {
690+ /*
691+ * Skip improper variable names
692+ * so the output remains usable
693+ * as shell input.
694+ */
695+ if (!isassignment(vp->text))
696+ continue;
697+ out1str(cmdname);
698+ out1c(' ');
699+ }
700+ if (values && !(vp->flags & VUNSET)) {
701+ outbin(vp->text,
702+ vp->name_len + 1, out1);
703+ out1qstr(vp->text +
704+ vp->name_len + 1);
705+ } else
706+ outbin(vp->text, vp->name_len,
707+ out1);
708+ out1c('\n');
709+ }
710+ }
711+ }
712+ }
713+ return 0;
714+}
715+
716+
717+/*
718+ * The "local" command.
719+ */
720+
721+int
722+localcmd(int argc __unused, char **argv __unused)
723+{
724+ char *name;
725+
726+ nextopt("");
727+ if (! in_function())
728+ error("Not in a function");
729+ while ((name = *argptr++) != NULL) {
730+ mklocal(name);
731+ }
732+ return 0;
733+}
734+
735+
736+/*
737+ * Make a variable a local variable. When a variable is made local, it's
738+ * value and flags are saved in a localvar structure. The saved values
739+ * will be restored when the shell function returns. We handle the name
740+ * "-" as a special case.
741+ */
742+
743+void
744+mklocal(char *name)
745+{
746+ struct localvar *lvp;
747+ struct var **vpp;
748+ struct var *vp;
749+
750+ INTOFF;
751+ lvp = ckmalloc(sizeof (struct localvar));
752+ if (name[0] == '-' && name[1] == '\0') {
753+ lvp->text = ckmalloc(sizeof optval);
754+ memcpy(lvp->text, optval, sizeof optval);
755+ vp = NULL;
756+ } else {
757+ vp = find_var(name, &vpp, NULL);
758+ if (vp == NULL) {
759+ if (strchr(name, '='))
760+ setvareq(savestr(name), VSTRFIXED | VNOLOCAL);
761+ else
762+ setvar(name, NULL, VSTRFIXED | VNOLOCAL);
763+ vp = *vpp; /* the new variable */
764+ lvp->text = NULL;
765+ lvp->flags = VUNSET;
766+ } else {
767+ lvp->text = vp->text;
768+ lvp->flags = vp->flags;
769+ vp->flags |= VSTRFIXED|VTEXTFIXED;
770+ if (name[vp->name_len] == '=')
771+ setvareq(savestr(name), VNOLOCAL);
772+ }
773+ }
774+ lvp->vp = vp;
775+ lvp->next = localvars;
776+ localvars = lvp;
777+ INTON;
778+}
779+
780+
781+/*
782+ * Called after a function returns.
783+ */
784+
785+void
786+poplocalvars(void)
787+{
788+ struct localvar *lvp;
789+ struct var *vp;
790+ int islocalevar;
791+
792+ INTOFF;
793+ while ((lvp = localvars) != NULL) {
794+ localvars = lvp->next;
795+ vp = lvp->vp;
796+ if (vp == NULL) { /* $- saved */
797+ memcpy(optval, lvp->text, sizeof optval);
798+ ckfree(lvp->text);
799+ optschanged();
800+ } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
801+ vp->flags &= ~VREADONLY;
802+ (void)unsetvar(vp->text);
803+ } else {
804+ islocalevar = (vp->flags | lvp->flags) & VEXPORT &&
805+ localevar(lvp->text);
806+ if ((vp->flags & VTEXTFIXED) == 0)
807+ ckfree(vp->text);
808+ vp->flags = lvp->flags;
809+ vp->text = lvp->text;
810+ if (vp->func)
811+ (*vp->func)(vp->text + vp->name_len + 1);
812+ if (islocalevar) {
813+ change_env(vp->text, vp->flags & VEXPORT &&
814+ (vp->flags & VUNSET) == 0);
815+ setlocale(LC_ALL, "");
816+ updatecharset();
817+ }
818+ }
819+ ckfree(lvp);
820+ }
821+ INTON;
822+}
823+
824+
825+int
826+setvarcmd(int argc, char **argv)
827+{
828+ if (argc <= 2)
829+ return unsetcmd(argc, argv);
830+ else if (argc == 3)
831+ setvar(argv[1], argv[2], 0);
832+ else
833+ error("too many arguments");
834+ return 0;
835+}
836+
837+
838+/*
839+ * The unset builtin command.
840+ */
841+
842+int
843+unsetcmd(int argc __unused, char **argv __unused)
844+{
845+ char **ap;
846+ int i;
847+ int flg_func = 0;
848+ int flg_var = 0;
849+ int ret = 0;
850+
851+ while ((i = nextopt("vf")) != '\0') {
852+ if (i == 'f')
853+ flg_func = 1;
854+ else
855+ flg_var = 1;
856+ }
857+ if (flg_func == 0 && flg_var == 0)
858+ flg_var = 1;
859+
860+ INTOFF;
861+ for (ap = argptr; *ap ; ap++) {
862+ if (flg_func)
863+ ret |= unsetfunc(*ap);
864+ if (flg_var)
865+ ret |= unsetvar(*ap);
866+ }
867+ INTON;
868+ return ret;
869+}
870+
871+
872+/*
873+ * Unset the specified variable.
874+ * Called with interrupts off.
875+ */
876+
877+int
878+unsetvar(const char *s)
879+{
880+ struct var **vpp;
881+ struct var *vp;
882+
883+ vp = find_var(s, &vpp, NULL);
884+ if (vp == NULL)
885+ return (0);
886+ if (vp->flags & VREADONLY)
887+ return (1);
888+ if (vp->text[vp->name_len + 1] != '\0')
889+ setvar(s, "", 0);
890+ if ((vp->flags & VEXPORT) && localevar(vp->text)) {
891+ change_env(s, 0);
892+ setlocale(LC_ALL, "");
893+ updatecharset();
894+ }
895+ vp->flags &= ~VEXPORT;
896+ vp->flags |= VUNSET;
897+ if ((vp->flags & VSTRFIXED) == 0) {
898+ if ((vp->flags & VTEXTFIXED) == 0)
899+ ckfree(vp->text);
900+ *vpp = vp->next;
901+ ckfree(vp);
902+ }
903+ return (0);
904+}
905+
906+
907+
908+/*
909+ * Returns true if the two strings specify the same variable. The first
910+ * variable name is terminated by '='; the second may be terminated by
911+ * either '=' or '\0'.
912+ */
913+
914+static int
915+varequal(const char *p, const char *q)
916+{
917+ while (*p == *q++) {
918+ if (*p++ == '=')
919+ return 1;
920+ }
921+ if (*p == '=' && *(q - 1) == '\0')
922+ return 1;
923+ return 0;
924+}
925+
926+/*
927+ * Search for a variable.
928+ * 'name' may be terminated by '=' or a NUL.
929+ * vppp is set to the pointer to vp, or the list head if vp isn't found
930+ * lenp is set to the number of characters in 'name'
931+ */
932+
933+static struct var *
934+find_var(const char *name, struct var ***vppp, int *lenp)
935+{
936+ unsigned int hashval;
937+ int len;
938+ struct var *vp, **vpp;
939+ const char *p = name;
940+
941+ hashval = 0;
942+ while (*p && *p != '=')
943+ hashval = 2 * hashval + (unsigned char)*p++;
944+ len = p - name;
945+
946+ if (lenp)
947+ *lenp = len;
948+ vpp = &vartab[hashval % VTABSIZE];
949+ if (vppp)
950+ *vppp = vpp;
951+
952+ for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
953+ if (vp->name_len != len)
954+ continue;
955+ if (memcmp(vp->text, name, len) != 0)
956+ continue;
957+ if (vppp)
958+ *vppp = vpp;
959+ return vp;
960+ }
961+ return NULL;
962+}
+129,
-0
1@@ -0,0 +1,129 @@
2+/*-
3+ * SPDX-License-Identifier: BSD-3-Clause
4+ *
5+ * Copyright (c) 1991, 1993
6+ * The Regents of the University of California. All rights reserved.
7+ *
8+ * This code is derived from software contributed to Berkeley by
9+ * Kenneth Almquist.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
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.
22+ *
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
33+ * SUCH DAMAGE.
34+ */
35+
36+/*
37+ * Shell variables.
38+ */
39+
40+/* flags */
41+#define VEXPORT 0x01 /* variable is exported */
42+#define VREADONLY 0x02 /* variable cannot be modified */
43+#define VSTRFIXED 0x04 /* variable struct is statically allocated */
44+#define VTEXTFIXED 0x08 /* text is statically allocated */
45+#define VSTACK 0x10 /* text is allocated on the stack */
46+#define VUNSET 0x20 /* the variable is not set */
47+#define VNOFUNC 0x40 /* don't call the callback function */
48+#define VNOSET 0x80 /* do not set variable - just readonly test */
49+#define VNOLOCAL 0x100 /* ignore forcelocal */
50+
51+
52+struct var {
53+ struct var *next; /* next entry in hash list */
54+ int flags; /* flags are defined above */
55+ int name_len; /* length of name */
56+ char *text; /* name=value */
57+ void (*func)(const char *);
58+ /* function to be called when */
59+ /* the variable gets set/unset */
60+};
61+
62+
63+struct localvar {
64+ struct localvar *next; /* next local variable in list */
65+ struct var *vp; /* the variable that was made local */
66+ int flags; /* saved flags */
67+ char *text; /* saved text */
68+};
69+
70+
71+extern struct localvar *localvars;
72+extern int forcelocal;
73+
74+extern struct var vifs;
75+extern struct var vmail;
76+extern struct var vmpath;
77+extern struct var vpath;
78+extern struct var vps1;
79+extern struct var vps2;
80+extern struct var vps4;
81+extern struct var vdisvfork;
82+#ifndef NO_HISTORY
83+extern struct var vhistsize;
84+extern struct var vterm;
85+#endif
86+
87+extern int localeisutf8;
88+/* The parser uses the locale that was in effect at startup. */
89+extern int initial_localeisutf8;
90+
91+/*
92+ * The following macros access the values of the above variables.
93+ * They have to skip over the name. They return the null string
94+ * for unset variables.
95+ */
96+
97+#define ifsval() (vifs.text + 4)
98+#define ifsset() ((vifs.flags & VUNSET) == 0)
99+#define mailval() (vmail.text + 5)
100+#define mpathval() (vmpath.text + 9)
101+#define pathval() (vpath.text + 5)
102+#define ps1val() (vps1.text + 4)
103+#define ps2val() (vps2.text + 4)
104+#define ps4val() (vps4.text + 4)
105+#define optindval() (voptind.text + 7)
106+#ifndef NO_HISTORY
107+#define histsizeval() (vhistsize.text + 9)
108+#define termval() (vterm.text + 5)
109+#endif
110+
111+#define mpathset() ((vmpath.flags & VUNSET) == 0)
112+#define disvforkset() ((vdisvfork.flags & VUNSET) == 0)
113+
114+void initvar(void);
115+void setvar(const char *, const char *, int);
116+void setvareq(char *, int);
117+struct arglist;
118+void listsetvar(struct arglist *, int);
119+char *lookupvar(const char *);
120+char *bltinlookup(const char *, int);
121+void bltinsetlocale(void);
122+void bltinunsetlocale(void);
123+void updatecharset(void);
124+void initcharset(void);
125+char **environment(void);
126+int showvarscmd(int, char **);
127+void mklocal(char *);
128+void poplocalvars(void);
129+int unsetvar(const char *);
130+int setvarsafe(const char *, const char *, int);
+118,
-16
1@@ -1,14 +1,14 @@
2 /* See LICENSE file for copyright and license details. */
3-#include <ctype.h>
4-#include <stdio.h>
5-#include <stdlib.h>
6-#include <string.h>
7-
8 #include "queue.h"
9 #include "text.h"
10 #include "utf.h"
11 #include "util.h"
12
13+#include <ctype.h>
14+#include <stdio.h>
15+#include <stdlib.h>
16+#include <string.h>
17+
18 struct keydef {
19 int start_column;
20 int end_column;
21@@ -36,6 +36,12 @@ enum {
22 static TAILQ_HEAD(kdhead, keydef) kdhead = TAILQ_HEAD_INITIALIZER(kdhead);
23
24 static int Cflag = 0, cflag = 0, uflag = 0;
25+#if FEATURE_SORT_BIG
26+static int zflag = 0;
27+#endif
28+#if FEATURE_SORT_STABLE
29+static int sflag = 0;
30+#endif
31 static char *fieldsep = NULL;
32 static size_t fieldseplen = 0;
33 static struct column col1, col2;
34@@ -94,7 +100,7 @@ columns(struct line *line, const struct keydef *kd, struct column *col)
35 skipcolumn(&start, 1);
36 if (kd->flags & MOD_STARTB)
37 skipblank(&start);
38- for (utflen = 0; start.len > 1 && utflen < kd->start_char - 1;) {
39+ for (utflen = 0; start.len > 1 && utflen < (size_t)(kd->start_char - 1);) {
40 rlen = chartorune(&r, start.data);
41 start.data += rlen;
42 start.len -= rlen;
43@@ -109,7 +115,7 @@ columns(struct line *line, const struct keydef *kd, struct column *col)
44 if (kd->flags & MOD_ENDB)
45 skipblank(&end);
46 if (kd->end_char) {
47- for (utflen = 0; end.len > 1 && utflen < kd->end_char;) {
48+ for (utflen = 0; end.len > 1 && utflen < (size_t)kd->end_char;) {
49 rlen = chartorune(&r, end.data);
50 end.data += rlen;
51 end.len -= rlen;
52@@ -173,6 +179,33 @@ skipmodcmp(struct line *a, struct line *b, int flags)
53 return r1 - r2;
54 }
55
56+#if FEATURE_SORT_BIG
57+static void
58+getlines_z(FILE *fp, struct linebuf *b)
59+{
60+ char *line = NULL;
61+ size_t size = 0, linelen = 0;
62+ ssize_t len;
63+
64+ while ((len = getdelim(&line, &size, '\0', fp)) > 0) {
65+ if (++b->nlines > b->capacity) {
66+ b->capacity += 512;
67+ b->lines = ereallocarray(b->lines, b->capacity, sizeof(*b->lines));
68+ }
69+ linelen = len;
70+ b->lines[b->nlines - 1].data = memcpy(emalloc(linelen + 1), line, linelen + 1);
71+ b->lines[b->nlines - 1].len = linelen;
72+ }
73+ free(line);
74+ if (b->lines && b->nlines && linelen && b->lines[b->nlines - 1].data[linelen - 1] != '\0') {
75+ b->lines[b->nlines - 1].data = erealloc(b->lines[b->nlines - 1].data, linelen + 2);
76+ b->lines[b->nlines - 1].data[linelen] = '\0';
77+ b->lines[b->nlines - 1].data[linelen + 1] = '\0';
78+ b->lines[b->nlines - 1].len++;
79+ }
80+}
81+#endif
82+
83 static int
84 slinecmp(struct line *a, struct line *b)
85 {
86@@ -208,19 +241,44 @@ slinecmp(struct line *a, struct line *b)
87 return res;
88 }
89
90+#if FEATURE_SORT_STABLE
91+struct sort_item {
92+ struct line line;
93+ size_t index;
94+};
95+
96+static int
97+slinecmp_stable(const struct sort_item *a, const struct sort_item *b)
98+{
99+ int res = slinecmp((struct line *)&a->line, (struct line *)&b->line);
100+ if (res == 0)
101+ return (a->index < b->index) ? -1 : 1;
102+ return res;
103+}
104+#endif
105+
106 static int
107 check(FILE *fp, const char *fname)
108 {
109 static struct line prev, cur, tmp;
110 static size_t prevsize, cursize, tmpsize;
111 ssize_t len;
112+ int delim = '\n';
113+
114+#if FEATURE_SORT_BIG
115+ if (zflag)
116+ delim = '\0';
117+#endif
118
119 if (!prev.data) {
120- if ((len = getline(&prev.data, &prevsize, fp)) < 0)
121- eprintf("getline:");
122+ if ((len = getdelim(&prev.data, &prevsize, delim, fp)) < 0) {
123+ if (feof(fp))
124+ return 0;
125+ eprintf("getdelim:");
126+ }
127 prev.len = len;
128 }
129- while ((len = getline(&cur.data, &cursize, fp)) > 0) {
130+ while ((len = getdelim(&cur.data, &cursize, delim, fp)) > 0) {
131 cur.len = len;
132 if (uflag > slinecmp(&cur, &prev)) {
133 if (!Cflag) {
134@@ -319,8 +377,14 @@ addkeydef(char *kdstr, int flags)
135 static void
136 usage(void)
137 {
138- enprintf(2, "usage: %s [-Cbcdfimnru] [-o outfile] [-t delim] "
139- "[-k def]... [file ...]\n", argv0);
140+ enprintf(2, "usage: %s [-Cbcdfimnru"
141+#if FEATURE_SORT_STABLE
142+ "s"
143+#endif
144+#if FEATURE_SORT_BIG
145+ "z"
146+#endif
147+ "] [-o outfile] [-t delim] [-k def]... [file ...]\n", argv0);
148 }
149
150 int
151@@ -370,6 +434,11 @@ main(int argc, char *argv[])
152 case 'r':
153 global_flags |= MOD_R;
154 break;
155+#if FEATURE_SORT_STABLE
156+ case 's':
157+ sflag = 1;
158+ break;
159+#endif
160 case 't':
161 fieldsep = EARGF(usage());
162 if (!*fieldsep)
163@@ -379,6 +448,11 @@ main(int argc, char *argv[])
164 case 'u':
165 uflag = 1;
166 break;
167+#if FEATURE_SORT_BIG
168+ case 'z':
169+ zflag = 1;
170+ break;
171+#endif
172 default:
173 usage();
174 } ARGEND
175@@ -394,7 +468,12 @@ main(int argc, char *argv[])
176 if (check(stdin, "<stdin>") && !ret)
177 ret = 1;
178 } else {
179- getlines(stdin, &linebuf);
180+#if FEATURE_SORT_BIG
181+ if (zflag)
182+ getlines_z(stdin, &linebuf);
183+ else
184+#endif
185+ getlines(stdin, &linebuf);
186 }
187 } else for (; *argv; argc--, argv++) {
188 if (!strcmp(*argv, "-")) {
189@@ -408,7 +487,12 @@ main(int argc, char *argv[])
190 if (check(fp, *argv) && !ret)
191 ret = 1;
192 } else {
193- getlines(fp, &linebuf);
194+#if FEATURE_SORT_BIG
195+ if (zflag)
196+ getlines_z(fp, &linebuf);
197+ else
198+#endif
199+ getlines(fp, &linebuf);
200 }
201 if (fp != stdin && fshut(fp, *argv))
202 ret = 2;
203@@ -418,8 +502,26 @@ main(int argc, char *argv[])
204 if (outfile && !(ofp = fopen(outfile, "w")))
205 eprintf("fopen %s:", outfile);
206
207- qsort(linebuf.lines, linebuf.nlines, sizeof(*linebuf.lines),
208- (int (*)(const void *, const void *))slinecmp);
209+#if FEATURE_SORT_STABLE
210+ if (sflag) {
211+ struct sort_item *items = ecalloc(linebuf.nlines, sizeof(*items));
212+ for (i = 0; i < linebuf.nlines; i++) {
213+ items[i].line = linebuf.lines[i];
214+ items[i].index = i;
215+ }
216+ qsort(items, linebuf.nlines, sizeof(*items),
217+ (int (*)(const void *, const void *))slinecmp_stable);
218+ for (i = 0; i < linebuf.nlines; i++) {
219+ linebuf.lines[i] = items[i].line;
220+ }
221+ free(items);
222+ } else {
223+#endif
224+ qsort(linebuf.lines, linebuf.nlines, sizeof(*linebuf.lines),
225+ (int (*)(const void *, const void *))slinecmp);
226+#if FEATURE_SORT_STABLE
227+ }
228+#endif
229
230 for (i = 0; i < linebuf.nlines; i++) {
231 if (!uflag || i == 0 ||
+3,
-3
1@@ -34,7 +34,7 @@ dropinit(int fd, const char *fname, size_t count)
2 }
3 break;
4 case 'c': /* bytes */
5- if (count > n) {
6+ if (count > (size_t)n) {
7 count -= n;
8 } else {
9 p = buf + count;
10@@ -165,8 +165,8 @@ main(int argc, char *argv[])
11 case 'n':
12 mode = ARGC();
13 numstr = EARGF(usage());
14- n = MIN(llabs(estrtonum(numstr, LLONG_MIN + 1,
15- MIN(LLONG_MAX, SIZE_MAX))), SIZE_MAX);
16+ n = MIN((unsigned long long)llabs(estrtonum(numstr, LLONG_MIN + 1,
17+ MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX))), (unsigned long long)SIZE_MAX);
18 if (strchr(numstr, '+'))
19 tail = dropinit;
20 break;
+2,
-2
1@@ -36,7 +36,7 @@ main(int argc, char *argv[])
2 nfds = argc + 1;
3 fds = ecalloc(nfds, sizeof(*fds));
4
5- for (i = 0; i < argc; i++) {
6+ for (i = 0; i < (size_t)argc; i++) {
7 if ((fds[i] = open(argv[i], O_WRONLY|O_CREAT|aflag, 0666)) < 0) {
8 weprintf("open %s:", argv[i]);
9 ret = 1;
10@@ -47,7 +47,7 @@ main(int argc, char *argv[])
11 while ((n = read(0, buf, sizeof(buf))) > 0) {
12 for (i = 0; i < nfds; i++) {
13 if (fds[i] >= 0 && writeall(fds[i], buf, n) < 0) {
14- weprintf("write %s:", (i != argc) ? argv[i] : "<stdout>");
15+ weprintf("write %s:", (i != (size_t)argc) ? argv[i] : "<stdout>");
16 fds[i] = -1;
17 ret = 1;
18 }
+1,
-0
1@@ -176,6 +176,7 @@ find_test(struct test *tests, char *name)
2 static int
3 noarg(char *argv[])
4 {
5+ (void)argv;
6 return 0;
7 }
8
+1,
-0
1@@ -42,6 +42,7 @@ main(int argc, char *argv[])
2 switch ((pid = fork())) {
3 case -1:
4 eprintf("fork:");
5+ /* fallthrough */
6 case 0:
7 execvp(argv[0], argv);
8 savederrno = errno;
+2,
-2
1@@ -21,7 +21,7 @@ parselist(const char *s)
2 if (*p == '\0')
3 eprintf("empty field in tablist\n");
4 tablist = ereallocarray(tablist, i + 1, sizeof(*tablist));
5- tablist[i] = estrtonum(p, 1, MIN(LLONG_MAX, SIZE_MAX));
6+ tablist[i] = estrtonum(p, 1, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
7 if (i > 0 && tablist[i - 1] >= tablist[i])
8 eprintf("tablist must be ascending\n");
9 }
10@@ -140,7 +140,7 @@ main(int argc, char *argv[])
11 tl = EARGF(usage());
12 if (!*tl)
13 eprintf("tablist cannot be empty\n");
14- /* Fallthrough: -t implies -a */
15+ /* fallthrough */
16 case 'a':
17 aflag = 1;
18 break;
+1,
-0
1@@ -113,6 +113,7 @@ uudecodeb64(FILE *fp, FILE *outfp)
2 case 1:
3 eprintf("%d: unexpected \"=\""
4 "appeared\n", l);
5+ /* fallthrough */
6 case 2:
7 *po++ = b24[0];
8 b = 0;
+5,
-4
1@@ -241,6 +241,7 @@ dospawn:
2 switch (fork()) {
3 case -1:
4 eprintf("fork:");
5+ /* fallthrough */
6 case 0:
7 execvp(*cmd, cmd);
8 savederrno = errno;
9@@ -283,7 +284,7 @@ main(int argc, char *argv[])
10 case 'n':
11 nflag = 1;
12 maxargs =
13- estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
14+ estrtonum(EARGF(usage()), 1, MIN((unsigned long long)SIZE_MAX, (unsigned long long)LLONG_MAX));
15 break;
16 case 'p':
17 pflag = 1;
18@@ -293,7 +294,7 @@ main(int argc, char *argv[])
19 break;
20 case 's':
21 argmaxsz =
22- estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
23+ estrtonum(EARGF(usage()), 1, MIN((unsigned long long)SIZE_MAX, (unsigned long long)LLONG_MAX));
24 break;
25 case 't':
26 tflag = 1;
27@@ -314,11 +315,11 @@ main(int argc, char *argv[])
28 case 'L':
29 Lflag = 1;
30 maxlines =
31- estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
32+ estrtonum(EARGF(usage()), 1, MIN((unsigned long long)SIZE_MAX, (unsigned long long)LLONG_MAX));
33 break;
34 case 'P':
35 maxprocs =
36- estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
37+ estrtonum(EARGF(usage()), 1, MIN((unsigned long long)SIZE_MAX, (unsigned long long)LLONG_MAX));
38 break;
39 default:
40 usage();
+177,
-0
1@@ -0,0 +1,177 @@
2+/* See LICENSE file for copyright and license details. */
3+#include "util.h"
4+#include "arg.h"
5+
6+#include <stdio.h>
7+#include <stdlib.h>
8+#include <string.h>
9+
10+static void
11+usage(void)
12+{
13+ eprintf("usage: %s [-d] [-i] [-w cols] [file]\n", argv0);
14+}
15+
16+static void
17+base64_encode(char *dst, const unsigned char *src, size_t len)
18+{
19+ static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
20+ size_t i;
21+
22+ for (i = 0; i < len; i += 3, dst += 4) {
23+ unsigned long x = (src[i] & 0xfful) << 16;
24+ dst[3] = i + 2 >= len ? '=' : b64[(x |= src[i + 2] & 0xfful) & 0x3f];
25+ dst[2] = i + 1 >= len ? '=' : b64[(x |= (src[i + 1] & 0xfful) << 8) >> 6 & 0x3f];
26+ dst[1] = b64[x >> 12 & 0x3f];
27+ dst[0] = b64[x >> 18];
28+ }
29+ *dst = '\0';
30+}
31+
32+static size_t
33+base64_decode(unsigned char *dst, const char *src)
34+{
35+ static const char b64[] = {
36+ ['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3,
37+ ['E'] = 4, ['F'] = 5, ['G'] = 6, ['H'] = 7,
38+ ['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11,
39+ ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15,
40+ ['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19,
41+ ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23,
42+ ['Y'] = 24, ['Z'] = 25, ['a'] = 26, ['b'] = 27,
43+ ['c'] = 28, ['d'] = 29, ['e'] = 30, ['f'] = 31,
44+ ['g'] = 32, ['h'] = 33, ['i'] = 34, ['j'] = 35,
45+ ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39,
46+ ['o'] = 40, ['p'] = 41, ['q'] = 42, ['r'] = 43,
47+ ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47,
48+ ['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51,
49+ ['0'] = 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
50+ ['+'] = 62, ['/'] = 63, ['='] = 0,
51+ };
52+ unsigned long x;
53+ size_t i, c, len, pad;
54+
55+ for (i = 0, x = 0, len = 0, pad = 0; src[i]; ++i) {
56+ c = (unsigned char)src[i];
57+ if (c == '=' && (!src[i + 1] || (src[i + 1] == '=' && !src[i + 2])))
58+ ++pad;
59+ else if (c >= sizeof(b64) || (!b64[c] && c != 'A'))
60+ return 0;
61+ x = x << 6 | b64[c];
62+ if (i % 4 == 3) {
63+ dst[len + 2] = x & 0xff, x >>= 8;
64+ dst[len + 1] = x & 0xff, x >>= 8;
65+ dst[len + 0] = x & 0xff;
66+ len += 3;
67+ }
68+ }
69+ if (i % 4 != 0)
70+ return 0;
71+ return len - pad;
72+}
73+
74+static void
75+encode_stream(FILE *fp, size_t wrap)
76+{
77+ unsigned char buf[3072];
78+ char out[4096 + 1];
79+ size_t n, i, col;
80+
81+ col = 0;
82+ while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
83+ base64_encode(out, buf, n);
84+ for (i = 0; out[i]; i++) {
85+ putchar(out[i]);
86+ if (wrap > 0 && ++col == wrap) {
87+ putchar('\n');
88+ col = 0;
89+ }
90+ }
91+ }
92+ if (wrap > 0 && col > 0)
93+ putchar('\n');
94+}
95+
96+static void
97+decode_stream(FILE *fp, int iflag)
98+{
99+ char in[5];
100+ unsigned char out[4];
101+ int c;
102+ size_t count, n;
103+
104+ count = 0;
105+ while ((c = fgetc(fp)) != EOF) {
106+ /* skip whitespace */
107+ if (c == '\r' || c == '\n' || c == '\t' || c == ' ')
108+ continue;
109+ /* check validity */
110+ if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
111+ (c >= '0' && c <= '9') || c == '+' || c == '/' || c == '=') {
112+ in[count++] = c;
113+ if (count == 4) {
114+ in[4] = '\0';
115+ n = base64_decode(out, in);
116+ if (n > 0)
117+ fwrite(out, 1, n, stdout);
118+ else
119+ eprintf("invalid input\n");
120+ if (strchr(in, '='))
121+ break;
122+ count = 0;
123+ }
124+ } else if (!iflag) {
125+ eprintf("invalid character\n");
126+ }
127+ }
128+ if (count > 0)
129+ eprintf("input is truncated\n");
130+}
131+
132+int
133+main(int argc, char *argv[])
134+{
135+ FILE *fp;
136+ int dflag, iflag, ret;
137+ size_t wrap;
138+
139+ dflag = 0;
140+ iflag = 0;
141+ wrap = 76;
142+ ret = 0;
143+ fp = stdin;
144+
145+ ARGBEGIN {
146+ case 'd':
147+ dflag = 1;
148+ break;
149+ case 'i':
150+ iflag = 1;
151+ break;
152+ case 'w':
153+ wrap = estrtonum(EARGF(usage()), 0, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SSIZE_MAX));
154+ break;
155+ default:
156+ usage();
157+ } ARGEND
158+
159+ if (argc > 1)
160+ usage();
161+
162+ if (argc == 1 && strcmp(argv[0], "-") != 0) {
163+ fp = fopen(argv[0], "r");
164+ if (!fp)
165+ eprintf("fopen %s:", argv[0]);
166+ }
167+
168+ if (dflag)
169+ decode_stream(fp, iflag);
170+ else
171+ encode_stream(fp, wrap);
172+
173+ if (fp != stdin)
174+ fclose(fp);
175+
176+ ret = fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
177+ return ret;
178+}
+0,
-0
+2,
-2
1@@ -30,7 +30,7 @@ main(int argc, char *argv[])
2 ARGBEGIN {
3 case 'c':
4 cflag = 1;
5- chars = estrtonum(EARGF(usage()), 1, MIN(LLONG_MAX, SIZE_MAX));
6+ chars = estrtonum(EARGF(usage()), 1, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
7 break;
8 default:
9 usage();
10@@ -38,7 +38,7 @@ main(int argc, char *argv[])
11
12 if (!cflag) {
13 if ((p = getenv("COLUMNS")))
14- chars = estrtonum(p, 1, MIN(LLONG_MAX, SIZE_MAX));
15+ chars = estrtonum(p, 1, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
16 else if (!ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) && w.ws_col > 0)
17 chars = w.ws_col;
18 }
+5,
-0
1@@ -345,14 +345,19 @@ freecte(struct ctabentry *cte, int nfields)
2 switch (nfields) {
3 case 6:
4 free(cte->cmd);
5+ /* fallthrough */
6 case 5:
7 free(cte->wday.val);
8+ /* fallthrough */
9 case 4:
10 free(cte->mon.val);
11+ /* fallthrough */
12 case 3:
13 free(cte->mday.val);
14+ /* fallthrough */
15 case 2:
16 free(cte->hour.val);
17+ /* fallthrough */
18 case 1:
19 free(cte->min.val);
20 }
+1,
-0
1@@ -56,6 +56,7 @@ main(int argc, char *argv[])
2 switch ((pid = fork())) {
3 case -1:
4 eprintf("fork:");
5+ /* fallthrough */
6 case 0:
7 if (oflag && close(fd) < 0)
8 eprintf("close:");
+1,
-1
1@@ -16,7 +16,7 @@ digitsleft(const char *d)
2 exp = strpbrk(d, "eE");
3 shift = exp ? estrtonum(exp + 1, INT_MIN, INT_MAX) : 0;
4
5- return MAX(0, strspn(d, "-0123456789") + shift);
6+ return MAX(0, (int)strspn(d, "-0123456789") + shift);
7 }
8
9 static int
+470,
-14
1@@ -1,15 +1,404 @@
2-/* See LICENSE file for copyright and license details. */
3-#include <sys/stat.h>
4-#include <sys/sysmacros.h>
5-#include <sys/types.h>
6+#include "config.h"
7+#include "util.h"
8
9+#include <grp.h>
10 #include <inttypes.h>
11+#include <pwd.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14+#include <sys/stat.h>
15+#include <sys/sysmacros.h>
16+#include <sys/types.h>
17+#include <sys/vfs.h>
18 #include <time.h>
19 #include <unistd.h>
20
21-#include "util.h"
22+static void
23+mode_to_str(mode_t mode, char *buf)
24+{
25+ buf[0] = S_ISDIR(mode) ? 'd' :
26+ S_ISLNK(mode) ? 'l' :
27+ S_ISCHR(mode) ? 'c' :
28+ S_ISBLK(mode) ? 'b' :
29+ S_ISFIFO(mode) ? 'p' :
30+ S_ISSOCK(mode) ? 's' : '-';
31+ buf[1] = (mode & S_IRUSR) ? 'r' : '-';
32+ buf[2] = (mode & S_IWUSR) ? 'w' : '-';
33+ buf[3] = (mode & S_IXUSR) ?
34+ ((mode & S_ISUID) ? 's' : 'x') :
35+ ((mode & S_ISUID) ? 'S' : '-');
36+ buf[4] = (mode & S_IRGRP) ? 'r' : '-';
37+ buf[5] = (mode & S_IWGRP) ? 'w' : '-';
38+ buf[6] = (mode & S_IXGRP) ?
39+ ((mode & S_ISGID) ? 's' : 'x') :
40+ ((mode & S_ISGID) ? 'S' : '-');
41+ buf[7] = (mode & S_IROTH) ? 'r' : '-';
42+ buf[8] = (mode & S_IWOTH) ? 'w' : '-';
43+ buf[9] = (mode & S_IXOTH) ?
44+ ((mode & S_ISVTX) ? 't' : 'x') :
45+ ((mode & S_ISVTX) ? 'T' : '-');
46+ buf[10] = '\0';
47+}
48+
49+#if FEATURE_STAT_FORMAT
50+static void
51+print_custom(const char *file, struct stat *st, const char *fmt)
52+{
53+ const char *p;
54+ char fmt_spec[64];
55+ int fmt_len;
56+ char buf[32];
57+ struct passwd *pw;
58+ struct group *gr;
59+
60+ for (p = fmt; *p; ) {
61+ if (*p == '\\') {
62+ p++;
63+ switch (*p) {
64+ case 'n': putchar('\n'); break;
65+ case 't': putchar('\t'); break;
66+ case 'r': putchar('\r'); break;
67+ case 'b': putchar('\b'); break;
68+ case 'f': putchar('\f'); break;
69+ case '\\': putchar('\\'); break;
70+ case '\0': return;
71+ default: putchar(*p); break;
72+ }
73+ p++;
74+ continue;
75+ }
76+ if (*p != '%') {
77+ putchar(*p);
78+ p++;
79+ continue;
80+ }
81+ p++;
82+ if (*p == '%') {
83+ putchar('%');
84+ p++;
85+ continue;
86+ }
87+ fmt_spec[0] = '%';
88+ fmt_len = 1;
89+ while (*p == '-' || *p == '+' || *p == '0' || *p == ' ' || *p == '#') {
90+ if (fmt_len < 60)
91+ fmt_spec[fmt_len++] = *p;
92+ p++;
93+ }
94+ while (*p >= '0' && *p <= '9') {
95+ if (fmt_len < 60)
96+ fmt_spec[fmt_len++] = *p;
97+ p++;
98+ }
99+ switch (*p) {
100+ case 'a':
101+ fmt_spec[fmt_len++] = 'o';
102+ fmt_spec[fmt_len] = '\0';
103+ printf(fmt_spec, (unsigned int)(st->st_mode & 0777));
104+ break;
105+ case 'A':
106+ fmt_spec[fmt_len++] = 's';
107+ fmt_spec[fmt_len] = '\0';
108+ mode_to_str(st->st_mode, buf);
109+ printf(fmt_spec, buf);
110+ break;
111+ case 'b':
112+ fmt_spec[fmt_len++] = 'l';
113+ fmt_spec[fmt_len++] = 'l';
114+ fmt_spec[fmt_len++] = 'u';
115+ fmt_spec[fmt_len] = '\0';
116+ printf(fmt_spec, (unsigned long long)st->st_blocks);
117+ break;
118+ case 'B':
119+ fmt_spec[fmt_len++] = 'u';
120+ fmt_spec[fmt_len] = '\0';
121+ printf(fmt_spec, 512U);
122+ break;
123+ case 'd':
124+ fmt_spec[fmt_len++] = 'l';
125+ fmt_spec[fmt_len++] = 'l';
126+ fmt_spec[fmt_len++] = 'u';
127+ fmt_spec[fmt_len] = '\0';
128+ printf(fmt_spec, (unsigned long long)st->st_dev);
129+ break;
130+ case 'D':
131+ fmt_spec[fmt_len++] = 'l';
132+ fmt_spec[fmt_len++] = 'l';
133+ fmt_spec[fmt_len++] = 'x';
134+ fmt_spec[fmt_len] = '\0';
135+ printf(fmt_spec, (unsigned long long)st->st_dev);
136+ break;
137+ case 'g':
138+ fmt_spec[fmt_len++] = 'u';
139+ fmt_spec[fmt_len] = '\0';
140+ printf(fmt_spec, st->st_gid);
141+ break;
142+ case 'G':
143+ fmt_spec[fmt_len++] = 's';
144+ fmt_spec[fmt_len] = '\0';
145+ gr = getgrgid(st->st_gid);
146+ printf(fmt_spec, gr ? gr->gr_name : "");
147+ break;
148+ case 'h':
149+ fmt_spec[fmt_len++] = 'l';
150+ fmt_spec[fmt_len++] = 'u';
151+ fmt_spec[fmt_len] = '\0';
152+ printf(fmt_spec, (unsigned long)st->st_nlink);
153+ break;
154+ case 'i':
155+ fmt_spec[fmt_len++] = 'l';
156+ fmt_spec[fmt_len++] = 'u';
157+ fmt_spec[fmt_len] = '\0';
158+ printf(fmt_spec, (unsigned long)st->st_ino);
159+ break;
160+ case 'n':
161+ fmt_spec[fmt_len++] = 's';
162+ fmt_spec[fmt_len] = '\0';
163+ printf(fmt_spec, file);
164+ break;
165+ case 'o':
166+ fmt_spec[fmt_len++] = 'l';
167+ fmt_spec[fmt_len++] = 'u';
168+ fmt_spec[fmt_len] = '\0';
169+ printf(fmt_spec, (unsigned long)st->st_blksize);
170+ break;
171+ case 's':
172+ fmt_spec[fmt_len++] = 'l';
173+ fmt_spec[fmt_len++] = 'l';
174+ fmt_spec[fmt_len++] = 'd';
175+ fmt_spec[fmt_len] = '\0';
176+ printf(fmt_spec, (long long)st->st_size);
177+ break;
178+ case 't':
179+ fmt_spec[fmt_len++] = 'x';
180+ fmt_spec[fmt_len] = '\0';
181+ printf(fmt_spec, major(st->st_rdev));
182+ break;
183+ case 'T':
184+ fmt_spec[fmt_len++] = 'x';
185+ fmt_spec[fmt_len] = '\0';
186+ printf(fmt_spec, minor(st->st_rdev));
187+ break;
188+ case 'u':
189+ fmt_spec[fmt_len++] = 'u';
190+ fmt_spec[fmt_len] = '\0';
191+ printf(fmt_spec, st->st_uid);
192+ break;
193+ case 'U':
194+ fmt_spec[fmt_len++] = 's';
195+ fmt_spec[fmt_len] = '\0';
196+ pw = getpwuid(st->st_uid);
197+ printf(fmt_spec, pw ? pw->pw_name : "");
198+ break;
199+ case 'X':
200+ fmt_spec[fmt_len++] = 'l';
201+ fmt_spec[fmt_len++] = 'd';
202+ fmt_spec[fmt_len] = '\0';
203+ printf(fmt_spec, (long)st->st_atime);
204+ break;
205+ case 'Y':
206+ fmt_spec[fmt_len++] = 'l';
207+ fmt_spec[fmt_len++] = 'd';
208+ fmt_spec[fmt_len] = '\0';
209+ printf(fmt_spec, (long)st->st_mtime);
210+ break;
211+ case 'Z':
212+ fmt_spec[fmt_len++] = 'l';
213+ fmt_spec[fmt_len++] = 'd';
214+ fmt_spec[fmt_len] = '\0';
215+ printf(fmt_spec, (long)st->st_ctime);
216+ break;
217+ default:
218+ putchar('%');
219+ if (*p) {
220+ putchar(*p);
221+ } else {
222+ return;
223+ }
224+ break;
225+ }
226+ p++;
227+ }
228+ putchar('\n');
229+}
230+#endif
231+
232+#if FEATURE_STAT_FILESYSTEM
233+static const char *
234+fs_type_name(long type)
235+{
236+ switch (type) {
237+ case 0x9fa0: return "proc";
238+ case 0x62656567: return "beegfs";
239+ case 0xef53: return "ext2/ext3";
240+ case 0x1cd1: return "devpctld";
241+ case 0xf15f: return "ecryptfs";
242+ case 0x65737566: return "fuse";
243+ case 0x4d44: return "msdos";
244+ case 0x564c: return "novell";
245+ case 0x6969: return "nfs";
246+ case 0x534f4654: return "overlay";
247+ case 0x73717368: return "squashfs";
248+ case 0x01021994: return "tmpfs";
249+ case 0x858458f6: return "ramfs";
250+ default: return "unknown";
251+ }
252+}
253+
254+static void
255+show_statfs(const char *file, struct statfs *st)
256+{
257+ printf(" File: \"%s\"\n", file);
258+ printf(" ID: %llx Namelen: %ld Type: %s\n",
259+ (unsigned long long)st->f_fsid.__val[0] | ((unsigned long long)st->f_fsid.__val[1] << 32),
260+ (long)st->f_namelen, fs_type_name(st->f_type));
261+ printf("Block size: %-10ld Fundamental block size: %ld\n",
262+ (long)st->f_bsize, (long)st->f_frsize ? (long)st->f_frsize : (long)st->f_bsize);
263+ printf("Blocks: Total: %-10lld Free: %-10lld Available: %lld\n",
264+ (long long)st->f_blocks, (long long)st->f_bfree, (long long)st->f_bavail);
265+ printf("Inodes: Total: %-10lld Free: %lld\n",
266+ (long long)st->f_files, (long long)st->f_ffree);
267+}
268+
269+#if FEATURE_STAT_FORMAT
270+static void
271+print_fs_custom(const char *file, struct statfs *st, const char *fmt)
272+{
273+ const char *p;
274+ char fmt_spec[64];
275+ int fmt_len;
276+
277+ for (p = fmt; *p; ) {
278+ if (*p == '\\') {
279+ p++;
280+ switch (*p) {
281+ case 'n': putchar('\n'); break;
282+ case 't': putchar('\t'); break;
283+ case 'r': putchar('\r'); break;
284+ case 'b': putchar('\b'); break;
285+ case 'f': putchar('\f'); break;
286+ case '\\': putchar('\\'); break;
287+ case '\0': return;
288+ default: putchar(*p); break;
289+ }
290+ p++;
291+ continue;
292+ }
293+ if (*p != '%') {
294+ putchar(*p);
295+ p++;
296+ continue;
297+ }
298+ p++;
299+ if (*p == '%') {
300+ putchar('%');
301+ p++;
302+ continue;
303+ }
304+ fmt_spec[0] = '%';
305+ fmt_len = 1;
306+ while (*p == '-' || *p == '+' || *p == '0' || *p == ' ' || *p == '#') {
307+ if (fmt_len < 60)
308+ fmt_spec[fmt_len++] = *p;
309+ p++;
310+ }
311+ while (*p >= '0' && *p <= '9') {
312+ if (fmt_len < 60)
313+ fmt_spec[fmt_len++] = *p;
314+ p++;
315+ }
316+ switch (*p) {
317+ case 'a':
318+ fmt_spec[fmt_len++] = 'l';
319+ fmt_spec[fmt_len++] = 'l';
320+ fmt_spec[fmt_len++] = 'd';
321+ fmt_spec[fmt_len] = '\0';
322+ printf(fmt_spec, (long long)st->f_bavail);
323+ break;
324+ case 'b':
325+ fmt_spec[fmt_len++] = 'l';
326+ fmt_spec[fmt_len++] = 'l';
327+ fmt_spec[fmt_len++] = 'd';
328+ fmt_spec[fmt_len] = '\0';
329+ printf(fmt_spec, (long long)st->f_blocks);
330+ break;
331+ case 'c':
332+ fmt_spec[fmt_len++] = 'l';
333+ fmt_spec[fmt_len++] = 'l';
334+ fmt_spec[fmt_len++] = 'd';
335+ fmt_spec[fmt_len] = '\0';
336+ printf(fmt_spec, (long long)st->f_files);
337+ break;
338+ case 'd':
339+ fmt_spec[fmt_len++] = 'l';
340+ fmt_spec[fmt_len++] = 'l';
341+ fmt_spec[fmt_len++] = 'd';
342+ fmt_spec[fmt_len] = '\0';
343+ printf(fmt_spec, (long long)st->f_ffree);
344+ break;
345+ case 'f':
346+ fmt_spec[fmt_len++] = 'l';
347+ fmt_spec[fmt_len++] = 'l';
348+ fmt_spec[fmt_len++] = 'd';
349+ fmt_spec[fmt_len] = '\0';
350+ printf(fmt_spec, (long long)st->f_bfree);
351+ break;
352+ case 'i':
353+ fmt_spec[fmt_len++] = 'l';
354+ fmt_spec[fmt_len++] = 'l';
355+ fmt_spec[fmt_len++] = 'x';
356+ fmt_spec[fmt_len] = '\0';
357+ printf(fmt_spec, (unsigned long long)st->f_fsid.__val[0] | ((unsigned long long)st->f_fsid.__val[1] << 32));
358+ break;
359+ case 'l':
360+ fmt_spec[fmt_len++] = 'l';
361+ fmt_spec[fmt_len++] = 'd';
362+ fmt_spec[fmt_len] = '\0';
363+ printf(fmt_spec, (long)st->f_namelen);
364+ break;
365+ case 'n':
366+ fmt_spec[fmt_len++] = 's';
367+ fmt_spec[fmt_len] = '\0';
368+ printf(fmt_spec, file);
369+ break;
370+ case 's':
371+ fmt_spec[fmt_len++] = 'l';
372+ fmt_spec[fmt_len++] = 'd';
373+ fmt_spec[fmt_len] = '\0';
374+ printf(fmt_spec, (long)st->f_bsize);
375+ break;
376+ case 'S':
377+ fmt_spec[fmt_len++] = 'l';
378+ fmt_spec[fmt_len++] = 'd';
379+ fmt_spec[fmt_len] = '\0';
380+ printf(fmt_spec, (long)st->f_frsize ? (long)st->f_frsize : (long)st->f_bsize);
381+ break;
382+ case 't':
383+ fmt_spec[fmt_len++] = 'l';
384+ fmt_spec[fmt_len++] = 'x';
385+ fmt_spec[fmt_len] = '\0';
386+ printf(fmt_spec, (long)st->f_type);
387+ break;
388+ case 'T':
389+ fmt_spec[fmt_len++] = 's';
390+ fmt_spec[fmt_len] = '\0';
391+ printf(fmt_spec, fs_type_name(st->f_type));
392+ break;
393+ default:
394+ putchar('%');
395+ if (*p) {
396+ putchar(*p);
397+ } else {
398+ return;
399+ }
400+ break;
401+ }
402+ p++;
403+ }
404+ putchar('\n');
405+}
406+#endif
407+#endif
408
409 static void
410 show_stat_terse(const char *file, struct stat *st)
411@@ -47,13 +436,27 @@ show_stat(const char *file, struct stat *st)
412 static void
413 usage(void)
414 {
415- eprintf("usage: %s [-L] [-t] [file...]\n", argv0);
416+ eprintf("usage: %s [-L] [-t]"
417+#if FEATURE_STAT_FILESYSTEM
418+ " [-f]"
419+#endif
420+#if FEATURE_STAT_FORMAT
421+ " [-c format]"
422+#endif
423+ " [file...]\n", argv0);
424 }
425
426 int
427 main(int argc, char *argv[])
428 {
429 struct stat st;
430+#if FEATURE_STAT_FILESYSTEM
431+ struct statfs stfs;
432+ int fflag = 0;
433+#endif
434+#if FEATURE_STAT_FORMAT
435+ char *format = NULL;
436+#endif
437 int i, ret = 0;
438 int (*fn)(const char *, struct stat *) = lstat;
439 char *fnname = "lstat";
440@@ -67,23 +470,76 @@ main(int argc, char *argv[])
441 case 't':
442 showstat = show_stat_terse;
443 break;
444+#if FEATURE_STAT_FILESYSTEM
445+ case 'f':
446+ fflag = 1;
447+ break;
448+#endif
449+#if FEATURE_STAT_FORMAT
450+ case 'c':
451+ format = EARGF(usage());
452+ break;
453+#endif
454 default:
455 usage();
456 } ARGEND;
457
458 if (argc == 0) {
459- if (fstat(0, &st) < 0)
460- eprintf("stat <stdin>:");
461- show_stat("<stdin>", &st);
462+#if FEATURE_STAT_FILESYSTEM
463+ if (fflag) {
464+ if (fstatfs(0, &stfs) < 0)
465+ eprintf("fstatfs <stdin>:");
466+#if FEATURE_STAT_FORMAT
467+ if (format)
468+ print_fs_custom("<stdin>", &stfs, format);
469+ else
470+#endif
471+ show_statfs("<stdin>", &stfs);
472+ } else {
473+#endif
474+ if (fstat(0, &st) < 0)
475+ eprintf("fstat <stdin>:");
476+#if FEATURE_STAT_FORMAT
477+ if (format)
478+ print_custom("<stdin>", &st, format);
479+ else
480+#endif
481+ show_stat("<stdin>", &st);
482+#if FEATURE_STAT_FILESYSTEM
483+ }
484+#endif
485 }
486
487 for (i = 0; i < argc; i++) {
488- if (fn(argv[i], &st) == -1) {
489- weprintf("%s %s:", fnname, argv[i]);
490- ret = 1;
491- continue;
492+#if FEATURE_STAT_FILESYSTEM
493+ if (fflag) {
494+ if (statfs(argv[i], &stfs) == -1) {
495+ weprintf("statfs %s:", argv[i]);
496+ ret = 1;
497+ continue;
498+ }
499+#if FEATURE_STAT_FORMAT
500+ if (format)
501+ print_fs_custom(argv[i], &stfs, format);
502+ else
503+#endif
504+ show_statfs(argv[i], &stfs);
505+ } else {
506+#endif
507+ if (fn(argv[i], &st) == -1) {
508+ weprintf("%s %s:", fnname, argv[i]);
509+ ret = 1;
510+ continue;
511+ }
512+#if FEATURE_STAT_FORMAT
513+ if (format)
514+ print_custom(argv[i], &st, format);
515+ else
516+#endif
517+ showstat(argv[i], &st);
518+#if FEATURE_STAT_FILESYSTEM
519 }
520- showstat(argv[i], &st);
521+#endif
522 }
523
524 return ret;
+106,
-21
1@@ -1,26 +1,24 @@
2-/* See LICENSE file for copyright and license details. */
3-#include <sys/stat.h>
4-#include <sys/time.h>
5-#include <sys/types.h>
6-#ifndef major
7-#include <sys/sysmacros.h>
8-#endif
9+#include "config.h"
10+#include "fs.h"
11+#include "utf.h"
12+#include "util.h"
13
14 #include <assert.h>
15 #include <errno.h>
16 #include <fcntl.h>
17+#include <fnmatch.h>
18 #include <grp.h>
19 #include <libgen.h>
20 #include <pwd.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24+#include <sys/stat.h>
25+#include <sys/sysmacros.h>
26+#include <sys/time.h>
27+#include <sys/types.h>
28 #include <unistd.h>
29
30-#include "fs.h"
31-#include "utf.h"
32-#include "util.h"
33-
34 #ifdef STD_NON_POSIX
35 static void
36 safe_puts(const char *s)
37@@ -98,6 +96,33 @@ static dev_t tardev;
38
39 static int mflag, vflag;
40 static int filtermode;
41+
42+#if FEATURE_TAR_EXCLUDE
43+static char **excludes = NULL;
44+static size_t excludes_cnt = 0;
45+
46+static void
47+add_exclude(const char *pattern)
48+{
49+ excludes = ereallocarray(excludes, excludes_cnt + 1, sizeof(*excludes));
50+ excludes[excludes_cnt++] = estrdup(pattern);
51+}
52+
53+static int
54+is_excluded(const char *path)
55+{
56+ size_t i;
57+ const char *base = strrchr(path, '/');
58+ base = base ? base + 1 : path;
59+
60+ for (i = 0; i < excludes_cnt; i++) {
61+ if (fnmatch(excludes[i], path, 0) == 0 ||
62+ fnmatch(excludes[i], base, 0) == 0)
63+ return 1;
64+ }
65+ return 0;
66+}
67+#endif
68 static const char *filtertool;
69
70 static const char *filtertools[] = {
71@@ -138,6 +163,7 @@ comp(int fd, const char *tool, const char *flags)
72 switch (fork()) {
73 case -1:
74 eprintf("fork:");
75+ /* fallthrough */
76 case 0:
77 dup2(fd, 1);
78 dup2(fds[0], 0);
79@@ -163,6 +189,7 @@ decomp(int fd, const char *tool, const char *flags)
80 switch (fork()) {
81 case -1:
82 eprintf("fork:");
83+ /* fallthrough */
84 case 0:
85 dup2(fd, 0);
86 dup2(fds[1], 1);
87@@ -197,7 +224,7 @@ ewrite(int fd, const void *buf, size_t n)
88 {
89 ssize_t r;
90
91- if ((r = write(fd, buf, n)) != n)
92+ if ((r = write(fd, buf, n)) < 0 || (size_t)r != n)
93 eprintf("write:");
94 return r;
95 }
96@@ -213,6 +240,7 @@ chksum(struct header *h)
97 return sum;
98 }
99
100+#if FEATURE_TAR_CREATE
101 static void
102 putoctal(char *dst, unsigned num, int size)
103 {
104@@ -224,8 +252,17 @@ static int
105 archive(const char *path)
106 {
107 static const struct header blank = {
108- "././@LongLink", "0000600", "0000000", "0000000", "00000000000",
109- "00000000000" , " ", AREG , "" , "ustar", "00",
110+ .name = "././@LongLink",
111+ .mode = "0000600",
112+ .uid = "0000000",
113+ .gid = "0000000",
114+ .size = "00000000000",
115+ .mtime = "00000000000",
116+ .chksum = " ",
117+ .type = AREG,
118+ .linkname = "",
119+ .magic = "ustar",
120+ .version = { '0', '0' }
121 };
122 char b[BLKSIZ + BLKSIZ], *p;
123 struct header *h = (struct header *)b;
124@@ -247,7 +284,7 @@ archive(const char *path)
125
126 *h = blank;
127 n = strlcpy(h->name, path, sizeof(h->name));
128- if (n >= sizeof(h->name)) {
129+ if ((size_t)n >= sizeof(h->name)) {
130 *++h = blank;
131 h->type = 'L';
132 putoctal(h->size, n, sizeof(h->size));
133@@ -255,9 +292,9 @@ archive(const char *path)
134 ewrite(tarfd, (char *)h, BLKSIZ);
135
136 for (p = (char *)path; n > 0; n -= BLKSIZ, p += BLKSIZ) {
137- if (n < BLKSIZ) {
138+ if ((size_t)n < BLKSIZ) {
139 p = memcpy(h--, p, n);
140- memset(p + n, 0, BLKSIZ - n);
141+ memset(p + n, 0, BLKSIZ - (size_t)n);
142 }
143 ewrite(tarfd, p, BLKSIZ);
144 }
145@@ -296,8 +333,8 @@ archive(const char *path)
146
147 if (fd != -1) {
148 while ((l = eread(fd, b, BLKSIZ)) > 0) {
149- if (l < BLKSIZ)
150- memset(b + l, 0, BLKSIZ - l);
151+ if ((size_t)l < BLKSIZ)
152+ memset(b + l, 0, BLKSIZ - (size_t)l);
153 ewrite(tarfd, b, BLKSIZ);
154 }
155 close(fd);
156@@ -305,6 +342,7 @@ archive(const char *path)
157
158 return 0;
159 }
160+#endif
161
162 static int
163 unarchive(char *fname, ssize_t l, char b[BLKSIZ])
164@@ -420,14 +458,21 @@ skipblk(ssize_t l)
165 static int
166 print(char *fname, ssize_t l, char b[BLKSIZ])
167 {
168+ (void)b;
169 safe_puts(fname);
170 skipblk(l);
171 return 0;
172 }
173
174+#if FEATURE_TAR_CREATE
175 static void
176 c(int dirfd, const char *name, struct stat *st, void *data, struct recursor *r)
177 {
178+ (void)data;
179+#if FEATURE_TAR_EXCLUDE
180+ if (is_excluded(r->path))
181+ return;
182+#endif
183 archive(r->path);
184 if (vflag)
185 safe_puts(r->path);
186@@ -435,6 +480,7 @@ c(int dirfd, const char *name, struct stat *st, void *data, struct recursor *r)
187 if (S_ISDIR(st->st_mode))
188 recurse(dirfd, name, NULL, r);
189 }
190+#endif
191
192 static void
193 sanitize(struct header *h)
194@@ -489,7 +535,7 @@ chktar(struct header *h)
195 tmp[i] = '\0';
196 }
197 sum = strtol(tmp, &err, 8);
198- if (sum < 0 || sum >= BLKSIZ*256 || *err != '\0') {
199+ if (sum < 0 || sum >= (long)(BLKSIZ*256) || *err != '\0') {
200 reason = "invalid checksum";
201 goto bad;
202 }
203@@ -594,27 +640,40 @@ int argn;
204 static void
205 usage(void)
206 {
207+#if FEATURE_TAR_CREATE
208 eprintf("usage: %s [x | t | -x | -t] [-C dir] [-J | -Z | -a | -j | -z] [-m] [-p] "
209 "[-f file] [file ...]\n"
210 " %s [c | -c] [-C dir] [-J | -Z | -a | -j | -z] [-h] path ... "
211 "[-f file]\n", argv0, argv0);
212+#else
213+ eprintf("usage: %s [x | t | -x | -t] [-C dir] [-J | -Z | -a | -j | -z] [-m] [-p] "
214+ "[-f file] [file ...]\n", argv0);
215+#endif
216 }
217
218 int
219 main(int argc, char *argv[])
220 {
221+#if FEATURE_TAR_CREATE
222 struct recursor r = { .fn = c, .follow = 'P', .flags = DIRFIRST };
223+#endif
224 struct stat st;
225 char *file = NULL, *dir = ".", mode = '\0';
226 int fd;
227
228 argv0 = argv[0];
229+#if FEATURE_TAR_CREATE
230 if (argc > 1 && strchr("cxt", mode = *argv[1]))
231+#else
232+ if (argc > 1 && strchr("xt", mode = *argv[1]))
233+#endif
234 *(argv[1]+1) ? *argv[1] = '-' : (*++argv = argv0, --argc);
235
236 ARGBEGIN {
237 case 'x':
238+#if FEATURE_TAR_CREATE
239 case 'c':
240+#endif
241 case 't':
242 mode = ARGC();
243 break;
244@@ -636,18 +695,35 @@ main(int argc, char *argv[])
245 filtertool = filtertools[filtermode];
246 break;
247 case 'h':
248+#if FEATURE_TAR_CREATE
249 r.follow = 'L';
250+#endif
251 break;
252 case 'v':
253 vflag = 1;
254 break;
255 case 'p':
256- break; /* Do nothing as already default behaviour */
257+ break; /* do nothing as already default behaviour */
258+#if FEATURE_TAR_EXCLUDE
259+ case '-':
260+ if (strncmp(argv[0], "-exclude=", 9) == 0) {
261+ add_exclude(argv[0] + 9);
262+ brk_ = 1;
263+ } else if (strcmp(argv[0], "-exclude") == 0) {
264+ argv[0] = "-";
265+ add_exclude(EARGF(usage()));
266+ brk_ = 1;
267+ } else {
268+ usage();
269+ }
270+ break;
271+#endif
272 default:
273 usage();
274 } ARGEND
275
276 switch (mode) {
277+#if FEATURE_TAR_CREATE
278 case 'c':
279 if (!argc)
280 usage();
281@@ -670,6 +746,7 @@ main(int argc, char *argv[])
282 for (; *argv; argc--, argv++)
283 recurse(AT_FDCWD, *argv, NULL, &r);
284 break;
285+#endif
286 case 't':
287 case 'x':
288 tarfd = 0;
289@@ -693,5 +770,13 @@ main(int argc, char *argv[])
290 usage();
291 }
292
293+#if FEATURE_TAR_EXCLUDE
294+ if (excludes) {
295+ size_t i;
296+ for (i = 0; i < excludes_cnt; i++)
297+ free(excludes[i]);
298+ free(excludes);
299+ }
300+#endif
301 return recurse_status;
302 }
+2,
-0
1@@ -45,6 +45,8 @@ install(const char *s1, const char *s2, int depth)
2 {
3 int f1, f2;
4
5+ (void)depth;
6+
7 if ((f1 = open(s1, O_RDONLY)) < 0)
8 eprintf("open %s:", s1);
9 if ((f2 = creat(s2, 0600)) < 0) {
+243,
-2
1@@ -7,6 +7,214 @@ RANLIB = ranlib
2 AR = ar
3 ARFLAGS = rc
4
5+# build toggles for groups of binaries
6+BUILD_POSIX = 1
7+BUILD_LINUX = 1
8+BUILD_NET = 1
9+BUILD_XSI = 1
10+BUILD_PSEUDO = 1
11+BUILD_MAKE = 1
12+
13+# posix tools
14+BUILD_POSIX_BASENAME = $(BUILD_POSIX)
15+BUILD_POSIX_CAL = $(BUILD_POSIX)
16+BUILD_POSIX_CAT = $(BUILD_POSIX)
17+BUILD_POSIX_CHGRP = $(BUILD_POSIX)
18+BUILD_POSIX_CHMOD = $(BUILD_POSIX)
19+BUILD_POSIX_CHOWN = $(BUILD_POSIX)
20+BUILD_POSIX_CKSUM = $(BUILD_POSIX)
21+BUILD_POSIX_CMP = $(BUILD_POSIX)
22+BUILD_POSIX_COMM = $(BUILD_POSIX)
23+BUILD_POSIX_CP = $(BUILD_POSIX)
24+BUILD_POSIX_CUT = $(BUILD_POSIX)
25+BUILD_POSIX_DATE = $(BUILD_POSIX)
26+BUILD_POSIX_DD = $(BUILD_POSIX)
27+BUILD_POSIX_DF = $(BUILD_POSIX)
28+BUILD_POSIX_DIRNAME = $(BUILD_POSIX)
29+BUILD_POSIX_DU = $(BUILD_POSIX)
30+BUILD_POSIX_ECHO = $(BUILD_POSIX)
31+BUILD_POSIX_ED = $(BUILD_POSIX)
32+BUILD_POSIX_ENV = $(BUILD_POSIX)
33+BUILD_POSIX_EXPAND = $(BUILD_POSIX)
34+BUILD_POSIX_EXPR = $(BUILD_POSIX)
35+BUILD_POSIX_FALSE = $(BUILD_POSIX)
36+BUILD_POSIX_FIND = $(BUILD_POSIX)
37+BUILD_POSIX_FOLD = $(BUILD_POSIX)
38+BUILD_POSIX_GETCONF = $(BUILD_POSIX)
39+BUILD_POSIX_GREP = $(BUILD_POSIX)
40+BUILD_POSIX_HEAD = $(BUILD_POSIX)
41+BUILD_POSIX_ID = $(BUILD_POSIX)
42+BUILD_POSIX_JOIN = $(BUILD_POSIX)
43+BUILD_POSIX_KILL = $(BUILD_POSIX)
44+BUILD_POSIX_LINK = $(BUILD_POSIX)
45+BUILD_POSIX_LN = $(BUILD_POSIX)
46+BUILD_POSIX_LOGGER = $(BUILD_POSIX)
47+BUILD_POSIX_LOGNAME = $(BUILD_POSIX)
48+BUILD_POSIX_LS = $(BUILD_POSIX)
49+BUILD_POSIX_MESG = $(BUILD_POSIX)
50+BUILD_POSIX_MKDIR = $(BUILD_POSIX)
51+BUILD_POSIX_MKFIFO = $(BUILD_POSIX)
52+BUILD_POSIX_MV = $(BUILD_POSIX)
53+BUILD_POSIX_NICE = $(BUILD_POSIX)
54+BUILD_POSIX_NL = $(BUILD_POSIX)
55+BUILD_POSIX_NOHUP = $(BUILD_POSIX)
56+BUILD_POSIX_OD = $(BUILD_POSIX)
57+BUILD_POSIX_PASTE = $(BUILD_POSIX)
58+BUILD_POSIX_PATHCHK = $(BUILD_POSIX)
59+BUILD_POSIX_PRINTF = $(BUILD_POSIX)
60+BUILD_POSIX_PS = $(BUILD_POSIX)
61+BUILD_POSIX_PWD = $(BUILD_POSIX)
62+BUILD_POSIX_READLINK = $(BUILD_POSIX)
63+BUILD_POSIX_RENICE = $(BUILD_POSIX)
64+BUILD_POSIX_RM = $(BUILD_POSIX)
65+BUILD_POSIX_RMDIR = $(BUILD_POSIX)
66+BUILD_POSIX_SED = $(BUILD_POSIX)
67+BUILD_POSIX_SLEEP = $(BUILD_POSIX)
68+BUILD_POSIX_SORT = $(BUILD_POSIX)
69+BUILD_POSIX_SPLIT = $(BUILD_POSIX)
70+BUILD_POSIX_TAIL = $(BUILD_POSIX)
71+BUILD_POSIX_TEE = $(BUILD_POSIX)
72+BUILD_POSIX_TEST = $(BUILD_POSIX)
73+BUILD_POSIX_TIME = $(BUILD_POSIX)
74+BUILD_POSIX_TOUCH = $(BUILD_POSIX)
75+BUILD_POSIX_TR = $(BUILD_POSIX)
76+BUILD_POSIX_TRUE = $(BUILD_POSIX)
77+BUILD_POSIX_TSORT = $(BUILD_POSIX)
78+BUILD_POSIX_TTY = $(BUILD_POSIX)
79+BUILD_POSIX_UNAME = $(BUILD_POSIX)
80+BUILD_POSIX_UNEXPAND = $(BUILD_POSIX)
81+BUILD_POSIX_UNIQ = $(BUILD_POSIX)
82+BUILD_POSIX_UNLINK = $(BUILD_POSIX)
83+BUILD_POSIX_UUDECODE = $(BUILD_POSIX)
84+BUILD_POSIX_UUENCODE = $(BUILD_POSIX)
85+BUILD_POSIX_WC = $(BUILD_POSIX)
86+BUILD_POSIX_WHO = $(BUILD_POSIX)
87+BUILD_POSIX_XARGS = $(BUILD_POSIX)
88+BUILD_POSIX_AWK = $(BUILD_POSIX)
89+BUILD_POSIX_SH = $(BUILD_POSIX)
90+BUILD_POSIX_PAX = $(BUILD_POSIX)
91+
92+# linux tools
93+BUILD_LINUX_BLKDISCARD = $(BUILD_LINUX)
94+BUILD_LINUX_CHVT = $(BUILD_LINUX)
95+BUILD_LINUX_CTRLALTDEL = $(BUILD_LINUX)
96+BUILD_LINUX_DMESG = $(BUILD_LINUX)
97+BUILD_LINUX_EJECT = $(BUILD_LINUX)
98+BUILD_LINUX_FALLOCATE = $(BUILD_LINUX)
99+BUILD_LINUX_FREE = $(BUILD_LINUX)
100+BUILD_LINUX_FREERAMDISK = $(BUILD_LINUX)
101+BUILD_LINUX_FSFREEZE = $(BUILD_LINUX)
102+BUILD_LINUX_HWCLOCK = $(BUILD_LINUX)
103+BUILD_LINUX_INSMOD = $(BUILD_LINUX)
104+BUILD_LINUX_LSMOD = $(BUILD_LINUX)
105+BUILD_LINUX_MKSWAP = $(BUILD_LINUX)
106+BUILD_LINUX_MOUNT = $(BUILD_LINUX)
107+BUILD_LINUX_MOUNTPOINT = $(BUILD_LINUX)
108+BUILD_LINUX_PIDOF = $(BUILD_LINUX)
109+BUILD_LINUX_PIVOT_ROOT = $(BUILD_LINUX)
110+BUILD_LINUX_PWDX = $(BUILD_LINUX)
111+BUILD_LINUX_READAHEAD = $(BUILD_LINUX)
112+BUILD_LINUX_RMMOD = $(BUILD_LINUX)
113+BUILD_LINUX_SWAPLABEL = $(BUILD_LINUX)
114+BUILD_LINUX_SWAPOFF = $(BUILD_LINUX)
115+BUILD_LINUX_SWAPON = $(BUILD_LINUX)
116+BUILD_LINUX_SWITCH_ROOT = $(BUILD_LINUX)
117+BUILD_LINUX_UMOUNT = $(BUILD_LINUX)
118+BUILD_LINUX_UNSHARE = $(BUILD_LINUX)
119+BUILD_LINUX_UPTIME = $(BUILD_LINUX)
120+BUILD_LINUX_VTALLOW = $(BUILD_LINUX)
121+
122+# net tools
123+BUILD_NET_NETCAT = $(BUILD_NET)
124+BUILD_NET_TFTP = $(BUILD_NET)
125+BUILD_NET_TUNCTL = $(BUILD_NET)
126+BUILD_NET_WGET = $(BUILD_NET)
127+BUILD_NET_PING = $(BUILD_NET)
128+BUILD_NET_IFCONFIG = $(BUILD_NET)
129+BUILD_NET_HOST = $(BUILD_NET)
130+BUILD_NET_HTTPD = $(BUILD_NET)
131+
132+# xsi tools
133+BUILD_XSI_MKNOD = $(BUILD_XSI)
134+BUILD_XSI_PASSWD = $(BUILD_XSI)
135+BUILD_XSI_SU = $(BUILD_XSI)
136+
137+# pseudo tools
138+BUILD_PSEUDO_CHROOT = $(BUILD_PSEUDO)
139+BUILD_PSEUDO_CLEAR = $(BUILD_PSEUDO)
140+BUILD_PSEUDO_COLS = $(BUILD_PSEUDO)
141+BUILD_PSEUDO_CRON = $(BUILD_PSEUDO)
142+BUILD_PSEUDO_FLOCK = $(BUILD_PSEUDO)
143+BUILD_PSEUDO_GETTY = $(BUILD_PSEUDO)
144+BUILD_PSEUDO_HALT = $(BUILD_PSEUDO)
145+BUILD_PSEUDO_HOSTNAME = $(BUILD_PSEUDO)
146+BUILD_PSEUDO_KILLALL5 = $(BUILD_PSEUDO)
147+BUILD_PSEUDO_LAST = $(BUILD_PSEUDO)
148+BUILD_PSEUDO_LASTLOG = $(BUILD_PSEUDO)
149+BUILD_PSEUDO_LOGIN = $(BUILD_PSEUDO)
150+BUILD_PSEUDO_MD5SUM = $(BUILD_PSEUDO)
151+BUILD_PSEUDO_MKTEMP = $(BUILD_PSEUDO)
152+BUILD_PSEUDO_NOLOGIN = $(BUILD_PSEUDO)
153+BUILD_PSEUDO_PAGESIZE = $(BUILD_PSEUDO)
154+BUILD_PSEUDO_PRINTENV = $(BUILD_PSEUDO)
155+BUILD_PSEUDO_RESPAWN = $(BUILD_PSEUDO)
156+BUILD_PSEUDO_REV = $(BUILD_PSEUDO)
157+BUILD_PSEUDO_SEQ = $(BUILD_PSEUDO)
158+BUILD_PSEUDO_SETSID = $(BUILD_PSEUDO)
159+BUILD_PSEUDO_SHA1SUM = $(BUILD_PSEUDO)
160+BUILD_PSEUDO_SHA224SUM = $(BUILD_PSEUDO)
161+BUILD_PSEUDO_SHA256SUM = $(BUILD_PSEUDO)
162+BUILD_PSEUDO_SHA384SUM = $(BUILD_PSEUDO)
163+BUILD_PSEUDO_SHA512SUM = $(BUILD_PSEUDO)
164+BUILD_PSEUDO_SHA512_224SUM = $(BUILD_PSEUDO)
165+BUILD_PSEUDO_SHA512_256SUM = $(BUILD_PSEUDO)
166+BUILD_PSEUDO_SPONGE = $(BUILD_PSEUDO)
167+BUILD_PSEUDO_STAT = $(BUILD_PSEUDO)
168+BUILD_PSEUDO_TAR = $(BUILD_PSEUDO)
169+BUILD_PSEUDO_TRUNCATE = $(BUILD_PSEUDO)
170+BUILD_PSEUDO_WATCH = $(BUILD_PSEUDO)
171+BUILD_PSEUDO_WHICH = $(BUILD_PSEUDO)
172+BUILD_PSEUDO_WHOAMI = $(BUILD_PSEUDO)
173+BUILD_PSEUDO_XINSTALL = $(BUILD_PSEUDO)
174+BUILD_PSEUDO_YES = $(BUILD_PSEUDO)
175+BUILD_PSEUDO_BASE64 = $(BUILD_PSEUDO)
176+
177+# make tool
178+BUILD_MAKE_MAKE = $(BUILD_MAKE)
179+
180+# feature gates for specific utilities
181+FEATURE_FIND_DELETE = 1
182+FEATURE_FIND_QUIT = 1
183+FEATURE_FIND_EMPTY = 1
184+FEATURE_FIND_INUM = 1
185+FEATURE_FIND_SAMEFILE = 1
186+FEATURE_FIND_MAXDEPTH = 1
187+FEATURE_FIND_MINDEPTH = 1
188+FEATURE_FIND_MMIN = 1
189+FEATURE_FIND_AMIN = 1
190+FEATURE_FIND_CMIN = 1
191+FEATURE_FIND_INAME = 1
192+FEATURE_FIND_IPATH = 1
193+FEATURE_FIND_REGEX = 1
194+FEATURE_FIND_PRINT0 = 1
195+FEATURE_SED_INPLACE = 1
196+FEATURE_GREP_CONTEXT = 1
197+FEATURE_GREP_MAX_COUNT = 1
198+FEATURE_TAR_CREATE = 1
199+FEATURE_TAR_EXCLUDE = 1
200+FEATURE_STAT_FILESYSTEM = 1
201+FEATURE_STAT_FORMAT = 1
202+FEATURE_SORT_BIG = 1
203+FEATURE_SORT_STABLE = 1
204+FEATURE_OD_ENDIAN = 1
205+FEATURE_SH_HISTEDIT = 0
206+FEATURE_SH_LOCAL = 1
207+FEATURE_SH_LET = 1
208+FEATURE_SH_ULIMIT = 1
209+FEATURE_SH_SETVAR = 1
210+FEATURE_SH_WORDEXP = 1
211+FEATURE_CAL_EXT = 1
212+
213 CPPFLAGS =\
214 -Ishared\
215 -DPREFIX=\"$(PREFIX)\"\
216@@ -16,8 +224,41 @@ CPPFLAGS =\
217 -D_BSD_SOURCE\
218 -D_XOPEN_SOURCE=700\
219 -D_FILE_OFFSET_BITS=64\
220- -DSTD_NON_POSIX
221+ -DSTD_NON_POSIX\
222+ -DFEATURE_FIND_DELETE=$(FEATURE_FIND_DELETE)\
223+ -DFEATURE_FIND_QUIT=$(FEATURE_FIND_QUIT)\
224+ -DFEATURE_FIND_EMPTY=$(FEATURE_FIND_EMPTY)\
225+ -DFEATURE_FIND_INUM=$(FEATURE_FIND_INUM)\
226+ -DFEATURE_FIND_SAMEFILE=$(FEATURE_FIND_SAMEFILE)\
227+ -DFEATURE_FIND_MAXDEPTH=$(FEATURE_FIND_MAXDEPTH)\
228+ -DFEATURE_FIND_MINDEPTH=$(FEATURE_FIND_MINDEPTH)\
229+ -DFEATURE_FIND_MMIN=$(FEATURE_FIND_MMIN)\
230+ -DFEATURE_FIND_AMIN=$(FEATURE_FIND_AMIN)\
231+ -DFEATURE_FIND_CMIN=$(FEATURE_FIND_CMIN)\
232+ -DFEATURE_FIND_INAME=$(FEATURE_FIND_INAME)\
233+ -DFEATURE_FIND_IPATH=$(FEATURE_FIND_IPATH)\
234+ -DFEATURE_FIND_REGEX=$(FEATURE_FIND_REGEX)\
235+ -DFEATURE_FIND_PRINT0=$(FEATURE_FIND_PRINT0)\
236+ -DFEATURE_SED_INPLACE=$(FEATURE_SED_INPLACE)\
237+ -DFEATURE_GREP_CONTEXT=$(FEATURE_GREP_CONTEXT)\
238+ -DFEATURE_GREP_MAX_COUNT=$(FEATURE_GREP_MAX_COUNT)\
239+ -DFEATURE_TAR_CREATE=$(FEATURE_TAR_CREATE)\
240+ -DFEATURE_TAR_EXCLUDE=$(FEATURE_TAR_EXCLUDE)\
241+ -DFEATURE_STAT_FILESYSTEM=$(FEATURE_STAT_FILESYSTEM)\
242+ -DFEATURE_STAT_FORMAT=$(FEATURE_STAT_FORMAT)\
243+ -DFEATURE_SORT_BIG=$(FEATURE_SORT_BIG)\
244+ -DFEATURE_SORT_STABLE=$(FEATURE_SORT_STABLE)\
245+ -DFEATURE_OD_ENDIAN=$(FEATURE_OD_ENDIAN)\
246+ -DFEATURE_SH_HISTEDIT=$(FEATURE_SH_HISTEDIT)\
247+ -DFEATURE_SH_LOCAL=$(FEATURE_SH_LOCAL)\
248+ -DFEATURE_SH_LET=$(FEATURE_SH_LET)\
249+ -DFEATURE_SH_ULIMIT=$(FEATURE_SH_ULIMIT)\
250+ -DFEATURE_SH_SETVAR=$(FEATURE_SH_SETVAR)\
251+ -DFEATURE_SH_WORDEXP=$(FEATURE_SH_WORDEXP)\
252+ -DFEATURE_CAL_EXT=$(FEATURE_CAL_EXT)
253
254 CFLAGS = -std=c99 -Wall -Wextra -pedantic
255 LDFLAGS =
256-LDLIBS = -lcrypt -lresolv
257+LDLIBS_HISTEDIT_0 =
258+LDLIBS_HISTEDIT_1 = -ledit
259+LDLIBS = -lcrypt -lresolv $(LDLIBS_HISTEDIT_$(FEATURE_SH_HISTEDIT))
1@@ -1,10 +1,83 @@
2 /* See LICENSE file for copyright and license details. */
3
4-#define ENV_PATH "/bin"
5-#define PW_CIPHER "$6$" /* SHA-512 */
6+#define ENV_PATH "/bin"
7+#define PW_CIPHER "$6$" /* sha-512 */
8 #undef UTMP_PATH
9-#define UTMP_PATH "/var/run/utmp"
10+#define UTMP_PATH "/var/run/utmp"
11 #undef BTMP_PATH
12-#define BTMP_PATH "/var/log/btmp"
13+#define BTMP_PATH "/var/log/btmp"
14 #undef WTMP_PATH
15-#define WTMP_PATH "/var/log/wtmp"
16+#define WTMP_PATH "/var/log/wtmp"
17+
18+#ifndef FEATURE_FIND_DELETE
19+#define FEATURE_FIND_DELETE 1
20+#endif
21+#ifndef FEATURE_FIND_QUIT
22+#define FEATURE_FIND_QUIT 1
23+#endif
24+#ifndef FEATURE_FIND_EMPTY
25+#define FEATURE_FIND_EMPTY 1
26+#endif
27+#ifndef FEATURE_FIND_INUM
28+#define FEATURE_FIND_INUM 1
29+#endif
30+#ifndef FEATURE_FIND_SAMEFILE
31+#define FEATURE_FIND_SAMEFILE 1
32+#endif
33+#ifndef FEATURE_FIND_MAXDEPTH
34+#define FEATURE_FIND_MAXDEPTH 1
35+#endif
36+#ifndef FEATURE_FIND_MINDEPTH
37+#define FEATURE_FIND_MINDEPTH 1
38+#endif
39+#ifndef FEATURE_FIND_MMIN
40+#define FEATURE_FIND_MMIN 1
41+#endif
42+#ifndef FEATURE_FIND_AMIN
43+#define FEATURE_FIND_AMIN 1
44+#endif
45+#ifndef FEATURE_FIND_CMIN
46+#define FEATURE_FIND_CMIN 1
47+#endif
48+#ifndef FEATURE_FIND_INAME
49+#define FEATURE_FIND_INAME 1
50+#endif
51+#ifndef FEATURE_FIND_IPATH
52+#define FEATURE_FIND_IPATH 1
53+#endif
54+#ifndef FEATURE_FIND_REGEX
55+#define FEATURE_FIND_REGEX 1
56+#endif
57+#ifndef FEATURE_FIND_PRINT0
58+#define FEATURE_FIND_PRINT0 1
59+#endif
60+#ifndef FEATURE_SED_INPLACE
61+#define FEATURE_SED_INPLACE 1
62+#endif
63+#ifndef FEATURE_GREP_CONTEXT
64+#define FEATURE_GREP_CONTEXT 1
65+#endif
66+#ifndef FEATURE_GREP_MAX_COUNT
67+#define FEATURE_GREP_MAX_COUNT 1
68+#endif
69+#ifndef FEATURE_TAR_CREATE
70+#define FEATURE_TAR_CREATE 1
71+#endif
72+#ifndef FEATURE_TAR_EXCLUDE
73+#define FEATURE_TAR_EXCLUDE 1
74+#endif
75+#ifndef FEATURE_STAT_FILESYSTEM
76+#define FEATURE_STAT_FILESYSTEM 1
77+#endif
78+#ifndef FEATURE_STAT_FORMAT
79+#define FEATURE_STAT_FORMAT 1
80+#endif
81+#ifndef FEATURE_SORT_BIG
82+#define FEATURE_SORT_BIG 1
83+#endif
84+#ifndef FEATURE_SORT_STABLE
85+#define FEATURE_SORT_STABLE 1
86+#endif
87+#ifndef FEATURE_OD_ENDIAN
88+#define FEATURE_OD_ENDIAN 1
89+#endif
1@@ -54,7 +54,7 @@ mdchecklist(FILE *listfp, struct crypt_ops *ops, uint8_t *md, size_t sz,
2 (*formatsucks)++;
3 continue;
4 }
5- if (file - line != sz * 2) {
6+ if ((size_t)(file - line) != sz * 2) {
7 (*formatsucks)++; /* checksum length mismatch */
8 continue;
9 }
1@@ -29,7 +29,7 @@ enmasse(int argc, char *argv[], int (*fn)(const char *, const char *, int))
2 len = snprintf(buf, sizeof(buf), "%s%s", dir, basename(argv[i]));
3 else
4 len = snprintf(buf, sizeof(buf), "%s/%s", dir, basename(argv[i]));
5- if (len < 0 || len >= sizeof(buf)) {
6+ if (len < 0 || (size_t)len >= sizeof(buf)) {
7 eprintf("%s/%s: filename too long\n", dir,
8 basename(argv[i]));
9 }
1@@ -13,7 +13,7 @@ humansize(off_t n)
2 double size;
3 int i;
4
5- for (size = n, i = 0; size >= 1024 && i < strlen(postfixes); i++)
6+ for (size = n, i = 0; size >= 1024 && i < (int)strlen(postfixes); i++)
7 size /= 1024;
8
9 if (!i)
1@@ -16,6 +16,8 @@ rm(int dirfd, const char *name, struct stat *st, void *data, struct recursor *r)
2 {
3 int quiet, ask, write, flags, ignore;
4
5+ (void)data;
6+
7 ignore = r->flags & IGNORE;
8 quiet = r->flags & SILENT;
9 ask = r->flags & CONFIRM;
10@@ -27,7 +29,7 @@ rm(int dirfd, const char *name, struct stat *st, void *data, struct recursor *r)
11 goto err;
12 }
13
14- if (!quiet && (!write && isatty(0) || ask)) {
15+ if (!quiet && ((!write && isatty(0)) || ask)) {
16 if (!confirm("remove file '%s'? ", r->path))
17 return;
18 }
1@@ -0,0 +1,163 @@
2+/* See LICENSE file for copyright and license details. */
3+#include "../sig.h"
4+
5+#include <signal.h>
6+#include <stdlib.h>
7+#include <string.h>
8+#include <strings.h>
9+
10+const char *const aruu_sys_signame[NSIG] = {
11+ [0] = "EXIT",
12+#ifdef SIGHUP
13+ [SIGHUP] = "HUP",
14+#endif
15+#ifdef SIGINT
16+ [SIGINT] = "INT",
17+#endif
18+#ifdef SIGQUIT
19+ [SIGQUIT] = "QUIT",
20+#endif
21+#ifdef SIGILL
22+ [SIGILL] = "ILL",
23+#endif
24+#ifdef SIGTRAP
25+ [SIGTRAP] = "TRAP",
26+#endif
27+#ifdef SIGABRT
28+ [SIGABRT] = "ABRT",
29+#endif
30+#ifdef SIGBUS
31+ [SIGBUS] = "BUS",
32+#endif
33+#ifdef SIGFPE
34+ [SIGFPE] = "FPE",
35+#endif
36+#ifdef SIGKILL
37+ [SIGKILL] = "KILL",
38+#endif
39+#ifdef SIGUSR1
40+ [SIGUSR1] = "USR1",
41+#endif
42+#ifdef SIGSEGV
43+ [SIGSEGV] = "SEGV",
44+#endif
45+#ifdef SIGUSR2
46+ [SIGUSR2] = "USR2",
47+#endif
48+#ifdef SIGPIPE
49+ [SIGPIPE] = "PIPE",
50+#endif
51+#ifdef SIGALRM
52+ [SIGALRM] = "ALRM",
53+#endif
54+#ifdef SIGTERM
55+ [SIGTERM] = "TERM",
56+#endif
57+#ifdef SIGCHLD
58+ [SIGCHLD] = "CHLD",
59+#endif
60+#ifdef SIGCONT
61+ [SIGCONT] = "CONT",
62+#endif
63+#ifdef SIGSTOP
64+ [SIGSTOP] = "STOP",
65+#endif
66+#ifdef SIGTSTP
67+ [SIGTSTP] = "TSTP",
68+#endif
69+#ifdef SIGTTIN
70+ [SIGTTIN] = "TTIN",
71+#endif
72+#ifdef SIGTTOU
73+ [SIGTTOU] = "TTOU",
74+#endif
75+#ifdef SIGURG
76+ [SIGURG] = "URG",
77+#endif
78+#ifdef SIGXCPU
79+ [SIGXCPU] = "XCPU",
80+#endif
81+#ifdef SIGXFSZ
82+ [SIGXFSZ] = "XFSZ",
83+#endif
84+#ifdef SIGVTALRM
85+ [SIGVTALRM] = "VTALRM",
86+#endif
87+#ifdef SIGPROF
88+ [SIGPROF] = "PROF",
89+#endif
90+#ifdef SIGWINCH
91+ [SIGWINCH] = "WINCH",
92+#endif
93+#ifdef SIGIO
94+ [SIGIO] = "IO",
95+#endif
96+#ifdef SIGPWR
97+ [SIGPWR] = "PWR",
98+#endif
99+#ifdef SIGSYS
100+ [SIGSYS] = "SYS",
101+#endif
102+};
103+
104+const int aruu_sys_nsig = NSIG;
105+
106+int
107+aruu_sig2str(int signo, char *str)
108+{
109+ const char *name;
110+
111+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
112+ if (signo <= 0 || signo >= sys_nsig)
113+ return -1;
114+ name = sys_signame[signo];
115+#else
116+ if (signo <= 0 || signo >= aruu_sys_nsig)
117+ return -1;
118+ name = aruu_sys_signame[signo];
119+#endif
120+
121+ if (name == NULL)
122+ return -1;
123+ strcpy(str, name);
124+ return 0;
125+}
126+
127+int
128+aruu_str2sig(const char *str, int *signop)
129+{
130+ int n;
131+ const char *s = str;
132+ const char *const *table;
133+ int limit;
134+
135+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
136+ table = sys_signame;
137+ limit = sys_nsig;
138+#else
139+ table = aruu_sys_signame;
140+ limit = aruu_sys_nsig;
141+#endif
142+
143+ if (strncasecmp(s, "SIG", 3) == 0)
144+ s += 3;
145+
146+ /* check if it is a number */
147+ char *ep;
148+ long l = strtol(s, &ep, 10);
149+ if (*s && !*ep) {
150+ if (l >= 0 && l < limit) {
151+ *signop = (int)l;
152+ return 0;
153+ }
154+ return -1;
155+ }
156+
157+ for (n = 1; n < limit; n++) {
158+ if (table[n] && strcasecmp(table[n], s) == 0) {
159+ *signop = n;
160+ return 0;
161+ }
162+ }
163+ return -1;
164+}
1@@ -39,7 +39,7 @@ unescape(char *s)
2 } else if (is_odigit(*r)) {
3 for (q = 0, m = 3; m && is_odigit(*r); m--, r++)
4 q = q * 8 + (*r - '0');
5- *w++ = MIN(q, 255);
6+ *w++ = MIN(q, (size_t)255);
7 } else if (*r == 'x' && isxdigit(r[1])) {
8 r++;
9 for (q = 0, m = 2; m && isxdigit(*r); m--, r++)
1@@ -0,0 +1,36 @@
2+/* See LICENSE file for copyright and license details. */
3+#ifndef SIG_H_
4+#define SIG_H_
5+
6+#include <signal.h>
7+#include <sys/types.h>
8+
9+#ifndef SIG2STR_MAX
10+#define SIG2STR_MAX 32
11+#endif
12+
13+#ifndef NSIG
14+#ifdef _NSIG
15+#define NSIG _NSIG
16+#else
17+#define NSIG 64
18+#endif
19+#endif
20+
21+extern const char *const aruu_sys_signame[NSIG];
22+extern const int aruu_sys_nsig;
23+
24+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
25+/* system already provides sys_signame and sys_nsig */
26+#else
27+#define sys_signame aruu_sys_signame
28+#define sys_nsig aruu_sys_nsig
29+#endif
30+
31+#define sig2str aruu_sig2str
32+#define str2sig aruu_str2sig
33+
34+int aruu_sig2str(int signo, char *str);
35+int aruu_str2sig(const char *str, int *signop);
36+
37+#endif /* !SIG_H_ */
1@@ -1,5 +1,9 @@
2 /* See LICENSE file for copyright and license details. */
3
4+#include <stddef.h>
5+#include <stdio.h>
6+#include <sys/types.h>
7+
8 struct line {
9 char *data;
10 size_t len;