1 /* Copyright (c) 2008, 2009, 2010 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"
42 VLOG_DEFINE_THIS_MODULE(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]);
101 parse_options(argc, argv);
102 signal(SIGPIPE, SIG_IGN);
107 /* Make sure that the ezio3 terminfo entry is available. */
108 dummy_fd = get_null_fd();
110 if (setupterm("ezio3", dummy_fd, &retval) == ERR) {
112 ovs_fatal(0, "Missing terminfo entry for ezio3. "
113 "Did you run \"make install\"?");
115 ovs_fatal(0, "Missing terminfo database. Is ncurses "
116 "properly installed?");
119 del_curterm(cur_term);
122 /* Lock serial port. */
123 retval = tty_lock(ezio_dev);
125 ovs_fatal(retval, "%s: lock failed", ezio_dev);
128 /* Open EZIO and configure as 2400 bps, N-8-1, in raw mode. */
129 ezio_fd = open(ezio_dev, O_RDWR | O_NOCTTY);
131 ovs_fatal(errno, "%s: open", ezio_dev);
133 retval = tty_set_raw_mode(ezio_fd, B2400);
135 ovs_fatal(retval, "%s: failed to configure tty parameters", ezio_dev);
138 /* Open keyboard device for input. */
140 retval = inputdev_open(input_dev, &inputdev);
142 ovs_fatal(retval, "%s: failed to open input device", input_dev);
148 /* Open pty master. */
149 pty_fd = tty_open_master_pty();
151 ovs_fatal(-pty_fd, "failed to open master pty");
153 tty_set_window_size(pty_fd, 2, 40);
155 /* Start child process. */
159 child_argv[0] = getenv("SHELL");
160 if (!child_argv[0]) {
161 child_argv[0] = "/bin/sh";
163 child_argv[1] = NULL;
164 retval = tty_fork_child(pty_fd, child_argv);
166 retval = tty_fork_child(pty_fd, argv);
169 ovs_fatal(retval, "failed to fork child process");
172 die_if_already_running();
175 terminal = terminal_create();
176 updater = updater_create();
177 scanner = scanner_create();
179 for (i = 0; i < 8; i++) {
180 ezio_set_default_icon(&ezio, i);
184 /* Get button presses and keyboard input into inputq, then push the
185 * inputq to the pty. */
186 handle_buttons(updater, scanner, &inputq, &ezio);
188 retval = inputdev_run(inputdev, &inputq);
190 VLOG_ERR("error reading from input device: %s",
192 inputdev_close(inputdev);
196 retval = byteq_write(&inputq, pty_fd);
197 if (retval && retval != EAGAIN) {
198 VLOG_ERR("error passing through input: %s",
199 retval == EOF ? "end of file" : strerror(retval));
202 /* Process data from pty in terminal emulator. */
203 retval = terminal_run(terminal, &ezio, pty_fd);
205 VLOG_ERR("error reading from terminal: %s",
206 retval == EOF ? "end of file" : strerror(retval));
210 /* Scroll left and right through text. */
211 scanner_run(scanner, &ezio);
213 /* Update the display to match what should be shown. */
214 retval = updater_run(updater, &ezio, ezio_fd);
216 VLOG_ERR("error writing to ezio: %s",
217 retval == EOF ? "end of file" : strerror(retval));
221 inputdev_update(inputdev, &ezio);
224 /* Wait for something to happen. */
225 terminal_wait(terminal, pty_fd);
226 scanner_wait(scanner);
227 if (updater_has_buttons(updater)) {
228 poll_immediate_wake();
230 updater_wait(updater, ezio_fd);
231 if (!byteq_is_empty(&inputq)) {
232 poll_fd_wait(pty_fd, POLLOUT);
235 inputdev_wait(inputdev);
239 terminal_destroy(terminal);
240 updater_destroy(updater);
241 scanner_destroy(scanner);
247 send_keys(struct byteq *q, const char *s)
249 size_t n = strlen(s);
250 if (byteq_avail(q) >= n) {
256 handle_buttons(struct updater *up, struct scanner *s,
257 struct byteq *q, struct ezio *ezio)
259 while (updater_has_buttons(up)) {
260 int btns = updater_get_buttons(up);
263 send_keys(q, "\x1b\x5b\x41"); /* Up arrow. */
266 case BTN_UP | BTN_ESC:
267 send_keys(q, "\x1b[5~"); /* Page up. */
271 send_keys(q, "\x1b\x5b\x42"); /* Down arrow. */
274 case BTN_DOWN | BTN_ESC:
275 send_keys(q, "\x1b[6~"); /* Page down. */
283 send_keys(q, "\x7f");
286 case BTN_UP | BTN_DOWN:
287 scanner_left(s, ezio);
290 case BTN_ESC | BTN_ENTER:
291 scanner_right(s, ezio);
294 case BTN_UP | BTN_DOWN | BTN_ENTER | BTN_ESC:
295 send_keys(q, "\x04"); /* End of file. */
298 case BTN_UP | BTN_ENTER | BTN_ESC:
302 case BTN_DOWN | BTN_ENTER | BTN_ESC:
309 /* EZIO screen updater. */
311 /* EZIO command codes. */
312 #define EZIO_CMD 0xfe /* Command prefix byte. */
313 #define EZIO_CLEAR 0x01 /* Clear screen. */
314 #define EZIO_HOME 0x02 /* Move to (0, 0). */
315 #define EZIO_READ 0x06 /* Poll keyboard. */
317 #define EZIO_ENTRY_MODE 0x04 /* Set entry mode: */
318 #define EZIO_LTOR_MODE 0x02 /* ...left-to-right (vs. r-to-l). */
319 #define EZIO_SHIFT_MODE 0x01 /* ...scroll with output (vs. don't). */
321 #define EZIO_DISPLAY_MODE 0x08 /* Set display mode: */
322 #define EZIO_ENABLE_DISPLAY 0x04 /* ...turn on display (vs. blank). */
323 #define EZIO_SHOW_CURSOR 0x02 /* ...show cursor (vs. hide). */
324 #define EZIO_BLOCK_CURSOR 0x01 /* ...block cursor (vs. underline). */
326 #define EZIO_INIT 0x28 /* Initialize EZIO. */
328 #define EZIO_MOVE_CURSOR 0x80 /* Set cursor position. */
329 #define EZIO_COL_SHIFT 0 /* Shift count for column (0-based). */
330 #define EZIO_ROW_SHIFT 6 /* Shift count for row (0-based). */
332 #define EZIO_DEFINE_ICON 0x40 /* Define icon. */
333 #define EZIO_ICON_SHIFT 3 /* Shift count for icon number (0-7). */
335 #define EZIO_SCROLL_LEFT 0x18 /* Scroll display left 1 position. */
336 #define EZIO_SCROLL_RIGHT 0x1c /* Scroll display right 1 position. */
337 #define EZIO_CURSOR_LEFT 0x10 /* Move cursor left 1 position. */
338 #define EZIO_CURSOR_RIGHT 0x14 /* Move cursor right 1 position. */
340 /* Rate limiting: the EZIO runs at 2400 bps, which is 240 bytes per second.
341 * Kernel tty buffers, on the other hand, tend to be at least 4 kB. That
342 * means that, if we keep the kernel buffer filled, then the queued data will
343 * be 4,096 kB / 240 bytes/s ~= 17 seconds ahead of what is actually
344 * displayed. This is not a happy situation. So we rate-limit with a token
347 * The parameters below work out as: (6 tokens/ms * 1000 ms) / (25
348 * tokens/byte) = 240 bytes/s. */
349 #define UP_TOKENS_PER_MS 6 /* Tokens acquired per millisecond. */
350 #define UP_BUCKET_SIZE (6 * 100) /* Capacity of the token bukect. */
351 #define UP_TOKENS_PER_BYTE 25 /* Tokens required to output a byte. */
354 /* Current state of EZIO device. */
358 struct byteq obuf; /* Output being sent to serial port. */
359 int tokens; /* Token bucket content. */
360 long long int last_fill; /* Last time we increased 'tokens'.*/
361 bool up_to_date; /* Does visible state match shadow state? */
364 struct byteq ibuf; /* Queued button pushes. */
365 long long int last_poll; /* Last time we sent a button poll request. */
366 enum btn_status last_status; /* Last received button status. */
367 long long int last_change; /* Time when status most recently changed. */
368 int repeat_count; /* Autorepeat count. */
369 bool releasing; /* Waiting for button release? */
372 static void send_command(struct updater *, uint8_t command);
373 static void recv_button_state(struct updater *, enum btn_status status);
374 static int range(int value, int min, int max);
375 static void send_command(struct updater *, uint8_t command);
376 static void set_cursor_position(struct updater *, int x, int y);
377 static bool icons_differ(const struct ezio *, const struct ezio *, int *idx);
378 static void update_char(struct updater *, const struct ezio *, int x, int y);
379 static void update_cursor_status(struct updater *, const struct ezio *);
381 /* Creates and returns a new updater. */
382 static struct updater *
385 struct updater *up = xmalloc(sizeof *up);
386 ezio_init(&up->visible);
387 byteq_init(&up->obuf);
388 up->tokens = UP_BUCKET_SIZE;
389 up->last_fill = time_msec();
390 byteq_init(&up->ibuf);
391 up->last_poll = LLONG_MIN;
393 up->last_change = time_msec();
394 up->releasing = false;
395 send_command(up, EZIO_INIT);
396 send_command(up, EZIO_INIT);
397 send_command(up, EZIO_CLEAR);
398 send_command(up, EZIO_HOME);
402 /* Destroys updater 'up. */
404 updater_destroy(struct updater *up)
409 /* Sends EZIO commands over file descriptor 'ezio_fd' to the EZIO represented
410 * by updater 'up', to make the EZIO display the contents of 'shadow'.
411 * Rate-limiting can cause the update to be only partial, but the next call to
412 * updater_run() will resume the update.
414 * Returns 0 if successful, otherwise a positive errno value. */
416 updater_run(struct updater *up, const struct ezio *shadow, int ezio_fd)
419 while (read(ezio_fd, &c, 1) > 0) {
420 if ((c & 0xf0) == 0xb0) {
421 recv_button_state(up, ~c & 0x0f);
425 up->up_to_date = false;
427 struct ezio *visible = &up->visible;
431 /* Flush the buffer out to the EZIO device. */
432 retval = byteq_write(&up->obuf, ezio_fd);
433 if (retval == EAGAIN) {
436 VLOG_WARN("error writing ezio: %s", strerror(retval));
440 /* Make sure we have some tokens before we write anything more. */
441 if (up->tokens <= 0) {
442 long long int now = time_msec();
443 if (now > up->last_fill) {
444 up->tokens += (now - up->last_fill) * UP_TOKENS_PER_MS;
446 if (up->tokens > UP_BUCKET_SIZE) {
447 up->tokens = UP_BUCKET_SIZE;
450 if (up->tokens <= 0) {
451 /* Still out of tokens. */
456 /* Consider what else we might want to send. */
457 if (time_msec() >= up->last_poll + 100) {
458 /* Send a button-read command. */
459 send_command(up, EZIO_READ);
460 up->last_poll = time_msec();
461 } else if (visible->show_cursor && !shadow->show_cursor) {
462 /* Turn off the cursor. */
463 update_cursor_status(up, shadow);
464 } else if (icons_differ(shadow, visible, &idx)) {
465 /* Update the icons. */
466 send_command(up, EZIO_DEFINE_ICON + (idx << EZIO_ICON_SHIFT));
467 byteq_putn(&up->obuf, &shadow->icons[idx][0], 8);
468 set_cursor_position(up, shadow->x, shadow->y);
469 memcpy(visible->icons[idx], shadow->icons[idx], 8);
470 } else if (visible->x_ofs != shadow->x_ofs) {
471 /* Scroll to the correct horizontal position. */
472 if (visible->x_ofs < shadow->x_ofs) {
473 send_command(up, EZIO_SCROLL_LEFT);
476 send_command(up, EZIO_SCROLL_RIGHT);
479 } else if (ezio_chars_differ(shadow, visible, shadow->x_ofs,
480 shadow->x_ofs + 16, &x, &y)) {
481 /* Update the visible region. */
482 update_char(up, shadow, x, y);
483 } else if (ezio_chars_differ(shadow, visible, 0, 40, &x, &y)) {
484 /* Update the off-screen region. */
485 update_char(up, shadow, x, y);
486 } else if ((visible->x != shadow->x || visible->y != shadow->y)
487 && shadow->show_cursor) {
488 /* Update the cursor position. (This has to follow updating the
489 * display content, because updating display content changes the
490 * cursor position.) */
491 set_cursor_position(up, shadow->x, shadow->y);
492 } else if (visible->show_cursor != shadow->show_cursor
493 || visible->blink_cursor != shadow->blink_cursor) {
494 /* Update the cursor type. */
495 update_cursor_status(up, shadow);
497 /* We're fully up-to-date. */
498 up->up_to_date = true;
501 up->tokens -= UP_TOKENS_PER_BYTE * byteq_used(&up->obuf);
505 /* Calls poll-loop functions that will cause poll_block() to wake up when
506 * updater_run() has work to do. */
508 updater_wait(struct updater *up, int ezio_fd)
510 if (!byteq_is_empty(&up->obuf)) {
511 poll_fd_wait(ezio_fd, POLLOUT);
512 } else if (up->tokens <= 0) {
513 poll_timer_wait((-up->tokens / UP_TOKENS_PER_MS) + 1);
514 } else if (!up->up_to_date) {
515 poll_immediate_wake();
518 if (!up->last_status && time_msec() - up->last_change > 100) {
519 /* No button presses in a while. Sleep longer. */
520 poll_timer_wait(100);
526 /* Returns a button or buttons that were pushed. Must not be called if
527 * updater_has_buttons() would return false. One or more BTN_* flags will be
528 * set in the return value. */
530 updater_get_buttons(struct updater *up)
532 return byteq_get(&up->ibuf);
535 /* Any buttons pushed? */
537 updater_has_buttons(const struct updater *up)
539 return !byteq_is_empty(&up->ibuf);
542 /* Adds 'btns' to the queue of pushed buttons */
544 buttons_pushed(struct updater *up, enum btn_status btns)
546 if (!byteq_is_full(&up->ibuf)) {
547 byteq_put(&up->ibuf, btns);
551 /* Updates the buttons-pushed queue based on the current button 'status'. */
553 recv_button_state(struct updater *up, enum btn_status status)
555 /* Calculate milliseconds since button status last changed. */
556 long long int stable_msec;
557 if (status != up->last_status) {
558 up->last_change = time_msec();
561 stable_msec = time_msec() - up->last_change;
566 up->releasing = false;
568 } else if (up->last_status) {
569 if (!(status & up->last_status)) {
570 /* Button(s) were pushed and released. */
571 if (!up->repeat_count) {
572 buttons_pushed(up, up->last_status);
574 } else if (stable_msec >= 150 && !up->repeat_count) {
575 /* Buttons have been stable for a while, so push them once. */
576 buttons_pushed(up, status);
578 } else if (stable_msec >= 1000) {
579 /* Autorepeat 10/second after 1 second hold time. */
580 int n = (stable_msec - 1000) / 100 + 1;
581 while (up->repeat_count < n) {
582 buttons_pushed(up, status);
585 } else if ((status & up->last_status) == up->last_status) {
586 /* More buttons pushed than at last poll. */
588 /* Some, but not all, buttons were released. Ignore the buttons
589 * until all are released. */
590 up->releasing = true;
594 up->repeat_count = 0;
596 up->last_status = status;
600 range(int value, int min, int max)
602 return value < min ? min : value > max ? max : value;
606 send_command(struct updater *up, uint8_t command)
608 byteq_put(&up->obuf, EZIO_CMD);
609 byteq_put(&up->obuf, command);
612 /* Moves the cursor to 0-based position (x, y). Updates 'up->visible' to
613 * reflect the change. */
615 set_cursor_position(struct updater *up, int x, int y)
617 int command = EZIO_MOVE_CURSOR;
618 command |= range(x, 0, 39) << EZIO_COL_SHIFT;
619 command |= range(y, 0, 1) << EZIO_ROW_SHIFT;
620 send_command(up, command);
625 /* If any of the icons differ from 'a' to 'b', returns true and sets '*idx' to
626 * the index of the first icon that differs. Otherwise, returns false. */
628 icons_differ(const struct ezio *a, const struct ezio *b, int *idx)
632 for (i = 0; i < ARRAY_SIZE(a->icons); i++) {
633 if (memcmp(&a->icons[i], &b->icons[i], sizeof a->icons[i])) {
641 /* Queues commands in 'up''s output buffer to update the character at 0-based
642 * position (x,y) to match the character that 'shadow' has there. Updates
643 * 'up->visible' to reflect the change. */
645 update_char(struct updater *up, const struct ezio *shadow, int x, int y)
647 if (x != up->visible.x || y != up->visible.y) {
648 set_cursor_position(up, x, y);
650 byteq_put(&up->obuf, shadow->chars[y][x]);
651 up->visible.chars[y][x] = shadow->chars[y][x];
655 /* Queues commands in 'up''s output buffer to change the EZIO's cursor shape to
656 * match that in 'shadow'. Updates 'up->visible' to reflect the change. */
658 update_cursor_status(struct updater *up, const struct ezio *shadow)
660 uint8_t command = EZIO_DISPLAY_MODE | EZIO_ENABLE_DISPLAY;
661 if (shadow->show_cursor) {
662 command |= EZIO_SHOW_CURSOR;
663 if (shadow->blink_cursor) {
664 command |= EZIO_BLOCK_CURSOR;
667 send_command(up, command);
668 up->visible.show_cursor = shadow->show_cursor;
669 up->visible.blink_cursor = shadow->blink_cursor;
672 /* An input device, such as a tty. */
676 int fd; /* File descriptor. */
678 /* State for mirroring the EZIO display to the device. */
679 bool is_tty; /* We only attempt to mirror to ttys. */
680 struct byteq outq; /* Output queue. */
681 struct ezio visible; /* Data that we have displayed. */
684 /* Opens 'name' as a input device. If successful, returns 0 and stores a
685 * pointer to the input device in '*devp'. On failure, returns a positive
688 inputdev_open(const char *name, struct inputdev **devp)
690 struct inputdev *dev;
695 if (!strcmp(name, "vt")) {
696 fd = vt_open(O_RDWR | O_NOCTTY);
700 } else if (!strcmp(name, "-")) {
701 fd = dup(STDIN_FILENO);
706 fd = open(name, O_RDWR | O_NOCTTY);
712 retval = tty_set_raw_mode(fd, B0);
715 VLOG_WARN("%s: failed to configure tty parameters: %s",
716 name, strerror(retval));
720 dev = xmalloc(sizeof *dev);
722 dev->is_tty = isatty(fd);
723 byteq_init(&dev->outq);
724 ezio_init(&dev->visible);
729 /* Closes and destroys input device 'dev'. */
731 inputdev_close(struct inputdev *dev)
739 /* Reads input from 'dev' into 'q'. Returns 0 if successful, otherwise a
740 * positive errno value. */
742 inputdev_run(struct inputdev *dev, struct byteq *q)
744 int retval = byteq_read(q, dev->fd);
745 return retval == EAGAIN ? 0 : retval;
748 /* Dumps data from 'dev''s output queue to the underlying file descriptor,
749 * updating the tty screen display. */
751 flush_inputdev(struct inputdev *dev)
753 int retval = byteq_write(&dev->outq, dev->fd);
754 if (retval && retval != EAGAIN) {
755 VLOG_WARN("error writing input device, "
756 "disabling further output");
761 /* Updates the tty screen display on 'dev' to match 'e'. */
763 inputdev_update(struct inputdev *dev, const struct ezio *e)
765 struct byteq *q = &dev->outq;
773 if (!byteq_is_empty(q)) {
777 if (!ezio_chars_differ(e, &dev->visible, 0, 40, &x, &y)
778 && e->x == dev->visible.x
779 && e->y == dev->visible.y
780 && e->x_ofs == dev->visible.x_ofs
781 && e->show_cursor == dev->visible.show_cursor) {
786 byteq_put_string(q, "\033[H\033[2J"); /* Clear screen. */
787 for (y = 0; y < 4; y++) {
788 byteq_put(q, "+||+"[y]);
789 for (x = 0; x < 40; x++) {
794 c = y == 0 || y == 3 ? '-' : e->chars[y - 1][x];
799 } else if (c < 0x20 || c > 0x7d) {
803 if (x == e->x_ofs + 15) {
807 byteq_put(q, "+||+"[y]);
811 if (e->show_cursor) {
812 int x = range(e->x, 0, 39) + 2 + (e->x >= e->x_ofs) + (e->x > e->x_ofs + 15);
813 int y = range(e->y, 0, 1) + 2;
815 sprintf(cup, "\033[%d;%dH", y, x); /* Position cursor. */
816 byteq_put_string(q, cup);
821 /* Calls poll-loop functions that will cause poll_block() to wake up when
822 * inputdev_run() has work to do. */
824 inputdev_wait(struct inputdev *dev)
827 if (dev->is_tty && !byteq_is_empty(&dev->outq)) {
830 poll_fd_wait(dev->fd, flags);
833 /* Scrolls the display left and right automatically to display all the
837 SCANNER_LEFT, /* Moving left. */
838 SCANNER_RIGHT /* Moving right. */
842 enum scanner_state state; /* Current state. */
843 int wait; /* No. of cycles to pause before continuing. */
844 long long int last_move; /* Last time the state machine ran. */
847 static void find_min_max(struct ezio *, int *min, int *max);
849 static struct scanner *
852 struct scanner *s = xmalloc(sizeof *s);
853 s->state = SCANNER_RIGHT;
855 s->last_move = LLONG_MIN;
860 scanner_destroy(struct scanner *s)
866 scanner_run(struct scanner *s, struct ezio *ezio)
868 long long int now = time_msec();
869 if (now >= s->last_move + 750) {
876 find_min_max(ezio, &min, &max);
877 if (max - min + 1 <= 16) {
884 if (ezio->x_ofs + 15 < max) {
887 s->state = SCANNER_LEFT;
893 if (ezio->x_ofs > min) {
896 s->state = SCANNER_RIGHT;
906 scanner_wait(struct scanner *s)
908 poll_timer_wait_until(s->last_move + 750);
912 scanner_left(struct scanner *s, struct ezio *ezio)
915 if (ezio->x_ofs > 0) {
921 scanner_right(struct scanner *s, struct ezio *ezio)
924 if (ezio->x_ofs < 40 - 16) {
930 find_min_max(struct ezio *ezio, int *min, int *max)
935 for (x = 0; x < 40; x++) {
936 if (ezio->chars[0][x] != ' ' || ezio->chars[1][x] != ' ') {
943 for (x = 39; x >= 0; x--) {
944 if (ezio->chars[0][x] != ' ' || ezio->chars[1][x] != ' ') {
950 if (ezio->show_cursor) {
951 if (ezio->x < *min) {
954 if (ezio->x > *max) {
961 parse_options(int argc, char *argv[])
964 OPT_DUMMY = UCHAR_MAX + 1,
967 static struct option long_options[] = {
968 {"ezio3", required_argument, 0, 'e'},
969 {"input", required_argument, 0, 'i'},
970 {"verbose", optional_argument, 0, 'v'},
971 {"help", no_argument, 0, 'h'},
972 {"version", no_argument, 0, 'V'},
977 char *short_options = long_options_to_short_options(long_options);
982 c = getopt_long(argc, argv, short_options, long_options, NULL);
993 input_dev = optarg ? optarg : "-";
1000 OVS_PRINT_VERSION(0, 0);
1003 DAEMON_OPTION_HANDLERS
1004 VLOG_OPTION_HANDLERS
1013 free(short_options);
1019 printf("%s: EZIO3 terminal front-end\n"
1020 "Provides a front-end to a 16x2 EZIO3 LCD display that makes\n"
1021 "it look more like a conventional terminal\n"
1022 "usage: %s [OPTIONS] [-- COMMAND [ARG...]]\n"
1023 "where COMMAND is a command to run with stdin, stdout, and\n"
1024 "stderr directed to the EZIO3 display.\n"
1025 "\nSettings (defaults in parentheses):\n"
1026 " -e, --ezio=TTY set EZIO3 serial device (/dev/ttyS1)\n"
1027 " -i, --input=TERMINAL also read input from TERMINAL;\n"
1028 " specify - for stdin, or vt to allocate\n"
1029 " and switch to a free virtual terminal\n"
1030 "\nOther options:\n"
1031 " -v, --verbose=MODULE:FACILITY:LEVEL configure logging levels\n"
1032 " -v, --verbose set maximum verbosity level\n"
1033 " -h, --help display this help message\n"
1034 " -V, --version display version information\n",
1035 program_name, program_name);