W.I.P AROS port of SDI examples
[AROS.git] / rom / filesys / console_handler / con_handler.c
blob365b7bdf2fcd4305337efda0cb7164a30ae9a1d2
1 /*
2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <proto/exec.h>
7 #include <exec/libraries.h>
8 #include <exec/resident.h>
9 #include <exec/memory.h>
10 #include <exec/io.h>
11 #include <exec/errors.h>
12 #include <exec/alerts.h>
13 #include <utility/tagitem.h>
14 #include <dos/exall.h>
15 #include <dos/dosasl.h>
16 #include <intuition/intuition.h>
17 #include <proto/dos.h>
18 #include <proto/intuition.h>
19 #include <devices/conunit.h>
21 #include <stddef.h>
22 #include <string.h>
24 #include "con_handler_intern.h"
25 #include "support.h"
27 #undef SDEBUG
28 #undef DEBUG
29 #define SDEBUG 0
30 #define DEBUG 0
31 #include <aros/debug.h>
33 static char *BSTR2C(BSTR srcs)
35 UBYTE *src = BADDR(srcs);
36 char *dst;
38 dst = AllocVec(src[0] + 1, MEMF_ANY);
39 if (!dst)
40 return NULL;
41 memcpy(dst, src + 1, src[0]);
42 dst[src[0]] = 0;
43 return dst;
45 static WORD isdosdevicec(CONST_STRPTR s)
47 UBYTE b = 0;
48 while (s[b])
50 if (s[b] == ':')
51 return b;
52 b++;
54 return -1;
57 #define ioReq(x) ((struct IORequest *)x)
59 static const struct NewWindow default_nw =
61 0, /* LeftEdge */
62 0, /* TopEdge */
63 -1, /* Width */
64 -1, /* Height */
65 1, /* DetailPen */
66 0, /* BlockPen */
67 0, /* IDCMP */
68 WFLG_DEPTHGADGET |
69 WFLG_SIZEGADGET |
70 WFLG_DRAGBAR |
71 WFLG_SIZEBRIGHT |
72 WFLG_SMART_REFRESH |
73 WFLG_ACTIVATE,
74 0, /* FirstGadget */
75 0, /* CheckMark */
76 "CON:", /* Title */
77 0, /* Screen */
78 0, /* Bitmap */
79 100, /* MinWidth */
80 70, /* MinHeight */
81 32767, /* MaxWidth */
82 32767, /* MaxHeight */
83 WBENCHSCREEN /* type */
87 static LONG MakeConWindow(struct filehandle *fh)
89 LONG err = 0;
91 if (fh->otherwindow == NULL)
93 struct TagItem win_tags[] =
95 { WA_PubScreen, 0 },
96 { WA_AutoAdjust, TRUE },
97 { WA_PubScreenName, 0 },
98 { WA_PubScreenFallBack, TRUE },
99 { TAG_DONE }
102 win_tags[2].ti_Data = (IPTR) fh->screenname;
103 D(bug("[contask] Opening window on screen %s, IntuitionBase = 0x%p\n", fh->screenname, IntuitionBase));
105 /* Autoadjust doesn't enforce the window's width and height to be larger than
106 minwidth and minheight, so we set it here to avoid crashes in devs/console
107 if a user does e.g. dir >con:0/0/0/0
109 fh->nw.Width = fh->nw.Width > fh->nw.MinWidth ? fh->nw.Width : -1;
110 fh->nw.Height = (fh->flags & FHFLG_BOOTCON && fh->nw.Height == -1) ||
111 fh->nw.Height > fh->nw.MinHeight ? fh->nw.Height : fh->nw.MinHeight;
113 fh->window = OpenWindowTagList(&fh->nw, (struct TagItem *) win_tags);
115 else
117 D(bug("[contask] Using window %p\n", fh->otherwindow));
118 fh->window = fh->otherwindow;
121 if (fh->window)
123 D(bug("contask: window opened\n"));
124 fh->conreadio->io_Data = (APTR) fh->window;
125 fh->conreadio->io_Length = sizeof(struct Window);
127 if (0 == OpenDevice("console.device", CONU_SNIPMAP, ioReq(fh->conreadio), 0))
129 const UBYTE lf_on[] =
130 { 0x9B, 0x32, 0x30, 0x68 }; /* Set linefeed mode */
132 D(bug("contask: device opened\n"));
134 fh->flags |= FHFLG_CONSOLEDEVICEOPEN;
136 fh->conwriteio = *fh->conreadio;
137 fh->conwriteio.io_Message.mn_ReplyPort = fh->conwritemp;
139 /* Turn the console into LF+CR mode so that both
140 linefeed and carriage return is done on
142 fh->conwriteio.io_Command = CMD_WRITE;
143 fh->conwriteio.io_Data = (APTR) lf_on;
144 fh->conwriteio.io_Length = 4;
146 DoIO(ioReq(&fh->conwriteio));
148 } /* if (0 == OpenDevice("console.device", CONU_STANDARD, ioReq(fh->conreadio), 0)) */
149 else
151 err = ERROR_INVALID_RESIDENT_LIBRARY;
153 if (err)
154 CloseWindow(fh->window);
156 } /* if (fh->window) */
157 else
159 D(bug("[contask] Failed to open a window\n"));
160 err = ERROR_NO_FREE_STORE;
163 return err;
166 static BOOL MakeSureWinIsOpen(struct filehandle *fh)
168 if (fh->window)
169 return TRUE;
170 return MakeConWindow(fh) == 0;
173 static void close_con(struct filehandle *fh)
175 /* Clean up */
177 D(bug("[CON] Deleting timer request 0x%p\n", fh->timerreq));
178 if (fh->timerreq)
180 CloseDevice((struct IORequest *) fh->timerreq);
181 DeleteIORequest((struct IORequest *) fh->timerreq);
184 D(bug("[CON] Deleting timer port 0x%p\n", fh->timermp));
185 DeleteMsgPort(fh->timermp);
187 if (fh->flags & FHFLG_CONSOLEDEVICEOPEN)
189 D(bug("[CON] Closing console.device...\n"));
190 CloseDevice((struct IORequest *) fh->conreadio);
193 D(bug("[CON] Closing window 0x%p\n", fh->window));
194 if (fh->window)
195 CloseWindow(fh->window);
197 D(bug("[CON] Delete console.device IORequest 0x%p\n", fh->conreadio));
198 DeleteIORequest(ioReq(fh->conreadio));
200 D(bug("[CON] Delete console.device MsgPort 0x%p\n", fh->conreadmp));
201 FreeVec(fh->conreadmp);
203 if (fh->screenname)
204 FreeVec(fh->screenname);
205 if (fh->wintitle)
206 FreeVec(fh->wintitle);
207 if (fh->pastebuffer)
208 FreeMem(fh->pastebuffer, PASTEBUFSIZE);
210 CloseLibrary((struct Library*) fh->intuibase);
211 CloseLibrary((struct Library*) fh->dosbase);
213 /* These libraries are opened only if completion was used */
214 if (fh->gfxbase)
215 CloseLibrary((struct Library*) fh->gfxbase);
216 if (fh->gtbase)
217 CloseLibrary(fh->gtbase);
219 FreeVec(fh);
222 static struct filehandle *open_con(struct DosPacket *dp, LONG *perr)
224 char *filename, *fn;
225 struct filehandle *fh;
226 struct DeviceNode *dn;
227 LONG err, ok;
228 LONG i;
230 dn = BADDR(dp->dp_Arg3);
231 *perr = ERROR_NO_FREE_STORE;
232 fh = AllocVec(sizeof(struct filehandle), MEMF_PUBLIC | MEMF_CLEAR);
233 if (!fh)
234 return NULL;
236 fh->intuibase = (APTR) OpenLibrary("intuition.library", 0);
237 fh->dosbase = (APTR) OpenLibrary("dos.library", 0);
238 fh->utilbase = (APTR) OpenLibrary("utility.library", 0);
239 Forbid();
240 fh->inputbase = (struct Device *) FindName(&SysBase->DeviceList, "input.device");
241 Permit();
243 if (!fh->intuibase || !fh->dosbase || !fh->utilbase || !fh->inputbase)
245 CloseLibrary((APTR) fh->utilbase);
246 CloseLibrary((APTR) fh->dosbase);
247 CloseLibrary((APTR) fh->intuibase);
248 FreeVec(fh);
249 return NULL;
252 fh->timermp = CreateMsgPort();
253 fh->timerreq = (struct timerequest*) CreateIORequest(fh->timermp, sizeof(struct timerequest));
254 OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *) fh->timerreq, 0);
256 err = 0;
257 filename = BSTR2C((BSTR) dp->dp_Arg1);
258 fn = filename;
259 i = isdosdevicec(fn);
260 if (i >= 0)
261 fn += i + 1;
263 fh->contask = FindTask(0);
265 NEWLIST(&fh->pendingReads);
267 /* Create msgport for console.device communication */
268 fh->conreadmp = AllocVec(sizeof(struct MsgPort) * 2, MEMF_PUBLIC | MEMF_CLEAR);
269 if (fh->conreadmp)
272 fh->conreadmp->mp_Node.ln_Type = NT_MSGPORT;
273 fh->conreadmp->mp_Flags = PA_SIGNAL;
274 fh->conreadmp->mp_SigBit = AllocSignal(-1);
275 fh->conreadmp->mp_SigTask = fh->contask;
276 NEWLIST(&fh->conreadmp->mp_MsgList);
278 fh->conwritemp = fh->conreadmp + 1;
280 fh->conwritemp->mp_Node.ln_Type = NT_MSGPORT;
281 fh->conwritemp->mp_Flags = PA_SIGNAL;
282 fh->conwritemp->mp_SigBit = AllocSignal(-1);
283 fh->conwritemp->mp_SigTask = fh->contask;
284 NEWLIST(&fh->conwritemp->mp_MsgList);
286 fh->conreadio = (struct IOStdReq *) CreateIORequest(fh->conreadmp, sizeof(struct IOStdReq));
287 if (fh->conreadio)
289 D(bug("contask: conreadio created, parms '%s'\n", fn));
291 fh->nw = default_nw;
293 if (parse_filename(fh, fn, &fh->nw))
295 if (!(fh->flags & FHFLG_AUTO))
297 err = MakeConWindow(fh);
298 if (!err)
299 ok = TRUE;
301 else
303 ok = TRUE;
306 else
307 err = ERROR_BAD_STREAM_NAME;
309 if (!ok)
311 DeleteIORequest(ioReq(fh->conreadio));
314 } /* if (fh->conreadio) */
315 else
317 err = ERROR_NO_FREE_STORE;
320 } /* if (fh->conreadmp) */
321 else
323 err = ERROR_NO_FREE_STORE;
326 if (dn->dn_Startup)
327 fh->flags |= FHFLG_RAW;
329 if (!ok)
330 close_con(fh);
332 *perr = err;
333 FreeVec(filename);
334 return fh;
337 static void startread(struct filehandle *fh)
339 if (fh->flags & FHFLG_ASYNCCONSOLEREAD)
340 return;
341 fh->conreadio->io_Command = CMD_READ;
342 fh->conreadio->io_Data = fh->consolebuffer;
343 fh->conreadio->io_Length = CONSOLEBUFFER_SIZE;
344 SendIO((struct IORequest*) fh->conreadio);
345 fh->flags |= FHFLG_ASYNCCONSOLEREAD;
348 static void stopwait(struct filehandle *fh, struct DosPacket *waitingdp, ULONG result)
350 if (waitingdp)
352 AbortIO((struct IORequest *) fh->timerreq);
353 WaitIO((struct IORequest *) fh->timerreq);
354 replypkt(waitingdp, result);
358 static void stopread(struct filehandle *fh, struct DosPacket *waitingdp)
360 struct Message *msg, *next_msg;
362 stopwait(fh, waitingdp, DOSFALSE);
364 ForeachNodeSafe(&fh->pendingReads, msg, next_msg)
366 struct DosPacket *dpr;
368 Remove((struct Node *) msg);
369 dpr = (struct DosPacket*) msg->mn_Node.ln_Name;
370 replypkt(dpr, DOSFALSE);
374 LONG CONMain(struct ExecBase *SysBase)
376 struct MsgPort *mp;
377 struct DosPacket *dp;
378 struct Message *mn;
379 struct FileHandle *dosfh;
380 LONG error;
381 struct filehandle *fh;
382 struct FileLock *fl;
383 struct DosPacket *waitingdp = NULL;
385 D(bug("[CON] started\n"));
386 mp = &((struct Process*) FindTask(NULL))->pr_MsgPort;
387 WaitPort(mp);
388 dp = (struct DosPacket*) GetMsg(mp)->mn_Node.ln_Name;
389 D(bug("[CON] startup message received. port=0x%p path='%b'\n", mp, dp->dp_Arg1));
391 fh = open_con(dp, &error);
392 if (!fh)
394 D(bug("[CON] init failed\n"));
395 goto end;
397 D(bug("[CON] 0x%p open\n", fh));
398 replypkt(dp, DOSTRUE);
400 for (;;)
402 ULONG conreadmask = 1L << fh->conreadmp->mp_SigBit;
403 ULONG timermask = 1L << fh->timermp->mp_SigBit;
404 ULONG packetmask = 1L << mp->mp_SigBit;
405 ULONG sigs;
407 sigs = Wait(packetmask | conreadmask | timermask);
409 if (sigs & timermask)
411 if (waitingdp)
413 replypkt(waitingdp, DOSFALSE);
414 waitingdp = NULL;
418 if (sigs & conreadmask)
420 GetMsg(fh->conreadmp);
421 fh->flags &= ~FHFLG_ASYNCCONSOLEREAD;
422 if (waitingdp)
424 stopwait(fh, waitingdp, DOSTRUE);
425 waitingdp = NULL;
427 D(bug("IO_READ %d\n", fh->conreadio->io_Actual));
428 fh->conbuffersize = fh->conreadio->io_Actual;
429 fh->conbufferpos = 0;
430 /* terminate with 0 char */
431 fh->consolebuffer[fh->conbuffersize] = '\0';
432 if (fh->flags & FHFLG_RAW)
434 LONG inp;
435 /* raw mode */
436 for (inp = 0; (inp < fh->conbuffersize) && (fh->inputpos < INPUTBUFFER_SIZE);)
438 fh->inputbuffer[fh->inputpos++] = fh->consolebuffer[inp++];
440 fh->inputsize = fh->inputstart = fh->inputpos;
441 HandlePendingReads(fh);
442 } /* if (fh->flags & FHFLG_RAW) */
443 else
445 /* Cooked mode */
446 if (process_input(fh))
449 * process_input() returns TRUE when EOF was received after the WAIT console
450 * has been closed by the owner.
452 dp = NULL;
453 goto end;
455 } /* if (fh->flags & FHFLG_RAW) else ... */
457 if (fh->flags & FHFLG_CONSOLEDEVICEOPEN) /* device could have been closed */
458 startread(fh);
461 while ((mn = GetMsg(mp)))
463 dp = (struct DosPacket*) mn->mn_Node.ln_Name;
464 dp->dp_Res2 = 0;
466 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,
467 dp->dp_Arg3));
468 error = 0;
469 switch (dp->dp_Type)
471 case ACTION_FH_FROM_LOCK:
472 fl = BADDR(dp->dp_Arg2);
473 if (fl->fl_Task != mp || fl->fl_Key != (IPTR) fh)
475 replypkt2(dp, DOSFALSE, ERROR_OBJECT_NOT_FOUND);
476 break;
478 fh->usecount--;
479 FreeMem(fl, sizeof(*fl));
480 /* Fallthrough */
481 case ACTION_FINDINPUT:
482 case ACTION_FINDOUTPUT:
483 case ACTION_FINDUPDATE:
484 dosfh = BADDR(dp->dp_Arg1);
485 dosfh->fh_Interactive = DOSTRUE;
486 dosfh->fh_Arg1 = (SIPTR) fh;
487 fh->usecount++;
488 fh->breaktask = dp->dp_Port->mp_SigTask;
489 D(bug("[CON] Find fh=%x. Usecount=%d\n", dosfh, fh->usecount));
490 replypkt(dp, DOSTRUE);
491 break;
492 case ACTION_COPY_DIR_FH:
493 fl = AllocMem(sizeof(*fl), MEMF_CLEAR | MEMF_PUBLIC);
494 if (fl == BNULL)
496 replypkt2(dp, (SIPTR) BNULL, ERROR_NO_FREE_STORE);
498 else
500 fh->usecount++;
501 fl->fl_Task = mp;
502 fl->fl_Access = ACCESS_READ;
503 fl->fl_Key = (IPTR) fh;
504 replypkt(dp, (SIPTR) MKBADDR(fl));
506 break;
507 case ACTION_FREE_LOCK:
508 fl = BADDR(dp->dp_Arg1);
509 fh = (struct filehandle *)fl->fl_Key;
511 FreeMem(fl, sizeof(*fl));
512 fh->usecount--;
514 replypkt(dp, DOSTRUE);
515 break;
516 case ACTION_END:
517 fh->usecount--;
518 D(bug("[CON] usecount=%d\n", fh->usecount));
519 if (fh->usecount <= 0)
521 if (fh->flags & FHFLG_WAIT)
523 D(bug("[CON] Delayed close, waiting...\n"));
526 * Bounce all pending read and waits (the same as we do when exiting).
527 * However the process is still around, waiting for EOF input.
528 * Our user has just closed his struct FileHandle and dropped us.
530 stopread(fh, waitingdp);
531 waitingdp = NULL;
532 fh->flags = (fh->flags & ~FHFLG_READPENDING) | FHFLG_WAITFORCLOSE;
534 else
535 goto end;
537 replypkt(dp, DOSTRUE);
538 break;
539 case ACTION_READ:
540 if (!MakeSureWinIsOpen(fh))
542 replypkt2(dp, DOSFALSE, ERROR_NO_FREE_STORE);
543 break;
545 fh->breaktask = dp->dp_Port->mp_SigTask;
546 startread(fh);
547 con_read(fh, dp);
548 break;
549 case ACTION_WRITE:
550 if (!MakeSureWinIsOpen(fh))
552 replypkt2(dp, DOSFALSE, ERROR_NO_FREE_STORE);
553 break;
555 fh->breaktask = dp->dp_Port->mp_SigTask;
556 startread(fh);
557 answer_write_request(fh, dp);
558 break;
559 case ACTION_SCREEN_MODE:
561 D(bug("ACTION_SCREEN_MODE %s\n", dp->dp_Arg1 ? "RAW" : "CON"));
562 if (dp->dp_Arg1 && !(fh->flags & FHFLG_RAW))
564 /* Switching from CON: mode to RAW: mode */
565 fh->flags |= FHFLG_RAW;
566 fh->inputstart = fh->inputsize;
567 fh->inputpos = fh->inputsize;
568 HandlePendingReads(fh);
570 else
572 /* otherwise just copy the flags */
573 if (dp->dp_Arg1)
574 fh->flags |= FHFLG_RAW;
575 else
576 fh->flags &= ~FHFLG_RAW;
578 replypkt(dp, DOSTRUE);
580 break;
581 case ACTION_CHANGE_SIGNAL:
583 struct Task *old = fh->breaktask;
584 if (dp->dp_Arg2)
585 fh->breaktask = (struct Task*) dp->dp_Arg2;
586 replypkt2(dp, DOSTRUE, (SIPTR) old);
588 break;
589 case ACTION_WAIT_CHAR:
591 if (!MakeSureWinIsOpen(fh))
593 replypkt2(dp, DOSFALSE, ERROR_NO_FREE_STORE);
594 break;
596 if (fh->inputsize > 0)
598 replypkt(dp, DOSTRUE);
600 else
602 LONG timeout = dp->dp_Arg1;
603 if (timeout != 0)
605 LONG sec = timeout / 1000000;
606 LONG usec = timeout % 1000000;
608 fh->timerreq->tr_node.io_Command = TR_ADDREQUEST;
609 fh->timerreq->tr_time.tv_secs = sec;
610 fh->timerreq->tr_time.tv_micro = usec;
611 SendIO((struct IORequest *) fh->timerreq);
613 waitingdp = dp;
615 startread(fh);
617 break;
618 case ACTION_IS_FILESYSTEM:
619 replypkt(dp, DOSFALSE);
620 break;
621 case ACTION_DISK_INFO:
623 /* strange console handler features */
624 struct InfoData *id = BADDR(dp->dp_Arg1);
625 memset(id, 0, sizeof(struct InfoData));
626 id->id_DiskType =
627 (fh->flags & FHFLG_RAW) ? AROS_MAKE_ID('R', 'A', 'W', 0) : AROS_MAKE_ID('C', 'O', 'N', 0);
628 id->id_VolumeNode = (BPTR) fh->window;
629 id->id_InUse = (IPTR) fh->conreadio;
630 replypkt(dp, DOSTRUE);
632 break;
633 case ACTION_SEEK:
634 /* Yes, DOSTRUE. Check Guru Book for details. */
635 replypkt2(dp, DOSTRUE, ERROR_ACTION_NOT_KNOWN);
636 break;
637 default:
638 bug("[CON] unknown action %d\n", dp->dp_Type);
639 replypkt2(dp, DOSFALSE, ERROR_ACTION_NOT_KNOWN);
640 break;
644 end:
645 D(bug("[CON] 0x%p closing\n", fh));
646 if (fh)
648 D(bug("[CON] Cancelling read requests...\n"));
649 stopread(fh, waitingdp);
651 if (fh->flags & FHFLG_ASYNCCONSOLEREAD)
653 D(bug("[CON] Aborting console ioReq 0x%p\n", fh->conreadio));
655 AbortIO(ioReq(fh->conreadio));
656 WaitIO(ioReq(fh->conreadio));
659 D(bug("[CON] Closing handle...\n"));
660 close_con(fh);
663 if (dp)
665 D(bug("[CON] Replying packet 0x%p\n", dp));
666 replypkt(dp, DOSFALSE);
669 D(bug("[CON] 0x%p closed\n", fh));
670 return 0;