+ Reply to Thread
Results 1 to 4 of 4

Thread: Listing a Directory Tree Contents

  1. Default Listing a Directory Tree Contents

    This Article was published in the March 2010 edition of iSeries Nation Network newsletter. The original article will be found in the Tekkie-Corner (Chapter 3) at http://www.sss-software.de/inn/power...-PowerInfo.pdf


    Listing a Directory Tree Contents
    Kit von Blerk

    My task recently was to attach x number of attachments to an email. “Piece of cake”, I hear you say. Allow me to explain the challenge then.

    The challenge here is that the IFS path as well as the extension of the document e.g. .PDF, .DOCX, .XLS, etc. (we will call them files for the duration of this article), is not known at the time the user chooses which file should be attached to which outgoing document type e.g. order confirmation, delivery note, invoice, etc. (we will call them documents for the duration of this article).

    In this TechTip, we will see that with just a few simple commands, one can obtain a wealth of information about the IFS file(s) to present to the user. But before we start, let me add: Yes, I know we could find a file in a less complicated way, and yes, I know we could make it more elegant as well – but I use this as an example only. Please note that this TechTip is written for v5r2.

    What we have
    The user currently has the facility to add (header and/or footer) text to outgoing documents which also includes a language delimiter and is defined by a 5 character Code field. Any number of Text Codes could be added to a document. A choice of text language is also available.

    So for the purpose of this exercise, we’ll assume the following:
    Text Code = TOC
    Year = 10
    Language = F

    So, your IFS structure might look something similar to picture below, with these 6 files occurring in each sub-directory of the Attachments directory.

    Attachment 381
    **click to enlarge

    What we want
    Let’s say the user wants to add the latest Terms and Conditions (TOC) to a document. But the TOC for 2009 is different to those for 2010, and the TOC for USA is different to those for Germany, and the TOC for customer A is different to those for customer B… and so on.

    Let’s do it
    Now let’s get the tree structure. To keep it simple, we’ll use the FIND QShell command and override the STDOUT output to a file.

    If we wanted just the directory, it would be simple from there on - and I would have nothing more to write about – but in this case we want more information about the document, for example the user name.

    So to list out the subdirectories and their files, we use the LIST command and again we override the output to a file. This command can give us all of the information we need for this exercise. There is a catch though. The output of this command lists the directory on one line, and all the contents therein on subsequent lines. In addition to that, the position of the data is dependent on how long the user name, of the longest user name for that directory, is. This means that the position (and length) of the “virtual” columns of one directory, does not have the same position as that of another sub-directory.

    So let’s wrap the above in a little CL program and our parameters are as follows;
    &AttchPath = /runtime/
    &DocName = TOC10F
    &PgmError = 00

    Code:
    /********************************************************************/
    /* SYSTEM       - Systems                                           */
    /* AUTHOR       - Deon A von Blerk (EcofIT Ltd)                     */
    /* DATE         - 25/01/09                                          */
    /* Description  - List the IFS files for a directory tree           */
    /*                                                                  */
    /*     MODIFICATION HISTORY                                         */
    /*     ====================                                         */
    /*   Date     Nr  By/Reason                                         */
    /*   ----    ---  ---------                                         */
    /********************************************************************/
    PGM        PARM(&AttchPath &DocName &PgmError)
    DCL        VAR(&AttchPath) TYPE(*CHAR) LEN(64)
    DCL        VAR(&DocName) TYPE(*CHAR) LEN(32)
    DCL        VAR(&QSHSTMT) TYPE(*CHAR) LEN(200)
    DCL        VAR(&Exists) TYPE(*CHAR) LEN(2)
    DCL        VAR(&PgmError) TYPE(*CHAR) LEN(2)
     
    MONMSG     MSGID(CPF0000 QSH0000) EXEC(GOTO CMDLBL(FAIL))
     
    /********************************************************************/
    /* Create the workfiles                                             */
    /********************************************************************/
    DLTF       FILE(QTEMP/QSHLIST)
    MONMSG     MSGID(CPF0000)
    CRTPF      FILE(QTEMP/QSHLIST) RCDLEN(200)
    DLTF       FILE(QTEMP/QSHFIND)
    MONMSG     MSGID(CPF0000)
    CRTPF      FILE(QTEMP/QSHFIND) RCDLEN(200)
     
    /********************************************************************/
    /* Fill the data                                                    */
    /********************************************************************/
    OVRDBF     FILE(STDOUT) TOFILE(QTEMP/QSHLIST) OVRSCOPE(*JOB)
    CHGVAR     VAR(&QSHSTMT) VALUE('ls -lRT' *BCAT &AttchPath)
    QSH        CMD(&QSHSTMT)
    DLTOVR     FILE(STDOUT) LVL(*JOB)
     
    OVRDBF     FILE(STDOUT) TOFILE(QTEMP/QSHFIND) OVRSCOPE(*JOB)
    CHGVAR     VAR(&QSHSTMT) VALUE('find' *BCAT &AttchPath *TCAT '*')
    QSH        CMD(&QSHSTMT)
    DLTOVR     FILE(STDOUT) LVL(*JOB)
     
    /********************************************************************/
    /* Now check if the document exists                                 */
    /********************************************************************/
    CALLPRC    PRC(IFS50R) PARM(&DocName) RTNVAL(&PgmError)
    GOTO       CMDLBL(PERFECT)
     
    /********************************************************************/
    /* Fail                                                             */
    /********************************************************************/
    FAIL:
    DLTF       FILE(QTEMP/QSHLIST)
    MONMSG     MSGID(CPF0000)
    DLTF       FILE(QTEMP/QSHFIND)
    MONMSG     MSGID(CPF0000)
    CHGVAR     VAR(&PgmError) VALUE('01')
     
    /********************************************************************/
    /* EOJ                                                              */
    /********************************************************************/
    PERFECT:
    ENDPGM
    So we now have 2 flat files; one of which (QSHFind) is straightforward and easy to work with, the other (QSHList) is not quite what we want. As we can see from the picture below, each directory content row is aligned to the longest user name in that directory. But we do have one constant character in the row – the ‘:’ character in the time delimiter. Other important information (for this exercise) in the row is the ‘1’ or ‘2’ in column 13 where 1 = file and 2 = sub-directory.

    Attachment 380
    **click to enlarge

    We now need a little SQLRPGLE service program to display the results. The results can be done a couple of ways; straightforward SQL SELECT, with views, or using a UDTF. I’ve chosen the former, mainly for the reason that I’m working over QTEMP.
    Code:
    Declare QSHDocs_csr cursor for
    with ListOut as 
    (select
    locate(':', QSHList, 51) -1 as Offset,
    QSHList
    from QSHList
    where substr(QSHList, 13, 1)  = '1'
    )
    select distinct QSHFind, 
    substr(QSHList, 47+Offset-5, 12) as DateTime,
    substr(QSHList, 55+Offset) as Document,
    substr(QSHList, 15, Offset+1) as Owner
    from QSHFind, ListOut
    where QSHFind like '%' concat trim(:DocName) concat '%'
    and substr(QSHList, 55+Offset)
    like '%' concat trim(:DocName) concat '%'; 
    This will then produce the list depicted below. You will note that I additionally select the QSHFind field as this is simple to store as a hidden field in the subfile which can then be opened directly from your display application. This way, we do not have to build a path and string bits and pieces together to enable the user to inspect or change the document.


    Attachment 382
    **click to enlarge


    Food for thought
    • This method can be used instead of the old-fashioned way of parsing for many of the IBM commands where the output options are only * or *PRINT i.e. no *OUTFILE option.
    • SQL is capable of using inline calculations in expressions (as in the “where” clause above).
    • In the CL, you could use more comprehensive message handling, including the use of environment variables; QIBM_QSH_CMD_OUTPUT and QIBM_QSH_CMD_ESCAPE_MSG. This obviously depends on the requirement of your application and or shop standards.
    If you wish to comment on this article, you can do so at http://www.support.ecofitonline.com/forumdisplay.php?28-Articles


    About the Author:
    Kit von Blerk is CEO of EcofIT Limited, an IBM i (AS/400, iSeries, System I, …) Software Solutions provider based in Germany. He started programming in COBOL (on an ICL ME29) in 1983 and changed to RPGIII on an IBM S/38 in 1986.
    Last edited by kitvb1; 24th November 2010 at 01:11. Reason: Highlighted "click to enlarge"
    Regards

    Kit

  2. Default

    Hi All.

    I have discovered a 6-month bug in this program.

    After sitting in IFS for 6 months, the display of the timestamp format of the 'ls' command changes from 'mmm dd tt:tt' to a datestamp of 'mmm dd yyyy' and the time is omitted. This is appears to be standard for linux, aix, etc.

    To correct this, change the relative line in the CLP to:
    Code:
    CHGVAR     VAR(&QSHSTMT) VALUE('ls -lRT' *BCAT &AttchPath)
    Regards

    Kit

  3. #3

    Default

    This Article has now also been published in the 9 December 2010 edition of System iNetwork Programming Tips edited by Scott Klement. In the new edition I added the Unix date quirk. This article can be found here http://systeminetwork.com/article/li...-tree-contents
    Regards

    Kit

  4. Default

    There is a limitation of 16327 for the number of entries that the QSH find command.
    You might want to limit the results for the find by adding the docname as a filter.
    Code:
    CHGVAR     VAR(&QSHSTMT) VALUE('find' *BCAT &AttchPath 
                *TCAT &DocName *TCAT '*')
    Regards

    Kit

+ Reply to Thread

Thread Information

Users Browsing this Thread

There are currently 2 users browsing this thread. (0 members and 2 guests)

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may post replies
  • You may post attachments
  • You may not edit your posts
  •