Q L H A C K E R ' S J O U R N A L =========================================== Supporting All QL Programmers =========================================== #30 December 1998 The QL Hacker's Journal (QHJ) is published by Tim Swenson as a service to the QL Community. The QHJ is freely distributable. Past issues are available on disk, via e-mail, or via the Anon-FTP server, garbo.uwasa.fi. The QHJ is always on the look out for article submissions. QL Hacker's Journal c/o Tim Swenson 2455 Medallion Dr. Union City, CA 94587 swensont@geocities.com swensont@jack.sns.com http://www.geocities.com/SiliconValley/Pines/5865/index.html EDITOR'S FORUMN In this issue I want to take a look at a couple of Unix languages that have recently been ported to the QL. In the ql-users mailing list, I've seen some comments that basically say "Why use these other languages on the QL? All development should be done in SuperBasic or C68 and use the Pointer Environment." As much as I agree with this, I don't see Perl or AWK being used to do serious development on the QL. I see them being used to accomplish short personal tasks. There are some tasks that can be done much easier with AWK or Perl than in SuperBasic or C68. Of course, there is the ability to use Perl/AWK programs from other environments on the QL and the ability to learn Perl/AWK even if you don't have access to a Unix system. I've done some Perl stuff at work because it is the best Unix language to hack in, and now that I have Perl on the QL, I can run the same programs at home. West Coast Sinclair Show Since the last US QL Show in Bedford, PA, there has been some talk about having two shows in 1999, one on the East Coast and one on the West Coast. The vendors support the idea, having one show right after another so they can hit both in one trip. I think they are just tired of the East Coast and want to visit sunny California. So, Don Walterman (who has recently moved out West) and I are organizing the West Coast Sinclair Show. This show will encompass all Sincliar computers with most of the vendors covering the QL and Z88, but we hope to have something of interest to all Sinclair users. At the moment the show is only in the planning stages. We are worried about getting enough people to attend to make it work having a show. We would like to get about 20-30 people to show. Here is what is being planned. The show will be in Union City, CA, on the east side of the San Francisco Bay, about half way between Oakland and San Jose. The show will be held in the Mehran Banquet Hall, a store front used by a Pakistani restaurant next door. The Banquet Hall is within walking distance of the Union City BART station (local commuter rail) and only about 2 miles off I-880. The show will be held on 5 June. For those coming from out of the Bay Area, the South Hayward Motel 6 is the motel of choice. It has a Denny's and McDonalds on the premisis and has a Taco Bell within walking distance. The motel has bus access to the Union City BART station (and then to the show site). The night before the show, I plan to have a Bar-B-Q at my house (about 1.25 miles from the motel). I'll supply the hamburgers, hot dogs, chips, and soda (Jochem, there will be 2 kinds of Root Beer). For any Europeans coming to the show, I'm offering a limited tourist service. Let me know what you would like to see in the Bay Area, how you plan to get around, and I will look into maps, travel directions, and any transit costs. If you only know of a general category, say natural history or aviation, I can provide a list of places of interest. Since there will be a week between the two shows, this will allow plenty of time for sightseeing in the Bay Area. If you are interested in coming to the show, please let me know. I am especially interested in hearing from those on the West Coast. I would hate to see most of the attendees be from Europe. AWK AWK has been ported to the QL by Peter Tillier and is a Unix language that is used for all sorts of list processing tasks. The name comes from the three writers of the language; Aho, Weinberger, and Kernighan. AWK works very similar to grep. For each line of input, it searches for a given pattern. If the pattern matches, then an action is performed. If there is no search pattern, then it is assumed to match and the action is performed. This is the formal definition of what AWK does, in reality, the code looks a lot like other languages, and each line usually does not have a pattern and is therefore executed. How this all works will become clearer as you read the code below. To give an example of how to use AWK, here is something that I'm using AWK to do. I keep a list of people that get the QHJ via e-mail. In the list is the persons last name, first name and e-mail address. From this list I want to generate a e-mail address only list, for sending an issue, and a list of people, sorted by last name. I've created a text file with each field seperated by colons and each record is a seperate line. Here is an example file: test_data: Andrews:Bob:bob@mail.com Johnson:Ralph:rj@newmail.com Smith:John:john@oldmail.com Wilson:Ted:trw@mail.com The first AWK script will print out only the 3rd field from each line. Here it is: rep1_awk: { FS = ":"; print $3; } And the output is: bob@mail.com rj@newmail.com john@oldmail.com trw@mail.com Basically all the script does is this: for each line, change the Field Seperator to be a colon and then print out field #3. The second report is a little more complicated. It will have a header, then the e-mail addreses, and then a footer giving a count of the e-mail addresses. The BEGIN block will only be executed at the beginning of a program, before any work is done on the input file. I'm using the BEGIN block to print out a header for the report. There is also an END block, which will only be executed at the end of the program. Here is the 2nd AWK script: rep2_awk: BEGIN { print " Name E-Mail Address" print "---------------------------------" } { FS = ":" printf("%6s %10s %s\n",$2,$1,$3) } Here is the output: Name E-Mail Address --------------------------------- Bob Andrews bob@mail.com Ralph Johnson rj@newmail.com John Smith john@oldmail.com Ted Wilson trw@mail.com This time I've decided to use a printf statement, which is very similar to the C version of printf. The problem though is that the output of the names is right justified and there is too much space between the first and last name. I want one space between the first and last name. Here is a 3rd AWK script that uses a regular print instead of a printf and gives me what I want: rep3_awk: BEGIN { print " Name E-Mail Address" print "-----------------------------------" count = 0 } { FS = ":" print $2,$1,"\t",$3 count = count + 1 } END { print "-----------------------------------" print " Total Addresses = ",count } Here is the output: Name E-Mail Address ----------------------------------- Bob Andrews bob@mail.com Ralph Johnson rj@newmail.com John Smith john@oldmail.com Ted Wilson trw@mail.com ----------------------------------- Total Addresses = 4 Now if I had to write this program in SuperBasic, I would first have to write a section that splits out the individual fields into different string variables. With AWK it's automatic and very easy to do. PERL Perl is a language that I started playing with back around 1989. When I started using Perl for a project, a few people wondered if this was a good idea, given that Perl was a relatively obcure language and they worried about people knowing Perl after I left. Well, Perl has now become THE language for Unix. There are over 15 books available on Perl, a Perl magazine, and many, many web sites. Perl was designed to be a kitchen-sink language for Unix and allows the programmer to get the same task done many ways. Perl is more expansive than AWK and you can get a lot more done, although some AWK programs will be shorter than Perl. If you do program in AWK and wish to convert to Perl, there is a nice AWK-to-Perl program that comes with the Perl distribution. To show how different AWK is from Perl, let's use the same example programs as above, but write them in Perl. The first Perl program is the same as the first AWK program. Note how many more lines it takes in Perl. I'm sure this Perl program is not the most optimal, but even an optimal Perl program would be longer than the 2 line AWK program. Here is the program in Perl: rep1_pl: #!/usr/bin/perl # Read the file into an array open(FILE,"test_data"); @array = ; close(FILE); foreach $line (@array) { @array2 = split(/:/,$line); print "$array2[2]"; } And the output is: bob@mail.com rj@newmail.com john@oldmail.com trw@mail.com The first line of a Perl program starts with #!/usr/bin/perl, or something like that. This is not a part of the Perl program, but is used to tell the Unix shell to run Perl and feed it the rest of the script. It is not needed for the QL version of Perl. With the second report, I decided to use the 'format' feature of Perl. It allows you to define fields so that the output can be formated into neat columns. Left and right justification and the decimal point can be handled for you. It can take a while to get used to formats, but once you do you find them usefull for all sorts of tasks. Here is the 2nd script: #!/usr/bin/perl #Define formated output format TOP = Name E-Mail Address ------------------------------------------ . format STDOUT = @<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<< $name, $email . format BOTTOM = ------------------------------------------ Total Addresss = @<<<< $count . # Read the file into an array open(FILE,"test_data"); @array = ; close(FILE); $^ = TOP; $~ = STDOUT; $count = 0; foreach $line (@array) { @array2 = split(/:/,$line); $name = $array2[1]." ".$array2[0]; $email = $array2[2]; write; $count = $count + 1; } $~ = BOTTOM; write; Name E-Mail Address ------------------------------------------ Bob Andrews bob@mail.com Ralph Johnson rj@newmail.com John Smith john@oldmail.com Ted Wilson trw@mail.com ------------------------------------------ Total Addresss = 4 As another example, here is a program that I wrote to figure out the odds on die rolls. #!/usr/bin/perl # # dice_pl # # This program determines the percentages in rolling X number # of Y sided dice. The simplest approach to doing this is # to do a something like this (for 2 6-sided die): # # FOR x = 1 to 6 # FOR y = 1 to 6 # roll = x + y # NEXT y # NEXT x # # But this means that the number of dice used is hard coded # into the program. This programs approach is like an # odometer. An array is used that is equal to the number of # die used. The first die is incremented and each other # die is incremented when the one next to each reaches 7 # (still using the 2 6-sided die example). # # Here is a visual diagram: # # ------------------------ # | 1 | 1 | 1 | 1 | # ------------------------ # # Once the first "die" hits 7, it goes back to 1 and the # "die" next to hit rolls over to 2. This continues until # all possible combinations have been made. # # Output is : # Die Roll - the value of the die. # Possible - how many times this die roll can occur. # % of Roll - Percentage of total rolls that this roll # can occur. # To-Hit % - Percent chance of making rolling this number # or less. format top = Die Roll Possible % of Roll To-Hit % -------------------------------------------- . format STDOUT = @### @### @###.## @###.## $x,$rolls,$indper,$totper . die "Usage: dice.pl die_sides num_die\n" if @ARGV < 2; $num_die = $ARGV[1]; $sides = $ARGV[0]; print "\n Roll of $num_die $sides-Sided Die\n"; # set all dice to 1 for ( $x = 1; $x <= $num_die; $x++) { $array[$x] = 1; } $array[1] = 0; for ( $y = 1; $y <= $sides**$num_die; $y++ ) { #Increment the first die $array[1] = $array[1] + 1; $total = 0; # Go through each die and see if they need to "rollover" # then calculate total of dice. for ($x = 1; $x <= $num_die; $x++) { if ( $array[$x] == $sides+1 ) { $array[$x] = 1; $array[$x+1] = $array[$x+1] + 1; } $total = $total + $array[$x]; } $total_array[$total] = $total_array[$total] + 1; } # Report Section # Output is # - Die Roll Of (total of dice) # - Number of Times Possible # - % of that Roll # - % of To-Hit number $total = 0; for ($x = $num_die; $x <= $sides*$num_die; $x++) { $rolls = $total_array[$x]; $indper = ($total_array[$x]/$sides**$num_die)*100; $total = $total + $total_array[$x]; $totper = ($total/$sides**$num_die)*100; write; # print("Die Roll of $x,$total_array[$x], $temp1, $temp2\n"); } When using Perl, remember that there is no "shell" in QDOS. In the standard Perl use, there are a couple of different ways to have Perl run an OS command, like DEL in MS-DOS or 'rm' in Unix. To do this, Perl executes the operating system shell and passes to it the command to execute. This works for real executables or internal shell commands. In QDOS these "shell" calls work out to be really nothing more than an EXEC, or EXEC_W call. There is no way to have Perl run a QDOS/SuperBasic imbedded command, like 'wdir'. Here is an example: system("ls"); This works out the the QDOS command of: EXEC_W ls The Perl command: system("unzip file_zip"); works out to the QDOS command of: EXEC_W unzip;"file_zip" So if the following Perl command: system("dir flp1_"); works out to the QDOS command of: EXEC_W dir;"flp1_" which is invalid. On the QL, the back tick character is really the Pound Sterling symbol (which, for portablilty reasons I won't show here). This means that if you take a Perl program written on another system and bring it to the QL, all of the back tick characters will show up as the Pound Sterling character. This also means that if you are writting a Perl program on the QL, use the Pound Sterling character and it will be treated as a back tick. Since the two characters have the same ASCII value, you don't need to worry about converting, by hand, the two characters, as it will happen automatically. Where this is important in Perl, is in the following command: @array = `ls bin*`; Having a string in back ticks (versus single or double quotes), tells Perl to execute what is in between the back ticks. This is the same convention used in the Unix C Shell scripting language. If you are looking for books on Perl, remember that Perl for QDOS is Perl Version 4 and most books deal with Perl Version 5. The first edition of "Programming Perl" and "Learning Perl" (red binding) dealt with Perl Version 4. There are some major changes between Perl 4 and Perl 5, including some syntax changes in common functions. THE SHELL While on the subject of Unix languages, Adrian Ives has continued the work of P. J. Taylor with The Shell, a command line shell for QDOS. Given my Unix background, I would say that The Shell is similar in functionality to Unix shells (C, Bourne, T, Korn). The Shell considers commands to be programs to execute and handles command line arguments in the standard way. Pipes are created with the pipe ( | ) symbol, and executing programs in the background (like EXEC) is done with the ampersand ( & ). Given that 'grep' is available from the C68 distribution, 'wc' comes with the GNU Text Utilities distribution, and that 'ls' is available with The Shell, you can execute the following command line in The Shell: ls | grep _txt | wc -l Another nice thing about The Shell is the way it handles directories. It treats level 2 directories as real directories. You can 'cd' to a directory, run 'ls' and see only those files in the directory. You can also 'cd /bin' or 'cd ../man'. The Shell understands both the MS-DOS and Unix directory slashes. The Shell also handles files with dots/periods in it (file.txt). So, if you are doing a lot of bringing programs over from other environment and have lots of file with dot extensions, The Shell is handles the files without having to put the files in quotes. The Shell version 1.10 is available from Adrian Ives web page: www.angelfire.com/ab/4fac/