Rocket jBASE

 View Only
  • 1.  Add PEQS file to jBASE

    ROCKETEER
    Posted 05-23-2022 13:07
    Edited by Peter Falson 05-23-2022 13:29

    If you have ever used D3, you may have interacted with the PEQS file which contains the hold entries for print jobs.

    Out of the box, jBASE stores its hold entries in a header/detail style:

    • The header can be found in jspool_log keyed by DEVJOBS*job
    • The detail is under the jobs directory and then under the relevant formqueue_n directory.

    Both jspool_log and jobs are located under the directory defined by the environment variable JBCSPOOLERDIR.

    Use jdiag if you're not sure where JBCSPOOLERDIR points.

    At the end of this post is the source code for a jEDI driver that will put the above header/detail and make it appear like the PEQS file.

    To use this (once it's compiled/cataloged):
    create-file peqs type=object class=peqsjedi​

    You should get the following output:

    [ 417 ] File peqs]D created , type = OBJECT
    [ 417 ] File peqs created , type = OBJECT

    We can now list it:

    $ LIST peqs
    
    PAGE    1                                                                                                11:00:16  23 MAY 2022
    
     job #    Job Name..    Statuses    Copies    Q Num        Size    Date.....    Time....    Owner....
    
         1                  HS               1        0         307    23 MAY 22    10:52:41    pfalson
         2                  HS               1        0         399    23 MAY 22    10:52:52    pfalson
         3                  HS               1        0       57383    23 MAY 22    10:52:55    pfalson
    
    
     3 Records Listed

    ...and jstat it...

    $ jstat peqs
    File: ./peqs
    Type: peqsjedi
    
    Total jobs:  3
    Total bytes: 58089
    

    ..and of course edit (or any program you wish to write opening the "peqs" file you create)...

    $ ED peqs 1
    1
    TOP
    .p
    TOP
    001  Port   PID    Account    Locale Location               Date       Time
    002  1      67469  pfalson    C      Peter Falson           23 MAY 22  09:01:30
    003 *2      78073  pfalson    en_US  Peter Falson           23 MAY 22  10:52:35
    004  6000   76654  pfalson    en_US  Peter Falson           23 MAY 22  10:41:49
    BOTTOM
    .


    And now - as promised - the source

        INCLUDE JBC.h
        DEFC VAR SpoolerFileDetails(INT)
        equ asterisk to '*'
    !
    ! This is an example driver for the new jEDI type of JABBA OBJECT.
    ! It is based on the dynobject.jabba template under the samples directory.
    !
    ! To use this. compile as follows
    !
    ! BASIC . peqsjedi.jabba
    ! CATALOG . peqsjedi.jabba
    !
    ! Recommended usage:
    !
    !     create-file fileName type=object class=peqsjedi
    !
    ! This will create a stub file of name 'filename' in the current directory (and a fileName]D for the dictionary)
    !
    ! In all the methods, the convention is that 'return 0' means the operation was successful
    ! and any other integer returned denotes some sort of error
    !
    !
    ! Method:: createfile(fileName , command)
    !
    ! fileName: The name of the file we are creating
    ! command:  We add here any extra information which will be saved
    !           in the stub file that the create-file creates.
    !
    ! Return value: 0 shows we can allow the create-file to continue, and other is an error
    !
        method peqsjedi::createfile(fileName , command )
    !
    ! If this is the DICT portion just return
    !
            this->original_name = fileName
            if index(fileName, ']D', 1) then
                this->dict = 1
                return 0
            end
    !
    ! This is the DATA section but we really want a regular jBASE dictionary file so
    ! we'll remove the ]D stub and create a regular jBASE dictionary
    !
            execute RM_CMD:' ':fileName:']D' capturing nada
            execute 'CREATE-FILE DICT ':fileName:' 1' capturing nada
    !
    ! Populate the DICT with some useful dictionaries to use by jQL
    !
            this->initDicts(fileName)
    !
    ! Look for any additional qualifiers
    !
    ! In this example we're not going to do anything but
    ! you could parse each command line in the "options" property
    !
    ! the ->next method (below) assumes its parameters have
    ! been initialised (if we don't we get a compiler warning)
    !
            keynxt = '' ; value = '' ; type = ''
    
            iter = new object('$iterator' , this->options)
            loop while iter->next(keynxt , value , type) gt 0 do
            repeat
    
            return 0
        end method
    
        method peqsjedi::peqsjedi()
    !
    ! For this jEDI we're allowing debugging (just in case) and/or
    ! tracing. In this case the value of the environment variable
    ! PEQSVERBOSE controls what type.
    !
            if not(getenv('PEQSVERBOSE',Verbose)) then Verbose = 0
            if Verbose > 1 then
                if Verbose = 9 then debug
            end
            this->openSpooler()
            this->Verbose = Verbose
        end method
    
        method peqsjedi::open()
    !
    ! We need to open the current directory because
    ! during the create-file process a Q pointer is
    ! written out which we need to detect in the read method
    !
            open '.' to fileVar else
                return 1 ;! should never happen but stranger things....
            end
            this->fileVar = fileVar
            this->openSpooler()
            return 0    ;! Return with a good return code
        end method
    
        method peqsjedi::close()
    !
    ! The close method has nothing to do in this case
    !
            return
        end method
    
    !
    ! read an item. Parameters are:
    ! record:     This is where we return any data
    ! job:        The item id to read
    ! flags:      A string of one or more characters as follows
    !                 L       A lock is to be taken.
    !                 W       A lock is to be taken, but do not wait.
    !                 R       A lock is to be taken, but a read lock only
    !                 U       Unlock the lock (for WRITE's only)
    !                 S       No lock, just return the status of the lock.
    !                 A       The unlock applies to ALL record keys
    !                 F       Field read/write, see 'fieldnbr' below
    !                         This is normally taken care of by the jEDI interface
    !                         but you can over-ride this behaviour with 'field_reads'
    !                         setting to 1 in the open method.
    !            NOTE:  In the 'open' we leave 'driver_locks' at 0, which
    !                   means jEDI itself does the record locking, not us, so
    !                   the above mentioned flags aren't used by this driver.
    !
    ! fieldnbr:   For field reads (e.g. readv) the attribute number if 'F'
    !               exists in the 'flags' string above.
    !
    ! The value to return is as follows.
    !
    ! 0          The record was read in and is okay, locks taken (if necessary)
    ! 1          The record does not exist.
    ! 2          The record is locked and we are returning because 'W' was in the  'flags'
    !
        method peqsjedi::read(record, job, flags, fieldnbr)
            if job matches "1N0N" then
                rc = this->parseIncomingJob(job, @false)
            end else rc = 0
    
            if this->pathFound then
                f.queue = this->fqueue
                id = this->jobId
            end else
    !
    ! We're probably in the middle of the CREATE-FILE
    !
                f.queue = this->fileVar
                id = job
            end
    
            read record from f.queue,id else
                rc = 1
                record = ''
            end
    
            return rc
        end method
    
    
        method peqsjedi::write(record, job, flags, fieldnbr)
            if job matches "1N0N" then
                rc = this->parseIncomingJob(job, @false)
                if this->pathFound then
                    f.queue = this->fqueue
                    Verbose = this->Verbose
                    if Verbose then crt 'Write job ':job:' back to ':this->filePath
                    write record on f.queue,job
                end
                return rc
            end
            write record on this->fileVar,job on error
                return 2
            end
            return 0
        end method
    
    !
    ! Delete an item from the file.
    !
    ! job:   The item id to delete
    ! flags: Not used
    !
    ! Return:   0 shows the delete worked, else an unspecified error
    !
        method peqsjedi::delete(job , flags)
            if job matches "1N0N" then
                execute 'sp-delete ':job capturing io
                if len(io) then
                    print
                    print io
                    print
                    return 1
                end
            end else
                delete this->fileVar, job
            end
            return 0
        end method
    
        method peqsjedi::select(selectVar)
    !
    ! Start off a SELECT of all the DEVJOBS* items
    !
            selCmd = 'SELECT ':this->LogPath
            selCmd := ' WITH *A0 = "DEVJOBS*]"'
    
            execute selCmd capturing Nada
    
            selectVar = ''
    !
    ! We only want the job nbrs which is the other part
    ! of our DEVJOBS* item id
    !
            loop while readnext id do selectVar<-1> = field(id, asterisk, 2) repeat
    
            return 0
        end method
    
        method peqsjedi::selectend(selectVar)
            return 0
        end method
    
    
        method peqsjedi::readnext(job, selectVar)
    !
    ! read in the next item from the select list.
    !
    ! Return: 0 if we have an item, 1 if end of list, any other is an error
    !
            readnext job from selectVar else
                return 1          ;! EOF
            end
            return 0
        end method
    
        method peqsjedi::clearfile()
    !
    ! CLEARFILE on the file
    !
            crt
            crt 'Operation not supported'
            crt
            rqm
            return 0
        end method
    
        method peqsjedi::deletefile(flags)
    !
    ! DELETEFILE on the file
    !
    ! flags: Not really used
    !
            execute RM_CMD:' ':this->path_name
            return 0
        end method
    
        method peqsjedi::sync()
    !
    ! SYNC. This means that all data that might be in cache, waiting to
    ! go to a network, not gone to disc etc., should be flushed now.
    !
    ! This is not relevant for this example
    !
            return 0
        end method
    
    !
    ! This method is called on "jstat"
    !
        method peqsjedi::stats(options)
    !
    ! Whatever we want here.
    ! "options" are the command line arguments
    ! We could get creative...but not today :-)
    ! For now let's just give the total jobs and byte count
    !
            totalBytes = 0
            totalJobs = 0
    
            selectVar = ''
            this->select(selectVar)
            job = ''
            loop
                rc = this->readnext(job, selectVar)
            while rc eq 0 do
                ++totalJobs
                this->parseIncomingJob(job, @true)
                totalBytes += this->initial_size
            repeat
    
            print "File: ":this->path_name
            print "Type: peqsjedi"
            print
            print "Total jobs:  ":totalJobs
            print "Total bytes: ":totalBytes
    
            return 0
        end method
    
        method peqsjedi::ioctl(command , data_in , data_out)
    !
    ! An ioctl() function call has been used.
    ! See the file JBC.h for all the JIOCTL_xxx values you might want to handle.
    !
            rc  = 1     ;! Default to an unknown command
            begin case
    !        case command eq JIOCTL_COMMAND_....
    !
    !            rc = 0
            end case
            return rc
        end method
    
    !
    ! Lock: Perform locking on this file.
    !
    ! Usually, this won't get called because in the ::open() method
    ! we leave 'driver_locks' set to 0. This means normally that
    ! we let jBASE handle the record locks.
    !
    ! However, other drivers may well use the external database locking,
    ! and so we will get calls here.
    ! The 'flags' variable is a number of characters to describe the actions
    ! and is one or more of the following:
    !
    ! 'L'     Take a lock on the item id in the 'itemid' parameter.
    ! 'U'     Unlock a record based on the item id in the 'itemid' parameter.
    ! 'A'     Unlock ALL records in the file and ignore the 'itemid' parameter.
    !
    ! Note that the 'flags' might contain both 'U' and 'A', and 'A' has precedence.
    !
    ! So, in normal working, this method won't get called,
    ! unless if you set the following in the ::open() method
    !
    ! this->driver_locks = 1
    !
    ! then this method will be called. Good for testing!
    !
        method peqsjedi::lock(job , flags)
            begin case
                case index(flags,'A',1)
                    crt 'UNLOCK ALL records'
                case index(flags,'U',1)
                    crt 'UNLOCK item id ':job
                case index(flags,'L',1)
                    crt 'LOCK item id ':job
            end case
            return 0
        end method
    
    !
    ! Internal method used throughout to open the jspool_log file
    !
        method peqsjedi::openSpooler()
            if not(this->$hasproperty('fspool')) then
                open SpoolerFileDetails(2) to f.spool else
                    stop 201,SpoolerFileDetails(2)
                end
    !
    ! Get the full path of this file into LogPath
    !
                LogPath = ''
                rc = ioctl(f.spool, JBC_COMMAND_GETFILENAME, LogPath)
    !
    ! Now store these vars in our class
    !
                this->fspool = f.spool
                this->LogPath = LogPath
            end
        end method
    
    !
    ! This method is called during create-file to populate the ]D (dict)
    ! with some useful dictionaries for jQL
    !
        method peqsjedi::initDicts(fileName)
            open 'DICT',fileName to f.dict then
                read hasAT from f.dict,'@' else
                    this->makeDicts(f.dict)
                end
                close f.dict
            end
            return 0
        end method
    
        method peqsjedi::makeDicts(f.dict)
            this->openSpooler()
            r.dict = 'A' ; r.dict<2> = '0'
            r.dict<3> = 'Account'
            r.dict<8> = 'F;CDEVJOBS*;0;:;(T':this->LogPath:';X;21;21)'
            r.dict<9> = 'L'
            r.dict<10> = 10
            write r.dict on f.dict,'JACCOUNT'
    
            r.dict = 'A' ; r.dict<2> = '0'
            r.dict<3> = 'Copies'
            r.dict<8> = 'F;CDEVJOBS*;0;:;(T':this->LogPath:';X;3;3)'
            r.dict<9> = 'R'
            r.dict<10> = 6
            write r.dict on f.dict,'JCOPIES'
    
            r.dict = 'A' ; r.dict<2> = '0'
            r.dict<3> = 'Date'
            r.dict<7> = 'D2'
            r.dict<8> = 'F;CDEVJOBS*;0;:;(T':this->LogPath:';X;10;10)'
            r.dict<9> = 'R'
            r.dict<10> = 9
            write r.dict on f.dict,'JDATE'
    
            r.dict = 'A' ; r.dict<2> = '0'
            r.dict<3> = ' job #'
            r.dict<9> = 'R'
            r.dict<10> = 6
            write r.dict on f.dict,'JNUM'
    
            r.dict = 'A' ; r.dict<2> = '0'
            r.dict<3> = 'Options'
            r.dict<8> = 'F;CDEVJOBS*;0;:;(T':this->LogPath:';X;8;8)'
            r.dict<9> = 'L'
            r.dict<10> = 4
            write r.dict on f.dict,'JOPTIONS'
    
            r.dict = 'A' ; r.dict<2> = '0'
            r.dict<3> = ' Port #'
            r.dict<8> = 'F;CDEVJOBS*;0;:;(T':this->LogPath:';X;7;7)'
            r.dict<9> = 'R'
            r.dict<10> = 7
            write r.dict on f.dict,'JPORT'
    
            r.dict = 'A' ; r.dict<2> = '0'
            r.dict<3> = 'Curr Pos'
            r.dict<8> = 'F;CDEVJOBS*;0;:;(T':this->LogPath:';X;5;5)'
            r.dict<9> = 'R'
            r.dict<10> = 8
            write r.dict on f.dict,'JPOS'
    
            r.dict = 'A' ; r.dict<2> = '0'
            r.dict<3> = 'Q Num'
            r.dict<8> = 'F;CDEVJOBS*;0;:;(T':this->LogPath:';X;1;1)'
            r.dict<9> = 'R'
            r.dict<10> = 5
            write r.dict on f.dict,'JQNUM'
    
            r.dict = 'A' ; r.dict<2> = '0'
            r.dict<3> = 'Report'
            r.dict<8> = 'F;CDEVJOBS*;0;:;(T':this->LogPath:';X;13;13)'
            r.dict<9> = 'R'
            r.dict<10> = 9
            write r.dict on f.dict,'JREPORT'
    
            r.dict = 'A' ; r.dict<2> = '0'
            r.dict<3> = 'Job Security'
            r.dict<8> = 'F;CDEVJOBS*;0;:;(T':this->LogPath:';X;16;16)'
            r.dict<9> = 'L'
            r.dict<10> = 10
            write r.dict on f.dict,'JSECURITY'
    
            r.dict = 'A' ; r.dict<2> = '0'
            r.dict<3> = '    Size'
            r.dict<8> = 'F;CDEVJOBS*;0;:;(T':this->LogPath:';X;4;4)'
            r.dict<9> = 'R'
            r.dict<10> = 8
            write r.dict on f.dict,'JSIZE'
    
            r.dict = 'A' ; r.dict<2> = '0'
            r.dict<3> = 'Stat'
            r.dict<8> = 'F;CDEVJOBS*;0;:;(T':this->LogPath:';X;2;2)'
            r.dict<9> = 'R'
            r.dict<10> = 4
            write r.dict on f.dict,'JSTATUS'
    
            r.dict = 'A' ; r.dict<2> = '0'
            r.dict<3> = 'Job Name'
            r.dict<8> = 'F;CDEVJOBS*;0;:;(T':this->LogPath:';X;19;19)'
            r.dict<9> = 'L'
            r.dict<10> = 10
            write r.dict on f.dict,'JNAME'
    
            r.dict = 'A' ; r.dict<2> = '0'
            r.dict<3> = 'Statuses'
            r.dict<8> = 'A;if N(JSTATUS) = "1" then "QUEUED" else if N(JSTATUS) = "2"  then "UNKNOWN(2)" else if N(JSTATUS) = "3" then "UNKNOWN3" else if N(JSTATUS) = "4" then "OPEN" else if N(JSTATUS) = "5" then "HS" else N(JSTATUS)'
            r.dict<9> = 'L'
            r.dict<10> = 8
            write r.dict on f.dict,'JSTATUSES'
    
            r.dict = 'A' ; r.dict<2> = '0'
            r.dict<3> = 'Time'
            r.dict<7> = 'MTS'
            r.dict<8> = 'F;CDEVJOBS*;0;:;(T':this->LogPath:';X;9;9)'
            r.dict<9> = 'R'
            r.dict<10> = 8
            write r.dict on f.dict,'JTIME'
    
            r.dict = 'A' ; r.dict<2> = '0'
            r.dict<3> = 'Owner'
            r.dict<8> = 'F;CDEVJOBS*;0;:;(T':this->LogPath:';X;6;6)'
            r.dict<9> = 'L'
            r.dict<10> = 9
            write r.dict on f.dict,'JUSERNAME'
    
            r.dict = 'A' ; r.dict<2> = '0'
            r.dict<3> = 'Peqs #'
            r.dict<9> = 'R'
            r.dict<10> = 6
            write r.dict on f.dict,'PEQS'
    !
    ! Write out the default listing PHrase
    !
            r.dict = 'PH'
            r.dict<2> = ' BY JNUM JNUM JNAME JSTATUSES JCOPIES JQNUM JSIZE JDATE JTIME JUSERNAME id-SUPP'
            write r.dict on f.dict,'@'
            return 0
        end method
    
    !
    ! Given a job nbr, this method sets various properties
    ! in our "this" object
    !
        method peqsjedi::parseIncomingJob(job, detail_required)
            pathFound = @false
            k.log = 'DEVJOBS*':job
            f.spool = this->fspool
            read job_detail from f.spool,k.log else
                return 1
            end
            fullPath = job_detail<22>
            lastSlash = count(fullPath,dir_delim_ch)
            filePath = oconv(fullPath,'G0':dir_delim_ch:lastSlash)
            this->jobId = field(fullPath,dir_delim_ch,lastSlash+1)
            Verbose = this->Verbose
            if Verbose then
                crt 'FullPath = "':fullPath:'"'
                crt 'FilePath = "':filePath:'"'
                crt 'JobID    = "':this->jobId:'"'
            end
            open filePath to f.queue then pathFound = @true
            this->fqueue = f.queue
            this->pathFound = pathFound
            this->filePath = filePath
    
            if detail_required then
                this->form_queue_number = job_detail<1>
                this->status    = job_detail<2>
                this->copies    = job_detail<3>
                this->initial_size  = job_detail<4>
                this->output_pos   = job_detail<5>
                this->username   = job_detail<6>
                this->portno    = job_detail<7>
                this->assign_options  = job_detail<8>
                this->time    = job_detail<9>
                this->date    = job_detail<10>
                this->edit_portno   = job_detail<11>
                this->edit_pid   = job_detail<12>
                this->report_no   = job_detail<13>
                this->align_flag   = job_detail<14>
                this->background_pid  = job_detail<15>
                this->security_jobs  = job_detail<16>
                this->security_altowners = job_detail<17>
                this->username_real  = job_detail<18>
                this->hold_file_name  = job_detail<19>
                this->application_id  = job_detail<20>
                this->username_jbase  = job_detail<21>
                this->unix_filename  = job_detail<22>
                this->timestamp   = job_detail<23>
                this->savecopies   = job_detail<24>
                this->spoolgroup   = job_detail<25>
            end
            return 0
        end method
    


    ------------------------------
    Peter Falson
    Rocket Internal - All Brands
    ------------------------------