|         XyPy: Python Extensions for XyWrite              |
  |        C.L. Distefano -- v0.99 rev. 2014-10-08           |
  |   For XyWrite 4 (DOS) + XyWWWeb Jumbo U2 + Python 3.x    |
  | Download: <http://users.datarealm.com/xywwweb/XYPY.ZIP>  |

XyPy proposes a framework for using Python as a second scripting
language for XyWrite, in conjunction with XyWrite's built-in Programming
Language, XPL.

Python is a general-purpose programming language used in a wide variety
of applications. It can be downloaded for free at
<www.python.org>. Python scripts are text files, and can be
created and edited in XyWrite.

Visually, Python and XPL are a study in contrast. Python looks more or
less like plain English; its syntax and programming conventions place
high value on the readability of code. XPL, as most XyWriters know, is
dense and often inscrutable. Yet both are high-level, interpreted
languages, rich in possibilities. XyPy's premise is that these idioms
can be combined to extend XyWrite in interesting and useful ways. For
example, you can:

  * Use XyWrite as a Python shell, to launch free-standing Python
    scripts from the XyWrite command line (CMline);

  * Embed a Python script within an XPL program (XPyL), using Python
    to extend XPL's baseline capabilities;

  * Write XyWrite macros/programs in Python, as an alternative to XPL;

  * Any combination of the above.

A suite of XPL routines and a Python module (xy.py) supply the "glue"
that makes it possible. XyPy also includes tools to enhance XyWrite's
usefulness as a Python editor.

Files included in XYPY.ZIP
XYPY.NOW ... quick-start instructions
XYPY.TXT ... this documentation file
XYPY.FRM ... U2 frames to be added to your U2 file (details below)
XYPY.TPL ... template file for xy.py, used by U2 frame MAKEXYPY to
               construct the actual xy.py module (details below)

General Information
XyPy uses Python Version 3. An introductory Python 3 tutorial can be
found at <http://docs.python.org/3/tutorial/introduction.html>.
Full documentation is available at <http://docs.python.org/3/>.

XyPy presumes familiarity with XyWrite 4 for DOS, XPL, and the XyWWWeb
Jumbo U2 customization file. Visit
<http://users.datarealm.com/xywwweb/> for information and

To gain familiarity with the XyWrite Programming Language (XPL), peruse
the XyWrite Programming Language User's Guide:

If you're new to XyWrite and want to learn more about this classic DOS
word-processor, subscribe to the XyWrite Mailing List
<http://www.freelists.org/list/xywrite>. You can also browse the
list archives at <http://www.freelists.org/archive/xywrite/>. For
software history buffs, the archives of predecessor XyWrite discussion
lists, going back to 1991, are available at XySearch

XyPy Setup
Step 0
Install Python 3 (as of this writing the latest standard release is
v3.4.2, dated 8 October 2014). Download at:
During installation, at the Customize Python 3.x screen, be sure to
choose the option to "Add python.exe to search path".

If you run XyWrite in a virtual machine, make sure that python.exe is
located on a drive that is accessible to XyWrite.

Step 1
Unzip XYPY.ZIP into the directory that contains EDITOR.EXE.

Step 2
MErge XYPY.FRM into your U2 file (near top of file). Be sure to delete
any earlier versions of these frames.
Issue LOADHELP<Helpkey> to reLOAD U2.

Step 3 (Critical -- Do NOT Skip!)
Issue MAKEXYPY<Helpkey> to install the xy.py module. When you run
MAKEXYPY, it uses XYPY.TPL to create a module named "xy.py", customized
to reflect the location of EDITOR.EXE on your system. Xy.py is saved to
a Python subdirectory named ".\Lib\site-packages\xypy". (If subdirectory
"xypy" does not exist in site-packages, MAKEXYPY creates it.) Any
existing copy of xy.py in that subdirectory is overwritten without

New as of 8/11/13: MAKEXYPY automatically configures variable Python_EXE
in XYWWWEB.REG. This should work if python.exe is in the Path (see
Step 0 above). If you get an error message, add python.exe to the Path
and try again, or configure the variable manually. Do so by adding the
following stanza to XYWWWEB.REG, editing the path to python.exe as

New as of 11/30/13:
Existing users of XyPy v0.81 or later can update XyPy to subsequent
versions simply by UNZIPping the latest version of XYPY.ZIP into
Editor's directory (be sure to OVERWRITE all existing files!) and then

You're ready to roll.

XyPy Core Routines (U2 Frames)
These routines have been tested in XyWrite 4 (DOS) running under Windows
XP. They may or may not work (probably not) in Nota Bene for Windows
(the actively-developed successor to XyWrite) or in the now rare XyWrite
for Windows.

Frame PY, PYI, PYW, PY/
Execute Python code in the current XyWrite window -- a must-have for
writing and testing Python code in XyWrite! Works on a DeFined
(selected) block of code or, if nothing is DeFined, the entire file. The
file need not have the ".py" extension. Arguments can be passed to the
Python routine by adding them on the CMline; (the Python routine must,
of course, be coded to accept arguments).
Usage (with DeFined Python code or script file in current window):
PY [arg(s)]<Helpkey>
Frame PY "shells" to the Python interpreter and executes the subject
code (file or DeFined block). The session remains open after execution,
so that output or error messages can be viewed.
PYI [args]<Helpkey> is same as PY except runs script with
python.exe -i option, leaving the Python interpreter active after the
script executes
PYW<Helpkey> takes output from *DeFined* Python code and displays
this output in a new Untitled XyWrite window. The input code must adhere
to the usage requirements for XPyL embedded code (see frame XPyL,
below). To see how it works, DeFine the following block of Python code
and issue PYW<Helpkey>:

# start
import xy
xy.xywrite(b'Hello, world!') # Note bytes, not text string!
# end

# same as above, with text string instead of bytes
# note text usage for func xy.xywrite()
import xy
xy.xywrite('Hello, world!', xy.OUTFN, 'wt') # text string usage
# end

PY/<Helpkey> (note forward slash in framename) takes you directly
to the Python interpreter command prompt

Note on Using XyWrite to Edit Python Scripts
The Ascii-26 end_of_file character that XyWrite appends to saved files
is sometimes interpreted by Python as an illegal instruction, causing
some otherwise error-free scripts to fail. Annoying, yes, but there's an
easy workaround: just add a short #comment at the bottom of the script;
do NOT end the comment with a CrLf a/k/a newline! For example:

# approaching end of script...
print('Goodbye!') # <== final instruction
# end<== script ends at the end of the word "end"; do not add a

Frame PY|PYI|PY/ appends the closing comment automatically when
operating on a DeFined block. You must, however, insert the closing
comment manually at the end of any .PY script file launched with frame
- - -

Frame IDLE
Usage 1 (open Python shell in IDLE):
Usage 2 (edit file in IDLE):
IDLE [d:\path\filename_to_edit]<Helpkey>
Usage 3 (edit file in current XyWrite window in IDLE):
IDLE/<Helpkey> <== Note trailing forward slash (/) in

Launch an external Python script from within an XPL program.
Usage (in XPL code):
<SV50,[d:\path\scriptname.py] [args]>JM 2.python[/K][/X][/I]Q2 ;*;
Optional framename switches /K and /X are XyWrite's DOS command
switches. By default, the DOS switches used to launch the script are
DOS/NV/Z /C ... Specifying /K in the framename overrides the default /C
(keeps the DOS window open after the script finishes, instead of closing
it). Specifying /X adds that switch to the DOS command to freeze the
XyWrite display while the Python script executes.
Optional framename switch /I runs script in Python's interactive mode
(python.exe -i), leaving the interpreter open after the script executes

Frames PYENC and PYDEC
Encode|Decode file or DeFined block to|from Python hexadecimal byte code
Usage (with input file or DeFined block in current window):
PYENC<Helpkey> <== Encode to bytes
Encodes, e.g., sa to b'\xff\x82\xabsa\xff\x82\x7f'
PYDEC<Helpkey> <== Decode from bytes (b'...')
Decodes, e.g., b'\xff\x82\xabsa\xff\x82\x7f' to sa

Time a DeFined block of Python code
Usage (with code_to_time DeFined):
TIMEDF [setup[, number[, repeat]]]<Helpkey>
Omit quotes around setup (supplied by frame TIMEDF)
Defaults: setup='', number=1000, repeat=10
TIMEDF uses timeit.repeat and reports *minimum* value returned
For further information, see Python 3 documentation for the timeit

Frame XPyL
Run Python code embedded in an XPL program; (Python code is executed
inline at runtime)
Usage (XPL):
<SV50,[text of Python script]>;*;
JM 2.XyPL[/K][/X][//][/50][/CM|/DG|/PR|/ME|/W]Q2 ;*;
Framename switches:
/K and /X are DOS command switches (default = DOS/NV/Z /C ...). /K keeps
the DOS session open after the Python script ends (useful for
debugging). /X freezes the XyWrite display while the Python script runs.
/50, /CM, /PR, /ME, /W determine how output from the Python script is
handled in XyWrite:
/50 directs output to Save/Get 50
/CM puts output on the CMline
/DG displays output in DialoG box (good for single-screen output)
/PR displays output as a PRompt (via S/G 50; i.e., "/PR" implies "/50")
/ME merges output into the current file at the cursor location
/W displays output in a new XyWrite Untitled window
Use switch "//" when no output is expected from embedded Python code.
It can be merged with /X or /K, thus: 2.XPyL//X

- Exit codes (S/G 61 out):
-1: Python not installed or missing Reg var Python_EXE
 0: Python routine not launched
 1: No output
 2: Output received

XPyL: Python Coding Conventions
Embed your Python code in Save/Get 50, immediately before the call to
JM 2.XPyL[/switches]Q2 (see frame XPyL usage above).

Use single-quotes ('') for Python quoted strings, to avoid confusion
with XPL's required double-quotes.

If your Python script returns output to XyWrite, or if it uses any of
XyPy's custom functions (details below), the first line of your embedded
code (in S/G 50) should import the XyPy module (xy.py):

import xy

If the script is expected to return output to XyWrite, the next line of
the embedded script should be:

# or simply

This sets a flag file ("xpyldata.ok", located in the directory that
contains EDITOR.EXE) to inform Editor that output is pending.

Output may be written to a file using XyPy's custom variable xy.OUTFN,
which points to a file called "xpyldata.out" located in Editor's
directory. If the output to be imported into XyWrite is a formatted
document or XPL code, don't attempt text decoding in Python; instead,
use binary data (Python byte strings), and let XyWrite do the decoding
for you. (Frame PYENC, described above, is handy for converting text and
XPL into Python byte strings.) 

After the line in your Python script that writes output to a file, add
the following line to remove the output flag file (usually at or near
the end of the embedded script):


Thus, a basic template for writing binary output is as follows:

import xy
spam = b'Better than ham!' # Note byte string, not string!
with open(xy.OUTFN, 'wb') as f:

Or, more compactly, using xy.py's xywrite function to write the output

import xy
spam = b'Better than ham!'

As an alternative to importing the entire xy.py module, you can import
only the xy.py objects required by your embedded code. The imported
object names become part of the namespace of your routine -- meaning, in
practical terms, that you must (1) omit the "xy." prefix from variable
and function names, and (2) guard against name clashes with variables
and other object names used in your routine. Using individual object
imports, we can rewrite the previous example thus:

from xy import outok, xywrite
spam = b'Better than ham!'

Be aware of XyWrite's programming memory limitations when stuffing
Python code into Save/Get 50. For longer scripts, use pithy variable
names and minimal indentation (e.g., one blank space per indent) to
conserve Save/Get memory.

XyPy includes several U2 routines as working examples of "XPyL". See
"Demo Frames", below.

Using Python to Manufacture XPL
Sometimes the data produced by your embedded Python script will be XPL
code, to be run when control is handed back to XyWrite, or perhaps a
formatted document, to be saved to disk and CAlled in XyWrite. To
manufacture XPL or XyWrite formatted text in Python, you will need to
use Python byte strings. The XyPy module, xy.py, includes functions that
produce byte strings corresponding to XyWrite embedded commands,
functions, SEarch wildcards, printable 3-byte characters, and XyWrite's
extended character set (Speedo characters). Some of these functions can
be seen in action in the two numbered demos below; read them in
XyWrite's eXPanded view.

Demo 1 uses a "for" loop and xyfunc() to assign byte codes for functions
BC (Blank the Command line) and GT (Go to Text, i.e., move cursor to
text area) to Python variables of the same name. It then manufactures a
snippet of XPL code that (for demo purposes) opens an Untitled window in
eXPanded view, writes some dummy text, waits 5 seconds, then ABorts the
window. In Demo 1, the constructed code is *displayed* in an Untitled
XyWrite window, using frame XPyL/W (cf. Demo 2, below); manually ABort
the window when you're done viewing the code. To run this demo, DeFine
the entire block of code and command RUNCODE<Helpkey>, or simply
command DEMO1<Helpkey>:

;*; Demo 1 (Python-made XPL code, displayed)
from xy import OUTFN, outok, xywrite, xyfunc, xycm, fBX

for varname in 'BC', 'GT':
  exec(varname + ' = xyfunc(varname)')

xywrite(fBX('es 1') + xycm(b'SX01,\xaeVA$NW\xaf') + 
        fBX('d nw=3', 'ne/100') + 
        BC + b'This Untitled window will ABort in 5 seconds...' + 
        GT + b'[Nothing here in the text window]' + 
        xycm('PRWaiting...') + fBX('p 5', 'ab') + 
        fBX(b'd nw=\xaePV01\xaf') + BC + GT + xycm('PRDone', 'EX'))

2.XPyL/X/WPRABort window after viewingEX
;*; end Demo 1

It works!

Demo 2 manufactures the same code, but this time instead of displaying
it, it *executes* the XPL upon returning to XyWrite. This is
accomplished by saving the manufactured code to Save/Get 50 (using frame
XPyL's /50 switch), and then executing it with <PV50>. To run this
demo, DeFine the entire block of code and command
RUNCODE<Helpkey>, or command DEMO2<Helpkey>:

;*; Demo 2 (Python-made XPL code, executed)
from xy import OUTFN, outok, xywrite, xyfunc, xycm, fBX

for varname in 'BC', 'GT':
  exec(varname + ' = xyfunc(varname)')

xywrite(fBX('es 1') + xycm(b'SX01,\xaeVA$NW\xaf') + 
        fBX('d nw=3', 'ne/100') + 
        BC + b'This Untitled window will ABort in 5 seconds...' + 
        GT + b'[Nothing here in the text window]' + 
        xycm('PRWaiting...') + fBX('p 5', 'ab') + 
        fBX(b'd nw=\xaePV01\xaf') + BC + GT + xycm('PRDone', 'EX'))

;*; end Demo 2

And the manufactured snippet of XPL works, too. QED.

Note that by using PYENC to byte-encode the output from Demo 1, you can
rewrite the XPL code as raw bytes, as follows:

;*; Demo 1a (Python-made XPL code, displayed)
from xy import outok, xywrite

xywrite(b'\xff\x82\xabes 1\xff\x82\x7f\xaeSX01,\xaeVA$NW\xaf\xaf\xff\x82\xabd nw=3\xff\x82\x7f\xff\x82\xabne/100\xff\x82\x7f\xff\x81\x1fThis Untitled window will ABort in 5 seconds...\xff\x80}[Nothing here in the text window]\xaePRWaiting...\xaf\xff\x82\xabp 5\xff\x82\x7f\xff\x82\xabab\xff\x82\x7f\xff\x82\xabdnw=\xaePV01\xaf\xff\x82\x7f\xff\x81\x1f\xff\x80}\xaePRDone\xaf\xaeEX\xaf')

2.XPyL/X/WPRABort window after viewingEX
;*; end Demo 1a

This works the same as the original Demo 1, and will run slightly
faster. (DeFine the demo code block and command RUNCODE<Helpkey>
to run it; or command DEMO1A<Helpkey>.) The trade-off is that the
raw byte code is less identifiably XPL-like.

Demo Frames
XyPy includes U2 frames that show XPyL in action. Some of these routines
serve mainly as proof-of-concept; others are actually useful. Feel free
to suggest or write real-world applications of your own. The included
frames are:

BCONV number, base_in, base_out<Helpkey>
Base-to-base conversion for base 2 - 36
Output on PRompt line and in S/G 50

WCPY [d:\path\filename]<Helpkey>
Performs word count on current file, specified file, or DeFined block

LISTWORDS [d:\path\filename]<Helpkey>
Compiles a sorted list of all unique words in current or specified file;
also reports average word length

LISTCP 'string'<Helpkey> [single quotes required!]
List all (case-sensitive) occurrences of 'string' in current file
(CharPos and snippet of text containing 'string').

LSTCP 'string'<Helpkey> [single quotes required!]
List bare CharPos of all (case-sensitive) occurrences of 'string' in
current file (faster than LISTCP)

JMPCP 'string'<Helpkey> [single quotes required!]
JuMP to CharPos of each (case-sensitive) occurrence of 'string' in
current file (simulates native SEA command). There is a 500-hit maximum
to avoid out-of-memory anomalies in XyWrite

Use Python to construct and list all XyWrite 3-byte functions in an
Untitled window

Use Python to construct and list a full set of 3-byte reverse-video
Ascii chars (a subset of which constitutes XyWrite's SEarch wildcards)
in an Untitled window

Use Python to construct and list a full set of XyWrite's printable
3-byte character set (including characters that cannot be displayed or
printed in XyWrite in 1-byte form)

Use Python to construct and list a full set of XyWrite's Speedo
character set

GOOPY [search terms]<Helpkey>
Google search

Read your e-mail messages in a XyWrite Untitled window
Note 1: You will be prompted for your POP server name and e-mail
address. Alternatively, this information can be supplied by setting user
variables in XYWWWEB.REG, like this:
Note 2: You will be prompted for a password. Exercise caution when
entering it as the password will be displayed!

PY3 [search terms]<Helpkey>
Search Python 3 documentation via Google

- - -
UH-HUH: Unlimited Hint
These companion routines extend Robert Holmgren's command Stack utility
by enabling unlimited command archiving and retrieval. Stack's command
history can hold about 200 commands in memory. That sounds like a lot,
but in an active editing session useful commands are quickly pushed down
and out of the stack. Although Stack has a facility for saving the
command history to a file for later retrieval, the number of commands in
any such file is still subject to the (approximately) 200-command limit.
UH-HUH overcomes this limitation, enabling *all* commands to be archived
permanently to disk, to be retrieved at any time by Stack's trademark
method of providing a "hint", or substring, of the desired command. (UH
stands for Unlimited Hint, HUH for Have Unlimited Hint.)

The catch is that you *must* establish frame HUH in an XPL routine that
you run *every time you quit XyWrite*. (The Jumbo U2 includes such a
routine: FINITO). Do this by adding the following line to FINITO or your
own routine:


New (7/28/13): XyPy now includes frame FINITO so modified. (If Python or
Stack is not detected, HUH backs off quietly.)

Alternatively, you can assign HUH to a key, along with the QUIT command,
in the KBD file:

HUH can also be assigned to another key to allow easy updating of
STACKALL.SAV during editing sessions. Alternatively, you can manually
issue HUH<Helpkey> after Stacking a key command to ensure that it
is permanently archived.

Every time you run HUH, the command stack is merged into STACKALL.SAV,
located in Editor's directory. STACKALL.SAV is a sorted listing of
archived commands (duplicate commands are purged). To retrieve commands
from STACKALL.SAV, use frame UH to issue a (case-INsensitive) hint, or
fragment of the desired command; for example:
UH .txt<Helpkey> [list all commands containing string '.txt']

A sorted list of commands containing the hint is opened in a new XyWrite
window. To pop any command to the CMline, place the cursor on the
desired command and hit <Helpkey>. Once popped and reSTACKed,
archived commands re-enter the active command Stack and become
accessible by scrolling through the Stack (Up|Down arrow keys or frame
STACKBOX) or normal Stack hinting.

New (7/29/13): UH now works on non-Python setups; STACKALL.SAV must be
present in Editor's directory. Python is still required to run HUH,
which creates and updates STACKALL.SAV, but you can now enjoy UH on
Python-less systems simply by copying your STACKALL.SAV to Editor's

New (10/16/13): HUH now also works on non-Python setups, via a new frame
HUHXPL, which is called by HUH if a Python installation is not detected.
As a result, a STACKALL.SAV created on a Python-enabled Xy setup can be
copied to a non-Python XyWrite directory and used interchangeably on the
two setups. See also frame ADDSTACKALL (immediately below), which
enables STACKALL.SAV archives created on different XyWrite setups to be
merged into a single STACKALL.SAV, useable on all setups.

New (10/27/13): Frame ADDSTACKALL enables merging a separate
STACKALL.SAV-type file into STACKALL.SAV. Command-line usage is:
  ADDSTACKALL [d:\path\filename]<Helpkey>
where "d:\path\filename" is the file to be merged into STACKALL.SAV.
The file-to-be-merged must be formatted either one command to a line
(like STACKALL.SAV) or Ascii-190[command]Ascii-190[command]Ascii-190
..., like the native command stack created by STACK.PM (located in
Save/Get 623). Use ADDSTACKALL to merge STACKALL.SAV files from multiple
XyWrite setups into a single STACKALL.SAV. I use ADDSTACKALL to keep my
home and work copies of STACKALL.SAV in sync.

New (10/30/13): STACKALL.SAV now lists each command on a separate line
(ending with CrLf) instead of using STACK.PM's native Ascii-190 command
separator. The new format makes it easier to browse and manually add
commands to, or delete commands from, STACKALL.SAV. Also new:
UH<Helpkey>, with no hint/argument, CAlls STACKALL.SAV for viewing
or editing.

New (11/01/13, 11/11/13): New frame ADDCM allows the current CMline or a
DeFined block to be added to the STACKALL.SAV command archive whether or
not STACK.PM is not installed. ADDCM must be assigned to a key of your
choosing in the KBD file, with:
Hit your dedicated ADDCM key to add the current CMline to STACKALL.SAV.
(Stack users can use ADDCM as well.) ADDCM opens up the possibilities of
UH-HUH unlimited hinting to all Xy4 users, whether or not STACK.PM is
installed -- and whether or not Python is installed as well.

New (11/21/13): The pure-XPL version of UH-HUH now uses Christian Maas's
CMSORT.EXE utility (freeware) for Windows to sort the command archive --
if and only if CMSORT.EXE is present in Editor's directory (otherwise
XyWrite's native SORT command is still used). CMSORT.EXE overcomes out-
of-memory anomalies that sometimes appear when the native SORT command
is used against longer files. If you are running the pure-XPL (non-
Python) version of UH-HUH on your Win32 system, it is recommended that
you download the CMSORT ZIP archive and unzip all files into the
directory that contains EDITOR.EXE (only CMSORT.EXE is required, but the
ZIP includes a helpful manual [PDF] and README file). For further
information see:
(download link is at bottom of page)

New (11/21/13): Added frame AHUH (Aleatory HUH), which runs frame HUH
probabilistically (1 in 10|50|100 times; default = 1/100). Assign AHUH
to one or more keys, or embed a call to AHUH in XPL routines, to ensure
that the STACKALL.SAV command archive is regularly, yet unobtrusively,
updated. For example, to assign AHUH to your SAve key:

UH-HUH really does provide unlimited hinting. The STACKALL.SAV archive
can grow indefinitely, and may come to contain many hundreds or even
thousands of commands. In my tests with a manufactured STACKALL.SAV
archive with 130,000+ commands, UH instantaneously processed a hint that
returned over 2,000 matching commands; a hint that returned more than
70,000 matching commands took just a couple of seconds to complete. HUH
had no trouble removing duplicates from this 2 Meg archive, immediately
reducing it to a 20KB file of about 1300 commands. UH-HUH, it works!

UHDEL [string]<Helpkey>
Delete all commands containing string from STACKALL.SAV command history
archive file. Two (2) available windows required. UHDEL also works on
non-Python Xy setups; STACKALL.SAV must be present in Editor's
- - -

WEB [url]<Helpkey>
- Open URL in the default browser.
- URL can be specified on the CMline or DeFined in text. If nothing is
DeFined and the cursor is in text, frame WEB tries to "lift" the URL (if
any) under the cursor. If the URL does not specify a protocol, 'http://'
is prepended.
Frame WEB uses Python's webbrowser module to create a Xy CMline-Web
interface in two (2) lines of XPyL code!
WEB [alias]<Helpkey>
[Alias] is a single-word abbreviation for a full URL. Set your aliases
by adding lines to XYWWWEB.REG, thus:
Then, for example:
WEB monty<Helpkey> opens <http://pythonline.com/>
Automatic alias: Single-word URLs not in dictionary are rendered as
www.URL.com. E.g.: WEB ebay<Helpkey> opens

LOOKUP [YA][suffix] search_term(s)<Helpkey>
Web lookup via U2 frame MDSOLBLinkSV50,YA2.HelpYA*MDNM

YA__ framenames must be correctly configured in the XyWWWeb REGistry
(XYWWWEB.U2). Command HELP YA<Helpkey> for further information.

VERITAS [keyword(s)]<Helpkey>
Wine|Liquor search via wine-searcher.com

MONTYPY [number_of_doors [iterations]]<Helpkey>
Demonstrates the Monty Hall Paradox, a famous probability brain-teaser.
The classic statement of the problem is:
`Suppose you're on a game show, and you're given the choice of three
doors: Behind one door is a car; behind the others, goats. You pick a
door, say No. 1, and the host, who knows what's behind the doors, opens
another door, say No. 3, which has a goat. He then says to you, "Do you
want to pick door No. 2?" Is it to your advantage to switch your
choice?' (See <http://en.wikipedia.org/wiki/Monty_Hall_problem>.)
Frame MONTYPY helps you find (and understand) the answer by simulating
10,000 random repetitions of the game situation and reporting the number
of times the car is behind the first door chosen versus the alternative
door. MONTYPY further illustrates the problem more generally by allowing
you to run the simulation with more than 3 doors and more (or fewer)
than 10,000 repetitions.

ZAPPY [d:\path\filename]<Helpkey>
Zap Ascii-26 End-of-File character
Works on current file, file specified on CMline, or filename under
  cursor in DIRectory listing
(XPyL equivalent of U2 frame ZAP26)

GSFPY[/50|/CM|/ME|/PR|/W] [Long_Filename]<Helpkey>
GSFPY[/50|/CM|/ME|/PR|/W]<Helpkey> with [Long_Filename] DeFined
SV50,Long_Filename2.gsfpy/50;*; S/G 50 in and out
Returns Windows Short Filename; framename switch determines output mode
/50 = S/G 50 out; /CM = put on CMline; /ME = MErge in text at cursor
position; /W = put in new text Window; /PR = display on PRompt line

The XyPy Module
The XyPy module (xy.py) enables the following Python variables, which
can be used in any XPyL routine or freestanding Python script:

EDDIR = Directory containing EDITOR.EXE (without trailing slash)
        Note: EDDIR is set automatically by frame MAKEXYPY
CMFN = EDDIR + '/cml.sav'       # File containing CMline at runtime
INFN = EDDIR + '/xpyldata.in'   # File for input from XyWrite
OUTFN = EDDIR + '/xpyldata.out' # File for output from Python script
OKFN = EDDIR + '/xpyldata.ok'   # Output ready flag file
bLG = b'\xAE'                   # Byte code for 1-byte left guillemet
bRG = b'\xAF'                   # Byte code for 1-byte right guillemet

To gain access to these variables, you must import the module into your
Python routine (see discussion of XPyL coding, above).

Recall that Python module, function, and variable names are case-
sensitive. In accordance with Python coding convention, some xy.py
variable names are uppercase because their values are intended to be
constants -- and should be treated as such by the user. If your script
calls for these values to be changed, manipulate a *copy* of the
variable's contents or assign a copy to a new variable; for example:

NewOutFn = xy.OUTFN[:] # provided variable type supports indexing!

Then manipulate the new variable.

Note: Since xy.py imports the os module, scripts that import xy can
access methods in os with xy.os.method_name(). E.g.,

XyPy Custom Functions
The xy.py module includes functions that provide a Python syntax for
constructing XyWrite commands, control characters, and XPL code in
Python. (Some of these functions are illustrated in the two XPyL demo
examples above.) After importing xy.py, you can invoke any function with
"xy.funcname(arg)" --- or simply "funcname(arg)" if the function has
been imported with the "from xy import funcname" usage. The functions in
xy.py are:

Read XyWrite input file (minus EOF)
var = xyread(filename_in, mode)
Default filename is 'xpyldata.in', in directory that contains
EDITOR.EXE. Default mode is read binary ('rb').
Returns input file contents if read succeeds, else returns None.

Write file for output to XyWrite
var = xywrite(bytes_out, 'd:/path/filename_out', mode)
Default filename is 'xpyldata.out', in directory that contains
EDITOR.EXE. Default mode is write binary ('wb').
Returns 1 if write succeeds, else returns None.

Fetch byte code representing the contents of the XyWrite command line
(CMline) at runtime. The XPL portion of the routine must first save the
CMline to file CML.SAV, located in Editor's directory. (xy.py global
variable CMFN points to this filename.) The XyWWWeb U2 convention for U2
frames is to pass the CMline via Save/Get 50; hence the required code
would look like this:
SX01,IS50;*; Hand CMline to S/G 01
SX50,VA$ED2.GetPath;*; Save Editor's dir to S/G 50
sa %01,PV50\CML.SAV;*; Save 01 to CML.SAV in that dir
Then, in the Python portion of your routine, you can save the CMline to
a variable, thus:
cmline = xy.GetCMline() # var cmline has the current command line

Returns bytes for XyWrite embedded command(s)
Note: cm_in must be all Ascii 32-127 text or else hex byte code
>>> LMargin = xy.xycm('LM5DI')
#   same as:
#   LMargin = b'\xAELM5DI\xAF'
>>> Margins = xy.xycm('LM5DI', 'RM75DI')
#   same as:
#   Margins = b'\xAELM5DI\xAF\xAELM75DI\xAF'

Return bytes for "command [args]" for one or more commands
Each command must be all Ascii 32-126 text or else hex byte code
>>> xy.fBX('lm 5di')
# Returns byte code for "lm 5di", i.e.:
# b'\xFF\x82\xABlm 5di\xFF\x82\x7F'
>>> xy.fBX('lm 5di', 'rm 75di')
# Returns byte code for "lm 5dirm 75di", i.e.:
# b'\xFF\x82\xABlm 5di\xFF\x82\x7F\xFF\x82\xABrm 75di\xFF\x82\x7F'
# If command is not plain Ascii text (32-126), input bytes instead
# E.g., instead of xy.fBX('se /th/'), use:
#                  xy.fBX(b'se /\xFF\xC0\xB3\xFF\xC0\xCEth/')
# Use frame PYENC to convert to hex byte code!

Return bytes for 3-byte function corresponding to one or more XyWrite
2-character function mnemonics
Assign byte code for func 'BC' to Python variable BC:
>>> BC = xy.xyfunc('BC')
#   same as:
#   BC = b'\xFF\x81\x01'
Assign byte code for }?;:
>>> myfuncs = xy.xyfunc('BC', 'GT', 'DO', 'FF')
#   same as:
#   myfuncs = b'\xFF\x81\x1F\xFF\x80\x7D\xFF\x81\x3F\xFF\x81\x3B'

Returns bytes for reverse-video wildcard character(s), specified
by (case-sensitive) 1-byte Ascii counterpart(s) or by Ascii-number
(0 - 255)
>>> wildX = xy.xywild('X')       # same as wildX =
>>> wildX = xy.xywild(88)        # same as above
>>> wild3X = xy.xywild('3', 'X') # same as wild3X =
>>> wild3X = xy.xywild(51, 88)   # same as above

Returns bytes for one or more XyWrite 3-byte printable chars (0 - 255);
arg = 1-byte Ascii char(s) or integer(s) in range 0 - 255
>>> my3byter = xy.xy3bchar('A')  # same as my3byter =
>>> my3byter = xy.xy3bchar(255)  # same as my3byter = b'\xFFFF'
>>> my3byters = xy.xy3bchar('A', 'u')  # same as:
                                         my3byter = b'\xFF41\xFF75'
>>> my3byters = xy.xy3bchar(254, 255)  # same as:
                                         my3byter = b'\xFFFE\xFFFF'

Returns bytes for XyWrite 3-byte Speedo char(s) (256-1023), specified by
>>> mySpeedo335 = xy.xychar(335)
#   same as:
#   mySpeedo335 = b'\xFEO\x00' # bytes for 'O'
>>> mySpeedos = xy.xychar(256, 257, 258)
#   same as:
#   mySpeedos = b'\xFE\x00\x00\xFE\x01\x00\xFE\x02\x00'
#               bytes for ''

Get byte contents of command history archive file STACKALL.SAV
(Used by frame UH)

Write byte contents of command history archive file STACKALL.SAV
(Used by frame HUH)

bconv(num, base_in, base_out)
Base-to-base number conversion (base 2 - 36)
Input number may be int if all numerals, else must be str
Default base_in = 16, default base_out = 10
bconv(101, 2, 8)
bconv('1g2f', 17, 16)

Revision History
08/22/14 (v0.98):
Undid tweak to frame GETM (Getmail)
08/22/14 (v0.97):
Tweak to frame GETM (Getmail)
06/22/14 (v0.96):
Bug in frame WCPY caused stray text to be written to screen -- fixed
05/27/14 (v0.95):
Updated XYPY.TXT to refer to latest standard release of Python 3
Default Python stanza name in XYWWWEB.REG changed from
  "[Python: Python3]" to "[Python]" (either one works; stanza name
  can be omitted: XYWWWEB.REG stanza names are non-functional and
  entirely optional)
Revised frame XyPyREG to recognize existing stem variable
  Python_EXE.1, Python_EXE.2, etc.
02/10/14 (v0.94):
Added frame FORMATD* (format digits), used in frame UH to display
formatted number of archived commands when no arg specified
02/01/14 (v0.93):
Frame POPCM translates 5-byte chars into 3-byte chars (via new frame
STR5TO3) before popping string to CMline
01/26/14 (v0.92):
Frame UH handles Speedo chars (Ascii-254 + byte2 + byte3) in hints, and
transforms 5-byte versions of Speedo chars to the corresponding 3-byte
char when listing hint results (applies only to XPyL version of UH, not
to pure-XPL frame UHLIST)
01/06/14 (v0.91):
Code economies/tweaks to function bconv in xypy.tpl
01/06/14 (v0.90):
Frame XPyL has option /DG to display output in DialoG box (via frame
Added frame MONTYPY (fun with the Monty Hall Paradox)
12/29/13 (v0.89):
Added frame GSFPY* (get Windows Short FileName)
12/29/13 (v0.88):
Updated XYPY.TXT to refer to latest standard release of Python 3
12/26/13 (v0.87):
Tweaks to frame XPyL; substantial rewrite of frame UPDATEXYPY
12/15/13 (v0.86):
Frame MAKEXYPY ChDirs to Editor's directory before running XYPY.BAT
(work around problems when a drive letter is SUBSTituted for Editor's
12/12/13 (v0.85):
Tweak to frame POPCM
12/12/13 (v0.84):
Frame XYPYVER saves XyPy version number to S/G 50; new framename
XYPYVER/[NV] suppresses PRompt on EXit
Frame UPDATEXYPY calls frame XYPYVER/NV to report updated XyPy version
number; other small tweaks to frame UPDATEXYPY
Frame HUH (now HUH,HUH/*) enables new framename HUH/[NV] to suppress
PRompt on EXit
Frame HUHXPL (now HUHXPL*) enables new framename HUHXPL/[NV] to suppress
PRompt on EXit
Frame AHUH now calls HUH/NV (or HUHXPL/NV if Python not installed)
12/07/13 (v0.83):
New frame XYPYVER reports version number of the installed XyPy
12/01/13 (v0.82):
Tweaks to frames AHUH (runs HUH 1 in 500 times), HUHXPL (tests for Win32
OS before using CMSORT.EXE)
11/30/13 (v0.81):
New frame UPDATEXYPY updates existing XyPy installation with a single
command (after UNZIPing latest XYPY.ZIP into Editor's directory); will
allow easy updating to future releases of XyPy or via the forthcoming
XYWWWEB.U2 v120 or later versions of U2. UPDATEXYPY calls new frame
MEXYPY, which MErges XYPY.FRM into U2, (replacing existing frames) and
reloads U2
Revisions to frames UH, UHLIST
11/24/13 (v0.80):
Revised frame UHLIST (pure-XPL variant of UH) to support listings of
arbitrarily long lines. Concomitantly, revised frame ADDCM to eliminate
79-byte limit on DeFined blocks (now, a DF block of any length can be
added to STACKALL.SAV with ADDCM)
11/22/13 (v0.79):
Small revision to frame UH
11/21/13 (v0.78):
Revised frame STACKBOX is bundled with XyPy (included in XYPY.FRM); also
included frame R?:* (RC|RK with workaround for UnDo), for compatibility
Pure-XPL version of frame HUH (frame HUHXPL) now uses Christian Maas's
freeware CMSORT.EXE for Win32 to sort the command archive, if and only
if CMSORT.EXE is present in Editor's directory (otherwise XyWrite's SORT
command is still used). CMSORT.EXE overcomes a bug (failure to remove
duplicate items despite default SK=4) that sometimes occurs when the
native SORT command is used with larger files
Added frame AHUH (Aleatory HUH), which runs frame HUH probabilistically
(1 in 10|50|100 times). Assign to key(s) or embed in XPL routines to
ensure that the STACKALL.SAV command archive is regularly, yet
unobtrusively, updated
11/17/13 (v0.77):
Further revisions to frame HUH
11/15/13 (v0.76):
Bug fix for frame UH if Stack.PM not installed; revision to frame HUH
11/12/13 (v0.75):
Added frame VERITAS (Wine|Liquor search via wine-searcher.com)
11/11/13 (v0.74):
Frame ADDCM now adds DeFined block (max. length 79 bytes) to
STACKALL.SAV (DF block takes precedence over CMline if length of DF
block is < 80)
11/09/13 (v0.73):
Added frame LOOKUP (Web lookup via frame YA* framename + search_term)
11/09/13 (v0.72):
Revised xy.py function xy3bchar to use '{}'.format()' string formatting
of hexadecimal values
11/09/13 (v0.71):
Revisions to frames HUH and HUHXPL to ensure that STACKALL.SAV is sorted
even if STACK.PM not active
11/05/13 (v0.70):
Frame POPCM (copy line of text under cursor to CMline) now calls frame
PUTDF-CM to ensure proper handling of all 3-byte chars
11/04/13 (v0.69):
"Hint" (argument) not passed from frame UH to frame UHLIST when Python
not detected -- fixed
11/02/13 (v0.68):
Improved error-checking in frame ADDCM; small revision to frame
11/01/13 (v0.67):
Added frames ADDCM (add current CMline to STACKALL.SAV) and POPCM (pop
line under cursor to CMLine): When assigned to a key, ADDCM enables
archiving of individual commands in STACKALL.SAV even if STACK.PM is not
installed. POPCM moves command under cursor in STACKALL.SAV up to the
10/30/13 (v0.66):
Revised frames UH, HUH and related frames to support formatting of
STACKALL.SAV with one command to a line (ending with CrLf).
UH<Helpkey> (no hint) now CAlls STACKALL.SAV for viewing or
10/28/13 (v0.65):
Revised frame HUHXPL to capture SORT error
10/27/13 (v0.64):
Added frame ADDSTACKALL (Merge external STACKALL.SAV-type command
archive and/or current Stack (S/G 623) into STACKALL.SAV); revised frame
HUHXPL to use file-based SORTing (much faster!)
10/24/13 (v0.63):
Fixed error in frame HUHXPL
10/16/13 (v0.62):
Added frame HUHXPL, a pure-XPL version of frame HUH (Have Unlimited
Hint) to XYPY.FRM; HUHXPL is called by HUH if (and only if) Python is
not installed
10/13/13 (v0.61):
Revisions to funcs xycm and fBX (xypy.tpl); revised Demos 1, 2 and 1a in
XYPY.TXT (and frames DEMO# and DEMO1A in XYPY.FRM)
10/13/13 (v0.60):
Revised frames UH and HUH to use bytearray methods to manipulate bytes
directly; deleted functions Bytes2Str and Str2Bytes from xypy.tpl as
10/12/13 (v0.59):
Further revisions to funcs Bytes2Str and Str2Bytes
10/12/13 (v0.58):
Revised funcs Bytes2Str and Str2Bytes to fix error in handling internal
single quotes (')
09/14/13 (v0.57):
Revised frame PYDEC to correct decoding of encoded Python byte strings
enclosed in double quotes; tweaks to frame WEB
09/02/13 (v0.56):
Frame WEB revised to handle local files (e.g., from DIR listing)
09/01/13 (v0.55):
Added frame GETM (read e-mail in XyWrite Untitled window)
08/31/13 (v0.54):
Tweaked examples for xy.py functions Bytes2Str and Str2Bytes
08/30/13 (v0.53):
Revisions to frame HUH and xy.py func PutStackall (trap 0-byte stackall
08/26/13 (v0.52):
Minor revisions to XYPY.TXT
08/25/13 (v0.51):
Revised functions xycm and xyfunc in xy.py (use list concatenations
instead of .append to create the returned bytearrays); other light
revisions to xy.py
Added frames DEMO# and DEMO1A, which run Demos 1, 2 and 1a in XYPY.TXT.
Revisions to XYPY.TXT
08/25/13 (v0.50):
Frame UH: reinstated support for hints with 3-byte chars (requires 3- to
5-byte transformation)
xy.py functions GetCMline and GetStackall zap Ascii-26 EOF character
before return
08/24/13 (v0.49):
Added functions Bytes2Str and Str2Bytes to xy.py; these functions are
now used in frames UH and HUH, making the embedded Python scripts more
08/24/13 (v0.47, 0.48):
Frames UH and HUH now use split/join procedure for stripping byte string
to bare string (fast!)
08/24/13 (v0.46):
Revised frames PYENC and PYDEC
Frames UH and HUH now check for presence of Ascii-26 EOF in STACKALL.SAV
rather than assuming it is there
08/23/13 (v0.45): Eliminated unnecessary import from frame HUH (XPyL)
08/21/13 (v0.44): Revised XYPY.TXT to reflect further testing of UH-HUH
unlimited command Stack save-and-retrieve utilities
08/21/13 (v0.43): Revised XYPY.TXT
08/19/13 (v0.42):
Added PYW usage to frame PY: displays output from Python code in new
Untitled XyWrite window
08/18/13 (v0.41):
Frame ZAPPY (Zap Ascii-26 EoF character) now avoids reading entire file
into memory if no Ascii-26 EoF is present
08/18/13 (v0.40):
Added frame SPEEDOSPY (write full set of Speedo chars 256-1023); cf.
08/16/13 (v0.39):
Light revisions to XYPY.TXT
08/14/13 (v0.38):
Added usage note for frame PY re need to add a closing #comment to
Python scripts after editing & SAving in XyWrite, to neutralize effect
of Ascii-26 EOF added by XyWrite
08/13/13 (v0.37):
Revise MAKEXYPY to trap error if Python not installed or RegVar
Python_EXE not configured
08/12/13 (v0.36):
Frame XyPyREG tests for existence (not content) of RegVar Python_EXE, to
avoid overwriting a complex assignment that evaluates to a (possibly
empty) S/G 99; XYPY.BAT, created by frame MAKEXYPY, now creates emptly
xy.py before writing the module file, to avoid XCOPY's File or Directory
08/11/13 (v0.33, v0.34, v.035):
Substituted new frame SWAPSLASH for SWAP$ to substitute forward slash
for backslash in filenames; further revisions to XYPY.TXT; frame ZAPPY
now accepts point-and-shoot filename from DIRectory listing; revised
function xyfunc() in xy.py
08/11/13 (v0.31, v0.32):
Revised setup instructions; light revisions to xy.py; revisions to
08/11/13 (v0.30):
Added frame XyPyREG, enabling hands-off configuration of variable
Python_EXE in XYWWWEB.REG (XyPyREG is called by MAKEXYPY)
08/10/13 (v0.29):
Revised frame TIMEDF to support multi-level indents in DeFined code (up
 to 6 levels assuming 2 spaces per indent)
08/10/13 (v0.28):
Revised frame TIMEDF
08/09/13 (v0.27):
Added frame TIMEDF (simple Python code timer); revisions to XYPY.TXT
08/08/13 (v0.26):
Revisions to XYPY.TXT
08/07/13 (v0.25):
Alternate Python code added to frame WILDPY; light revisions to XYPY.TXT
08/06/13 (v0.24):
Added alternate coding methods to frames FUNCSPY, WILDPY, and 3BYTERSPY
Revisions to XYPY.TXT; plain-text version of XYPY text posted to XyWWWeb
as XYPYDOC.TXT (http://users/datarealm.com/xywwweb/xypydoc.txt)
08/05/13 (v0.22, 0.23):
Minor tweaks to xy.py (calls to built-in function value() replaced with
calls to isinstance()); revisions to XYPY.TXT.
08/04/13 (v0.21):
Added U2 frame IDLE (start IDLE GUI from CMline)
08/04/13 (v0.20):
Added frame PY3 (Google the Python 3 Standard Library documentation)
08/04/13 (v0.19):
Added function bconv to xy.py and frame BCONV to XYPY.FRM (base-to-base
conversions for base 2-36)
08/02/13 (v0.18):
Revised frame HUH; light revisions to XYPY.TXT
08/01/13 (v0.17):
Added functions xyread() and xywrite() to xy.py; various U2 frames
revised to make use of these functions
07/31/13 (v0.16):
Minor revisions to various U2 frames
Deleted unnecessary global variable statements at top of xy.py
07/29/13 (v0.15):
Streamlined URL dictionary code in frame WEB
Frames UH (Stack Unlimited Hinting) UHDEL (Stacked-command delete
facility) now work whether or not Python is installed (STACKALL.SAV must
be in Editor's directory). Python is still required to run frame HUH,
which updates STACKALL.SAV
Tweaks/corrections to frame UH
07/29/13 (v0.14):
Added frame PYDEC, companion to PYENC
07/28/13 (v0.13):
Added frame UHDEL (delete commands from STACKALL.SAV command history
07/27/13 (v0.12):
Deleted frame CHECKFORXYPY (unfinished, unused). Minor edits to
07/27/13 (v0.11):
Functions GetCMline(), GetStackAll(), PutStackall() added to xy.py
module. New U2 frames UH and HUH (Unlimited Hinting from command archive
file STACKALL.SAV; also included: frame FINITO, revised to call HUH (to
archive the Stack) before QUITting
07/26/13 (v0.10):
Removed frames PYENCODE and PYDECODE (PY??CODE), and renamed frame
PYENCPY to PYENC. PYENC is now the preferred (and only) XyPy tool for
encoding from XyWrite into Python byte strings.
07/26/13 (v0.09):
Added frame PYENCPY (XPyL version of PYENCODE); later renamed to PYENC
Revised XYPY.TXT
07/25/13 (v0.08):
Added frame GOOPY (Google search from Xy CMline)
Frame WEB now accepts URL aliases set in XYWWWEB.REG
Added frame GOOPY (Google keyword search)
07/24/13 (v0.07):
Revised frame ZAPPY
Adopted parens/brackets instead of '\' for Python code line
07/23/13 (v0.06):
Added frame ZAPPY (XPyL equivalent of frame ZAP26)
07/21/13 (v0.05):
Revised XYPY.TXT to document that xy.py imports os module.
Frame WEB: Added dictionary lookup for popular URLs; single-word URL not
in dictionary is rendered as 'www.url.com'
Frame XPyL: Added framename switch "//" to signal no output expected
from embedded Python code
Modified frames PYTHON and PY to add option to run script in with python
-i switch, leaving interpreter open after script executes
Added frame WEB (open URL in new browser tab via Python's webbrowser
Minor tweaks to frame XPyL
07/21/13 (v0.04):
Frame MAKEXYPY deletes any existing copy of xy.py in \Lib
Removed unused variable LG and RG from XYPY.TPL (xy.py)
Frame MAKEXYPY revised to conform to Python's conventions regarding the
location of third-party add-on modules. The XyPy module, xy.py, is now
installed in a subdirectory of .\Lib\site-packages call "xypy"
(.Lib\site-packages\xypy\xy.py) The "xypy" subdirectory is added to the
Python module search path by means of a .pth file named "xypy.pth",
located in .\Lib\site-packages.
07/20/13 (v0.03):
Light revisions to XYPY.TPL and XYPY.TXT
07/20/13 (v0.02):
Fixed XPyL coding errors in frame JMPCP; revised XYPY.TXT; added author
contact e-mail address
07/20/13 (v0.01):
Initial release on XyWWWeb:

C.L. Distefano
palacreide-xylist (at) yahoo.com

[End of file XYPY.TXT]

Addendum 1: XyPy Python Module xy.py (XYPY.TPL)
(Rev. 1/11/14; current version may differ)

#! python3
   xy.py: Module for XyPy|XPyL (Python embedded in XPL)
   C.L.Distefano rev. 2014-01-11
   Requires XyWrite 4 (DOS) + XyWWWeb.U2 + Python 3.x.
   See XYPY.TXT (in http://users.datarealm.com/xywwweb/XYPY.ZIP)
     for setup instructions and usage.


import os

# Global variables
# ----------------
EDDIR = '' # Editor's directory (set by U2 frame MAKEXYPY)
CMFN = EDDIR + '/cml.sav'
INFN = EDDIR + '/xpyldata.in'   # Input from XyWrite
OUTFN = EDDIR + '/xpyldata.out' # Output from Python script
OKFN = EDDIR + '/xpyldata.ok'   # Output ready flag file
bLG = b'\xAE' # byte code for left guillemet
bRG = b'\xAF' # byte code for right guillemet

# 3-byte Function mnemonics
'@0', '@1', '@2', '@3', '@4', '@5', '@6', '@7', '@8', '@9', '@A', '@B',
'@C', '@D', '@E', '@F', '@G', '@H', '@I', '@J', '@K', '@L', '@M', '@N',
'@O', '@P', '@Q', '@R', '@S', '@T', '@U', '@V', '@W', '@X', '@Y', '@Z',
'AD', 'AS', 'BF', 'BK', 'BS', 'CC', 'CD', 'CH', 'CI', 'CL', 'CM', 'CN',
'CP', 'CR', 'CS', 'CU', 'DC', 'DF', 'GH', 'DL', 'DP', 'DS', 'DW', 'EL',
'ER', 'EX', 'GT', 'HM', 'M0', 'M1', 'M2', 'M3', 'M4', 'M5', 'M6', 'M7',
'M8', 'MD', 'MU', 'MV', 'NC', 'NL', 'NK', 'NP', 'NR', 'NS', 'NT', 'NW',
'PC', 'PD', 'PL', 'PP', 'PR', 'PS', 'PT', 'PU', 'PW', 'R0', 'R1', 'R2',
'R3', 'R4', 'R5', 'R6', 'R7', 'R8', 'R9', 'RC', 'RD', 'RE', 'RL', 'RP',
'RS', 'RV', 'RW', 'SD', 'SH', 'SI', 'SK', 'SM', 'SN', 'SS', 'SU', 'SV',
'TF', 'TI', 'TN', 'TS', 'UD', 'WA', 'WC', 'WC', 'WN', 'WS', 'WX', 'WW',
'XC', 'XD', 'DT', 'S1', 'S2', 'S3', 'S4', 'S5', 'S6', 'S7', 'SP', 'BC',
'LB', 'LE', 'NF', 'PF', 'TP', 'BD', 'MS', 'NM', 'LD', 'LL', 'LR', 'LU',
'UP', 'FF', 'YD', 'DO', 'DX', 'MK', 'SO', 'OP', 'WZ', 'NX', 'SW', 'FD',
'FM', 'TL', 'TR', 'TE', 'ED', 'EE', 'HC', 'EC', 'MC', '#1', '#2', '#3',
'#4', '#5', '#6', '#7', '#8', '#9', '$1', '$2', '$3', '$4', '$5', '$6',
'$7', '$8', '$9', 'DR', 'EN', 'C0', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6',
'C7', 'C8', 'C9', 'EF', 'IB', 'NO', 'NI', 'CO', '$0', 'LS', 'XP', 'WG',
'XM', '&0', '&1', '&2', '&3', '&4', '&5', '&6', '&7', '&8', '&9', '&A',
'&B', '&C', '&D', '&E', '&F', '&G', '&H', '&I', '&J', '&K', '&L', '&M',
'&N', '&O', '&P', '&Q', '&R', '&S', '&T', '&U', '&V', '&W', '&X', '&Y',
'&Z', 'HL', '$A', '$A', '$C', '$D', '$E', '$F', '$G', '$H', '$I', '$J',
'$K', '$L', '$M', '$N', '$O', '$P', '$Q', '$R', '$S', '$T', '$U', '$V',
'$W', '$X', '$Y', '$Z', 'XX', 'H@', 'VH', 'MW', 'QH', 'DK', 'SR', 'SC',
'TG', 'H1', 'JH', 'DZ', 'DD', 'DM', 'LT', 'RK', 'NN', 'MT', 'ET', 'ZT',
'T1', 'TT', '<<', '>>', 'IT', 'SL', 'SF', 'FL', 'FR', 'FC',
'SY', 'ME',
'AC', 'FS', 'TW', 'MI', 'RO', 'NB', 'Q1', 'Q2', 'Q3', 'Q4', 'Q5', 'Q6',
'Q7', 'Q8', 'TO', 'IR', 'AR', 'AX', 'DB', 'DE', 'HF', 'SA', 'OV', 'TC',
'TB', 'JM', 'SG', 'XH', 'FT', 'BX', 'MN', 'CB', 'M9', 'MZ', 'ZZ', 'RX',
'ST', 'KF', 'JC', 'AK', 'TM', 'NU', 'B4', 'QP', 'HG', 'US', 'XE', 'ES',
'RB', 'S-', 'S+', '**', 'BN', 'RU', 'CF', 'UI', 'XS', 'EA', 'BT', 'KD',
'DN', 'HI', 'WH', 'XN', 'FX', 'UN', 'MX', 'AZ', 'BR', 'HK', '#X', '#X',
'BM', 'JR', 'XO', 'XW', 'TX', 'LF', 'LO', 'BL', 'XT', 'WT', 'IC', 'CT',
'VB', '-D', 'WD', 'RM', 'LM', 'aL', 'aR', 'aB', 'aE', 'MP', 'mN', 'QL',
'QR', 'MF']

# Function definitions
# --------------------
def outok(flag = False, okfile = OKFN):
  ''' Output ready flag file (0=write|1=delete, flag_filename) '''
  if not flag:
    with open(okfile, 'wt') as okfo:
    except FileNotFoundError:

def xyread(file_in = INFN, mode = 'rb'):
  ''' Read XyWrite input file (minus EOF) '''
    with open(file_in, mode) as f:
      fb = f.read()
      if fb[-1] == 26: fb = fb[:-1] # fails with 0-byte file
      return fb
  except: return None

def xywrite(bytes_in, file_out = OUTFN, mode = 'wb'):
  ''' Write file for output to XyWrite '''
    with open(file_out, mode) as f:
      return 1
  except: return None

def GetCMline():
  ''' Fetch CMline file in binary mode '''
  with open(CMFN, 'rb') as f:
    cmline = f.read()
    if cmline[-1] == 26: # fails with 0-byte file
      cmline = cmline[:-1]
    return cmline

def xycm(*cm_in):
  ''' Returns bytes for XyWrite embedded command(s) '''
  ba = bytearray()
  for cm in cm_in:
    if isinstance(cm, str):
      cm = cm.encode()
      ba.extend(b'\xae' + cm + b'\xaf')
    except ValueError:
      print('Bad:', cm, '- must be Ascii text string or bytes')
      return None
    except Exception as e:
      return None
  return ba
def fBX(*cm_in):
  ''' Returns bytes for BX [command]Q2 '''
  ba = bytearray()
  for cm in cm_in:
    if isinstance(cm, str):
      cm = cm.encode()
      ba.extend(b'\xff\x82\xab' + cm + b'\xff\x82\x7f')
    except ValueError:
      print('Bad:', cm, '- must be Ascii text string or bytes')
      return None
    except Exception as e:
      return None
  return ba
def xyfunc(*func_mnemonic):
  ''' Return bytes for XyWrite 3-byte function mnemonic(s) '''
  ba = bytearray()
  for mn in func_mnemonic:
      if mn not in FUNCNAMES: mn = mn.upper()
      n = FUNCNAMES.index(mn)
      #        byte1 byte2            byte3
      for b in 255,  128 + n // 128,  (1 + n * 2) % 256:
    except Exception:
      print(mn, 'is not a valid 3-byte func mnemonic')
      return None
  return ba
def xywild(*char_in):
  ''' Returns bytes for standard reverse-video wildcard character(s)
       by normal Ascii char or Ascii-number (0 - 255) '''
  ba = bytearray()
  for n in char_in:
    if isinstance(n, str):
      if len(n) == 1: n = ord(n)
    if isinstance(n, int):
      if 0 <= n < 256:
        n = (n + 128) % 256
        b2 = 192
        if n < 128: b2 = 193
        for v in 255, b2, n:
      raise ValueError('1-byte Ascii char or int 0-255 only')
      return None
  return ba
def xy3bchar(*char_in):
  ''' Returns bytes for XyWrite 3-byte printable char(s) (0 -255)
      args = 1-byte Ascii char or integer 0 - 255 
  ba = bytearray()
  for n in char_in:
    if isinstance(n, str):
      if len(n) == 1: n = ord(n)
    if isinstance(n, int):
      #   next line converts, e.g., 0xa to '0A'
      h = '{0:0>2s}'.format(hex(n).split('x')[1].upper())
      #         byte1 byte2       byte3
      for bt in 255,  ord(h[0]),  ord(h[1]):
      raise ValueError('1-byte Ascii 0 - 255 char(s) only')
      return None    
  return ba

def xychar(*char_num):
  ''' Return bytes for XyWrite 3-byte Speedo char(s) 256 - 1023 '''
  ba = bytearray()
  errmsg = 'arg must be integer(s) in range 256-1023'
  for n in char_num:
    if isinstance(n, int):
      if 256 <= n < 1024:
        #        byte1 byte2             byte3
        for b in 254,  (n - 256) % 256,  (n - 256) // 256:
        return ba
        raise ValueError(errmsg)
        return None        
      raise ValueError(errmsg)
      return None

def GetStackall():
  ''' Read command archive file STACKALL.SAV '''
  stackall = None
  with open(EDDIR + '/stackall.sav', 'rb') as f:
    stackall = f.read()
    if stackall[-1] == 26:
      stackall = stackall[:-1]
  except: pass
  return stackall

def PutStackall(bytes_in):
  ''' Write command archive file STACKALL.SAV '''
  if bytes_in:
    with open(EDDIR + '/stackall.sav', 'wb') as f:
        return 1
      except: pass
  else: pass

def bconv(num, base_in = 16, base_out = 10):
  ''' Base-to-base number conversion '''
  prefix = ''
    num = int(str(num), base_in)
    if base_out == 10: return num
    if str(num).startswith('-'):
      num = str(num)[1:]
      prefix = '-'
    L = '0123456789abcdefghijklmnopqrstuvwxyz'
    num = int(num)
    digits_out = '' # convert base10 to base_out value
    while num >= base_out:
      i = divmod(num, base_out)
      digits_out += L[:base_out][i[1]]
      num = i[0]
    digits_out += L[:base_out][num]
    return prefix + digits_out[::-1]
  except Exception as e:
    return None

if __name__ == '__main__':

# end xy.py

Addendum 2: XyPy U2 Frames (XPL/XPyL) in Pseudo-Code
[as revised 2/10/14; current real-code version may differ]

/-- Start XyPy routines

{{5xypyver*}} Report XyPy version number (S/G 50 out; XYPYVER/[NV]
suppresses PRompt) [CLD]
y v@50><EI>{2}

{{5py,pyi,pyw,py/}} PyShell: Run Python code from XyWrite [CLD
{2};*; PY [arg(s)]<Helpkey> runs current file
;*;    or DF block as Python script
;*;    PYI<Helpkey> runs file|DF block in python -i interactive
;*;    PYW<Helpkey> displays output from DeFined Python (XPyL)
;*;    PY/<Helpkey> shells directly to Python interpreter
r(<VA$FR>)>DZ ;*;
SV50>JM 2.XPyL/X/WQ2
JM 2.GetPathQ2 <SX03,<IS50>+"\xytmp.py"><SV04,
# EoF comment workaround>BX sa %04,<PV03>Q2 BX waitQ2 
DZ BX apt <PV03>Q2 BX waitQ2 ;*;
JM 2.python/k<IF<IS01>{240}"I">/i<EI>Q2 DO FF

{{5python*}} Launch Python interpreter/script [CLD rev.7/21/13]
;*;  Fully-qualified path to python.exe must be in Reg var Python_EXE
;*;  Usage: << SV50,[scriptname.py] [args]>> JM
2.python[/k][/x]Q2 ;*;
;*;  /K and /X are DOS command switches (default = DOS/NV/Z /C ...)
;*;  /I launches the script with the python -i option, which returns
;*;    you to the Python interpreter prompt >>> after the
;*;    executes
<SX62,@upr(<VA$FR>)><SV99,Python_EXE>JM 2.RegData/RQ2
<IF"PYTHON.EXE"{238}@upr(<IS99>){less_than}0><PRPython 3
and XyWWWeb
RegVar Python_EXE
JM 2.GetPathQ2 <SX65,<IS50>+"\xpyldata.out">BX ernv
<PV65>Q2 BX waitQ2
<SX65,<IS50>+"\xpyldata.ok">BX ernv <PV65>Q2 BX waitQ2
BX dos/nv<IF<IS62>{240}"/X">/x<EI>/z
<PV63><IF<IS62>{240}"/I"> -i
<PV64><EI>Q2 BX waitQ2 {2}

{{5XPyL*}} Run Python script embedded in S/G 50 ("XPyL") [CLD
{2}BX es 1Q2 <SX61,-1>
# end"><SX63,@upr(<VA$FR>)><SX50,<VA$ED>>JM
2.GetPathQ2 ;*;
<SV99,Python_EXE>JM 2.RegData/RQ2 
<PRPython 3 and XyWWWeb RegVar Python_EXE
<SX61,0><SX65,<IS50>+"\xpyltmp.py">BX sa
BX waitQ2 <SV62,>;*;
<SX66,<IS50>+"\xpyldata.out">BX ernv <PV66>Q2 BX
waitQ2 ;*;
<SX67,<IS50>+"\xpyldata.ok">BX ernv <PV67>Q2 BX waitQ2
BX dos/nv<IF<IS63>{240}"/X">/x<EI>/z 
cmd.exe /c 
<PV64> <PV65><IF<VA|68>{greater_than}0>
<PV68><EI>Q2 BX waitQ2 
DO FF <IF<IS63>{240}"//"><EX><EI>;*;
;*; Output modes
<SX61,1><PRWaiting for output...><SX62,3>;*;
<CUb,62>BX exist <PV67>Q2
BX pQ2 <LBb><PRNo output><EX>;*;
<LBc><SX61,2>BX ernv <PV67>Q2 BX waitQ2 ;*;
;*; Store output in S/G 50; optionally display on CMline (/CM),
;*;   in DialoG box (/DG), or on PRompt line (/PR)
<SX50,<IS66>>JM 2.SV50Q2 ;*;
2.ShowSG01Q2 <EX><EI>;*;
;*;BX ernv <PV66>Q2 BX waitQ2 ;*;
;*; MErge at cursor position (/ME) or into Untitled file in 
 new window (/W)
gt;{less_than}1><PRNo window>
BX pQ2 <GLe><EI>;*;
BX func #<PV62>Q2 BX ne/100Q2 <EI>
<SX50,<CP>>BX me <PV66>Q2 BX waitQ2 JM 2.ReJuMPQ2
;*; Else do nothing (output retained in XPYLDATA.OUT)
;*;BC ca <PV66><SX62,"Output in "+<IS66>><PR@62>
<EX><EI><PRXPL usage: 

{{5idle*}} Start Python IDLE GUI [CLD rev.8/4/13]
<SV99,Python_EXE>JM 2.RegData/RQ2
<SX50,<IS99>>JM 2.GetPathQ2 ;*;
<SX02,"@echo off
call "+<IS50>+"\Lib\IDLELIB\IDLE.BAT "+<IS01>+"
exit"><SX50,<VA$ED>>JM 2.GetPathQ2 ;*;
BX sa %02,<PV50>\idlexy.batQ2 ;*;
BX dos/nv/x/z /c cmd.exe /c start <PV50>\idlexy.bat <PV01>Q2
BX p 5Q2 BX ernv <PV50>\idlexy.batQ2
<EX><EI><PRPython not

{{5pyenc}} Encode file|DF block to Python hex byte code (XPyL) [CLD
gt;JM 2.GetPathQ2 
BX copy/nv <PV01> <PV02>Q2 BX waitQ2 <EI>DZ 
BX sa<IF<VA$DE>{greater_than}0>d<EI>/nv <PV02>Q2
BX waitQ2 
from xy import outok, xyread, xywrite, OUTFN
xywrite(str(xyread()), OUTFN, 'wt')
outok(1)">JM 2.XPyL/X/WQ2 <PROriginal is in Alternate
<EI><PRNo window><EX>
<EI><PRNo file><EX>
<EI><SX01,<VA$FP>>YD <GLa>{2}

{{5pydec}} Decode Python hex byte code (file|DF block) (XPyL) [CLD
gt;JM 2.GetPathQ2 
BX copy/nv <PV01> <PV02>Q2 BX waitQ2 <EI>
DZ BX sa<IF<VA$DE>{greater_than}0>d<EI>/nv
<PV02>Q2 BX waitQ2 
from xy import outok, xyread, xywrite, INFN
fb = xyread(INFN, 'r')
# zap EOF:
if fb[-1] == chr(26): fb = fb[:-1]
# adjust if double-quotes ("")
if 'b""' in fb:
  if fb.index('b""') == 0 and '\'' in fb:
    fb = fb.replace('\'', '\\\'')
    fb = fb.replace('""', '\'')
exec('fb = ' + fb)
if not xywrite(fb):
  xywrite(b'Decoding failed. Input must be b\'hex_bytes\'')
outok(1)">JM 2.XPyL/X/WQ2 XP TF <PROriginal is in Alternate
<EI><PRNo window><EX>
<EI><PRNo file><EX>
<EI><SX01,<VA$FP>>YD <GLa>{2}

{{5timedf}} Time a DeFined block of Python code (XPyL) [CLD rev.8/10/13]
{2};*; TIMEDF [setup[, number[, repeat]]]<Helpkey>
;*;  Defaults: setup='', number=1000, repeat=10
;*;  Renumber: Left$ "0+", Freeze 50
BX es 1Q2 DX DZ
<SX06,<IS50>>;*; <SV06,args>
;*; Fix code (temporarily using 03, 04, 05):
;*; Convert space indents to tabs (to 6 levels assuming 2 spaces per
;*; Default setup, number, repeat:
;*; Parse args, if any, into <SV03,><SV04,><SV05>
;*; Python script:
<SX50,"import xy, timeit
# print('Timing (could take a while)...')
out = min(timeit.repeat('"+<IS02>+"', setup='"+<IS03>+"',
number="+<IS04>+", repeat="+<IS05>+"))*1000
out = 'Min %.2f msec (number="+<IS04>+" repeat="+<IS05>+")'
% out
xy.xywrite(out, xy.OUTFN, 'wt')
xy.outok(1)"><PRTiming (could take a while)...>JM 2.XPyL/X/50Q2
<PR@50><EX><EI><PRNothing DeFined>{2}

{{5bconv}} Base-to-Base number conversion (XPyL; S/G50 out) [CLD
from xy import outok, xywrite, bconv
res = bconv("+<IS01>+")
if not res: res = 'Conversion failed'
xywrite(str(res), mode = 'wt')
outok(1)">JM 2.XPyL/X/PRQ2 <EX>
<EI><PRBCONV num_in, base_in, base_out<Helpkey>>{2}

{{5wcpy}} Word count of DeFined block, or current or specified file
(XPyL) [CLD rev. 6/22/14]
{2}BX es 1Q2 DX <PRWorking>;*;
n"><EI>BX sad/nv
<PV01>Q2 BX waitQ2
<EI><IF<VA|01>{greater_than}0>BX exist <PV01>Q2
<IF@not(<ER>)><SX50,<IS01>>JM 2.swapslashQ2
98,>JM 2.swapslashQ2
<SX03,<IS50>><SX50,"import xy, re
+"infn = xy.INFN
"><GLa><EI><SX50,<IS50>+"infn =
<SX50,<IS50>+"with open(infn, 'rb') as f1, open(xy.OUTFN, 'wt')
as f2:
 words = re.findall('\w+', f1.read().decode(encoding='latin-1'))
 f2.write('{0:,d}'.format(len(words)) + ' words in ' +
xy.outok(1)">JM 2.XPyL/X/PRQ2 <EX><EI><PRNon-existent
@01><EX><EI><PRNo file specified>{2}

{{5listwords}} List all words in specified or current file (XPyL) [CLD
{2}BX es 1Q2
<IF<VA|50>{greater_than}0>JM 2.swapslashQ2
<SX50,<IS02>>JM 2.swapslashQ2 <SX03,<IS50>>
<SX50,"import xy, re
fn1, fn2 = '"+<IS01>+"', '"+<IS03>+"'
print('Compiling case-sensitive list of words in', fn1)
with open(fn1, 'rb') as f1, open(fn2, 'wt') as f2:
 wds = []
 txt = f1.read().decode(encoding='latin-1')
 # wds = re.findall('\w+', txt)
 # wds += re.findall('\w+(?:[-/]\w+)+', txt)
 # wds = re.split('\w+', txt)
 wds = re.findall('[A-Z,a-z]+', txt)
 wds += re.findall(""[A-Z,a-z]+(?:[-/'][A-Z,a-z]+)+"", txt)
 if wds:
  txt = wds
  wds = set(wds)
  wdlist, wdlistlen = [], 0
  wdlgth = 0
  for wd in wds:
   if ',' in wd: wd = wd.replace(',', '')
   # wc = txt.count(wd)
   # if len(wd) & wc: wdlist.append(wd+' ('+str(wc)+')')
   if len(wd):
    wdlgth += len(wd)
  if wdlist:
   wdlist = sorted(set(wdlist))
   wdlistlen = len(wdlist)
   wdlgth = wdlgth/wdlistlen
   wdlist = '\n'.join(wdlist)
   print('Done\n\t', '{0:,d}'.format(wdlistlen), 'unique words found
in', fn1)
   print('\t Average word length = {0:.1f} chars'.format(wdlgth))
   input('\nHit Enter to view word list in XyWrite{greater_than}')
   print('No words found!')
   input('\nHit Enter to return to XyWrite{greater_than}')
 xy.outok(1)">JM 2.XPyL/WQ2 
<IF<VA$FI>=="[UNTITLED]">BX ch  [wC] [wC]Q2
<SX01,<VA$WC>+1><SX01,<IS01>+" words">DO F.2D
<EI><PRLISTWORDS [d:\\path\\filename] (default = current

{{5listcp}} List CharPos of all case-sensitive occurrences of '$tring'
in current file, plus text snippet containing '$tring' (XPyL) [CLD
{2}BX es 1Q2
JM 2.swapslashQ2 <SX02,<IS50>><SX50,<VA$ED>>JM
<SX50,"# -*- coding: latin-1 -*-
import xy, re
fn1, fn2 = '"+<IS02>+"', '"+<IS03>+"'
def snip(cp, text):
 return text[cp-45:cp+65]
with open(fn1, 'rb') as f1, open(fn2, 'wt') as f2:
 mch = "+<IS01>+"
 ixo = ''
 txt = f1.read().decode(encoding='latin-1')
 ixs = ['\n--\n'+str(i.start())+':\n... '+
  snip(i.start(), txt)+' ...\n' for i in re.finditer(mch, txt)]
 ixo = ('""'+mch+'"" in '+fn1+' ('+str(len(ixs))+
  ' occurences):\n--\n[CP]:\n[snippet]\n\n' + ''.join(ixs))
 except Exception as e:
  f2.write('Python error message:\n'+str(e))  
xy.outok(1)">JM 2.XPyL/x/wQ2 ;*;
;*;<IF<PV61>{greater_than}1>BX ch ",",[wC][wC]"Q2 BX waitQ2
<EX><EI><PRLISTCP 'string'<Helpkey>>{2}

{{5lstcp}} List CharPos of all case-sensitive occurrences of '$tring' in
current file (XPyL) [CLD rev.6/15/13]
{2}BX es 1Q2
JM 2.swapslashQ2 <SX02,<IS50>>
<SX50,<VA$ED>>JM 2.GetPathQ2
JM 2.swapslashQ2 <SX03,<IS50>>
<SX50,"# -*- coding: latin-1 -*-
import xy, re
fn1, fn2 = '"+<IS02>+"', '"+<IS03>+"'
with open(fn1, 'rb') as f1, open(fn2, 'wt') as f2:
 mch = "+<IS01>+"
 txt = f1.read().decode(encoding='latin-1')
 # mch = str(re.findall("+<IS01>+", txt))
 ixs = [str(i.start()) for i in re.finditer(mch, txt)]
 ixo = '\n'.join(ixs)
 ixo = mch+' in '+fn1+' ('+str(len(ixs))+' occurences):\n' + ixo
 except Exception as e:
  f2.write('Python error message:\n'+str(e))  
xy.outok(1)">JM 2.XPyL/x/wQ2 <EX><EI><PRLSTCP

{{5jmpcp}} JuMP to all (case-sensitive) occurrences of '$tring' in
current file (simulates native SEA command) (XPyL) [CLD rev.7/20/13]
{2}BX es 1Q2
JM 2.swapslashQ2 <SX02,<IS50>>
<SX50,"# -*- coding: latin-1 -*-
import re
from xy import OUTFN, outok, bLG, bRG, FUNCNAMES, xyfunc, xycm, f
BX outok()
infn = '"+<IS02>+"'
for v in 'BX', Q2 ', 'BC', 'GT':
  exec(v + ' = xyfunc(v)')
with open(infn, 'rb') as f1, open(OUTFN, 'wb') as f2:
 mch = "+<IS01>+"
 txt = f1.read().decode(encoding='latin-1')
 ixs = [str(i.start()) for i in re.finditer(mch, txt)]
 if len(ixs) {greater_than} 500: ixs = ixs[:500]
 ixo = '""'+mch+'"" in '+infn+' ('+str(len(ixs))+' hits [500 max])'
 ixs = '|'.join(ixs) + '|'
 ixs = (
  fBX('es 1') + BC + b'Press any key to locate hits, Esc to quit' +
  xycm('PR'+ixo, 'SV01,'+ixs, 'SV02,|', 'LBa', (b'SX03,'+bLG+b'RK'+bRG),
       'SV04,', 'XS01,02,03,04,04', (b'SX01,'+bLG+b'IS04'+bRG)) +
  fBX(bJM p '+bLG+b'PV03'+bRG) + xycm('GLa', 'EI', 'EI') +
  BC + GT + xycm('PRDone'))
outok(1)">JM 2.XPyL/x/50Q2

{{5funcspy}} Write full set of 3-byte functions [CLD rev. 8/6/13]
{2}<IF<VA$WA>{greater_than}0><SV50,from xy import outok,
b = bytearray()

for x in ([(255, i, j) for i in range(128, 131) 
                       for j in range(1, 256, 2)]):
  for y in x:

# for x in range(128, 131):
#   for y in range(1, 256, 2):
#     for z in 255, x, y:
#       b.append(z)

if not xywrite(b): xywrite(b'File write error')
outok(1)>JM 2.XPyL/X/WQ2 <EX><EI><PRNo window>{2}

{{5wildpy}} Write full set of 3-byte reverse-video (wildcard) chars [CLD
{2}<IF<VA$WA>{greater_than}0><SV50,from xy import outok,

b = bytearray()

# fastest:
for x in range(128, 256):
  for y in 255, 192, x:
for x in range(128):
  for y in 255, 193, x:

# a little slower:
# for x in ([(255, 192, i) for i in range(128, 256)] + 
#           [(255, 193, i) for i in range(128)]):
#   for y in x:
#     b.append(y)

# slowest:
# z = (x for y in 
#          ([(255, 192, i) for i in range(128, 256)] + 
#           [(255, 193, i) for i in range(128)]) 
#          for x in y)
# for v in z:
#   b.append(v)

if not xywrite(b): xywrite(b'File write error')
outok(1)>JM 2.XPyL/X/WQ2 <EX><EI><PRNo window>{2}

{{53byterspy}} Write full set of 3-byte printable chars [CLD rev.8/6/13]
{2}<IF<VA$WA>{greater_than}0><SV50,from xy import outok,
b = bytearray()

for x in map(xy3bchar, range(256)):
  for y in x:

# for x in range(256):
#   for y in xy3bchar(x):
#     b.append(y)

if not xywrite(b): xywrite(b'File write error')
outok(1)>JM 2.XPyL/X/WQ2 <EX><EI><PRNo window>{2}

{{5speedospy}} Write full set of Speedo chars [CLD rev.8/18/13]
{2}<IF<VA$WA>{greater_than}0><SV50,from xy import outok,
xychar, xywrite
b = bytearray()

for x in map(xychar, range(256, 1024)):
  for y in x + b' ':

if not xywrite(b): xywrite(b'File write error')
outok(1)>JM 2.XPyL/X/WQ2 <EX><EI><PRNo window>{2}

{{5goopy}} Google search (XPyL) [CLD rev.7/25/13]
{2}<IF<VA|50>{greater_than}0><SX50,"import webbrowser
">JM 2.XPyL//XQ2 <EX><EI><PRGOOPY [search

{{5py3}} Search the Python 3 Standard Library via Google [CLD]
site:docs.python.org/3/library/">JM 2.goopyQ2 <EX><EI>
<PRPY3 [keywords]<Helpkey> [Python 3 documentation

{{5getm}} Simple GetMail (XPyL) [CLD rev.9/1/13]
{2};*; Review all messages in Untitled file. 
;*;  Messages are NOT deleted!
<SV99,POP_Server>JM 2.RegDataQ2 <SX01,<IS99>>;*;
<SV99,Email_Address>JM 2.RegDataQ2 <SX02,<IS99>>;*;
import poplib
from xy import outok, xywrite
  p = '"+<IS01>+"'
  u = '"+<IS02>+"'
  if not p: p = input('Enter POP3 server name: ')
  if not u: u = input('E-mail address: ')
  print('Caution: Password will be displayed...')
  s = input('Password: ')
  M = poplib.POP3(p, port=110, timeout=15)
  numMessages = len(M.list()[1])
  if numMessages:
    print(numMessages, 'messages')
    msgs = bytearray()
    for i in range(numMessages):
      print('Downloading message #', i+1)
      msgs += (
        ('-='*10).encode() + b' [Message # ' + str(i+1).encode() + 
        b' of ' + str(numMessages).encode() + b'] ' + 
        ('-='*10).encode() + b'\r\n'
      for j in M.retr(i+1)[1]:
#     for j in M.retr(i+1):
        msgs += j + b'\r\n'
    msgs += b'\r\n\r\n'
  else: msgs = b'No mail'
except: msgs = b'Error retrieving mail'
outok(1)">JM 2.XPyL/WQ2 {2}

{{5uh}} Unlimited Hint: List commands matching "hint" (any substring of
command) from omnibus command archive STACKALL.SAV [CLD rev.1/26/14]
{2}<IF<VA$WA>{greater_than}0>BX es 1Q2 DX
JM 2.RegDataQ2 ;*;
JM 2.GetPathQ2 ;*;
BX sa %01,<PV50>\cml.savQ2 <SX01,<IS50>>;*;
from xy import outok, GetCMline, GetStackall, xywrite
hint = GetCMline()
hint = hint.replace(b'\xff', b'\xffFF').replace(b'\xfe', b'\xffFE')
ft = GetStackall()
ftlist = ft.split(b'\r\n') # list commands
hits = bytearray()
if hint:
  for h in ftlist:
    if hint.upper() in h.upper():
       b'\xffFF', b'\xff').replace(
       b'\xfe', b'\xffFE') + b'\r\n')
if hits:
  if not xywrite(hits): xywrite(b'File write error')
">JM 2.XPyL/X/WQ2 BX exist <PV01>\xpyldata.outQ2 
<IF<ER>&<VA|99>{greater_than}0>BX ab/nvQ2 DO F.2D
<PRNot found><EX><EI>
DO FF BC popCMGT <PR{less_than}Helpkey{greater_than} copies command
under cursor to
CMline><EX><EI><SX50,<IS01>>JM 2.uhlistQ2
<SX01,<VA$WA>>BX func #<PV01>Q2
JM 2.CallorGo/100Q2 TF BX ch/t  [wC] [wC]Q2 BX waitQ2 BC popCMGT
<SX50,<VA$WC>>JM 2.formatd/nvQ2 <SX01,<IS51>+"
archived commands">
<PR@01><EX><EI><PRNo window>{2}

{{5uhdel,uhlist}} Delete|List commands containing substring from
STACKALL.SAV command history archive file [CLD rev.11/27/13]
n}0>BX es 1Q2 
$ED>>JM 2.GetPathQ2 
<SX50,<IS50>+"\STACKALL.SAV">BX exist <PV50>Q2
<SU03,BX ab/nvQ2 <IF<VA$WS>{less_than}1>BX rsQ2
<LBa>BX gofile <PV50>Q2
BX func #<PV04>Q2 BX ca/100 <PV50>Q2 BX waitQ2
BX func #<PV04>Q2 BX ne/100Q2 AS <SV04,DP AS CP YD AS
<LBc>BX se [999]<PV01>[999]Q2
X01,""""+<IS01>+""" not found in
"+<IS50>><LBd><GT03><GT03>DO F.2D
<IF"DN "{238}<IS04>{less_than}0>AS TF DO FF 
<IF<IS02>{240}"DE"><LBe><SX05,"Delete ALL of these
<SV07,|71|92|=TF |14|72|100|=LU |73|93|=PU |75|101|=CL |77|103|=CR
|79|96|=BF |80|102|=LD |81|97|=PD |>
DX <GT03><SV04,DP DN ><SX05,0>TF
AS <GT03>BC popCMGT <PR{less_than}Helpkey{greater_than} copies
under cursor to
<SX01,<IS01>+": SAve STACKALL.SAV to make deletions permanent;
otherwise, ABort file"><EI>TF
<PR@01><EX><EI><SX01,<IS50>+" does not
<PRTwo (2) available windows required>{2}

{{5ahuh}} Aleatory HUH [CLD rev.12/12/13]
{2}<IF<VA$ET>{240}"3.33"!<VA$ET>{240}"7.77">;*; 1 in
500 chance
{240}"7.77">;*; 1 in
;*;<IF<VA$ET>{240}".77">;*; 1 in 100
;*;<IF<VA$ET>{240}".17"!<VA$ET>{240}".71">;*; 1 in 50
;*;<IF<VA$ET>{240}".7">;*; 1 in 10
JM 2.HIDE:01-03,50,99Q2 JM 2.huh/nvQ2 BX pQ2 JM 2.UNHIDEQ2 ;*;

{{5huh,huh/*}} Have Unlimited Hint: Add the current command Stack to
 omnibus command archive STACKALL.SAV [CLD rev.12/12/13]
{2}<SX01,<VA$FR>><SV99,Python_EXE>JM 2.RegDataQ2
DX BX es 1Q2 <SX50,<VA$ED>>JM 2.GetPathQ2
BX ernv <PV02>\623.SAVQ2 BX waitQ2 ;*;
<IF<VA@623>{240}"{190}">BX sa %623,<PV02>\623.savQ2 BX
waitQ2 <EI>;*;
BX exist <PV02>\stackall.savQ2 ;*;
<IF<ER>><SV03,>BX sa %03,<PV02>\stackall.savQ2
BX waitQ2 <EI>;*;
<SX50,"from xy import outok, EDDIR, xyread, xywrite, GetStackall,
# outok()
# (current) Stack:
newstack = b''
try: newstack = xyread(EDDIR+'/623.sav').replace(b'\xbe', b'\r\n')
except: pass
oldstack = GetStackall() #  (archived) Stack
# Grandfather STACKALL.SAV using Ascii-190 separator:
oldstack = oldstack.replace(b'\xbe', b'\r\n')
if newstack: oldstack += newstack
s = sorted(set(oldstack.split(b'\r\n')))
cbs = bytearray(b'')
for i in s:
  if i: cbs.extend(i + b'\r\n')
if cbs:
  # xywrite(b'Stack merged into STACKALL.SAV')
# else: xywrite(b'Oops (unexpected): STACKALL.SAV not modified')
# outok(1)
# end">;*;
;*;JM 2.XPyL/X/50Q2 ;*;
JM 2.XPyL//XQ2 ;*;
BX ernv <PV02>\623.SAVQ2 BX waitQ2
<PRStack merged into STACKALL.SAV><EI><EX>
<EI>JM 2.huhxpl<IF<IS01>{240}"/">/nv<EI>Q2
<EX>;*; Run pure-XPL HUH if
 Python not installed{2}

{{5huhxpl*}} HUH (Have Unlimited Hint-pure XPL version)
 [CLD rev.12/12/13 (uses CMSORT.EXE for Win32 OSes)]
{2}<IF<VA$WA>{greater_than}0>BX es 1Q2 DX
2.GetPathQ2 ;*;
BX sa %623,<PV02>Q2 BX waitQ2 <EI>;*;
<SX03,<IS50>+"\STACKALL.">BX exist <PV03>SAVQ2 ;*;
<IF<ER>><SV04,>BX sa %04,<PV03>SAVQ2 BX waitQ2
<SU05,<SX04,<VA$WA>>BX func #<PV04>Q2
BX gofile <PV03>SAVQ2 <IF<ER>>BX ca/100
<PV03>SAVQ2 <EI>XP BX waitQ2 ;*;
<IF<VA|02>{greater_than}0>BX me <PV02>Q2 BX waitQ2
TF BX ch/1  {190}Q2 BX waitQ2 BX ch  {190} [wC]Q2 BX waitQ2 ;*;
<SX04,<VA$SK>><SX04,"BX d sk="+<IS04>+"Q2 ">BX d
sk=4,80Q2 ;*;
BX st/nv <PV03>TM0Q2 ;*;
BX exist <PV50>\cmsort.exeQ2 <IF@not(<ER>)>JM
2.GetXyOSQ2 ;*;
BX dos/nv/x/z /c <PV50>\cmsort.exe /B /D /Q <PV03>TM0
<EI>BX sort/nv <PV03>TM0,<PV03>TM1Q2
<LBa><IF@not(<ER>)>BX waitQ2
BX copy/nv <PV03>TM1 <PV03>SAVQ2 BX waitQ2 ;*;
BX ernv <PV03>TM0Q2 BX waitQ2 BX ernv <PV03>TM1Q2 BX waitQ2
<GT05>BX ca/100 <PV03>SAVQ2 ;*;
<LBb>BX ch  [wC][wC] [wC]Q2 <IF@not(<ER>)>BX waitQ2
BX st/nv <PV03>SAVQ2 <IF<VA$WS>{less_than}1>BX rsQ2
<PRStack merged into STACKALL.SAV><EX><EI>;*;
<PRSort error: STACKALL.SAV not modified>BX pQ2
<EX><EI><PRNo window>{2}

{{5addstackall}} Merge external STACKALL.SAV-type command archive and/or
 current Stack (S/G 623) into STACKALL.SAV [CLD rev.11/2/13]
{2};*; Usage: ADDSTACKALL
BX es 1Q2
<SX50,<VA$ED>>JM 2.GetPathQ2 ;*;
<SX51,<IS50>+"\">JM 2.tmpfileQ2 ;*;
<IF<VA|01>{greater_than}0>BX exist <PV01>Q2
<SX02,<IS51>+"STACKALL.SAV">BX exist <PV02>Q2
BX dos/nv/x/z /c copy <PV01>/a+<PV02>
<PV50>/b{greater_than}NULQ2 ;*;
<IF<VA$RR>==0>BX copy/nv <PV50> <PV02>Q2
SX01,<PV01>><PR@01>BX pQ2 <EI>;*;
BX waitQ2 BX ernv <PV50>Q2 BX waitQ2 <EI>;*;
<SX01,"DOS error code "+<IS02>+" while copying
BX pQ2 <EI><EI><EI><EI>JM 2.huhQ2 {2}

{{5popCM}} Copy line of text under cursor to CMline [CLD rev.12/13/13]
{2}<IF<VA$WS>==1>BX es 1Q2 GT YD DP DM CL DZ <SV50>YD
JM 2.str5to3Q2 
<EI><PRToo long for CMline><EX><EI><PRNo

{{5str5to3}} Change 5-byters to 3-byters in $tring (S/G50 in|out)
 [CLD rev.2/1/14]
;*; <SV51,3-byte 255|254> <SV52,1-byte 255|254 [SUb]>;*;
;*; <SV53,1-byte 255> <SV54,1-byte 254> <SV55,special 3-

{{5addCM}} Add CMline|DF_block to STACKALL.SAV [CLD rev.11/24/13]
{2};*; Must be assigned to key with nn=NOJM(,2,.,a,d,d,C,M,)
BX es 1Q2 <PRWorking><SU45,DZ

<SX50,<VA$ED>>JM 2.GetPathQ2 <SX51,<IS50>+"\">JM
2.tmpfileQ2 ;*;
<SX47,<IS50>>;*; <SV47,tempfile><SV51,Editor's
<SX45,<IS51>+"STACKALL.SAV">BX exist <PV45>Q2
BX sa %46,<PV47>Q2 <IF@not(<ER>)>;*;
BX waitQ2 BX apt <PV47>,<PV45>Q2
BX waitQ2 JM 2.huhQ2 <SX45,"CMline|DeFine added to
<LBb>BX ernv <PV47>Q2
BX sa %46,<PV45>Q2 BX waitQ2 <GLb><EI><PREmpty

{{5formatd*}} Format digits (S/G 50 in, S/G 51 out) [CLD rev.2/10/14]
number|DeFined_number{less_than}Helpkey{greater_than} (S/G 51

{{5Stackbox}} Stack list|hint utility v2 [CLD Rev.10/14/08; rev.8/11/13
  to call frame UH if hint not found
{2};*;          As "Hint" key: nn=NOXHJM(,2,.,s,t,a,c,k,b,o,x,)
;*;           or: STACKBOX [hint]{less_than}Helpkey{greater_than}
XH BX es 1Q2
<SV01,Empty Stack or STACK.PM not
JM 2.CMlineQ2 <IF<VA|50>{greater_than}-
;*; JM 2.ahuhQ2 ;*;
<SX50,<IS616>>JM 2.uhQ2 <SV616,>;*;
;*;<PRNo Stack Match>;*;
t;GT04>TF ;*;
DX BX window/nv n,
  3,1,<PV07>,<PV06><LBc>Q2 ;*;
<IF@not(<ER>)><SX09,<VAIP>><SX09,"BX d
ip="+<IS09>+"Q2 FF ">
BX d ip=0di,2di,0diQ2 BX
<GT04><SX07,0><LBd><SV08,>BX jmp <PV07>Q2
<SX96,<IS05>+" Stack "+<IS03>+":
{less_than}cr{greater_than}|{less_than}H{greater_than}key {less_than}P|
-{greater_than}urge {less_than}S{greater_than}ort|
BX jmp <PV07>Q2 <SV10,><LBe>DO FF JM 2.RK:01Q2 YD
92|=TF |14|72|100|
=LU |73|93|=PU |75|101|=CL |77|103|=CR |79|96|=BF |80|102|=LD |81|97|
JM 2.ab/nvQ2
BX esQ2 DO FF <SX627,<IS04>>JM 2.$SQ2 <SV04,>
<IF<IS08>{240}"U"><SV08,>JM 2.PrsCMlineQ2
<IF"R"==<IS01>><SX07,2><EI>BX d
DX BF YD DF TF DF BX sort/nvQ2 BX waitQ2 <SV1816,>;*;
BX d sk=<PV04>Q2 TF DO F.2D
<SV02,!><GLe><EI><IF"U"==<IS01>>DX BF YD
TF DF DN <SV02,!><GLa><EI>;*;
  @upr(<IS01>)==<IS01>!"XC $2 Q8 $X "{240}<IS01>>YD
BX seb  [wC]Q2 <IF<ER>>TF <GLg><EI>CR
F BX se/f  [wC]Q2 DF <SV04><IF"XXC 
XC ><EI>
<IF"H$2 Q8 $X "{240}<IS01>><SV08,U><EI>
DM CR DZ DN <SX627,"{190}M{190}P"+<IS04>>JM 2.$SQ2 
<SX05,<PV05>-1><IF<PV05>{less_than}2>TF YD DF BX
se/f  [wC]Q2 DF
<SU04,<SX50,<CP>>BX se [999][wC]<PV10>[999]Q2

BX se [999]<PV10>[999]Q2 <IF<CP>==1>CL
<SX10,<IS01>>JM 2.ReJuMPQ2
o window>{2}

{{5finito}} Optionally Save the Stack, then Quit Unconditionally  RJH
LastRev.8/7/08 CLD rev.7/27/13 (HUH)
er_than}1>AS <IF<VA$WN>{less_than}{greater_than}66>AS
;*; Fully qualified d:\path\filename in which to save Stack
<SV99,Stored_Stack>JM 2.RegData/RQ2
JM 2.stackauxQ2 BX waitQ2 <EI>BX ab/nvQ2
<SX50,<VA$ED>>JM 2.GetPathQ2
<SX49,<IS50>+"\KillNB.exe">BX exist <PV49>Q2
exist <PV50>\UNDOPIDQ2
BX do/nv/x/z <PV49> <PV51>Q2 <EI><EI>JM
<IF<VA|50>{greater_than}0>BX del/nv <PV51>DATQ2 BX
do/nv/x/z <PV49>
<PV50>Q2 <EX1><EI><EI>JM 2.huhQ2 BX quit/nvQ2

{{5web}} Open URL in new browser tab (XPyL) [CLD rev.9/14/13]
{2}DX BX es 1Q2
><EI>DZ ;*;
t;>BX seb  [wC]Q2 
<IF<ER>>LB <EI>JM 2.FindNextURLQ2 <SV01>YD JM
2.ReJuMPQ2 <EI>;*;
;*; Import URL alias list from XYWWWEB.REG
<SX99,"URL"+<IS02>>JM 2.RegDataQ2
<SX50,"import webbrowser
url = '"+<IS01>+"'
if '\\' in url:
  url = 'file:///' + url.replace('\\', '/')
urls = '"+<IS03>+"'
if url + '=' in urls: # urls = 'alias1=url1\nalias2=url2\n ...'
  url = {x.split('=')[0]: x.split('=')[1] for x in
if '.' not in url: url = 'www.' + url + '.com'
if '://' not in url: url = 'http://' + url
webbrowser.open(url, new=2)
">JM 2.XPyL//XQ2 <EX><EI><PRNo

{{5lookup}} Web lookup via frame YA* framename + search_term
  [CLD rev.11/9/13]
JM 2.RegDataQ2 <IF<VA|99>{greater_than}0>;*;
<SX50,"import webbrowser
url = '"+<IS99>+"'
if '://' not in url: url = 'http://' + url
term = '"+<IS01>+"'
newterm = term
for char in term:
  if (
    ord(char) in range(45) or 
    ord(char) in (47, 58, 59, 61, 63, 64, 91, 93) or 
    ord(char) in range(127, 256)
      newterm = newterm.replace( char, 
url += newterm
webbrowser.open(url, new=2)">JM 2.XPyL//XQ2
<PRLOOKUP [YA][suffix]

{{5veritas}} Wine|Liquor search via wine-searcher.com (XPyL)
  [CLD rev.11/12/13]
<SV02,'><LBa><IF<IS01>{240}"'">;*; replace
single quotes with hex
<SX50,"from webbrowser import open keywords = '"+<IS01>+"'
if ' ' in keywords: keywords = keywords.replace(' ','+')
open('http://www.wine-searcher.com/find/' + keywords, new=2)">JM

{{5montypy}} Monty Hall Paradox (XPyL) [CLD rev.1/6/14]
{2};*;       For information, issue:
;*;          WEB en.wikipedia.org/wiki/Monty_Hall_problem<Helpkey>
BX es 1Q2
<IF<IS01>{240}" "><SX02,<VA@01
2>><SX01,<VA@01 1>><EI>;*;
<SV50,# setup
from xy import outok, xywrite
import random, sys
doors_number = 3    # 'Let's Make A Deal' default
times = 10000       # default iterations
  doors_number = max("+<IS01>+", 3)
<IF<VA|02>{greater_than}0><SX50,<IS50>+"  times =
max("+<IS02>+", 100)
<IF<IS50>{240}"try:"><SX50,<IS50>+"except: pass

# main [the problem]
doors = list(range(1, doors_number + 1))
contestant_wins, new_door_wins = 0, 0
for n in range(times):
  car = random.choice(doors)
  contestant_door = random.choice(doors)
  goats = [g for g in doors if g is not car and g is not
  zonk = random.choice(goats)
  other_doors = [d for d in doors
                  if d is not zonk and d is not contestant_door]
  new_door = random.choice(other_doors)
  if contestant_door == car: contestant_wins += 1
  if new_door == car: new_door_wins += 1
# end main

output = ('\r\n'+str(doors_number)+' doors, '+str(times)+' times\r\n'+
  '-'*len(str(doors_number)+' doors, '+str(times)+' times')+'\r\n'+
  'Contestant\'s door had car '+str(contestant_wins)+' times\r\n'+
  'New door had car '+str(new_door_wins)+' times\r\n\r\n')
diff = abs(new_door_wins - contestant_wins)
lower = min(new_door_wins, contestant_wins)
if lower {greater_than} 0:
  pctdiff = 'or {0:.1f}%'.format(100*diff/lower)
  pctdiff = ''
if new_door_wins {greater_than} contestant_wins:
  output += 'New door better by '+str(diff)+' '+pctdiff
elif new_door_wins {less_than} contestant_wins:
  output += 'Contestant\'s door better by '+str(diff)+' '+pctdiff
  print('Contestant\'s door better by', diff, pctdiff)
else: output += 'It\'s a tie'
output += '\r\n'
if not xywrite(output.encode()): xywrite(b'[Fail]')
outok(1)>JM 2.XPyL/X/DGQ2 {2}

{{5zappy}} Zap End-of-File 1Ah|Ascii-26|Ctrl-Z|EOF from file (XPyL) [CLD
<LBa>JM 2.swapslashQ2 ;*;
# Rev.8/18/13: read entire file only if b'\x1a' EoF is present
fn = '"+<IS50>+"'
newEoF = None
with open(fn, 'rb') as f:
  f.seek(-1, 2)
  fd = f.read()
  if fd == b'\x1a':
    newEoF = f.tell() - 1
    fd = f.read(newEoF)
if newEoF:
  with open(fn, 'wb') as f:
">JM 2.XPyL//XQ2 <PRDone - Do not SAve file
copy or Untitled file - No operation><EX><EI><PRFile
modified - No
operation><EX><EI><PRNo file><EX><EI>BX
exist <PV50>Q2
<IF@not(<ER>)><GLa><EI><PRNon-existent file

{5zappy} Zap End-of-File 1Ah|Ascii-26|Ctrl-Z|EOF from file (XPyL) [CLD
<LBa>JM 2.swapslashQ2 ;*;
fn = '"+<IS50>+"'
with open(fn, 'rb') as f:
  fd = f.read()
if fd[-1] == 26:
  with open(fn, 'wb') as f:
">JM 2.XPyL//XQ2 <PRDone - Do not SAve file
copy or Untitled file - No operation><EX><EI><PRFile
modified - No
operation><EX><EI><PRNo file><EX><EI>BX
exist <PV50>Q2
<IF@not(<ER>)><GLa><EI><PRNon-existent file

{{5gsfpy*}} Get Short Filename (XPyL) [CLD rev.12/25/13]
{2}BX es 1Q2 <IF<VA|50>{less_than}1>DZ 
<SX50,"from xy import outok, OUTFN, xywrite
from win32api import GetShortPathName
long_name = r'''"+<IS50>+"'''
  xywrite( GetShortPathName(long_name), OUTFN, 'wt' )
except Exception as e:
  xywrite( str(e).encode() )
t;EI>PR<LBa>Q2 <EX>
<EI><PRGSFPY[/50|/CM|/ME|/PR|/W] [LongPathName]|[DeFined

{{5testpy}} XPyL test frame (CLD rev.7/19/13)
{2}<SV50,import xy
with open(xy.OUTFN, 'wb') as f:
  f.write(xy.xywild('a', 'E', 'i', 'O', 'u'))
  f.write(xy.xy3bchar('a', 'E', 'i', 'O', 'u'))
  f.write(b' {less_than}== 3-byters\r\n')
  f.write(xy.xyfunc('BC', 'GT', 'DO', 'FF', '{less_than}{less_than}'))
  f.write(xy.xychar(256, 266, 499, 998, 1013))
xy.os.system('cls')>JM 2.XPyL/x/wQ2 XP TF GH {2}

{{5demo#}} Demos 1 and 2 from XYPY.TXT [CLD rev.10/13/13]
from xy import OUTFN, outok, xywrite, xyfunc, xycm, fBX

for varname in 'BC', 'GT':
  exec(varname + ' = xyfunc(varname)')

xywrite(fBX('es 1') + xycm(b'SX01,\xaeVA$NW\xaf') + 
        fBX('d nw=3') + fBX('ne/100') + 
        BC + b'This Untitled window will ABort in 5 seconds...' + 
        GT + b'[Nothing here in the text window]' + 
        xycm('PRWaiting...') + fBX('p 5', 'ab') + 
        fBX(b'd nw=\xaePV01\xaf') + BC + GT + xycm('PRDone', 'EX'))

XP <PRABort window after viewing>{2}

{{5demo1a}} Demos 1a from XYPY.TXT [CLD rev.10/13/13]
{2}<SV50,from xy import outok, xywrite

xywrite(b'\xff\x82\xabes 1\xff\x82\x7f\xaeSX01,\xaeVA$NW\xaf\xaf
\xff\x82\xabd nw=3\xff\x82\x7f\xff\x82\xabne/100\xff\x82\x7f
\xff\x81\x1fThis Untitled window will ABort in 5 seconds...
\xff\x80}[Nothing here in the text window]\xaePRWaiting...\xaf
\xff\x82\xabp 5\xff\x82\x7f\xff\x82\xabab\xff\x82\x7f
\xff\x82\xabd nw=\xaePV01\xaf\xff\x82\x7f\xff\x81\x1f

outok(1)>JM 2.XPyL/X/WQ2 XP <PRABort window after viewing>{2}

{{5XyPyREG}} Set RegVar Python_EXE in XYWWWEB.REG [CLD rev.5/27/14]
{2}BX es 1Q2 DX <SX50,<VA$ED>>JM 2.GetPathQ2 ;*;
;*; Test for existence (not content) of RegVar Python_EXE:
<SX02,<IS50>+"\xpyldata.out">BX ernv <PV02>Q2 BX
waitQ2 ;*;
<SX04,"import sys
with open('"+<IS03>+"', 'wt') as f:
# end">BX sa %04,<PV01>Q2 BX waitQ2 ;*;
BX do/nv/x/z python.exe <PV01>Q2 BX waitQ2 ;*;
<IF<VA$RR>{less_than}1>BX exist <PV02>Q2
BX exist <PV50>\XYWWWEB.REGQ2 <IF@not(<ER>)>BX gofile
<IF@not(<ER>)>BX ab/nvQ2 <EI>;*;
BX ca/100 <PV50>\XYWWWEB.REGQ2 ;*;
BX se  [wC]Python_EXE=[wO][wC]Python_EXE.1=Q2 ;*;
<IF<ER>>BX se  [RD]Q2 <IF<ER>>BF BX seb 
;[wC];Q2 <EI>LB ;*;
><GT01>LU LU LE <EI>BX me <PV02>Q2 BX waitQ2 YD DF
LE DF DN ;*;
BX stQ2 BX waitQ2 <IF<VA$WS>==0>BX rsQ2
Nonexistent XYWWWEB.REG --
Abort><EX1><EI><EI><PRUnable to set RegVar 
Python_EXE. Set it manually><EX1><EI><PRWindow needed
to configure 
XYWWWEB.REG><EX1><EI><LB RegVar Python_EXE already

{{5updatexypy}} Create xy.py module in Python /Lib/site-packages/xypy
 directory [CLD LastRev.12/25/13]
{2}JM 2.mexypyQ2 JM 2.makexypyQ2 JM 2.xypyver/nvQ2 <PRXyPy updated to

{{5mexypy}} MErge XYPY.FRM into U2 (replace existing frames) [CLD
{2}<IF<VA$WA>{greater_than}0>BX es 1Q2
<SV99,Edit_Copy_of_U2_File>JM 2.RegDataQ2 ;*;
JM 2.CallorGo/100Q2 <IF<PV51>==1><SV21,!><EI>;*;
TF <SV24,/-- >BX se "<PV24>Start XyPy routines[wC]"Q2
LB YD DF ;*;
BX se "<PV24>End XyPy routines[wC][wC]"Q2 ;*;
<IF@not(<ER>)>DF DN <GLa><EI><EI>;*;
YD TF <SV24,{>BX se  [wC]{<PV24>Q2 ;*;
<IF<ER>><PRLoad the XyWWWEB
U2!><EX1><EI>LB ;*;
<LBa><SX50,<VA$ED>>JM 2.GetPathQ2 ;*;
BX me <PV50>\XYPY.FRMQ2 BX waitQ2 ;*;

"><SX25,<IS50>+"\NO.U2">BX sa %24,<PV25>Q2
<IF<ER>><LBb>BX ab/nvQ2 
BX waitQ2 ;*;
BX load <PV25>Q2 <IF<ER>><GLb><EI>BX
waitQ2 ;*;
BX saQ2 <IF<ER>><GLb><EI>BX waitQ2 ;*;
<IF<VA|23>{greater_than}0>BX sa/nv <PV22>Q2
BX waitQ2 <EI>;*;
BX ()BX load <PV22>Q2
BX waitQ2 ;*;
GH BX ernv <PV25>Q2 <IF<VA|21>{greater_than}0>TF BX se 
LU LB <EI><IF<VA|21>{less_than}1>BX ab/nvQ2
BX rsQ2 <EI><EI><EX><EI><EI><PRNo

{{5makexypy}} Create xy.py module in Python /Lib/site-packages/xypy
 directory [CLD LastRev.12/15/13]
{2}JM 2.XyPyREGQ2 <IF<VA$WA>{greater_than}0>BX es 1Q2 DX
<SV99,Python_EXE>JM 2.RegData/RQ2
<SX91,<IS50>+"\Lib">BX exist <PV91>\xy.pyQ2
<IF@not(<ER>)>BX ernv
<PV91>\xy.pyQ2 BX waitQ2 <EI>
<SX93,<VA$PA>><SX94,"BX "+<VA@93:1>+":Q2 BX cd
"+<IS93>+"Q2 ">
<SX93,<VA@92:1>>BX <PV93>:Q2 BX cd <PV92>Q2 ;*;
BX exist <PV92>\XYPY.TPLQ2 <IF@not(<ER>)>JM
;JM 2.CallorGo/1Q2
<IF<PV51>==1>BX ab/nvQ2 <GLa><EI>
BX sa/ne/nv <PV92>\xypy.tmpQ2 <IF@not(<ER>)>BX waitQ2
TF BX ch/1 "[wC]EDDIR = ''"[wC]EDDIR = '<PV93>'"Q2 ;*;
<IF@not(<ER>)>BX saQ2 BX waitQ2 BX ab/nvQ2
rsQ2 <EI>;*;
<SX93,"@echo off
if not exist "+<IS91>+"\xypy.pth echo .\xypy{greater_than}
if not exist "+<IS91>+"\xypy\*.* mkdir "+<IS91>+"\xypy
:: Next line creates xy.py, to avoid XCOPY Directory or File prompt
xcopy /q/y "+<IS92>+"\xypy.tmp
xcopy /q/y "+<IS92>+"\xypy.tmp
del "+<IS92>+"\xypy.tmp">BX sa %93,<PV92>\xypy.batQ2
waitQ2 ;*;
BX dos/nv/x/z /c cmd.exe /c <PV92>\xypy.batQ2 ;*;
<PV94><IF@not(<ER>)>BX waitQ2 <SX91,"Created
creating "+<IS92>+"\XYPY.BAT --
XYPY.TPL! Unzip it
"Could not create "xy.py! (Error #"+<IS91>+":
"+<IS50>+" does not exist!"><GLb><EI>
<SV91,Python not installed or RegVar Python_EXE not
<SV91,No window [MAKEXYPY]><GLb>{2}

{{5SV50}} SaVe file contents to S/G 50 (S/G 50 in & out: 
d:\path\filename in; file contents out) [CLD 1/18/09]
{2}<IF<VA|50>{greater_than}0>BX exist <PV50>Q2 
<SX49,<IS50>>JM 2.UsurpBQ2 
<PV51>BX ldpm <PV49>,<PV50>Q2 BX waitQ2 
BX remove "+<IS50>+"Q2 <EI>
<SX50,<IS50>+",50">JM 2.ldnbQ2

{{5swapslash}} Swap "/" for "\" in in_string (S/G 50 in|out) [CLD]

/-- End XyPy routines

[End file XYPYDOC.TXT]