Parser.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. #include "Parser.h"
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. void Parser::commit_token()
  5. {
  6. if (m_token.is_empty())
  7. return;
  8. if (m_state == InRedirectionPath) {
  9. m_redirections.last().path = String::copy(m_token);
  10. m_token.clear_with_capacity();
  11. return;
  12. }
  13. m_tokens.append(String::copy(m_token));
  14. m_token.clear_with_capacity();
  15. };
  16. void Parser::commit_subcommand()
  17. {
  18. if (m_tokens.is_empty())
  19. return;
  20. m_subcommands.append({ move(m_tokens), move(m_redirections), {} });
  21. }
  22. void Parser::commit_command()
  23. {
  24. if (m_subcommands.is_empty())
  25. return;
  26. m_commands.append({ move(m_subcommands) });
  27. }
  28. void Parser::do_pipe()
  29. {
  30. m_redirections.append({ Redirection::Pipe, STDOUT_FILENO });
  31. commit_subcommand();
  32. }
  33. void Parser::begin_redirect_read(int fd)
  34. {
  35. m_redirections.append({ Redirection::FileRead, fd });
  36. }
  37. void Parser::begin_redirect_write(int fd)
  38. {
  39. m_redirections.append({ Redirection::FileWrite, fd });
  40. }
  41. Vector<Command> Parser::parse()
  42. {
  43. for (int i = 0; i < m_input.length(); ++i) {
  44. char ch = m_input.characters()[i];
  45. switch (m_state) {
  46. case State::Free:
  47. if (ch == ' ') {
  48. commit_token();
  49. break;
  50. }
  51. if (ch == ';') {
  52. commit_token();
  53. commit_subcommand();
  54. commit_command();
  55. break;
  56. }
  57. if (ch == '|') {
  58. commit_token();
  59. if (m_tokens.is_empty()) {
  60. fprintf(stderr, "Syntax error: Nothing before pipe (|)\n");
  61. return {};
  62. }
  63. do_pipe();
  64. break;
  65. }
  66. if (ch == '>') {
  67. commit_token();
  68. begin_redirect_write(STDOUT_FILENO);
  69. // Search for another > for append.
  70. m_state = State::InWriteAppendOrRedirectionPath;
  71. break;
  72. }
  73. if (ch == '<') {
  74. commit_token();
  75. begin_redirect_read(STDIN_FILENO);
  76. m_state = State::InRedirectionPath;
  77. break;
  78. }
  79. if (ch == '\\') {
  80. if (i == m_input.length() - 1) {
  81. fprintf(stderr, "Syntax error: Nothing to escape (\\)\n");
  82. return {};
  83. }
  84. char next_ch = m_input.characters()[i + 1];
  85. m_token.append(next_ch);
  86. ++i;
  87. break;
  88. }
  89. if (ch == '\'') {
  90. m_state = State::InSingleQuotes;
  91. break;
  92. }
  93. if (ch == '\"') {
  94. m_state = State::InDoubleQuotes;
  95. break;
  96. }
  97. m_token.append(ch);
  98. break;
  99. case State::InWriteAppendOrRedirectionPath:
  100. if (ch == '>') {
  101. commit_token();
  102. m_state = State::InRedirectionPath;
  103. ASSERT(m_redirections.size());
  104. m_redirections[m_redirections.size() - 1].type = Redirection::FileWriteAppend;
  105. break;
  106. }
  107. // Not another > means that it's probably a path.
  108. m_state = InRedirectionPath;
  109. [[fallthrough]];
  110. case State::InRedirectionPath:
  111. if (ch == '<') {
  112. commit_token();
  113. begin_redirect_read(STDIN_FILENO);
  114. m_state = State::InRedirectionPath;
  115. break;
  116. }
  117. if (ch == '>') {
  118. commit_token();
  119. begin_redirect_read(STDOUT_FILENO);
  120. m_state = State::InRedirectionPath;
  121. break;
  122. }
  123. if (ch == '|') {
  124. commit_token();
  125. if (m_tokens.is_empty()) {
  126. fprintf(stderr, "Syntax error: Nothing before pipe (|)\n");
  127. return {};
  128. }
  129. do_pipe();
  130. m_state = State::Free;
  131. break;
  132. }
  133. if (ch == ' ')
  134. break;
  135. m_token.append(ch);
  136. break;
  137. case State::InSingleQuotes:
  138. if (ch == '\'') {
  139. commit_token();
  140. m_state = State::Free;
  141. break;
  142. }
  143. m_token.append(ch);
  144. break;
  145. case State::InDoubleQuotes:
  146. if (ch == '\"') {
  147. commit_token();
  148. m_state = State::Free;
  149. break;
  150. }
  151. if (ch == '\\') {
  152. if (i == m_input.length() - 1) {
  153. fprintf(stderr, "Syntax error: Nothing to escape (\\)\n");
  154. return {};
  155. }
  156. char next_ch = m_input.characters()[i + 1];
  157. if (next_ch == '$' || next_ch == '`'
  158. || next_ch == '"' || next_ch == '\\') {
  159. m_token.append(next_ch);
  160. ++i;
  161. continue;
  162. }
  163. m_token.append('\\');
  164. break;
  165. }
  166. m_token.append(ch);
  167. break;
  168. };
  169. }
  170. commit_token();
  171. commit_subcommand();
  172. commit_command();
  173. if (!m_subcommands.is_empty()) {
  174. for (auto& redirection : m_subcommands.last().redirections) {
  175. if (redirection.type == Redirection::Pipe) {
  176. fprintf(stderr, "Syntax error: Nothing after last pipe (|)\n");
  177. return {};
  178. }
  179. }
  180. }
  181. return move(m_commands);
  182. }