summaryrefslogtreecommitdiff
path: root/shell/hush_doc.txt
blob: ec5dd00f22c5ace31d40f2a531744cbf9c6a460c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
2008-07-14

	Command parsing

Command parsing results in "pipe" structures. "Pipe" structure
does not always correspond to what sh language calls "pipe",
it also controls execution of if, while, etc statements.

struct pipe fields:
  smallint res_word - "none" for normal commands,
                      "if" for if condition etc
  struct child_prog progs[] - array of commands in pipe
  smallint followup - how this pipe is related to next: is it
                      "pipe; pipe", "pipe & pipe" "pipe && pipe",
                      "pipe || pipe"?

Blocks of commands { pipe; pipe; } and (pipe; pipe) are represented
as one pipe struct with one progs[0] element which is a "group" -
struct child_prog can contain a list of pipes. Sometimes these
"groups" are created implicitly, e.g. every control
statement (if, while, etc) sits inside its own "pipe" struct).

res_word controls statement execution. Examples:

"echo Hello" -
pipe 0 res_word=NONE followup=SEQ prog[0] 'echo' 'Hello'
pipe 1 res_word=NONE followup=1 SEQ

"echo foo || echo bar" -
pipe 0 res_word=NONE followup=OR  prog[0] 'echo' 'foo'
pipe 1 res_word=NONE followup=SEQ prog[0] 'echo' 'bar'
pipe 2 res_word=NONE followup=SEQ

"if true; then echo Hello; true; fi" -
res_word=NONE followup=SEQ
 prog 0 group {}:
  pipe 0 res_word=IF followup=SEQ prog[0] 'true'
  pipe 1 res_word=THEN followup=SEQ prog[0] 'echo' 'Hello'
  pipe 2 res_word=THEN followup=SEQ prog[0] 'true'
  pipe 3 res_word=FI followup=SEQ
  pipe 4 res_word=NONE followup=(null)
pipe 1 res_word=NONE followup=SEQ

"if true; then { echo Hello; true; }; fi" -
pipe 0 res_word=NONE followup=SEQ
 prog 0 group {}:
  pipe 0 res_word=IF followup=SEQ prog[0] 'true'
  pipe 1 res_word=THEN followup=SEQ
   prog 0 group {}:
    pipe 0 res_word=NONE followup=SEQ prog[0] 'echo' 'Hello'
    pipe 1 res_word=NONE followup=SEQ prog[0] 'true'
    pipe 2 res_word=NONE followup=SEQ
  pipe 2 res_word=NONE followup=(null)
pipe 1 res_word=NONE followup=1 SEQ

"for v in a b; do echo $v; true; done" -
pipe 0 res_word=NONE followup=SEQ
 prog 0 group {}:
  pipe 0 res_word=FOR followup=SEQ prog[0] 'v'
  pipe 1 res_word=IN followup=SEQ prog[0] 'a' 'b'
  pipe 2 res_word=DO followup=SEQ prog[0] 'echo' '$v'
  pipe 3 res_word=DO followup=SEQ prog[0] 'true'
  pipe 4 res_word=DONE followup=SEQ
  pipe 5 res_word=NONE followup=(null)
pipe 1 res_word=NONE followup=SEQ

Note how "THEN" and "DO" does not just mark the first pipe,
it "sticks" to all pipes in the body. This is used when
hush executes parsed pipes.

Dummy trailing pipes with no commands are artifacts of imperfect
parsing algorithm - done_pipe() appends new pipe struct beforehand
and last one ends up empty and unused.


2008-01

	This is how hush runs commands:

/* callsite: process_command_subs */
generate_stream_from_list(struct pipe *head) - handles `cmds`
  create UNIX pipe
  [v]fork
  child:
  redirect pipe output to stdout
  _exit(run_list(head));   /* leaks memory */
  parent:
  return UNIX pipe's output fd
  /* head is freed by the caller */

/* callsite: parse_and_run_stream */
run_and_free_list(struct pipe *)
  run_list(struct pipe *)
  free_pipe_list(struct pipe *)

/* callsites: generate_stream_from_list, run_and_free_list, pseudo_exec, run_pipe */
run_list(struct pipe *) - handles "cmd; cmd2 && cmd3", while/for/do loops
  run_pipe - for every pipe in list

/* callsite: run_list */
run_pipe - runs "cmd1 | cmd2 | cmd3 [&]"
  run_list - used if only one cmd and it is of the form "{cmds;}"
  forks for every cmd if more than one cmd or if & is there
  pseudo_exec - runs each "cmdN" (handles builtins etc)

/* callsite: run_pipe */
pseudo_exec - runs "cmd" (handles builtins etc)
  exec - execs external programs
  run_list - used if cmdN is "(cmds)" or "{cmds;}"
  /* problem: putenv's malloced strings into environ -
  ** with vfork they will leak into parent process
  */
  /* problem with ENABLE_FEATURE_SH_STANDALONE:
  ** run_applet_no_and_exit(a, argv) uses exit - this can interfere
  ** with vfork - switch to _exit there?
  */