uvcopy7.doc - Contents by Part#


Part_1 - demo uvcopy for All Purpose 'Text file' Manipulation
- uvcopy can save a great amount of manual editing drudgery
- with a few lines of uvcopy code, you can do a complex job that
  would require hundreds of lines of COBOL code.
repage1 - demo mass changes to documentation
codefix1 - demo mass changes to program code
codefix2 - cleanup scripts & C programs
scripts - ensure 1st line is '#!/bin/ksh'
C prgms - insert /* filename */ on first line as a /* comment */
 emailfix1 - cleanup email

Part_2 - demo uvcopy for All Purpose 'Data File' Utility jobs
adrsfix1 - eliminate duplicate Names & Addresses by standardizing
street addresses (street to st., road to rd., etc)
- drop duplicates with uvsort unique key option
adrsfix2 - alternate version using search/replace tables
vs embedded code as in adrsfix1
adrsfix3 - alternate version to combine amount fields into 1 remaining
record for duplicate key record group
splitprint1 - split large print file into multiple files
of specified number of pages each

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

Part_1 Text file Manipulation

Part 1 - Contents


1A1. 'repage1' - demo mass changes to documentation
- replace common contents page 1 in all documents
- saves many hours of manual editing
- page 1 is contents for all Vancouver Utilities
& needs to be updated when any document title changed

1B1. repage1 problem illustration
1C1. problem solution - uvcopy job 'repage1' operating instructions
1D1. 'repage1' uvcopy job listing
1E1. 'repage1' vital instructions explained

codefix1 - demo mass changes to program code

The Vancouver Utilities includes over 160,000 lines of 'C' in 55 programs. The code was developed over many years on various unix & linux systems.

HP compilers did not accept '/*' embedded in '/* ..code.. /* ..comments.. */'. uvcopy codefix1 made it easy to change it to '/* ..code.. |* ..comments.. */'.


1F1. Fix embedded '/*' comments in all 'C' programs in source directory
- demo uvcopy mass change to text files saving days of manual editing
1F2.  codefix1 Operating Instructions
1F3. alldiff - verify mass changes
1F4.  codefix1 uvcopy job listing
1F5.  codefix1 vital instructions explained

codefix2 - cleanup scripts & C programs


1G1. codefix2 - cleanup scripts & C programs
- convert tabs to 3 spaces & reduce multiple blank lines to 1
scripts - ensure 1st line is '#!/bin/ksh'
- insert #filename on 2nd line as a #comment
- insert '\' to split long lines exceeding column 80
C prgms - insert /* filename */ on first line as a /* comment */
- left adjust lines with leading blanks to a /* comment */
- insert /*eject*/ prior to a /*----*/ if < 25 lines since last

1G2. sample script & C program BEFORE & AFTER codefix2 cleanup
1G3. codefix2 uvcopy job listing

emailfix1 - cleanup email


1H1. emailfix1 - cleanup email
- remove the '>' retransmitted indicators
- left justify lines & leave just 1 blank between words
- reformat lines to about 50 characters
- reduce multiple blank lines to 1

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1A1. General Purpose Text file Manipulation

'repage1' - replace page 1 in multiple docs - Overview

'repage1' is a 'uvcopy job' that UV Software uses to update the 1st page of any file in the doc directory (/home/uvadm/doc/...) that has the standard 'contents page'. There are over 100 files in the doc/... subdir & about 60 of them have the common page 1 contents that you see on the 1st page of this document uvcopy7.htm.

It was a very laborious job to make the updates when I needed to change page 1 in all those files, so I finally developed this 'repage1' uvcopy job to do it automatically (after I edited 1 master copy of the new page 1).

These files are all unix/linux text files maintained with the 'vi' editor ' (despite the .doc extension). Periodically the text files are automatically converted to HTML (with uvcopy jobs as described in 'HTMLjobs.doc) & FTP'd to the UV Software web site (www.uvsoftware.ca).

Note that the common page 1 contents is printed for the hard-copy manuals, but is usually dropped when we convert to HTML for the website, because the website has the home-page & many links for navigation. The common page 1 on the hard-copy manuals is intended to help the reader relate the current manual to the other manuals available. I set the option to retain page 1 on this uvcopy7.doc so you can see the subject under discussion.

'repage1' was developed as a UV Software internal aid, but it is a good illustration of the power of uvcopy - how to do a lot of complex data manipulation with very few instructions.

You may have similar problems with your data files that could be solved with similar techniques. If your files are in a database or WORD or Excel spread sheets, you could export them to text or delimited files, and re-import them after manipulating with uvcopy.

uvcopy also works with mainframe type files (fixed/variable length, sequential or indexed files compatible with Micro Focus COBOL), but this section is focused on general purpose solutions rather than the mainframe conversions.

If you study this example, you will realize that 'uvcopy' could save you hundreds of hours of manual editing. If you needed help to implement a solution to your problem, describe your problem in an email to UV Software, and if we are not too busy we will help you with the solution.

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1B1. repage1 - sample text file manipulation

'repage1' - problem illustration

Here is a cut-down version of page 1 of this & most UV Software documents. I have chopped off the right side, so I could make comments

            CNVaids.doc - Mainframe    <-- Title on line 2 of all page 1's
 ***********************************     - restored from input file to new page1
 ** <--- this section is related to        (vs replacement page 1)
              ** volume 1 - Installa
    install.doc   - install guide fo
    versions.doc  - update history f
    uvprices.doc  - Prices & License
      ... etc (lines removed) ...
                     ** volume 2 - P
    uvlist.doc     - list text files
    uvhd.doc       - file display/se
    uvsort.doc     - parameter drive
    uvcopy7.doc    - general purpose
      ... etc (lines removed) ...
                     ** volume 3 - MVS
 ** CNVaids.doc     - Mainframe Aids    <-- '**' marks the current file       **
                                          - restored in new page1 by searching
                                            for document name ('CNVaids' here)
    MVSJCL.doc      - Converting JCL
    MVSCOBOL.doc    - Converting COBOL
      ... etc (lines removed) ...

repage1 - objectives

  1. Replace page 1 of files that have a contents page - identified by unique words such as "volume" & "install.doc" known to occur in 1st 55 lines.

  2. Must recover line 2 (title) from the current/old file to the new output file.

  3. Must store the '**' markers beside the line containing the current document's name, as shown above ('** uvcopy7.doc' in this case).

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1C1. repage1 - sample text file manipulation

'repage1' - problem solution


 #1a. vi xxxx.doc         <-- edit any existing file with contents page
      ===========
      :1,55 w new/page1   <-- write page1 to a separate file (in subdir new/)

 #1b. vi new/page1        <-- edit new page1 as desired
      ============          - also remove existing '**' markers
      ........

 #2a. mv doc doc.old      <-- rename existing doc dir to doc.old
      ==============

 #2b. mkdir doc           <-- make a new (empty) doc dir
      =========

 #3. uvcopy repage1,fild1=doc.old,fild2=doc,fili3=new/page1
     ======================================================
     - execute job to replace the 1st page on existing files
       that have a 'contents page' (identified by unique words)

 #3a. uvcopy repage1     <-- same as above, but easier
      ==============       - files default as shown above

 #4.  vi doc/*           <-- spot check results
      ========               OR (better) use alldiff2 below

 #5a. alldiff2 doc.old doc  <-- create diff report for all files in 2 subdirs
      ====================    - writes diff's to tmp/doc.dif
                              - see alldiff2 script listed at scripts.htm#83H

 #5b. vi tmp/doc.dif        <-- verify changes, only on page 1's
      ==============            & no changes beyond line 55 of any document

 #6a. tar cvf /dev/st0 doc.old  <-- backup to DAT tape (or other disc)
      ========================

 #6b. rm -rf doc.old            <-- remove old directory
      ==============

'repage1' - uvcopy job listing

'repage1' is listed on the next 3 pages, with line #s inserted, for references from explanations that follow on page '1E1'+ Inserting sequence#s is performed with uvcopy job 'seqnum1' a pre-programmed job that accepts I/O filenames, a displacement (0 in this case),& a pattern for seq# '#999_' in this case).

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1D1. repage1 - sample text file manipulation

repage1 - replace page1 (contents) in most files in directory


 #001 # repage1 - uvcopy Parameter File from UVSI stored in: /home/uvadm/pf/adm/
 #002 # repage1 - replace page1 (contents) of most files in /home/uvadm/doc/...
 #003 #         - by Owen Townsend, UV Software, Jan 12/2008
 #004 #
 #005 # 1. mv doc doc.old     <-- change name of existing doc dir
 #006 # 2. mkdir doc          <-- make new empty doc dir
 #007 # 3. uvcopy repage1,fild1=doc.old,fild2=doc,fili3=new/page1
 #008 #    ======================================================
 #009 #    - copy all docs from doc.old/... to new doc/...
 #010 #    - replacing page 1 if criteria met ("volume" & "install.doc" found)
 #011 #
 #012 # At program init:
 #013 # - load new page 1 into memory area 'm'
 #014 #   (will copy to alt area 'n' for modify & output)
 #015 # For each file in direcctory:
 #016 # - read entire file into area 'a'
 #017 # - test for contents page (patterns "volume" & "install.doc" present)
 #018 # If contents page:
 #019 # - copy new page1 from area 'm' to altternate area 'n' for modify
 #020 # - modify page1, insert page hdr from curent file, restore the '**' markers
 #021 # - dump modified new page1 from area 'n' to output file
 #022 # - clear page 1 (to 1st '*Eject') to nulls (inhibits output)
 #023 # Common to all files (whether contents page present/replaced or not)
 #024 # - dump file data from area 'a' to output file
 #025 # - repeat for all files in directory
 #026 #
 #027 opr='$jobname - copy all doc files, replacing page 1 if contents'
 #028 was=a4000000m6000n6000                  # increase sizes of areas a,m,n
 #029 # area 'a' 4 Meg (20,000 lines max * 200 per line max)
 #030 # area 'm' & 'n' 6K (60 lines max * 100 per line max)
 #031 fild1=?doc.old,typ=DIR,rcs=80           #input directory
 #032 fili1=xxxxxxxx,typ=LST,rcs=512          #current input file from directory
 #033 fild2=?doc,typ=DIR,rcs=80               #output directory
 #034 filo2=xxxxxxxx,typ=LSTt,rcs=512         #current output file
 #035 fili3=?new/page1,typ=LSTt,rcs=128       #new page1 contents
 #036 @run
 #037        opn    fild1                     open input directory
 #038        opn    fild2                     open output directory (must exist)
 #039        opn    fili3                     open the new page1 file
 #040        msgwy  'did you rename doc as doc.old & create new empty doc ?'
 #041        clr    m0(6000),'~'              ensure page area ends with tildes
 #042        rtb    fili3,m0(100),m0(100)     read new page1 into area 'm'
 #043 #

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page


 #045 # begin loop to read directory for next filename
 #046 man10  get    fild1,f0(80)              get next record (filename) in directory
 #047        skp>   man90                     (cc set > at EOF)
 #048        skp<   man10                     (cc set < if any subdirs)
 #049 # create I/O filenames for opens by concat dirname + '/' + filename
 #050        clr    f100(200),' '
 #051        mvu    f100(80),$fild1,x'00'     move dirname until ending null
 #052        cat    f100(80),'/'              concat the '/'
 #053        cata8  f100(80),f0(40)           concat current filename (a8 null terms)
 #054        mvu    f200(80),$fild2,x'00'     move output dirname until null reached
 #055        cat    f200(80),'/'              concat the '/'
 #056        cata8  f200(80),f0(40)           concat current filename (a8 null terms)
 #057 # store current pair of input/output files & open the files
 #058        mvc    $fili1,f100               store input filename before open
 #059        mvc    $filo2,f200               store output filename before open
 #060        opn    fili1
 #061        opn    filo2
 #062 #
 #063 # read current file into area 'a' & test for .doc page1 to be replaced ?
 #064 # - specify end table char x'EF' (vs dflt '~') since doc might have '~'s
 #065 man20  rtb    fili1,a0(200),a0(200),x'EF',x'EF'  read file into memory
 #066        scn    f0(40),'.doc'             UVSI ____.doc ?
 #067        skp!   man50
 #068        scni   a0(4000),'volume'         pattern1 known to be in 1st 20 lines
 #069        skp!   man50
 #070        scni   a0(4000),'install.d'      pattern2 known to be in 1st 20 lines
 #071        skp!   man50
 #072        lok    a0(200),a0(6),'*eject'    look for 1st end page pattern
 #073        skp!   man50
 #074        mvn    $ra,$rx                   save length to '*eject' for clr
 #075 #
 #076 # contents page1 confirmed - copy new page1(area 'm') to area 'n' to modify/dump
 #077 # - copy current file Title (line 2) to new page1
 #078 # - try to locate current doc name on new page1 & flag '**' before & after
 #079 #   (avoid matching title line, load $rn with 400 to start search on line 5)
 #080 # - nullify old page1 to inhibit output on table dump (wtb)
 #081 man30  mvc    n0(6000),m0               copy new page1 from 'm' to 'n' to modify
 #082        mvc    n100(100),a200            move old page1 hdr to new page1
 #083        rep    f0(20),'.doc',' '         remove .doc to match name for ** insert
 #084        mvn    $rn,400                   set rgstr 'n' to start search on line 5
 #085        stsg4  nn00(100),n0(100),f0(20)  search table for current doc name
 #086        skp!   man36
 #087        mvc    nn00(2),'**'              flag ** at begin line
 #088        mvc    nn77(2),'**'              flag ** at end line
 #089 man36  clr    a0($ra12000),x'00'        clear page1 (length in $ra saved above)
 #090 man40  wtbe   filo2,n0(100),n0(100)     dump new page1 (area 'n') to outfile
 #091 #
 #092 # common point to write out doc file data from area 'a'
 #093 # - 1st page cleared if volume contents, otherwise not
 #094 man50  wtbe   filo2,a0(200),a0(200),x'EF'  dump file data to outfile
 #095        cls    fili1                     close current input file
 #096        cls    filo2                     close current output file
 #097        skp    man10                     return to process next file
 #098 #
 #099 # end input directory - close all files & end job
 #100 man90  cls    all
 #101        eoj

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1E1. repage1 - sample text file manipulation

'repage1' - vital instructions explained

I will explain here only the instructions I think you might not fully appreciate without some additional comments. You can see all uvcopy instructions documented in uvcopy3.htm which has an alphabetical index. Note that all uvcopy instrns are 3 characters (anything following is an option).

Please relate the sequence# here to the full listing above.


 #031 fild1=?doc.old,typ=DIR,rcs=80           #input directory
 #032 fili1=xxxxxxxx,typ=LST,rcs=512          #current input file from directory
 #033 fild2=?doc,typ=DIR,rcs=80               #output directory
 #034 filo2=xxxxxxxx,typ=LSTt,rcs=512         #current output file
 #035 fili3=?new/page1,typ=LSTt,rcs=128       #new page1 contents

 #042        rtb    fili3,m0(100),m0(100)    read new page1 into area 'm'

 #046 man10  get    fild1,f0(80)
        ...............
 #061        opn    filo2

 #065 man20  rtb    fili1,a0(200),a0(200),x'EF',x'EF'   read file into memory

 #068        scni   a0(4000),'volume'        pattern1 known to be in 1st 20 lines
       ..................
 #074        mvn    $ra,$rx                  save length to '*eject' for clr

 #081 man30  mvc    n0(6000),m0              copy new page1 for modify
 #082        mvc    n100(100),a200           move old page1 hdr to new page1

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1E2. repage1 - sample text file manipulation

'repage1' - vital instructions explained (cont)


 #083        mvn    $rn,400
 #084        stsg4  nn0(100),n0(100),f0(20)   search table for current name.doc
 #085        skp!   man36
 #086        mvc    nn0(2),'**'              flag ** at begin line
 #087        mvc    nn77(2),'**'             flag ** at end line

 #089 man36  clr    a0($ra12000),x'00'        clear page1 (length in $ra saved above)

 #090 man40  wtbe   filo2,n0(100),n0(100)    dump new page1 to outfile

 #094 man50  wtbe   filo2,a0(200),a0(200),x'EF'  dump file data to outfile

 #095        cls    fili1                     close current input file
 #096        cls    filo2                     close current output file
 #097        skp    man10                     return to process next file

I hope I have explained this clearly. Please see all instructions documented in uvcopy3.htm which has an alphabetical index.

repage1 vs repage2

'repage1' only replaces page 1 (if recognized as a contents page). I use repage1 whenever the contents pages needs to be updated for all documents.

I have a 'repage2' job that also inserts the filename/title on all page headings, but it was not as useful as repage1 since I did not necessarily want the title on all page headings.

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1F1. uvcopy Text File Maintenance & Manipulation

codefix1 - mass changes to program code

The Vancouver Utilities includes over 160,000 lines of 'C' in 55 programs. The code was developed over many years on various unix & linux systems.

In April 2008, I had my first chance to compile the programs on an HP Itanium, and encountered a problem. The HP compiler did not accept '/*' embedded within '/* ...comments ...*/'. In C a comment must begin with '/*' & end with '*/', But other compilers did not complain about the 2nd embedded '/*'.

embedded '/*' problem

Here are 3 coding examples illustrating the problem:


 #1. /* prepend with 'cat > ...' & append with '/*' */
     =================================================

 #2. /* fwrite((char*)fcip,1,100,fo2p);  /* write FCD for DEBUG    */
     ================================================================

 #3. gg = memcmp(recw,"//*",3);          /* gg=0 if line has '//*' */
     ================================================================
  1. a comment in JCL converter that makes reference to '/*' occurring in JCL
  2. a line of DEBUG code /*commented out in case later required
  3. a line of code with "//*" as a constant (comment symbol in JCL)

There were almost 500 lines of code similar to the above examples. As you can imagine it would be a monumental task to fix the problem via manual editing.

So I wrote the 'codefix1' job to copy all programs, scanning for the 2nd '/*' & changing it to '|*'.

codefix1 results illustrated


 #1. /* prepend with 'cat > ...' & append with '|*' */
     =================================================

 #2. /* fwrite((char*)fcip,1,100,fo2p);  |* write FCD for DEBUG    */
     ================================================================

 #3. gg = memcmp(recw,"//*",3);   /* gg=0 if current line is '//*' */
     ================================================================

Note that the 2nd '/*' in example #3 is not changed because the 1st '/*' is a legitimate use of a constant "//*". My first coding of codefix1 missed this, but compile errors quickly alerted me to this problem, which was fixed as explained below (see codefix1 listing lines #67 & #68).

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1F2. codefix1 - mass changes to program code

codefix1 Operating Instructions


 #1. Login as uvadm --> /home/uvadm
     ==============

 #2a. mv src src.old    <-- change name of existing source code subdir
      ==============

 #2b. mkdir src         <-- make new source code subdir
      =========

 #3. uvcopy codefix1,fild1=src.old,fild2=src
     =======================================
     - execute codefix1 to copy all programs from src.old to src
     - scanning for 2nd '/*' & changing to '|*'
     - see the console log stats report below

 #4. alldiff2 src.old src     <-- create a 'difference report'
     ====================
     - a great tool to verify mass changes to test files
     - see a few lines of the report on the next page

 #5a. ccuvall LNX L32 P32 disamLNX
      ============================
      - recompile all programs on my Linux machine
      - see 'ccuvall' script at install.htm#L3

 #5b. ccuvall HP L32 P32 disamHPIA
      ============================
      - recompile all programs on the HP Itanium

console log msg statistics

 codefix1 - copy all files fixing embedded /* comments
 78 fixed in 11159 total lines of file: src.old/jclunix51.c
 080413:091642:codefix1: EOF fili01 rds=11159 size=475936: src.old/jclunix51.c
 080413:091642:codefix1: EOF filo02 wrts=11159 size=475146: src/jclunix51.c
 8 fixed in 17286 total lines of file: src.old/uvcopy.c
 080413:091643:codefix1: EOF fili01 rds=17286 size=759457: src.old/uvcopy.c
 080413:091643:codefix1: EOF filo02 wrts=17286 size=758537: src/uvcopy.c
 3 fixed in 7979 total lines of file: src.old/uvhd.c
 080413:091644:codefix1: EOF fili01 rds=7979 size=339701: src.old/uvhd.c
 080413:091644:codefix1: EOF filo02 wrts=7979 size=331318: src/uvhd.c
     - - - stats shown for only 4 of 55 programs total - - -
 080413:091650:codefix1: EOF fild02 size=4096: src
 441 fixed in 161598 total lines of directory: src.old

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1F3. codefix1 - mass changes to program code

alldiff - verify mass changes

 1634c1634
 < /* today[12] = '\0';   /* drop seconds  <-- disabled Feb18/07         */
 ---
 > /* today[12] = '\0';   |* drop seconds  <-- disabled Feb18/07         */
 6394c6394
 < /* fd1 = fileno(fp1);  /* get filedes from fileptr <-- NOneed May2001 */
 ---
 > /* fd1 = fileno(fp1);  |* get filedes from fileptr <-- NOneed May2001 */
 diff file# 43 - src.old/... vs src/uvhd.c
 408c408
 < /* "t!&l00004H",  /* 05 PaperTRAY: &l4H=alt source (t2=tray#2)           */
 ---
 > /* "t!&l00004H",  |* 05 PaperTRAY: &l4H=alt source (t2=tray#2)           */
 diff file# 45 - src.old/... vs src/uvlist.c
 2335,2337c2335,2337
 < /* fwrite((char*)fcip,1,100,fo2p);     /* write FCD           */
 < /* fwrite((char*)fi1kdr,1,100,fo2p);   /* write KDI retrieval */
 < /* fwrite((char*)kdip,1,374,fo2p);     /* write KDB           */
 ---
 > /* fwrite((char*)fcip,1,100,fo2p);     |* write FCD           */
 > /* fwrite((char*)fi1kdr,1,100,fo2p);   |* write KDI retrieval */
 > /* fwrite((char*)kdip,1,374,fo2p);     |* write KDB           */
 2859,2860c2859,2860
 < /* fwrite((char*)fcop,1,100,fo2p);           /* write FCD   */
 < /* fwrite((char*)kdop,1,374,fo2p);           /* write KDB   */
 ---
 > /* fwrite((char*)fcop,1,100,fo2p);           |* write FCD   */
 > /* fwrite((char*)kdop,1,374,fo2p);           |* write KDB   */
 diff file# 55 - src.old/... vs src/uxsort.c

20 different of 55 files compared src.old to src

Re-Compile - the best verification

Of course the best verification is 're-compiling' the programs & see if error free.

The 'alldiff2' script is most vital when masschanging text files such as documentation where you have no 're-compile' to verify.

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1F4. codefix1 - mass changes to program code

codefix1 uvcopy job listing


 #01 # codefix1 - uvcopy Parameter File from UVSI stored in: /home/uvadm/pf/adm/
 #02 # codefix1 - remove/disable '/*' embedded in '/* code /* comments */'
 #03 #          - by Owen Townsend, UV Software, April 2008
 #04 #
 #05 # I used SCO unix & Red Hat Linux to write & compile the Vancouver Utilities
 #06 # - 160,000 lines of code in 55 programs (over may years)
 #07 # In April 2008, I had a problem when I compiled on HP Itanium
 #08 # - HP 'C' compiler would not accept a 2nd '/*' embedded in comments, example:
 #09 #
 #10 # /* ...code... /* ...comment... */   <-- common practise to cmt out code
 #11 # /* ...code... |* ...comment... */   <-- this job changes 2nd '/*' to '|*'
 #12 #
 #13 # There were _____ occurrences in 160,000 lines of code
 #14 # - a monumental job to fix by manual editing
 #15 # - I wrote this job in less than 1 hour & ran in 10 seconds
 #16 #
 #17 # 1. mv src src.old      <-- rename source library
 #18 #    ==============
 #19 #
 #20 # 2. uvcopy codefix1,fild1=src.old,fild2=src
 #21 #    =======================================
 #22 #    - copy all files from src.old to src/...
 #23 #    - fixing embedded /* comments as illustrated above
 #24 #
 #25 opr='$jobname - copy all files fixing embedded /* comments '
 #26 fild1=?indir,typ=DIR,rcs=80           #input directory
 #27 fili1=xxxxxxxx,typ=LST,rcs=256        #current input file from directory
 #28 fild2=?outdir,typ=DIR,rcs=80          #output directory
 #29 filo2=xxxxxxxx,typ=LSTt,rcs=256       #current output file
 #30 @run
 #31        opn    fild1                    open input directory
 #32        msgwy 'did you create outdir (dflt tmp) ? (or remove files?)'
 #33        opn    fild2                    open output directory (must exist)
 #34 #

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page


 #36 # begin outer loop to read directory for next filename
 #37 man10  get    fild1,a0(80)             get next record (filename) in directory
 #38        skp>   man90                    (cc set > at EOD)
 #39        skp<   man10                    (cc set < on subdir vs file)
 #40 #
 #41 # create input filename by concat: indir/infile & open
 #42        clr    f0(300),' '
 #43        mvu    f100(80),$fild1,x'00'    move dirname until ending null
 #44        cat    f100(80),'/'             concat the '/'
 #45        cata8  f100(80),a0(80)          concat current filename (a8 null terms)
 #46        mvc    $fili1,f100              store input filename before open
 #47        opn    fili1                    open current input file
 #48 #
 #49 # create same filename in output subdir & open
 #50        mvu    f200(80),$fild2,x'00'    move output dirname until null reached
 #51        cat    f200(80),'/'             concat the '/'
 #52        cata8  f200(80),a0(80)          concat current filename (a8 null terms)
 #53        mvc    $filo2,f200              store output filename before open
 #54        opn    filo2                    open current output file
 #55 #
 #56 # clear counters for current files
 #57        mvn    $ca1,0                   clear count, lines in current file
 #58        mvn    $ca3,0                   clear count, fixes in current file
 #59 #
 #60 # begin inner loop: get/put records from/to current file until EOF
 #61 man20  get    fili1,b0(256)            get each record into area 'b'
 #62        skp>   man40
 #63        add    $ca1,1                   count lines for current infile
 #64        add    $ca2,1                   count total lines in directory
 #65 #
 #66 # scan for embedded cmts & disable any 2nd '/*' by changing to '|*'
 #67        scne1z1q2 bb0(80),'/*'          scan to 1st '/*' not in "quotes"
 #68        skp!   man30
 #69        add    $rb,1                    rgstr 'b' +1 for scan to 2nd
 #70        scne1  bb0(100),'/*'            continue scan for 2nd '/*'
 #71        skp!   man30
 #72        mvc    bb0(1),'|'               yes - change '/*' to '|*'
 #73        add    $ca3,1                   count lines modified in current file
 #74        add    $ca4,1                   count total lines modified in dir
 #75 #
 #76 # common point whether current line modified or not
 #77 man30  put    filo2,b0(256)            write current line
 #78        skp    man20                    return to get next line
 #79 #
 #80 # EOF current file - output counts, close files,& return for next file pair
 #81 man40  msgv1  '$ca3 fixed in $ca1 total lines of file: $fili1'
 #82        cls    fili1                    close current input file
 #83        cls    filo2                    close current output file
 #84        skp    man10
 #85 #
 #86 # end input directory - display stats, close all dirs & files, & eoj
 #87 man90  cls    all
 #88        msgv1  '$ca4 fixed in $ca2 total lines of directory: $fild1'
 #89        eoj

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1F5. codefix1 - mass changes to program code

codefix1 vital instructions explained

I will explain here only the instructions I think you might not fully appreciate without some additional comments. You can see all uvcopy instructions documented in uvcopy3.htm which has an alphabetical index. Note that all uvcopy instrns are 3 characters (anything following is an option).

Please relate the sequence# here to the full listing above.


 #26 fild1=?indir,typ=DIR,rcs=80           #input directory
 #27 fili1=xxxxxxxx,typ=LST,rcs=256        #current input file from directory
 #28 fild2=?outdir,typ=DIR,rcs=80          #output directory
 #29 filo2=xxxxxxxx,typ=LSTt,rcs=256       #current output file

 #41 # create input filename by concat: indir/infile & open
 #42        clr    f0(300),' '
 #43        mvu    f100(80),$fild1,x'00'   move dirname until ending null
 #44        cat    f100(80),'/'            concat the '/'
 #45        cata8  f100(80),a0(80)         concat current filename, a8 null terms
 #46        mvc    $fili1,f100             store input filename before open
 #47        opn    fili1                   open current input file

 #66 # scan for embedded cmts & disable any 2nd '/*' by changing to '|*'
 #67        scne1z1q2 bb0(80),'/*'          scan to 1st '/*' not in "quotes"
 #68        skp!   man30
 #69        add    $rb,1                    rgstr 'b' +1 for scan to 2nd
 #70        scne1  bb0(100),'/*'            continue scan for 2nd '/*'
 #71        skp!   man30
 #72        mvc    bb0(1),'|'               yes - change '/*' to '|*'
        The op1 register is the 2nd 'b' of 'bb0(80)' (1st 'b' is area 'b')
        (the register could be any letter, but I like to make it same as area)

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1F6. codefix1 - mass changes to program code

codefix1 instructions explained (continued)


 #70        scne1  bb0(100),'/*'            continue scan for 2nd '/*'
      Do you understand the index registers ?

 #69        nop                         <-- replace 'add $rb,1' with 'nop'
 #70        scne1  bb1(100),'/*'            continue scan for 2nd '/*'
 #71        skp!   man30
 #72        mvc    bb1(1),'|'               yes - change '/*' to '|*'
      Do you see that we could have saved 1 instruction (line #69 add $rb,1)
      if we change op1 on lines #70 & #72 from 'bb0(1)' to 'bb1(1)' ?

 #80 # EOF current file - output counts, close files,& return for next file pair
 #81 man40  msgv1  '$ca3 fixed in $ca1 total lines of file: $fili1'
 #82        cls    fili1                    close current input file
 #83        cls    filo2                    close current output file
 #84        skp    man10                    return to get next filename from dir

I hope I have explained this clearly. Please see all instructions documented in uvcopy3.htm which has an alphabetical index.

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1G1. uvcopy Text File Maintenance & Manipulation

codefix2 - cleanup scripts & C programs

  1. convert tabs to 3 spaces
  2. reduce multiple blank lines to 1

For scripts:

  1. Ensure 1st line is '#!/bin/ksh'
  2. insert #filename on 2nd line as a #comment
  3. insert '\' to split long lines exceeding column 80

For C programs:

  1. insert /* filename */ on first line as a /* comment */
  2. left adjust lines with leading blanks to a /* comment */
  3. insert /*Eject*/ prior to a /*----*/ if < 25 lines since last

    sample SCRIPT 'rpgcomp1' BEFORE codefix2

: # compile and/or link converted rpg programs

 #####
 if [ "$RPGCDIR" = "" -o ! -f $xid/rpgtocfp.h -o ! -f $xid/rpgtoctd.h -o ! -f $xid/rpgtocex.h -o ! -f $xld/librc.a ];
 	then
 		echo "ERROR - The environment variable RPGCDIR is not set"
 		exit
 fi

sample script 'rpgcomp1' AFTER codefix2

#!/bin/ksh # tools/rpgcomp1 # compile and/or link converted rpg programs

 #####
 if [ "$RPGCDIR" = "" -o ! -f $xid/rpgtocfp.h -o ! -f $xid/rpgtoctd.h -o ! -f\
    $xid/rpgtocex.h -o ! -f $xld/librc.a ];
    then
       echo "ERROR - The environment variable RPGCDIR is not set"
       exit
 fi

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1G2. codefix2 - cleanup scripts & C programs

sample C PROGRAM 'keyenter.c' BEFORE codefix2

/* keyenter.c Enter key to Version Control File */

/* Get input */ static void get_input(char *argv1) { int x; char keywork[200];

 for(;;)
 	{ for (x=0;keywork[x];x++)      /* make it lower case */
 		{ keywork[x] = (char)tolower((int)keywork[x]);
 			if (!strchr("0123456789abcdef",(int)keywork[x]))
 				{ printf("\nError - Incorrect character in position %d",x+1);
 				}
 		}
 	}
 /*--------------------------------------*/
 /* Write version control file */
 static void write_vc(char *vcon)
 { ...etc...remaining code omitted...

sample C program 'keyenter.c' AFTER codefix2

/* tools/keyenter.c */ /* keyenter.c Enter key to Version Control File */

/* Get input */ static void get_input(char *argv1) { int x; char keywork[200];

 for(;;)
    { for (x=0;keywork[x];x++)      /* make it lower case */
       { keywork[x] = (char)tolower((int)keywork[x]);
          if (!strchr("0123456789abcdef",(int)keywork[x]))
             { printf("\nError - Incorrect character in position %d",x+1);
             }
       }
    }
 /*EJect*/  <-- changed to '*EJect' to prevent uvlist new page on '*eject'
 /*--------------------------------------*/
 /* Write version control file */
 static void write_vc(char *vcon)
 { ...etc...remaining code omitted...

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1G3. codefix2 - cleanup scripts & C programs

uvcopy job 'codefix2' - cleanup C programs & scripts

 # codefix2 - clean up C programs & scripts
 #          - by Owen Townsend, UV Software, May 2008
 #
 #May2008 - 1st used on Morada RPG libraries (libr,sort,tools,util)
 #
 # uvcopy codefix2,fili1=tools/mkurpg,filo1=tmp/mkurpg
 # ===================================================
 # - convert 1 file at a time (for test/debug)
 # - then convert All files in directories as follows:
 #
 # 1. mv libr libr.old; mv sort sort.old; mv tools tools.old; mv util util.old;
 #    =========================================================================
 # 2. mkdir libr sort tools util
 #    ==========================
 #
 # 3a. uvcopyx codefix2 libr.old libr uop=q0i7
 #     =======================================
 # 3a. uvcopyx codefix2 sort.old sort uop=q0i7
 #     =======================================
 # 3a. uvcopyx codefix2 tools.old tools uop=q0i7
 #     =========================================
 # 3a. uvcopyx codefix2 util.old util uop=q0i7
 #     =======================================
 #
 #                   ** changes **
 #
 # 1. Convert each tab to 3 blanks
 #
 # 2. Reduce multiple blank lines to 1
 #
 #                 ** Only for C programs **
 #
 # 3. Insert filename on 1st line /* subdir/filename */
 #
 # 4. Left adjust lines with only leading blanks to /*---------*/
 #
 # 5. Insert /*Eject*/ prior to /*---------*/ lines (with no code)
 #    - if we have not inserted /*Eject*/ in last 25 lines
 #
 #                 ** Only for scripts **
 #
 # 6. Insert filename on 2nd line: # subdir/filename
 #    (since 1st line will be: #!/bin/ksh)
 #
 # 7. if 1st line ':' or '#!' change to: '#!/bin/ksh'
 #    else insert '#!/bin/ksh' as 1st line
 #
 # 8. Insert '\' to split long lines
 #    - insert prior to word exceeding col 80
 #    - line up part2 under 1st non-blank of part1
 #      but indent 3 columns to the right
 #

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

 opr='$jobname - clean up C programs & scripts'
 fili1=?input,rcs=512,typ=LST
 filo1=?output,rcs=256,typ=LSTt
 @run
        opn    fili1                open input file
 man01  mvu    f0(80),$fili1,x'00'   retrieve filename
        rep    f0(80),'.old',''      remove any '.old' suffix
 #
 # To help determine if file is script or C, we will read 1st 5 lines
 # - counting '# ' & '/*' in cols 1-2
 man02  bal    getr                  get next line
        skp>   man06
        cmc    a0(2),'# '            probable script ?
        skp!   1
        add    $cb11,1               count '# ' in 1st 5 lines
        cmc    a0(2),'/*'            probable C code or hdr file ?
        skp!   1
        add    $cb12,2               count '/*' in 1st 5 lines
        cmn    $ca1,5                reached 5 lines ?
        skp<   man02
 #
 # end 5 line test for #script vs C program
 # - close & re-open input file, so we can copy all lines below
 man06  cls    fili1
        opn    fili1
        opn    filo1
        mvn    $ca1,0                reset total line ctr
        mvn    $ca2,0                reset lines since last *Eject
        bal    getr                  get 1st line for #! or : test
        skp>   man90                 (cc set > if EOF)
 #
 # set C $cb1=2 if: filename suffix .c or .h or '/*' in 1st 5 lines
 man10  scn    f0(80),'.c '          C program ?
        skp=   man30
        scn    f0(80),'.h '          C program ?
        skp=   man30
        cmn    $cb12,1               found /* 1st 5 lines ?
        skp=>  man30
 #
 # set script $cb1=1 if: C not detected above and:
 # - 1st line '#!' or ':', or found '# ' in 1st 5 lines
 man14  cmc    a0(2),'#!'            shell spcfd on 1st line ?
        skp=   man20
        cmc    a0(1),':'             ':' col 1 of 1st line ?
        skp=   man20
        cmn    $cb11,1               found '# ' 1st 5 lines ?
        skp=>  man21
 #
 # neither C nor script detected
 man18  skp    man60                 go write 1st line & return to loop
 #

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

 # script detected by '#!' or ':' - ensure ksh spcfd
 man20  mvc    a0(10),'#!/bin/ksh'   yes - replace with Korn shell
 #
 # script detected by counting '# ' in 1st 5 lines (write 1st line unchanged)
 man21  put    filo1,a0              write 1st line
 #
 # set script switch & insert line 2 as script name
 man22  mvn    $cb1,1                set switch to ID scripts
        ins    f0(80),'# '           insert '#' prior to filename
        put    filo1,f0(80)          write inserted line # file-name
        skp    man40                 goto record processing loop
 #
 # C program - set switch to ID C & insert program name on 1st line
 man30  mvn    $cb1,2                set switch to ID C program/header
        ins    f0(80),'/* '          insert comment ID begin
        cat    f0(80),' */'          append comment ID end
        put    filo1,f0(80)          write 1st line /* file-name */
        skp    man60                 go write 1st line & return to loop
 #

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

 # begin mainline loop to get,process,write lines until EOF
 man40  bal    getr                  get next line
        skp>   man90
 #
 # reduce multiple blank lines to 1
 # - getr subrtn counts blank lines in $cb3 & resets on non-blank
 man42  cmn    $cb3,2               2nd+ blank line ?
        skp=>  man40                ifso - bypass, return to get next
 #
 # insert /*Eject*/ on /*--------*/ (in not done in last 25 lines)
 # - getr subrtn sets switch $cb2 if /*-----*/ present
 man44  cmn    $cb2,1               comment /*......*/ present ?
        skp<   man50
 getr45 cnt    a0(80),'-'           count '-'s
        cmn    $ci1,15              15+ '_'s ?
        skp<   man50
 getr46 cmn    $ca2,20              20 lines since last Eject ?
        skp<   man50
        put    filo1,'/*eject*/'    insert Eject
        mvn    $ca2,0               reset 25 line ctr
 #
 # insert '\' to split long lines in scripts (not if C code)
 man50  cmn    $cb1,1               script ? (vs C)
        skp!   man60
 man51  cmc    a80(10),' '          col 80 exceeded ?
        skp=   man60
 man52  scnre1z1 aa0(80),' '        scan from right to begin word
 ###    mvn    $ra,$rx              save ptr to blank at begin word
        cmn    $ra,40               col 40+ ?
        skp<   man60
        scn    aa0(80),'"'          within quotes ?
        skp=   man60
        mvc    aa0(1),'\'           insert '\' continuation char
        mvc    b0(256),aa1          save 2nd part in area 'b'
        clr    aa1(256),' '         clear part2 from original
        put    filo1,a0             write part 1
 #
 # indent (by 3 columns) part2 under 1st non-blank of part1
 man56  clr    c0(256),' '          clear another work area
        scne1z1 bb0(80),>' '        scan to 1st non-blank in part1
 ###    mvn    $rb,$rx              save ptr to 1st non-blank
        mvc    cb3(256),b0          indent part2 under part1 1st NB +3
        put    filo1,c0             write part2
        skp    man40                return to get next line
 #
 # common point to write current line
 # - unless it was split as above
 man60  put   filo1,a0             write out current line
        skp    man40                return to get next line
 #
 # EOF - close files & end job
 man90  cls   all
        eoj
 #

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

 # getr - subrtn to get next line of code
 #      - convert tabs to 3 blanks
 #      - left adjust lines with leading blanks to /*---------*/
 #      - count blank lines & clear on non-blank
 #      - count all lines (in $ca1)
 #      - count lines since last /*Eject*/ insert (in $ca2)
 #
 getr   get    fili1,a0          get next line
        skp>   getr9             (cc set > at EOF)
        add    $ca1,1            count total lines
        add    $ca2,1            count lines since last Eject insert
 #
        rep    a0(256),x'09','   '  replace each tab with 3 blanks
 #
 # test for all comment lines /*..........*/ (no C code)
 # - set switch to test in mainline for /*-------*/ & insert /*eject*/
 # - if any leading blanks, remove them, left adjust squeeze out blanks
 getr2  mvn    $cb2,0            presume not all cmt
        scn    a0(80),>' '       scan to 1st non-blank
        mvn    $ra,$rx           save ptr to 1st NB
        cmc    aa0(2),'/*'       '/*' after leading blanks ?
        skp!   getr6
        scnr   a0(80),>' '       scan from right to last non-blank
        mvn    $rb,$rx           save ptr to last NB
        sub    $rb,1             -1 to pint to last 2 chars
        cmc    ab0(2),'*/'       '*/' ending data on line ?
        skp!   getr6
 getr4  mvn    $cb2,1            set switch for /*.....*/
        cmc    a0(1),' '         leading blanks ?
        skp!   getr6
        sqzc1  a0(256),' '       squeeze left (drop leading blanks)
 #
 # count blank lines & clear ctr on non-blank line
 getr6  cmc    a0(80),' '        blank line ?
        skp=   getr7
        mvn    $cb3,0            clear blank line ctr
        skp    getr8
 getr7  add    $cb3,1            count blank lines
 #
 # common exit with: cc = if not EOF, cc > if EOF
 getr8  ret=
 getr9  ret>
 #------------------------- end codefix2 --------------------------

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1H1. uvcopy Text File Maintenance & Manipulation

emailfix1 - cleanup email

Sometimes you get email that has been forwarded several times is very ugly (many '>>>' prior message indicators, long lines, bad spacing, etc).

'emailfix1' will cleanup the mess for important things (like good jokes) before you pass it on to your friends.

sample email BEFORE cleanup

             > >>> > >> Biscuit Recipe
             > >>> > >>
             > >>> > >>
             > >>> > >> THIS IS A TRUE STORY
             > >>> > >>
             > >>> > >> My daughter and I had just finished lunch at a Neiman-Marcus Cafe
             > >>> > >> in
             > >>> > >> Dallas.
             > >>> > >> Because both of us are such biscuit lovers, we decided to try the
             > >>> > >> 'Neiman-Marcus cookie'. It was so excellent that I asked if they
             > >>>would
             > >>> > >> give me the recipe. The waitress said with a small frown, 'I'm
             > >>>afraid
             > >>> > >> not, but you can BUY the recipe.' I asked how much, and she
             > >>>responded;
             > >>> > >> 'Only two fifty - it's a great deal!' I agreed to that, and told
             > >>> > >> her
             > >>> > >> to add it to my bill.
             > >>> > >>
             > >>> > >> Thirty days later, I got my VISA statement, and the Neiman-Marcus
             > >>> > >> charge was $285.00. I looked at it again, and I remembered I had
             > >>>only
             > >>> > >> spent $9.95 for two sandwiches and about $20.00 for a scarf. At the
             > >>> > >> bottom of the statement, it said, 'Cookie Recipe-$250.00'. That was
             > >>> > >> outrageous!
             > >>> > >>
             > >>> > >> I called Neiman's Accounting Department and told them the waitress
             > >>>had
             > >>> > >> said it was 'two fifty', which clearly does not mean 'two hundred
             > >>>and
             > >>> > >> fifty dollars' by any reasonable interpretation of the phrase.
             > >>> > >> Neiman-Marcus refused to budge. They would not refund my money
             > >>> > >> because, according to them; 'What the waitress told you is not our
             > >>> > >> problem. You have already seen the recipe. We absolutely will not
             > >>> > >> refund your money.'
             > >>> > >>
                      ------- you can guess the rest -------

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1H2. emailfix1 - cleanup email

sample email AFTER cleanup

Biscuit Recipe

 THIS IS A TRUE STORY
 My daughter and I had just finished lunch at a Neiman-Marcus
 Cafe in Dallas. Because both of us are such biscuit lovers,
 we decided to try the 'Neiman-Marcus cookie'. It was so excellent
 that I asked if they would give me the recipe. The waitress
 said with a small frown, 'I'm afraid not, but you can BUY the
 recipe.' I asked how much, and she responded; 'Only two fifty
 - it's a great deal!' I agreed to that, and told her to add
 it to my bill.

Thirty days later, I got my VISA statement, and the Neiman-Marcus charge was $285.00. I looked at it again, and I remembered I had only spent $9.95 for two sandwiches and about $20.00 for a scarf. At the bottom of the statement, it said, 'Cookie Recipe-$250.00'. That was outrageous!

I called Neiman's Accounting Department and told them the waitress had said it was 'two fifty', which clearly does not mean 'two hundred and fifty dollars' by any reasonable interpretation of the phrase. Neiman-Marcus refused to budge. They would not refund my money because, according to them; 'What the waitress told you is not our problem. You have already seen the recipe. We absolutely will not refund your money.'

                      ------- you can guess the rest -------

uvcopy emailfix1 Op. Instrns.

  1. Save email as a '.txt' file. Following assumes saved in subdir tmp/...


 #2. uvcopy emailfix1,fili1=tmp/NeimanMarcus.txt,filo1=tmp/NeimanMarcus.fixed
     ========================================================================
  1. Capture to windows clipboard & paste into outgoing email

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

1H3. emailfix1 - cleanup email

emailfix1 - uvcopy code listing

 # emailfix1 - cleanup email
 #
 # - remove the '>' retransmitted indicators
 # - left justify lines & leave just 1 blank between words
 # - reformat lines to about 60 characters
 #   (split lines at 1st word break right of 60 chars)
 # - reduce multiple blank lines to 1
 #
 rop=r1       # option to prompt oprtr for disposition of output
 opr='uop=q1l60   - option defaults'
 opr='      l60   - line length default 60 bytes'
 uop=q1l60    # set option defaults
 was=b20000   # increase area b paragraph storage area
 fili1=?input,typ=LST,rcs=1024
 filo1=?output,typ=LSTtd3,rcs=128
 @run
        opn    all
        mvn    $rl,$uopbl           store line lth in rgstr 'l'
 #
 # begin paragraph
 # - bypass any blank lines til next data found
 man20  bal    getr                 get next line
        skp>   eof
        cmc    a0(10),' '           blank line ?
        skp=   man20
 #
 # non-blank line reached
 # - get & store paragraph (til next blank line reached)
 man30  clr    b0(10000),' '        clear para storage area
        mvn    $rb,0                init para storage ctl rgstr
 #
 # begin loop to get lines & store para, til next blank line reached
 man32  mvc    bb0($ra1024),a0      store current line by rgstrs
        add    $rb,$ra              up para store ctl rgstr by line lth
        bal    getr                 get next line
        skp>   man40
        cmc    a0(10),' '           blank line reached ?
        skp!   man32
 #

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

 # blank line reached (ending para store)
 # - output stored para in $rl (dflt 60) byte lines
 man40  mvn    $rb,0                init rgstr b back to begin para
 # begin loop to output lines of 60+ bytes til end para blanks reached
 man42  mvn    $rc,$rb              ptr to current line in para storage
        add    $rc,$rl              up by desired line lth
        scn    bc0(20),' '          scan to end of word
        add    $rc,$rx              adjust line lth to end word
        mvn    $rd,$rc              pointer to end of current line
        sub    $rd,$rb              calc current line length
        clr    c0(128),' '          clear output area
        mvc    c0($rd128),bb0       move current line to out area
        sqzc1  c0(128),' '          ensure left justified
        put    filo1,c0             write current line
        add    $rb,$rd              up para ctl rgstr by line length
        cmc    c0(20),' '           reached blank line ending para ?
        skp!   man42                no - repeat loop
        skp    man20                yes - return to begin next para
 #
 # EOF - close files & end job
 eof    cls    all
        eoj
 #
 #-----------------------------------------------------------
 # getr - subrtn to get next line
 #      - convert any '>'s to blanks
 #      - left justify
 #      - store data length in $ra
 getr   get    fili1,a0(1024)
        skp>   getr9
        rep    a0(30),'>',' '           remove '>' indicators
        sqzc1  a0(1024),' '             left justify
        mvn    $ra,$rx                  store data lth in $ra
        add    $ra,1                    +1 to leave blank btwn lines
        ret=
 getr9  ret>

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

Part_2 uvcopy7.doc - Data File Maintenance

Part 2 uvcopy File Maintenance - Contents


2A1. adrsfix1 - eliminate duplicate Names & Addresses by standardizing
  street addresses (street to st., road to rd., etc)
- drop duplicates with uvsort unique key option

2B1. adrsfix2 - alternate version using search/replace tables
vs embedded code as in adrsfix1

2C1. adrsfix3 - alternate version to combine amount fields into 1 remaining
record for duplicate key record group

2D1. splitprint1 - split large print file into multiple files
of specified number of pages each

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

2A1. using uvcopy for Data File Maintenance

Eliminate Duplicate Name & Address Records

Thanks to Jack Stamy of Berks EIT, for suggesting this in April 2004. We think that other sites might benefit from these techniques.

duplicate Name & Address records is a common problem at many customer sites. New customer Name & Address records are often set up when personnel do not know that existing records are already onfile for the customer.

The application software may be checking for duplicates, but this check may fail due to minor differences in the new address. These failures are usually due to spelling differences in abbreviations (ST. vs STREET, RD. vs ROAD, N. vs North, etc).

Note that checks for duplicates must check both Name & Address fields since it is not unusual to have duplicate Company Names at the same Address.

We have provided sample test data, uvcopy jobs,& uvsort commands to illustrate our solutions & to help you to develop solutions for your own data files. First we will give you an overview of the steps involved.

Solution Overview

  1. Standardize abbreviation spellings used for STREET,ROAD,NORTH,SOUTh,etc. Our demo uvcopy job 'adrsfix1' makes it easy to do this, since it uses an easily updated search/replace table.

  2. Next we will use 'uvsort' to sort the file on the Name & Address fields. We will specify the unique sort key option 'u1', which drops duplicate records except for the last record of each duplicate key set.

  3. Lastly, we will sort the file back to customer number sequence and reload (overwrite) the original customer master file.

To make it easier for you, we have supplied our test data file as a simple sequential file without packed fields & with LineFeeds in the last byte (so you can inspect with vi). Usually the customer master file would be a multi-key Indexed file & it would often contain packed fields (which makes it impossible to use vi).

test/demo file record layout

 001-006 - customer#         - 1st key in Indexed file
 011-035 - customer name     - 2nd key in Indexed file
 036-060 - address         <-- address field to be modified
 061-076 - city
 078-079 - province
 081-090 - postal code
 091-102 - telephone#        - 3rd key in Indexed file
 103-120 - contact name
Note
  • above layout is 1 relative, instruction displacements are 0 relative

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

2A2. Eliminate Duplicate Name & Address Records

sample#1 - input to adrsfix1

 130140    EVERGREEN MOTORS LTD.    1815 BOWEN ROAD          NANAIMO          BC V9S1H1
 132588    GEECOE GENERATOR SERVICE 2851 SIMPSON ST          RICHMOND         BC V6X2R2
 139923    JOHNSTONE BOILER & TANKS 1250 EAST PENDER STREET  VANCOUVER        BC V5L1W1
 140055    JOHNSTONE BOILER & TANKS 1250 EAST PENDER ST.     VANCOUVER        BC V5L1W1
 142175    LILLY ELECTRIC LTD       16809 - 24TH AVENUE EAST SURREY           BC V4B5E7
 147615    O'CONNER R.V. CENTRE     44430 YALE ROAD WEST     CHILLIWACK       BC V2P6J1
 148800    LILLY ELECTRIC LTD       16809 - 24TH AVE E       SURREY           BC V4B5E7
 149304    POINT GREY GOLF&COUNTRY  3350 S.W. MARINE DRIVE   VANCOUVER        BC V6N3Y9
 151500    LILLY ELECTRIC LTD       16809 - 24TH AVE. E.     SURREY           BC V4B5E7
 152040    EVERGREEN MOTORS LTD.    1815 BOWEN RD            NANAIMO          BC V9S1H1

sample#2 - output from adrsfix1

 130140    EVERGREEN MOTORS LTD.    1815 BOWEN RD.           NANAIMO          BC V9S1H1
 132588    GEECOE GENERATOR SERVICE 2851 SIMPSON ST.         RICHMOND         BC V6X2R2
 139923    JOHNSTONE BOILER & TANKS 1250 E. PENDER ST.       VANCOUVER        BC V5L1W1
 140055    JOHNSTONE BOILER & TANKS 1250 E. PENDER ST.       VANCOUVER        BC V5L1W1
 142175    LILLY ELECTRIC LTD       16809 - 24TH AVE. E.     SURREY           BC V4B5E7
 147615    O'CONNER R.V. CENTRE     44430 YALE RD. W.        CHILLIWACK       BC V2P6J1
 148800    LILLY ELECTRIC LTD       16809 - 24TH AVE. E.     SURREY           BC V4B5E7
 149304    POINT GREY GOLF&COUNTRY  3350 S.W. MARINE DR.     VANCOUVER        BC V6N3Y9
 151500    LILLY ELECTRIC LTD       16809 - 24TH AVE. E.     SURREY           BC V4B5E7
 152040    EVERGREEN MOTORS LTD.    1815 BOWEN RD.           NANAIMO          BC V9S1H1

sample#3 - output from uvsort (dups dropped)

 152040    EVERGREEN MOTORS LTD.    1815 BOWEN RD.           NANAIMO          BC V9S1H1
 132588    GEECOE GENERATOR SERVICE 2851 SIMPSON ST.         RICHMOND         BC V6X2R2
 139923    JOHNSTONE BOILER & TANKS 1250 E. PENDER ST.       VANCOUVER        BC V5L1W1
 151500    LILLY ELECTRIC LTD       16809 - 24TH AVE. E.     SURREY           BC V4B5E7
 147615    O'CONNER R.V. CENTRE     44430 YALE RD. W.        CHILLIWACK       BC V2P6J1
 149304    POINT GREY GOLF&COUNTRY  3350 S.W. MARINE DR.     VANCOUVER        BC V6N3Y9

sample#4 - output from uvsort (back to cust# sequence)

 132588    GEECOE GENERATOR SERVICE 2851 SIMPSON ST.         RICHMOND         BC V6X2R2
 139923    JOHNSTONE BOILER & TANKS 1250 E. PENDER ST.       VANCOUVER        BC V5L1W1
 147615    O'CONNER R.V. CENTRE     44430 YALE RD. W.        CHILLIWACK       BC V2P6J1
 149304    POINT GREY GOLF&COUNTRY  3350 S.W. MARINE DR.     VANCOUVER        BC V6N3Y9
 151500    LILLY ELECTRIC LTD       16809 - 24TH AVE. E.     SURREY           BC V4B5E7
 152040    EVERGREEN MOTORS LTD.    1815 BOWEN RD.           NANAIMO          BC V9S1H1

Note that sample#3 is in Name & Address sequence, after duplicates dropped by the unique sortkey option, which is effective during the output phase of the sort. Sample#4 has been sorted back to customer# sequence to reload the customer master file (usually a multi-key Indexed file).

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

2A3. Eliminate Duplicate Name & Address Records

demo Operating Instructions


 1. uvcopy adrsfix1,fili1=dat1/custmas4,filo1=tmp/custmas4a
    =======================================================

 2. uvsort "fili1=tmp/custmas4a,typ=RSF,rcs=120,filo1=tmp/custmas4b\
    ================================================================
            ,key1=10(25),key2u1=35(25)"
            ===========================

 3. uvsort "fili1=tmp/custmas4b,typ=RSF,rcs=120,filo1=tmp/custmas4c,typ=ISF
    =======================================================================
            ,key1=0(6),isk1=0(6),isk2=10(25d),isk3=90(12d)"

Modifications for your N&A file

  1. adrsfix1 - copy N&A master to work file, standardizing addresses - see the uvcopy code listed on the next page - you would change the input file def to match your file (filename, recsize,typ=ISF probably) - modify code for your record layout, address here is 35(25) - modify the search/replace table as you desire

  2. uvsort - sorts the file on Name & Address, dropping duplicates - modify for your filename, recsize,& sort key (address field) - note option 'u1' on 'key2u1=35(25)' is the 'unique' key option that causes duplicate records to be dropped (saves last 1) - also specify key1 (Name), in case of 2 customers at 1 address - we could have used 'key1u1=10(50)' since Name&Address adjacent

  3. uvsort - sort the file back into cust# sequence & reload master file - usually an Indexed file with multiple keys (1st key cust#, 2nd key Name, 3rd key Telephone#, etc) - for our demo, the output file is left in the 'tmp' subdir but you would overwrite your input master file

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

2A4. Eliminate Duplicate Name & Address Records

adrsfix1 - uvcopy demo job

 # adrsfix1 - standardize Names & Addresses for duplicate elimination
 #          - using search/replace table to change STREET to ST., ROAD to RD.,etc
 #          - followed by uvsort to drop duplicate records
 #          - demo based on supplied test file /home/uvadm/dat1/custmas4
 #
 # uvcopy adrsfix1,fili1=dat1/custmas4,filo1=tmp/custmas4a
 # =======================================================
 #
 was=c8000   - increase area c to 8000 bytes (allows 200 * 40 byte table entries)
 fili1=?dat1/custmas4,typ=RSF,rcs=120       #<-- would be typ=ISF in real life
 filo1=?tmp/custmas4a,typ=RSF,rcs=120
 #
 # load table of search/replace addresses
 # - applied to address field b35(25) by 'rpt' instrn below
 # - 40 byte table entries, 01-20=search pattern, 21-40=replacement
 # - patterns preceded & followed with 1 blank to avoid matches within words
 # - table ended with a line of all tildes
 lod=c0(40)
  STREET ~~~~~~~~~~~~ ST. ~~~~~~~~~~~~~~~
  ROAD ~~~~~~~~~~~~~~ RD. ~~~~~~~~~~~~~~~
  AVENUE ~~~~~~~~~~~~ AVE. ~~~~~~~~~~~~~~
  DRIVE ~~~~~~~~~~~~~ DR. ~~~~~~~~~~~~~~~
  NORTH ~~~~~~~~~~~~~ N. ~~~~~~~~~~~~~~~~
  SOUTH ~~~~~~~~~~~~~ S. ~~~~~~~~~~~~~~~~
  EAST ~~~~~~~~~~~~~~ E. ~~~~~~~~~~~~~~~~
  WEST ~~~~~~~~~~~~~~ W. ~~~~~~~~~~~~~~~~
  ST ~~~~~~~~~~~~~~~~ ST. ~~~~~~~~~~~~~~~
  RD ~~~~~~~~~~~~~~~~ RD. ~~~~~~~~~~~~~~~
  AVE ~~~~~~~~~~~~~~~ AVE. ~~~~~~~~~~~~~~
  DR ~~~~~~~~~~~~~~~~ DR. ~~~~~~~~~~~~~~~
  N ~~~~~~~~~~~~~~~~~ N. ~~~~~~~~~~~~~~~~
  S ~~~~~~~~~~~~~~~~~ S. ~~~~~~~~~~~~~~~~
  E ~~~~~~~~~~~~~~~~~ E. ~~~~~~~~~~~~~~~~
  W ~~~~~~~~~~~~~~~~~ W. ~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 # - we need to repeat our patterns to ensure we get the '.' on abbreviations
 #   in case the abbreviation already present but without '.'
 @run
        opn    all
 #
 # begin loop to get/process/write records until EOF
 man20  get    fili1,a0               get next record
        skp>   eof                    (cc set > at EOF)
        mvc    b0(120),a0             move to outarea 'b' from inarea 'a'
        rpta2  b35(25),c0(40),c0(20)  search/replace address by table above
        put    filo1,b0               write record to output
        skp    man20                  return to get next record
 #
 # EOF - close files & end job
 eof    cls    all
        eoj

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

2A5. Eliminate Duplicate Name & Address Records

notes re uvcopy code

  1. there are only 12 lines of instructions in this job, since most of the lines are #comment lines for documentation & explanation

  2. The search/replace table is 16 lines, but you could add many more

  3. Note the 'rpta2' instruction below that uses the search/replace table Option 'a2' is required to allow for multiple replacements in 1 field You can see the instruction details in uvcopy3.htm

  4. If desired you could add a 2nd table & a 2nd 'rpt' instruction to standardize the NAME field (change 'LIMITED' to 'LTD.', 'LTD' to 'LTD.', etc)

  5. If desired you could maintain the search/replace table as a separate file This is illustrated on the next page --->

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

2B1. Eliminate Duplicate Name & Address Records

Here is an alternate version that allows you to maintain the search/replace table as a separate file for easier maintenance (adding new entries). You can run the (previously illustrated) demo as shown below in adrsfix2.

search/replace table as a separate file

 # adrsfix.tbl - search/replace table to standardize N&A abbreviations
 #             - separate file read by uvcopy job adrsfix1 on startup
 #             - for easier maintenance & addition of new entries
 #             - this demo file supplied in /home/uvadm/tf/adrsfix.tbl
  STREET ~~~~~~~~~~~~ ST. ~~~~~~~~~~~~~~~
  ROAD ~~~~~~~~~~~~~~ RD. ~~~~~~~~~~~~~~~
  AVENUE ~~~~~~~~~~~~ AVE. ~~~~~~~~~~~~~~
       - - - - lines omitted - - - -
  S ~~~~~~~~~~~~~~~~~ S. ~~~~~~~~~~~~~~~~
  E ~~~~~~~~~~~~~~~~~ E. ~~~~~~~~~~~~~~~~
  W ~~~~~~~~~~~~~~~~~ W. ~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

adrsfix2 - alternate version of adrsfix1

 # adrsfix2 - standardize Names & Addresses for duplicate elimination
 #          - alternate version of /home/uvadm/pf/demo/adrsfix1
 # - loads the search/replace table from a file vs embedded as in adrsfix1
 # - adrsfix1 & adrsfix2 are extensively documented in uvcopy7.htm
 #
 # uvcopy adrsfix2,fili1=dat1/custmas4,filo1=tmp/custmas4a,fili2=tf/adrsfix.tbl
 # ============================================================================
 # uvcopy adrsfix2   <-- same as above (files default as above)
 #
 opr='$jobname - standardize Names & Addresses for duplicate elimination'
 fili1=?dat1/custmas4,typ=RSF,rcs=120   #<-- would be typ=ISF in real life
 filo1=?tmp/custmas4a,typ=RSF,rcs=120   #<-- output file default in tmp subdir
 fili2=?tf/adrsfix.tbl,typ=LST,rcs=80   #<-- search/replace table text file LST
 @run
        opn    all
        rtb    fili2,c0(40),c0(40)   <-- read table into area c (40 byte entries)
 #
 # begin loop to get/process/write records until EOF
 man20  get    fili1,a0               get next record
        skp>   eof                    (cc set > at EOF)
        mvc    b0(120),a0             move to outarea 'b' from inarea 'a'
        rpta2  b35(25),c0(40),c0(20)  search/replace address by table above
        put    filo1,b0               write record to output
        skp    man20                  return to get next record
 #
 # EOF - close files & end job
 eof    cls    all
        eoj

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

2C1. Eliminating Duplicate Name&Address Records with Amount fields

Eliminating Duplicate N&A Records with Amount fields

The previous pages illustrated techniques for eliminating duplicate Name & Address records, but those techniques did not allow for the possibility that the N&A records might also contain quantity or $amount fields that should be combined into the 1 remaining record.

Combining amount fields is made possible by the 'sum' feature of uvsort (which will be further explained on the next page).

Alternate test file 'dat1/custmas5' is provided to demonstrate the accumulation of amount fields into the 1 remaining record. The 1st 120 bytes of 'custmas5' is the same as the previously illustrated 'custmas4', but custmas5 also contains 24 * 6 byte packed fields in 121-240.

test/demo file record layout

 001-006 - customer#         - 1st key in Indexed file
 011-035 - customer name     - 2nd key in Indexed file
 036-060 - address         <-- address field to be modified
 061-076 - city
 078-079 - province
 081-090 - postal code
 091-102 - telephone#        - 3rd key in Indexed file
 103-120 - contact name
 121-180 - this year monthly sales (12 * 5 bytes packed)
 181-240 - last year monthly sales (12 * 5 bytes packed)
 241-256 - unused

We will show you the 1st record using the 'uvhd' utility (since the packed fields make it impossible to list the records as we did for custmas4).


 uvhd dat1/custmas5 r256        <-- run uvhd to illustrate 1st record
 =======================
                      10        20        30        40        50        60
 r#        1 0123456789012345678901234567890123456789012345678901234567890123
           0 130140    EVERGREEN MOTORS LTD.    1815 BOWEN ROAD          NANA
             3333332222454545444244545524542222233332445442544422222222224444
             130140000056527255E0DF4F230C44E0000181502F75E02F140000000000E1E1
          64 IMO          BC V9S1H1                                  ..4V|...
             4442222222222442535343222222222222222222222222222222222201357000
             9DF000000000023069318100000000000000000000000000000000000246C000
         128 .........W0....`........)X}..f3.....\.................4V}...f...
             0000000005300016000000002570063100095000000000000000013570016000
             0C0000C0270D0540C0000C0098D0263C0444C0000C0000C0000C0246D0056C00
         192 ...............................f.....<........f.C 19950531
             0000008900000000880000000018000680001300000000694233333333222222
             00C0026C0000C0023C0000C0083C0056D0012C0000C0016D3019950531000000

Packed fields can be identified by the ending sign bytes x'_C' or x'_D'.

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

2C2. Eliminating Duplicate Name&Address Records with Amount fields

We can demonstrate this technique using uvcopy job 'adrsfix3', which is similar to 'adrsfix2' (previously listed on page '2B1'), except that the input file record size is now 256 (vs 120).

adrsfix3 Operating Instructions


 1. uvcopy adrsfix3,fili1=dat1/custmas5,filo1=tmp/custmas5a
    =======================================================

 2. uvsort "fili1=tmp/custmas5a,typ=RSF,rcs=256,filo1=tmp/custmas5b\
    ================================================================
            ,key1=10(25),key2u1=35(25),sum1x24=120(5p)"
            ===========================================

 3. uvsort "fili1=tmp/custmas5b,typ=RSF,rcs=256,filo1=tmp/custmas5c,typ=ISF
    =======================================================================
            ,key1=0(6),isk1=0(6),isk2=10(25d),isk3=90(12d)"

The most important difference (vs previous Op. Instrns on page '2A3' is the 'sum1x24=120(5p)' instruction on the uvsort that drops the duplicate records.

The 'x' option specifies 24 contiguous fields, all 5 bytes packed starting at byte 120 (zero relative). Without the 'x' option we would have had to specify:


 sum1=120(5p),sum2=125(5p),sum3=130(5p),......,sum24=235(5p)
 ===========================================================

If you run this demo, you could use 'uvhd' to verify that the amount fields have been added together into the 1 remaining record for a duplicate set. It is easier to see this if you print out the input & output files using the immediate print instruction of uvhd.


 uvhd dat1/custmas5 r256        <-- run uvhd on the input file
 =======================
 ---> i10f2 <-- immediate print 10 recs, 2 per form(page)
 ---> q     <-- quit

 uvhd tmp/custmas5b r256        <-- run uvhd on the uvsort output file
 =======================
 ---> i6f2  <-- immediate print 6 recs, 2 per form(page)
 ---> q     <-- quit

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

2D1. using uvcopy for Data File Utility jobs

splitprint1 - split file into multi-files of specified pages

Here is a job suggested by Gaetan Cadieux (City of Laval) in March 2009. We are listing the job here in case useful to other users. See the operating instructions example as #comments at begining of code below.


 vi /home/uvadm/pf/util/splitprint1
 ==================================
 # splitprint1 - split print file into multi-files
 #               of specified no of pages each (default 10,000)
 #             - by Owen Townsend, UV Software, March 10, 2009
 #
 # uvcopy splitprint1,fili1=rpts/filexx,fild2=tmp,uop=p10000
 # =========================================================
 # - split rpts/bigfile into multi-files (10000 pages each)
 # - writing output files to tmp/filexx_001, tmp/filexx_002, etc
 #
 # uvlp13L tmp/filexx_001   <-- print 1st file Landscape at 13 cpi & 8.5 lpi
 # ======================       (fits 132 columns & 66 lines on 8 1/2 x 11)
 #
 # uvlp13LD tmp/filexx_002   <-- print 2nd file Landscape & Duplex
 # =======================
 #
 opr='$jobname - split print file into multi-files of specified no of pages'
 opr='- write outfiles to tmp/filexx_001, tmp/filexx_002, etc
 opr='uop=p10000 - option defaults'
 opr='    p10000 - desired pages in each output file'
 uop=q1p10000    # option defaults
 was=a9000       # allow 9000 bytes get area a (1 page 132 * 66 = 8712 max)
 fili1=?infile,rcs=9000,typ=LST
 fild2=?outdir,rcs=80,typ=DIR
 filo2=outdir/infile_###,rcs=9000,typ=LSTt

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

 @run
        opn     fili1                  open input file
        opn     fild2                  open output subdir
 #
 # save input filename to construct output filenames
 # - output to subdir tmp/infilename + '_' separator + 3 digit seq#
 # tmp/infilename_001, tmp/infilename_002, etc
        mvu     f1(80),$fili1,x'00'    retrieve infilename
        scnr    f0(80),'/'             scan from Right side to any subdir
        mvc     f200(80),fx1           save infile basename (w/o subdirs)
        cat     f200(80),'_'           concat separator (seq# will follow)
 #
 # begin outer loop for each split-file
 # - increment outfile# & clear page#
 man20  add     $ca1,1                 increment outfile#
        mvn     $ca2,0                 clear page#
 #
 # format outfilename = tmp/infilename_###
        clr     f400(80),' '           clear output filename build area
        mvu     f400(80),$fild2,x'00'  setup output subdir
        cat     f400(80),'/'           append '/' separator
        cat     f400(80),f200(80)      append infilename
        scn     f400(80),'_ '          scan to ending '_ '
        mvn     fx401(3),$ca1          insert outfile seq# (at dsplcmnt $rx+1)
        mvf     $filo2,f400(80),x'00'  store outfilename & null terminate
        opn     filo2                  open next output file
 #
 # begin inner loop to copy specified no of pages (option p)
 man30  getd0e0s8 fili1,a0(9000),' ',x'0C'
        skp>    man80
        puts8t8 filo2,a0($rv9000),' ',x'0C'
        add     $ca2,1                 count pages
        cmn     $ca2,$uopbp            reached page limit ?
        skp<    man30
 #
 # page count limit reached (or EOF)
 # - close output file, test EOF & return for next file if not EOF
 man80  cls     filo2
        cmc     a0(4),'~EOF'           EOF on infile ?
        skp!    man20
 man90  cls     all
        eoj

Goto:   Begin this doc End this doc Index this doc Contents this library UVSI Home-Page

Visitor Counters for ThisYear and LastYear