Index: trunk/skirmish/README |
— | — | @@ -0,0 +1,17 @@ |
| 2 | + Skirmish |
| 3 | + ======== |
| 4 | + |
| 5 | +Skirmish is a command-line SQL client with support for several DBMSs: |
| 6 | + |
| 7 | + * MySQL |
| 8 | + * PostgreSQL |
| 9 | + * Oracle |
| 10 | + * MaxDB |
| 11 | + * SQLite |
| 12 | + * Any other ODBC-compliant database |
| 13 | + |
| 14 | +The client connects to the database and executes SQL command in a similar |
| 15 | +manner to the database's native client. Multiple connections can be opened |
| 16 | +and used from within a single client instance. |
| 17 | + |
| 18 | +For more information, see the manual page skirmish(1). |
Index: trunk/skirmish/terminal.cc |
— | — | @@ -1,9 +1,11 @@ |
2 | 2 | #include <stdio.h> |
| 3 | +#include <unistd.h> |
3 | 4 | #include <readline/readline.h> |
4 | 5 | #include <readline/history.h> |
5 | 6 | |
6 | 7 | #include <cstdlib> |
7 | 8 | #include <iostream> |
| 9 | +#include <sstream> |
8 | 10 | |
9 | 11 | #include <sys/types.h> |
10 | 12 | #include <sys/ioctl.h> |
— | — | @@ -13,11 +15,19 @@ |
14 | 16 | |
15 | 17 | terminal::terminal(void) |
16 | 18 | : rows_output(0) |
| 19 | + , rows(0) |
| 20 | + , cols(0) |
17 | 21 | { |
18 | | - struct winsize wz; |
19 | | - ioctl(0, TIOCGWINSZ, &wz); |
20 | | - rows = wz.ws_row; |
21 | | - cols = wz.ws_col; |
| 22 | + if (isatty(STDOUT_FILENO)) { |
| 23 | + struct winsize wz; |
| 24 | + ioctl(STDOUT_FILENO, TIOCGWINSZ, &wz); |
| 25 | + rows = wz.ws_row; |
| 26 | + cols = wz.ws_col; |
| 27 | + } |
| 28 | + |
| 29 | + tcgetattr(0, &norm); |
| 30 | + std::memcpy(&raw, &norm, sizeof(norm)); |
| 31 | + cfmakeraw(&raw); |
22 | 32 | } |
23 | 33 | |
24 | 34 | terminal::~terminal(void) |
— | — | @@ -88,12 +98,15 @@ |
89 | 99 | return; |
90 | 100 | } |
91 | 101 | |
92 | | - std::string rest = line; |
93 | | - while (rest.size() > cols) { |
94 | | - really_put_line(rest.substr(0, cols)); |
95 | | - rest = rest.substr(cols); |
| 102 | + std::istringstream strm(line); |
| 103 | + std::string rest; |
| 104 | + while (std::getline(strm, rest)) { |
| 105 | + while (rest.size() > cols) { |
| 106 | + really_put_line(rest.substr(0, cols)); |
| 107 | + rest = rest.substr(cols); |
| 108 | + } |
| 109 | + really_put_line(rest); |
96 | 110 | } |
97 | | - really_put_line(rest); |
98 | 111 | } |
99 | 112 | |
100 | 113 | void |
— | — | @@ -101,10 +114,39 @@ |
102 | 115 | { |
103 | 116 | if (rows - 1 == rows_output) { |
104 | 117 | rows_output = 0; |
105 | | - std::cout << "-- More --"; |
106 | | - std::string dummy; |
107 | | - std::getline(std::cin, dummy); |
| 118 | + std::cout << "-- More --" << std::flush; |
| 119 | + char c; |
| 120 | + if (!rawread(c)) |
| 121 | + return; |
| 122 | + switch (c) { |
| 123 | + case ' ': |
| 124 | + rows_output = 0; |
| 125 | + break; |
| 126 | + default: |
| 127 | + case '\n': |
| 128 | + rows_output = rows - 2; |
| 129 | + break; |
| 130 | + } |
| 131 | + std::cout << '\r'; |
108 | 132 | } |
109 | 133 | std::cout << line << '\n'; |
110 | 134 | ++rows_output; |
111 | 135 | } |
| 136 | + |
| 137 | +void |
| 138 | +terminal::reset_pager(void) |
| 139 | +{ |
| 140 | + rows_output = 0; |
| 141 | +} |
| 142 | + |
| 143 | +bool |
| 144 | +terminal::rawread(char &c) |
| 145 | +{ |
| 146 | + tcsetattr(0, TCSANOW, &raw); |
| 147 | + if (read(0, &c, 1) < 1) { |
| 148 | + tcsetattr(0, TCSANOW, &norm); |
| 149 | + return false; |
| 150 | + } |
| 151 | + tcsetattr(0, TCSANOW, &norm); |
| 152 | + return true; |
| 153 | +} |
Index: trunk/skirmish/terminal.h |
— | — | @@ -4,6 +4,8 @@ |
5 | 5 | #include <string> |
6 | 6 | #include <map> |
7 | 7 | |
| 8 | +#include <termio.h> |
| 9 | + |
8 | 10 | struct terminal { |
9 | 11 | terminal(); |
10 | 12 | ~terminal(); |
— | — | @@ -11,6 +13,8 @@ |
12 | 14 | bool readline(std::string &, std::string const &); |
13 | 15 | void set_prompt_variable(std::string const &var, std::string const &value); |
14 | 16 | void putline(std::string const &line); |
| 17 | + void reset_pager(void); |
| 18 | + bool rawread(char &c); |
15 | 19 | |
16 | 20 | private: |
17 | 21 | std::string form_prompt(std::string const &); |
— | — | @@ -19,6 +23,7 @@ |
20 | 24 | std::map<std::string, std::string> promptvars; |
21 | 25 | int rows, cols; |
22 | 26 | int rows_output; |
| 27 | + struct termios norm, raw; |
23 | 28 | }; |
24 | 29 | |
25 | 30 | #endif |
Index: trunk/skirmish/skirmish.cc |
— | — | @@ -12,6 +12,7 @@ |
13 | 13 | static db::connectionptr open_connection(std::string const &); |
14 | 14 | static void show_connection(); |
15 | 15 | static void handle_internal(std::string const &); |
| 16 | +static bool read_input_line(std::string &, std::string const &); |
16 | 17 | |
17 | 18 | static void add_connection(std::string const &); |
18 | 19 | static void list_connections(std::string const &); |
— | — | @@ -82,6 +83,7 @@ |
83 | 84 | db::connectionptr conn; |
84 | 85 | std::string input; |
85 | 86 | for (;;) { |
| 87 | + term.reset_pager(); |
86 | 88 | std::string cnrs; |
87 | 89 | if (cnr == -1) { |
88 | 90 | term.set_prompt_variable("desc", "not connected"); |
— | — | @@ -91,7 +93,7 @@ |
92 | 94 | term.set_prompt_variable("desc", conns[cnr]->desc); |
93 | 95 | } |
94 | 96 | |
95 | | - if (!term.readline(input, prompt)) |
| 97 | + if (!read_input_line(input, prompt)) |
96 | 98 | break; |
97 | 99 | |
98 | 100 | if (input.empty()) |
— | — | @@ -152,7 +154,7 @@ |
153 | 155 | } |
154 | 156 | |
155 | 157 | for (int row = 0; row < data.size(); ++row) { |
156 | | - std::stringstream output; |
| 158 | + std::ostringstream output; |
157 | 159 | |
158 | 160 | for (int col = 0; col < ncols; ++col) { |
159 | 161 | output << ' ' << std::setw(sizes[col]) << std::left << data[row][col]; |
— | — | @@ -178,7 +180,7 @@ |
179 | 181 | } |
180 | 182 | |
181 | 183 | if (nrows) |
182 | | - term.putline(str(boost::format("\nOK (%d rows).\n") % nrows)); |
| 184 | + term.putline(str(boost::format("\nOK (%d rows).") % nrows)); |
183 | 185 | } |
184 | 186 | } |
185 | 187 | |
— | — | @@ -410,3 +412,41 @@ |
411 | 413 | else |
412 | 414 | prompt = arg; |
413 | 415 | } |
| 416 | + |
| 417 | +static bool |
| 418 | +read_input_line(std::string &input, std::string const &prompt) |
| 419 | +{ |
| 420 | + std::string ti, rp = prompt; |
| 421 | + bool instr = false; |
| 422 | + |
| 423 | + input = ""; |
| 424 | + for (;;) { |
| 425 | + if (!term.readline(ti, rp)) |
| 426 | + return false; |
| 427 | + |
| 428 | + if (!ti.empty() && input.empty() && ti[0] == '\\') { |
| 429 | + input = ti; |
| 430 | + return true; |
| 431 | + } |
| 432 | + |
| 433 | + for (int i = 0; i < ti.size(); ++i) { |
| 434 | + switch (ti[i]) { |
| 435 | + case '\'': |
| 436 | + instr = !instr; |
| 437 | + break; |
| 438 | + default: |
| 439 | + ; |
| 440 | + } |
| 441 | + } |
| 442 | + |
| 443 | + if (!instr && ti[ti.size() - 1] == ';') { |
| 444 | + ti.resize(ti.size() - 1); |
| 445 | + input += ti; |
| 446 | + return true; |
| 447 | + } |
| 448 | + |
| 449 | + input += ti + '\n'; |
| 450 | + rp = "... "; |
| 451 | + } |
| 452 | + return true; |
| 453 | +} |