1 /* Copyright (c) 2008, 2009 Nicira Networks, Inc.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
30 #include "command-line.h"
31 #include "extras/ezio/tty.h"
32 #include "extras/ezio/vt.h"
35 #include "poll-loop.h"
36 #include "socket-util.h"
41 #define THIS_MODULE VLM_ezio_term
44 /* EZIO button status. */
52 /* -e, --ezio: EZIO3 serial device file. */
53 static char *ezio_dev = "/dev/ttyS1";
55 /* -i, --input: Terminal from which to accept additional keyboard input. */
56 static char *input_dev = NULL;
59 static int inputdev_open(const char *name, struct inputdev **);
60 static void inputdev_close(struct inputdev *);
61 static int inputdev_run(struct inputdev *, struct byteq *);
62 static void inputdev_update(struct inputdev *, const struct ezio *);
63 static void inputdev_wait(struct inputdev *);
65 static struct scanner *scanner_create(void);
66 static void scanner_destroy(struct scanner *);
67 static void scanner_run(struct scanner *, struct ezio *);
68 static void scanner_wait(struct scanner *);
69 static void scanner_left(struct scanner *, struct ezio *);
70 static void scanner_right(struct scanner *, struct ezio *);
72 static struct updater *updater_create(void);
73 static void updater_destroy(struct updater *);
74 static int updater_run(struct updater *, const struct ezio *shadow,
76 static void updater_wait(struct updater *, int ezio_fd);
77 enum btn_status updater_get_buttons(struct updater *);
78 bool updater_has_buttons(const struct updater *);
80 static void handle_buttons(struct updater *, struct scanner *,
81 struct byteq *, struct ezio *);
83 static void usage(void) NO_RETURN;
84 static void parse_options(int argc, char *argv[]);
87 main(int argc, char *argv[])
89 struct terminal *terminal;
90 struct updater *updater;
91 struct scanner *scanner;
92 struct inputdev *inputdev;
95 int ezio_fd, pty_fd, dummy_fd;
99 proctitle_init(argc, argv);
100 set_program_name(argv[0]);
103 parse_options(argc, argv);
104 signal(SIGPIPE, SIG_IGN);
109 /* Make sure that the ezio3 terminfo entry is available. */
110 dummy_fd = get_null_fd();
112 if (setupterm("ezio3", dummy_fd, &retval) == ERR) {
114 ovs_fatal(0, "Missing terminfo entry for ezio3. "
115 "Did you run \"make install\"?");
117 ovs_fatal(0, "Missing terminfo database. Is ncurses "
118 "properly installed?");
121 del_curterm(cur_term);
124 /* Lock serial port. */
125 retval = tty_lock(ezio_dev);
127 ovs_fatal(retval, "%s: lock failed", ezio_dev);
130 /* Open EZIO and configure as 2400 bps, N-8-1, in raw mode. */
131 ezio_fd = open(ezio_dev, O_RDWR | O_NOCTTY);
133 ovs_fatal(errno, "%s: open", ezio_dev);
135 retval = tty_set_raw_mode(ezio_fd, B2400);
137 ovs_fatal(retval, "%s: failed to configure tty parameters", ezio_dev);
140 /* Open keyboard device for input. */
142 retval = inputdev_open(input_dev, &inputdev);
144 ovs_fatal(retval, "%s: failed to open input device", input_dev);
150 /* Open pty master. */
151 pty_fd = tty_open_master_pty();
153 ovs_fatal(-pty_fd, "failed to open master pty");
155 tty_set_window_size(pty_fd, 2, 40);
157 /* Start child process. */
161 child_argv[0] = getenv("SHELL");
162 if (!child_argv[0]) {
163 child_argv[0] = "/bin/sh";
165 child_argv[1] = NULL;
166 retval = tty_fork_child(pty_fd, child_argv);
168 retval = tty_fork_child(pty_fd, argv);
171 ovs_fatal(retval, "failed to fork child process");
174 die_if_already_running();
177 terminal = terminal_create();
178 updater = updater_create();
179 scanner = scanner_create();
181 for (i = 0; i < 8; i++) {
182 ezio_set_default_icon(&ezio, i);
186 /* Get button presses and keyboard input into inputq, then push the
187 * inputq to the pty. */
188 handle_buttons(updater, scanner, &inputq, &ezio);
190 retval = inputdev_run(inputdev, &inputq);
192 VLOG_ERR("error reading from input device: %s",
194 inputdev_close(inputdev);
198 retval = byteq_write(&inputq, pty_fd);
199 if (retval && retval != EAGAIN) {
200 VLOG_ERR("error passing through input: %s",
201 retval == EOF ? "end of file" : strerror(retval));
204 /* Process data from pty in terminal emulator. */
205 retval = terminal_run(terminal, &ezio, pty_fd);
207 VLOG_ERR("error reading from terminal: %s",
208 retval == EOF ? "end of file" : strerror(retval));
212 /* Scroll left and right through text. */
213 scanner_run(scanner, &ezio);
215 /* Update the display to match what should be shown. */
216 retval = updater_run(updater, &ezio, ezio_fd);
218 VLOG_ERR("error writing to ezio: %s",
219 retval == EOF ? "end of file" : strerror(retval));
223 inputdev_update(inputdev, &ezio);
226 /* Wait for something to happen. */
227 terminal_wait(terminal, pty_fd);
228 scanner_wait(scanner);
229 if (updater_has_buttons(updater)) {
230 poll_immediate_wake();
232 updater_wait(updater, ezio_fd);
233 if (!byteq_is_empty(&inputq)) {
234 poll_fd_wait(pty_fd, POLLOUT);
237 inputdev_wait(inputdev);
241 terminal_destroy(terminal);
242 updater_destroy(updater);
243 scanner_destroy(scanner);
249 send_keys(struct byteq *q, const char *s)
251 size_t n = strlen(s);
252 if (byteq_avail(q) >= n) {
258 handle_buttons(struct updater *up, struct scanner *s,
259 struct byteq *q, struct ezio *ezio)
261 while (updater_has_buttons(up)) {
262 int btns = updater_get_buttons(up);
265 send_keys(q, "\x1b\x5b\x41"); /* Up arrow. */
268 case BTN_UP | BTN_ESC:
269 send_keys(q, "\x1b[5~"); /* Page up. */
273 send_keys(q, "\x1b\x5b\x42"); /* Down arrow. */
276 case BTN_DOWN | BTN_ESC:
277 send_keys(q, "\x1b[6~"); /* Page down. */
285 send_keys(q, "\x7f");
288 case BTN_UP | BTN_DOWN:
289 scanner_left(s, ezio);
292 case BTN_ESC | BTN_ENTER:
293 scanner_right(s, ezio);
296 case BTN_UP | BTN_DOWN | BTN_ENTER | BTN_ESC:
297 send_keys(q, "\x04"); /* End of file. */
300 case BTN_UP | BTN_ENTER | BTN_ESC:
304 case BTN_DOWN | BTN_ENTER | BTN_ESC:
311 /* EZIO screen updater. */
313 /* EZIO command codes. */
314 #define EZIO_CMD 0xfe /* Command prefix byte. */
315 #define EZIO_CLEAR 0x01 /* Clear screen. */
316 #define EZIO_HOME 0x02 /* Move to (0, 0). */
317 #define EZIO_READ 0x06 /* Poll keyboard. */
319 #define EZIO_ENTRY_MODE 0x04 /* Set entry mode: */
320 #define EZIO_LTOR_MODE 0x02 /* ...left-to-right (vs. r-to-l). */
321 #define EZIO_SHIFT_MODE 0x01 /* ...scroll with output (vs. don't). */
323 #define EZIO_DISPLAY_MODE 0x08 /* Set display mode: */
324 #define EZIO_ENABLE_DISPLAY 0x04 /* ...turn on display (vs. blank). */
325 #define EZIO_SHOW_CURSOR 0x02 /* ...show cursor (vs. hide). */
326 #define EZIO_BLOCK_CURSOR 0x01 /* ...block cursor (vs. underline). */
328 #define EZIO_INIT 0x28 /* Initialize EZIO. */
330 #define EZIO_MOVE_CURSOR 0x80 /* Set cursor position. */
331 #define EZIO_COL_SHIFT 0 /* Shift count for column (0-based). */
332 #define EZIO_ROW_SHIFT 6 /* Shift count for row (0-based). */
334 #define EZIO_DEFINE_ICON 0x40 /* Define icon. */
335 #define EZIO_ICON_SHIFT 3 /* Shift count for icon number (0-7). */
337 #define EZIO_SCROLL_LEFT 0x18 /* Scroll display left 1 position. */
338 #define EZIO_SCROLL_RIGHT 0x1c /* Scroll display right 1 position. */
339 #define EZIO_CURSOR_LEFT 0x10 /* Move cursor left 1 position. */
340 #define EZIO_CURSOR_RIGHT 0x14 /* Move cursor right 1 position. */
342 /* Rate limiting: the EZIO runs at 2400 bps, which is 240 bytes per second.
343 * Kernel tty buffers, on the other hand, tend to be at least 4 kB. That
344 * means that, if we keep the kernel buffer filled, then the queued data will
345 * be 4,096 kB / 240 bytes/s ~= 17 seconds ahead of what is actually
346 * displayed. This is not a happy situation. So we rate-limit with a token
349 * The parameters below work out as: (6 tokens/ms * 1000 ms) / (25
350 * tokens/byte) = 240 bytes/s. */
351 #define UP_TOKENS_PER_MS 6 /* Tokens acquired per millisecond. */
352 #define UP_BUCKET_SIZE (6 * 100) /* Capacity of the token bukect. */
353 #define UP_TOKENS_PER_BYTE 25 /* Tokens required to output a byte. */
356 /* Current state of EZIO device. */
360 struct byteq obuf; /* Output being sent to serial port. */
361 int tokens; /* Token bucket content. */
362 long long int last_fill; /* Last time we increased 'tokens'.*/
363 bool up_to_date; /* Does visible state match shadow state? */
366 struct byteq ibuf; /* Queued button pushes. */
367 long long int last_poll; /* Last time we sent a button poll request. */
368 enum btn_status last_status; /* Last received button status. */
369 long long int last_change; /* Time when status most recently changed. */
370 int repeat_count; /* Autorepeat count. */
371 bool releasing; /* Waiting for button release? */
374 static void send_command(struct updater *, uint8_t command);
375 static void recv_button_state(struct updater *, enum btn_status status);
376 static int range(int value, int min, int max);
377 static void send_command(struct updater *, uint8_t command);
378 static void set_cursor_position(struct updater *, int x, int y);
379 static bool icons_differ(const struct ezio *, const struct ezio *, int *idx);
380 static void update_char(struct updater *, const struct ezio *, int x, int y);
381 static void update_cursor_status(struct updater *, const struct ezio *);
383 /* Creates and returns a new updater. */
384 static struct updater *
387 struct updater *up = xmalloc(sizeof *up);
388 ezio_init(&up->visible);
389 byteq_init(&up->obuf);
390 up->tokens = UP_BUCKET_SIZE;
391 up->last_fill = time_msec();
392 byteq_init(&up->ibuf);
393 up->last_poll = LLONG_MIN;
395 up->last_change = time_msec();
396 up->releasing = false;
397 send_command(up, EZIO_INIT);
398 send_command(up, EZIO_INIT);
399 send_command(up, EZIO_CLEAR);
400 send_command(up, EZIO_HOME);
404 /* Destroys updater 'up. */
406 updater_destroy(struct updater *up)
411 /* Sends EZIO commands over file descriptor 'ezio_fd' to the EZIO represented
412 * by updater 'up', to make the EZIO display the contents of 'shadow'.
413 * Rate-limiting can cause the update to be only partial, but the next call to
414 * updater_run() will resume the update.
416 * Returns 0 if successful, otherwise a positive errno value. */
418 updater_run(struct updater *up, const struct ezio *shadow, int ezio_fd)
421 while (read(ezio_fd, &c, 1) > 0) {
422 if ((c & 0xf0) == 0xb0) {
423 recv_button_state(up, ~c & 0x0f);
427 up->up_to_date = false;
429 struct ezio *visible = &up->visible;
433 /* Flush the buffer out to the EZIO device. */
434 retval = byteq_write(&up->obuf, ezio_fd);
435 if (retval == EAGAIN) {
438 VLOG_WARN("error writing ezio: %s", strerror(retval));
442 /* Make sure we have some tokens before we write anything more. */
443 if (up->tokens <= 0) {
444 long long int now = time_msec();
445 if (now > up->last_fill) {
446 up->tokens += (now - up->last_fill) * UP_TOKENS_PER_MS;
448 if (up->tokens > UP_BUCKET_SIZE) {
449 up->tokens = UP_BUCKET_SIZE;
452 if (up->tokens <= 0) {
453 /* Still out of tokens. */
458 /* Consider what else we might want to send. */
459 if (time_msec() >= up->last_poll + 100) {
460 /* Send a button-read command. */
461 send_command(up, EZIO_READ);
462 up->last_poll = time_msec();
463 } else if (visible->show_cursor && !shadow->show_cursor) {
464 /* Turn off the cursor. */
465 update_cursor_status(up, shadow);
466 } else if (icons_differ(shadow, visible, &idx)) {
467 /* Update the icons. */
468 send_command(up, EZIO_DEFINE_ICON + (idx << EZIO_ICON_SHIFT));
469 byteq_putn(&up->obuf, &shadow->icons[idx][0], 8);
470 set_cursor_position(up, shadow->x, shadow->y);
471 memcpy(visible->icons[idx], shadow->icons[idx], 8);
472 } else if (visible->x_ofs != shadow->x_ofs) {
473 /* Scroll to the correct horizontal position. */
474 if (visible->x_ofs < shadow->x_ofs) {
475 send_command(up, EZIO_SCROLL_LEFT);
478 send_command(up, EZIO_SCROLL_RIGHT);
481 } else if (ezio_chars_differ(shadow, visible, shadow->x_ofs,
482 shadow->x_ofs + 16, &x, &y)) {
483 /* Update the visible region. */
484 update_char(up, shadow, x, y);
485 } else if (ezio_chars_differ(shadow, visible, 0, 40, &x, &y)) {
486 /* Update the off-screen region. */
487 update_char(up, shadow, x, y);
488 } else if ((visible->x != shadow->x || visible->y != shadow->y)
489 && shadow->show_cursor) {
490 /* Update the cursor position. (This has to follow updating the
491 * display content, because updating display content changes the
492 * cursor position.) */
493 set_cursor_position(up, shadow->x, shadow->y);
494 } else if (visible->show_cursor != shadow->show_cursor
495 || visible->blink_cursor != shadow->blink_cursor) {
496 /* Update the cursor type. */
497 update_cursor_status(up, shadow);
499 /* We're fully up-to-date. */
500 up->up_to_date = true;
503 up->tokens -= UP_TOKENS_PER_BYTE * byteq_used(&up->obuf);
507 /* Calls poll-loop functions that will cause poll_block() to wake up when
508 * updater_run() has work to do. */
510 updater_wait(struct updater *up, int ezio_fd)
512 if (!byteq_is_empty(&up->obuf)) {
513 poll_fd_wait(ezio_fd, POLLOUT);
514 } else if (up->tokens <= 0) {
515 poll_timer_wait((-up->tokens / UP_TOKENS_PER_MS) + 1);
516 } else if (!up->up_to_date) {
517 poll_immediate_wake();
520 if (!up->last_status && time_msec() - up->last_change > 100) {
521 /* No button presses in a while. Sleep longer. */
522 poll_timer_wait(100);
528 /* Returns a button or buttons that were pushed. Must not be called if
529 * updater_has_buttons() would return false. One or more BTN_* flags will be
530 * set in the return value. */
532 updater_get_buttons(struct updater *up)
534 return byteq_get(&up->ibuf);
537 /* Any buttons pushed? */
539 updater_has_buttons(const struct updater *up)
541 return !byteq_is_empty(&up->ibuf);
544 /* Adds 'btns' to the queue of pushed buttons */
546 buttons_pushed(struct updater *up, enum btn_status btns)
548 if (!byteq_is_full(&up->ibuf)) {
549 byteq_put(&up->ibuf, btns);
553 /* Updates the buttons-pushed queue based on the current button 'status'. */
555 recv_button_state(struct updater *up, enum btn_status status)
557 /* Calculate milliseconds since button status last changed. */
558 long long int stable_msec;
559 if (status != up->last_status) {
560 up->last_change = time_msec();
563 stable_msec = time_msec() - up->last_change;
568 up->releasing = false;
570 } else if (up->last_status) {
571 if (!(status & up->last_status)) {
572 /* Button(s) were pushed and released. */
573 if (!up->repeat_count) {
574 buttons_pushed(up, up->last_status);
576 } else if (stable_msec >= 150 && !up->repeat_count) {
577 /* Buttons have been stable for a while, so push them once. */
578 buttons_pushed(up, status);
580 } else if (stable_msec >= 1000) {
581 /* Autorepeat 10/second after 1 second hold time. */
582 int n = (stable_msec - 1000) / 100 + 1;
583 while (up->repeat_count < n) {
584 buttons_pushed(up, status);
587 } else if ((status & up->last_status) == up->last_status) {
588 /* More buttons pushed than at last poll. */
590 /* Some, but not all, buttons were released. Ignore the buttons
591 * until all are released. */
592 up->releasing = true;
596 up->repeat_count = 0;
598 up->last_status = status;
602 range(int value, int min, int max)
604 return value < min ? min : value > max ? max : value;
608 send_command(struct updater *up, uint8_t command)
610 byteq_put(&up->obuf, EZIO_CMD);
611 byteq_put(&up->obuf, command);
614 /* Moves the cursor to 0-based position (x, y). Updates 'up->visible' to
615 * reflect the change. */
617 set_cursor_position(struct updater *up, int x, int y)
619 int command = EZIO_MOVE_CURSOR;
620 command |= range(x, 0, 39) << EZIO_COL_SHIFT;
621 command |= range(y, 0, 1) << EZIO_ROW_SHIFT;
622 send_command(up, command);
627 /* If any of the icons differ from 'a' to 'b', returns true and sets '*idx' to
628 * the index of the first icon that differs. Otherwise, returns false. */
630 icons_differ(const struct ezio *a, const struct ezio *b, int *idx)
634 for (i = 0; i < ARRAY_SIZE(a->icons); i++) {
635 if (memcmp(&a->icons[i], &b->icons[i], sizeof a->icons[i])) {
643 /* Queues commands in 'up''s output buffer to update the character at 0-based
644 * position (x,y) to match the character that 'shadow' has there. Updates
645 * 'up->visible' to reflect the change. */
647 update_char(struct updater *up, const struct ezio *shadow, int x, int y)
649 if (x != up->visible.x || y != up->visible.y) {
650 set_cursor_position(up, x, y);
652 byteq_put(&up->obuf, shadow->chars[y][x]);
653 up->visible.chars[y][x] = shadow->chars[y][x];
657 /* Queues commands in 'up''s output buffer to change the EZIO's cursor shape to
658 * match that in 'shadow'. Updates 'up->visible' to reflect the change. */
660 update_cursor_status(struct updater *up, const struct ezio *shadow)
662 uint8_t command = EZIO_DISPLAY_MODE | EZIO_ENABLE_DISPLAY;
663 if (shadow->show_cursor) {
664 command |= EZIO_SHOW_CURSOR;
665 if (shadow->blink_cursor) {
666 command |= EZIO_BLOCK_CURSOR;
669 send_command(up, command);
670 up->visible.show_cursor = shadow->show_cursor;
671 up->visible.blink_cursor = shadow->blink_cursor;
674 /* An input device, such as a tty. */
678 int fd; /* File descriptor. */
680 /* State for mirroring the EZIO display to the device. */
681 bool is_tty; /* We only attempt to mirror to ttys. */
682 struct byteq outq; /* Output queue. */
683 struct ezio visible; /* Data that we have displayed. */
686 /* Opens 'name' as a input device. If successful, returns 0 and stores a
687 * pointer to the input device in '*devp'. On failure, returns a positive
690 inputdev_open(const char *name, struct inputdev **devp)
692 struct inputdev *dev;
697 if (!strcmp(name, "vt")) {
698 fd = vt_open(O_RDWR | O_NOCTTY);
702 } else if (!strcmp(name, "-")) {
703 fd = dup(STDIN_FILENO);
708 fd = open(name, O_RDWR | O_NOCTTY);
714 retval = tty_set_raw_mode(fd, B0);
717 VLOG_WARN("%s: failed to configure tty parameters: %s",
718 name, strerror(retval));
722 dev = xmalloc(sizeof *dev);
724 dev->is_tty = isatty(fd);
725 byteq_init(&dev->outq);
726 ezio_init(&dev->visible);
731 /* Closes and destroys input device 'dev'. */
733 inputdev_close(struct inputdev *dev)
741 /* Reads input from 'dev' into 'q'. Returns 0 if successful, otherwise a
742 * positive errno value. */
744 inputdev_run(struct inputdev *dev, struct byteq *q)
746 int retval = byteq_read(q, dev->fd);
747 return retval == EAGAIN ? 0 : retval;
750 /* Dumps data from 'dev''s output queue to the underlying file descriptor,
751 * updating the tty screen display. */
753 flush_inputdev(struct inputdev *dev)
755 int retval = byteq_write(&dev->outq, dev->fd);
756 if (retval && retval != EAGAIN) {
757 VLOG_WARN("error writing input device, "
758 "disabling further output");
763 /* Updates the tty screen display on 'dev' to match 'e'. */
765 inputdev_update(struct inputdev *dev, const struct ezio *e)
767 struct byteq *q = &dev->outq;
775 if (!byteq_is_empty(q)) {
779 if (!ezio_chars_differ(e, &dev->visible, 0, 40, &x, &y)
780 && e->x == dev->visible.x
781 && e->y == dev->visible.y
782 && e->x_ofs == dev->visible.x_ofs
783 && e->show_cursor == dev->visible.show_cursor) {
788 byteq_put_string(q, "\033[H\033[2J"); /* Clear screen. */
789 for (y = 0; y < 4; y++) {
790 byteq_put(q, "+||+"[y]);
791 for (x = 0; x < 40; x++) {
796 c = y == 0 || y == 3 ? '-' : e->chars[y - 1][x];
801 } else if (c < 0x20 || c > 0x7d) {
805 if (x == e->x_ofs + 15) {
809 byteq_put(q, "+||+"[y]);
813 if (e->show_cursor) {
814 int x = range(e->x, 0, 39) + 2 + (e->x >= e->x_ofs) + (e->x > e->x_ofs + 15);
815 int y = range(e->y, 0, 1) + 2;
817 sprintf(cup, "\033[%d;%dH", y, x); /* Position cursor. */
818 byteq_put_string(q, cup);
823 /* Calls poll-loop functions that will cause poll_block() to wake up when
824 * inputdev_run() has work to do. */
826 inputdev_wait(struct inputdev *dev)
829 if (dev->is_tty && !byteq_is_empty(&dev->outq)) {
832 poll_fd_wait(dev->fd, flags);
835 /* Scrolls the display left and right automatically to display all the
839 SCANNER_LEFT, /* Moving left. */
840 SCANNER_RIGHT /* Moving right. */
844 enum scanner_state state; /* Current state. */
845 int wait; /* No. of cycles to pause before continuing. */
846 long long int last_move; /* Last time the state machine ran. */
849 static void find_min_max(struct ezio *, int *min, int *max);
851 static struct scanner *
854 struct scanner *s = xmalloc(sizeof *s);
855 s->state = SCANNER_RIGHT;
857 s->last_move = LLONG_MIN;
862 scanner_destroy(struct scanner *s)
868 scanner_run(struct scanner *s, struct ezio *ezio)
870 long long int now = time_msec();
871 if (now >= s->last_move + 750) {
878 find_min_max(ezio, &min, &max);
879 if (max - min + 1 <= 16) {
886 if (ezio->x_ofs + 15 < max) {
889 s->state = SCANNER_LEFT;
895 if (ezio->x_ofs > min) {
898 s->state = SCANNER_RIGHT;
908 scanner_wait(struct scanner *s)
910 long long int now = time_msec();
911 long long int expires = s->last_move + 750;
912 if (now >= expires) {
913 poll_immediate_wake();
915 poll_timer_wait(expires - now);
921 scanner_left(struct scanner *s, struct ezio *ezio)
924 if (ezio->x_ofs > 0) {
930 scanner_right(struct scanner *s, struct ezio *ezio)
933 if (ezio->x_ofs < 40 - 16) {
939 find_min_max(struct ezio *ezio, int *min, int *max)
944 for (x = 0; x < 40; x++) {
945 if (ezio->chars[0][x] != ' ' || ezio->chars[1][x] != ' ') {
952 for (x = 39; x >= 0; x--) {
953 if (ezio->chars[0][x] != ' ' || ezio->chars[1][x] != ' ') {
959 if (ezio->show_cursor) {
960 if (ezio->x < *min) {
963 if (ezio->x > *max) {
970 parse_options(int argc, char *argv[])
973 OPT_DUMMY = UCHAR_MAX + 1,
976 static struct option long_options[] = {
977 {"ezio3", required_argument, 0, 'e'},
978 {"input", required_argument, 0, 'i'},
979 {"verbose", optional_argument, 0, 'v'},
980 {"help", no_argument, 0, 'h'},
981 {"version", no_argument, 0, 'V'},
986 char *short_options = long_options_to_short_options(long_options);
991 c = getopt_long(argc, argv, short_options, long_options, NULL);
1002 input_dev = optarg ? optarg : "-";
1009 OVS_PRINT_VERSION(0, 0);
1012 DAEMON_OPTION_HANDLERS
1013 VLOG_OPTION_HANDLERS
1022 free(short_options);
1028 printf("%s: EZIO3 terminal front-end\n"
1029 "Provides a front-end to a 16x2 EZIO3 LCD display that makes\n"
1030 "it look more like a conventional terminal\n"
1031 "usage: %s [OPTIONS] [-- COMMAND [ARG...]]\n"
1032 "where COMMAND is a command to run with stdin, stdout, and\n"
1033 "stderr directed to the EZIO3 display.\n"
1034 "\nSettings (defaults in parentheses):\n"
1035 " -e, --ezio=TTY set EZIO3 serial device (/dev/ttyS1)\n"
1036 " -i, --input=TERMINAL also read input from TERMINAL;\n"
1037 " specify - for stdin, or vt to allocate\n"
1038 " and switch to a free virtual terminal\n"
1039 "\nOther options:\n"
1040 " -v, --verbose=MODULE:FACILITY:LEVEL configure logging levels\n"
1041 " -v, --verbose set maximum verbosity level\n"
1042 " -h, --help display this help message\n"
1043 " -V, --version display version information\n",
1044 program_name, program_name);