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.
29 #include "command-line.h"
30 #include "extras/ezio/byteq.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 set_program_name(argv[0]);
102 parse_options(argc, argv);
103 signal(SIGPIPE, SIG_IGN);
108 /* Make sure that the ezio3 terminfo entry is available. */
109 dummy_fd = get_null_fd();
111 if (setupterm("ezio3", dummy_fd, &retval) == ERR) {
113 ovs_fatal(0, "Missing terminfo entry for ezio3. "
114 "Did you run \"make install\"?");
116 ovs_fatal(0, "Missing terminfo database. Is ncurses "
117 "properly installed?");
120 del_curterm(cur_term);
123 /* Lock serial port. */
124 retval = tty_lock(ezio_dev);
126 ovs_fatal(retval, "%s: lock failed", ezio_dev);
129 /* Open EZIO and configure as 2400 bps, N-8-1, in raw mode. */
130 ezio_fd = open(ezio_dev, O_RDWR | O_NOCTTY);
132 ovs_fatal(errno, "%s: open", ezio_dev);
134 retval = tty_set_raw_mode(ezio_fd, B2400);
136 ovs_fatal(retval, "%s: failed to configure tty parameters", ezio_dev);
139 /* Open keyboard device for input. */
141 retval = inputdev_open(input_dev, &inputdev);
143 ovs_fatal(retval, "%s: failed to open input device", input_dev);
149 /* Open pty master. */
150 pty_fd = tty_open_master_pty();
152 ovs_fatal(-pty_fd, "failed to open master pty");
154 tty_set_window_size(pty_fd, 2, 40);
156 /* Start child process. */
160 child_argv[0] = getenv("SHELL");
161 if (!child_argv[0]) {
162 child_argv[0] = "/bin/sh";
164 child_argv[1] = NULL;
165 retval = tty_fork_child(pty_fd, child_argv);
167 retval = tty_fork_child(pty_fd, argv);
170 ovs_fatal(retval, "failed to fork child process");
173 die_if_already_running();
176 terminal = terminal_create();
177 updater = updater_create();
178 scanner = scanner_create();
180 for (i = 0; i < 8; i++) {
181 ezio_set_default_icon(&ezio, i);
185 /* Get button presses and keyboard input into inputq, then push the
186 * inputq to the pty. */
187 handle_buttons(updater, scanner, &inputq, &ezio);
189 retval = inputdev_run(inputdev, &inputq);
191 VLOG_ERR("error reading from input device: %s",
193 inputdev_close(inputdev);
197 retval = byteq_write(&inputq, pty_fd);
198 if (retval && retval != EAGAIN) {
199 VLOG_ERR("error passing through input: %s",
200 retval == EOF ? "end of file" : strerror(retval));
203 /* Process data from pty in terminal emulator. */
204 retval = terminal_run(terminal, &ezio, pty_fd);
206 VLOG_ERR("error reading from terminal: %s",
207 retval == EOF ? "end of file" : strerror(retval));
211 /* Scroll left and right through text. */
212 scanner_run(scanner, &ezio);
214 /* Update the display to match what should be shown. */
215 retval = updater_run(updater, &ezio, ezio_fd);
217 VLOG_ERR("error writing to ezio: %s",
218 retval == EOF ? "end of file" : strerror(retval));
222 inputdev_update(inputdev, &ezio);
225 /* Wait for something to happen. */
226 terminal_wait(terminal, pty_fd);
227 scanner_wait(scanner);
228 if (updater_has_buttons(updater)) {
229 poll_immediate_wake();
231 updater_wait(updater, ezio_fd);
232 if (!byteq_is_empty(&inputq)) {
233 poll_fd_wait(pty_fd, POLLOUT);
236 inputdev_wait(inputdev);
240 terminal_destroy(terminal);
241 updater_destroy(updater);
242 scanner_destroy(scanner);
248 send_keys(struct byteq *q, const char *s)
250 size_t n = strlen(s);
251 if (byteq_avail(q) >= n) {
257 handle_buttons(struct updater *up, struct scanner *s,
258 struct byteq *q, struct ezio *ezio)
260 while (updater_has_buttons(up)) {
261 int btns = updater_get_buttons(up);
264 send_keys(q, "\x1b\x5b\x41"); /* Up arrow. */
267 case BTN_UP | BTN_ESC:
268 send_keys(q, "\x1b[5~"); /* Page up. */
272 send_keys(q, "\x1b\x5b\x42"); /* Down arrow. */
275 case BTN_DOWN | BTN_ESC:
276 send_keys(q, "\x1b[6~"); /* Page down. */
284 send_keys(q, "\x7f");
287 case BTN_UP | BTN_DOWN:
288 scanner_left(s, ezio);
291 case BTN_ESC | BTN_ENTER:
292 scanner_right(s, ezio);
295 case BTN_UP | BTN_DOWN | BTN_ENTER | BTN_ESC:
296 send_keys(q, "\x04"); /* End of file. */
299 case BTN_UP | BTN_ENTER | BTN_ESC:
303 case BTN_DOWN | BTN_ENTER | BTN_ESC:
310 /* EZIO screen updater. */
312 /* EZIO command codes. */
313 #define EZIO_CMD 0xfe /* Command prefix byte. */
314 #define EZIO_CLEAR 0x01 /* Clear screen. */
315 #define EZIO_HOME 0x02 /* Move to (0, 0). */
316 #define EZIO_READ 0x06 /* Poll keyboard. */
318 #define EZIO_ENTRY_MODE 0x04 /* Set entry mode: */
319 #define EZIO_LTOR_MODE 0x02 /* ...left-to-right (vs. r-to-l). */
320 #define EZIO_SHIFT_MODE 0x01 /* ...scroll with output (vs. don't). */
322 #define EZIO_DISPLAY_MODE 0x08 /* Set display mode: */
323 #define EZIO_ENABLE_DISPLAY 0x04 /* ...turn on display (vs. blank). */
324 #define EZIO_SHOW_CURSOR 0x02 /* ...show cursor (vs. hide). */
325 #define EZIO_BLOCK_CURSOR 0x01 /* ...block cursor (vs. underline). */
327 #define EZIO_INIT 0x28 /* Initialize EZIO. */
329 #define EZIO_MOVE_CURSOR 0x80 /* Set cursor position. */
330 #define EZIO_COL_SHIFT 0 /* Shift count for column (0-based). */
331 #define EZIO_ROW_SHIFT 6 /* Shift count for row (0-based). */
333 #define EZIO_DEFINE_ICON 0x40 /* Define icon. */
334 #define EZIO_ICON_SHIFT 3 /* Shift count for icon number (0-7). */
336 #define EZIO_SCROLL_LEFT 0x18 /* Scroll display left 1 position. */
337 #define EZIO_SCROLL_RIGHT 0x1c /* Scroll display right 1 position. */
338 #define EZIO_CURSOR_LEFT 0x10 /* Move cursor left 1 position. */
339 #define EZIO_CURSOR_RIGHT 0x14 /* Move cursor right 1 position. */
341 /* Rate limiting: the EZIO runs at 2400 bps, which is 240 bytes per second.
342 * Kernel tty buffers, on the other hand, tend to be at least 4 kB. That
343 * means that, if we keep the kernel buffer filled, then the queued data will
344 * be 4,096 kB / 240 bytes/s ~= 17 seconds ahead of what is actually
345 * displayed. This is not a happy situation. So we rate-limit with a token
348 * The parameters below work out as: (6 tokens/ms * 1000 ms) / (25
349 * tokens/byte) = 240 bytes/s. */
350 #define UP_TOKENS_PER_MS 6 /* Tokens acquired per millisecond. */
351 #define UP_BUCKET_SIZE (6 * 100) /* Capacity of the token bukect. */
352 #define UP_TOKENS_PER_BYTE 25 /* Tokens required to output a byte. */
355 /* Current state of EZIO device. */
359 struct byteq obuf; /* Output being sent to serial port. */
360 int tokens; /* Token bucket content. */
361 long long int last_fill; /* Last time we increased 'tokens'.*/
362 bool up_to_date; /* Does visible state match shadow state? */
365 struct byteq ibuf; /* Queued button pushes. */
366 long long int last_poll; /* Last time we sent a button poll request. */
367 enum btn_status last_status; /* Last received button status. */
368 long long int last_change; /* Time when status most recently changed. */
369 int repeat_count; /* Autorepeat count. */
370 bool releasing; /* Waiting for button release? */
373 static void send_command(struct updater *, uint8_t command);
374 static void recv_button_state(struct updater *, enum btn_status status);
375 static int range(int value, int min, int max);
376 static void send_command(struct updater *, uint8_t command);
377 static void set_cursor_position(struct updater *, int x, int y);
378 static bool icons_differ(const struct ezio *, const struct ezio *, int *idx);
379 static void update_char(struct updater *, const struct ezio *, int x, int y);
380 static void update_cursor_status(struct updater *, const struct ezio *);
382 /* Creates and returns a new updater. */
383 static struct updater *
386 struct updater *up = xmalloc(sizeof *up);
387 ezio_init(&up->visible);
388 byteq_init(&up->obuf);
389 up->tokens = UP_BUCKET_SIZE;
390 up->last_fill = time_msec();
391 byteq_init(&up->ibuf);
392 up->last_poll = LLONG_MIN;
394 up->last_change = time_msec();
395 up->releasing = false;
396 send_command(up, EZIO_INIT);
397 send_command(up, EZIO_INIT);
398 send_command(up, EZIO_CLEAR);
399 send_command(up, EZIO_HOME);
403 /* Destroys updater 'up. */
405 updater_destroy(struct updater *up)
410 /* Sends EZIO commands over file descriptor 'ezio_fd' to the EZIO represented
411 * by updater 'up', to make the EZIO display the contents of 'shadow'.
412 * Rate-limiting can cause the update to be only partial, but the next call to
413 * updater_run() will resume the update.
415 * Returns 0 if successful, otherwise a positive errno value. */
417 updater_run(struct updater *up, const struct ezio *shadow, int ezio_fd)
420 while (read(ezio_fd, &c, 1) > 0) {
421 if ((c & 0xf0) == 0xb0) {
422 recv_button_state(up, ~c & 0x0f);
426 up->up_to_date = false;
428 struct ezio *visible = &up->visible;
432 /* Flush the buffer out to the EZIO device. */
433 retval = byteq_write(&up->obuf, ezio_fd);
434 if (retval == EAGAIN) {
437 VLOG_WARN("error writing ezio: %s", strerror(retval));
441 /* Make sure we have some tokens before we write anything more. */
442 if (up->tokens <= 0) {
443 long long int now = time_msec();
444 if (now > up->last_fill) {
445 up->tokens += (now - up->last_fill) * UP_TOKENS_PER_MS;
447 if (up->tokens > UP_BUCKET_SIZE) {
448 up->tokens = UP_BUCKET_SIZE;
451 if (up->tokens <= 0) {
452 /* Still out of tokens. */
457 /* Consider what else we might want to send. */
458 if (time_msec() >= up->last_poll + 100) {
459 /* Send a button-read command. */
460 send_command(up, EZIO_READ);
461 up->last_poll = time_msec();
462 } else if (visible->show_cursor && !shadow->show_cursor) {
463 /* Turn off the cursor. */
464 update_cursor_status(up, shadow);
465 } else if (icons_differ(shadow, visible, &idx)) {
466 /* Update the icons. */
467 send_command(up, EZIO_DEFINE_ICON + (idx << EZIO_ICON_SHIFT));
468 byteq_putn(&up->obuf, &shadow->icons[idx][0], 8);
469 set_cursor_position(up, shadow->x, shadow->y);
470 memcpy(visible->icons[idx], shadow->icons[idx], 8);
471 } else if (visible->x_ofs != shadow->x_ofs) {
472 /* Scroll to the correct horizontal position. */
473 if (visible->x_ofs < shadow->x_ofs) {
474 send_command(up, EZIO_SCROLL_LEFT);
477 send_command(up, EZIO_SCROLL_RIGHT);
480 } else if (ezio_chars_differ(shadow, visible, shadow->x_ofs,
481 shadow->x_ofs + 16, &x, &y)) {
482 /* Update the visible region. */
483 update_char(up, shadow, x, y);
484 } else if (ezio_chars_differ(shadow, visible, 0, 40, &x, &y)) {
485 /* Update the off-screen region. */
486 update_char(up, shadow, x, y);
487 } else if ((visible->x != shadow->x || visible->y != shadow->y)
488 && shadow->show_cursor) {
489 /* Update the cursor position. (This has to follow updating the
490 * display content, because updating display content changes the
491 * cursor position.) */
492 set_cursor_position(up, shadow->x, shadow->y);
493 } else if (visible->show_cursor != shadow->show_cursor
494 || visible->blink_cursor != shadow->blink_cursor) {
495 /* Update the cursor type. */
496 update_cursor_status(up, shadow);
498 /* We're fully up-to-date. */
499 up->up_to_date = true;
502 up->tokens -= UP_TOKENS_PER_BYTE * byteq_used(&up->obuf);
506 /* Calls poll-loop functions that will cause poll_block() to wake up when
507 * updater_run() has work to do. */
509 updater_wait(struct updater *up, int ezio_fd)
511 if (!byteq_is_empty(&up->obuf)) {
512 poll_fd_wait(ezio_fd, POLLOUT);
513 } else if (up->tokens <= 0) {
514 poll_timer_wait((-up->tokens / UP_TOKENS_PER_MS) + 1);
515 } else if (!up->up_to_date) {
516 poll_immediate_wake();
519 if (!up->last_status && time_msec() - up->last_change > 100) {
520 /* No button presses in a while. Sleep longer. */
521 poll_timer_wait(100);
527 /* Returns a button or buttons that were pushed. Must not be called if
528 * updater_has_buttons() would return false. One or more BTN_* flags will be
529 * set in the return value. */
531 updater_get_buttons(struct updater *up)
533 return byteq_get(&up->ibuf);
536 /* Any buttons pushed? */
538 updater_has_buttons(const struct updater *up)
540 return !byteq_is_empty(&up->ibuf);
543 /* Adds 'btns' to the queue of pushed buttons */
545 buttons_pushed(struct updater *up, enum btn_status btns)
547 if (!byteq_is_full(&up->ibuf)) {
548 byteq_put(&up->ibuf, btns);
552 /* Updates the buttons-pushed queue based on the current button 'status'. */
554 recv_button_state(struct updater *up, enum btn_status status)
556 /* Calculate milliseconds since button status last changed. */
557 long long int stable_msec;
558 if (status != up->last_status) {
559 up->last_change = time_msec();
562 stable_msec = time_msec() - up->last_change;
567 up->releasing = false;
569 } else if (up->last_status) {
570 if (!(status & up->last_status)) {
571 /* Button(s) were pushed and released. */
572 if (!up->repeat_count) {
573 buttons_pushed(up, up->last_status);
575 } else if (stable_msec >= 150 && !up->repeat_count) {
576 /* Buttons have been stable for a while, so push them once. */
577 buttons_pushed(up, status);
579 } else if (stable_msec >= 1000) {
580 /* Autorepeat 10/second after 1 second hold time. */
581 int n = (stable_msec - 1000) / 100 + 1;
582 while (up->repeat_count < n) {
583 buttons_pushed(up, status);
586 } else if ((status & up->last_status) == up->last_status) {
587 /* More buttons pushed than at last poll. */
589 /* Some, but not all, buttons were released. Ignore the buttons
590 * until all are released. */
591 up->releasing = true;
595 up->repeat_count = 0;
597 up->last_status = status;
601 range(int value, int min, int max)
603 return value < min ? min : value > max ? max : value;
607 send_command(struct updater *up, uint8_t command)
609 byteq_put(&up->obuf, EZIO_CMD);
610 byteq_put(&up->obuf, command);
613 /* Moves the cursor to 0-based position (x, y). Updates 'up->visible' to
614 * reflect the change. */
616 set_cursor_position(struct updater *up, int x, int y)
618 int command = EZIO_MOVE_CURSOR;
619 command |= range(x, 0, 39) << EZIO_COL_SHIFT;
620 command |= range(y, 0, 1) << EZIO_ROW_SHIFT;
621 send_command(up, command);
626 /* If any of the icons differ from 'a' to 'b', returns true and sets '*idx' to
627 * the index of the first icon that differs. Otherwise, returns false. */
629 icons_differ(const struct ezio *a, const struct ezio *b, int *idx)
633 for (i = 0; i < ARRAY_SIZE(a->icons); i++) {
634 if (memcmp(&a->icons[i], &b->icons[i], sizeof a->icons[i])) {
642 /* Queues commands in 'up''s output buffer to update the character at 0-based
643 * position (x,y) to match the character that 'shadow' has there. Updates
644 * 'up->visible' to reflect the change. */
646 update_char(struct updater *up, const struct ezio *shadow, int x, int y)
648 if (x != up->visible.x || y != up->visible.y) {
649 set_cursor_position(up, x, y);
651 byteq_put(&up->obuf, shadow->chars[y][x]);
652 up->visible.chars[y][x] = shadow->chars[y][x];
656 /* Queues commands in 'up''s output buffer to change the EZIO's cursor shape to
657 * match that in 'shadow'. Updates 'up->visible' to reflect the change. */
659 update_cursor_status(struct updater *up, const struct ezio *shadow)
661 uint8_t command = EZIO_DISPLAY_MODE | EZIO_ENABLE_DISPLAY;
662 if (shadow->show_cursor) {
663 command |= EZIO_SHOW_CURSOR;
664 if (shadow->blink_cursor) {
665 command |= EZIO_BLOCK_CURSOR;
668 send_command(up, command);
669 up->visible.show_cursor = shadow->show_cursor;
670 up->visible.blink_cursor = shadow->blink_cursor;
673 /* An input device, such as a tty. */
677 int fd; /* File descriptor. */
679 /* State for mirroring the EZIO display to the device. */
680 bool is_tty; /* We only attempt to mirror to ttys. */
681 struct byteq outq; /* Output queue. */
682 struct ezio visible; /* Data that we have displayed. */
685 /* Opens 'name' as a input device. If successful, returns 0 and stores a
686 * pointer to the input device in '*devp'. On failure, returns a positive
689 inputdev_open(const char *name, struct inputdev **devp)
691 struct inputdev *dev;
696 if (!strcmp(name, "vt")) {
697 fd = vt_open(O_RDWR | O_NOCTTY);
701 } else if (!strcmp(name, "-")) {
702 fd = dup(STDIN_FILENO);
707 fd = open(name, O_RDWR | O_NOCTTY);
713 retval = tty_set_raw_mode(fd, B0);
716 VLOG_WARN("%s: failed to configure tty parameters: %s",
717 name, strerror(retval));
721 dev = xmalloc(sizeof *dev);
723 dev->is_tty = isatty(fd);
724 byteq_init(&dev->outq);
725 ezio_init(&dev->visible);
730 /* Closes and destroys input device 'dev'. */
732 inputdev_close(struct inputdev *dev)
740 /* Reads input from 'dev' into 'q'. Returns 0 if successful, otherwise a
741 * positive errno value. */
743 inputdev_run(struct inputdev *dev, struct byteq *q)
745 int retval = byteq_read(q, dev->fd);
746 return retval == EAGAIN ? 0 : retval;
749 /* Dumps data from 'dev''s output queue to the underlying file descriptor,
750 * updating the tty screen display. */
752 flush_inputdev(struct inputdev *dev)
754 int retval = byteq_write(&dev->outq, dev->fd);
755 if (retval && retval != EAGAIN) {
756 VLOG_WARN("error writing input device, "
757 "disabling further output");
762 /* Updates the tty screen display on 'dev' to match 'e'. */
764 inputdev_update(struct inputdev *dev, const struct ezio *e)
766 struct byteq *q = &dev->outq;
774 if (!byteq_is_empty(q)) {
778 if (!ezio_chars_differ(e, &dev->visible, 0, 40, &x, &y)
779 && e->x == dev->visible.x
780 && e->y == dev->visible.y
781 && e->x_ofs == dev->visible.x_ofs
782 && e->show_cursor == dev->visible.show_cursor) {
787 byteq_put_string(q, "\033[H\033[2J"); /* Clear screen. */
788 for (y = 0; y < 4; y++) {
789 byteq_put(q, "+||+"[y]);
790 for (x = 0; x < 40; x++) {
795 c = y == 0 || y == 3 ? '-' : e->chars[y - 1][x];
800 } else if (c < 0x20 || c > 0x7d) {
804 if (x == e->x_ofs + 15) {
808 byteq_put(q, "+||+"[y]);
812 if (e->show_cursor) {
813 int x = range(e->x, 0, 39) + 2 + (e->x >= e->x_ofs) + (e->x > e->x_ofs + 15);
814 int y = range(e->y, 0, 1) + 2;
816 sprintf(cup, "\033[%d;%dH", y, x); /* Position cursor. */
817 byteq_put_string(q, cup);
822 /* Calls poll-loop functions that will cause poll_block() to wake up when
823 * inputdev_run() has work to do. */
825 inputdev_wait(struct inputdev *dev)
828 if (dev->is_tty && !byteq_is_empty(&dev->outq)) {
831 poll_fd_wait(dev->fd, flags);
834 /* Scrolls the display left and right automatically to display all the
838 SCANNER_LEFT, /* Moving left. */
839 SCANNER_RIGHT /* Moving right. */
843 enum scanner_state state; /* Current state. */
844 int wait; /* No. of cycles to pause before continuing. */
845 long long int last_move; /* Last time the state machine ran. */
848 static void find_min_max(struct ezio *, int *min, int *max);
850 static struct scanner *
853 struct scanner *s = xmalloc(sizeof *s);
854 s->state = SCANNER_RIGHT;
856 s->last_move = LLONG_MIN;
861 scanner_destroy(struct scanner *s)
867 scanner_run(struct scanner *s, struct ezio *ezio)
869 long long int now = time_msec();
870 if (now >= s->last_move + 750) {
877 find_min_max(ezio, &min, &max);
878 if (max - min + 1 <= 16) {
885 if (ezio->x_ofs + 15 < max) {
888 s->state = SCANNER_LEFT;
894 if (ezio->x_ofs > min) {
897 s->state = SCANNER_RIGHT;
907 scanner_wait(struct scanner *s)
909 long long int now = time_msec();
910 long long int expires = s->last_move + 750;
911 if (now >= expires) {
912 poll_immediate_wake();
914 poll_timer_wait(expires - now);
920 scanner_left(struct scanner *s, struct ezio *ezio)
923 if (ezio->x_ofs > 0) {
929 scanner_right(struct scanner *s, struct ezio *ezio)
932 if (ezio->x_ofs < 40 - 16) {
938 find_min_max(struct ezio *ezio, int *min, int *max)
943 for (x = 0; x < 40; x++) {
944 if (ezio->chars[0][x] != ' ' || ezio->chars[1][x] != ' ') {
951 for (x = 39; x >= 0; x--) {
952 if (ezio->chars[0][x] != ' ' || ezio->chars[1][x] != ' ') {
958 if (ezio->show_cursor) {
959 if (ezio->x < *min) {
962 if (ezio->x > *max) {
969 parse_options(int argc, char *argv[])
972 OPT_DUMMY = UCHAR_MAX + 1,
975 static struct option long_options[] = {
976 {"ezio3", required_argument, 0, 'e'},
977 {"input", required_argument, 0, 'i'},
978 {"verbose", optional_argument, 0, 'v'},
979 {"help", no_argument, 0, 'h'},
980 {"version", no_argument, 0, 'V'},
985 char *short_options = long_options_to_short_options(long_options);
990 c = getopt_long(argc, argv, short_options, long_options, NULL);
1001 input_dev = optarg ? optarg : "-";
1008 OVS_PRINT_VERSION(0, 0);
1011 DAEMON_OPTION_HANDLERS
1012 VLOG_OPTION_HANDLERS
1021 free(short_options);
1027 printf("%s: EZIO3 terminal front-end\n"
1028 "Provides a front-end to a 16x2 EZIO3 LCD display that makes\n"
1029 "it look more like a conventional terminal\n"
1030 "usage: %s [OPTIONS] [-- COMMAND [ARG...]]\n"
1031 "where COMMAND is a command to run with stdin, stdout, and\n"
1032 "stderr directed to the EZIO3 display.\n"
1033 "\nSettings (defaults in parentheses):\n"
1034 " -e, --ezio=TTY set EZIO3 serial device (/dev/ttyS1)\n"
1035 " -i, --input=TERMINAL also read input from TERMINAL;\n"
1036 " specify - for stdin, or vt to allocate\n"
1037 " and switch to a free virtual terminal\n"
1038 "\nOther options:\n"
1039 " -v, --verbose=MODULE:FACILITY:LEVEL configure logging levels\n"
1040 " -v, --verbose set maximum verbosity level\n"
1041 " -h, --help display this help message\n"
1042 " -V, --version display version information\n",
1043 program_name, program_name);