2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
6 #include <proto/exec.h>
7 #include <exec/libraries.h>
8 #include <exec/resident.h>
9 #include <exec/memory.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 <proto/workbench.h>
20 #include <workbench/startup.h>
21 #include <devices/conunit.h>
26 #include "con_handler_intern.h"
33 #include <aros/debug.h>
35 static char *BSTR2C(BSTR srcs
)
37 UBYTE
*src
= BADDR(srcs
);
40 dst
= AllocVec(src
[0] + 1, MEMF_ANY
);
43 memcpy(dst
, src
+ 1, src
[0]);
47 static WORD
isdosdevicec(CONST_STRPTR s
)
59 #define ioReq(x) ((struct IORequest *)x)
61 static const struct NewWindow default_nw
=
84 32767, /* MaxHeight */
85 WBENCHSCREEN
/* type */
89 static LONG
MakeConWindow(struct filehandle
*fh
)
93 if (fh
->otherwindow
== NULL
)
95 struct TagItem win_tags
[] =
98 { WA_AutoAdjust
, TRUE
},
99 { WA_PubScreenName
, 0 },
100 { WA_PubScreenFallBack
, TRUE
},
104 win_tags
[2].ti_Data
= (IPTR
) fh
->screenname
;
105 D(bug("[contask] Opening window on screen %s, IntuitionBase = 0x%p\n", fh
->screenname
, IntuitionBase
));
107 /* Autoadjust doesn't enforce the window's width and height to be larger than
108 minwidth and minheight, so we set it here to avoid crashes in devs/console
109 if a user does e.g. dir >con:0/0/0/0
111 fh
->nw
.Width
= fh
->nw
.Width
> fh
->nw
.MinWidth
? fh
->nw
.Width
: -1;
112 fh
->nw
.Height
= (fh
->flags
& FHFLG_BOOTCON
&& fh
->nw
.Height
== -1) ||
113 fh
->nw
.Height
> fh
->nw
.MinHeight
? fh
->nw
.Height
: fh
->nw
.MinHeight
;
115 fh
->window
= OpenWindowTagList(&fh
->nw
, (struct TagItem
*) win_tags
);
119 D(bug("[contask] Using window %p\n", fh
->otherwindow
));
120 fh
->window
= fh
->otherwindow
;
125 D(bug("contask: window opened\n"));
126 fh
->conreadio
->io_Data
= (APTR
) fh
->window
;
127 fh
->conreadio
->io_Length
= sizeof(struct Window
);
129 if (0 == OpenDevice("console.device", CONU_SNIPMAP
, ioReq(fh
->conreadio
), 0))
131 const UBYTE lf_on
[] =
132 { 0x9B, 0x32, 0x30, 0x68 }; /* Set linefeed mode */
134 D(bug("contask: device opened\n"));
136 fh
->flags
|= FHFLG_CONSOLEDEVICEOPEN
;
138 fh
->conwriteio
= *fh
->conreadio
;
139 fh
->conwriteio
.io_Message
.mn_ReplyPort
= fh
->conwritemp
;
141 /* Turn the console into LF+CR mode so that both
142 linefeed and carriage return is done on
144 fh
->conwriteio
.io_Command
= CMD_WRITE
;
145 fh
->conwriteio
.io_Data
= (APTR
) lf_on
;
146 fh
->conwriteio
.io_Length
= 4;
148 DoIO(ioReq(&fh
->conwriteio
));
150 if (fh
->workbenchbase
&& (fh
->appmsgport
= CreateMsgPort()))
152 if ((fh
->appwindow
= AddAppWindow(0, 0, fh
->window
, fh
->appmsgport
, NULL
)))
153 D(bug("[CON] Console promoted to be an AppWindow\n"));
156 } /* if (0 == OpenDevice("console.device", CONU_STANDARD, ioReq(fh->conreadio), 0)) */
159 err
= ERROR_INVALID_RESIDENT_LIBRARY
;
162 CloseWindow(fh
->window
);
164 } /* if (fh->window) */
167 D(bug("[contask] Failed to open a window\n"));
168 err
= ERROR_NO_FREE_STORE
;
174 static BOOL
MakeSureWinIsOpen(struct filehandle
*fh
)
178 return MakeConWindow(fh
) == 0;
181 static void close_con(struct filehandle
*fh
)
185 D(bug("[CON] Deleting timer request 0x%p\n", fh
->timerreq
));
188 CloseDevice((struct IORequest
*) fh
->timerreq
);
189 DeleteIORequest((struct IORequest
*) fh
->timerreq
);
192 D(bug("[CON] Deleting timer port 0x%p\n", fh
->timermp
));
193 DeleteMsgPort(fh
->timermp
);
195 if (fh
->flags
& FHFLG_CONSOLEDEVICEOPEN
)
197 D(bug("[CON] Closing console.device...\n"));
198 CloseDevice((struct IORequest
*) fh
->conreadio
);
203 D(bug("[CON] Unpromote console window from being an AppWindow\n"));
204 RemoveAppWindow(fh
->appwindow
);
208 struct AppMessage
*appmsg
;
209 while ((appmsg
= (struct AppMessage
*) GetMsg(fh
->appmsgport
)))
210 ReplyMsg ((struct Message
*) appmsg
);
211 D(bug("[CON] Delete MsgPort for AppWindow\n"));
212 DeleteMsgPort(fh
->appmsgport
);
215 D(bug("[CON] Closing window 0x%p\n", fh
->window
));
217 CloseWindow(fh
->window
);
219 D(bug("[CON] Delete console.device IORequest 0x%p\n", fh
->conreadio
));
220 DeleteIORequest(ioReq(fh
->conreadio
));
222 D(bug("[CON] Delete console.device MsgPort 0x%p\n", fh
->conreadmp
));
223 FreeVec(fh
->conreadmp
);
226 FreeVec(fh
->screenname
);
228 FreeVec(fh
->wintitle
);
230 FreeMem(fh
->pastebuffer
, PASTEBUFSIZE
);
232 CloseLibrary((struct Library
*) fh
->intuibase
);
233 CloseLibrary((struct Library
*) fh
->dosbase
);
234 CloseLibrary((struct Library
*) fh
->workbenchbase
);
236 /* These libraries are opened only if completion was used */
238 CloseLibrary((struct Library
*) fh
->gfxbase
);
240 CloseLibrary(fh
->gtbase
);
245 static struct filehandle
*open_con(struct DosPacket
*dp
, LONG
*perr
)
248 struct filehandle
*fh
;
249 struct DeviceNode
*dn
;
253 dn
= BADDR(dp
->dp_Arg3
);
254 *perr
= ERROR_NO_FREE_STORE
;
255 fh
= AllocVec(sizeof(struct filehandle
), MEMF_PUBLIC
| MEMF_CLEAR
);
259 fh
->intuibase
= (APTR
) OpenLibrary("intuition.library", 0);
260 fh
->dosbase
= (APTR
) OpenLibrary("dos.library", 0);
261 fh
->utilbase
= (APTR
) OpenLibrary("utility.library", 0);
262 fh
->workbenchbase
= (APTR
) OpenLibrary("workbench.library", 0);
264 fh
->inputbase
= (struct Device
*) FindName(&SysBase
->DeviceList
, "input.device");
267 if (!fh
->intuibase
|| !fh
->dosbase
|| !fh
->utilbase
|| !fh
->inputbase
)
269 CloseLibrary((APTR
) fh
->utilbase
);
270 CloseLibrary((APTR
) fh
->dosbase
);
271 CloseLibrary((APTR
) fh
->intuibase
);
272 CloseLibrary((APTR
) fh
->workbenchbase
);
277 fh
->timermp
= CreateMsgPort();
278 fh
->timerreq
= (struct timerequest
*) CreateIORequest(fh
->timermp
, sizeof(struct timerequest
));
279 OpenDevice(TIMERNAME
, UNIT_MICROHZ
, (struct IORequest
*) fh
->timerreq
, 0);
282 filename
= BSTR2C((BSTR
) dp
->dp_Arg1
);
284 i
= isdosdevicec(fn
);
288 fh
->contask
= FindTask(0);
290 NEWLIST(&fh
->pendingReads
);
292 /* Create msgport for console.device communication */
293 fh
->conreadmp
= AllocVec(sizeof(struct MsgPort
) * 2, MEMF_PUBLIC
| MEMF_CLEAR
);
297 fh
->conreadmp
->mp_Node
.ln_Type
= NT_MSGPORT
;
298 fh
->conreadmp
->mp_Flags
= PA_SIGNAL
;
299 fh
->conreadmp
->mp_SigBit
= AllocSignal(-1);
300 fh
->conreadmp
->mp_SigTask
= fh
->contask
;
301 NEWLIST(&fh
->conreadmp
->mp_MsgList
);
303 fh
->conwritemp
= fh
->conreadmp
+ 1;
305 fh
->conwritemp
->mp_Node
.ln_Type
= NT_MSGPORT
;
306 fh
->conwritemp
->mp_Flags
= PA_SIGNAL
;
307 fh
->conwritemp
->mp_SigBit
= AllocSignal(-1);
308 fh
->conwritemp
->mp_SigTask
= fh
->contask
;
309 NEWLIST(&fh
->conwritemp
->mp_MsgList
);
311 fh
->conreadio
= (struct IOStdReq
*) CreateIORequest(fh
->conreadmp
, sizeof(struct IOStdReq
));
314 D(bug("contask: conreadio created, parms '%s'\n", fn
));
318 if (parse_filename(fh
, fn
, &fh
->nw
))
320 if (!(fh
->flags
& FHFLG_AUTO
))
322 err
= MakeConWindow(fh
);
332 err
= ERROR_BAD_STREAM_NAME
;
336 DeleteIORequest(ioReq(fh
->conreadio
));
339 } /* if (fh->conreadio) */
342 err
= ERROR_NO_FREE_STORE
;
345 } /* if (fh->conreadmp) */
348 err
= ERROR_NO_FREE_STORE
;
352 fh
->flags
|= FHFLG_RAW
;
362 static void startread(struct filehandle
*fh
)
364 if (fh
->flags
& FHFLG_ASYNCCONSOLEREAD
)
366 fh
->conreadio
->io_Command
= CMD_READ
;
367 fh
->conreadio
->io_Data
= fh
->consolebuffer
;
368 fh
->conreadio
->io_Length
= CONSOLEBUFFER_SIZE
;
369 SendIO((struct IORequest
*) fh
->conreadio
);
370 fh
->flags
|= FHFLG_ASYNCCONSOLEREAD
;
373 static void stopwait(struct filehandle
*fh
, struct DosPacket
*waitingdp
, ULONG result
)
377 AbortIO((struct IORequest
*) fh
->timerreq
);
378 WaitIO((struct IORequest
*) fh
->timerreq
);
379 replypkt(waitingdp
, result
);
383 static void stopread(struct filehandle
*fh
, struct DosPacket
*waitingdp
)
385 struct Message
*msg
, *next_msg
;
387 stopwait(fh
, waitingdp
, DOSFALSE
);
389 ForeachNodeSafe(&fh
->pendingReads
, msg
, next_msg
)
391 struct DosPacket
*dpr
;
393 Remove((struct Node
*) msg
);
394 dpr
= (struct DosPacket
*) msg
->mn_Node
.ln_Name
;
395 replypkt(dpr
, DOSFALSE
);
399 LONG
CONMain(struct ExecBase
*SysBase
)
402 struct DosPacket
*dp
;
404 struct FileHandle
*dosfh
;
406 struct filehandle
*fh
;
408 struct DosPacket
*waitingdp
= NULL
;
410 D(bug("[CON] started\n"));
411 mp
= &((struct Process
*) FindTask(NULL
))->pr_MsgPort
;
413 dp
= (struct DosPacket
*) GetMsg(mp
)->mn_Node
.ln_Name
;
414 D(bug("[CON] startup message received. port=0x%p path='%b'\n", mp
, dp
->dp_Arg1
));
416 fh
= open_con(dp
, &error
);
419 D(bug("[CON] init failed\n"));
422 D(bug("[CON] 0x%p open\n", fh
));
423 replypkt(dp
, DOSTRUE
);
427 ULONG conreadmask
= 1L << fh
->conreadmp
->mp_SigBit
;
428 ULONG timermask
= 1L << fh
->timermp
->mp_SigBit
;
429 ULONG packetmask
= 1L << mp
->mp_SigBit
;
430 ULONG appwindowmask
= fh
->appmsgport
? 1L << fh
->appmsgport
->mp_SigBit
: 0L;
431 ULONG i
= 0, insertedlen
= 0;
433 UBYTE iconpath
[INPUTBUFFER_SIZE
];
434 WORD currentpos
, currentrest
;
436 sigs
= Wait(packetmask
| conreadmask
| timermask
| appwindowmask
);
438 if (sigs
& appwindowmask
)
440 while ((fh
->appmsg
= (struct AppMessage
*)GetMsg(fh
->appmsgport
)))
442 if (fh
->appmsg
->am_Type
== AMTYPE_APPWINDOW
)
444 if (fh
->appmsg
->am_NumArgs
>= 1)
448 if (fh
->appmsg
->am_ArgList
[i
].wa_Lock
)
450 NameFromLock(fh
->appmsg
->am_ArgList
[i
].wa_Lock
,
451 iconpath
, INPUTBUFFER_SIZE
- 1);
452 AddPart(iconpath
, fh
->appmsg
->am_ArgList
[i
].wa_Name
,
453 INPUTBUFFER_SIZE
- 1);
454 D(bug("[CON]: D&D iconpath: %s\n", iconpath
));
456 if ((insertedlen
= strlen(iconpath
)))
459 * Get rid of trailing slashes of drawers?
460 if ((iconpath[insertedlen - 1] == '/'))
463 if ( strchr(iconpath
, ' ')
464 && insertedlen
<= (INPUTBUFFER_SIZE
- 2))
466 memmove(iconpath
+ 1, iconpath
, ++insertedlen
);
467 iconpath
[0] = iconpath
[insertedlen
++] = '"';
469 if (insertedlen
<= (INPUTBUFFER_SIZE
- 1))
470 iconpath
[insertedlen
++] = ' ';
472 currentpos
= fh
->inputpos
;
473 currentrest
= fh
->inputsize
- fh
->inputpos
;
474 memmove(&fh
->inputbuffer
[currentpos
+ insertedlen
],
475 &fh
->inputbuffer
[currentpos
],
477 CopyMem(iconpath
, &fh
->inputbuffer
[currentpos
],
479 fh
->inputsize
+= insertedlen
;
480 fh
->inputpos
+= insertedlen
;
482 do_write(fh
, &fh
->inputbuffer
[currentpos
],
483 insertedlen
+ currentrest
);
484 do_movecursor(fh
, CUR_LEFT
, currentrest
);
487 } while (++i
< fh
->appmsg
->am_NumArgs
);
490 ReplyMsg((struct Message
*)fh
->appmsg
);
492 ActivateWindow(fh
->window
);
495 if (sigs
& timermask
)
499 replypkt(waitingdp
, DOSFALSE
);
504 if (sigs
& conreadmask
)
506 GetMsg(fh
->conreadmp
);
507 fh
->flags
&= ~FHFLG_ASYNCCONSOLEREAD
;
510 stopwait(fh
, waitingdp
, DOSTRUE
);
513 D(bug("IO_READ %d\n", fh
->conreadio
->io_Actual
));
514 fh
->conbuffersize
= fh
->conreadio
->io_Actual
;
515 fh
->conbufferpos
= 0;
516 /* terminate with 0 char */
517 fh
->consolebuffer
[fh
->conbuffersize
] = '\0';
518 if (fh
->flags
& FHFLG_RAW
)
522 for (inp
= 0; (inp
< fh
->conbuffersize
) && (fh
->inputpos
< INPUTBUFFER_SIZE
);)
524 fh
->inputbuffer
[fh
->inputpos
++] = fh
->consolebuffer
[inp
++];
526 fh
->inputsize
= fh
->inputstart
= fh
->inputpos
;
527 HandlePendingReads(fh
);
528 } /* if (fh->flags & FHFLG_RAW) */
532 if (process_input(fh
))
535 * process_input() returns TRUE when EOF was received after the WAIT console
536 * has been closed by the owner.
541 } /* if (fh->flags & FHFLG_RAW) else ... */
543 if (fh
->flags
& FHFLG_CONSOLEDEVICEOPEN
) /* device could have been closed */
547 while ((mn
= GetMsg(mp
)))
549 dp
= (struct DosPacket
*) mn
->mn_Node
.ln_Name
;
552 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
,
557 case ACTION_FH_FROM_LOCK
:
558 fl
= BADDR(dp
->dp_Arg2
);
559 if (fl
->fl_Task
!= mp
|| fl
->fl_Key
!= (IPTR
) fh
)
561 replypkt2(dp
, DOSFALSE
, ERROR_OBJECT_NOT_FOUND
);
565 FreeMem(fl
, sizeof(*fl
));
567 case ACTION_FINDINPUT
:
568 case ACTION_FINDOUTPUT
:
569 case ACTION_FINDUPDATE
:
570 dosfh
= BADDR(dp
->dp_Arg1
);
571 dosfh
->fh_Interactive
= DOSTRUE
;
572 dosfh
->fh_Arg1
= (SIPTR
) fh
;
574 fh
->breaktask
= dp
->dp_Port
->mp_SigTask
;
575 D(bug("[CON] Find fh=%x. Usecount=%d\n", dosfh
, fh
->usecount
));
576 replypkt(dp
, DOSTRUE
);
578 case ACTION_COPY_DIR_FH
:
579 fl
= AllocMem(sizeof(*fl
), MEMF_CLEAR
| MEMF_PUBLIC
);
582 replypkt2(dp
, (SIPTR
) BNULL
, ERROR_NO_FREE_STORE
);
588 fl
->fl_Access
= ACCESS_READ
;
589 fl
->fl_Key
= (IPTR
) fh
;
590 replypkt(dp
, (SIPTR
) MKBADDR(fl
));
593 case ACTION_FREE_LOCK
:
594 fl
= BADDR(dp
->dp_Arg1
);
595 fh
= (struct filehandle
*)fl
->fl_Key
;
597 FreeMem(fl
, sizeof(*fl
));
600 replypkt(dp
, DOSTRUE
);
604 D(bug("[CON] usecount=%d\n", fh
->usecount
));
605 if (fh
->usecount
<= 0)
607 if (fh
->flags
& FHFLG_WAIT
)
609 D(bug("[CON] Delayed close, waiting...\n"));
612 * Bounce all pending read and waits (the same as we do when exiting).
613 * However the process is still around, waiting for EOF input.
614 * Our user has just closed his struct FileHandle and dropped us.
616 stopread(fh
, waitingdp
);
618 fh
->flags
= (fh
->flags
& ~FHFLG_READPENDING
) | FHFLG_WAITFORCLOSE
;
623 replypkt(dp
, DOSTRUE
);
626 if (!MakeSureWinIsOpen(fh
))
628 replypkt2(dp
, DOSFALSE
, ERROR_NO_FREE_STORE
);
631 fh
->breaktask
= dp
->dp_Port
->mp_SigTask
;
636 if (!MakeSureWinIsOpen(fh
))
638 replypkt2(dp
, DOSFALSE
, ERROR_NO_FREE_STORE
);
641 fh
->breaktask
= dp
->dp_Port
->mp_SigTask
;
643 answer_write_request(fh
, dp
);
645 case ACTION_SCREEN_MODE
:
647 D(bug("ACTION_SCREEN_MODE %s\n", dp
->dp_Arg1
? "RAW" : "CON"));
648 if (dp
->dp_Arg1
&& !(fh
->flags
& FHFLG_RAW
))
650 /* Switching from CON: mode to RAW: mode */
651 fh
->flags
|= FHFLG_RAW
;
652 fh
->inputstart
= fh
->inputsize
;
653 fh
->inputpos
= fh
->inputsize
;
654 HandlePendingReads(fh
);
658 /* otherwise just copy the flags */
660 fh
->flags
|= FHFLG_RAW
;
662 fh
->flags
&= ~FHFLG_RAW
;
664 replypkt(dp
, DOSTRUE
);
667 case ACTION_CHANGE_SIGNAL
:
669 struct Task
*old
= fh
->breaktask
;
671 fh
->breaktask
= (struct Task
*) dp
->dp_Arg2
;
672 replypkt2(dp
, DOSTRUE
, (SIPTR
) old
);
675 case ACTION_WAIT_CHAR
:
677 if (!MakeSureWinIsOpen(fh
))
679 replypkt2(dp
, DOSFALSE
, ERROR_NO_FREE_STORE
);
682 if (fh
->inputsize
> 0)
684 replypkt(dp
, DOSTRUE
);
686 else if (dp
->dp_Arg1
== 0)
688 replypkt(dp
, DOSFALSE
);
692 LONG timeout
= dp
->dp_Arg1
;
693 LONG sec
= timeout
/ 1000000;
694 LONG usec
= timeout
% 1000000;
696 fh
->timerreq
->tr_node
.io_Command
= TR_ADDREQUEST
;
697 fh
->timerreq
->tr_time
.tv_secs
= sec
;
698 fh
->timerreq
->tr_time
.tv_micro
= usec
;
699 SendIO((struct IORequest
*) fh
->timerreq
);
705 case ACTION_IS_FILESYSTEM
:
706 replypkt(dp
, DOSFALSE
);
708 case ACTION_DISK_INFO
:
710 /* strange console handler features */
711 struct InfoData
*id
= BADDR(dp
->dp_Arg1
);
712 memset(id
, 0, sizeof(struct InfoData
));
714 (fh
->flags
& FHFLG_RAW
) ? AROS_MAKE_ID('R', 'A', 'W', 0) : AROS_MAKE_ID('C', 'O', 'N', 0);
715 id
->id_VolumeNode
= (BPTR
) fh
->window
;
716 id
->id_InUse
= (IPTR
) fh
->conreadio
;
717 replypkt(dp
, DOSTRUE
);
721 /* Yes, DOSTRUE. Check Guru Book for details. */
722 replypkt2(dp
, DOSTRUE
, ERROR_ACTION_NOT_KNOWN
);
725 bug("[CON] unknown action %d\n", dp
->dp_Type
);
726 replypkt2(dp
, DOSFALSE
, ERROR_ACTION_NOT_KNOWN
);
732 D(bug("[CON] 0x%p closing\n", fh
));
735 D(bug("[CON] Cancelling read requests...\n"));
736 stopread(fh
, waitingdp
);
738 if (fh
->flags
& FHFLG_ASYNCCONSOLEREAD
)
740 D(bug("[CON] Aborting console ioReq 0x%p\n", fh
->conreadio
));
742 AbortIO(ioReq(fh
->conreadio
));
743 WaitIO(ioReq(fh
->conreadio
));
746 D(bug("[CON] Closing handle...\n"));
752 D(bug("[CON] Replying packet 0x%p\n", dp
));
753 replypkt(dp
, DOSFALSE
);
756 D(bug("[CON] 0x%p closed\n", fh
));