I've always missed having a powerful and portable scripting language on QDOS. S*BASIC of course has its adherents, but if, like me, you don't use it very often, it's easy to forget the syntax, which I find awkward and unnecessarily verbose and, after perl, its not a particularly powerful language.
I'd considered porting perl to QDOS for some time, but as ever, there were more pressing things to do, and besides, it would probably be difficult. As it happened, porting perl wasn't too difficult, initially taking about three hours ...however ironing out the final bugs resulting from the arcane QDOS file system then took a few days longer; the problems having been initially masked by the more flexible and forgiving uqlx file-system. A bug report some weeks after the first release and some testing on 'real' hardware by Thierry Godefroy (thanks Thierry) resulted in a quick solution once the problem had been identified.
The version ported to QDOS is perl 4 (4.036), this is perhaps disappointing as perl 4 is obsolete in the real world (1992 vintage), having been superseded by perl 5, which offers a richer syntax, dynamic loading of language extensions and object orientated methods. perl 4 remains a good introductory approach for QDOS for a number of reasons:
Perl was invented, if that's the right word, by Larry Wall, then a harassed Unix administrator (and well known author of tools such as patch and rn (an internet new reader)), in the late 1980s to solve a practical problem of synchronizing files on two sites a continent apart. Since then it has grown to become the lingua franca running much of the internet and most Web servers, as well as an immensely popular language for system administration on many different operating systems and a powerful text processing language. Many of the ``X2Y'' text format conversion tools (latex2html, html2ps for example) are written in perl.
Perl 5 is available for most modern operating systems, (Unix, Plan 9, Beos, Open VMS, Windows, MSDOS, MacOS, AmigaOS, OS2, OS390, QNX) and is highly portable. The QDOS perl 4 version offers a comparable feature set to other non-Unix perl 4 ports.
Here's how the author describes it, verbatim from the man page.
Perl is a language optimized for scanning arbitrary text files, extracting information from those text files, and printing reports based on that information. It's also a good language for many system management tasks. The language is intended to be practical (easy to use, efficient, complete) rather than beautiful (tiny, elegant, minimal).Perl combines (in the author's opinion, anyway) some of the best features of C, sed, awk, and sh, so people familiar with those languages should have little difficulty with it. (Language historians will also note some vestiges of csh, Pascal, and even BASIC-PLUS.) Expression syntax corresponds quite closely to C expression syntax. Unlike most Unix utilities, Perl does not arbitrarily limit the size of your data-if you've got the memory, Perl can slurp in your whole file as a single string. Recursion is of unlimited depth. And the tables used by hashes (previously called "associative arrays") grow as necessary to prevent degraded performance. Perl uses sophisticated pattern matching techniques to scan large amounts of data very quickly. Although optimized for scanning text, Perl can also deal with binary data, and can make dbm files look like hashes.
If you have a problem that would ordinarily use sed or awk or sh, but it exceeds their capabilities or must run a little faster, and you don't want to write the silly thing in C, then Perl may be for you.
I find that people tend to either love or hate perl. The ``hater's'' hate it because the syntax is weird, there are numerous ways of expressing things, and some claim it a write only (impossible to read) language.
The ``lover's'' love it for the same reasons. The syntax is weird, but that's OK, because its meant to be weird. That there are numerous ways of doing things is great too, it just appeals to our creativity. And its dead easy to read ...as I shall demonstrate, at least to my satisfaction.
Let's say you wanted a program that would change the last underscore in any file name in the current directory into a dot (maybe you wanted to use thq QDOS lynx program), for example:
| From | To |
| test_html | test.html |
| pretty_picture_gif | pretty_picture.gif |
| index_html | index.html |
| AnotherFile | AnotherFile |
while (<*>) {rename($_,$new) if ($new = $_) =~ s/(.*)_(.*)/$1\.$2/;}
And if you wanted to be able to pass in the directory as a parameter,
it would be two:
chdir $dir if ($dir = shift);
while (<*>) { rename($_,$new) if ($new = $_) =~ s/(.*)_(.*)/$1\.$2/;}
which, assuming you'd saved the file as qlmv you'd run as
perl qlmv ram2_if you use Adrian Ives 'sh' shell program, or from QDOS.
ex perl;'qlmv ram2_'And I did say the syntax was a little weird, didn't I? Well it's no more obscure than I find S*BASIC (but then I use perl everyday, and S*BASIC almost never). The two line version does:
if ($ARGV[0])
{
chdir $ARGV[0]; # $ARGV[0] holds any parameter passed in
}
opendir (DIR, ".") || die "Can't open the directory";
@files = readdir DIR; # read all file names into an array (@files)
closedir DIR;
foreach $file (@files) # now examine each file in turn
{
if(($pos = rindex($file,"_"))) # get the last underscore
{
$new = $file; # copy the file name
substr($new, $pos, 1) = '.'; # substitute a dot
rename ($file, $new); # rename the file
}
}
Even without the comments, the program is fairly self explanatory.
But which is the most efficient? Well difficult to say; the first
example (at least on perl4qdos) requires an external program to
perform the ``globbing'' (the while(<*>) construct, but as that
perlglob is a minimal 'C' program, it may be as fast the second
example. The second example is perhaps inefficient in that it uses
rindex/substr rather than s///. So the best solution might
be.
chdir $dir if ($dir = shift);
opendir (DIR, ".") || die "Can't open the directory";
@files = readdir DIR; # read all file names into an array (@files)
closedir DIR;
foreach (@files)
{
rename($_,$new) if ($new = $_) =~ s/(.*)_(.*)/$1\.$2/;
}
Perl's string handling makes many tasks easy. As another example, convert all tabs in a file into spaces, maintaining the correct spacing. Run this fine example as:
perl -pi_bak test_fileThe -pi options cause (-i_bak) the original file to be backed up as test_file_bak, and the -p option to print the changed lines back to the original file name. So here's the massive detab program:
1 while s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e;To avoid having to remember to say -pi_bak each time, perl 5 would let us add a comment to the perl program to tell perl to do that anyway; alas for perl4 we have to remember do give it as part of the command
#!/usr/bin/perl -pi_bak 1 while s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e;Again, a prize for anyone who does it more succinctly in S*BASIC.
We can take this a step further, imagine that you had a directory containing hundreds of articles for 'QL Today', and each of those articles contained hundreds of references to 'QL Today', only, you'd made a horrible mistake and consistently mis-spelt it as 'QL Toady' (pax Dilwyn, the old gags are still the best). perl to the rescue, a simple one-liner on the command line will sort it all out.
ex perl;'-pi -e "s/Toady/Today/g;" *_txt';The -e option allows us to run a perl operation from the command line on an arbitrary list of files. Somewhat easier than changing it all manually.
I hear you groan. What is this? The perl programmer's revenge for tedious pages of S*BASIC in Quanta? Well, just two more examples to go, and then some poetry.
perl is not just about simple but incredibly powerful single line programs; it can handle files and binary data equally well. Another practical example. The InfoZip program unzip is usually distributed as a self extracting archive. This is a program you can run, and it will decompress the archive and you then have a working unzip program. Otherwise you would require unzip to decompress the archive ...well you get the point (catch 22).
This fine scheme fails for QDOS, because you can't distribute executable files (without using zip or similar archiver, and hence needing an unzip) over electronic links and still preserve the vital data space that executable (EX) QDOS programs require. The self extracting archive for QDOS saves its data space at the end of its file, in the last eight bytes, in the same format used by the Linux hosted xtc68 cross-compiler.
| Offset from EOF | Data |
| -8 | "XTcc" |
| -4 | data space |
#!/usr/bin/perl -w
sub setqhead
{
local($f) = shift; # get the parameter
local($magic,$dspc,$res); # local variables
$res = -1; # preset result as 'failed'
open(F,$f) || die "Can't open your file $f ".$!; # open the file
seek(F,-8,2); # and seek to end-8
read(F,$buf,8); # read the data
($magic,$dspc) = unpack("a4N",$buf); # unpack binary data to variables
if($magic eq "XTcc")
{
$hdr = pack("NccNN",tell(F),0,1,$dspc,0); # pack binary strucure
$res = syscall(3,0x46,0,0,-1,fileno(F),$hdr); # call the QDOS trap
} # to set executable and data space
else
{
die "Bad magic\n";
}
close F;
$res; # return (0 == success)
}
$fn = shift || "ram2_unz532xQ.exe"; # get users file name (with default)
system ($fn) unless &setqhead($fn); # run the file is setqhead succeeds
The interesting bits are the $hdr = pack ...line, where the
binary structure used by the trap #3, FS.HEADS (set file
header) is filled with the required values, and the next line, where
standard perl syscall() (system call) is mapped onto the QDOS
trap architecture.
$d0 = syscall($trapno, $d0, $d1, $d2, $d3, $a0, $a1, $a2, $a3);An extension module qdos.pl is provided, allowing constructs like:
require "qdos.pl"; $res = &sd_bordr(STDIN,-1,4,2); &sd_clear(STDIN, -1); &sd_flood(STDIN,-1,1); $res = &sd_elipse (STDIN,-1, (0, 0, 0.5, 40, 1));which again might be intuitive to programmers with some QDOS experience.
The last line system ($fn) unless &setqhead($fn); is worthy of
comment. unless is just a negated if, so the last line
might be written using any of the following:
if(&setqhead($fn) == 0)
{
system($fn);
}
!&setqhead($fn) && system($fn);
system($fn) if (!&setqhead($fn));
&setqhead($fn) || system($fn);usually, there's more than one way to do it (TMTOWTDI). Have I already mentioned that?
perl is pretty good at TCP/IP (Internet) network stuff as well, though unless you're fortunate enough to use uqlx as your QDOS system, this may only be of academic interest. If your uqlx/QDOS system happened to share a network with a Windows machine, which had a version of Windows prior to NT 4.0/SP3, then the following snippet would cause the Windows machine to BSOD (crash). Only you wouldn't want do that, would you?
#!/usr/contrib/bin/perl -w
# you'll need to get these values from your system header files
# these are for Linux or uqlx/Linux.
$AF_INET = 2;
$SOCK_STREAM = 1;
$MSG_OOB = 1;
$port = 139;
$target = shift;
($name, $aliases, $proto) = getprotobyname('tcp');
($name, $aliases, $type, $len, $thataddr) = gethostbyname($target);
$that = pack('S n a4 x8', $AF_INET, $port, $thataddr);
socket(S, $AF_INET, $SOCK_STREAM, $proto) || die "socket: $!";
connect(S, $that) || die "connect: $!";
send(S, "Die,msdog", $MSG_OOB) || warn "send $!";
close S;
And here's a slightly more powerful version of the same program nice
OO perl 5.
#!/usr/bin/perl -w
use IO::Socket;
use IO::Handle;
while(my $host = shift)
{
my $s = new IO::Socket::INET(PeerAddr => $host, PeerPort => 139,
Proto => 'tcp', Type => SOCK_STREAM) or die "Connect $!";
$s->send("WinULoose", MSG_OOB);
$s->close();
}
#!/usr/bin/perl -c
APPEAL:
listen (please, please);
open yourself, wide;
join (you, me),
connect (us, together),
tell me.
do something if distressed;
@dawn, dance;
@evening, sing;
read (books, $poems, stories) until peaceful;
study if able;
write me if-you-please;
sort your feelings, reset goals, seek (friends, family, anyone);
do*not*die (like this)
if sin abounds;
keys (hidden), open (locks, doors), tell secrets;
do not, I-beg-you, close them, yet.
accept (yourself, changes),
bind (grief, despair);
require truth, goodness if-you-will, each moment;
select (always), length(of-days);
# listen (a perl poem)
# Sharon Hopkins
# rev. June 19, 1995
Yes, its legal perl (or at least it compiles).
While the perl 4 documentation is at best adequate, the perl 5 documentation is excellent and I would recommend at least downloading the (perl5) FAQs, as much is relevant to perl 4 as well.
Any comment about perl4qdos should be directed to the author jrhudson@bigfoot.com , or general discussion can be posted to Usenet maus.computer.ql.intl or the FIDONET equivalent for BBS users. Please post any questions to m.c.q.i, so everyone can learn from the answers.
perl -e '$_="wHFG NABGURE cREY UNPXRE,\n";y/a-zA-Z/N-ZA-Mn-za-m/;print;'
This document was generated using the LaTeX2HTML translator Version 98.1p1 release (March 2nd, 1998)
Copyright © 1993, 1994, 1995, 1996, 1997, Nikos Drakos, Computer Based Learning Unit, University of Leeds.
The command line arguments were:
latex2html -no_subdir -split 0 -local_icons perl.tex.
The translation was initiated by Jonathan Hudson on 1998-11-16