home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 2: PC / frozenfish_august_1995.bin / bbs / d09xx / d0938.lha / Angie / ImportedModules.lha / CxLib.mod < prev    next >
Text File  |  1993-10-29  |  20KB  |  588 lines

  1.  
  2. (* ------------------------------------------------------------------------
  3.   :Program.     CxLib
  4.   :Author       Franz Schwarz
  5.   :Address.     Mⁿhlenstra▀e 2, D-78591 Durchhausen, Germany / R.F.A.
  6.   :Address.     uucp: Franz.Schwarz@mil.ka.sub.org; Fido: 2:241/7506.18
  7.   :Copyright.   Freeware (freely distributable, copyrighted software)
  8.   :Language.    Oberon-2
  9.   :Translator.  Amiga-Oberon 3.00
  10.   :Contents.    Oberon-Implementation of Commodore's cx.lib
  11.   :Contents.    All functions of Commodore's cx.lib are implemented.
  12.   :Support.     BlackMagic
  13.   :History.     22-Jul-93 Version 1.0 fSchwarz
  14.   :History.     30-Sep-93 Version 1.0a fSchwarz adapted to OS3.0 interfaces
  15.   :History.     03-Oct-93 Version 1.0b fSchwarz now only tries once to get
  16.   :History.       default arguments (due to minor problems with
  17.   :History.       Dos.ReadArgs; ArgArrayInit() returns a valid ptr if the 
  18.   :History.       CLI commandline is empty.
  19.   :Remark.      Requires OS3.0 interface modules update by hartmut Goebel
  20.   :Remark.      As of Amiga Oberon Release 3.00: possible odd pointers to
  21.   :Remark.      array of char/byte: _don't_ compile with OddChk
  22.   ------------------------------------------------------------------------- *)
  23.  
  24. MODULE CxLib;
  25.  
  26. (****** CxLib/--overview-- **************************************************
  27. *
  28. *           CxLib is an Oberon implementation of Commodore's
  29. *       commoditiy.library related functions in amiga.lib .    
  30. *       Moreover, it offers several gimmicks that make it superior
  31. *       to Commodore's amiga.lib - functions. There is an additional
  32. *       function ArgBool() that eases handling of boolean-type
  33. *       ToolTypes, as well as an InvertStringForwd() function returning
  34. *       the InputEvents in forward order. The InvertString() function
  35. *       has some additional functionality, like the special magic RETURN
  36. *       mapping (see InvertString() documentation) and the '\xff' Hex
  37. *       esc code. This module closes a possibly open ArgArray ressource
  38. *       handle on shutdown, and correctly gains access to the program's
  39. *       arguments with ArgArrayInit() when calling the ToolType functions
  40. *       with NIL ToolType array handles, but may be flexibly used with
  41. *       different ToolType arrays by supplying valid ToolType array handles,
  42. *       too.
  43. *       
  44. ******************************************************************************)
  45.  
  46. (****** CxLib/--legal-- **************************************************
  47. *
  48. *   LEGAL STATUS
  49. *       CxLib is Freeware, Copyright ⌐ 1993 by F.Schwarz. All Rights
  50. *       reserved. Freeware is an abbreviation for Freely Distributable
  51. *       Copyrighted Software. That means, you may freely distribute this
  52. *       software for non-profit-making purposes, and use it in your own
  53. *       freely distributable software. However if you intend to use it
  54. *       in commercial software or shareware you may only use it under the
  55. *       condition you consider me to be a registred, legitimate user of
  56. *       that software and you contact me before releasing that software.
  57. *
  58. *   DISCLAIMER
  59. *       Liability - what liability?? In fact, no liability whatsoever is
  60. *       provided by the author of this software - this is generally known
  61. *       as "USE AT YOUR OWN RISK" - and that is exactly what it means.
  62. *
  63. *   DISTRIBUTION
  64. *       This software may be distributed if only a _reasonable_ copying
  65. *       fee is charged apart from the consts for the media it is copied to.
  66. *       Furthermore, it may be included in Freely Distributable software
  67. *       libraries like AMOK, etc, including CD-ROM versions of them.
  68. *
  69. *   Contact addresses for bug reports, comments, inquiries or anything else:
  70. *
  71. *          Mⁿhlenstra▀e 2, D-78591 Durchhausen, Germany / R.F.A.
  72. *     email: uucp: Franz_Schwarz@mil.ka.sub.org; Fido: 2:241/7506.18
  73. *
  74. *****************************************************************************)
  75.  
  76.  
  77. IMPORT 
  78.   e: Exec, d: Dos, km: KeyMap, kml: KeyMapLib, co: Commodities,
  79.   wb: Workbench, ic: Icon, o: OberonLib, b: BlackMagic, ie: InputEvent,
  80.   st: Strings, a: ASCII, y: SYSTEM;
  81.  
  82.  
  83. VAR 
  84.   ArgArrayInitTried: BOOLEAN;
  85.   dObject  : wb.DiskObjectPtr;
  86.   rda      : d.RDArgsPtr;  
  87.   ttypes - : b.TTPtr;
  88.   EmptyArgs: b.LStrPtr; (* this is a pseudo CONST (NIL) *)
  89.  
  90.  
  91. (****** CxLib/ArgArrayInit **************************************************
  92. *
  93. *   NAME
  94. *       ArgArrayInit - get a pointer to the program's argument array
  95. *
  96. *   SYNOPSIS
  97. *       ArgArrayInit(): BlackMagic.TTPtr
  98. *
  99. *   FUNCTION
  100. *       Returns a pointer to the argument array no matter whether the
  101. *       program was started from CLI or Workbench. It is safe to call
  102. *       this function multiple times.
  103. *
  104. *   RESULT
  105. *       The pointer to the NIL-terminated array of (untraced) pointers
  106. *       to the argument strings or NIL for failure.
  107. *
  108. *   NOTES
  109. *       Due to Commodore's Dos/ReadArgs() implementation, all CLI args
  110. *       containing a '=' have to be surrounded with quotes. (i.e.
  111. *       "CX_PRIORITY=-1" instead of just CX_PRIORITY=-1)
  112. *       
  113. *   SEE ALSO
  114. *       ArgsArrayDone()
  115. *
  116. ******************************************************************************)
  117.  
  118. PROCEDURE ArgArrayInit* (): b.TTPtr;
  119. VAR
  120.   dir : d.FileLockPtr;
  121. BEGIN
  122.   IF ArgArrayInitTried THEN RETURN ttypes; END;
  123.   ArgArrayInitTried := TRUE;
  124.   IF o.wbStarted THEN
  125.     dir := d.CurrentDir (o.wbenchMsg(wb.WBStartup).argList[0].lock);
  126.     dObject := ic.GetDiskObjectNew (o.wbenchMsg(wb.WBStartup).argList[0].name^);
  127.     IF dObject # NIL THEN ttypes := dObject.toolTypes; END;
  128.     dir := d.CurrentDir (dir);
  129.   ELSE (* Shell *)
  130.     IF rda = NIL THEN
  131.       rda := d.OldReadArgs ("/M", ttypes, NIL);
  132.       IF (rda # NIL) & (ttypes = NIL) THEN ttypes := y.ADR (EmptyArgs); END;
  133.     END;  
  134.   END;
  135.   RETURN ttypes;
  136. END ArgArrayInit;
  137.       
  138. (****** CxLib/ArgArrayDone **************************************************
  139. *
  140. *   NAME
  141. *       ArgArrayDone - free the resources allocated with ArgArrayInit()
  142. *
  143. *   SYNOPSIS
  144. *       ArgArrayDone ()
  145. *
  146. *   FUNCTION
  147. *       ArgArrayDone frees any possible ressources that have been allocated
  148. *       by previous calls to ArgArrayInit().
  149. *
  150. *   NOTES
  151. *       There is no need to call this function explicitely since it is
  152. *       called during CxLib's shutdown code.
  153. *
  154. *   SEE ALSO
  155. *       ArgArrayInit()
  156. *
  157. ****************************************************************************)
  158.  
  159. PROCEDURE ArgArrayDone *();
  160. BEGIN
  161.   IF dObject#NIL THEN
  162.     ic.FreeDiskObject(dObject);  dObject:=NIL;
  163.   ELSIF rda # NIL THEN
  164.     d.FreeArgs(rda);  rda := NIL;
  165.   END;
  166.   ttypes:=NIL;
  167. END ArgArrayDone;
  168.  
  169. (****** CxLib/ArgInt ********************************************************
  170. *
  171. *   NAME
  172. *       ArgInt - get number stored in a specific arg or default number
  173. *
  174. *   SYNOPSIS
  175. *       ArgInt (tt        : BlackMagic.TTPtr; 
  176. *               typeName  : ARRAY OF CHAR;
  177. *               defaultVal: LONGINT          ): LONGINT;
  178. *
  179. *   FUNCTION
  180. *       Searches the argument array tt for a typeName-named argument,
  181. *       and returns a valid integer stored in the argument's array.
  182. *       Otherwise, defaultVal is returned.
  183. *
  184. *   INPUTS
  185. *       tt        - a valid pointer to an argument array or NIL, in
  186. *                   which case the argument array returned by 
  187. *                   ArgArrayInit() is used.
  188. *       typeName  - the argument name
  189. *       defaultVal- the default value returned in case of 'failure'
  190. *
  191. *   RESULT
  192. *       If the argument was found, and it contained a valid dec/hex number
  193. *       char sequence, the appertaining integer representation, otherwise
  194. *       defaultVal.
  195. *
  196. *   SEE ALSO
  197. *       ArgString(), ArgBool(), BlackMagic/StrToLong()
  198. *
  199. ****************************************************************************)
  200.  
  201. PROCEDURE ArgInt* (tt: b.TTPtr; typeName: ARRAY OF CHAR;
  202.                    defaultVal: LONGINT): LONGINT;
  203. VAR str: e.STRPTR;
  204.   v: LONGINT;
  205. (* $CopyArrays- *)
  206. BEGIN
  207.   IF tt=NIL THEN tt:=ArgArrayInit(); END;
  208.   IF tt#NIL THEN
  209.     str:=ic.FindToolType(tt, typeName);
  210.     IF str#NIL THEN
  211.       IF b.StrToLong (str^, v) THEN
  212.         RETURN v
  213.       END;
  214.     END;
  215.   END;
  216.   RETURN defaultVal;
  217. END ArgInt;
  218.  
  219. (****** CxLib/ArgString *******************************************************
  220. *
  221. *   NAME
  222. *       ArgString - get string stored in a specific arg or default string
  223. *
  224. *   SYNOPSIS
  225. *       ArgString (tt        : BlackMagic.TTPtr; 
  226. *                  typeName  : ARRAY OF CHAR;
  227. *                  defaultStr: ARRAY OF CHAR    ): BlackMagic.LongStrPtr
  228. *
  229. *   FUNCTION
  230. *       Searches the argument array tt for a typeName-named argument,
  231. *       and returns its string. If the argument does not exist,
  232. *       defaultVal is returned.
  233. *
  234. *   INPUTS
  235. *       tt        - a valid pointer to an argument array or NIL, in
  236. *                   which case the argument array returned by 
  237. *                   ArgArrayInit() is used.
  238. *       typeName  - the argument name
  239. *       defaultStr- the default string returned in case of 'failure'
  240. *
  241. *   RESULT
  242. *       If the argument was found, the appertaining arg string, 
  243. *       otherwise defaultStr;
  244. *
  245. *   SEE ALSO
  246. *       ArgInt(), ArgBool()
  247. *
  248. ****************************************************************************)
  249.  
  250. PROCEDURE ArgString *(tt: b.TTPtr; typeName: ARRAY OF CHAR;
  251.                       defaultStr: ARRAY OF CHAR): b.LStrPtr;
  252. VAR str: e.APTR;
  253. (* $CopyArrays- *)
  254. BEGIN
  255.   IF tt=NIL THEN tt:=ArgArrayInit(); END;
  256.   IF tt#NIL THEN
  257.     str:=ic.FindToolType(tt, typeName);
  258.     IF str#NIL THEN RETURN str; END;
  259.   END;
  260.   RETURN b.StrIndex (defaultStr, 0);
  261. END ArgString;
  262.  
  263. (****** CxLib/ArgBool *********************************************************
  264. *
  265. *   NAME
  266. *       ArgBool - get boolean value stored in specific arg or default bool
  267. *
  268. *   SYNOPSIS
  269. *       ArgBool (tt         : BlackMagic.TTPtr; 
  270. *                typeName   : ARRAY OF CHAR;
  271. *                defaultBool: BOOLEAN          ): BOOLEAN
  272. *
  273. *   FUNCTION
  274. *       Searches the argument array tt for a typeName-named argument,
  275. *       and returns TRUE if the argument's value string is empty, or
  276. *       if it includes 'TRUE' / 'YES'  (case-insensitive). It returns
  277. *       FALSE if the typeName-named argument was found and its value
  278. *       string is not empty and doesn't contain 'TRUE' or 'YES'. If
  279. *       the searched argument is not found, defaultBool is returned.
  280. *
  281. *   INPUTS
  282. *       tt        - a valid pointer to an argument array or NIL, in
  283. *                   which case the argument array returned by 
  284. *                   ArgArrayInit() is used.
  285. *       typeName  - the argument name
  286. *       defaultStr- the default boolean value.
  287. *
  288. *   RESULT
  289. *       If the argument was found, the appertaining boolean value,
  290. *       otherwise defaultBool;
  291. *
  292. *   SEE ALSO
  293. *       ArgInt(), ArgString()
  294. *
  295. ****************************************************************************)
  296.  
  297. PROCEDURE ArgBool* (tt: b.TTPtr; typeName: ARRAY OF CHAR;
  298.                     defaultBool: BOOLEAN): BOOLEAN;
  299. VAR str: e.STRPTR;
  300. (* $CopyArrays- *)
  301. BEGIN
  302.   IF tt=NIL THEN tt:=ArgArrayInit(); END;
  303.   IF tt#NIL THEN
  304.     str:=ic.FindToolType(tt, typeName);
  305.     IF str#NIL THEN 
  306.       IF str[0]='\000' THEN
  307.         RETURN TRUE;
  308.       ELSIF ic.MatchToolValue(str^,"YES") OR ic.MatchToolValue(str^,"TRUE") THEN
  309.         RETURN TRUE;
  310.       ELSE
  311.         RETURN FALSE;
  312.       END;
  313.     END;
  314.   END;
  315.   RETURN defaultBool;
  316. END ArgBool;
  317.  
  318.  
  319. (****** CxLib/HotKey ********************************************************
  320. *
  321. *   NAME
  322. *       HotKey - create a triad of commodities objects for a hotkey
  323. *
  324. *   SYNOPSIS
  325. *       HotKey (descr: ARRAY OF CHAR;
  326. *               port : Exec.MsgPortPtr;
  327. *               id   : LONGINT         ): Commodities.CxObjPtr
  328. *
  329. *   FUNCTION
  330. *       This function creates a Filter-, a Sender-, and a Translate-
  331. *       CxObject and connects them so that any input event matching
  332. *       <descr> will dispatch a message to <port> with identifier
  333. *       code <id> and the input event will be removed from the input
  334. *       stream.
  335. *
  336. *   INPUTS
  337. *       descr    - the input description string for the hotkey
  338. *       port     - the Exec.MsgPort that shall receive a msg 
  339. *                  whenever the hotkey is pressed
  340. *       id       - the id code to be sent to the port with the msg.
  341. *
  342. *   RESULT
  343. *       a valid Commodities.CxObjPtr, or NIL in case of failure.
  344. *
  345. *   SEE ALSO
  346. *       Commodities.doc
  347. *
  348. ****************************************************************************)
  349.  
  350. PROCEDURE HotKey* (descr: ARRAY OF CHAR; port: e.MsgPortPtr;
  351.                    id: LONGINT): co.CxObjPtr;
  352. VAR cx1: co.CxObjPtr;
  353. (* $CopyArrays- *)
  354. BEGIN
  355.   cx1:=co.CxFilter (b.StrIndex (descr, 0));
  356.   IF cx1#NIL THEN
  357.     co.AttachCxObj (cx1, co.CxSender (port, id));
  358.     co.AttachCxObj (cx1, co.CxTranslate (NIL));
  359.     IF co.CxObjError (cx1) = LONGSET{} THEN
  360.       RETURN cx1;
  361.     END;
  362.     co.DeleteCxObjAll (cx1);
  363.   END;
  364.   RETURN NIL;
  365. END HotKey;
  366.  
  367. (****** CxLib/FreeIEvents *******************************************************
  368. *
  369. *   NAME
  370. *       FreeIEvents - frees a chain of input events from InvertString()
  371. *
  372. *   SYNOPSIS
  373. *       FreeIEvents (VAR iEvent: InputEvent.InputEventPtr);
  374. *
  375. *   FUNCTION
  376. *       This functions frees a chain of input events allocated by
  377. *       InvertString() / InvertStringForwd()
  378. *       
  379. *   INPUTS
  380. *       iEvent   - pointer to a chain of InputEvents. MUST have been
  381. *                  returned from InvertString() / InvertStringForwd()
  382. *                  May be NIL.
  383. *
  384. *   RESULT
  385. *       the VAR iEvent is cleared
  386. *
  387. *   SEE ALSO
  388. *       InvertString(), InvertStringForwd()
  389. *
  390. ****************************************************************************)
  391.  
  392. PROCEDURE FreeIEvents* (VAR iEvent: ie.InputEventPtr);
  393. VAR 
  394.   ie1: ie.InputEventPtr;
  395. BEGIN
  396.   WHILE iEvent#NIL DO
  397.     ie1:=iEvent; iEvent:=iEvent.nextEvent;
  398.     e.FreeVec (ie1);
  399.   END;
  400. END FreeIEvents;
  401.  
  402. CONST  
  403.   returnRawKeyCode = 44H;
  404.   retEvent = ie.InputEvent (NIL, ie.rawkey, 0, returnRawKeyCode, {}, 0, 0, 0, 0);
  405.  
  406. (****** CxLib/InvertString ******************************************************
  407. *
  408. *   NAME
  409. *       InvertString - create chain of ievents from mixed str / IX descr
  410. *       InvertStringForwd - create chain of ievents in forward order
  411. *
  412. *   SYNOPSIS
  413. *       InvertString (string: ARRAY OF CHAR;
  414. *                     km    : KeyMapPtr     ): InputEvent.InputEventPtr;
  415. *
  416. *       InvertStringForwd (string: ARRAY OF CHAR;
  417. *                          km    : KeyMapPtr   ): InputEvent.InputEventPtr;
  418. *
  419. *   FUNCTION
  420. *       These functions create a chain of InputEvents from a string
  421. *       consisting of normal string sequences with backslash escape
  422. *       sequences ('\\','\r','\n','\f','\e',"\'",'\"','\xff','\<','\t',
  423. *       '\0') and Commodities' input description strings enclosed in
  424. *       angled brackets ('<input desr>'). As a special bonus, this
  425. *       function always converts carriage returns from not within
  426. *       input description sequences ('<inp descr>') into RETURN_RAWKEY
  427. *       events if the return rawkey maps to carriage return. This
  428. *       functionality is not available in Commodore's amiga.lib-
  429. *       InvertString() although this is of significance for inserting
  430. *       text into many editors, as internal remapping of ENTER is quite
  431. *       frequent in that field.
  432. *
  433. *   INPUTS
  434. *       string   - the string to be converted into a chain of InputEvents
  435. *       km       - the keymap to be used for converting. May be NIL in
  436. *                  which case the system's current default keymap is used.
  437. *
  438. *   RESULT
  439. *       a pointer to the first InputEvent of an InputEvent chain or NIL.
  440. *
  441. *   NOTES
  442. *       Like Commodore's InvertString() function, InvertString() returns
  443. *       the InputEvents chained in REVERSE ORDER, ie. the resulting pointer
  444. *       points to the last InputEvent, and the first InputEvent is last
  445. *       in the list. Thus you should preferably use InvertStringForwd(),
  446. *       since that function returns the generated events in forward order,
  447. *       as its name already implies.
  448. *
  449. *   SEE ALSO
  450. *       Commodities/ParseIX()
  451. *
  452. ****************************************************************************)
  453.  
  454. PROCEDURE InvertString* (string: ARRAY OF CHAR; km: km.KeyMapPtr): ie.InputEventPtr;
  455.  
  456. VAR 
  457.   iEvent1,iEvent2: ie.InputEventPtr;
  458.   fail           : BOOLEAN;
  459.   ch             : CHAR;
  460.   cb             : ARRAY 1 OF CHAR;
  461.   str            : b.LStrPtr;
  462.   remapret       : BOOLEAN;
  463.   
  464.   PROCEDURE DoAngle(): BOOLEAN;
  465.   VAR 
  466.     tempstr: b.DynStrPtr;
  467.     l      : LONGINT;
  468.     ix     : co.IXPtr;
  469.     r      : BOOLEAN;
  470.   (* $CopyArrays- *)
  471.   BEGIN
  472.     tempstr := NIL; ix := NIL;
  473.     l:=0; r:=FALSE;
  474.     WHILE (str[l] # '\000') & (str[l] # '>') DO INC(l); END;
  475.     IF str[l] # '\000' THEN
  476.       IF b.DynExpand (tempstr, l) THEN 
  477.         st.Cut (str^, 0, l, tempstr^);
  478.         ix:=e.AllocVec (SIZE (co.IX), LONGSET{e.memClear,e.public});
  479.         IF ix # NIL THEN
  480.           str := b.StrIndex (str^, l);
  481.           IF co.ParseIX (tempstr^,ix^) # 0 THEN
  482.             y.SETREG (0, d.SetIoErr (d.objectWrongType));
  483.           ELSE  
  484.             iEvent2.class := ix.class;
  485.             iEvent2.code := y.VAL(INTEGER,ix.code);
  486.             iEvent2.qualifier := ix.qualifier;
  487.             r:=TRUE;
  488.           END;
  489.         END;
  490.       END;    
  491.     END;  
  492.     IF ix # NIL THEN e.FreeVec (ix); END;
  493.     IF tempstr # NIL THEN DISPOSE (tempstr); END;
  494.     RETURN r;
  495.   END DoAngle;
  496.   
  497.   PROCEDURE DoEsc (): LONGINT; 
  498.   VAR
  499.     c: CHAR;
  500.     n: LONGINT;
  501.   BEGIN
  502.     CASE CAP (str[1]) OF
  503.     '\"','\'','<','\\': ch := str[1]; RETURN 1; |
  504.     '0': ch := a.nul; RETURN 1; |
  505.     'N','R': ch := a.cr; RETURN 1; |
  506.     'F': ch := a.ff; RETURN 1; |
  507.     'T': ch := a.ht; RETURN 1; |
  508.     'X': 
  509.       c := CAP (str[2]);
  510.       CASE c OF '0'..'9', 'A'..'F': 
  511.         n := ORD (c) - ORD ('0');
  512.         CASE c OF 'A'..'F': n := n + 10 + ORD ('0') - ORD ('A'); ELSE END;
  513.         c := CAP (str[3]);
  514.         CASE c OF '0'..'9', 'A'..'F': 
  515.           n := n * 16 + ORD (c) - ORD ('0');
  516.           CASE c OF 'A'..'F': n := n + 10 + ORD ('0') - ORD ('A'); ELSE END;
  517.           ch := CHR (n); RETURN 3;
  518.         ELSE END;
  519.         ch := CHR (n); RETURN 2;
  520.       ELSE END;
  521.     ELSE END;
  522.     ch := '\\'; RETURN 0;
  523.   END DoEsc;  
  524.  
  525. (* $CopyArrays- *)
  526. BEGIN
  527.   iEvent1 := NIL; iEvent2 := NIL; remapret := FALSE;
  528.   IF kml.base # NIL THEN IF kml.MapRawKey (y.ADR (retEvent), cb, 1, km) = 1 THEN
  529.     remapret := cb[0] = a.cr (* is carriage return mapped to rawkey code RETURN ? *)
  530.   END; END;
  531.   str := b.StrIndex (string, 0);
  532.   LOOP
  533.     fail := FALSE;
  534.     iEvent1 := NIL;
  535.     IF ORD (str[0]) = 0 THEN RETURN NIL; END;
  536.     REPEAT
  537.       iEvent2 := e.AllocVec (SIZE (ie.InputEvent), LONGSET{e.memClear,e.public});
  538.       IF iEvent2 = NIL THEN fail := TRUE; EXIT; END;
  539.       ch := str[0];
  540.       IF (ch = '<') & (str[1] # a.nul) THEN
  541.         str := b.StrIndex (str^, 1);
  542.         IF ~DoAngle () THEN fail := TRUE; EXIT; END;  
  543.       ELSE  
  544.         CASE ch OF '\\':
  545.           str := b.StrIndex (str^, DoEsc ());
  546.         ELSE END;
  547.         CASE ch OF a.lf: ch := a.cr; ELSE END;
  548.         IF (ch = a.cr) & remapret THEN (* cr magic *)
  549.           iEvent2^ := retEvent;
  550.         ELSE  
  551.           IF ~co.InvertKeyMap (ORD (ch), iEvent2, km) THEN 
  552.             y.SETREG (0, d.SetIoErr (d.objectWrongType)); fail := TRUE; EXIT;
  553.           END;  
  554.         END;
  555.       END;
  556.       str := b.StrIndex (str^, 1);
  557.       iEvent2.nextEvent := iEvent1;
  558.       iEvent1 := iEvent2; iEvent2 := NIL;
  559.     UNTIL str[0] = '\000';
  560.     EXIT;
  561.   END;
  562.   IF fail THEN FreeIEvents (iEvent1); FreeIEvents (iEvent2); RETURN NIL; END;
  563.   RETURN iEvent1;
  564. END InvertString;
  565.  
  566. PROCEDURE InvertStringForwd* (str: ARRAY OF CHAR; km: km.KeyMapPtr):
  567.                               ie.InputEventPtr;
  568. VAR 
  569.   ie1,ie2,ie3,ie4: ie.InputEventPtr;
  570. (* $CopyArrays- *)
  571. BEGIN
  572.   ie1:=InvertString(str, km);
  573.   IF ie1#NIL THEN
  574.     ie4:=ie1; ie3:=ie1.nextEvent;
  575.     WHILE ie3#NIL DO ie2:=ie1; ie1:=ie3; ie3:=ie1.nextEvent; ie1.nextEvent:=ie2; END;
  576.     ie4.nextEvent:=NIL;
  577.   END;
  578.   RETURN ie1;
  579. END InvertStringForwd;
  580.  
  581. BEGIN
  582.  
  583. CLOSE
  584.   ArgArrayDone();
  585.  
  586. END CxLib.
  587.  
  588.