xref: /aosp_15_r20/external/toybox/toys/net/microcom.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
1*cf5a6c84SAndroid Build Coastguard Worker /* microcom.c - Simple serial console.
2*cf5a6c84SAndroid Build Coastguard Worker  *
3*cf5a6c84SAndroid Build Coastguard Worker  * Copyright 2017 The Android Open Source Project.
4*cf5a6c84SAndroid Build Coastguard Worker 
5*cf5a6c84SAndroid Build Coastguard Worker USE_MICROCOM(NEWTOY(microcom, "<1>1s#X", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_NOBUF))
6*cf5a6c84SAndroid Build Coastguard Worker 
7*cf5a6c84SAndroid Build Coastguard Worker config MICROCOM
8*cf5a6c84SAndroid Build Coastguard Worker   bool "microcom"
9*cf5a6c84SAndroid Build Coastguard Worker   default y
10*cf5a6c84SAndroid Build Coastguard Worker   help
11*cf5a6c84SAndroid Build Coastguard Worker     usage: microcom [-s SPEED] [-X] DEVICE
12*cf5a6c84SAndroid Build Coastguard Worker 
13*cf5a6c84SAndroid Build Coastguard Worker     Simple serial console. Hit CTRL-] for menu.
14*cf5a6c84SAndroid Build Coastguard Worker 
15*cf5a6c84SAndroid Build Coastguard Worker     -s	Set baud rate to SPEED
16*cf5a6c84SAndroid Build Coastguard Worker     -X	Ignore ^] menu escape
17*cf5a6c84SAndroid Build Coastguard Worker */
18*cf5a6c84SAndroid Build Coastguard Worker 
19*cf5a6c84SAndroid Build Coastguard Worker #define FOR_microcom
20*cf5a6c84SAndroid Build Coastguard Worker #include "toys.h"
21*cf5a6c84SAndroid Build Coastguard Worker 
GLOBALS(long s;int fd,stok;struct termios old_stdin,old_fd;)22*cf5a6c84SAndroid Build Coastguard Worker GLOBALS(
23*cf5a6c84SAndroid Build Coastguard Worker   long s;
24*cf5a6c84SAndroid Build Coastguard Worker 
25*cf5a6c84SAndroid Build Coastguard Worker   int fd, stok;
26*cf5a6c84SAndroid Build Coastguard Worker   struct termios old_stdin, old_fd;
27*cf5a6c84SAndroid Build Coastguard Worker )
28*cf5a6c84SAndroid Build Coastguard Worker 
29*cf5a6c84SAndroid Build Coastguard Worker // TODO: tty_sigreset outputs ansi escape sequences, how to disable?
30*cf5a6c84SAndroid Build Coastguard Worker static void restore_states(int i)
31*cf5a6c84SAndroid Build Coastguard Worker {
32*cf5a6c84SAndroid Build Coastguard Worker   if (TT.stok) tcsetattr(0, TCSANOW, &TT.old_stdin);
33*cf5a6c84SAndroid Build Coastguard Worker   tcsetattr(TT.fd, FLAG(s)*TCSAFLUSH, &TT.old_fd);
34*cf5a6c84SAndroid Build Coastguard Worker }
35*cf5a6c84SAndroid Build Coastguard Worker 
36*cf5a6c84SAndroid Build Coastguard Worker // TODO bookmark, jump to bottom line (if !end), clear and return afterwards
handle_esc(void)37*cf5a6c84SAndroid Build Coastguard Worker static void handle_esc(void)
38*cf5a6c84SAndroid Build Coastguard Worker {
39*cf5a6c84SAndroid Build Coastguard Worker   char input;
40*cf5a6c84SAndroid Build Coastguard Worker 
41*cf5a6c84SAndroid Build Coastguard Worker   xputsn("\r\n[b]reak, [p]aste file, [q]uit: ");
42*cf5a6c84SAndroid Build Coastguard Worker   if (read(0, &input, 1)<1 || input == CTRL('D') || input == 'q') {
43*cf5a6c84SAndroid Build Coastguard Worker     xputs("exit\r");
44*cf5a6c84SAndroid Build Coastguard Worker     xexit();
45*cf5a6c84SAndroid Build Coastguard Worker   }
46*cf5a6c84SAndroid Build Coastguard Worker   if (input == 'b') tcsendbreak(TT.fd, 0);
47*cf5a6c84SAndroid Build Coastguard Worker   else if (input == 'p') {
48*cf5a6c84SAndroid Build Coastguard Worker     long long written = 0, size;
49*cf5a6c84SAndroid Build Coastguard Worker     char* filename;
50*cf5a6c84SAndroid Build Coastguard Worker     int len = 0, fd;
51*cf5a6c84SAndroid Build Coastguard Worker 
52*cf5a6c84SAndroid Build Coastguard Worker     // TODO: share code with hexedit's prompt() and vi's ex mode.
53*cf5a6c84SAndroid Build Coastguard Worker     // TODO: tab completion!
54*cf5a6c84SAndroid Build Coastguard Worker     memset(toybuf, 0, sizeof(toybuf));
55*cf5a6c84SAndroid Build Coastguard Worker     while (1) {
56*cf5a6c84SAndroid Build Coastguard Worker       xprintf("\r\e[2K\e[1mFilename: \e[0m%s", toybuf);
57*cf5a6c84SAndroid Build Coastguard Worker       if (read(0, &input, 1) <= 0 || input == CTRL('[')) {
58*cf5a6c84SAndroid Build Coastguard Worker         len = 0;
59*cf5a6c84SAndroid Build Coastguard Worker         break;
60*cf5a6c84SAndroid Build Coastguard Worker       }
61*cf5a6c84SAndroid Build Coastguard Worker       if (input == '\r') break;
62*cf5a6c84SAndroid Build Coastguard Worker       if (input == 0x7f && len > 0) toybuf[--len] = 0;
63*cf5a6c84SAndroid Build Coastguard Worker       else if (input == CTRL('U')) while (len > 0) toybuf[--len] = 0;
64*cf5a6c84SAndroid Build Coastguard Worker       else if (input >= ' ' && input <= 0x7f && len < sizeof(toybuf))
65*cf5a6c84SAndroid Build Coastguard Worker         toybuf[len++] = input;
66*cf5a6c84SAndroid Build Coastguard Worker     }
67*cf5a6c84SAndroid Build Coastguard Worker     xputsn("\r\e[2K");
68*cf5a6c84SAndroid Build Coastguard Worker     toybuf[len] = 0;
69*cf5a6c84SAndroid Build Coastguard Worker     if (!len) return;
70*cf5a6c84SAndroid Build Coastguard Worker     if ((fd = xopen(toybuf, O_RDONLY | WARN_ONLY)) < 0) {
71*cf5a6c84SAndroid Build Coastguard Worker       // xopen() warning message ends with a LF without CR, so manually print a
72*cf5a6c84SAndroid Build Coastguard Worker       // CR here to move the cursor back to the front.
73*cf5a6c84SAndroid Build Coastguard Worker       fputc('\r', stderr);
74*cf5a6c84SAndroid Build Coastguard Worker       return;
75*cf5a6c84SAndroid Build Coastguard Worker     }
76*cf5a6c84SAndroid Build Coastguard Worker     // TODO xsendfile with progress indicator
77*cf5a6c84SAndroid Build Coastguard Worker     filename = xstrdup(toybuf);
78*cf5a6c84SAndroid Build Coastguard Worker     size = fdlength(fd);
79*cf5a6c84SAndroid Build Coastguard Worker     // The alternative would be to just feed this fd into the usual loop,
80*cf5a6c84SAndroid Build Coastguard Worker     // so we're reading back these characters if they're being echoed, but
81*cf5a6c84SAndroid Build Coastguard Worker     // for my specific use case of pasting into `base64 -d -i > foo`, this
82*cf5a6c84SAndroid Build Coastguard Worker     // is a much more convenient UI.
83*cf5a6c84SAndroid Build Coastguard Worker     while ((len = read(fd, toybuf, sizeof(toybuf))) > 0) {
84*cf5a6c84SAndroid Build Coastguard Worker       written += len;
85*cf5a6c84SAndroid Build Coastguard Worker       xprintf("\r\e[2KPasting '%s' %lld/%lld (%lld%%)...", filename, written,
86*cf5a6c84SAndroid Build Coastguard Worker         size, written*100/size);
87*cf5a6c84SAndroid Build Coastguard Worker       xwrite(TT.fd, toybuf, len);
88*cf5a6c84SAndroid Build Coastguard Worker     }
89*cf5a6c84SAndroid Build Coastguard Worker     free(filename);
90*cf5a6c84SAndroid Build Coastguard Worker     close(fd);
91*cf5a6c84SAndroid Build Coastguard Worker   } else xprintf("Ignoring unknown command.");
92*cf5a6c84SAndroid Build Coastguard Worker 
93*cf5a6c84SAndroid Build Coastguard Worker   xprintf("\r\n");
94*cf5a6c84SAndroid Build Coastguard Worker }
95*cf5a6c84SAndroid Build Coastguard Worker 
microcom_main(void)96*cf5a6c84SAndroid Build Coastguard Worker void microcom_main(void)
97*cf5a6c84SAndroid Build Coastguard Worker {
98*cf5a6c84SAndroid Build Coastguard Worker   struct termios tio;
99*cf5a6c84SAndroid Build Coastguard Worker   struct pollfd fds[2];
100*cf5a6c84SAndroid Build Coastguard Worker   int i;
101*cf5a6c84SAndroid Build Coastguard Worker 
102*cf5a6c84SAndroid Build Coastguard Worker   // Open with O_NDELAY, but switch back to blocking for reads.
103*cf5a6c84SAndroid Build Coastguard Worker   TT.fd = xopen(*toys.optargs, O_RDWR | O_NOCTTY | O_NDELAY);
104*cf5a6c84SAndroid Build Coastguard Worker   if (-1==(i = fcntl(TT.fd, F_GETFL, 0)) || fcntl(TT.fd, F_SETFL, i&~O_NDELAY)
105*cf5a6c84SAndroid Build Coastguard Worker       || tcgetattr(TT.fd, &TT.old_fd))
106*cf5a6c84SAndroid Build Coastguard Worker     perror_exit_raw(*toys.optargs);
107*cf5a6c84SAndroid Build Coastguard Worker 
108*cf5a6c84SAndroid Build Coastguard Worker   // Set both input and output to raw mode.
109*cf5a6c84SAndroid Build Coastguard Worker   memcpy(&tio, &TT.old_fd, sizeof(struct termios));
110*cf5a6c84SAndroid Build Coastguard Worker   cfmakeraw(&tio);
111*cf5a6c84SAndroid Build Coastguard Worker   if (FLAG(s)) xsetspeed(&tio, TT.s);
112*cf5a6c84SAndroid Build Coastguard Worker   if (tcsetattr(TT.fd, FLAG(s)*TCSAFLUSH, &tio)) perror_exit("set speed");
113*cf5a6c84SAndroid Build Coastguard Worker   if (!set_terminal(0, 1, 0, &TT.old_stdin)) TT.stok++;
114*cf5a6c84SAndroid Build Coastguard Worker   // ...and arrange to restore things, however we may exit.
115*cf5a6c84SAndroid Build Coastguard Worker   sigatexit(restore_states);
116*cf5a6c84SAndroid Build Coastguard Worker 
117*cf5a6c84SAndroid Build Coastguard Worker   fds[0].fd = TT.fd;
118*cf5a6c84SAndroid Build Coastguard Worker   fds[1].fd = 0;
119*cf5a6c84SAndroid Build Coastguard Worker   fds[0].events = fds[1].events = POLLIN;
120*cf5a6c84SAndroid Build Coastguard Worker 
121*cf5a6c84SAndroid Build Coastguard Worker   if (!FLAG(X)) xputs("Escape character is '^]'.\r");
122*cf5a6c84SAndroid Build Coastguard Worker   while (poll(fds, 2, -1) > 0) {
123*cf5a6c84SAndroid Build Coastguard Worker 
124*cf5a6c84SAndroid Build Coastguard Worker     // Read from connection, write to stdout.
125*cf5a6c84SAndroid Build Coastguard Worker     if (fds[0].revents) {
126*cf5a6c84SAndroid Build Coastguard Worker       if (0 < (i = read(TT.fd, toybuf, sizeof(toybuf)))) xwrite(0, toybuf, i);
127*cf5a6c84SAndroid Build Coastguard Worker       else break;
128*cf5a6c84SAndroid Build Coastguard Worker     }
129*cf5a6c84SAndroid Build Coastguard Worker 
130*cf5a6c84SAndroid Build Coastguard Worker     // Read from stdin, write to connection.
131*cf5a6c84SAndroid Build Coastguard Worker     if (fds[1].revents) {
132*cf5a6c84SAndroid Build Coastguard Worker       if (read(0, toybuf, 1) != 1) break;
133*cf5a6c84SAndroid Build Coastguard Worker       if (!FLAG(X) && *toybuf == CTRL(']')) handle_esc();
134*cf5a6c84SAndroid Build Coastguard Worker       else xwrite(TT.fd, toybuf, 1);
135*cf5a6c84SAndroid Build Coastguard Worker     }
136*cf5a6c84SAndroid Build Coastguard Worker   }
137*cf5a6c84SAndroid Build Coastguard Worker }
138