3
0

hush_doc.txt 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. 2008-07-14
  2. Command parsing
  3. Command parsing results in a list of "pipe" structures.
  4. This list correspond not only to usual "pipe1 || pipe2 && pipe3"
  5. lists, but it also controls execution of if, while, etc statements.
  6. Every such statement is a list for hush. List consists of pipes.
  7. struct pipe fields:
  8. smallint res_word - "none" for normal commands,
  9. "if" for if condition etc
  10. struct child_prog progs[] - array of commands in pipe
  11. smallint followup - how this pipe is related to next: is it
  12. "pipe; pipe", "pipe & pipe" "pipe && pipe",
  13. "pipe || pipe"?
  14. Blocks of commands { pipe; pipe; } and (pipe; pipe) are represented
  15. as one pipe struct with one progs[0] element which is a "group" -
  16. struct child_prog can contain a list of pipes. Sometimes these
  17. "groups" are created implicitly, e.g. every control
  18. statement (if, while, etc) sits inside its own group.
  19. res_word controls statement execution. Examples:
  20. "echo Hello" -
  21. pipe 0 res_word=NONE followup=SEQ prog[0] 'echo' 'Hello'
  22. pipe 1 res_word=NONE followup=SEQ
  23. "echo foo || echo bar" -
  24. pipe 0 res_word=NONE followup=OR prog[0] 'echo' 'foo'
  25. pipe 1 res_word=NONE followup=SEQ prog[0] 'echo' 'bar'
  26. pipe 2 res_word=NONE followup=SEQ
  27. "if true; then echo Hello; true; fi" -
  28. res_word=NONE followup=SEQ
  29. prog 0 group {}:
  30. pipe 0 res_word=IF followup=SEQ prog[0] 'true'
  31. pipe 1 res_word=THEN followup=SEQ prog[0] 'echo' 'Hello'
  32. pipe 2 res_word=THEN followup=SEQ prog[0] 'true'
  33. pipe 3 res_word=FI followup=SEQ
  34. pipe 4 res_word=NONE followup=(null)
  35. pipe 1 res_word=NONE followup=SEQ
  36. Above you see that if is a list, and it sits in a {} group
  37. implicitly created by hush. Also note two THEN res_word's -
  38. it is explained below.
  39. "if true; then { echo Hello; true; }; fi" -
  40. pipe 0 res_word=NONE followup=SEQ
  41. prog 0 group {}:
  42. pipe 0 res_word=IF followup=SEQ prog[0] 'true'
  43. pipe 1 res_word=THEN followup=SEQ
  44. prog 0 group {}:
  45. pipe 0 res_word=NONE followup=SEQ prog[0] 'echo' 'Hello'
  46. pipe 1 res_word=NONE followup=SEQ prog[0] 'true'
  47. pipe 2 res_word=NONE followup=SEQ
  48. pipe 2 res_word=NONE followup=(null)
  49. pipe 1 res_word=NONE followup=SEQ
  50. "for v in a b; do echo $v; true; done" -
  51. pipe 0 res_word=NONE followup=SEQ
  52. prog 0 group {}:
  53. pipe 0 res_word=FOR followup=SEQ prog[0] 'v'
  54. pipe 1 res_word=IN followup=SEQ prog[0] 'a' 'b'
  55. pipe 2 res_word=DO followup=SEQ prog[0] 'echo' '$v'
  56. pipe 3 res_word=DO followup=SEQ prog[0] 'true'
  57. pipe 4 res_word=DONE followup=SEQ
  58. pipe 5 res_word=NONE followup=(null)
  59. pipe 1 res_word=NONE followup=SEQ
  60. Note how "THEN" and "DO" does not just mark the first pipe,
  61. it "sticks" to all pipes in the body. This is used when
  62. hush executes parsed pipes.
  63. Dummy trailing pipes with no commands are artifacts of imperfect
  64. parsing algorithm - done_pipe() appends new pipe struct beforehand
  65. and last one ends up empty and unused.
  66. "for" and "case" statements (ab)use progs[] to keep their data
  67. instead of argv vector progs[] usually do. "for" keyword is forcing
  68. pipe termination after first word, which makes hush see
  69. "for v in..." as "for v; in...". "case" keyword does the same.
  70. Other judiciuosly placed hacks make hush see
  71. "case word in a) cmd1;; b) cmd2;; esac" as if it was
  72. "case word; match a; cmd; match b; cmd2; esac"
  73. ("match" is a fictitious keyword here):
  74. "case word in a) cmd1;; b) cmd2; esac" -
  75. pipe 0 res_word=NONE followup=1 SEQ
  76. prog 0 group {}:
  77. pipe 0 res_word=CASE followup=SEQ prog[0] 'word'
  78. pipe 1 res_word=MATCH followup=SEQ prog[0] 'a'
  79. pipe 2 res_word=CASEI followup=SEQ prog[0] 'cmd1'
  80. pipe 3 res_word=MATCH followup=SEQ prog[0] 'b'
  81. pipe 4 res_word=CASEI followup=SEQ prog[0] 'cmd2'
  82. pipe 5 res_word=CASEI followup=SEQ prog[0] 'cmd3'
  83. pipe 6 res_word=ESAC followup=SEQ
  84. pipe 7 res_word=NONE followup=(null)
  85. pipe 1 res_word=NONE followup=SEQ
  86. 2008-01
  87. Command execution
  88. /* callsite: process_command_subs */
  89. generate_stream_from_list(struct pipe *head) - handles `cmds`
  90. create UNIX pipe
  91. [v]fork
  92. child:
  93. redirect pipe output to stdout
  94. _exit(run_list(head)); /* leaks memory */
  95. parent:
  96. return UNIX pipe's output fd
  97. /* head is freed by the caller */
  98. /* callsite: parse_and_run_stream */
  99. run_and_free_list(struct pipe *)
  100. run_list(struct pipe *)
  101. free_pipe_list(struct pipe *)
  102. /* callsites: generate_stream_from_list, run_and_free_list, pseudo_exec, run_pipe */
  103. run_list(struct pipe *) - handles "cmd; cmd2 && cmd3", while/for/do loops
  104. run_pipe - for every pipe in list
  105. /* callsite: run_list */
  106. run_pipe - runs "cmd1 | cmd2 | cmd3 [&]"
  107. run_list - used if only one cmd and it is of the form "{cmds;}"
  108. forks for every cmd if more than one cmd or if & is there
  109. pseudo_exec - runs each "cmdN" (handles builtins etc)
  110. /* callsite: run_pipe */
  111. pseudo_exec - runs "cmd" (handles builtins etc)
  112. exec - execs external programs
  113. run_list - used if cmdN is "(cmds)" or "{cmds;}"
  114. /* problem: putenv's malloced strings into environ -
  115. ** with vfork they will leak into parent process
  116. */
  117. /* problem with ENABLE_FEATURE_SH_STANDALONE:
  118. ** run_applet_no_and_exit(a, argv) uses exit - this can interfere
  119. ** with vfork - switch to _exit there?
  120. */