devcons.c (6043B)
1 #include <os9/os9.h> 2 3 #include <libk.h> 4 5 #include <ctype.h> 6 #include <errno.h> 7 #include <string.h> 8 9 #include "dev.h" 10 11 #define CONSOUT 2 12 #define NAMESIZE 15 13 #define CONSSTATUS 128 14 15 #define CTRL(x) ((x) & 0x1f) 16 #define DEL 127 17 18 enum Orootqid { 19 Qconsfs, 20 Qraw, 21 Qctl 22 }; 23 24 struct conscmd { 25 char *name; 26 int (*fn)(struct conscmd *, char *); 27 }; 28 29 static struct conscmd cmds[]; 30 31 static Dirtab dirtab[] = { 32 {"raw", QID(CHFILE, 0, Qraw), 0, O_READ | O_WRITE}, 33 {"ctl", QID(CHFILE, 0, Qctl), 0, O_READ | O_WRITE} 34 }; 35 36 static Chan *in; 37 static char inname[NAMESIZE]; 38 static Chan *out[CONSOUT]; 39 static char outname[CONSOUT][NAMESIZE]; 40 41 static char buffer[LINELEN]; 42 static int head; 43 static int tail; 44 static int cookedf, echof; 45 46 static int 47 conswalk(Chan *c, char *name) 48 { 49 return devwalk(c, name, dirtab, NELEM(dirtab), devgen); 50 } 51 52 static int 53 consstat(Chan *c, char *file, unsigned char *buf, int n) 54 { 55 return devstat(c, file, buf, n, dirtab, NELEM(dirtab), devgen); 56 } 57 58 static int 59 consstatus(void *buf, Chan *c, int n) 60 { 61 int i; 62 int len = 0; 63 char tmp[CONSSTATUS]; 64 65 if (in) 66 len += ksnprint(tmp, sizeof(tmp), 67 "addin %s\n", 68 inname); 69 70 for (i = 0; i < CONSOUT; i++) { 71 if (out[i]) { 72 len += ksnprint(tmp + len, 73 sizeof(tmp) - len, 74 "addout %s\n", 75 outname[i]); 76 } 77 } 78 79 if (len == sizeof(tmp)) 80 panic("consstatus"); 81 82 return buf2chan(c, buf, tmp, n, len); 83 } 84 85 static int 86 flushraw(char *buf, int n) 87 { 88 Chan **pp, *p; 89 int r, w; 90 91 r = 0; 92 for (pp = out; pp < &out[CONSOUT]; pp++) { 93 if ((p = *pp) == NULL) 94 continue; 95 w = devtab[p->type]->write(p, buf, n); 96 if (w < 0) { 97 r = -1; 98 } else if (w != n) { 99 seterror(EIO); 100 r = -1; 101 } 102 } 103 104 return (r < 0) ? r : n; 105 } 106 107 static int 108 conswriteraw(char *buf, int n) 109 { 110 int i, idx; 111 char wrtbuf[LINELEN]; 112 113 idx = 0; 114 for (i = 0; i < n; i++) { 115 if (idx + 2 > sizeof(wrtbuf)) { 116 if (flushraw(wrtbuf, idx) < 0) 117 return -1; 118 idx = 0; 119 } 120 121 if (buf[i] == '\n') 122 wrtbuf[idx++] = '\r'; 123 124 wrtbuf[idx++] = buf[i]; 125 } 126 127 if (idx > 0) { 128 if (flushraw(wrtbuf, idx) < 0) 129 return -1; 130 } 131 return n; 132 } 133 134 static int 135 consaddin(struct conscmd *cmd, char *s) 136 { 137 if (in) { 138 seterror(ENOMEM); 139 return -1; 140 } 141 142 in = namec(s, O_READ); 143 strcpy(inname, s); 144 return 0; 145 } 146 147 static int 148 consaddout(struct conscmd *cmd, char *s) 149 { 150 int i; 151 152 for (i = 0; i < CONSOUT; i++) { 153 if (!out[i]) { 154 out[i] = namec(s, O_WRITE); 155 strcpy(outname[i], s); 156 return 0; 157 } 158 } 159 seterror(ENOMEM); 160 return -1; 161 } 162 163 static int 164 consdelin(struct conscmd *cmd, char *s) 165 { 166 if (!in || strcmp(s, inname)) { 167 seterror(ENOENT); 168 return -1; 169 } 170 171 chanclose(in); 172 in = NULL; 173 inname[0] = '\0'; 174 return 0; 175 } 176 177 static int 178 consdelout(struct conscmd *cmd, char *s) 179 { 180 int i; 181 182 for (i = 0; i < CONSOUT; i++) { 183 if (out[i] && !strcmp(s, outname[i])) { 184 chanclose(out[i]); 185 out[i] = NULL; 186 outname[i][0] = '\0'; 187 return 0; 188 } 189 } 190 seterror(ENOENT); 191 return -1; 192 } 193 194 static int 195 consmode(struct conscmd *cmd, char *s) 196 { 197 int mode; 198 199 if (!strcmp(s, "on")) { 200 mode = 1; 201 } else if(!strcmp(s, "off")) { 202 mode = 0; 203 } else { 204 seterror(EINVAL); 205 return -1; 206 } 207 208 if (!strcmp(cmd->name, "cooked")) { 209 cookedf = mode; 210 } else if (!strcmp(cmd->name, "echo")) { 211 echof = mode; 212 } else { 213 seterror(EINVAL); 214 return -1; 215 } 216 217 return 0; 218 } 219 220 static int 221 conswrite(Chan *c, void *buf, int n) 222 { 223 struct conscmd *cmd; 224 char *tokens[2]; 225 226 switch (c->qid.path) { 227 case Qraw: 228 return conswriteraw(buf, n); 229 case Qctl: 230 if (tokenize(buf, n, tokens, 2) != 2) { 231 seterror(EINVAL); 232 return -1; 233 } 234 235 for (cmd = cmds; *cmd->name; ++cmd) { 236 if (!strcmp(tokens[0], cmd->name)) 237 return (*cmd->fn)(cmd, tokens[1]); 238 } 239 240 seterror(EINVAL); 241 return -1; 242 default: 243 panic("conswrite"); 244 } 245 } 246 247 static int 248 echo(char *buf, int n) 249 { 250 if (echof) { 251 if (conswriteraw(buf, n) < 0) 252 return -1; 253 } 254 return buf[n-1]; 255 } 256 257 static void 258 erase(int n) 259 { 260 int i; 261 char ch; 262 263 if (head < n) 264 n = head; 265 266 ch = '\b'; 267 for (i = 0; i < n; i++) 268 echo(&ch, 1); 269 ch = ' '; 270 for (i = 0; i < n; i++) 271 echo(&ch, 1); 272 ch = '\b'; 273 for (i = 0; i < n; i++) 274 echo(&ch, 1); 275 276 head -= n; 277 } 278 279 static int 280 wordsize(void) 281 { 282 int n = 1; 283 284 while (head - n > 0 && buffer[head - n] == ' ') 285 n++; 286 while (head - n > 0 && buffer[head - n] != ' ') 287 n++; 288 289 return n; 290 } 291 292 static int 293 edit(char key) 294 { 295 char ch; 296 297 if (!cookedf) { 298 buffer[head++] = key; 299 return echo(&key, 1); 300 } 301 302 switch (key) { 303 case '\0': 304 return 0; 305 case '\b': 306 case DEL: 307 erase(1); 308 return 0; 309 case CTRL('L'): 310 ch = '\r'; 311 echo(&ch, 1); 312 echo(buffer, head-1); 313 return 0; 314 case CTRL('U'): 315 ch = '\r'; 316 echo(&ch, 1); 317 erase(head); 318 echo(&ch, 1); 319 return 0; 320 case CTRL('W'): 321 erase(wordsize()); 322 return 0; 323 case '\r': 324 key = '\n'; 325 default: 326 buffer[head++] = key; 327 return echo(&key, 1); 328 } 329 } 330 331 static int 332 fillbuffer(void) 333 { 334 char ch; 335 336 while (head + 1 < sizeof(buffer)) { 337 if (devtab[in->type]->read(in, &ch, 1) < 0) { 338 ch = EOF; 339 break; 340 } 341 if (edit(ch) == '\n') 342 break; 343 } 344 if (ch == EOF && head == 0) { 345 seterror(EIO); 346 return -1; 347 } 348 349 return 0; 350 } 351 352 static int 353 readqueue(void *buf, int n) 354 { 355 int size = head - tail; 356 357 if (n < size) { 358 memcpy(buf, &buffer[tail], n); 359 tail += n; 360 return n; 361 } else { 362 memcpy(buf, &buffer[tail], size); 363 head = 0; 364 tail = 0; 365 return size; 366 } 367 } 368 369 static int 370 consread(Chan *c, void *buf, int n) 371 { 372 switch (c->qid.path) { 373 case Qconsfs: 374 return dirread(c, buf, n, dirtab, NELEM(dirtab), devgen); 375 case Qraw: 376 if (!in) { 377 seterror(ENOENT); 378 return -1; 379 } 380 if (head == 0) { 381 if (fillbuffer() < 0) 382 return -1; 383 } 384 385 return readqueue(buf, n); 386 case Qctl: 387 return consstatus(buf, c, n); 388 default: 389 panic("consread"); 390 } 391 } 392 393 static struct conscmd cmds[] = { 394 {"addin", consaddin}, 395 {"addout", consaddout}, 396 {"delin", consdelin}, 397 {"delout", consdelout}, 398 {"cooked", consmode}, 399 {"echo", consmode}, 400 {""} 401 }; 402 403 Dev consdevtab = { 404 .id = 'c', 405 .stat = consstat, 406 .clone = devclone, 407 .attach = devattach, 408 .walk = conswalk, 409 .read = consread, 410 .write = conswrite, 411 .seek = deverrseek, 412 .sync = devsync, 413 .close = devclose, 414 };