When slower development might open new doors
Count on those massive disks really working

Quiz teaches 3000 CCTL carriage control

The HP 3000 has Carriage Control (CCTL) capabilities that can come in handy when formatting output from aged legacy applications. Robelle's SMUG Book entry says that CCTL

is the process of moving the print position (or "carriage") to the next line or page, as well as performing advanced functions such as overprinting and printing through the perforation. Normally the program specifies the CCTL code for a line through the Fwrite intrinsic. MPE has too many ways to do CCTL and modern printers are controlled via Postscript or Printer Command Language (PCL) anyway.

But CCTL has its users, and understanding how it works can be a key to resolving print problems with some of those aging applications you might have under your wing. Dave Powell wrote up a quiz on CCTL behavior recently, one he shared with the 3000 community. He explained some nuances in his article, one he researched as part of his ongoing CCTL-to-Rich Text Format (RTF) utility.

By Dave Powell

Start with a simple program that generates a 132-column, fixed length report. Run it with output to a spool file,  like:

file rptfile; dev=lp,1
run progname

Then run it with output to a permanent cctl file, like:

file  rptfile = permfile, new; dev=disc; cctl; save &
  ; rec=,32,f,ascii; disc=60000,32,0
run progname

Finally send the perm file to a spool file, like:

file newspool; dev=lp,1; cctl
print  permfile > *newspool

Questions
1: Are the two spoolfiles identical ?
2: What about the printouts ?

Answers:

1:  The spool files should be identical except for trailing blanks.  The first spoolfile has all 133-column records for the actual report output (plus special zero-length records at the beginning and end).  The second file may have some shorter records because trailing blanks have magically disappeared. So, the second may be much smaller, and will look more like a “real” variable length file. Check the file size with:
calc finfo(“o###.out.hpspool”,”bytefilesize”).

Just because spool files are ‘variable’ doesn’t mean anything eats blanks your app sends.  But :print seems to eat them.  Test “:print SomeFixedAsciiFile > NewTempFile” without any file equations, and see what it builds.

The non-spool file created the second time the program runs has all 133-column records, because omitting the record size in the file-equation causes the rec-len to be picked up from the program.

2:  What printouts?  The out-pri is 1.  Well, besides that... the printouts will appear identical, unless your eyesight is good enough to spot trailing blanks on paper :)

Most of our big print jobs happen this way -- works like a charm.

Powell has a command file available that reports info on a CCTL file -- the CCTL code, the first few bytes of the data, and the record length.

This command file shows an HP CCTL report file's CCTL codes. :printspf may be better for spool files, because it shows the p1 and p2 fields.

Parms:
1: File name to read (must be cctl type, can be spool or not)
2: max number of records to read, even if the file is longer. Its SLOOOOW, so use this.

For each line of the input file, it shows:
• Line #
• Carriage control code, if any, in decimal, octal and my description of the meaning. Different codes that have the same effect may have different descriptions.
• The beginning of the data part of the record, padded or truncated to 16 bytes.
• The total record length (including the cctl byte). Spoolfiles will have records with ZERO length, hence no cctl code. The 0-len records at the start and end have open and close commands in the function word of the hidden area. See :printspf and Appendix A of the Native Mode Spooler Manual.

parm     _from   =   "";   _limit = 200                                 
parm     entry   =   'main'                                             
option nolist                                                           
if   "!entry"    =   "main"  then                                       
     if  bound (X_ERRORS)                                               
         deletevar   X_@                                                
     endif                                                              
     setvar  X_ERRORS  0                                                

     if  fsyntax('!_from')<>"MPE"  and  fsyntax('!_from')<>"POSIX"      
         setvar  X_ERRORS  X_ERRORS + 1                                 
         echo Parm-1 (file name to convert FROM) is required            
         echo and must be a valid file name.                            
     endif                                                              
     if  len ("!_limit") < 1  or not numeric ("!_limit")                
         setvar  X_ERRORS  X_ERRORS + 1                                 
         echo Invalid limit on #-of-records to read                     
         echo If not omitted, 2nd-parm must be a positive integer       
     endif                                                              
     if  X_ERRORS      >   0                                            
         showvar X_@                                                    
         return                                                         
     endif                                                             

     setvar  X_FROM    '!_from'                                         
     file    XFROM =   !X_FROM,  OLD;    CCTL                           
     if  not finfo('*XFROM','EXISTS')                                   
         echo No such permanent file as "!X_FROM"                       
         setvar  X_ERRORS  X_ERRORS + 1                                 
     else                                                               
         setvar  X_EOF     finfo('*XFROM','EOF')                        
         if  X_EOF     <   2                                            
             echo File '!X_FROM' does not have enough records           
             setvar  X_ERRORS  X_ERRORS + 1                             
         endif                                                          
     endif                                                              
     if  X_ERRORS      >   0                                            
         echo Old file '!X_FROM' cannot be processed                    
         echo format.                                                   
         showvar X_@                                                    
         return                                                         
     endif                                                             

     setvar  X_LIMIT     min (X_EOF, !_LIMIT)                           

                                                                        
     xeq !hpfile  entry = ReadLoop   < *XFROM                           

     if  X_ERRORS  =   0                                                
         echo Done OK                                                   
     else                                                               
         showvar X_@                                                    
     endif                                                              
     return                                                            

elseif   "!entry"    =   "ReadLoop"                                     
     echo Line  Cctl-code            Data        RecLength             
     setvar  X_CNT       0                                              
     while   setvar  (X_CNT, X_CNT+1)    <=  X_LIMIT     do             
         input   X_REC                                                  
         setvar  X_CNT_      RHT ('   !X_CNT',4)                        

         setvar  X_LEN       LEN (X_REC)                                
         if  X_LEN           >   0                                      
             setvar  X_CCTL      LFT(X_REC,1)                           
             setvar  X_ORD       ORD (X_CCTL)                           
             setvar  X_ORD__     "!X_ORD"                               
             setvar  X_OCT__     "![OCTAL(X_ORD)]"                      
             if  X_ORD >= 128   and   X_ORD <= 192                      
                 setvar  X_DESC  "Adv" + "![X_ORD - 128]"               
             elseif  X_ORD >= 2   and   X_ORD <= 42                     
                 setvar  X_DESC      "SingleSp"                         
             elseif  X_ORD >= 192   and   X_ORD <= 207                  
                 setvar  X_DESC      "VFC code"                         
             elseif  X_ORD = 49   or   X_ORD = 51                      
                 setvar  X_DESC      "CondEject"                        
             elseif  X_ORD   =    0                                     
                 setvar  X_DESC      "Single"                           
             elseif  X_ORD   =   45                                     
                 setvar  X_DESC      "Triple"                           
             elseif  X_ORD   =   48                                     
                 setvar  X_DESC      "Double"                           
             elseif  X_ORD   =   64                                     
                 setvar  X_DESC      "PostSp"                           
             elseif  X_ORD   =   65                                     
                 setvar  X_DESC      "PreSpace"                         
             elseif  X_ORD   =   67                                     
                 setvar  X_DESC      "NoAutoPage"                       
             elseif  X_ORD   =  208                                     
                 setvar  X_DESC      "NoSpNoRet"                        
             else                                                       
                 setvar  X_DESC      " "                                
             endif                                                      
         else                                                           
             setvar  X_CCTL      '*NONE*'                               
             setvar  X_ORD       0                                      
             setvar  X_DESC      "{none}"                               
             setvar  X_ORD__     " "                                    
             setvar  X_OCT__     " "                                    
         endif                                                         

         if  X_LEN       >   1                                          
             setvar  X_DATA      str (X_REC+rpt(" ",16), 2, 16)         
             setvar  X_DATA__    repl (X_DATA, ">", "!>")               
         else                                                           
             setvar  X_DATA  " "                                        
             setvar  X_DATA__    rpt (" ", 16)                          
         endif                                                          
         echo ![RHT ("   !X_CNT", 4)];&                                 
              ![RHT ("   !X_ORD__", 4)] &                               
              ![RHT ("    "+X_OCT__, 5)] &                              
              ![LFT ("!X_DESC          ", 10)];&                        
              ![X_DATA__];&                                             
              ![RHT ("   !X_LEN", 4)]                                  

         deletevar   X_REC                                              
     endwhile                                                           
     return                                                            

##-  just one more error-check                                          
else                                                                    
     setvar  X_ERRORS  X_ERRORS + 1                                     
     echo Error - invalid entry point                                   
     showvar X_@                                                        
     return                                                             
endif                                                                  

return

Comments