![]() |
The NetRexx Tutorial
![]() |
We collect here all those instructions that have we have not so far had the pleasure to comment on or show, because they did not fall into any of the categories we looked at. This does not imply that they are any less important.
+----------------------------------------------------------------------+ | -- method......: dump |27 | -- purpose.....: dump array's contents |28 | -- |29 | method dump(a=rexx[],name) public static |30 | len = a.length |31 | fil = name'(dim='len')' |32 | fil = fil.left(10) |33 | oval = 'DUMMY' |34 | skip = 0 |35 | dosay = 0 |36 | loop i = 0 to len-1 |37 | if a[i] = NULL |38 | then val = 'NULL' |39 | else val = a[i] |40 | dosay = 0 |41 | if val = oval |42 | then skip = skip+1 |43 | else dosay = 1 |44 | if i = len-1 then dosay = 1 |45 | if dosay then |46 | do |47 | if skip > 0 then |48 | do |49 | if (i = len - 1) then skip = skip - 1 |50 | if skip > 0 then say fil '(...' skip 'lines not display|51 | if i <> len-1 then say fil '['i-1']' oval |52 | skip = 0 |53 | end |54 | say fil '['i']' val |55 | end |56 | oval = val |57 | fil = ' '.copies(10) |58 | end |59 | say |60 | |61 +----------------------------------------------------------------------+ xarray.nrx(Method:dump) | ![]() |
+----------------------------------------------------------------------+ | -- method......: copy |51 | -- purpose.....: copy array's contents |52 | -- |53 | method copy(a=rexx[],b=rexx[]) public static |54 | System.arraycopy(a,0,b,0,a.length) |55 | |56 +----------------------------------------------------------------------+ xarray.nrx(Method:copy) | ![]() |
Let's use the routines we've built in the xarray library.
+----------------------------------------------------------------------+ | -- arrex1.nrx |01 | -- Simple example of array handling |02 | -- |03 | |04 | a = rexx[10] -- define array dimentions |05 | b = rexx[12] -- |06 | |07 | xarray.init(a,") -- initialize array a |08 | a[0] = 'line1' -- with some values |09 | a[1] = 'line2' |10 | |11 | xarray.dump(a,'a') -- look at a and b |12 | xarray.dump(b,'b') |13 | |14 | xarray.copy(a,b) -- copy a to b |15 | |16 | b[0] = 'XXXXXXXXXXXX' |17 | xarray.dump(a,'a') -- look at a and b |18 | xarray.dump(b,'b') |19 | |20 | exit 0 |21 +----------------------------------------------------------------------+ arrex1.nrx | ![]() |
In this small example we consider how to deal with non NetRexx (Rexx) arrays.
+----------------------------------------------------------------------+ | -- tstring.nrx |01 | -- small example of String[] handling |02 | -- |03 | |04 | class tstring1 public |05 | |06 | method t1() returns String[] public static |07 | s = String[2] |08 | s[0] = 'Francesca' |09 | s[1] = 'Elisabetta' |10 | say s.length |11 | return s |12 | |13 | method main(args=String[]) public static |14 | arg = rexx(args) |15 | parse arg . |16 | |17 | in = String[100] |18 | in = t1() |19 | loop i = 0 to in.length - 1 |20 | say in[i] |21 | end |22 | |23 | line = rexx(in) |24 | say line |25 | exit 0 |26 +----------------------------------------------------------------------+ tstring1.nrx | ![]() |
Byte array handling is a bit tedious. This is the motivation of the methods described in xarray.
In a byte array, infact, the quantities are, from the NetRexx point of view, stored as signed integer, so it will be:
a[0] = '01' 1 a[1] = '81' -127 a[2] = 'FE' -2 a[3] = '41' 65
In order to convert it to HEX, for example, you'll need to follow the procedure:
ch = rexx a[2] -> -2 ch = ch.d2x(2) -> FE
The methods we've developed are:
xarray.ba2x(array,start,length) xarray.ba2c(array,start,length) xarray.ba2d(array,start,length) xarray.bagrepx(array,HEX,start)
Using the a[] array, we can look at some simple examples, like:
will give: ---------- xarray.ba2x(a,1,2) -> 81FE xarray.ba2c(a,3,1) -> A xarray.bagrepx(a,'81FE',0) -> 2
REMARK: those methods are SLOW! I should probably find a faster way to implement them. Suggestions are welcome!
+----------------------------------------------------------------------+ | -- method......: ba2x |57 | -- purpose.....: ByteArray to HEX |58 | -- |59 | method ba2x(a=byte[],start=rexx,length=rexx) public static |60 | ostr = '' |61 | loop i = start to start + length - 1 |62 | ch = rexx a[i] |63 | xch = ch.d2x(2) |64 | ostr = ostr||xch |65 | end |66 | return ostr |67 | |68 +----------------------------------------------------------------------+ xarray.nrx(Method:ba2x) | ![]() |
The following method will search an ARRAY for an HEX quantity, which you write in the form (for example):
'A0FF'
the methods returns the value of the FIRST occurrence (from the start) of the HEX string.
+----------------------------------------------------------------------+ | -- method......: bagrepx |89 | -- purpose.....: grep an HEX qty in a ByteArray |90 | -- |91 | method bagrepx(a=byte[],search=rexx,start=rexx) public static |92 | l = search.length() |93 | b = byte[l/2] |94 | |95 | -- convert the HEX string |96 | -- to decimal |97 | -- |98 | list = search |99 | i = 0 |00 | loop while list <> '' |01 | parse list nb +2 list |02 | b[i] = nb.x2d(2) |03 | i = i+1 |04 | end |05 | |06 | lend = a.length - 1 |07 | match = 0 |08 | loop i = start to lend |09 | if a[i] == b[0] then |10 | do |11 | match = 1 |12 | loop j = 1 to b.length - 1 |13 | if b[j] <> a[i+j] then |14 | do |15 | match = 0 |16 | leave |17 | end |18 | end |19 | if match then leave |20 | end |21 | end |22 | if match |23 | then return i |24 | else return -1 |25 | |26 +----------------------------------------------------------------------+ xarray.nrx(Method:bagrepx) | ![]() |
To apply the methods described above, let's write a small program that finds the size, in pixels, of a JPEG picture file.
Without going into details, we say that a JPEG (Joint Photographic Experts Group) file is a binary file. The header looks like:
Marker: FF D8 : FF E0 00 10 ID: 4A 46 49 46 (== JFIF)
JFIF stands for JPEG File Interchange Format. The marker we look at is 'FFC0' that contains the image size.
+----------------------------------------------------------------------+ | -- grab info on JPEG file |01 | -- |02 | parse arg fn . |03 | if fn = '' then |04 | do |05 | say 'usage: java jpginfo FILEID' |06 | exit 1 |07 | end |08 | |09 | -- read input file; |10 | -- if ERROR, abort |11 | -- |12 | fid = xfile(fn) |13 | rc = fid.readbuf() |14 | if rc <> 0 then |15 | do |16 | say 'Error reading file "'fn'".' |17 | exit 2 |18 | end |19 | buf = fid.buffer |20 | |21 | -- check for signature |22 | -- |23 | si = xarray.ba2c(buf,6,4) |24 | if si <> 'JFIF' then |25 | do |26 | say 'Unable to find signature.' |27 | exit 3 |28 | end |29 | |30 | -- find the marker |31 | -- |32 | p = xarray.bagrepx(buf,'FFC0',0) |33 | if p = -1 then |34 | do |35 | say 'Could not locate "FFC0" mark.' |36 | exit 4 |37 | end |38 | |39 | -- all OK, |40 | -- get the info |41 | -- |42 | w = xarray.ba2d(buf,p+7,2) |43 | h = xarray.ba2d(buf,p+9,2) |44 | say h'*'w |45 | |46 | exit 0 |47 +----------------------------------------------------------------------+ jpginfo.nrx | ![]() |
For the graphics formats, look at: http://wsspinfo.cern.ch/faq/graphics/fileformats-faq/part3 The Independent JPEG Group archive on ftp.uu.net contains an on-line copy of the JFIF specification and additional JPEG information. Look at: ftp://ftp.uu.net/graphics/jpeg/jfif.ps.gz ftp://ftp.uu.net/graphics/jpeg/jpeg.documents.gz
We use the xsys.time() function to get the local time in the format "hh:mm:ss" (hours, minutes, seconds). The xsys.time() function can be called with arguments that change the output format a little. The complete list of arguments is:
N - hh:mm:ss - Normal (the default); C - hh:mmxx - Civil L - hh:mm:ss.uuuuu - Long H - hh - Hours M - mmmm - Minutes (minutes since midnight) S - ssss - Seconds (seconds since midnight)
The best way to see all those options is to write a small program that shows all of them. The small timeexa1 program does it.
+----------------------------------------------------------------------+ | -- simple test of the xsys.time() |01 | -- function |02 | |03 | list = 'N C H M S Z' |04 | loop while list <> '' |05 | parse list kind list |06 | say xsys.time(kind) |07 | end |08 | exit 0 |09 +----------------------------------------------------------------------+ timeexa1.nrx | ![]() |
Here is what you get if you run it. The output will of course depends on the time at which you run it.
.................................................................... rsl3pm1 (68) eti1 Option "N" returns: 17:46:30 Option "H" returns: 17 Option "M" returns: 1066 Option "S" returns: 63990 Option "L" returns: 17:46:30.121 Option "C" returns: 5:46pm Option "Z" returns: GMT rsl3pm1 (69) .................................................................... |
You usually need to measure time intervals in your programs. In this way you can measure how long an operation takes to perform.
You can use the Java System class System.currenttimemillis()time method, and measure the time differences yourself.
now = System.currenttimemillis
This method returns the current time in milliseconds GMT since the EPOCH (00:00:00 UTC, January 1, 1970).
The numbers returned are BIG
We define then a timer class. The two basic instructions are:
(...) -- define a timer timer1 = timer() (...) (...) -- get the elapsed time elapsed = timer1.elapsed() (to get the elapsed time since the LAST reset) (...) (...) -- reset the timer zero = timer1.reset() (to reset the timer) (...)
+----------------------------------------------------------------------+ | -- method......: elapsed |46 | -- purpose.....: returns the elapsed time in SSSS.MMM |47 | -- |48 | method elapsed() public returns Rexx |49 | current = System.currenttimemillis |50 | numeric digits 16 |51 | delta = current - start |52 | delta = delta/1000 |53 | numeric digits 9 |54 | delta = delta.format(NULL,3) |55 | return delta |56 | |57 +----------------------------------------------------------------------+ xsys.nrx(Method:elapsed) | ![]() |
+----------------------------------------------------------------------+ | -- method......: reset |29 | -- purpose.....: reset the timer; returns '0.000' seconds |30 | -- |31 | method reset() public returns Rexx |32 | start = System.currenttimemillis |33 | return '0.000' |34 | |35 +----------------------------------------------------------------------+ xsys.nrx(Method:reset) | ![]() |
* * WARNING: * REXX's date function * will be implemented in xsys v2.000. *
Use the date() instruction to get the current local date in the format 'dd Mmm yyyy'. As we saw for time() also date() has many options. These are:
N - dd Mmm yyyy - Normal; E - dd/mm/yy - European; U - mm/dd/yy - USA; O - yy/mm/dd - Ordered; C - ddddd - days (so far) in this century; D - ddd - say (so far) in this year; S - yyyymmdd - Standard;
As for time(), we do the same exercise also for date(). I simply write the results, since the program is easily modified from eti1.
.................................................................... rsl3pm1 (75) eda1 Option "N" returns: () 5 Feb 1995 Option "E" returns: () 05/02/95 Option "U" returns: () 02/05/95 Option "O" returns: () 95/02/05 Option "S" returns: () 19950205 Option "C" returns: () 34734 Option "D" returns: () 36 Option "M" returns: () February Option "W" returns: () Sunday rsl3pm1 (76) .................................................................... eda1.out |
The NetRexx xsys function xdate (for eXtended DATE) is the function for performing all imaginable operations related to date. The original code was developed for VM/CMS by Bernard Antoine of CERN/CN in IBM/370 assembler code. The version I describe here is a porting of that code done by its original author in pure NetRexx.
This code is totally platform independent, and is available on the WWW NetRexx Tutorial page (in the xsys library). xdate can be used in two ways:
- to display a certain date in a given output format (ex: xsys.xdate('TODAY','U') ) - to perform a conversion of a date from one format to another (ex: xsys.xdate('E','01/12/95','J') )
The valid input formats are:
D,ddd - number of days since the beginning of the year format; J,[yy]yyddd - julian format; S,[yy]yymmdd - sorted format; O,[[yy]yy/]mm/dd - ordered format; E,dd/mm[/[yy]yy] - European format; U,mm/dd[/[yy]yy] - USA format; B,nnnn - number of days since the January 1st, 0001 format; C,nnnn - number of days since the beginning of the century format; K,[yy]yyww - format according to ISO 2015 & 2711; I,nnnn - incremental format; I,+nnnn I,-nnnn
Output_format may be any single character accepted by the REXX DATE function:
O to obtain the date in ordered form, i.e. yy/mm/dd U to obtain the date in USA form, i.e. mm/dd/yy E to obtain the date in European form, i.e. dd/mm/yy S to obtain the date in 'sorted' form, i.e. yyyymmdd J to obtain the date in julian form, i.e. yyddd B to obtain the number of days since the January 1st, 0001 C to obtain the number of days since the beginning of the century D to obtain the number of days since the beginning of the year M to obtain the month name W to obtain the weekday name
In addition, XDATE also accepts:
I to obtain the date in increment form Ñ i.e. relative to today K to return the id of the current week, in the form yyyyww (according to ISO 2015 & 2711) L a logical value to tell if the year is a leap one or not N to obtain the month num (instead of name as in M) in the range 1Ð12 X to obtain the weekday num (instead of name as in W) in the range 1Ð7
Here is a small example of the xdate function (look at the comments to see what the program really does):
+----------------------------------------------------------------------+ | -- xdt0 |01 | -- Exercise a bit the XDATE functions |02 | -- |03 | |04 | -- Get today's date |05 | -- |06 | say xmisc.xdate('TODAY') |07 | |08 | -- Get next monday's |09 | -- |10 | say xmisc.xdate('NEXT','MONDAY') |11 | |12 | -- convert 31 DEC 1994 in from European to Julian Format |13 | -- |14 | say xmisc.xdate('E','31/12/94','J') |15 | |16 | -- find out which weekday I was born |17 | -- |18 | say xmisc.xdate('E','28/09/67','W') |19 | |20 | -- find out which date will be in 1000 days |21 | -- |22 | say xmisc.xdate('I',1000,'E') |23 | |24 | -- find out how many days I have |25 | -- |26 | say xmisc.xdate('TODAY','C') - xmisc.xdate('E','28/09/67','C') |27 | |28 | -- find out when I'll have 20000 days |29 | -- |30 | nn = xmisc.xdate('TODAY','C') - xmisc.xdate('E','28/09/67','C') |31 | nn = 20000 - nn |32 | say xmisc.xdate('I', nn , 'S') |33 | |34 | say 'Today is:' xmisc.date('E') |35 | say ' ' xmisc.date('W') |36 | |37 | exit |38 +----------------------------------------------------------------------+ xdt0.nrx | ![]() |
NOTEs: And here is the output:
.................................................................... rsl3pm1 (39) java xdt0 5 Feb 1995 94365 Thursday 01/11/97 9992 20220701 rsl3pm1 (40) .................................................................... xdt0.out |
If you are wondering about all the possible output formats, here is a program for showing them:
+----------------------------------------------------------------------+ | -- xdt1.nrx |01 | -- exercise all XDATE formats |02 | -- |03 | |04 | kind = 'O U S J B C D M W I K L N X' |05 | loop while kind <> '' |06 | parse kind item kind |07 | date = xmisc.xdate('TODAY',item) |08 | say 'Format "'item'" is: 'date'.' |09 | end |10 | exit 0 |11 +----------------------------------------------------------------------+ xdt1.nrx | ![]() |
And this is what you will get if you run the program:
.................................................................... rsl3pm1 (43) java xdt1 Format "O" is: 95/02/05. Format "U" is: 02/05/95. Format "S" is: 19950205. Format "J" is: 95036. Format "B" is: 728328. Format "C" is: 34734. Format "D" is: 36. Format "M" is: February. Format "W" is: Sunday. Format "I" is: 0. Format "K" is: 199505. Format "L" is: 0. Format "N" is: 2. Format "X" is: 7. rsl3pm1 (44) .................................................................... xdt1.out |
It is often usefull to sleep() N seconds. The easyest way is to call the Thread.sleep() function:
-- just pause MILLISEC Thread.sleep(MILLISEC)
where MILLISEC is the time you want to sleep (expressed in milliseconds).
As we saw in the previous chapters, there is ONLY one native data type in NetRexx, and that is the string. NetRexx considers even the numbers as strings. Indeed, you can build yourself data types, the most useful one being the following:
list (string) (stem) = 'ITEM1' , ---> value[ITEM1] 'ITEM2' , ---> value[ITEM2] 'ITEM3' , ---> value[ITEM3] (...) 'ITEMN' ---> value[ITEMN]
We have a string that holds a list of items, which are in their turn pointers for an array (or for many arrays) holding the data for that particular array.
We want to see how this data structure works in practice. An accounting program may be the best way. Supposing we are producing some accounting records whenever an user prints something on a printer, an accounting record is generated. The format of these records would be the following:
date userid nodeid printerid no_of_pages
where:
date.......: the date in the format YYMMDDhhmmss; userid.....: the user identifier; nodeid.....: the node he used to print from; printerid..: the name of the printer; no_of_pages: how many pages he printed.
Here is a small (usually this kind of files is MUCH bigger) example of such a file:
+------------------------------------------------------------------+ | 110195110233 mount slil308.cern.ch prt21 200 | | 110195120000 marchesi rsl3pm1.cern.ch prt56 82 | | 120195120340 marchesi rsl3pm1.cern.ch prt56 20 | | 120195123030 mount hpl3sn1.cern.ch prt11 1 | | 120195123400 clare shift31.cern.ch prt11 25 | +------------------------------------------------------------------+ printer.CARDS |
The structure of our accounting program will be:
READ the accounting file REDUCE the data POST processing (if any) DISPLAY results
In our first version for this program, we simply want to see how many pages a user has printed. The following program (called pracc) will do it. In the first portion of the code, we check for the input argument and read a file. We will not go into the details: what we do is simply get the lines of the accounting cards into the array infid.line[].
+----------------------------------------------------------------------+ | /* prologue |01 | */ |02 | parse arg fid . |03 | if fid = '-h' then |04 | do |05 | say 'usage pacc <fid>' |06 | exit 3 |07 | end |08 | if fid = '' then fid = 'printer.CARDS' |09 | if \xfile.fexist(fid) then |10 | do |11 | say 'file "'fid'" does not exist.' |12 | exit 4 |13 | end |14 | infid = xfile(fid) |15 | rc = infid.read() |16 | if rc <> 0 then |17 | do |18 | say 'RC:' rc 'from READ.' |19 | exit 3 |20 | end |21 | |22 +----------------------------------------------------------------------+ pracc.nrx | ![]() |
We are now ready to analyse our data ,i.e. the lines contained in the stem CARDS. As you can see, we loop over the accounting cards Ñ from the first over to the last one. We parse the information contained in a card line 28. We check if the user contained in the card is known. If not, we add the user to the 'known users' list (user_list), and just for double security, we initialise the number of pages printed to 0 (line 32). We then add the pages for this accounting card to the total for the user.
+----------------------------------------------------------------------+ | /* Data Collection |23 | */ |24 | user_list = '' |25 | pages_printed_by = 0 |26 | loop i = 1 to infid.lines |27 | parse infid.line[i] date user node printer pages |28 | if user_list.wordpos(user) = 0 then |29 | do |30 | user_list = user_list user |31 | end |32 | pages_printed_by[user] = pages_printed_by[user] + pages |33 | end |34 | |35 +----------------------------------------------------------------------+ pracc.nrx | ![]() |
If we take the data we showed in the example printer.CARDS, this is what we get at the end of the code:
user_list = 'mount marchesi clare' pages_printed_by.mount = 201 pages_printed_by.marchesi = 102 pages_printed_by.clare = 25
Now that the raw data is reduced in this format, we can do whatever we want over it: order by name of the user the user_list, order by number of printed pages, etc. We can even do nothing, such as here:
+------------------------------------------------------------------+ | /* post process |37 | */ |38 +------------------------------------------------------------------+ |
Now we can display the 'reduced' data. This is just a loop over the users, and each time we will display the user and the pages printed.
+----------------------------------------------------------------------+ | /* display |36 | */ |37 | list = user_list |38 | loop for list.words() |39 | parse list item list |40 | say item.left(12,'.')':' pages_printed_by[item].right(7) |41 | end |42 | |43 | /* end |44 | */ |45 | exit 0 |46 +----------------------------------------------------------------------+ pracc.nrx | ![]() |
That is all. Here is what you get from the program itself:
.................................................................... rsl3pm1 (23) java pracc2 mount.......: 201 marchesi....: 102 clare.......: 25 rsl3pm1 (24) .................................................................... pacc.out |
Suppose that now your manager asks you to have the report not only for users, but ALSO for printers. The modifications are quite trivial you simply need to create a new list for the printers, and clone the logic you used so far:
+----------------------------------------------------------------------+ | /* prologue |01 | */ |02 | (LIKE ABOVE) |03 | |22 | /* Data Collection |23 | */ |24 | user_list = '' |25 | printer_list = '' |26 | pages_printed_by = 0 |27 | loop i = 1 to infid.lines |28 | parse infid.line[i] date user node printer pages |29 | if user_list.wordpos(user) = 0 then |30 | do |31 | user_list = user_list user |32 | end |33 | if printer_list.wordpos(printer) = 0 then |34 | do |35 | printer_list = printer_list printer |36 | end |37 | pages_printed_by[user] = pages_printed_by[user] + pages |38 | pages_printed_by[printer] = pages_printed_by[printer] + pages |39 | end |40 | |41 | /* display |42 | */ |43 | list = user_list |44 | loop for list.words() |45 | parse list item list |46 | say item.left(12,'.')':' pages_printed_by[item].right(7) |47 | end |48 | |49 | list = printer_list |50 | loop for list.words() |51 | parse list item list |52 | say item.left(12,'.')':' pages_printed_by[item].right(7) |53 | end |54 +----------------------------------------------------------------------+ pracc2.nrx | ![]() |
And this is what you will get on your screen:
.................................................................... rsl3pm1 (58) pacc2 Users: clare.......: 25 marchesi....: 102 mount.......: 201 Printers: prt11.......: 26 prt21.......: 200 prt56.......: 102 rsl3pm1 (59) .................................................................... pacc2.out |
Another kind of data structure are linked lists. With NetRexx you can easily simulate a linked list data structure. I remind you of what a linked list is:
POINTER ---> data.1 +--> data.2 | info.1 | info.2 | next.1 --+ next.2 --> NULL
A good case study for the linked lists is a program for building a ps command tree. The UNIX ps command is used to show the current status of processes running on your machine. Each process has an id (the processid) and a parent process (also called ppid). The output of the ps command does not immediately show how a process is "linked" in terms of parent process to the previous ones. Here is a typical example:
.................................................................... rsl3pm1 (226) ps -f USER PID PPID C STIME TTY TIME CMD marchesi 8161 13399 6 20:35:42 pts/4 0:00 rexx ps1 marchesi 10723 13026 3 20:35:42 pts/4 0:00 bsh bsh bsh marchesi 13026 8161 1 20:35:42 pts/4 0:00 rexx ps1 marchesi 13399 9555 1 Jan 25 pts/4 0:06 -usr/local/bin/tcsh marchesi 14564 10723 8 20:35:42 pts/4 0:00 ps -f rsl3pm1 (227) .................................................................... ps1.out |
For our discussion, the important columns are the second and the third: the process that started all is the PID 13399; it generated PID 8161; which generated 13026; which executed 10723; which finally executed 14564 (and fortunately for us, nothing else other than printing what you see here). This is an "easy" case: if we had done a 'ps -ef', you would have got even more than 100 processes in no particular order. Our pstree wants to make order in this 'mess', and see how each process is linked by the parental relationship. The following code does the job. We skip all the 'unrelevant' portion of the program, since it does not add anything to our discussion. The first thing we do is execute the ps command with the proper options, depending on whether we want to see all the processes of the system ps -ef or just the ones belonging to us ps -f.
+------------------------------------------------------------------+ | if all |42 | then rc = xexec('ps -ef' , 'ARRAY' , 'ABORT') |43 | else rc = xexec('ps -f' , 'ARRAY' , 'ABORT') |44 +------------------------------------------------------------------+ |
We reorder copy the array out[] into the array ps[]. We skip the very first line of the ps command output.
+------------------------------------------------------------------+ | j = 0 |45 | loop i = 2 to out[0] |46 | j = j+1 |47 | ps[j] = out[i] |48 | end |49 | ps[0] = out[0] -1 |50 +------------------------------------------------------------------+ |
We now create two lists: the pidl is a string containing all the process_ids, while the ppidl is a string containing all the processes that are parents. The full information about the process is stored in the array info[PID] and the parent for each process is in ppid[PID]
+------------------------------------------------------------------+ | pidl = '' |52 | ppidl = '' |53 | do i = 1 to ps[0] |54 | parse ps[i] . pid ppid . |55 | pidl = pidl pid |56 | ppidl = ppidl ppid |57 | info[pid] = ps[i] -- full process info |58 | ppid[pid] = ppid -- parent |59 | end |60 +------------------------------------------------------------------+ |
We loop over the process list. We look for the processes that are not parents of other processes. Those processes are saved in lastl: they are the last in a chain of processes.
+------------------------------------------------------------------+ | list = pidl |62 | lastl = '' |63 | loop list.words() |64 | parse list item list |65 | if ppidl.wordpos(item) = 0 then |66 | do |67 | lastl = lastl item |68 | end |69 | end |70 +------------------------------------------------------------------+ |
Now the most tricky part. We start from all the processes in lastl and go backwards. This is where we use the pseudo linked list. For each process in lastl we build the chain with the processes in order of generation.
+------------------------------------------------------------------+ | list = lastl |72 | loop list.words() |73 | parse list item list |74 | titem = ppid[item] |75 | chain[item] = titem item |76 | loop forever |77 | titem = ppid[titem] |78 | if pidl.wordpos(titem) = 0 then leave |79 | chain[item] = titem chain[item] |80 | end |81 | end |82 +------------------------------------------------------------------+ |
Et voila': we have now only to print this chain.
+------------------------------------------------------------------+ | list = lastl |84 | loop list.words() |85 | parse list item list |86 | llist = chain[item] |87 | say ' ' |88 | loop llist.words() |89 | parse llist item2 llist |90 | parse info[item2] owner p1 p2 . rest |91 | say p1.left(6) p2.left(6) '['owner']'left(,10) rest.left(50) |92 | end |93 | end |94 +------------------------------------------------------------------+ |
A short output example:
.................................................................... rsl3pm1 (231) pstree -a (...) 1 0 [root] Jan 19 - 9:28 /etc/init 2584 1 [marchesi] Jan 19 hft/0 0:01 -tcsh 5668 2584 [marchesi] Jan 19 hft/0 0:00 xinit 6698 5668 [marchesi] Jan 19 hft/0 0:19 mwm 7220 6698 [marchesi] Jan 19 - 0:16 aixterm 8765 7220 [marchesi] Jan 19 pts/0 0:01 -tcsh 12329 8765 [marchesi] Jan 24 pts/0 0:00 rlogin sgil301 -l f 13610 12329 [marchesi] Jan 24 pts/0 0:00 rlogin sgil301 -l f 1 0 [root] Jan 19 - 9:28 /etc/init 9555 1 [marchesi] Jan 25 hft/0 1:16 aixterm 13399 9555 [marchesi] Jan 25 pts/4 0:06 -usr/local/bin/tcsh 8174 13399 [marchesi] 20:59:12 pts/4 0:00 rexx pstree -a 13039 8174 [marchesi] 20:59:13 pts/4 0:00 rexx pstree -a 10736 13039 [marchesi] 20:59:13 pts/4 0:00 bsh bsh bsh 14577 10736 [marchesi] 20:59:13 pts/4 0:00 ps -ef 1 0 [root] Jan 19 - 9:28 /etc/init 7746 1 [marchesi] Jan 19 hft/0 6:15 aixterm 9030 7746 [marchesi] Jan 19 pts/2 0:03 -usr/local/bin/tcsh 15292 9030 [marchesi] 20:14:31 pts/2 0:48 x rxuser.texinfo rsl3pm1 (231) .................................................................... pstree.out |
You can find additional information about data structures in Java at those URLs:
http://www.geocities.com/SiliconValley/Way/7650/javadata.html http://www.objectspace.com/jgl/
*** This section is:*** and will be available in next releases
A resume' of the main concepts encountered in this chapter.
*** This section is:*** and will be available in next releases