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 #if defined(__AROSPLATFORM_SMP__)
36 #include <aros/types/spinlock_s.h>
37 #include <proto/execlock.h>
38 #include <resources/execlock.h>
41 static char *BSTR2C(BSTR srcs
)
43 UBYTE
*src
= BADDR(srcs
);
46 dst
= AllocVec(src
[0] + 1, MEMF_ANY
);
49 memcpy(dst
, src
+ 1, src
[0]);
53 static WORD
isdosdevicec(CONST_STRPTR s
)
65 #define ioReq(x) ((struct IORequest *)x)
67 static const struct NewWindow default_nw
=
90 32767, /* MaxHeight */
91 WBENCHSCREEN
/* type */
95 static LONG
MakeConWindow(struct filehandle
*fh
)
99 if (fh
->otherwindow
== NULL
)
101 struct TagItem win_tags
[] =
104 { WA_AutoAdjust
, TRUE
},
105 { WA_PubScreenName
, 0 },
106 { WA_PubScreenFallBack
, TRUE
},
110 win_tags
[2].ti_Data
= (IPTR
) fh
->screenname
;
111 D(bug("[contask] Opening window on screen %s, IntuitionBase = 0x%p\n", fh
->screenname
, IntuitionBase
));
113 /* Autoadjust doesn't enforce the window's width and height to be larger than
114 minwidth and minheight, so we set it here to avoid crashes in devs/console
115 if a user does e.g. dir >con:0/0/0/0
117 fh
->nw
.Width
= fh
->nw
.Width
> fh
->nw
.MinWidth
? fh
->nw
.Width
: -1;
118 fh
->nw
.Height
= (fh
->flags
& FHFLG_BOOTCON
&& fh
->nw
.Height
== -1) ||
119 fh
->nw
.Height
> fh
->nw
.MinHeight
? fh
->nw
.Height
: fh
->nw
.MinHeight
;
121 fh
->window
= OpenWindowTagList(&fh
->nw
, (struct TagItem
*) win_tags
);
125 D(bug("[contask] Using window %p\n", fh
->otherwindow
));
126 fh
->window
= fh
->otherwindow
;
131 D(bug("contask: window opened\n"));
132 fh
->conreadio
->io_Data
= (APTR
) fh
->window
;
133 fh
->conreadio
->io_Length
= sizeof(struct Window
);
135 if (0 == OpenDevice("console.device", CONU_SNIPMAP
, ioReq(fh
->conreadio
), 0))
137 const UBYTE lf_on
[] =
138 { 0x9B, 0x32, 0x30, 0x68 }; /* Set linefeed mode */
140 D(bug("contask: device opened\n"));
142 fh
->flags
|= FHFLG_CONSOLEDEVICEOPEN
;
144 fh
->conwriteio
= *fh
->conreadio
;
145 fh
->conwriteio
.io_Message
.mn_ReplyPort
= fh
->conwritemp
;
147 /* Turn the console into LF+CR mode so that both
148 linefeed and carriage return is done on
150 fh
->conwriteio
.io_Command
= CMD_WRITE
;
151 fh
->conwriteio
.io_Data
= (APTR
) lf_on
;
152 fh
->conwriteio
.io_Length
= 4;
154 DoIO(ioReq(&fh
->conwriteio
));
156 if (fh
->workbenchbase
&& (fh
->appmsgport
= CreateMsgPort()))
158 if ((fh
->appwindow
= AddAppWindow(0, 0, fh
->window
, fh
->appmsgport
, NULL
)))
159 D(bug("[CON] Console promoted to be an AppWindow\n"));
162 } /* if (0 == OpenDevice("console.device", CONU_STANDARD, ioReq(fh->conreadio), 0)) */
165 err
= ERROR_INVALID_RESIDENT_LIBRARY
;
168 CloseWindow(fh
->window
);
170 } /* if (fh->window) */
173 D(bug("[contask] Failed to open a window\n"));
174 err
= ERROR_NO_FREE_STORE
;
180 static BOOL
MakeSureWinIsOpen(struct filehandle
*fh
)
184 return MakeConWindow(fh
) == 0;
187 static void close_con(struct filehandle
*fh
)
191 D(bug("[CON] Deleting timer request 0x%p\n", fh
->timerreq
));
194 CloseDevice((struct IORequest
*) fh
->timerreq
);
195 DeleteIORequest((struct IORequest
*) fh
->timerreq
);
198 D(bug("[CON] Deleting timer port 0x%p\n", fh
->timermp
));
199 DeleteMsgPort(fh
->timermp
);
201 if (fh
->flags
& FHFLG_CONSOLEDEVICEOPEN
)
203 D(bug("[CON] Closing console.device...\n"));
204 CloseDevice((struct IORequest
*) fh
->conreadio
);
209 D(bug("[CON] Unpromote console window from being an AppWindow\n"));
210 RemoveAppWindow(fh
->appwindow
);
214 struct AppMessage
*appmsg
;
215 while ((appmsg
= (struct AppMessage
*) GetMsg(fh
->appmsgport
)))
216 ReplyMsg ((struct Message
*) appmsg
);
217 D(bug("[CON] Delete MsgPort for AppWindow\n"));
218 DeleteMsgPort(fh
->appmsgport
);
221 D(bug("[CON] Closing window 0x%p\n", fh
->window
));
223 CloseWindow(fh
->window
);
225 D(bug("[CON] Delete console.device IORequest 0x%p\n", fh
->conreadio
));
226 DeleteIORequest(ioReq(fh
->conreadio
));
228 D(bug("[CON] Delete console.device MsgPort 0x%p\n", fh
->conreadmp
));
229 FreeVec(fh
->conreadmp
);
232 FreeVec(fh
->screenname
);
234 FreeVec(fh
->wintitle
);
236 FreeMem(fh
->pastebuffer
, PASTEBUFSIZE
);
238 CloseLibrary((struct Library
*) fh
->intuibase
);
239 CloseLibrary((struct Library
*) fh
->dosbase
);
240 CloseLibrary((struct Library
*) fh
->workbenchbase
);
242 /* These libraries are opened only if completion was used */
244 CloseLibrary((struct Library
*) fh
->gfxbase
);
246 CloseLibrary(fh
->gtbase
);
251 static struct filehandle
*open_con(struct DosPacket
*dp
, LONG
*perr
)
253 #if defined(__AROSPLATFORM_SMP__)
254 void *ExecLockBase
= OpenResource("execlock.resource");
257 struct filehandle
*fh
;
258 struct DeviceNode
*dn
;
262 dn
= BADDR(dp
->dp_Arg3
);
263 *perr
= ERROR_NO_FREE_STORE
;
264 fh
= AllocVec(sizeof(struct filehandle
), MEMF_PUBLIC
| MEMF_CLEAR
);
268 fh
->intuibase
= (APTR
) OpenLibrary("intuition.library", 0);
269 fh
->dosbase
= (APTR
) OpenLibrary("dos.library", 0);
270 fh
->utilbase
= (APTR
) OpenLibrary("utility.library", 0);
271 fh
->workbenchbase
= (APTR
) OpenLibrary("workbench.library", 0);
273 #if defined(__AROSPLATFORM_SMP__)
275 ObtainSystemLock(&SysBase
->DeviceList
, SPINLOCK_MODE_READ
, LOCKF_FORBID
);
282 fh
->inputbase
= (struct Device
*) FindName(&SysBase
->DeviceList
, "input.device");
284 #if defined(__AROSPLATFORM_SMP__)
286 ReleaseSystemLock(&SysBase
->DeviceList
, LOCKF_FORBID
);
293 if (!fh
->intuibase
|| !fh
->dosbase
|| !fh
->utilbase
|| !fh
->inputbase
)
295 CloseLibrary((APTR
) fh
->utilbase
);
296 CloseLibrary((APTR
) fh
->dosbase
);
297 CloseLibrary((APTR
) fh
->intuibase
);
298 CloseLibrary((APTR
) fh
->workbenchbase
);
303 fh
->timermp
= CreateMsgPort();
304 fh
->timerreq
= (struct timerequest
*) CreateIORequest(fh
->timermp
, sizeof(struct timerequest
));
305 OpenDevice(TIMERNAME
, UNIT_MICROHZ
, (struct IORequest
*) fh
->timerreq
, 0);
308 filename
= BSTR2C((BSTR
) dp
->dp_Arg1
);
310 i
= isdosdevicec(fn
);
314 fh
->contask
= FindTask(0);
316 NEWLIST(&fh
->pendingReads
);
318 /* Create msgport for console.device communication */
319 fh
->conreadmp
= AllocVec(sizeof(struct MsgPort
) * 2, MEMF_PUBLIC
| MEMF_CLEAR
);
322 memset( fh
->conreadmp
, 0, sizeof( *fh
->conreadmp
) );
323 fh
->conreadmp
->mp_Node
.ln_Type
= NT_MSGPORT
;
324 fh
->conreadmp
->mp_Flags
= PA_SIGNAL
;
325 fh
->conreadmp
->mp_SigBit
= AllocSignal(-1);
326 fh
->conreadmp
->mp_SigTask
= fh
->contask
;
327 NEWLIST(&fh
->conreadmp
->mp_MsgList
);
329 fh
->conwritemp
= fh
->conreadmp
+ 1;
331 memset( fh
->conwritemp
, 0, sizeof( *fh
->conwritemp
) );
332 fh
->conwritemp
->mp_Node
.ln_Type
= NT_MSGPORT
;
333 fh
->conwritemp
->mp_Flags
= PA_SIGNAL
;
334 fh
->conwritemp
->mp_SigBit
= AllocSignal(-1);
335 fh
->conwritemp
->mp_SigTask
= fh
->contask
;
336 NEWLIST(&fh
->conwritemp
->mp_MsgList
);
338 fh
->conreadio
= (struct IOStdReq
*) CreateIORequest(fh
->conreadmp
, sizeof(struct IOStdReq
));
341 D(bug("contask: conreadio created, parms '%s'\n", fn
));
345 if (parse_filename(fh
, fn
, &fh
->nw
))
347 if (!(fh
->flags
& FHFLG_AUTO
))
349 err
= MakeConWindow(fh
);
359 err
= ERROR_BAD_STREAM_NAME
;
363 DeleteIORequest(ioReq(fh
->conreadio
));
366 } /* if (fh->conreadio) */
369 err
= ERROR_NO_FREE_STORE
;
372 } /* if (fh->conreadmp) */
375 err
= ERROR_NO_FREE_STORE
;
379 fh
->flags
|= FHFLG_RAW
;
389 static void startread(struct filehandle
*fh
)
391 if (fh
->flags
& FHFLG_ASYNCCONSOLEREAD
)
393 fh
->conreadio
->io_Command
= CMD_READ
;
394 fh
->conreadio
->io_Data
= fh
->consolebuffer
;
395 fh
->conreadio
->io_Length
= CONSOLEBUFFER_SIZE
;
396 SendIO((struct IORequest
*) fh
->conreadio
);
397 fh
->flags
|= FHFLG_ASYNCCONSOLEREAD
;
400 static void stopwait(struct filehandle
*fh
, struct DosPacket
*waitingdp
, ULONG result
)
404 AbortIO((struct IORequest
*) fh
->timerreq
);
405 WaitIO((struct IORequest
*) fh
->timerreq
);
406 replypkt(waitingdp
, result
);
410 static void stopread(struct filehandle
*fh
, struct DosPacket
*waitingdp
)
412 struct Message
*msg
, *next_msg
;
414 stopwait(fh
, waitingdp
, DOSFALSE
);
416 ForeachNodeSafe(&fh
->pendingReads
, msg
, next_msg
)
418 struct DosPacket
*dpr
;
420 Remove((struct Node
*) msg
);
421 dpr
= (struct DosPacket
*) msg
->mn_Node
.ln_Name
;
422 replypkt(dpr
, DOSFALSE
);
426 LONG
CONMain(struct ExecBase
*SysBase
)
429 struct DosPacket
*dp
;
431 struct FileHandle
*dosfh
;
433 struct filehandle
*fh
;
435 struct DosPacket
*waitingdp
= NULL
;
437 D(bug("[CON] started\n"));
438 mp
= &((struct Process
*) FindTask(NULL
))->pr_MsgPort
;
440 dp
= (struct DosPacket
*) GetMsg(mp
)->mn_Node
.ln_Name
;
441 D(bug("[CON] startup message received. port=0x%p path='%b'\n", mp
, dp
->dp_Arg1
));
443 fh
= open_con(dp
, &error
);
446 D(bug("[CON] init failed\n"));
449 D(bug("[CON] 0x%p open\n", fh
));
450 replypkt(dp
, DOSTRUE
);
454 ULONG conreadmask
= 1L << fh
->conreadmp
->mp_SigBit
;
455 ULONG timermask
= 1L << fh
->timermp
->mp_SigBit
;
456 ULONG packetmask
= 1L << mp
->mp_SigBit
;
457 ULONG appwindowmask
= fh
->appmsgport
? 1L << fh
->appmsgport
->mp_SigBit
: 0L;
458 ULONG i
= 0, insertedlen
= 0;
460 UBYTE iconpath
[INPUTBUFFER_SIZE
];
461 WORD currentpos
, currentrest
;
463 sigs
= Wait(packetmask
| conreadmask
| timermask
| appwindowmask
);
465 if (sigs
& appwindowmask
)
467 while ((fh
->appmsg
= (struct AppMessage
*)GetMsg(fh
->appmsgport
)))
469 if (fh
->appmsg
->am_Type
== AMTYPE_APPWINDOW
)
471 if (fh
->appmsg
->am_NumArgs
>= 1)
475 if (fh
->appmsg
->am_ArgList
[i
].wa_Lock
)
477 NameFromLock(fh
->appmsg
->am_ArgList
[i
].wa_Lock
,
478 iconpath
, INPUTBUFFER_SIZE
- 1);
479 AddPart(iconpath
, fh
->appmsg
->am_ArgList
[i
].wa_Name
,
480 INPUTBUFFER_SIZE
- 1);
481 D(bug("[CON]: D&D iconpath: %s\n", iconpath
));
483 if ((insertedlen
= strlen(iconpath
)))
486 * Get rid of trailing slashes of drawers?
487 if ((iconpath[insertedlen - 1] == '/'))
490 if ( strchr(iconpath
, ' ')
491 && insertedlen
<= (INPUTBUFFER_SIZE
- 2))
493 memmove(iconpath
+ 1, iconpath
, ++insertedlen
);
494 iconpath
[0] = iconpath
[insertedlen
++] = '"';
496 if (insertedlen
<= (INPUTBUFFER_SIZE
- 1))
497 iconpath
[insertedlen
++] = ' ';
499 currentpos
= fh
->inputpos
;
500 currentrest
= fh
->inputsize
- fh
->inputpos
;
501 memmove(&fh
->inputbuffer
[currentpos
+ insertedlen
],
502 &fh
->inputbuffer
[currentpos
],
504 CopyMem(iconpath
, &fh
->inputbuffer
[currentpos
],
506 fh
->inputsize
+= insertedlen
;
507 fh
->inputpos
+= insertedlen
;
509 do_write(fh
, &fh
->inputbuffer
[currentpos
],
510 insertedlen
+ currentrest
);
511 do_movecursor(fh
, CUR_LEFT
, currentrest
);
514 } while (++i
< fh
->appmsg
->am_NumArgs
);
517 ReplyMsg((struct Message
*)fh
->appmsg
);
519 ActivateWindow(fh
->window
);
522 if (sigs
& timermask
)
526 replypkt(waitingdp
, DOSFALSE
);
531 if (sigs
& conreadmask
)
533 GetMsg(fh
->conreadmp
);
534 fh
->flags
&= ~FHFLG_ASYNCCONSOLEREAD
;
537 stopwait(fh
, waitingdp
, DOSTRUE
);
540 D(bug("IO_READ %d\n", fh
->conreadio
->io_Actual
));
541 fh
->conbuffersize
= fh
->conreadio
->io_Actual
;
542 fh
->conbufferpos
= 0;
543 /* terminate with 0 char */
544 fh
->consolebuffer
[fh
->conbuffersize
] = '\0';
545 if (fh
->flags
& FHFLG_RAW
)
549 for (inp
= 0; (inp
< fh
->conbuffersize
) && (fh
->inputpos
< INPUTBUFFER_SIZE
);)
551 fh
->inputbuffer
[fh
->inputpos
++] = fh
->consolebuffer
[inp
++];
553 fh
->inputsize
= fh
->inputstart
= fh
->inputpos
;
554 HandlePendingReads(fh
);
555 } /* if (fh->flags & FHFLG_RAW) */
559 if (process_input(fh
))
562 * process_input() returns TRUE when EOF was received after the WAIT console
563 * has been closed by the owner.
568 } /* if (fh->flags & FHFLG_RAW) else ... */
570 if (fh
->flags
& FHFLG_CONSOLEDEVICEOPEN
) /* device could have been closed */
574 while ((mn
= GetMsg(mp
)))
576 dp
= (struct DosPacket
*) mn
->mn_Node
.ln_Name
;
579 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
,
584 case ACTION_FH_FROM_LOCK
:
585 fl
= BADDR(dp
->dp_Arg2
);
586 if (fl
->fl_Task
!= mp
|| fl
->fl_Key
!= (IPTR
) fh
)
588 replypkt2(dp
, DOSFALSE
, ERROR_OBJECT_NOT_FOUND
);
592 FreeMem(fl
, sizeof(*fl
));
594 case ACTION_FINDINPUT
:
595 case ACTION_FINDOUTPUT
:
596 case ACTION_FINDUPDATE
:
597 dosfh
= BADDR(dp
->dp_Arg1
);
598 dosfh
->fh_Interactive
= DOSTRUE
;
599 dosfh
->fh_Arg1
= (SIPTR
) fh
;
601 fh
->breaktask
= dp
->dp_Port
->mp_SigTask
;
602 D(bug("[CON] Find fh=%x. Usecount=%d\n", dosfh
, fh
->usecount
));
603 replypkt(dp
, DOSTRUE
);
605 case ACTION_COPY_DIR_FH
:
606 fl
= AllocMem(sizeof(*fl
), MEMF_CLEAR
| MEMF_PUBLIC
);
609 replypkt2(dp
, (SIPTR
) BNULL
, ERROR_NO_FREE_STORE
);
615 fl
->fl_Access
= ACCESS_READ
;
616 fl
->fl_Key
= (IPTR
) fh
;
617 replypkt(dp
, (SIPTR
) MKBADDR(fl
));
620 case ACTION_FREE_LOCK
:
621 fl
= BADDR(dp
->dp_Arg1
);
622 fh
= (struct filehandle
*)fl
->fl_Key
;
624 FreeMem(fl
, sizeof(*fl
));
627 replypkt(dp
, DOSTRUE
);
631 D(bug("[CON] usecount=%d\n", fh
->usecount
));
632 if (fh
->usecount
<= 0)
634 if (fh
->flags
& FHFLG_WAIT
)
636 D(bug("[CON] Delayed close, waiting...\n"));
639 * Bounce all pending read and waits (the same as we do when exiting).
640 * However the process is still around, waiting for EOF input.
641 * Our user has just closed his struct FileHandle and dropped us.
643 stopread(fh
, waitingdp
);
645 fh
->flags
= (fh
->flags
& ~FHFLG_READPENDING
) | FHFLG_WAITFORCLOSE
;
650 replypkt(dp
, DOSTRUE
);
653 if (!MakeSureWinIsOpen(fh
))
655 replypkt2(dp
, DOSFALSE
, ERROR_NO_FREE_STORE
);
658 fh
->breaktask
= dp
->dp_Port
->mp_SigTask
;
663 if (!MakeSureWinIsOpen(fh
))
665 replypkt2(dp
, DOSFALSE
, ERROR_NO_FREE_STORE
);
668 fh
->breaktask
= dp
->dp_Port
->mp_SigTask
;
670 answer_write_request(fh
, dp
);
672 case ACTION_SCREEN_MODE
:
674 D(bug("ACTION_SCREEN_MODE %s\n", dp
->dp_Arg1
? "RAW" : "CON"));
675 if (dp
->dp_Arg1
&& !(fh
->flags
& FHFLG_RAW
))
677 /* Switching from CON: mode to RAW: mode */
678 fh
->flags
|= FHFLG_RAW
;
679 fh
->inputstart
= fh
->inputsize
;
680 fh
->inputpos
= fh
->inputsize
;
681 HandlePendingReads(fh
);
685 /* otherwise just copy the flags */
687 fh
->flags
|= FHFLG_RAW
;
689 fh
->flags
&= ~FHFLG_RAW
;
691 replypkt(dp
, DOSTRUE
);
694 case ACTION_CHANGE_SIGNAL
:
696 struct Task
*old
= fh
->breaktask
;
698 fh
->breaktask
= (struct Task
*) dp
->dp_Arg2
;
699 replypkt2(dp
, DOSTRUE
, (SIPTR
) old
);
702 case ACTION_WAIT_CHAR
:
704 if (!MakeSureWinIsOpen(fh
))
706 replypkt2(dp
, DOSFALSE
, ERROR_NO_FREE_STORE
);
709 if (fh
->inputsize
> 0)
711 replypkt(dp
, DOSTRUE
);
713 else if (dp
->dp_Arg1
== 0)
715 replypkt(dp
, DOSFALSE
);
719 LONG timeout
= dp
->dp_Arg1
;
720 LONG sec
= timeout
/ 1000000;
721 LONG usec
= timeout
% 1000000;
723 fh
->timerreq
->tr_node
.io_Command
= TR_ADDREQUEST
;
724 fh
->timerreq
->tr_time
.tv_secs
= sec
;
725 fh
->timerreq
->tr_time
.tv_micro
= usec
;
726 SendIO((struct IORequest
*) fh
->timerreq
);
732 case ACTION_IS_FILESYSTEM
:
733 replypkt(dp
, DOSFALSE
);
735 case ACTION_DISK_INFO
:
737 /* strange console handler features */
738 struct InfoData
*id
= BADDR(dp
->dp_Arg1
);
739 memset(id
, 0, sizeof(struct InfoData
));
741 (fh
->flags
& FHFLG_RAW
) ? AROS_MAKE_ID('R', 'A', 'W', 0) : AROS_MAKE_ID('C', 'O', 'N', 0);
742 id
->id_VolumeNode
= (BPTR
) fh
->window
;
743 id
->id_InUse
= (IPTR
) fh
->conreadio
;
744 replypkt(dp
, DOSTRUE
);
748 /* Yes, DOSTRUE. Check Guru Book for details. */
749 replypkt2(dp
, DOSTRUE
, ERROR_ACTION_NOT_KNOWN
);
752 bug("[CON] unknown action %d\n", dp
->dp_Type
);
753 replypkt2(dp
, DOSFALSE
, ERROR_ACTION_NOT_KNOWN
);
759 D(bug("[CON] 0x%p closing\n", fh
));
762 D(bug("[CON] Cancelling read requests...\n"));
763 stopread(fh
, waitingdp
);
765 if (fh
->flags
& FHFLG_ASYNCCONSOLEREAD
)
767 D(bug("[CON] Aborting console ioReq 0x%p\n", fh
->conreadio
));
769 AbortIO(ioReq(fh
->conreadio
));
770 WaitIO(ioReq(fh
->conreadio
));
773 D(bug("[CON] Closing handle...\n"));
779 D(bug("[CON] Replying packet 0x%p\n", dp
));
780 replypkt(dp
, DOSFALSE
);
783 D(bug("[CON] 0x%p closed\n", fh
));