console-handler: Tabs->spaces
[AROS.git] / rom / filesys / console_handler / con_handler.c
blobfe7a62ccc8d09b26ca7874f794a875c3462836ca
1 #include <proto/exec.h>
2 #include <exec/libraries.h>
3 #include <exec/resident.h>
4 #include <exec/memory.h>
5 #include <exec/io.h>
6 #include <exec/errors.h>
7 #include <exec/alerts.h>
8 #include <utility/tagitem.h>
9 #include <dos/exall.h>
10 #include <dos/dosasl.h>
11 #include <intuition/intuition.h>
12 #include <proto/dos.h>
13 #include <proto/intuition.h>
14 #include <devices/conunit.h>
16 #include <stddef.h>
17 #include <string.h>
19 #include "con_handler_intern.h"
20 #include "support.h"
22 #undef SDEBUG
23 #undef DEBUG
24 #define SDEBUG 0
25 #define DEBUG 0
26 #include <aros/debug.h>
28 static char *BSTR2C(BSTR srcs)
30 UBYTE *src = BADDR(srcs);
31 char *dst;
33 dst = AllocVec(src[0] + 1, MEMF_ANY);
34 if (!dst)
35 return NULL;
36 memcpy(dst, src + 1, src[0]);
37 dst[src[0]] = 0;
38 return dst;
40 static WORD isdosdevicec(CONST_STRPTR s)
42 UBYTE b = 0;
43 while (s[b])
45 if (s[b] == ':')
46 return b;
47 b++;
49 return -1;
52 #define ioReq(x) ((struct IORequest *)x)
54 static const struct NewWindow default_nw =
56 0, /* LeftEdge */
57 0, /* TopEdge */
58 -1, /* Width */
59 -1, /* Height */
60 1, /* DetailPen */
61 0, /* BlockPen */
62 0, /* IDCMP */
63 WFLG_DEPTHGADGET |
64 WFLG_SIZEGADGET |
65 WFLG_DRAGBAR |
66 WFLG_SIZEBRIGHT |
67 WFLG_SMART_REFRESH |
68 WFLG_ACTIVATE,
69 0, /* FirstGadget */
70 0, /* CheckMark */
71 "CON:", /* Title */
72 0, /* Screen */
73 0, /* Bitmap */
74 100, /* MinWidth */
75 70, /* MinHeight */
76 32767, /* MaxWidth */
77 32767, /* MaxHeight */
78 WBENCHSCREEN /* type */
82 static LONG MakeConWindow(struct filehandle *fh)
84 LONG err = 0;
86 if (fh->otherwindow == NULL)
88 struct TagItem win_tags[] =
90 { WA_PubScreen, 0 },
91 { WA_AutoAdjust, TRUE },
92 { WA_PubScreenName, 0 },
93 { WA_PubScreenFallBack, TRUE },
94 { TAG_DONE }
97 win_tags[2].ti_Data = (IPTR) fh->screenname;
98 D(bug("[contask] Opening window on screen %s, IntuitionBase = 0x%p\n", fh->screenname, IntuitionBase));
100 /* Autoadjust doesn't enforce the window's width and height to be larger than
101 minwidth and minheight, so we set it here to avoid crashes in devs/console
102 if a user does e.g. dir >con:0/0/0/0
104 fh->nw.Width = fh->nw.Width > fh->nw.MinWidth ? fh->nw.Width : -1;
105 fh->nw.Height = fh->nw.Height == -1 || fh->nw.Height > fh->nw.MinHeight ? fh->nw.Height : fh->nw.MinHeight;
107 fh->window = OpenWindowTagList(&fh->nw, (struct TagItem *) win_tags);
109 else
111 D(bug("[contask] Using window %p\n", fh->otherwindow));
112 fh->window = fh->otherwindow;
115 if (fh->window)
117 D(bug("contask: window opened\n"));
118 fh->conreadio->io_Data = (APTR) fh->window;
119 fh->conreadio->io_Length = sizeof(struct Window);
121 if (0 == OpenDevice("console.device", CONU_SNIPMAP, ioReq(fh->conreadio), 0))
123 const UBYTE lf_on[] =
124 { 0x9B, 0x32, 0x30, 0x68 }; /* Set linefeed mode */
126 D(bug("contask: device opened\n"));
128 fh->flags |= FHFLG_CONSOLEDEVICEOPEN;
130 fh->conwriteio = *fh->conreadio;
131 fh->conwriteio.io_Message.mn_ReplyPort = fh->conwritemp;
133 /* Turn the console into LF+CR mode so that both
134 linefeed and carriage return is done on
136 fh->conwriteio.io_Command = CMD_WRITE;
137 fh->conwriteio.io_Data = (APTR) lf_on;
138 fh->conwriteio.io_Length = 4;
140 DoIO(ioReq(&fh->conwriteio));
142 } /* if (0 == OpenDevice("console.device", CONU_STANDARD, ioReq(fh->conreadio), 0)) */
143 else
145 err = ERROR_INVALID_RESIDENT_LIBRARY;
147 if (err)
148 CloseWindow(fh->window);
150 } /* if (fh->window) */
151 else
153 D(bug("[contask] Failed to open a window\n"));
154 err = ERROR_NO_FREE_STORE;
157 return err;
160 static BOOL MakeSureWinIsOpen(struct filehandle *fh)
162 if (fh->window)
163 return TRUE;
164 return MakeConWindow(fh) == 0;
167 static void close_con(struct filehandle *fh)
169 /* Clean up */
171 D(bug("[CON] Deleting timer request 0x%p\n", fh->timerreq));
172 if (fh->timerreq)
174 CloseDevice((struct IORequest *) fh->timerreq);
175 DeleteIORequest((struct IORequest *) fh->timerreq);
178 D(bug("[CON] Deleting timer port 0x%p\n", fh->timermp));
179 DeleteMsgPort(fh->timermp);
181 if (fh->flags & FHFLG_CONSOLEDEVICEOPEN)
183 D(bug("[CON] Closing console.device...\n"));
184 CloseDevice((struct IORequest *) fh->conreadio);
187 D(bug("[CON] Closing window 0x%p\n", fh->window));
188 if (fh->window)
189 CloseWindow(fh->window);
191 D(bug("[CON] Delete console.device IORequest 0x%p\n", fh->conreadio));
192 DeleteIORequest(ioReq(fh->conreadio));
194 D(bug("[CON] Delete console.device MsgPort 0x%p\n", fh->conreadmp));
195 FreeVec(fh->conreadmp);
197 if (fh->screenname)
198 FreeVec(fh->screenname);
199 if (fh->wintitle)
200 FreeVec(fh->wintitle);
201 if (fh->pastebuffer)
202 FreeMem(fh->pastebuffer, PASTEBUFSIZE);
204 CloseLibrary((struct Library*) fh->intuibase);
205 CloseLibrary((struct Library*) fh->dosbase);
207 /* These libraries are opened only if completion was used */
208 if (fh->gfxbase)
209 CloseLibrary((struct Library*) fh->gfxbase);
210 if (fh->gtbase)
211 CloseLibrary(fh->gtbase);
213 FreeVec(fh);
216 static struct filehandle *open_con(struct DosPacket *dp, LONG *perr)
218 char *filename, *fn;
219 struct filehandle *fh;
220 struct DeviceNode *dn;
221 LONG err, ok;
222 LONG i;
224 dn = BADDR(dp->dp_Arg3);
225 *perr = ERROR_NO_FREE_STORE;
226 fh = AllocVec(sizeof(struct filehandle), MEMF_PUBLIC | MEMF_CLEAR);
227 if (!fh)
228 return NULL;
230 fh->intuibase = (APTR) OpenLibrary("intuition.library", 0);
231 fh->dosbase = (APTR) OpenLibrary("dos.library", 0);
232 fh->utilbase = (APTR) OpenLibrary("utility.library", 0);
233 Forbid();
234 fh->inputbase = (struct Device *) FindName(&SysBase->DeviceList, "input.device");
235 Permit();
237 if (!fh->intuibase || !fh->dosbase || !fh->utilbase || !fh->inputbase)
239 CloseLibrary((APTR) fh->utilbase);
240 CloseLibrary((APTR) fh->dosbase);
241 CloseLibrary((APTR) fh->intuibase);
242 FreeVec(fh);
243 return NULL;
246 fh->timermp = CreateMsgPort();
247 fh->timerreq = (struct timerequest*) CreateIORequest(fh->timermp, sizeof(struct timerequest));
248 OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *) fh->timerreq, 0);
250 err = 0;
251 filename = BSTR2C((BSTR) dp->dp_Arg1);
252 fn = filename;
253 i = isdosdevicec(fn);
254 if (i >= 0)
255 fn += i + 1;
257 fh->contask = FindTask(0);
259 NEWLIST(&fh->pendingReads);
261 /* Create msgport for console.device communication */
262 fh->conreadmp = AllocVec(sizeof(struct MsgPort) * 2, MEMF_PUBLIC | MEMF_CLEAR);
263 if (fh->conreadmp)
266 fh->conreadmp->mp_Node.ln_Type = NT_MSGPORT;
267 fh->conreadmp->mp_Flags = PA_SIGNAL;
268 fh->conreadmp->mp_SigBit = AllocSignal(-1);
269 fh->conreadmp->mp_SigTask = fh->contask;
270 NEWLIST(&fh->conreadmp->mp_MsgList);
272 fh->conwritemp = fh->conreadmp + 1;
274 fh->conwritemp->mp_Node.ln_Type = NT_MSGPORT;
275 fh->conwritemp->mp_Flags = PA_SIGNAL;
276 fh->conwritemp->mp_SigBit = AllocSignal(-1);
277 fh->conwritemp->mp_SigTask = fh->contask;
278 NEWLIST(&fh->conwritemp->mp_MsgList);
280 fh->conreadio = (struct IOStdReq *) CreateIORequest(fh->conreadmp, sizeof(struct IOStdReq));
281 if (fh->conreadio)
283 D(bug("contask: conreadio created, parms '%s'\n", fn));
285 fh->nw = default_nw;
287 if (parse_filename(fh, fn, &fh->nw))
289 if (!(fh->flags & FHFLG_AUTO))
291 err = MakeConWindow(fh);
292 if (!err)
293 ok = TRUE;
295 else
297 ok = TRUE;
300 else
301 err = ERROR_BAD_STREAM_NAME;
303 if (!ok)
305 DeleteIORequest(ioReq(fh->conreadio));
308 } /* if (fh->conreadio) */
309 else
311 err = ERROR_NO_FREE_STORE;
314 } /* if (fh->conreadmp) */
315 else
317 err = ERROR_NO_FREE_STORE;
320 if (dn->dn_Startup)
321 fh->flags |= FHFLG_RAW;
323 if (!ok)
324 close_con(fh);
326 *perr = err;
327 FreeVec(filename);
328 return fh;
331 static void startread(struct filehandle *fh)
333 if (fh->flags & FHFLG_ASYNCCONSOLEREAD)
334 return;
335 fh->conreadio->io_Command = CMD_READ;
336 fh->conreadio->io_Data = fh->consolebuffer;
337 fh->conreadio->io_Length = CONSOLEBUFFER_SIZE;
338 SendIO((struct IORequest*) fh->conreadio);
339 fh->flags |= FHFLG_ASYNCCONSOLEREAD;
342 static void stopwait(struct filehandle *fh, struct DosPacket *waitingdp, ULONG result)
344 if (waitingdp)
346 AbortIO((struct IORequest *) fh->timerreq);
347 WaitIO((struct IORequest *) fh->timerreq);
348 replypkt(waitingdp, result);
352 static void stopread(struct filehandle *fh, struct DosPacket *waitingdp)
354 struct Message *msg, *next_msg;
356 stopwait(fh, waitingdp, DOSFALSE);
358 ForeachNodeSafe(&fh->pendingReads, msg, next_msg)
360 struct DosPacket *dpr;
362 Remove((struct Node *) msg);
363 dpr = (struct DosPacket*) msg->mn_Node.ln_Name;
364 replypkt(dpr, DOSFALSE);
368 LONG CONMain(struct ExecBase *SysBase)
370 struct MsgPort *mp;
371 struct DosPacket *dp;
372 struct Message *mn;
373 struct FileHandle *dosfh;
374 LONG error;
375 struct filehandle *fh;
376 struct FileLock *fl;
377 struct DosPacket *waitingdp = NULL;
379 D(bug("[CON] started\n"));
380 mp = &((struct Process*) FindTask(NULL))->pr_MsgPort;
381 WaitPort(mp);
382 dp = (struct DosPacket*) GetMsg(mp)->mn_Node.ln_Name;
383 D(bug("[CON] startup message received. port=0x%p path='%b'\n", mp, dp->dp_Arg1));
385 fh = open_con(dp, &error);
386 if (!fh)
388 D(bug("[CON] init failed\n"));
389 goto end;
391 D(bug("[CON] 0x%p open\n", fh));
392 replypkt(dp, DOSTRUE);
394 for (;;)
396 ULONG conreadmask = 1L << fh->conreadmp->mp_SigBit;
397 ULONG timermask = 1L << fh->timermp->mp_SigBit;
398 ULONG packetmask = 1L << mp->mp_SigBit;
399 ULONG sigs;
401 sigs = Wait(packetmask | conreadmask | timermask);
403 if (sigs & timermask)
405 if (waitingdp)
407 replypkt(waitingdp, DOSFALSE);
408 waitingdp = NULL;
412 if (sigs & conreadmask)
414 GetMsg(fh->conreadmp);
415 fh->flags &= ~FHFLG_ASYNCCONSOLEREAD;
416 if (waitingdp)
418 stopwait(fh, waitingdp, DOSTRUE);
419 waitingdp = NULL;
421 D(bug("IO_READ %d\n", fh->conreadio->io_Actual));
422 fh->conbuffersize = fh->conreadio->io_Actual;
423 fh->conbufferpos = 0;
424 /* terminate with 0 char */
425 fh->consolebuffer[fh->conbuffersize] = '\0';
426 if (fh->flags & FHFLG_RAW)
428 LONG inp;
429 /* raw mode */
430 for (inp = 0; (inp < fh->conbuffersize) && (fh->inputpos < INPUTBUFFER_SIZE);)
432 fh->inputbuffer[fh->inputpos++] = fh->consolebuffer[inp++];
434 fh->inputsize = fh->inputstart = fh->inputpos;
435 HandlePendingReads(fh);
436 } /* if (fh->flags & FHFLG_RAW) */
437 else
439 /* Cooked mode */
440 if (process_input(fh))
443 * process_input() returns TRUE when EOF was received after the WAIT console
444 * has been closed by the owner.
446 dp = NULL;
447 goto end;
449 } /* if (fh->flags & FHFLG_RAW) else ... */
450 startread(fh);
453 while ((mn = GetMsg(mp)))
455 dp = (struct DosPacket*) mn->mn_Node.ln_Name;
456 dp->dp_Res2 = 0;
458 bug("[CON 0x%p] packet 0x%p:%d 0x%p,0x%p,0x%p\n", fh, dp, dp->dp_Type, dp->dp_Arg1, dp->dp_Arg2,
459 dp->dp_Arg3));
460 error = 0;
461 switch (dp->dp_Type)
463 case ACTION_FH_FROM_LOCK:
464 fl = BADDR(dp->dp_Arg2);
465 if (fl->fl_Task != mp || fl->fl_Key != (IPTR) fh)
467 replypkt2(dp, DOSFALSE, ERROR_OBJECT_NOT_FOUND);
468 break;
470 fh->usecount--;
471 FreeMem(fl, sizeof(*fl));
472 /* Fallthrough */
473 case ACTION_FINDINPUT:
474 case ACTION_FINDOUTPUT:
475 case ACTION_FINDUPDATE:
476 dosfh = BADDR(dp->dp_Arg1);
477 dosfh->fh_Interactive = DOSTRUE;
478 dosfh->fh_Arg1 = (SIPTR) fh;
479 fh->usecount++;
480 fh->breaktask = dp->dp_Port->mp_SigTask;
481 D(bug("[CON] Find fh=%x. Usecount=%d\n", dosfh, fh->usecount));
482 replypkt(dp, DOSTRUE);
483 break;
484 case ACTION_COPY_DIR_FH:
485 fl = AllocMem(sizeof(*fl), MEMF_CLEAR | MEMF_PUBLIC);
486 if (fl == BNULL)
488 replypkt2(dp, (SIPTR) BNULL, ERROR_NO_FREE_STORE);
490 else
492 fh->usecount++;
493 fl->fl_Task = mp;
494 fl->fl_Key = (IPTR) fh;
495 replypkt(dp, (SIPTR) MKBADDR(fl));
497 break;
498 case ACTION_END:
499 fh->usecount--;
500 D(bug("[CON] usecount=%d\n", fh->usecount));
501 if (fh->usecount <= 0)
503 if (fh->flags & FHFLG_WAIT)
505 D(bug("[CON] Delayed close, waiting...\n"));
508 * Bounce all pending read and waits (the same as we do when exiting).
509 * However the process is still around, waiting for EOF input.
510 * Our user has just closed his struct FileHandle and dropped us.
512 stopread(fh, waitingdp);
513 waitingdp = NULL;
514 fh->flags = (fh->flags & ~FHFLG_READPENDING) | FHFLG_WAITFORCLOSE;
516 else
517 goto end;
519 replypkt(dp, DOSTRUE);
520 break;
521 case ACTION_READ:
522 if (!MakeSureWinIsOpen(fh))
524 replypkt2(dp, DOSFALSE, ERROR_NO_FREE_STORE);
525 break;
527 fh->breaktask = dp->dp_Port->mp_SigTask;
528 startread(fh);
529 con_read(fh, dp);
530 break;
531 case ACTION_WRITE:
532 if (!MakeSureWinIsOpen(fh))
534 replypkt2(dp, DOSFALSE, ERROR_NO_FREE_STORE);
535 break;
537 fh->breaktask = dp->dp_Port->mp_SigTask;
538 startread(fh);
539 answer_write_request(fh, dp);
540 break;
541 case ACTION_SCREEN_MODE:
543 D(bug("ACTION_SCREEN_MODE %s\n", dp->dp_Arg1 ? "RAW" : "CON"));
544 if (dp->dp_Arg1 && !(fh->flags & FHFLG_RAW))
546 /* Switching from CON: mode to RAW: mode */
547 fh->flags |= FHFLG_RAW;
548 fh->inputstart = fh->inputsize;
549 fh->inputpos = fh->inputsize;
550 HandlePendingReads(fh);
552 else
554 /* otherwise just copy the flags */
555 if (dp->dp_Arg1)
556 fh->flags |= FHFLG_RAW;
557 else
558 fh->flags &= ~FHFLG_RAW;
560 replypkt(dp, DOSTRUE);
562 break;
563 case ACTION_CHANGE_SIGNAL:
565 struct Task *old = fh->breaktask;
566 if (dp->dp_Arg2)
567 fh->breaktask = (struct Task*) dp->dp_Arg2;
568 replypkt2(dp, DOSTRUE, (SIPTR) old);
570 break;
571 case ACTION_WAIT_CHAR:
573 if (!MakeSureWinIsOpen(fh))
575 replypkt2(dp, DOSFALSE, ERROR_NO_FREE_STORE);
576 break;
578 if (fh->inputsize > 0)
580 replypkt(dp, DOSTRUE);
582 else
584 LONG timeout = dp->dp_Arg1;
585 LONG sec = timeout / 1000000;
586 LONG usec = timeout % 1000000;
588 fh->timerreq->tr_node.io_Command = TR_ADDREQUEST;
589 fh->timerreq->tr_time.tv_secs = sec;
590 fh->timerreq->tr_time.tv_micro = usec;
591 SendIO((struct IORequest *) fh->timerreq);
592 waitingdp = dp;
594 startread(fh);
596 break;
597 case ACTION_IS_FILESYSTEM:
598 replypkt(dp, DOSFALSE);
599 break;
600 case ACTION_DISK_INFO:
602 /* strange console handler features */
603 struct InfoData *id = BADDR(dp->dp_Arg1);
604 memset(id, 0, sizeof(struct InfoData));
605 id->id_DiskType =
606 (fh->flags & FHFLG_RAW) ? AROS_MAKE_ID('R', 'A', 'W', 0) : AROS_MAKE_ID('C', 'O', 'N', 0);
607 id->id_VolumeNode = (BPTR) fh->window;
608 id->id_InUse = (IPTR) fh->conreadio;
609 replypkt(dp, DOSTRUE);
611 break;
612 case ACTION_SEEK:
613 /* Yes, DOSTRUE. Check Guru Book for details. */
614 replypkt2(dp, DOSTRUE, ERROR_ACTION_NOT_KNOWN);
615 break;
616 default:
617 bug("[CON] unknown action %d\n", dp->dp_Type);
618 replypkt2(dp, DOSFALSE, ERROR_ACTION_NOT_KNOWN);
619 break;
623 end:
624 D(bug("[CON] 0x%p closing\n", fh));
625 if (fh)
627 D(bug("[CON] Cancelling read requests...\n"));
628 stopread(fh, waitingdp);
630 if (fh->flags & FHFLG_ASYNCCONSOLEREAD)
632 D(bug("[CON] Aborting console ioReq 0x%p\n", fh->conreadio));
634 AbortIO(ioReq(fh->conreadio));
635 WaitIO(ioReq(fh->conreadio));
638 D(bug("[CON] Closing handle...\n"));
639 close_con(fh);
642 if (dp)
644 D(bug("[CON] Replying packet 0x%p\n", dp));
645 replypkt(dp, DOSFALSE);
648 D(bug("[CON] 0x%p closed\n", fh));
649 return 0;