1 #include "EXTERN.h"
2 #include "perl.h"
3 #include "XSUB.h"
4 
5 #undef __USE_INLINE__
6 #include <exec/types.h>
7 #include <utility/tagitem.h>
8 #include <proto/exec.h>
9 #include <proto/intuition.h>
10 #include <proto/rexxsyslib.h>
11 #include <proto/utility.h>
12 
13 #include <rexx/rxslib.h>
14 #include <rexx/errors.h>
15 //#include "rexxmsgext.h" // this should change depening on the ultimate location of the structures
16 
17 /* utils */
18 
19 /*
20  * Structure for the rexx host. Most of the code is inspired from Olaf
21  * Barthel's sample ARexx code from the developer CD 2.1
22  */
23 
24 
25 struct RexxHost
26 {
27 	struct MsgPort *Port;
28 	TEXT PortName[81];
29 } ;
30 
31 struct ARexxMsg
32 {
33 	struct RexxMsg *rexxMsg;
34 	BOOL isReplied;
35 	struct RexxHost *rexxHost;
36 };
37 
38 STRPTR dupstr(STRPTR src)
39 {
40     STRPTR dest = NULL;
41     ULONG len;
42     if(src)
43     {
44         len = strlen(src);
45         if((dest = IExec->AllocVec(len + 1, MEMF_ANY)))
46         {
47             strcpy(dest,src);
48         }
49     }
50     return dest;
51 }
52 
53 
54 struct TimeRequest *
55 OpenTimer(void)
56 {
57 	struct MsgPort *port = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
58 	if (port == NULL)
59 	{
60 		return NULL;
61 	}
62 
63 	struct TimeRequest *req = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
64 		ASOIOR_Size, sizeof(struct TimeRequest),
65 		ASOIOR_ReplyPort, port,
66 		TAG_END);
67 
68 	if (req == NULL)
69 	{
70 		IExec->FreeSysObject(ASOT_PORT, port);
71 		return NULL;
72 	}
73 
74 	int8 deverr = IExec->OpenDevice("timer.device", UNIT_MICROHZ,
75 		&req->Request, 0);
76 
77 	if (deverr != IOERR_SUCCESS)
78 	{
79 		IExec->FreeSysObject(ASOT_IOREQUEST, req);
80 		IExec->FreeSysObject(ASOT_PORT, port);
81 		return NULL;
82 	}
83 
84 	return req;
85 }
86 
87 
88 void
89 CloseTimer(struct TimeRequest *req)
90 {
91 	if (req != NULL)
92 	{
93 		struct MsgPort *port = req->Request.io_Message.mn_ReplyPort;
94 
95 		IExec->CloseDevice(&req->Request);
96 		IExec->FreeSysObject(ASOT_IOREQUEST, req);
97 		IExec->FreeSysObject(ASOT_PORT, port);
98 	}
99 }
100 
101 LONG
102 ReturnRexxMsg(struct RexxMsg * Message, CONST_STRPTR Result)
103 {
104 	STRPTR ResultString = NULL;
105 
106 	/* No error has occured yet. */
107 	int32 ErrorCode = 0;
108 
109 	/* Set up the RexxMsg to return no error. */
110 	Message->rm_Result1 = RC_OK;
111 	Message->rm_Result2 = 0;
112 
113 	/* Check if the command should return a result. */
114 	if((Message->rm_Action & RXFF_RESULT) && Result != NULL)
115 	{
116 		/* To return the result string we need to make
117 		 * a copy for ARexx to use.
118 		 */
119 		if((ResultString = IRexxSys->CreateArgstring(Result, strlen(Result))))
120 		{
121 			/* Put the string into the secondary
122 			 * result field.
123 			 */
124 			Message->rm_Result2 = (LONG)ResultString;
125 		}
126 		else
127 		{
128 			/* No memory available. */
129 			ErrorCode = ERR10_003;
130 		}
131 	}
132 
133 	/* Reply the message, regardless of the error code. */
134 	IExec->ReplyMsg((struct Message *)Message);
135 
136 	return(ErrorCode);
137 }
138 
139 
140 void
141 ReturnErrorMsg(struct RexxMsg *msg, CONST_STRPTR port, int32 rc, int32 rc2)
142 {
143 	/* To signal an error the rc_Result1
144 	 * entry of the RexxMsg needs to be set to
145 	 * RC_ERROR. Unfortunately, we cannot convey
146 	 * the more meaningful error code through
147 	 * this interface which is why we set a
148 	 * Rexx variable to the error number. The
149 	 * Rexx script can then take a look at this
150 	 * variable and decide which further steps
151 	 * it should take.
152 	 */
153 	msg->rm_Result1 = rc;
154 	msg->rm_Result2 = rc2;
155 
156 	/* Turn the error number into a string as
157 	 * ARexx only deals with strings.
158 	 */
159 	char value[12];
160 	IUtility->SNPrintf(value, sizeof(value), "%ld", rc2);
161 
162 	/* Build the name of the variable to set to
163 	 * the error number. We will use the name of
164 	 * the host name and append ".LASTERROR".
165 	 */
166 	IRexxSys->SetRexxVarFromMsg("RC2", value, msg);
167 
168 	IExec->ReplyMsg(&msg->rm_Node);
169 }
170 
171 BOOL
172 PutMsgTo(CONST_STRPTR name, struct Message *msg)
173 {
174 	BOOL done = FALSE;
175 
176 	IExec->Forbid();
177 
178 	struct MsgPort *port = IExec->FindPort(name);
179 	if (port != NULL)
180 	{
181 		IExec->PutMsg(port, msg);
182 		done = TRUE;
183 	}
184 
185 	IExec->Permit();
186 
187 	return done;
188 }
189 
190 
191 STRPTR DoRexx(STRPTR port, STRPTR command, int32 *rc, int32 *rc2)
192 {
193 	*rc = 0;
194 	*rc2 = 0;
195 	STRPTR result = NULL;
196 	STRPTR dup = NULL;
197 
198 	struct MsgPort *replyPort = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
199 	if (replyPort == NULL)
200 	{
201 		return NULL;
202 	}
203 
204 	struct RexxMsg *rexxMsg = IRexxSys->CreateRexxMsg(replyPort, NULL, NULL);
205 	((struct Node *)rexxMsg)->ln_Name = "REXX";
206 	if (rexxMsg == NULL)
207 	{
208 		IExec->FreeSysObject(ASOT_PORT, replyPort);
209 		return NULL;
210 	}
211 	BOOL sent = FALSE;
212 
213 
214 	rexxMsg->rm_Args[0] = IRexxSys->CreateArgstring(command, strlen(command));
215 
216 	if (rexxMsg->rm_Args[0] != NULL)
217 	{
218 		rexxMsg->rm_Action = RXCOMM | RXFF_RESULT | RXFF_STRING;
219 
220 		sent = PutMsgTo(port, (struct Message*)rexxMsg);
221 
222 		if (sent)
223 		{
224 			IExec->WaitPort(replyPort);
225 			(void)IExec->GetMsg(replyPort);
226 		}
227 		else
228 		{
229 
230 		}
231 
232 		*rc = rexxMsg->rm_Result1;
233 
234 		if (*rc == RC_OK)
235 		{
236 			if (rexxMsg->rm_Result2 != 0)
237 			{
238 				result = (STRPTR)rexxMsg->rm_Result2;
239 			}
240 		}
241 		else
242 		{
243 			*rc2 = rexxMsg->rm_Result2;
244 		}
245 
246 		IRexxSys->DeleteArgstring(rexxMsg->rm_Args[0]);
247 		rexxMsg->rm_Args[0] = NULL;
248 	}
249 
250 	IRexxSys->DeleteRexxMsg(rexxMsg);
251 	rexxMsg = NULL;
252 
253 	IExec->FreeSysObject(ASOT_PORT, replyPort);
254 	replyPort = NULL;
255 
256 	if (result != NULL)
257 	{
258 		dup = dupstr(result);
259 
260 		IRexxSys->DeleteArgstring(result);
261 		result = NULL;
262 	}
263 
264 	return dup;
265 }
266 
267 
268 struct RexxHost *CreateRexxHost(CONST_STRPTR PortName)
269 {
270 	struct RexxHost *newHost = IExec->AllocVecTags(sizeof(struct RexxHost),
271 	AVT_Type, MEMF_PRIVATE, AVT_ClearWithValue, 0, TAG_DONE);
272 
273 	if (newHost == NULL)
274 	{
275 	return NULL;
276 	}
277 
278 	IUtility->Strlcpy(newHost->PortName, PortName, sizeof(newHost->PortName));
279 
280 	IExec->Forbid();
281 
282 	/* Check if the name already exists */
283 	if (IExec->FindPort(PortName) != NULL)
284 	{
285 	int32 index = 1;
286 	do
287 	{
288 	IUtility->SNPrintf(newHost->PortName, sizeof(newHost->PortName), "%s.%ld", PortName, index);
289 	index++;
290 
291 	if (IExec->FindPort(newHost->PortName) == NULL)
292 	{
293 	break;
294 	}
295 	} while (1);
296 	}
297 
298 	newHost->Port = IExec->AllocSysObjectTags(ASOT_PORT,
299 	ASOPORT_Name, 	newHost->PortName,
300 	ASOPORT_Public, TRUE,
301 	TAG_DONE);
302 
303 	IExec->Permit();
304 
305 	if (newHost->Port == NULL)
306 	{
307 	IExec->FreeVec(newHost);
308 	return NULL;
309 	}
310 
311 	return newHost;
312 }
313 
314 
315 void DeleteRexxHost(struct RexxHost *host)
316 {
317 	if (host)
318 	{
319 	if (host->Port)
320 	{
321 	struct RexxMsg *msg;
322 
323 	IExec->Forbid();
324 	while ((msg = (struct RexxMsg *)IExec->GetMsg(host->Port)) != NULL)
325 	{
326 	msg->rm_Result1 = RC_FATAL;
327 	IExec->ReplyMsg((struct Message *)msg);
328 	}
329 
330 	IExec->FreeSysObject(ASOT_PORT, host->Port);
331 	IExec->Permit();
332 	}
333 
334 	IExec->FreeVec(host);
335 	}
336 }
337 
338 void WaitRexxHost(struct RexxHost *rexxHost, int timeout)
339 {
340 
341 	struct TimeRequest *req = NULL;
342 	uint32 timermask        = 0;
343 
344 	if (timeout > 0)
345 	{
346 		req = OpenTimer();
347 
348 		if (req != NULL)
349 		{
350 			timermask = 1L << req->Request.io_Message.mn_ReplyPort->mp_SigBit;
351 
352 			req->Request.io_Command = TR_ADDREQUEST;
353 			req->Time.Seconds       = 0;
354 			req->Time.Microseconds  = timeout;
355 
356 			IExec->SendIO(&req->Request);
357 		}
358 	}
359 
360 	uint32 hostmask = 1L << rexxHost->Port->mp_SigBit;
361 	uint32 waitmask = timermask | hostmask | SIGBREAKF_CTRL_C;
362 
363 	uint32 sigmask = IExec->Wait(waitmask);
364 
365 	if (req != NULL)
366 	{
367 		IExec->AbortIO(&req->Request);
368 		IExec->WaitIO(&req->Request);
369 		CloseTimer(req);
370 	}
371 
372 	if (sigmask & SIGBREAKF_CTRL_C)
373 	{
374 		return;
375 	}
376 
377 
378 }
379 
380 struct ARexxMsg *GetMsgRexxHost(struct RexxHost *rexxHost)
381 {
382 	struct ARexxMsg *am = NULL;
383 
384 	struct RexxMsg *rexxMsg = NULL;
385 
386 	rexxMsg = (struct RexxMsg *)IExec->GetMsg(rexxHost->Port);
387 	if (rexxMsg != NULL)
388 	{
389 		if((am = IExec->AllocVecTags(sizeof(struct ARexxMsg),AVT_Type, MEMF_PRIVATE, AVT_ClearWithValue, 0, TAG_DONE)))
390 		{
391 			am->rexxMsg = rexxMsg;
392 			am->rexxHost = rexxHost;
393 			am->isReplied = FALSE;
394 		}
395 
396 	}
397 	return am;
398 }
399 
400 uint32 GetSignalRexxHost(struct RexxHost *rexxHost)
401 {
402 	return rexxHost->Port->mp_SigBit;
403 }
404 
405 
406 void ReplyARexxMsg(struct ARexxMsg *am, int rc, int rc2, STRPTR result)
407 {
408 	if(am)
409 	{
410 		if(!am->isReplied)
411 		{
412 			if(rc == 0)
413 			{
414 				ReturnRexxMsg(am->rexxMsg, result);
415 			}
416 			else
417 			{
418 				ReturnErrorMsg(am->rexxMsg, am->rexxHost->PortName,rc,rc2);
419 			}
420 			am->isReplied = TRUE;
421 		}
422 	}
423 }
424 
425 STRPTR GetVarARexxMsg(struct ARexxMsg *am, STRPTR varname)
426 {
427 	STRPTR result = IExec->AllocVecTags(256,AVT_Type, MEMF_PRIVATE, AVT_ClearWithValue, 0, TAG_DONE);
428 	if(result)
429 	{
430 		IRexxSys->GetRexxVarFromMsg(varname, result, am->rexxMsg);
431 	}
432 	return result;
433 }
434 
435 void SetVarARexxMsg(struct ARexxMsg *am, STRPTR varname, STRPTR value)
436 {
437 	IRexxSys->SetRexxVarFromMsg(varname, value, am->rexxMsg);
438 }
439 
440 void DeleteARexxMsg(struct ARexxMsg *am)
441 {
442 	if(!am->isReplied)
443 	{
444 		IExec->ReplyMsg(&am->rexxMsg->rm_Node);
445 		am->isReplied = TRUE;
446 	}
447 	IExec->FreeVec(am);
448 }
449 
450 STRPTR GetArgsARexxMsg(struct ARexxMsg *am)
451 {
452 	return am->rexxMsg->rm_Args[0];
453 }
454 
455 MODULE = Amiga::ARexx              PACKAGE = Amiga::ARexx
456 
457 PROTOTYPES: DISABLE
458 
459 
460 APTR Host_init(name)
461     STRPTR name;
462     CODE:
463     	RETVAL = CreateRexxHost(name);
464     OUTPUT:
465     	RETVAL
466 
467 void Host_delete(rexxhost)
468 	APTR rexxhost;
469 	CODE:
470 		DeleteRexxHost(rexxhost);
471 
472 void Host_wait(rexxhost,timeout)
473 	APTR rexxhost
474 	int timeout
475 	CODE:
476 		WaitRexxHost(rexxhost,timeout);
477 
478 uint32 Host_signal(rexxhost)
479 	APTR rexxhost
480 	CODE:
481 		RETVAL = GetSignalRexxHost(rexxhost);
482 	OUTPUT:
483 		RETVAL
484 
485 APTR Host_getmsg(rexxhost)
486 	APTR rexxhost
487 	CODE:
488 		RETVAL = GetMsgRexxHost(rexxhost);
489 	OUTPUT:
490 		RETVAL
491 
492 void Msg_reply(rexxmsg,rc,rc2,result)
493 	APTR rexxmsg
494 	int rc
495 	int rc2
496 	STRPTR result
497 	CODE:
498 		ReplyARexxMsg(rexxmsg,rc,rc2,result);
499 
500 void Msg_delete(rexxmsg)
501 	APTR rexxmsg
502 	CODE:
503 		DeleteARexxMsg(rexxmsg);
504 
505 STRPTR Msg_argstr(rexxmsg)
506 	APTR rexxmsg
507 	CODE:
508 		RETVAL = GetArgsARexxMsg(rexxmsg);
509 	OUTPUT:
510 		RETVAL
511 
512 STRPTR Msg_getvar(rexxmsg,varname)
513 	APTR rexxmsg
514 	STRPTR varname
515 	PPCODE:
516 		RETVAL = GetVarARexxMsg(rexxmsg,varname);
517 		sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG;
518 		if (RETVAL) IExec->FreeVec(RETVAL);
519 
520 void Msg_setvar(rexxmsg,varname,value)
521 	APTR rexxmsg
522 	STRPTR varname
523 	STRPTR value
524 	CODE:
525 		SetVarARexxMsg(rexxmsg,varname,value);
526 
527 STRPTR _DoRexx(port,command,rc,rc2)
528 	STRPTR port
529 	STRPTR command
530 	int32 &rc
531 	int32 &rc2
532 	PPCODE:
533 		RETVAL = DoRexx(port,command,&rc,&rc2);
534 		sv_setiv(ST(2), (IV)rc);
535 		SvSETMAGIC(ST(2));
536 		sv_setiv(ST(3), (IV)rc2);
537 		SvSETMAGIC(ST(3));
538 		sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG;
539 		IExec->FreeVec(RETVAL);
540 
541