/* uvhd.c - binary file investigation utility written in ANSI C             */
/*        - this is the ANSI 'C' source program for Unix/Linux/Windows      */
/*        - written by Owen Townsend, UV Software, owen@uvsoftware.ca       */
/*        - Copyright(C) 1993-2008, UV Software Inc, distributed under GPLv3*/
/*                                                                          */
/*                    ** uvhd - Program Description **                      */
/*                                                                          */
/* 'uvhd' is a binary file investigation utility. It displays any file in   */
/* vertical hexadecimal and prompts for commands to browse, search, select, */
/* update, scan/replace, print, translate, etc. uvhd is an interactive      */
/* utility with a command line interface and 19 help screens.               */
/*                                                                          */
/*                     ** Licensed under GPLv3 **                           */
/*                                                                          */
/* This program is free software: you may redistribute it and/or modify     */
/* it under the terms of GPLv3 (GNU General Public License version 3),      */
/* as published by the Free Software Foundation.                            */
/*                                                                          */
/* This program is distributed in the hope that it will be useful,          */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of           */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                     */
/*                                                                          */
/* See the full description of the GNU General Public License at:           */
/* http://www.gnu.org/licenses.                                             */
/*                                                                          */
/* Note - for Unix/Linux you must download the source & compile on your OS  */
/*      - see Compile Instructions about 100 lines below                    */
/*                                                                          */
/* Note - for Windows or DOS you may download the precompiled execcutable   */
/*      - store in program files or setup environment PATH to your download */
/*                                                                          */
/* uvhd is an efective tool for investigating problems and verifying test   */
/* results in files that have packed/binary fields, and that may not have   */
/* linefeeds. Most Unix/Linix tools require linefeeds (vi, cat, lp, etc).   */
/*                                                                          */
/* 'uvhd' is offered as a Free sample of the Vancouver Utilties which are   */
/* often used to convert mainframe JCL, COBOL,& DATA to Unix/Linux/Windows  */
/* Mainframe JCL is converted to Korn shell scripts, and Mainframe COBOL is */
/* converted to Micro Focus COBOL to run under Unix/Linux.                  */
/*                                                                          */
/* uvhd is especially valuable to investigate mainframe type files with     */
/* packed fields and no linefeeds (required by most unix/linux utilities)   */
/* uvhd can investigate all converted file types: sequential, fixed, variable,*/
/* indexed, text, binary, C-ISAM, D-ISAM, Micro Focus COBOL IDXFORMAT1,3,8) */
/*                                                                          */
/* ======================================================================== */
/* Owen Townsend, UV Software, 4667 Hoskins Rd, North Vancouver BC, V7K2R3  */
/*         Tel:   604-980-5434          Fax: 604-980-5404                   */
/*         Email: owen@uvsoftware.ca    Web: http://www.uvsoftware.ca       */
/* General purpose utilities for file conversion, maintenance,& sorting.    */
/* uvhd free program to investigate binary/packed files without linefeeds   */
/* ======================================================================== */

/*eject*/
/* uvhd is intended to be a 'FREE SAMPLE' of the Vancouver Utility package.*/
/*                                                                         */
/* See the web site for descriptions of other programs in the package      */
/* (The prices are available in the 'uvprices.doc' html document)          */
/*                                                                         */
/* uvhd is the 1 free program in the Vancouver utility package, which is   */
/* copy-righted and proprietary to UV Software Inc. You may copy & distribute*/ 
/* the uvhd program under the terms of the GPLv3 license (see above)       */
/*                                                                         */
/* See instructions to download & compile 2 pages ahead (about line# 125)  */
/*                                                                         */
/* See update history following HELP screens (about line# 700)             */

char pid[72] = "uvhd.c Copyright: UV Software, 1993-2008, GNU/FSF GPLv3";


/*eject*/
/***************************************************************************/
/*                      ** uvhd INSTALL/COMPILE **                         */
/***************************************************************************/
/*                                                                         */
/* For Unix/Linux you must download the source program and compile under   */
/* your Unix/Linux O/S. For Windows or DOS you may download the executable */
/* and store it in your program files or setup your environment PATH to    */
/* wherever you download.                                                  */
/*                                                                         */
/* - you must have downloaded the program from www.uvsoftware.ca           */
/* - may also download the custmas1 file to test/demo the program          */
/* - the following assumes the program has been stored in subdir 'src'     */
/* - you must ensure the filename is 'uvhd.c'                              */
/* - subdirectory 'bin' exists to receive the compiled output              */
/* - your Unix/Linux/Windows system has an ANSI C compiler                 */
/*                                                                         */
/* 1. cp uvhd.c src/uvhd.c   - store program in source subdir              */
/*    ====================   - ensure the filenmae is 'uvhd.c'             */
/*                                                                         */
/* 2. cc -DH64 src/uvhd.c -obin/uvhd  <-- compile the program              */
/*    ==============================      '-DH64' option for 64 bits       */
/*                                        '-DH32' or omit for 32 bits      */
/*                                                                         */
/* 3a. bin/uvhd dat1/custmas1 r256 - test with supplied downloaded datafile*/
/*    ============================                                         */
/*                                                                         */
/* 3b. bin/uvhd xxxxx r???   - display any of your data files (??? recsize)*/
/*     ===================                                                 */
/* 3c. bin/uvhd xxxxx t2     - use option 't2' for text (vs 'r' fixed rcsz)*/
/*     =================                                                   */
/*                                                                         */
/* 4. Add bin to your PATH so you can execute it from anywhere.            */
/*                                                                         */
/*    PATH=$PATH:/home/userx/bin; export PATH                              */
/*    =======================================                              */
/*                                                                         */

/*eject*/
/*------------------------- compile uvhd for DWIN -----------------------*/
/*Feb2003 - uvhd compiled on windows using lcc-win32 compiler in DOS box */
/* D:\uvwin\uvhd - directory for windows compile, contains uvhd.c        */
/* PATH environment must include D:\lcc\bin PATH to lcc-win32 comiler    */

/* 1. lcc -DDWIN uvhd.c   - compile uvhd source to object              */
/* 2. lcclnk uvhd.obj       - link uvhd object to executable             */
/* 3. uvhd custmas1 r256    - execute/test uvhd on custmas1 demo file    */

/*------------------- Problems compiling on windows ---------------------*/
/* 1 - truncate function not handled by uvhd.exe (compiled by lcc-win32) */
/*   - see truncate function called by uvhd 'z' command (see help above) */
/*   - 'z' subfunction trunc1 calls the unix/linux truncate function     */
/*   - truncate is defined by #include unistd.h on unix/linux            */
/*   - bypassed (with errmsg) when compiled on windows by testing DWIN   */
/*     (compile time variable defined only on windows compiles)          */
/*                                                                       */
/*     lcc -DDWIN uvhd.c  - compile with DWIN defined                    */
/*     =================                                                 */

/*eject*/
/***************************************************************************/
/*              ** possible customization re: PRINTING **                  */
/***************************************************************************/
/*                                                                         */
/* The print commands (p/i) use the command stored in variable 'prtcmd'    */
/*                                                                         */
/*     char prtcmd[30] = "uvlp12";     <-- default command                 */
/*                                                                         */
/* The 'uvlp12' script is part of the Vancouver Utilities package.         */
/* You can change this to the following & recompile as above.              */
/*                                                                         */
/*     char prtcmd[30] = "lp -onobanner -dlaser1";    (specify printer)    */
/*                                                                         */
/* Alternatively, you could add the following to your .profile             */
/*                                                                         */
/*     export UVHDPRINT="lp -onobanner -dlaser2"                           */
/*                                                                         */
/* Note that the other print command 'p' does not print immediately, but   */
/* collects all the printed data in a tmp/file, which is subsequently      */
/* printed manually after you quit the uvhd utility.                       */
/*                                                                         */
/* Mar2002 - added a 2nd variable for wide printouts                       */
/* Feb2003 - used only when option 'l' specified on 'p' or 'i' commands    */
/*                                                                         */
/*  char prtcmdW[30] = "uvlp12L"; <-- default cmd for print/iprint cmds    */
/*                                  - gets 100 chars on Landscape 8 1/2    */
/*                                                                         */
/*  export UVHDPWIDE="uvlp14L";   <-- env-var to modify default script     */
/*                                  - uvlp14L gets 128 chars on Landscape  */
/*                                                                         */
/*  export UVHDPWIDE="uvlp18";    <-- alternate for wide printouts         */
/*                                  - gets 128 chars on Portrait 8 1/2     */
/*                                                                         */
/*-------------------- uvhd files on distribution CD --------------------- */
/*                                                                         */
/* If you have installed the Vancouver Utiliites from CD (vs web download) */
/* the directories & filenames relevant to uvhd will be as follows:        */
/* (see complete details in install.doc)                                   */
/*                                                                         */
/* /home/uvadm/src/uvhd.c       - source program (ansi C)                  */
/* /home/uvadm/bin/uvhd         - executable program                       */
/* /home/uvadm/doc/uvhd.doc     - documentation as a text file             */
/* /home/uvadm/dochtml/uvhd.htm - documentation in HTML                    */

/*eject*/
/***************************************************************************/
/*                        ** uvhd functions **                             */
/***************************************************************************/
/*                                                                         */
/* 1. Browse  - display records consecutively                              */
/*            - jump to & display any record in the file by record#        */
/*                                                                         */
/* 2. Search  - search by pattern, anywhere in the record (by default)     */
/*              or within specified column locations                       */
/*            - may search on 1 or 2 fields with AND/OR relationships      */
/*            - match data =,>,< equal(default), greater than, less than   */
/*            - repeat previous search via 'ss' vs 's' + search args       */
/*            - search backward via 'sb' or 'ssb'                          */
/*                                                                         */
/* 3. Update  - update the current record on display                       */
/*            - specified columns replaced with specified data (char/hex)  */
/*            - option to rollback current record updates                  */
/*            - option for multi-record updates with 1 qualifier field     */
/*                                                                         */
/* 4. Print   - write formatted records to tmp file for later lp,uvlp,etc  */
/*            - output file will be: tmp/filename_yymmdd_hhmmssP           */
/*            - may specify a number of records from current position      */
/*            - may specify records matching 1 or 2 patterns & a max count */
/*            - To actually print, you must quit uvhd & select the desired */
/*              filename from the tmp directory.                           */
/*                                                                         */
/* 5. Iprint  - write formatted records to a file for immediate printing   */
/*              via the 'uvlp12' command (see uvlist.doc).                 */
/*            - may specify a number of records &/or records matching      */
/*              1 or 2 specified patterns within specified columns.        */
/*                                                                         */
/* 6. Write   - write UNformattted records to file: tmp/fname_yymmdd_hhmmssW*/
/*              for subsequent use as test files, etc.                     */
/*            - may specify number of records from current position        */
/*              or records matching 1 or 2 patterns within specified columns*/
/*            - select specified records from big files for small test files*/
/*                                                                         */
/* 7. Drop    - copy to tmp file, dropping records matching 1 or 2 patterns*/
/*            - also drop any deleted records (x'00' last byte)            */
/*              in Indexed files (if the 'i' option is specified).         */
/*                                                                         */
/* 8. Check   - check sequence of specified field                          */
/*            - options for ascending, descending, duplicates              */
/*                                                                         */
/* 9. Tally   - count records from current position matching 1 or 2        */
/*              specified patterns until EOF or within a max record count  */
/*                                                                         */
/* 10. scan/replace - scan specified record field for a search pattern     */
/*                  - if found replace with specified replacement pattern  */
/*                                                                         */
/* 11. generate sequence numbers - command 'g'                             */

/*eject*/
/*--------------------------- help screens ----------------------------*/
/* - displayed if cmd line has no args or too many args                */
/* - also displayed by operator command '?'                            */

char *help01[28] = {
" ",
"                    ** uvhd HELP MENU (screen #1 of 19) **",
"uvhd filename [options]    <-- enter filename & options on command line",
"uvhd dat1/custmas1 r256us2 <-- Example (r256=recsize,u=updates,s2=space2)",
"==========================   - options lower case, optional numeric values",
"        ** HELP MENU - select by entering 1-19, or null for next **",
"help01 - (this screen) uvhd command line & Help screen menu",
"help02 - command line options a-l",
"help03 - command line options m-z",
"help04 - Browsing commands (r#, b#, +, -, R#, ?)",
"help05 - Search commands ",
"help06 - Search commands continued",
"help07 - Update (u) & Rollback (x/X)",
"help08 - Update - more examples",
"help09 - Scan/Replace (v) ",
"help10 - Write selected records to date/time stamped filename in tmp subdir",
"help11 - Write options (a=ASCII,c=chars,r=recsize to write,t=terminator,etc)",
"help12 - Print & Iprint (Immediate print)",
"help13 - Translate to Ascii,Ebcdic,Upper,Lower,Characters,Periods",
"help14 - sequence number checking & generating",
"help15 - Enumerate/count (e) ",
"help16 - Drop records(d), Truncate file(z)",
"help17 - uvhd Tips & Techniques (saving keystrokes)",
"help18 - Customizing uvhd for User Preferences re Options & Printing",
"help19 - Free Software Notice - Licensed under GNU FSF GPLv3",
"!"};

/*eject*/
char *help02[32] = {
" ",
"                   ** help02 of 19 - command line options (a-l) **",
" ",
"uvhd dat1/custmas1 r256us2  <-- Example (r256=recsize,u=updates,s2=space2)",
"==========================",
" ",
"- options entered as arg 2 on uvhd command line, must be contiguous string",
"- options are lower case alphabetics (optionally followed by numeric value)",
"a1   - translate to ASCII, character line (prior to zone & digit lines)",
"b1   - inhibit BOLD search patterns, b2=no BOLD errmsgs, b3=both(DOS dflt)",
"c1   - display extended ASCII chars values 128-255 (vs showing as periods)",
"d2   - show file dsplcmnts on all segments vs only 1st segment of record",
"e1   - no errmsg filesize/recsize not divisible, e2=no recsize calc, e3=both",
"f#   - first record# to be displayed (default #1)",
"g0   - zero relative column scale (default), use g1 for 1 relative",
"g1   - also use 1 relative dsplcmnts on commands (search,update,etc)",
"h0   - auto determine display: chars only or hexadecimal (depending on data)",
"h1/h2- h1=force characters only, h2=force hexadecimal (chars/zones/digits)",
"l#   - line display width, default 64, max 1024 ",
" ",
" ",
" ",
" ",
" ",
"!"};
/* "j1   - maintain command history .file (specify env-var UVHDROP)", */

/*eject*/
char *help03[28] = {
" ",
"            ** help03 of 19 - command line options continued (m-z) **",
" ",
"m#   - max lines/screen (default 16) allow for hex/char, sp1/sp2",
"n#   - number of records per screen (between prompts, for short records)",
"o#   - inhibit column scale to every #records, use with n# records/screen",
"r#   - record size, default 256, max 32768 ",
"s#   - space before: s1=scale, s2=data, s4=prompt, s8=after scale, s15=all",
"t1   - text records ended by CR=x'0D' - unusual, old Macs, obsolete ?",
"t2   - text records ended by LF=x'0A' - standard unix/windows",
"u    - required when uvhd invoked, to allow update command (else read only)",
"v    - variable length records Micro Focus Seqntl/Indexed IDXFORMAT3/8",
"       show recs: v1=filehdr, v2=deleted, v4=active data, v8=all (dflt=v5)",
"w1   - combine all Write cmd outputs into 1 file (vs 1 file per Write cmd)",
"     - output files written to: tmp/inputfilename_yymmdd_hhmmssW",
"z#   - input RDW format (2,4,8 byte prefixes with recsize in binary)",
"z2   - prefix 2 bytes only, recsize EXCLUDES prefix size",
"z4   - prefix 4 bytes, recsize 1st 2, 3&4 nulls, recsize INCLUDES prefix",
"z8   - prefix 8 bytes, recsize 1st 2, 3&4 null, 5-8 ignored",
"z1   - (+ 1 bit) = z3/z5/z9 Little-end binary, z2/z4/z8 default BIG-end",
" ",
" ",
" ",
"!"};

char *help04[28] = {
" ",
"                ** help04 of 19 - file Browsing commands **",
" ",
" >>> uvhd commands begin here on help04 & continue thru help19 <<<",
"uvhd commands are entered at --> prompt following each record display",
" ",
"null   - next record (or next segment if rcsz > 384)",
"r#     - goto specific record# in the file (one relative)",
"##     - can omit the 'r' (numeric value considered a record#)",
"+#     - advance specified # of records (from current position)",
"+        (default 1 if no value specified)",
"-#     - backup  specified # of records (from current position)",
"b#     - goto specific  byte#  in the file (zero relative)",
"R#     - change Record size (vs option 'r' on command line)",
"?      - display help (19 screens)",
"!"};

/*eject*/
char *help05[26] = {
" ",
"                    ** help05 of 19 - Search (1st of 2) **",
" ",
"uvhd dat1/custmas1 r256u  - examples for demo file 'custmas1' (may download)",
"========================  - r256=recsize,u=update allowed",
" - search for patterns until EOF (should goto rec #1 before 1st search)",
" - try these on the custmas1 demo file, then try similar on your file",
" - add 1 to following byte#s if you used option g1 for 1 relative (vs 0 rel)",
"s 'LTD'           - search for 'LTD' anywhere in record",
"ss                - repeat last search (starting from next record)",
"s 0(60),'LTD'     - search for 'LTD' anywhere in 1st 60 bytes",
"s 77(2),='BC'     - search for 'BC' in bytes 77-78 (match = assumed)",
"s 77(2),!'BC'     - search for Not 'BC' in bytes 77-78",
"s 77(2),>'BC'     - search for greater than 'BC' in bytes 77-78",
"s 120(5),x'000000000C'  - search for packed decimal zero field in 120-124",
"s 120(5),!x'000000000C' - search for non-zero packed field in 120-124",
"s 'ROAD',|,'STREET'     - search for ROAD or STREET anywhere in record (|=OR)",
"s 'ROAD',|,'STREET',|,'DRIVE' - search for 3 patterns anywhere in record",
"s 0(1),'1',77(2),'BC'               - '1' in byte 0 And 'BC' in 77-78",
"s 0(1),'1',|77(2),'BC'              - '1' in byte 0 Or 'BC' in 77-78 ",
"s 0(1),'1',77(2),'BC',90(3),'604'   - '1' in 0 And BC(77-78) And 604(90-92)",
"s 0(1),'1',|77(2),'BC',|90(3),'604' - '1' in 0 Or BC(77-78) Or 604(90-92)",
"s adrs1,value1,adrs2,value2,adrs3,value3 - 3 pairs of address & value",
" - 'And' is the default when 2 or 3 conditions are specified",
" - 'Or' is signalled by the '|' (pipe symbol)",
"!"
};

/*eject*/
char *help06[26] = {
" ",
"                  ** help06 of 19 - Search (continued) **",
" ",
"uvhd dat1/custmas1 r256u - demo file shown at www.uvsoftware.ca/uvhd.htm#I1",
"======================== - must specify option 'u' if updates intended",
" search for patterns until EOF (may goto rec #1 before 1st search)",
" ",
"s  'LTD'          - search (forward by default) for LTD anywhere in record",
"sb 'LTD'          - search backward for LTD anywhere in record",
"ss                - repeat last search (starting from next record)",
"sss               - repeat search (in same record incrementing byte#)",
" ",
"s 77(2),'BC',242(4),>'1989',242(4),<'2000' - search for BC updated in 1990's",
"========================================== - search date range ",
"s 77(2),'BC',242(4),>'1989',<'2000' - same (3rd adrs defaults to 2nd)",
" ",
"s 77(2),'AB',|77(2),'AL'   - search for 'AB' or 'AL in 77-78",
"s 77(2),'AB',|,'AL'        - same as above (2nd field defaults to 1st)",
"s 77(2),'AB',|,'AL',|,'YK' - search for 'AB' Or 'AL' Or 'YK' in 77-78",
"s 'LTD','ROAD','BC'        - searches entire record, all 3 must be found",
"s 'LTD',|,'ROAD',|,'BC'    - searches entire record, any 1 found is a match",
" ",
"sn5 77(2),'BC'   - search for 5th occurrence (option 'n') of 'BC' ",
"!"
};

/*eject*/
char *help07[26] = {
" ",
"                  ** help07 of 19 - Update (u) & Rollback (x/X) **",
" ",
"uvhd dat1/custmas1 r256u - demo file shown at www.uvsoftware.ca/uvhd.htm#I1",
"======================== - must specify option 'u' when updates intended",
" ",
"u 77(2),'AB'     - update current record 77-78 with 'AB' (fixing some 'AL's)",
"x                - rollback last update (repeat 'x' toggles last update)",
"uu               - repeat last update (after moving to different record)",
" ",
"u9999 77(2),'AB' - update next 9999 records, or until EOF (no rollback) ",
"                 - no x/X rollback for multi-record updates (backup file 1st)",
" ",
"u99 77(2),'AB',77(2),'AL' - update next 99 recs with AB 77-78 if currently AL",
"u99 77(2),'AB',,'AL'   - same as above, 2nd adrs defaults to 1st",
"u99 9(1),'*',8(1),'D',77(2),'BC' - update byte 9 '*', if 8 is 'D' & prov BC",
" ",
">>> x/X - rollback last update or all updates to current rec ",
"x       - rollback last update to current rec (repeat x toggles last update)",
"X       - rollback ALL updates to current rec (repeat X toggles ALL updts)",
"x/X only for record on display & applies to both u(update) & v(scan/replace)",
" ",
"!"
};

/*eject*/
char *help08[26] = {
" ",
"          ** help08 of 19 - Update - more examples **",
" ",
"uvhd dat1/custmas1 r256u - demo file shown at www.uvsoftware.ca/uvhd.htm#I1",
"======================== - must specify option 'u' if updates intended",
" ",
"u 75(1),'*',77(2),'BC'  - test conditional update on 1 (current) record",
"uu9999                  - then re-execute update on ALL records via uu9999",
" ",
"                 - OR for interactive updates, use 'ss' & 'uu' interleaved",
"s 77(2),'BC'     - 1st search to qualifying record",
"u 75(1),'*'      - 1st update to record on display",
"ss               - repeat last search (s 77(2),'BC')",
"uu               - repeat last update (u 75(1),'*')",
"                 - can also use this for interleaved 'ss' & 'vv' (scan/rep)",
" ",
"x - rollback last replace on current record (repeat 'x' toggles last rep)",
"X - rollback all replacements on current record ('X' toggles)",
"  - rollback applies only to 1 current record & ability lost if you move off",
"!"
};

/*eject*/
char *help09[26] = {
" ",
"                  ** help09 of 19 - Scan/Replace (v) **",
" ",
">>> v=scan/replace - scan a record field for a search pattern",
"      - if found replace with a specified replacement pattern",
"      - may truncate or blank fill if search/replace pattern lengths unequal",
" ",
"uvhd dat1/custmas1 r256u - demo file layout at www.uvsoftware.ca/uvhd.htm#I1",
"======================== - must specify option 'u' if 'v' updates intended",
"v 10(25),'LTD','LIMITED' - replace 'LTD' with 'LIMITED' anywhere in 10-34",
"      - will lose any data pushed beyond byte 34 (fixed length record/fields)",
" ",
"v999 10(25),'LTD','LIMITED' - 'LTD' to 'LIMITED' on next 999 records (or EOF)",
"v999 35(25),'AVE.','AVENUE',77(2),'BC' - replace only if BC in 77-78",
"v999 'ST.','STREET'         - a big mistake for fixed length record layouts",
"v999 0(256),'ST.','STREET'  - same as this, entire record would be shifted",
" ",
"v999w 10(25),'LTD','LIMITED' - option 'w' to write separate file to tmp/...",
"      - writes tmp/filename_yymmdd_hhmmssV (Text records, this field only)",
" ",
"uvhd tf/tabtest1 t <-- demo TEXT file with tabs (unix expands to 8 spaces)",
"==================   - option 't' Text file, 'u' not req'd to write sep file",
"v999w x'09','    ' <-- replace tabs (x'09') with 4 spaces (tf/tabtest1 file)",
"      - option 'w' with dsp(lth) omitted causes scan/rep on entire record",
"      - records written to tmp/... will be expanded/contracted as required",
"!"
};

/*eject*/
char *help10[26] = {
" ",
"           ** help10 of 19 - Write selected records to tmp subdir **",
" ",
">>> w=write - write data records only for later use (test files,etc)",
"            - writes start at current position & end at EOF (or max count)",
"            - write outputs written/appended to tmp/filename_yymmdd_hhmmssW",
" ",
"w5              - write (unformatted) 5 records from current position",
"ww              - repeat last write command (from current position)",
" ",
"w 'VANCOUVER'   - write all records with 'VANCOUVER' anywhere in record",
"w25 77(2),'BC'  - write (25 max) records with 'BC' in bytes 77-78",
" ",
"w 8(1),'D',77(2),'AB',|,'AL' - write recs with 'D' in 8 & AB or AL in 77-78",
" ",
"we10            - write every 10th record (to make smaller test file ?)",
"w100e5          - write 100 records, select every 5th (file must have 500+)",
"we10 77(2),'BC' - write every 10th rec with province 'BC' (from here to EOF)",
" ",
"wi              - write entire file (if at 1st rec), except ISAM deleted",
"wi 77(2),'BC'   - select BC bytes 77-78, 'i' option drops ISAM deleted",
" ",
"!" };

char *help11[26] = {
" ",
"                   ** help11 of 19 - Write options **",
" ",
"w100r64t2 - sample Write command to Write next 100 records with options",
"          - r64=fixedsize 64 byte recs, t2=terminate with LF in last byte",
"w99z2a1c1 - write next 99 recs in RDWz2 format (recsize in 2 byte prefix)",
"          - translate to ASCII, translate any unprintable chars to periods",
" ",
"a1   - translate to ASCII, write command output records",
"c1   - convert unprintable chars in write output records to periods",
" ",
"n1   - insert variable recsize as 4 numerics at BEGIN write output",
"n2   - insert variable recsize as 4 numerics at END write output",
" ",
"r#   - record size for fixed-length output, omit for RDWz2/z4",
" ",
"t1   - insert Carriage-Return at end write records (for MAC)",
"t2   - insert Line-Feed at end of write records (for Unix/Linux)",
"t3   - insert both CR & LF at end write records (for some Windows programs)",
"t4   - insert CR/LF after Last-Non-Blank, else at end specified record-size",
" ",
"z#   - write RDW format (2 or 4 byte prefixes with recsize in binary)",
"z2   - prefix 2 bytes only, recsize EXCLUDES prefix size",
"z4   - prefix 4 bytes, recsize 1st 2, 3&4 nulls, recsize INCLUDES prefix",
"z1   - (add 1 bit) = z3/z5 Little-end binary, z2/z4 default BIG-end binary",
"!" };

/*eject*/
char *help12[26] = {
" ",
"            ** help12 of 19 - Print selected records to tmp subdir **",
" ",
">>> p=print - write formatted records to a file for subsequent lp,uvlp,vi,etc",
"            - writes tmp/filename_yymmdd_hhmmssP (concat for uvhd session)",
"            - export UVLPDEST=\"lp -dlaserxx\" for unix lp (see help17 below)",
"p5                - print 5 recs from current position",
"p25 'VANCOUVER'   - search & print (25 max) records with 'VANCOUVER' anywhere",
"p 77(2),'BC'      - search & print (til EOF) records with BC in bytes 77-78",
"p 77(2),'BC',90(3),!'604' - print recs with BC in 77-78 & Not 604 in 90-92",
"p9s2f3            - print 9 records, space 2, formfeed every 3rd record",
"pp                - repeat last print command (from current position)",
" ",
">>> i=iprint - write formatted records to tmp file for immediate uvlp12/lp",
"             - writes tmp/filename_yymmdd_hhmmssI & uvlp12 issued immediately",
"i5              - iprint 5 recs 64 char lines (dflt: export UVHDPRINT=uvlp12)",
"ii              - repeat last iprint command (from current position)",
"i 'VANCOUVER'       - iprint records with 'VANCOUVER' anywhere in record",
"i 77(2),'AB',|,'AL' - print records with AB or AL in 77-78",
"i 0(120),<x'20'     - iprint records with any bytes < space in 1st 120 bytes",
"i6s2f4      - iprint 6 records, space 2, formfeed every 4th record",
"i3s2l128    - iprint 3 recs, 128 bytes/line, using export UVHDPWIDE=uvlp14L",
"            - UVHDPWIDE=uvlp12L (Landscape) default if option 'l' on 'i' cmd ",
"            - UVHDPRINT=uvlp12 (Portrait) defaults option 'l' not specified",
"!"
};
/*eject*/
char *help13[26] = {
" ",
"   ** help13 of 19 - Translate to Ascii,Ebcdic,Upper,Lower,Characters **",
" ",
">>> t=translate - to ASCII,EBCDIC,UPPER,lower,characters,periods",
"    ta=Ascii, te=Ebcdic, tu=Upper, tl=lower, tc=characters, tp=periods",
"ta         - translate to Ascii, entire current record, (t1a is same)",
"ta 0(120)  - translate to ASCII, 1st 120 bytes of current record",
"ta 240(16) - translate bytes 240-255 (zero relative, 241-256 one relative)",
"t999999a   - translate to Ascii, all records in file (from current to EOF)",
"te         - translate to Ebcdic, entire current record",
"tu         - translate to Upper, entire current record",
"tl         - translate to Lower, entire current record",
"tc         - translate to Characters, any unprintables translated to blanks",
"           - unprintables are x'00'- x'1F' & x'7F'-x'FF'",
"tc1        - translate unprintable Chars to blanks, except LineFeeds x'0A'",
"tc2        - translate to Characters, except for Carriage-Returns x'0D'",
"tc4        - translate to Characters, except for FormFeeds x'0C'",
"tc8        - translate to Characters, except for Tabs x'09'",
"tc15       - translate to Chars, except LFs,CRs,FFs,Tabs (1+2+4+8=15)",
"tc3        - translate to Chars, except for LFs & Crs",
"tp         - translate any unprintable Characters to carats",
"tp15       - translate unprintables to carats, except LFs,CRs,FFs,Tabs ",
"t999999p1 0(120) translate all recs, unprintables to carats, except LF",
"!"
};
/*Apr04/06 - changed unprint char rep from period to carat */
/*         - did not like, should change back              */
/* - or provide 3 options tb=blanks, tc=carats, tp=periods */

/*eject*/
char *help14[26] = {
" ",
"          ** help14 of 19 - sequence number checking & generating **",
" ",
">>> c=check - check sequence in specified field - until EOF or SEQUENCE ERR",
"c  0(6)    - check sequence in key field 0-5 (cols 1-6), show 1st out of seq",
"cd 0(6)    - option 'd' to check sequence descending (vs ascending default)",
"ce 74(6)   - option 'e' to consider EQUAL keys out-of-sequence",
"ci 74(6)   - option 'i' to check sequence number increment by 1",
"ci10 74(6) - option 'i10' to check sequence number increment by 10, etc",
"cc         - repeat last sequence check (from current position to EOF)",
" ",
"g9999 75(5),'00001' - generate seqnums in cols 76-80 incrmnt by 1 from 00001",
"                    - specify count (9999) greater than no of recs in file",
"                    - else seqnum inserted only in current record",
"                    - must specify 'u' option on command line (like update)",
" ",
"gi10 72(8),'#00010.' - seqnum cols 74-79 from 000010 incrmnt by 10",
"       - any non-numeric data is inserted before & after the seqnum digits",
"       - might omit count 9999 to observe results in current record",
"gg9999 - then repeat command (gg) with count to seqnum entire file",
" ",
"g9999 74(6),'A00001',0(1),'A' - conditional seqnum, if col 1 = 'A'",
"r1                            - return to record #1 before next command",
"g9999 74(6),'B00001',0(1),'B' - conditional seqnum, if col 1 = 'B'",
"!"
};

/*eject*/
char *help15[26] = {
" ",
"                   ** help15 of 19 - Enumerate (e) **",
" ",
">>> e=enumerate - count records from current point to EOF",
"e                - count records from current position to EOF",
" ",
"uvhd dat1/custmas1 r256 - demo file shown at www.uvsoftware.ca/uvhd.htm#I1",
"=======================", 
" ",
"e 'LTD'          - count records with 'LTD' anywhere in current record",
" ",
"e25 77(2),'BC'   - count (within next 25 recs) recs with BC in bytes 77-78",
" ",
"e99 77(2),'AB',|77(2),'AL'  - count records with AB or AL in bytes 77-78",
"e99 77(2),'AB',|,'AL'       - same as above (2nd adrs defaults to 1st)",
"e99 77(2),'BC',90(3),!'604' - count recs with BC in 77-78 & Not 604 in 90-92",
"                            - powerful technique to verify record data",
"!"
};

/*eject*/
char *help16[26] = {
" ",
"             ** help16 of 19 - Drop records, Truncate file **",
" ",
">>>drop - copies file to tmp/fname_yyyymmdd_hhmmssD, drop matching records",
"          a pattern & optionally with D-ISAM flag x'00' in last byte",
"        - file overwritten if command repeated (no collection as on write)",
"        - copy starts at current position & ends at EOF (or max count)",
" ",
"d  8(1),'D'     - copy to tmp file dropping records with 'D' in column 9",
"d500i           - copy 500 recs max to tmp file dropping D-ISAM flagged recs",
"di              - copy entire file to tmp dropping D-ISAM flagged records",
" ",
">>> z=truncate (modify) filesize (truncate or extend with x'00's)",
" ",
"z100000 - truncate file to 100,000 bytes",
"        - works on Unix,Linux,Windows/SFU, but not on Windows/DOS",
"!" };

/*eject*/
char *help17[26] = {
" ",
"        ** help17 of 19 - uvhd Tips & Techniques (save keystrokes) **",
" ",
" tip#1 - use command repeats to save keystrokes",
"       - use 'double letter commands' to repeat prior cmd w/o rekeying args",
"    ss - repeat last search, uu - repeat last update, ii - repeat last print",
"       - 1st re-position to desired rec before executing repeat command",
"       - or 'r1' to return to begin file to include all recs",
"   ww5 - may specify count on repeat commands, if original was 'w5 0(1),'A''",
"         ww5 would write next 5 recs with A in 1st byte (bypassing non 'A's)",
" ",
"tip#2 - Transfer Search arguments to other commands (w,e,p,i)",
"      - allows you to verify correct arguments before other commands:",
"      - then transfer verified args to desired command w/o having to re-key",
"        for example, we could specify args on search & transfer to write: ",
"  s 77(2),'BC',242(4),>'1997'  - search for BC records newer than 1997",
"  w=s                          - transfer Search args to Write command",
"  ww99999                      - Write out selected records until EOF",
"      - transfer also applies to Enumerate(e=s), Print(p=s),& Iprint(i=s)",
" ",
"tip#3 - using NOT '!' condition, can use to find exceptions to most records",
"      - for example to find any provice codes that are NOT 'BC'",
"  s 77(2),!'BC'       <-- display records with province codes NOT = 'BC'",
"  w9999 77(2),!'BC'   <-- write NON 'BC' records to tmp/filename_date_time",
"!" };

/*eject*/
char *help18[26] = {
" ",
"      ** help18 of 19 - Customizing uvhd - User Preference Options **",
" ",
"- You may add Environmental Variables in your .profile, read by uvhd",
"  to save having to remember to key preference options on the command line",
"- For example setup option 'g1' if you prefer one relative to zero relative",
" ",
"export UVHDROP=s2z1  <-- user preference options example",
"                         s2=space2, g1=one relative byte#s (vs 0 rel default)",
" ",
"- There are 3 environmental variables to control printing preferences",
"- Effective for the Iprint (Immediate print) command ",
"  (Print command just writes vertical hex formatted lines to the tmp subdir",
" ",
"export UVLPDEST=-dlaserxx <-- specify printer destination for 'i' command",
"export UVHDPRINT=uvlp12   <-- specify iprint script for 64 char lines",
"                            - uvlp12 is UV script for laser Portrait printing",
"export UVHDPWIDE=uvlp12L  <-- recommended script for Landscape 100+ chars ",
"                            - UVHDPWIDE used if you specify 'il100' ",
"export UVHDPRINT=\"lp -dlaserxx\" <-- may specify standard unix lp command",
"export UVHDPWIDE=\"lp -dlaserxx -onobanner\" - for 64 chars or 100 chars",
" ",
"uvhd version 20090308 - Copyright UV Software Inc. 1993-2009",
" - see full documentation at: www.uvsoftware.ca/libuvhd.htm",
"!" };

char *help19[26] = {
" ",
"             ** help19 of 19 - Program Licensed under GPLv3",
" ",
"'uvhd' is a binary file investigation utility. It displays any file in",
"vertical hexadecimal and prompts for commands to browse, search, select,",
"update, scan/replace, print, translate, etc. uvhd is an interactive",
"utility with a command line interface and 17 help screens.",
" ",
"Copyright(C) 1993-2008, Owen Townsend, UV Software, owen@uvsoftware.ca",
" ",
"                    ** Licensed under GPLv3 **",
" ",
"This program is free software: you may redistribute it and/or modify",
"it under the terms of GPLv3 (GNU General Public License version 3),",
"as published by the Free Software Foundation.",
" ",
"This program is distributed in the hope that it will be useful,",
"but WITHOUT ANY WARRANTY; without even the implied warranty of",
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.",
" ",
"See the full description of the GNU General Public License at:",
"http://www.gnu.org/licenses.",
"!" };

/* dummy help20 */
char *help20[26] = {
" ",
"!" };

/*eject*/
/*--------------------- update history (latest 1st) -----------------------*/
/*                                                                         */
/*Feb09/09 - fix write optn t4, scan back not writing shorter size         */
/*         - fix write optn n1/n2, insert input size, not optn r outsize   */
/*                                                                         */
/*Feb05/09 - combine getrev & getrecx as getrecv using getrecx code        */
/*         - change all UVint to int                                       */
/*         - on write cmd, remove option x to write 2nd file of non-matches*/
/*         - write options changed from command line to write command:     */
/*         - a2->a1(ASCII), c2->c1(chars) ,k1/k2->n1/n2(recsizenumerics)   */
/*         - w#->r#(recsize), y#->t#(terminator)                           */
/*         - add write optns z2/z4 to write RDW records                    */
/*         - common subfunctions openfileX & closefileX for P,I,W,V,D files*/
/*         - chg P/I/W/V/D cmds to open & close output file on each cmd    */
/*         - cmd line option w1 to write 1 combined file vs separate       */
/*                                                                         */
/*Feb04/09 - remove the command history code, save in src/uvhd_history.c   */
/*         - change UVHDOPS to UVHDROP (consistent with UVCOPYROP,etc)     */
/*                                                                         */
/*Jan24/09 - correct recnum printed for RDW files (was 1 too high)         */
/*         - add option 'n' to search for nth occurrence                   */
/*         - validate RDW recsize <= 16384, else search for next RDW prefix*/
/*                                                                         */
/*Jan21/09 - if no cmd in parse() entered - return now (fix AIX seg fault) */
/*                                                                         */
/*Jan12/08 - compile scripts changed, L32/L64&P32/P64 combined to H32/H64*/
/*         - test for AIX 64 bit optn & add -X64 on 'ar' commands        */
/*         - specify both uvlib32/64.a & disam___32/64.a on command line */
/*                                                                       */
/*eject*/
/*Sep09/08 - provide option z1 for little-end RDW                          */
/*                                                                         */
/*Aug22/08 - added "of record# %d...recnum1" to search1 results opmsg      */
/*Aug07/08 - updt search arg1 lth with current recsize if op1 lth defaulted*/
/*                                                                         */
/*May16/08 - for scan/replace cmd 'v', write separate file if option 'w'   */
/*         - sep file req'd if search pattern lth != replace pattern lth   */
/*         - useful for multi-record scan/replace on text files            */
/*         - reorg help screens (now 19)                                   */
/*                                                                         */
/*Apr16/08 (Uchar*) inserted line 5025 HP compile warning mismatch ptr type*/
/*Mar19/08 - uxcopy rcs=4095 creates 4 byte rechdr, rcs=4094 2 bytes       */
/*         - so change test from (vrmax > 4095) to (vrmax >= 4095)         */
/*Dec11/07 - ensure write fixed size = optn w independent optns p,k        */
/*         - less if optn y4 (LF after LNB), more if y1/y2/y3 CR/LF        */
/*         - clear outrec area 2K vs 1K                                    */
/*Dec09/07 - fixed problem w Large Files on Windows lcc-win32              */
/*         - fixed by new lseeki64 modified from fseeki64, emailed by Jacob*/
/*Dec08/07 - compile script args H64/S64/H32 replaced by L32/L64+P64/P32   */
/*         - UVi64 now always 64 bits (long or long long)                  */
/*         - I64 & LF64 set in prgm, no need for -DI64 -DLF64 on compile   */
/*Dec05/07 - H64,S64 set I64 for 64 bit counters                           */
/*         - other prgms chgd in July but missed for uvhd until Dec05/07   */
/*Nov11/07 - recalc rec# on every get (better for varlth files)            */
/*         - new function calcrecs() & chkrcsz modified                    */
/*Nov09/07 - add 2nd help screen for options (now 15 help screens vs 14)   */
/*         - create getrect (for text) separate from getrecf               */
/*Nov05/07 - add option a2 to translate output records to ASCII            */
/*         - change option c2 to c1 (display hi bit chars)                 */
/*         - change option p1 to c2 (cnvrt write unprintables to periods)  */
/*         - add option p# prefix bytes to omit on write out records       */
/*         - remove option y4 (to drop RDW prefix, now option p4)          */
/*         - add optn y4 to insert CR as well LF (if p1) at EOR or LNB (y2)*/
/*         - add option k1 to insert recsize as 4 numchars at BEGIN outrec */
/*         - add option k2 to insert recsize as 4 numchars at END outrec   */
/*Nov05/07 - verify variable length RDW option z = z0,z2,z4,or z8 (vs z1)  */
/*         - now always assume recsize in 1st 2 bytes                      */
/*         - option z now = bytes added to size in hdr to reach next record*/
/*Oct26/07 - fix bug for + advance using option z1                         */
/*         - fix bug search 'xxx' errmsg if option z1, chg rszo to rsz1    */
/*Oct25/07 - in getrdw, if fileptr <= 4, set fileptr = 0 & recnum1=0       */
/*Jul02/07 - fix bugs to Write records for file types v,x,z                */
/*         - clear wrec area to allow for varilable length records         */
/*         - drop record headers if option y4                              */
/*Jun23/07 - for varlth files, make hdr rec #0, so data recs begin with #1 */
/*         - disable (op1+dsp > recsz) check for varlth files              */
/*Jun20/07 - op1 dsplcmnt check changed from min to max rsz for varlth files*/
/*Jun20/07 - record count calcs chg to max, varlth files often all max     */
/*Jun12/07 - if optn 'n' multi-recs/screen, restore fileptr to 1st on screen*/
/*         - for commands other than browse, record#, byte#                */
/*                                                                         */
/*eject*/
/*May15/07 - history optional via 'j1' (may set in profile UVHDROP)        */
/*         - option i chg to o display scale every o# lines                */
/*Apr05/07 - add option i# to display scale every # records                */
/*Apr01/07 - inhibit rec# incrmnt getrecv/getrecx (getrecf recalc from fptr)*/
/*         - if redisplay after update, etc                                 */
/*Feb23/07 - fix reading IDXFORMAT8 switchx roundup multiple of 4 not 8    */
/*Feb18/07 - extend date/time stamps, include seconds (file_yymmdd_hhmmssP)*/
/*         - insert '_' btwn date_time, chg fname.date to fname_date       */
/*Aug16/06 - add option 'z' for RDW filetype (4 byte hdr recsize binary)   */
/*         - old option 'z' (zero/one rel scale) changed to 'g'            */
/*         - add option 'y4' to omit 4 byte RDW hdr from output records    */
/*Jan14/06 - add generate sequence number command 'g'                      */
/*Dec2004 - add option 'x' for IDXFORMAT8 files MicroFocus fixed ISAM > 2gig*/
/*Nov2004 - RedHat Linux uses __USE_LARGEFILE64                            */
/*Aug2004 - add Translate to Ascii,Ebcdic,Upper,Lower,Charcters,Periods    */
/*        - (we changed the t=tally instruction to e=enumerate)            */
/*Jun2004 - add compile variable SFU (for windows SFU 3.5)                 */
/*Feb2004 - For text files (optn t) change rcsz default to 1024 (vs 256)   */
/*        - change option 'm' lines/screen from 16 to 19                   */
/*May2003 - allow 3 search patterns (see search cmd on help screen above)  */
/*        - add scan/replace command to scan a field for a search pattern  */
/*          & if found replace with specified replacement pattern          */
/*Mar2003 - bug fixed, searching for Not= '!' condition in last byte of record*/
/*        - warnmsg filesize not multiple recsize in reverse video + info */
/*        - shows next lower & higher recsize that divides into filesize  */
/*Mar2003 - add history file to display & prompt user to pick by #         */
/*          (history shown if user enters no filename on command line)     */
/*Feb2003 - allow line lengths up to 1024                                  */
/*        - highlight found search strings by standout mode (reverse/red)  */
/*        - ignore last 2 bytes of record (CRLF) when determining hex vs char*/
/*Jan2003 - fix bug re bypassing deleted records in variable indexed files */
/*Dec2002 - assume EBCDIC constant type e'---' if option 'a' on cmd line   */
/*        - allow 1 relative displacements if option g1 on command line    */
/*          for the various commands (search, update, etc)                 */
/*        - disable max & min rules in switchb subrtn                      */
/*        - problem on file hdr rec if rcsz < 128                          */
/*Nov2002 - enhanced IDXFORMAT3 record display options v1,v2,v4,v8         */
/*          v1=filehdr, v2=deleted, v4=active data, v8=all (default v5)    */
/*        - RSV Record Seqntl Variable & ISV Indexed Seqntl Variable       */
/*Dec2001 - option d0/d2 record dsplcmnts or file dsplcmnts                */
/*        - env-var $UVHDROP for user preference options                   */
/*May2001 - modified to allow files over 2 gigs, compile as follows:       */
/*        - UNIX version must support Large Files & 64 bit integers        */
/*        - verified on unixware 7.1.1                                     */
/*9911 - add support for Micro Focus ISAM variable length records          */
/*     - implement as option v (see cmd line options v1,v2,v4,v8)          */
/*     - change existing option 'v' to 't2' text file variable             */
/*9902 - allows 4 operands on commands (search,write,print,etc)            */
/*     - so you can search 2 different fields for different values&conditns*/

/*eject*/
/*--------------------------- LargeFile support ------------------------*/
/*May2001 - test largefile -DLF64 set in compile script                 */
/*        - tested on unixware 7.1.1, see intro(2) describes 2 methods  */
/* method#1 _FILE_OFFSET_BITS=64   <-- excludes method#2 ??             */
/* method#2 _LARGEFILE_SOURCE 1  + explicit calls open64 lseek64        */

#define _LARGEFILE64_SOURCE 1
#define _FILE_OFFSET_BITS 64
#define __USE_LARGEFILE64 1

/*Dec08/07 - above used for other uv programs, check folwng Nov2004 cmt?*/
/*Nov2004 - RedHat Linux uses __USE_LARGEFILE64 ????? */
/*Dec2007 - adding __USE_LARGEFILE64 also ?? */

/*Dec08/07 - LF64 always defined for Linux */
#if !defined(DWIN)
#define LF64 1
#endif

/*Dec07/07 - for lcc-win32, use _stati64 */
/*May18/08 - problems on Windows for 64 bit lseeki64  */
/* - tf/tabtest1 text files seek problem, reads 256 ? */
/* - stuck at EOF, r1 does not return to BOF          */
/* So - LF64 NOT defined on DWIN (see above)          */
/*    - use stat vs stati64 on DWIN (below)           */
#if defined(DWIN)

#if defined(LF64)
struct _stati64 fstat1;
char uvlargef[16] = "LF64W";
#else
struct stat fstat1;
char uvlargef[16] = "LF32";
#endif

#else /* not DWIN */
struct stat64 fstat1;
char uvlargef[16] = "LF64";
#endif

/*eject*/
/*------------------------ includes & defines ------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <unistd.h>

/* #include "UVtyp.h" - def's common to Vancouver utility C programs     */
/* - most Vancouver Utility prgms #include UVtyp.h header file           */
/*   but typdefs included in uvhd.c self-contained for internet freeware */

typedef int UVi32;
typedef unsigned char Uchar;
typedef unsigned int  intU;

/*Jul23/07 - chg compile script I64 to H64,S64,S32 (H64 to set PTR64) */
/*         - H64,S64 set I64 for 64 bit counters                      */
/*Dec05/07 - above chg in July was missed for uvhd until Dec05/07     */
/*Dec08/07 - H64/S64/H32 changed to L32/L64 + P64/P32                 */
/*Jan12/08 - compile scripts changed, L32/L64&P32/P64 combined to H32/H64*/
/* - always set I64 64 bit acums (long if H64, else long long assumed)*/
#define I64 1
#if defined(H64)
#define PTR64 1
char longbits[8] = "L64 ";
#else
char longbits[8] = "LL64";
#endif


/* define UVI64: 64 bit long if -DH64, else long long */
#if defined(H64)
typedef long UVi64;
#else
typedef long long UVi64;
#endif

/*Apr2001 - setup Edit mask for sprintf -  4 or 8 depending on I32/I64 */
/*        - compile time option I32/I64: ccc uvhd INT I64 NOISAM       */
/*Apr2003 - %Ld changed to %lld (some compilers dont like %Ld)         */
/*Mar05/08 - #if def(I64) changed to #if def(H64)                      */
#if defined(H64)
char E64[8] = "%ld";      /*Mar05/08 chg %lld to %ld */
#else
char E64[8] = "%lld";     /*Mar05/08 chg %d to %lld  */
#endif

/*eject*/
/* capture machine type from compile script manifest define       */
/* also define BEM (Big End Machines) vs LEM (Little End Machines)*/
#if defined(SCO)
char machine[8] = "SCO";
#elif defined(INT)
char machine[8] = "INT";
#elif defined(HP)
char machine[8] = "HP";
#define BEM 1
#elif defined(SUN)
char machine[8] = "SUN";
#define BEM 1
#elif defined(AIX)
char machine[8] = "AIX";
#define BEM 1
#elif defined(DEC)
char machine[8] = "DEC";
#define BEM 1
#elif defined(LNX)
char machine[8] = "LNX";
#elif defined(UWIN)           /* UWIN or CYGWIN under windows */
char machine[8] = "UWIN";
#elif defined(CWIN)           /* UWIN or CYGWIN under windows */
char machine[8] = "CWIN";
#elif defined(SFU)            /* SFU under windows */
char machine[8] = "SFU";
#endif

/* if BEM not defined, define LEM */
#if (!BEM)
#define LEM 1
#define BEM 0
#endif
/*Jan2004 - replace compound machine type tests with 1 simple test   */
/* #if defined (INT) || defined (LNX)  <-- I sometimes forgot LNX    */

/*eject*/
/*------------------------ defines,etc ------------------------------*/
#define RMAX  65536         /* maximum record size                   */
#define RDFLT 256           /* default rcsz - if no option 'r'       */
#define RDFLT2 1024         /* default rcsz - text optn & no optn 'r'*/
#define LMAX  1024          /* maximum line size                     */
#define LDFLT 64            /* default line size - if no option 'l'  */

time_t tmsecs;              /* unix time - seconds since 1970        */
char today[16];             /* todays date "yyyymmddhhmm"            */
char date6[8];              /* date in 6 digits                      */
char time6[8];              /* time in 6 digits                      */

/*------------------- DWIN/unix dependent defs ---------------------*/
#ifdef DWIN
char slashc = '\\';
char slashs[2] = "\\";
#else
char slashc = '/';
char slashs[2] = "/";
#endif

char cwdir[80];         /* current directory from $PWD, or null if undef  */


/*-------------------------- history file stuff --------------------------*/
/*Feb04/09 - most cmd history code removed (saved in src/uvhd_history.c)  */

char homedir[80];       /* $HOME directory or current dir if $HOME not def*/

char cwdir[80];         /* current directory from $PWD, or null if undef  */

/*eject*/
/*-------------------------- filename & options -----------------------*/
/*Feb2003 - prompt for filename & options if not entered on cmd line   */
/*        - if reply null, then show help screens & quit as before     */

char fnops[256];            /* filename & options reply to prompt      */
char ops[64];               /* options separated from filename         */
int fnopsl;                 /* length of filename & option reply       */
int fnl;                    /* length of filename til ending blank/null*/
int opsl;                   /* length of options                       */

/*------------------------ option storage -----------------------------*/
/*Dec2001 - provide user preferences options env-var $UVHDROP          */
/*        - stored 1st, any cmnd line options appended                 */
char runops[80];            /* options from command line               */
char opsu[64];              /* options $UVHDROP + command line         */
char opsc[28];              /* option characters                       */
                            /* sorted into alpha position              */
int opsi[28];               /* option parameter integer storage        */
                            /* opsp numerics cnvrtd by atoi            */
char *opsp[28];             /* option string pointers                  */
char  opsb[256];            /* buffer for option string data           */


/*Feb2003 -  control codes to highlight found search patterns             */
char smso[16] = "\x1B\x5B\x37\x6D"; /* start bold standout mode           */
char rmso[16] = "\x1B\x5B\x6D";     /*  end  bold standout mode           */
int smsol;                          /* lth of smso calculated at prgm init*/
int rmsol;                          /* lth of rmso calculated at prgm init*/

/*Mar18/03 - warning message areas for printRV (reverse video)        */
char warnmsg1[100];
char warnmsg2[100];

/*Nov11/07 - filetype code f,t,v,x,z to simplify tests */
/* - set by options t,v,x,z, else default to f         */
char ftype = '?';

/*eject*/
/*----------------------- file access vital w/s ----------------------*/

char fn1[100];          /* filename from prmtr1 of cmnd line          */

char fn1a[80];          /* full path filename (for history file)      */

char fn1b[80];          /* base filename (after last '/')             */

/* May2001 - change fopen,fread,fseek,fwrite to open,read,lseek,write */
/*         - because there is no fseek64                              */
/* - will use: open64, lseek64, stat64 for Large Files (> 2 gig)      */
/* - will still use fopen,fwrite for output files (print,write,etc)   */

int fd1;                 /* file descriptor from open (May 2001)       */

char fmdate[16];         /* file modifiction date "yyyymmddhhmm"       */

int openflags;           /* fileopen flags default O_RDONLY (0)        */
                         /* changed to O_RDWR (2) by option u (update) */
                         /* see subfunc fileopen()                     */

char recb[RMAX+40];      /* record extracted from buffer               */

char *irecd = recb;      /* record data same as record base for uvhd   */
                         /* uvhdcob offsets by IDX or RDW prefixes     */

char linr[LMAX+40];      /* record segment extracted from record       */
                         /* - for display on 3 vertical hex lines:     */

Uchar linc[LMAX+40];     /* line 1 - char with ctlchars cnvrtd to '.'s      */
Uchar linp[LMAX+40];     /* line 1 - smso/rmso codes inserted for search pat*/
char linz[LMAX+40];      /* line 2 - zones for vertical hex display         */
char lind[LMAX+40];      /* line 3 - digits for vertical hex display        */

UVi64 filesize;          /* end of file fileptr (via stat)             */
UVi64 filesiz0;          /* filesize evenly divisible by rcsz (but <)  */
UVi64 filextra;          /* partial record at EOF (fsize % rsize)      */
int filextra2;           /* (fsize % rsize) for sprintf %d stop warning*/
UVi64 fileptr1;          /* ptr to last record in file fsz1-rszo       */
UVi64 fileptr1s;         /* save fileptr when lastrec/EOF 1st detected */
                         /*Nov12/07 - to stop rec# incrmting at EOF    */
                         /* - until user enters file reposition cmd    */

UVi64 frecsc;            /* records calculated (filesize/rcsz)         */
int frecsp;              /* records for printing (screen headings)     */

int remndr;              /* remainder filesize % recsize for errmsg    */

int rszo;                /* record size from option r or RDFLT         */

int lszo;               /* line size from optn l or dflt to LDFLT     */

/*eject*/
/*---------------- current fileptr & recnum calc fields --------------*/

UVi64 fileptr;        /* new file position                             */
                      /* modified by user commands for byte# or rec#   */
                      /* never allowed < 0 or > filesize               */

UVi64 fpsv;           /* previous file position                        */
                      /* used in update, write, etc                    */

UVi64 fileptrp;       /* previous file ptr saved before get            */

UVi64 fileptrLR;      /* fileptr of Last Record used in Verify data    */

UVi64 fileptrLG;      /* fileptr from Last Get record                  */
                      /* to fix recnum1 on RDW files                   */
                      /* increment recnum1 only if fileptr > fileptrLG */
                      /* inhibited by recnum1x by r# or +/-            */

UVi64 recnum;         /* current record# in file  (0 relative)         */
                      /* - calculated from fileptr on each get         */
                      /* - will subtract 1 when oprtr enters           */
                      /*   to calc new fileptr = (rec#-1 * rcsz)       */

int recnum1;          /* recnum for display/print (1 relative)         */
                      /* fixed size - recum + 1                        */
                      /* text/variable - increment on get              */
                      /*               - reset to 1 when fileptr reset */

int recnum1o;         /* recnum1 % option 'o' display scale every # recs*/

int recnum1x;         /* recnum incrmnt inhibit 1/0              */
                      /* - for getrecv/getrecz after update, etc */

int rsz1;             /* current recsize - returned by getrec()        */

int rsz1d;            /* actual data size - depending on file type     */
                      /* (rsz1 - hdr size of RDW or IDX files)         */

int rsz1p;            /* last valid recsize returned by getrec()       */
                      /*Nov11/07 - used to calc browse fileptr at EOF  */

int rsz2;             /* record size for current command calcs         */
                      /* - to calc prt from rec#                       */
                      /* fixed files - use rszo from r optn on cmd line*/
                      /* text files - use last rsz1                    */

/*eject*/
int lsz1;             /* current line size                             */

int rii;              /* record index to current byte in record        */
UVi64 rii1;           /* record index b4 current line segment          */
int lii;              /* line   index to current byte in line          */

int linctr;           /* line ctr on current screen                    */
                      /* depending on hex, char only, space 1/2        */

int linmax;           /* max line count per screen option m            */

int xchar;            /* indicator 0/1 print current record in hex     */
                      /* 1 if any < 0x20 or > 0x7E                     */
                      /* But allow hex chars in last 2 bytes of record */
                      /* - to allow LF in last byte or CR/LF last2     */
                      /* also depends on option 'h'                    */
                      /* h0=auto determine, h1=chars only, h2=force hex*/

char firstop[12];     /* option f# for first rec to be displayed       */

int rpsctr;           /* records per screen - for option n              */
                      /* used only on null reply when browsing          */
UVi64 fileptro1;      /* fileptr saved from 1st on screen               */
                      /* restored on cmds other than browse, rec#, byte#*/

/*eject*/
/*-------------------------- getcbuf buffer ----------------------------*/
/* for getcbuf subfunction - get 1 byte at a time from a text file      */
/* - returns 1 character (unsigned) or EOF (-1) at end of file          */
/* - buffered by reading rszo (r option) bytes at begin each getrec     */
/* - or if buffer becomes empty before LF reached                       */

char bufa[16384];        /* buffer read area            */
char *bufp = bufa;       /* buffer pointer to next char */
int bufn = 0;            /* buffer bytes remaining      */


/*---------------------------- work area -------------------------------*/
/* work area for scan/replace - copy out & back to record field         */
char wa1[RMAX];          /* work area for scan/replace                  */


/*----------------------- help screen pointer table --------------------*/
/* for showhelps function (allows user to enter desired screen#)        */

char **hsp[20] = {
help01,
help02,
help03,
help04,
help05,
help06,
help07,
help08,
help09,
help10,
help11,
help12,
help13,
help14,
help15,
help16,
help17,
help18,
help19,
help20
};

/*eject*/
/*------------------------ column scale --------------------------------*/
/* work areas used to setup column scales for units & tens              */
/* depending on option g0/g1 for zero/one relative                      */

char scaleu0[12];        /* '0123456789' or '1234567890'  */
char scalet0[12];        /* '________10' --> '______1030' */

char scaleu1[1032];      /* units scale built depending on option g0/g1 */
char scalet1[1032];      /* tens scale built depending on option g0/g1  */

char scaleu2[1032];      /* units scale copied & truncated depending on lsize*/
char scalet2[1032];      /* tens scale copied & truncated depending on lsize*/


char fpedit[20];          /* 32/64 bit fileptr edit area, insert in scale1*/

char fsedit[20];          /* 32/64 bit filesize edit area                 */

/*eject*/
/*------------------------ command formats -----------------------------*/
/* command format - 2 types                                             */
/* type1 - 1 char command + optional numeric value                      */
/* r100                     - goto & display record #100                */
/*                                                                      */
/* type2 - cmd + op1,op2,op3,op4,op5,op6                                */
/* s 0(20),='ABC'             - search for ABC within 1st 20 bytes      */
/* p 0(3),>'800'              - print records > 800 in 1st 3 bytes      */
/* u 0(3),='ABC'              - update cols 1-3 of current rec w ABC    */
/* s ='ABC'                   - search for ABC anywhere in entire record*/
/* s 0(3),!'ABC'              - search for NOT ABC in cols 1-3          */
/*note - op1 search lth must match op2 constant lth                     */
/*       if searching NOT EQUAL (!) on fields > 1 byte                  */
/*                                                                      */
/* s 0(40),='ABC',40(40),='XYZ' - search for ABC in 1-40 & XYZ in 41-80 */
/*                                                                      */
/* s 0(1),='1',40(1),='A'   - search for 1 in byte 0 AND A in byte 40   */
/* s 0(1),'1',|40(1),'A'    - search for 1 in byte 0 OR A in byte 40    */
/*                                                                      */
/* s 0(1),>'1',40(1),<'Z'   - search for >1 in byte 0 & <Z in byte 40   */
/*                                                                      */
/* s 0(80),<x'20',0(80),>x'7E' - search for non printable chars in 1-80 */
/*                                                                      */
/* s 0(1),'1',|,'2'         - op3 omitted defaults to op1 search area   */
/*                          - must specify OR (|) for = conditions      */
/* s 0(1),>'1',,<'9'        - search for 2-8 in byte 0 (range search)   */
/*                                                                      */
/*eject*/
/*--------------------- command argument storage -----------------------*/
/*                                                                      */

char opcmd[80];                /* oprtr reply to command prompt         */
char opcmd2[80];               /* op reply alt w/s to dflt 99 to r99    */

char cms[80];                  /* command string (up to 1st space)      */

UVi64 cmn;                     /* numeric value appended to cmnd        */
                               /* May2001 - cmn changed to UVi64        */

char args[128];                /* arguments (after 1st space to next sp)*/
int argsd;                     /* dsplcmnt to current arg (for sdscopy) */

/* args separated op1,op2,op3,op4 before standardizing & defaulting     */
char arg1a[80];                /* field1 search area - dspl(lth)        */
char arg2a[80];                /* field1 search constant                */
char arg3a[80];                /* field2 search area - dspl(lth)        */
char arg4a[80];                /* field2 search constant                */
char arg5a[80];                /* field3 search area - dspl(lth)        */
char arg6a[80];                /* field3 search constant                */

/* args after defaulting & standardizing constants =c'ccc' >x'xx',etc   */
char arg1b[80];
char arg2b[80];
char arg3b[80];
char arg4b[80];
char arg5b[80];
char arg6b[80];

/* args after stripping quotes/conditions & cnvrsn to hex if indicated  */
char arg1c[80];
char arg2c[80];
char arg3c[80];
char arg4c[80];
char arg5c[80];
char arg6c[80];

/* arg dsplcmnts & lengths (only lengths for constants op2/op4)       */
int arg1d;                    /* op1 displacement                     */
int arg1l;                    /* op1 length                           */
int arg2l;                    /* op2 length (constant)                */
int arg3d;                    /* op3 displacement                     */
int arg3l;                    /* op3 length                           */
int arg4l;                    /* op4 length (constant)                */
int arg5d;                    /* op5 displacement                     */
int arg5l;                    /* op5 length                           */
int arg6l;                    /* op6 length (constant)                */
int op1dflt;                  /* op1 defaulted to 0(rsz1)             */

char cops[80];                 /* command options string                */
char copsc[28];                /* cmd options char array                */
int copsi[28];               /* cmd options integer array             */

char opmsg[128];               /* operator msg, stored by subrtns       */
char opmsg2[128];              /* operator msg2, stored by subrtns      */
                               /* for display after current screen      */

/*eject*/
/*--------------------- command structure storage ----------------------*/
/* structure to store command values                                    */
/* - initially stored here, then moved depending on 's' 'p' 'u'         */

struct CA {
  char cmd[80];             /* orig cmnd for errmsgs             */
  char cmc[80];             /* command chars                     */
  char cmo[80];             /* command options                   */
  char a2c[80];             /* op2 constant data                 */
  char a4c[80];             /* op4 constant data                 */
  char a6c[80];             /* op4 constant data                 */
  char a1cc[4];             /* op1 condition (not used)          */
  char a2cc[4];             /* op2 condition <=!> (in 1st byte)  */
  char a3cc[4];             /* op3 condition '|' 1st byte = OR   */
  char a4cc[4];             /* op4 condition <=!> (in 1st byte)  */
  char a5cc[4];             /* op5 condition '|' 1st byte = OR   */
  char a6cc[4];             /* op6 condition <=!> (in 1st byte)  */
  int cmv;                /* cmd value (numerics appended)     */
  int a1d;                /* op1 dsplcmnt                      */
  int a1l;                /* op1 length                        */
  int a2l;                /* op2 length                        */
  int a3d;                /* op3 dsplcmnt                      */
  int a3l;                /* op3 length                        */
  int a4l;                /* op4 length                        */
  int a5d;                /* op5 dsplcmnt                      */
  int a5l;                /* op5 length                        */
  int a6l;                /* op6 length                        */
  int op1dflt;            /* op1 defaulted to 0(rsz1)          */
  int pscd;               /* prior search continuation dsp     */
  int psfp;               /* prior search file ptr             */
  int opi[26];            /* cmnd options - integer            */
  char opc[26];             /* cmnd options - character          */
};

/* note re pscd - op1 dsp prior match (for search continue)      */
/* cmd ss  - repeat search (from next record)                    */
/* cmd sss - repeat search (same record, incrmnt column)         */
/* cmd ssr - repeat search (same record, resetting to column 1)  */

struct CA carg0;            /* cmd arg structure for clearing    */
struct CA cargs;            /* cmd args processing storage       */
struct CA sargs;            /* search args strorage              */
struct CA pargs;            /* print args storage                */
struct CA iargs;            /* print args storage                */
struct CA wargs;            /* write args storage                */
struct CA uargs;            /* update args storage               */
struct CA dargs;            /* copy/drop args storage            */
struct CA kargs;            /* check sequence args storage       */
struct CA eargs;            /* enumerate args storage            */
struct CA vargs;            /* scan/replace args storage         */
struct CA targs;            /* translate args storage            */
struct CA gargs;            /* generate sequence number          */

/*eject*/
/*--------------------------- output files -----------------------------*/
/*Feb05/09 - common functions openfileX & closefileX for P,I,W,V,D files*/
/*         - chg P/I/W/V/D cmds to open & close output file on each cmd */
/*         - cmd line option w1 to write 1 combined file vs separate    */
/*         - filename format as follows:                                */

/* tmp/inputfilename_yymmdd_hhmmssX                     <-- X=P/I/W/V/D */
/* tmp/inputfilename_yymmdd_       <-- basefilename + time + X each open*/

char bfname[80];               /* basefilename formatted at prgm init  */

/*------------------ output file for print command --------------------*/
/* print filename = tmp/inputfilename_yymmdd_hhmmssP                   */

char pfname[80];               /* print output filename built here */
FILE *pfptr;                   /* output file for print command    */
int pfopn;                     /* switch to open file on 1st print */
int prints;                    /* print command counter            */

/*------------------ output file for iprint command ------------------*/
/* iprint filename = tmp/inputfilename_yymmdd_hhmmssI                 */

char ifname[80];               /* iprint output filename built here   */
FILE *ifptr;                   /* output file for iprint command      */
int ifopn;                     /* switch to open file on 1st print    */
int iprints;                   /* iprint command counter              */

/* Iprint commands - modified depending on unix or DWIN               */
/*   - & depending on env-vars UVHDPRINT & UVHDPWIDE                  */
/*   - or you could modify source code here to 'lp' if desired        */
#ifdef DWIN
char prtcmd[30] = "uvlpr12";   /* iprint cmd string for system cmd    */
char prtcmdW[30] = "uvlpr12L"; /* iprint cmd Wide (Landscape?) i#l100 */
#else
char prtcmd[30] = "uvlp12";    /* iprint cmd string for system cmd    */
char prtcmdW[30] = "uvlp12L";  /* iprint cmd Wide (Landscape?) i#l100 */
#endif

char prtcmdfile[80];           /* iprint cmd string w file appended   */
char prtcmdWfile[80];          /* iprint Wide cmd with file appended  */

/*eject*/
/*------------------ output file for write command --------------------*/
/* write filename = tmp/inputfilename.datetimeW                        */
char wfname[80];               /* write output filename built here     */
FILE *wfptr;                   /* output file for write command        */
int wfopn;                     /* switch to open file on 1st write     */
int writes;                    /* write command counter                */

char wrecb[RMAX+2];         /* record area for write command        */

char *wrecd = wrecb;        /* adrs for data, offset by optns z2,z4,n1 */

int wrhs;              /* record hdr size for Write records            */

int wrs1w;             /* recsize for write I/O, for RDWz2 = data + 2  */
                       /* for RDWz4 = data + 4                         */

int wrs1h;             /* recsize for RDW hdr, sames as wrs1w for RDWz2*/
                       /* - must add 4 for RDWz4                       */
short wrs1hs;          /* write size in short for union switch ends    */

int wrsn1;             /* length of recsize numerics inserted by n1 */
                       /* - set to 4 if k1, else 0                  */
int wrsn2;             /* length of recsize numerics inserted by n2 */
                       /* - set to 4 if k2, else 0                  */
int wrsn3;             /* wrsn1 + wrsn2 (4 or 0)                    */

char *wrecn1;          /* adrs for optn n1 numeric recsize insert at bgnrec*/
char *wrecn2;          /* adrs for optn n2 numeric recsize insert at endrec*/

/*eject*/
/*------------------ output file for scan/replace command ----------------*/
/*May16/08 - scan/replace command added                                   */
/* tmp/inputfilename_yymmdd_hhmmssV       <-- scan replace filename format*/

char vfname[80];         /* filename_yymmdd_ formatted at program init    */
FILE *vfptr;             /* output file pointer for scan/replace command  */
int vfopn;               /* switch to open file on 1st scan/replace       */

/*------------------ output file for drop command --------------------*/
/* tmp/inputfilename_yymmdd_hhmmssD   <-- drop records filename format*/

char dfname[80];               /* drop output filename built here */
FILE *dfptr;                   /* output file for drop command    */
int dfopn;                     /* switch to open file on 1st drop */
int drops;                     /* drop command counter            */

/*------------------- file status structure ---------------------------*/
/* - to test for presence of 'tmp' sub-directory                       */
/* - will create if not present in current directory                   */

struct stat tmpstat;           /* file stat structure - test tmp dir   */

/*--------------------------- update storage ---------------------------*/
/* work storage for updates - sample update commands follow             */
/*   u0(3),='ABC'    - change 1st 3 bytes of current record to 'ABC'    */
/*   u10(3),=x'0D0A' - change bytes 11 & 12 of current record to x'0D0A'*/

char recsav1[RMAX+2];            /* current record before any updates   */
char recsav2[RMAX+2];            /* current record before last update   */

UVi64 fplastup = -1;             /* fileptr of last record updated      */
                                 /* -1 for init purposes                */

UVi64 seekstat;                 /* I/O status for seeks                 */

int writestat;                  /* I/O status for writes                */

UVi64 statstat;                 /* I/O status for stat                  */

int opstat;                     /* status returned by update & rollback*/

/*--------------------- check sequence field storage --------------------*/

char chksav1[128];               /* check sequence field storage         */
char chksav2[128];               /* check sequence field storage         */

int chksav1i;                    /* for option 'i' incrmnt i1,i10,etc    */
int chksav2i;

/*eject*/
/*-------------------- MF COBOL variable lth records --------------------*/
/* added 9911, option v (existing option v changed to option t)          */

char vfhdr[8192];        /* MF COBOL variable lth file hdr stored here   */
                         /*Dec2004 - increased to 8K for IDXFORMAT8      */

int vrmax;             /* max rcsz from file hdr bytes 54-57           */

int vrmin;             /* min rcsz from file hdr bytes 58-61           */

short vr4k;            /* indicator 1 if max rcsz > 4095, else 0       */

int vfhs;              /* file header size for IDXFORMAT8              */

int vfxtra;            /* vfhs-128 to read remainder of file header    */

int vrhs = 0;          /* rec hdr size = 4 if maxrcsz > 4095, else 2   */

union INTC             /* union used in getrecv                        */
{ int ii;
  short ss;
  char cc[4];
};

/*eject*/
/*------------------------------ RDW stuff -----------------------------*/

char rdwhdr[16];         /* work area to read 2, 4,or 8 byte header     */

int rdwhs = 0;           /* RDW header size from option z               */
/*Nov05/07 - variable length RDW option z verify z0,z2,z4,or z8         */
/* - now always assume recsize in 1st 2 bytes                           */
/*Sep09/08 - provide option z1 for little-end RDW                       */
/* z2=2bytehdr, z4=4bytehdr, z8=8bytehdr, z0 deflt z4,                  */
/* - remove prior z# considered lth of hdr                              */

/*Sep09/08 - use rdwhx = extra bytes for total recsize */
int rdwhx;             /* 2 for z2, 0 for z4 & z8    */

int vrsize;            /* current record size                         */
                       /* extracted from the 2 or 4 byte hdr          */

int vrslot;            /* current record slot (hdr+data+fill to mult4)*/

Uchar vrtyp;           /* current record type                         */
                       /* isolated from hdr zone only, digit cleared  */

int vfhs = 128;        /* file hdr size (128 bytes)                   */

/*Pre Nov2002 - vfhs used to calc fileptr from rec#, +recs, -recs       */
/*            - presuming Indexed files, no good for RSV seqntl files   */
/* Nov2002 - now change to presume RSV sequential files                 */
/*         - also presumes all recs minsize ??                          */
/*      IE - using random rec# probably wont work well for RSV/ISV files*/

/*Mar2003 - unions u1 & u2 moved here from switchb subfunction         */
/*        - was getting erroneous 'usage before def' msg from lcc-win32*/
union INTC u1;                 /* work area unions to switch ends      */
union INTC u2;

/*eject*/
/* trtprint.c - translate table - convert unprintable chars to periods   */
/*            - originally linked in from archive UVlib.a                */
/* 971107 - included here to make prgm self-contained for internet share */
/*Apr04/06 - changed unprintable char rep from period to carat 0x2E to 0x5E */
/*         - but did not like so changed back                               */

unsigned char trtprint[256] = {
0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,
0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x2E,
0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,
0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,
0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,
0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,
0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,
0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,
0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,
0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E
};

/*eject*/
/* ASCII to EBCDIC translation table  */
unsigned char asc2ebc[256] = {
0x00,0x01,0x02,0x03,0x37,0x2d,0x2e,0x2f,0x16,0x05,0x25,0x0b,0x0c,0x0d,0x0e,0x0f,
0x10,0x11,0x12,0x13,0x3c,0x3d,0x32,0x26,0x18,0x19,0x3f,0x27,0x1c,0x1d,0x1e,0x1f,
0x40,0x5a,0x7f,0x7b,0x5b,0x6c,0x50,0x7d,0x4d,0x5d,0x5c,0x4e,0x6b,0x60,0x4b,0x61,
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0x7a,0x5e,0x4c,0x7e,0x6e,0x6f,
0x7c,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,
0xd7,0xd8,0xd9,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0x4a,0xe0,0x4f,0x5f,0x6d,
0x79,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x91,0x92,0x93,0x94,0x95,0x96,
0x97,0x98,0x99,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xc0,0x6a,0xd0,0xa1,0x07,
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x1c,0x1d,
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0xbc,
0xab,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0xbb,0xac,0x40,0x40,0x40,0x40,0x40,
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
0x40,0x9e,0xae,0x8c,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40
};

/* EBCDIC to ASCII translation table  */
unsigned char ebc2asc[256] = {
0x00,0x01,0x02,0x03,0x04,0x09,0x06,0x7f,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
0x10,0x11,0x12,0x13,0x14,0x15,0x08,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
0x20,0x21,0x22,0x23,0x24,0x0a,0x17,0x1b,0x28,0x29,0x2a,0x2b,0x2c,0x05,0x06,0x07,
0x30,0x31,0x16,0x33,0x34,0x35,0x36,0x04,0x38,0x39,0x3a,0x3b,0x14,0x15,0x3e,0x1a,
 ' ',0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49, '[', '.', '<', '(', '+', ']',
 '&',0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59, '!', '$', '*', ')', ';', '^',
 '-', '/',0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69, '|', ',', '%', '_', '>', '?',
0x70,0x71,0x72,0x73,0x74,0x76,0x77,0x78,0x79, '`', ':', '#', '@',0x27, '=', '"',
0x80, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
0x90, 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
0xa0, '~', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',0xaa,0xab,0xac,0xad,0xae,0xaf,
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
 '{', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',0xca,0xcb,0xcc,0xcd,0xce,0xcf,
 '}', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',0xda,0xdb,0xdc,0xdd,0xde,0xdf,
0x5c,0xe1, 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',0xea,0xeb,0xec,0xed,0xee,0xef,
 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',0xfa,0xfb,0xfc,0xfd,0xfe,0xff
};

/*eject*/
/*-------------------------- translate tables -------------------------*/

/* neutral translate table - generated at program init                 */
Uchar trtn[256];

/* table to translate unprintables to chracters or periods(carats?)   */
Uchar trtc[256];

/* trtn copied to trtc & modified depending on translate options       */
/* - unprintables to blanks or periods(carats)                         */
/* - except for LFs, CRs, FFs, Tabs                                    */
/* - depending on options c1, c2, c4, c8                               */

/*eject*/
/*--------------- declare functions - unique to this program ------------*/

int getrec(short bits);
int getrecf(short bits);
int getrect(short bits);
int getrecv(short bits);
int getrdw(short bits);
int getrdw1(short bits);
int getlinr(int lsize);
void filterchar(unsigned char *string, int lsize);
void hexzone(char *out, char *in, int lsize);
void hexdigit(char *out, char *in, int lsize);
int search1(void);
int search2(char *recb, struct CA *cap);
int search3(char *recb, struct CA *cap);
int search4(char *recb, struct CA *cap);
int search234(char *recb, struct CA *cap);
int search34(char *recb, struct CA *cap);
int print1(int lsize);
int iprint1(int lsize);
int printrec(FILE *fptr, int lsize, int pspace);
int write1(void);
int drop1(void);
int writerec(void);
int update(void);
int seqgen(void);
int back1(void);
int backall(void);
int parse(void);
int chkrcsz(int recsiz0);
int calcrecs(int recsiz1, int rszdflt);
int setlast1(int recsiz1, int rszdflt);
int testlast1(int fhsize);
int check1(void);
int enum1(void);
int switchb(char *vrhdr, short bits);
int switchx(char *vrhdr, short bits);
int trunc1(void);
int openfile(void);
int fileseek(char *emsg);
int getcbuf(void);
short boldpat(Uchar *linp, Uchar *linc);
short sorthtdd(char *tbl, int ecnt);
short bublsort(char *tbl, int el, int kd, int kl, int ecnt);
/* short writehist(void); */
short printRV(char *warnmsg);
int calcrsl(int rsiz, int fsiz);
int calcrsg(int rsiz, int fsiz);
short sortint2(int *tbl, int ents);
int scanrep(void);
int scanrep1(char *recb, struct CA *cap);
int trans(void);
int tran1(void);
int tolower2(char *str, int max, short bits);
int toupper2(char *str, int max, short bits);

/*eject*/
/*--------------- declare functions - originally in UVhdr.h -------------*/
/* 971107 - included here to make prgm self-contained for internet share */

char *atol1(int *result, char *in, char stopc, int max, short bits);
int atol2(char *str, char stopc, int max, short bits);
char *atol3(int *result, char *in, char *stopc, int max, short bits);
int sncopy(char *out, char *in, int max, short bits);
int stncopy(char *out,char *in,char stopc,int max,short bits);
void errmsg(char *msg, char *inf1, char *inf2, int num, short bits);
int showhelps(char **hsp[], int hsmax);
int showhelp(char **helpmsgs, int max, short bits);
int waitreply1(char *reply, short quitctl);
int searchcm(char *data,char *matchchars,int max1,int max2,short bits);
void cnvdttm(char *dttm, time_t unixtime);
int hex2data(char *out, char *in, char stopc, int max, short bits);
int sortops(char *opsu, char *opsc, int *opsi, short bits);
int sortops4(char*opsu,char*opsc,int*opsi,char**opsp,char*opsb,short bits);
int toascii2(unsigned char *str, int max, short bits);
int toebcdic2(unsigned char *str, int max, short bits);
int toprint2(unsigned char *str, int max, short bits);
int sdscopy(char *out,char *in,int max,int *sds,char *stop,short bits);
int getlin0(FILE *fp, char *rec, int count, int stop, short bits);
int putlin0(FILE *fp, char *rec, int count, int stop, short bits);
short repchars(char *string, char *srcs, char *reps, int max, short bits);
/* int inithist(int argc, char **argv); */
int getdatetime(void);

int openfileX(char *xfname, char *xfid, FILE **xfptr, int *xfopn, short bits);
int closefileX(char *xfname, char *xfid, FILE **xfptr, int *xfopn, short bits);

/*eject*/
/**************************** main ************************************/
int main(int argc, char **argv)
{
int ii,ll,nn,ss,rr;               /* misc int (status, etc)         */
char *pp,*cc;                     /* misc char ptr                    */

getdatetime();                    /* get current date & time & format */

/* store options from user preferences env-var $UVHDROP first          */
/* - will later append any command line options (to override UVHDROP)  */
pp = getenv("UVHDROP");
if (pp)                           /* if env-var UVHDROP defined ?      */
  { strcpy(opsu,pp);
  }
ss = sortops4(opsu,opsc,opsi,opsp,opsb,0x10); /* sort/store alpha/num arrays*/
if (ss < 0)
  { errmsg("options invalid, must be alpha+numeric a1b2c300 etc"
            ,opsu,"",0,0x21);
  }

/* store users HOME dir for history file, or null = current dir if undef */
pp = getenv("HOME");
if (pp)
  { strcpy(homedir,pp);
  }

/* store users current working directory, or null = relative dir if undef */
pp = getenv("PWD");
if (pp)
  { strcpy(cwdir,pp);
  }

/* generate neutral translate table at x2100(256) or $trt           */
/* - for user to copy & modify before using trt instruction         */
for (ii=0; ii < 256; ii++)
     trtn[ii] = (Uchar) ii;

/*eject*/
/*May15/07 - test history file option j1 (may set in UVHDROP)        */
/*Feb04/09 - cmd history code removed (saved in src/uvhd_history.c)  */
/* if (opsi['j'-'a'] & 0x01) */
/*   { inithist(argc,argv);  */
/*   }                       */
/* writehist();              */
/* else ...                  */
    if (argc < 2)
      { showhelps(hsp,19);
        /* writehist();      write history file for any deletes*/
        exit(1);
      }
    else /* command line args (filename [& options] are present     */
      { strcpy(fn1,argv[1]);            /* store filename from arg1 */
        if (argc > 2)
          { strcat(runops,argv[2]);     /* concat options from arg2 */
          }
      }

/* store full pathname (whether history file or command line)       */
strcpy(fn1a,fn1);             /* presume full path already existing */
if ((fn1[0] != '/') && (fn1[0] != '\\'))
  { if (cwdir[0])             /* if $PWD was stored OK              */
      { strcpy(fn1a,cwdir);
        strcat(fn1a,slashs);
        strcat(fn1a,fn1);
      }
    else
      { strcpy(fn1a,fn1);
      }
  }

/*eject*/
/* calc length of smso & rmso (in case user modifies stringa)          */
smsol = strlen(smso);
rmsol = strlen(rmso);

/* create filenames for print & write commands (in case needed)        */
/* print filename = tmp/inputfilename_yymmdd_hhmmssP                   */
/* write filename = tmp/inputfilename_yymmdd_hhmmssW                   */
/* drop filename  = tmp/inputfilename_yymmdd_hhmmssD                   */

/* 1st extract root filename from input filename (removing prefix dirs)*/
strcpy(fn1b,fn1);                 /* presume no prefix directories     */
pp = strrchr(fn1,'/');            /* find last '/' (if any)            */
cc = strrchr(fn1,'\\');           /* find last '\' (if any)            */
if (pp)
  { strcpy(fn1b,pp+1);            /* copy root filename after last '/' */
  }
else if (cc)
  { strcpy(fn1b,cc+1);            /* copy root filename after last '/' */
  }

/* if WinDOS, convert any '/' to '\' in filename for write, print, etc */
#ifdef DWIN
repchars(fn1b,"/","\\",80,0);
#endif

/*Feb05/09 - common functions openfileX & closefileX for P,I,W,V,D files*/
/*         - chg P/I/W/V/D cmds to open & close output file on each cmd */
/*         - cmd line option w1 to write 1 combined file vs separate    */
/*         - filename format as follows:                                */
/* tmp/inputfilename_yymmdd_hhmmssX                     <-- X=P/I/W/V/D */
/* tmp/inputfilename_yymmdd_       <-- basefilename + time + X each open*/

/* create basefilename now at prgrm init & when files opened on each cmd*/
/* - will copy to pfname,ifname,wfname,etc & append time + ID P/I/W/V/D */
strcpy(bfname,"tmp");
strcat(bfname,slashs);
strcat(bfname,fn1b);
strcat(bfname,"_");
strcat(bfname,date6);
strcat(bfname,"_");

/*Jun12/07 - optn 'n' multi-recs/scrn fix restore fileptr to 1st on screen */
/*         - commands other than browse, rec#, byte# (write, print, etc)   */
/*         - not working try init cmd to blank (see test at end screen)    */
opcmd[0] = ' ';

/*eject*/
/* setup iprint command + filename, default command to uvlp12        */
/* - override if export UVHDPRINT=lp or uvlp12L, etc                 */
pp = getenv("UVHDPRINT");
if (pp)
  { strcpy(prtcmd,pp);
  }

/* setup iprint cmd Wide/Landscape + filename, default to uvlp12L     */
/* - override if export UVHDPWIDE=lp or uvlp12L, etc                  */
pp = getenv("UVHDPWIDE");
if (pp)
  { strcpy(prtcmdW,pp);
  }

/*eject*/
/*-------------------------- process options ----------------------------*/
/* process options - concat options from user or history onto any UVHDROP*/
/* - sort by alpha, store alpha array,& convert any num prmtrs to integer*/
strcat(opsu,runops);            /* concat any this run ops onto any UVHDROP */
ss = sortops4(opsu,opsc,opsi,opsp,opsb,0x10); /* sort/store alpha/num arrays*/
if (ss < 0)
  { errmsg("options invalid, must be alpha+numeric a1b2c300 etc"
            ,opsu,"",0,0x21);
  }

/* default option b3 if options b1 or b2 unspecified on DWIN               */
/* b1 inhibits Bold search patterns, b2 inhibits BOLD warns re fsize/rsize */
#ifdef DWIN
if (opsc['b'-'a'] == 0)          /* option b unspcfd ?         */
  { opsi['b'-'a'] = 3;           /* force inhibit Bold on DWIN */
  }
#endif

/* store recsize option */
/*Nov11/07 - default to 256 if not spcfd or too big */
rszo = opsi['r'-'a'];
if (rszo < 1 || rszo > RMAX)
  { rszo = RDFLT;
  }
/*Nov11/07 - init rsz1p last valid recsize to option r (or 256) at init*/
rsz1p = rszo;

/* store line width option - default if not spcfd or too big */
lszo = opsi['l'-'a'];
if (lszo < 1 || lszo > LMAX)
    lszo = LDFLT;

/* setup units scale depending on option g1/g0 1/0 relative         */
/*Aug16/06 - option changed to 'g' from 'z' (now RDW varlth filetype*/
if (opsi['g'-'a'] & 0x01)
    memcpy(scaleu0,"1234567890",10);
else
    memcpy(scaleu0,"0123456789",10);

/* now duplicate units scale for max line length 1030                */
for (ii=0; ii < 1030; ii+=10)
  { memcpy(scaleu1+ii,scaleu0,10);
  }

/*eject*/
/* setup tens scale depending on option g1/g0 1/0 relative         */
/*Aug16/06 - option changed to 'g' from 'z' (now RDW varlth filetype*/
memset(scalet1,' ',1030);                /* init to all blanks     */
if (opsi['g'-'a'] & 0x01)
 { for (ii=0; ii <= 1030; ii+=10)
     { sprintf(scalet0,"%4d",ii+10);
       memcpy(scalet1+6+ii,scalet0,4);
     }
 }
else
 { for (ii=0; ii <= 1030; ii+=10)
     { sprintf(scalet0,"%4d",ii+10);
       memcpy(scalet1+7+ii,scalet0,4);
     }
 }

/* setup max lines/screen from option 'm', or set default if m unspcfd */
linmax = opsi['m'-'a'];
if (linmax <= 0)
    linmax = 19;

/*Aug16/06 - inhibit warnmsg 'filesize not multiple of recsize' */
/*         - if varlth options 'v', 'z', or 't' (Nov11/07)      */
if ((opsc['v'-'a']) || (opsc['z'-'a']) || (opsc['t'-'a']))
  { opsi['e'-'a'] = 3;
  }

/* call subfunction to open file (also used by truncate command)       */
openfile();                   /* open file, store filesize, etc        */

/* now add new filename & options onto the end of the history file table */
/* & write the file back to disc, drop off 100 entries if > 400 entries  */
/* create new entry for history file --> yymmdd filename options <-- */
/*Feb04/09 - cmd history code removed (saved in src/uvhd_history.c)  */
/*         - about 20 lines of code (histadd) removed                */

/*eject*/
/*Dec2004 - test option 'v' (was x) Micro Focus COBOL IDXFORMAT8       */
/*Feb05/09 - combine getrev & getrecx as getrecv using getrecx code    */
if (opsc['v'-'a'])
  { ftype = 'v';
    /* v options: v1=filehdr,v2=deleted,v4=actixe data,v8=all (dflt=v5)*/
    /* - default to v5 if not explicit                                 */
    if (opsi['v'-'a'] == 0)
        opsi['v'-'a'] = 5;

/* - read file prefix (1st 128 bytes) to get max rcsz, min rcsz, etc   */
/* - will read remainder of file header later, depending on header size*/
/* - verify file is IDXFORMAT_ (1st 4 bits 0x30)                       */
/* - store max rcsz, min rcsz, file header size                        */

    rsz1 = read(fd1,vfhdr,128);          /* read 1st 128 of file       */

    /* verify file hdr rec valid                                       */
    if ((vfhdr[0] & 0xF0) == 0x30)
      { ;
      }
    else
      { errmsg("file header invalid (1st 4 bits not 0x30)",fn1,"",0,0x40);
      }
    /* store max rcsz, min rcsz                                       */
    vrmax = switchx(vfhdr+54,1);        /* store max rcsz             */
    vrmin = switchx(vfhdr+58,1);        /* store min rcsz             */

    /* store rec hdr size vrhs=4 if macrcsz > 4095, else vrhs=2       */
    /* store indicator vr4k = 1 if maxrcsz > 4095, else vr4k = 0      */
    /*Mar19/08 - uxcopy rcs=4095 creates 4 byte rechdr, rcs=4094 2 bytes*/
    /* - so change test below from (vrmax > 4095) to (vrmax >= 4095)  */
    if (vrmax >= 4095)
      { vrhs = 4;
        vr4k = 1;
        wrhs = 4;
      }
    else
      { vrhs = 2;
        vr4k = 0;
        wrhs = 2;
      }

/*eject*/
    /* call switchx to calc slot size of file header record            */
    switchx(vfhdr,vr4k);                   /* calc slsot size of header*/
    vfhs = vrslot;                         /* save file header size    */

    /* call subfunctn to check rcsz & calc number of records in file   */
    /* set rcsz = min rcsz for ISAM IDXFORMAT8                         */
    /*Jun20/07 - changed to max recsize, varlth files often all max    */
    rszo = vrmax;
    chkrcsz(rszo);

    /* reset ptr = 0 in case filehdr rec dsiplay desired (option v1)   */
    fileptr = 0;

    /* read any remaining size of file header (128 read above)         */
    vfxtra = (vrslot - 128);               /* calc size yet to be read */
    if (vfxtra > 0)
      { rsz1 = read(fd1,vfhdr+128,vfxtra); /* read remainder of header */
      }
  }

/*eject*/
/*Jul02/07 - store write record hdr size for file type z         */
/*Nov05/07 - save RDW rechdrsize from option z & verify 0,2,4,8  */
/* - optn z value is extra bytes reqd to reach begin next rec    */
/*Sep09/08 - provide option z1 for little-end RDW                */
/* z2=2bytehdr, z4=4bytehdr, z8=8bytehdr, z0 deflt z4,           */
/* - remove prior z# considered lth of hdr                       */
else if (opsc['z'-'a'])
  { ftype = 'z';
    if (opsi['z'-'a'] == 0)
      { opsi['z'-'a'] = 4;
      } 
    /* validate option z & set rdwhs = RDW hdr size             */
    /*Sep09/08 - also set rdwhx = extra bytes for total recsize */
    if (opsi['z'-'a'] & 0x02)
      { rdwhs = 2; 
        rdwhx = 2; 
      }
    else if (opsi['z'-'a'] & 0x04)
      { rdwhs = 4; 
        rdwhx = 0; 
      }
    else if (opsi['z'-'a'] & 0x08)
      { rdwhs = 8; 
        rdwhx = 0; 
      }
    else
      { errmsg("valid option z=z2,z4,z8(big-end) or z3,z5,z9(little-end)"
               ,opsu,"",0,0x21);
      }
    wrhs = 2;   /* wrhs used to offset translates etc */
  }
else if (opsc['t'-'a'])
  { ftype = 't';
    /* calcs for variable length text records (option 't') */
    chkrcsz(rszo);
  }
else
  { ftype = 'f';
    /* calcs for fixed lth records only                                   */
    /* call subfunctn to check rcsz & calc number of records in file      */
    /* - also called by command 'R' (change rcsz w/o quit & cmd line optn */
    chkrcsz(rszo);

    /* allow option 'f', first record to be displayed                     */
    /* calc fileptr using option f value & spcfd rcsz                     */
    nn = opsi['f'-'a'] - 1;      /* option f rec# -1 for 0 relative  */
    fileptr = (rszo * nn);       /* calc initial ptr = rec# * rszo   */
  }

fileptr1 = filesize;             /* init fileptr1 for optn f to work */

/*eject*/
/*-------------------------- begin record -----------------------------*/
/* get next sequential record - or depending on fileptr                */

bgnrec:

rsz1 = getrec(0);                     /* get next record           */

if (rsz1 <= 0)                        /* error or EOF ?            */
  { /*Nov11/07 - set fileptr1 back to filesize(EOF)                */
    /* - to reread last record after empty prompt at endscreen     */
    /* - see logic in getcalc1 (before get) & getcalc2 (after get) */
    fileptr1 = filesize;  /*Nov11/07 - to cause reread last record */
    goto endscreen;
  }

/* if at begin file - output hdr with file statistics                  */
if (fileptr == 0)
  { printf("\nfilename=%s options=%s\n",fn1a,opsu);
    printf("records=%d filesize=%s recsize=%d fsize%%rsize(remainder)=%d \n",
                 frecsp,fsedit,rsz1,remndr);
  }

/*---------------------------begin screen-------------------------------*/
bgnscreen:

linctr = 0;                        /* clear line ctr for current screen */

/*Apr05/07 - test option o# to display scale every # records */
/*May15/07 - option changed from 'i' to 'o'                  */
/* calc remainder (recnum1 % optn o) only if optn o spcfd    */
recnum1o = 1;     /* presume no option o for test below      */
if ((opsi['o'-'a']) && (recnum1 > 1))
  { recnum1o = (recnum1 % opsi['o'-'a']);
  }
if (recnum1o == 1)
  { /* test option s1 - space prior to scale  */
    if (opsi['s'-'a'] & 0x01)
      {  printf("\n");
         linctr++;
      }
    /* output screen hdr record column scale, 0/1 relative by option g0/g1  */
    sncopy(scalet2,scalet1,lszo,0x03);      /* setup scale truncated at lszo*/
    sncopy(scaleu2,scaleu1,lszo,0x03);
    printf("            %s\n",scalet2);    /* tens scale fileptr removed   */
    printf("r#%9ld %s\n",recnum1,scaleu2); /* units scale                  */
    linctr += 2;                           /* incrmnt line ctr for scale   */
    /*Apr2004 - option s8 added to space after scale  */
    if (opsi['s'-'a'] & 0x08)
      {  printf("\n");
         linctr++;
      }
  }
else
  {  printf("\n");        /* space 1 vs print scale */
     linctr++;
  }

/*eject*/
/*----------------------- begin line -----------------------------------*/
bgnline:
/* get next line & convert to vertical hex display format               */
/* getlinr - extracts next display segment from current record          */
/*         - returns 0 if eof, else number of bytes in current segment  */

lsz1 = getlinr(lszo);             /* get next segment from record        */
if (lsz1 <= 0)
  { /* test option s4 - space after record (prior to prompt at endscreen) */
    if (opsi['s'-'a'] & 0x04)
      {  printf("\n");
         linctr++;
      }
    goto endscreen;
  }

/* test option s2 - space before data lines (3 line hex or 1 line char) */
if (opsi['s'-'a'] & 0x02)
  { printf("\n");
    linctr++;                          /* increment screen line ctr */
  }

memcpy(linc,linr,lszo);            /* copy for translate/filter (LMAX)  */

/* test option 'a' to translate character line from EBCDIC to ASCII   */
/* note - bit 0x04 converts non-print EBCDIC chars to periods(carats) */
if (opsc['a'-'a'])                   /* option 'a' active ?           */
   toascii2((Uchar*)linc,lszo,0x04);  /* translate EBCDIC to ASCII (LMAX)*/

/* now convert data line to 3 lines for vertical hex display            */
/* - presuming full 64 bytes per line (will compensate below)           */
filterchar(linc,lszo);            /* format line #1 - character display */
hexzone(linz,linr,lszo);          /* format line #2 - hex zones display */
hexdigit(lind,linr,lszo);         /* format line #3 - hex digits display*/

/* linc,lind,linz have assumed 64 byte lines   (null in 65th)         */
/* now insert the array nulls again - based on length returned by     */
/* getlinr which may be < 64 if optn 'l' or optn 'f' or eof           */
   linc[lsz1] = '\0';
   linz[lsz1] = '\0';
   lind[lsz1] = '\0';

/* highlight last found search pattern, if any & not inhibited by optn b1 */
boldpat(linp,linc);              /* copy char data inserting highlight  */

/*eject*/
/*--------- now ready to print the 3 vertical hex lines ----------------*/
/*Nov2001 - fileptr removed on tens scale line, now insert on data segments*/
/*Dec2001 - always display fileptr on 1st segment of record             */
/*        - then test option d0/d2 for following record segments        */
/*     d0 - display record dsplcmnts on following segments of record    */
/*     d2 - display file dsplcmnts on all segments of record            */

if (rii1 == 0)
  { sprintf(fpedit,E64,fileptr);          /* edit allowing 32/64 bits   */
  }
else
  { if (opsi['d'-'a'] & 0x02)
      { sprintf(fpedit,E64,fileptr+rii1); /* file dsplcmnts on all segs */
      }
    else
      { sprintf(fpedit,E64,rii1);         /* record dsplmnts on segs 2+ */
      }
  }

/* display character line of current segment of current record          */
printf("%11s %s\n",fpedit,linp);      /* show character line of segment */
linctr++;                             /* increment screen line ctr      */

/* show zones & digits line depending on option 'h'                       */
/* display depending on option 'h' & < 0x20 or > 0x7E (allow CRLF last2)  */
/* h0 = auto determine by data, h1 = force chars only, h2 = force hex     */
if (((opsi['h'-'a'] & 0x02) && (!(opsi['h'-'a'] & 0x01)))
    || (((opsi['h'-'a'] & 0x03) == 0) && (xchar > 0)))
  { printf("            %s\n",linz);
    printf("            %s\n",lind);
    linctr += 2;                      /* increment for zones/digits    */
  }

/* test line ctr for screen full - depends on hex/char/sp1/sp2      */
if (linctr >= linmax)
    goto endscreen;
goto bgnline;

/*eject*/
/*---------------------- end of current screen -------------------------*/
endscreen:
/* clear recnum incrmnt inhbit - for getrecv/getrecz after update,etc   */
recnum1x = 0;

/* return point for some error conditions                               */
reprompt:

/* print any stored opmsg & opmsg2 & clear to prevent repeat            */
/* msgs such as search found locations stored in subrtns & stored       */
/* - for display after the relevant screen is displayed above           */
if (opmsg[0])
  { printf("%s",opmsg);
    opmsg[0] = '\0';
  }
if (opmsg2[0])
  { printf("%s",opmsg2);
    opmsg2[0] = '\0';
  }

/* warn operator if end of file reached */
/*Aug16/06 - not if varlth filetypes v,x,z,t (forced e3 at bgnjob) */
if ((fileptr == fileptr1) && (opsi['e'-'a'] == 0))
  { printf("record above is LAST record in file\n");
  }

/* issue warning msg if recsize not divisible evenly into filesize */
if ((filesize > filesiz0) && (opsi['e'-'a'] & 0x01) == 0)
  { filextra2 = filextra;        /* prevent warning if UVi64 long long*/
    sprintf(warnmsg1,"filesize NOT multiple of recsize, %d bytes remain\n"
                     ,filextra2);
    printRV(warnmsg1);           /* display msg in reverse video */
  }

/*Aug16/06 - inhibit EOF for varlth v,x,z,t (forced e3 at bgnjob) */
if ((fileptr >= filesiz0) && (opsi['e'-'a'] == 0))
  { printf("*** EOF reached (enter -1 to see last rec)\n");
  }

/* test for option 'n' (multi-recs per screen) when last cmd null=browse*/
if ((opsi['n'-'a']) && (opcmd[0] <= ' '))
  { rpsctr++;                    /* increment recs per screen ctr  */
    if (rpsctr < opsi['n'-'a'])
      { fileptr += rsz1;         /* advance ptr to next record     */
        goto bgnrec;             /* return to next rec w/o prompt  */
      }
    else
      { rpsctr = 0;
        fileptro1 = fileptr;     /* save fileptr of 1st on screen  */
                                 /* for restore on non-browse cmds */
      }
  }

/*eject*/
/* prompt operator for next command:                                    */
printf("rec#=%d rcount=%d rsize=%d fsize=%s %s"
              ,recnum1,frecsp,rsz1,fsedit,fn1);

/*Mar18/03 - display more info if filesize not multiple of recsize      */
/*           when on 1st record in file unless inhibited by option e2   */
if ((filesize > filesiz0) && (fileptr == 0) && ((opsi['e'-'a'] & 0x02) == 0))
  { ll = calcrsl(rsz1,filesize);        /* calc next multiple < recsize */
    rr = calcrsg(rsz1,filesize);        /* calc next multiple > recsize */
    printRV("\nIf Text file (with LineFeeds),  quit/restart with option 't'");
    printRV("\nIf Variable (IDXFORMAT3/8,RDW), quit/restart with optn v,or z");
    sprintf(warnmsg1
    ,"\nIf Fixed length records, change recsize now via command 'R' (R%d current)",rsz1);
    printRV(warnmsg1);
    sprintf(warnmsg1
    ,"\nnext evenly divisible record sizes lower/higher are --> R%d/R%d"
    ,ll,rr);
    printRV(warnmsg1);
    printRV("\nOR you may continue (ignoring these warnings) --> ");
  }
else
  { printf("\nnull=next,r#=rec,s=search,u=update,x=rollback,p=print,i=iprint,w=write,e=count");
    printf("\n,g=genseq#,c=chkseq#,t=translate(ta=Asc,te=Ebc,tu=Upr,tl=Lwr,tc=Chars,tp=Pers)");
    printf("\n,R#=Recsize,h1=char,h2=hex,q=quit,?=help --> ");
  }

/* wait for op command - return point (vs repormpt) if return value 2    */
/* - following transfer search args to write,print,iprint,enumerate,drop */
getopcmd:

memset(opcmd,'\0',80);             /* clear any prior command            */
fgets(opcmd,80,stdin);             /* wait for operator reply            */

/* convert any LineFeed in fgets reply to null */
ll = strlen(opcmd);
if ((ll > 0) && (opcmd[ll-1] == '\n'))
  { opcmd[ll-1] = '\0';
  }

ss = parse();                      /* parse oprtr reply                  */
                                   /* validate & store cmd args          */
if (ss <= 0)                       /* command valid status ?             */
    goto reprompt;

/*eject*/
/*-------- analyze operator response & perform indicated action -------- */

if ((opcmd[0] == 'q') || (opcmd[0] == ':') || (opcmd[0] == 0x1B))
  { goto endprgm;
  }

if (memcmp(opcmd,"help",4) == 0)
  { showhelps(hsp,19);
    goto endscreen;
  }

/* test for search cmd 1st, to reset search continue col on any other cmd*/
if (opcmd[0] == 's')                        /* search for pattern ?      */
  { ss = search1();
    if (ss <= 0)
      { errmsg("    - reset fileptr to BOF(r0) & retry(ss) ?","","",0,0x00);
        goto reprompt;
      }
    else
      { goto bgnscreen;
      }
  }

/* reset search continue column# - on any cmd other than search repeat*/
sargs.pscd = 0;                        /* reset search continue col#  */

/* if Null reply - advance to next record, or next screen if rsize>320*/
if (opcmd[0] <= ' ')                   /* null reply ?                */
  { if (rii >= rsz1)
      { fileptr += rsz1;           /* advance to next record        */
        /*Note - above works for varlth as well as fixlth           */
        /*     - since last getrec stored rsz1 correct for all types*/
        goto bgnrec;
      }
    else
        goto bgnscreen;
  }

/*Nov12/07 - user enter a cmd (vs ' ' null entry) */
/* - reset fileptr1s to allow recnum1 incrmnt for varlth files    */
/*   (inhibited after EOF by set fileptr1s to last record)        */
fileptr1s = filesize; /* allow recnum1 incrmnt after non-null cmd */

/* store rsz2 to be used for following fileptr calcs from rec#     */
/* fixed files - use rszo from r optn on cmd line                  */
/* variable files - use last valid recsize (might be prior to EOF) */
if (ftype == 'f')
  { rsz2 = rszo;       /* fixed files - use rszo r optn cmd line   */
  }
else
  { rsz2 = rsz1p;      /* variable files - use last valid recsize  */
  }

/*eject*/
if (opcmd[0] == 'r')               /* r# (goto specific record) ? */
  { /* subtract 1 from oprtrs 1 relative entry, verify > 0, recalc ptr*/
    cmn--;
    if (cmn < 0)
      { cmn = 0;                   /* correct any oprtr confusion */
      }
    /*Nov11/07 - calc new file ptr & goto display record          */
    fileptr = (cmn * rsz2);        /* calc new fileptr (fixed)    */
    goto bgnrec;
  }

else if (opcmd[0] == 'b')              /* b# goto specific byte#      */
  { fileptr = (cmn);                   /* store new fileptr           */
    goto bgnrec;
  }

else if (opcmd[0] == '+')                   /* +# (forward #recs)      */
  { if (cmn == 0) cmn = 1;                  /* default value to 1      */
    if ((ftype == 'v') || (ftype == 'z'))
      { fileptr += (cmn * vrslot);          /* calc fileptr (variable) */
        recnum1 += cmn;                     /* calc recnum             */
        recnum1x = 1;                       /* inhibit recnum++ in getrec*/
      }
    else
      { fileptr += (cmn * rsz2);          /* calc new fileptr (fixed) */
      }
    goto bgnrec;
  }

/*Apr01/07 - calc recnum for getrecv/getrecz (+= above & -= below) */

else if (opcmd[0] == '-')                   /* -# (back #recs)         */
  { if (cmn == 0) cmn = 1;                  /* default value to 1      */
    if ((ftype == 'v') || (ftype == 'z'))
      { fileptr -= (cmn * vrslot);          /* calc fileptr (variable) */
        recnum1 -= cmn;                     /* calc recnum             */
        recnum1x = 1;                       /* inhibit recnum++ in getrec*/
      }
    else
      { fileptr -= (cmn * rsz2);          /* calc new fileptr (fixed) */
      }
    goto bgnrec;
  }

/*eject*/
/*Jun12/07 - optn 'n' multi-recs/screen & command not browse, rec# byte# */
/* - restore fileptr of 1st on screen for print, write, update, etc      */
if (opsi['n'-'a'])
  { fileptr = fileptro1;     /* save fileptr of 1st on screen  */
  }

/*Jun12/07 - 'if else' chgd to 'if' for option 'n' test above (s/b OK) */
if (opcmd[0] == 'p')           /* print formatted recs to a file ? */
  { ss = print1(lszo);
    if (ss <= 0)
      { errmsg("    - reset fileptr to BOF(r0) & retry(pp) ?","","",0,0x00);
        goto reprompt;
      }
    else if (ss == 2)
      { goto getopcmd;
      }
    else
      { goto bgnrec;
      }
  }

else if (opcmd[0] == 'i')          /* iprint formatted recs to a file ? */
  { ss = iprint1(lszo);
    if (ss <= 0)
      { errmsg("    - reset fileptr to BOF(r0) & retry(ii) ?","","",0,0x00);
        goto reprompt;
      }
    else if (ss == 2)
      { goto getopcmd;
      }
    else
      { goto bgnrec;
      }
  }

else if (opcmd[0] == 'w')        /*  write unformatted recs to a file ? */
  { ss = write1();
    if (ss <= 0)
      { errmsg("    - reset fileptr to BOF(r0) & retry(ww) ?","","",0,0x00);
        goto reprompt;
      }
    else if (ss == 2)
      { goto getopcmd;
      }
    else
      { goto bgnrec;
      }
  }

else if (opcmd[0] == 'd')        /*  copy/drop unformatted recs to a file ? */
  { ss = drop1();
    if (ss == 2)
      { goto getopcmd;
      }
    goto reprompt;
  }

/*eject*/
else if (opcmd[0] == 'c')        /*  check sequence to EOF (or seq err)  */
  { ss = check1();
    if (ss <= 0)
      { goto reprompt;
      }
    else
      { goto bgnrec;
      }
  }

else if (opcmd[0] == 'u')                 /* update current record ?   */
  { opstat = update();
    recnum1x = 1;         /*Apr01/07 inhibit recnum++ for getrecv/getrecz */
    if (opstat <= 0)
        goto reprompt;
    else
        goto bgnrec;
  }
else if (opcmd[0] == 'v')                 /* scan/replace current record ? */
  { opstat = scanrep();
    recnum1x = 1;
    if (opstat <= 0)
        goto reprompt;
    else
        goto bgnrec;
  }
/* x = rollback last update, Jun12/07 disallow if option n multi recs/scrn*/
else if ((opcmd[0] == 'x') && (!(opsi['n'-'a'])))
  { opstat = back1();
    recnum1x = 1;
    if (opstat <= 0)
        goto reprompt;
    else
        goto bgnrec;
  }
/* X = rollback all updates, Jun12/07 disallow if option n multi recs/scrn*/
else if ((opcmd[0] == 'X') && (!(opsi['n'-'a'])))
  { opstat = backall();
    recnum1x = 1;
    if (opstat <= 0)
        goto reprompt;
    else
        goto bgnrec;
  }
else if (opcmd[0] == 'g')                 /* sequence number ? */
  { opstat = seqgen();
    if (opstat <= 0)
        goto reprompt;
    else
        goto bgnrec;
  }
else if (opcmd[0] == 'R')               /* change Rcsz (w/o quit reenter)  */
  { rszo = cmn;                         /* store new rcsz                  */
    chkrcsz(rszo);                      /* validate & calc new recs in file*/
    goto bgnrec;                        /* go redisplay current record     */
  }

/*eject*/
else if (opcmd[0] == 'e')               /* enumerate (count) records ?     */
  { ss = enum1();
    if (ss == 2)
      { goto getopcmd;
      }
    goto bgnrec;                        /* go redisplay current record     */
  }
else if (opcmd[0] == 'h')               /* display h0=auto,h1=chars,h2=hex */
  { opsi['h'-'a'] = copsi['h'-'a'];     /* replace cmd line option         */
    goto bgnrec;
  }
else if (opcmd[0] == 'z')               /* truncate filesize           */
  { opstat = trunc1();                  /* execute truncate            */
    goto bgnrec;
  }
else if (opcmd[0] == 't')                 /* translate ? (Aug2004) */
  { opstat = trans();
    if (opstat <= 0)
        goto reprompt;
    else
        goto bgnrec;
  }
else
  { showhelps(hsp,19);
    goto endscreen;
  }

/*eject*/
/*------------------------ end of program ------------------------*/
endprgm:

close(fd1);               /* May2001 fclose -> close for LFS      */

/* close output files - if open */
/*Feb05/09 - most output files now opened & closed on each command       */
/* - but write has option w1 on cmd line to combine all writes to 1 file */
closefileX(wfname,"W",&wfptr,&wfopn,0x00);   /* does nothing if not open */

printf("** quit request - program ended **\n");

return(0);
/* Feb2002 changed exit(0) to return(0) win lcc complained missing ret val */
}

/*eject*/
/*============================ subrtns =============================*/

/*-------------------------- chkrcsz ---------------------------------*/
/* call subfunctn to check rcsz & calc number of records in file      */
/* - called on program entry (check rcsz entered on cmd line r option)*/
/* - also called by command 'R' (change rcsz w/o quit & cmd line optn */
/* - no errmsgs if variable text of indexed file                      */

int chkrcsz(int recsiz0)
{
/* inhibit errmsgs if variable length records */
/* - option 't' (text) 'v' (IDXf3/IDXF8) 'z' (RDW) */
if ((ftype == 'v') || (ftype == 'z') || (ftype == 't'))
  { /* calculate items for variable length records */
    /*Nov11/07 - default recsize to 256 if < 1 or > RMAX */
    if (recsiz0 < 1 || recsiz0 > RMAX)
      { recsiz0 = RDFLT;
      }

    /* calc recs in file, etc - based on 256 for text or varlth files */
    /* - will recalc on every record based on current recsize         */
    calcrecs(recsiz0,rszo);          /* calc recs in file, etc */
  }
else
  { /* chkrcsz for fixed length records */
    /*Nov11/07 - remove errmsg if recsize invalid */
    /* - default recsize to option r defltd to 256 at pgm init for fixlth*/
    if (recsiz0 < 1 || recsiz0 > RMAX)
      { recsiz0 = rszo;
      }

    calcrecs(recsiz0,rszo);          /* calc recs in file, etc */

    /* issue warning msg if rcsz does not divide evenly into filesize   */
    if (remndr != 0)
      { printf("WARNING: rcsz does not divide evenly into filesize\n");
      }
  }

return(1);
}

/*eject*/
/*----------------------------- calcrecs -----------------------------*/
/*Nov11/07 - calculate records in file, based on current recsize      */
/* - vs prior method calc only at begin program based on optn 'r'     */
/* - if recsize invalid, default to arg2                              */

int calcrecs(int recsiz1, int rsxdflt)
{

/* if recsiz1 <= 0, default to 256 to prevent divide errors */
if (recsiz1 < 1)
  { recsiz1 = rsxdflt;
  }

/* calculate records in file & any remainder                      */
frecsc = (filesize / recsiz1);
frecsp = frecsc;                /* for printing printf %d         */
remndr = (filesize % recsiz1);
filextra = remndr;              /* same as remndr - not sure why ? */

/* calculate filesize evenly divisible by rcsz (but <)             */
/* - to prevent fileptr getting off record boundaries at EOF -recs */
filesiz0 = frecsc * recsiz1;

return(1);
}

/*eject*/
/*----------------------------- setlast1 -----------------------------*/
/*Nov12/07 - if on last record, set fileptr1 = begin last record      */
/* - last rec if current fileptr + recsize >= filesize                */
/* - if arg1 recsize invalid, default to arg2                         */

int setlast1(int recsiz1, int rsxdflt)
{
UVi64 fileptr1test;

/* if recsiz1 <= 0, default to 256 to prevent divide errors */
if (recsiz1 < 1)
  { recsiz1 = rsxdflt;
  }

/*Nov11/07 - if on last record, set fileptr1 = begin last record   */
/* test filetpr + rsz1 >= filesize ?                               */
/* - ifso set fileptr1 = current fileptr (begin last record)       */
/* - to re-display last rec until user resets rec# 0/1 ?           */
/* - at begin file, we init'd fileptr1 to filesize (after last rec)*/
/* - reset to filesize when getrec returns 0(EOF)                  */
/* - to cause last rec to be displayed until user enters rec# 0/1  */

/*Dec07/07 - for lcc 64bit+32bit compare wrong */
/* if ((fileptr + recsiz1) >= filesize)        */

fileptr1test = fileptr + recsiz1;
if (fileptr1test >= filesize)
  { fileptr1 = fileptr;     /* set last record ptr & EOF switch      */
    fileptr1s = fileptr1;   /* set fileptr1s to inhibit recnum1 inc  */
                            /* - until after user enters non-null cmd*/
    return 2;
  }

return(1);
}

/*eject*/
/*-------------------------- testlast1 -----------------------*/
/*Nov12/07 - test EOF (called at begin getrec all types)      */
/*         - this function called at begin getrec any type    */
/* if on last record, return 0 as EOF signal                  */
/* - set fileptr to last rec in file                          */
/* - to re-display last record until user resets rec# 0/1 ?   */

int testlast1(int fhsize)
{

/*Nov11/07 - save last valid recsize (possibly at EOF) */
/*         - to calc browse cmds for varlth files      */
/*         - rsz1 is stored by all calls to getrec     */
if (rsz1 > 0)
  { rsz1p = rsz1;
  }

/* if fileptr <= 0 (or varlth file hdr size, 128 IDXf3, 1024 IDXf8)  */
/* - init fileptr1 = filesize                                        */
/* - at begin file or because user set rec#0/1 after EOF             */
if (fileptr <= fhsize)     /* file hdr size (0 fixed, 128 for IDXf3) */
  {  fileptr = 0;          /* ensure fileptr not negative            */
     fileptr1 = filesize;  /* set fileptr1=EOF til last rec reached  */
     recnum1 = 1;          /* reset rec# for display/print           */
  }

/*Nov11/07 - if on last record, return 0 as EOF signal        */
/* set fileptr to last rec in file                            */
/* - to re-display last record until user resets rec# 0/1 ?   */
/*Note - at begin file, we init'd fileptr1 to filesize (after last rec) */
/*     - after getrec, we check for last record via:          */
/*     - by testing filetpr + rsz1 >= filesize ?              */
/*       ifso - set fileptr1 = fileptr to begin last record   */
if (fileptr >= fileptr1)
  { fileptr = fileptr1;         /*Nov11/07 - fileptr1 vs filesiz0  */
    return 0;
  }

return(1);
}

/*eject*/
/*---------------------------- getrec ------------------------------*/
/* get next record (return record length)                           */
/* - calls subfunction getrecf or getrecv                           */
/* - getrecf used for fixed record size                             */
/* - getrect used for text variable lth to CR/LF option 't'         */
/* - getrecv used for option 'v' IDXFORMAT3 variable indexed        */
/* - getrecx used for option 'v' IDXFORMAT8 fixed ISAM files > 2 gig*/
/*Feb05/09 - combine getrev & getrecx as getrecv using getrecx code */

int getrec(short bits)
{
int rs1,ss;

rii = 0;                  /* init rec index for getlinr subfunction */

fileptrp = fileptr;       /* save fileptr before get in case EOF*/

/* clear record area - for text records variable length             */
/* - so search commands wont find data from longer records          */
memset(recb,'\0',rszo);

/* test file type option & call appropriate subfunction */
if (ftype == 't')
  { rs1 = getrect(bits);
  }
else if (ftype == 'v')
  { rs1 = getrecv(bits);
  }
else if (ftype == 'z')
  { rs1 = getrdw(bits);
  }
else
  { rs1 = getrecf(bits);
  }

/* if EOF - store fileptr of Last Record from fileptrp (previous fileptr) */
if (rs1 <= 0)
  { fileptrLR = fileptrp;
  }

return (rs1);
}

/*eject*/
/*---------------------------- getrecf ------------------------------*/
/* get next record (return record length)                            */
/* - record size from option 'r' on cmd line (default 256)           */
/* - getrecf used for fixed record size                              */

int getrecf(short bits)
{
int cc,ii,ss;
int rs1,rs2;
Uchar uc;

/*Nov12/07 - test fileptr at begin last record (ifso return 0)    */
/*         - call testlast1() common to all filetypes             */
/* EOF test after getrec() call will reset fileptr1 to filesize   */
/* - causes null display, & last rec display, til user resets rec#*/
ss = testlast1(0);
if (ss == 0)
  { return 0;
  }

fileseek("getrecf");               /* seek to current fileptr position */
rs1 = 0;                           /* reset read size                  */

/*May2001 - fopen,fread,fseek change to open/open64,read,lseek/lseek64*/
rs1 = read(fd1,recb,rszo);      /* get fixed size records           */
recnum = (fileptr / rszo);      /* recalc rec# to ensure within file*/
recnum1 = (recnum + 1);         /* make 1 relative for display/print*/

/*  remainder = (filesize % rszo); */
/*Mar01/03 - remndr garbage on print ? should not need to recalc if fixed*/
if (rs1 < 0)
    rs1 = 0;

/* set switch 1 if any bytes < 0x20 or > 0x7E, except \n \r \t \f      */
/* to determine print mode, also depends on option 'h'                 */
/* h0 = auto determine, h1 = force chars only, h2 = force hex display  */
/* allow hex chars in last 2 bytes of record for CR/LF at end text recs*/
/* Feb20/03 - scan only to 3rd last byte (dont check for x'0D' or x'0A'*/
xchar = 0;
for (ii=0; ii < rs1-2; ii++)
  { uc = recb[ii];
    if ( uc >= ' ' && uc <= '~')
       continue;
    xchar = 1;
    break;
  }

/*Nov11/07 - recalc records in file based on current record size */
/*         - redundant for fixed length, required for varlth     */
calcrecs(rs1,rszo);

/*Nov12/07 - if fileptr on last rec, set fileptr1 = last rec     */
/* - last rec if current fileptr + recsize >= filesize           */
/* - if arg1 recsize invalid, default to arg2                    */
setlast1(rs1,rszo);

return(rs1);
}
/*eject*/
/*---------------------------- getrect ------------------------------*/
/* get next record (return record length)                            */
/* - record size from option 'r' on cmd line (default 256)           */
/* - getrect used for text variable lth to CR/LF (option 't')        */

int getrect(short bits)
{
int cc,ii,ss;
int rs1,rs2;
Uchar uc;

/*Nov12/07 - test fileptr at begin last record (ifso return 0)    */
/*         - call testlast1() common to all filetypes             */
/* EOF test after getrec() call will reset fileptr1 to filesize   */
/* - causes null display, & last rec display, til user resets rec#*/
ss = testlast1(0);
if (ss == 0)
  { return 0;
  }

fileseek("getrect");               /* seek to current fileptr position */
rs1 = 0;                           /* reset read size                  */
bufn = 0;                      /* forces fill buf on 1st getcbuf   */

/* text files - read last rszo (optn r) into buffer                */
/* - use getcbuf to extract til LF (getcbuf refills if required)   */

/*Nov11/07 - should I change rszo to 1024 or ???                   */
while (rs1 <= rszo)
  { cc = getcbuf();            /* get next char via buffered read  */
    if (cc == EOF)
       return(-1);
    recb[rs1++] = (char) cc;
    /*Mar2003 - add option 't1' for text files terminated by CR x'0D'*/
    if ((opsi['t'-'a'] & 0x01) && (!(opsi['t'-'a'] & 0x02)))
      { if (cc == '\r')        /* text rec terminated by CR x'0D' ? */
            break;
      }
    else
      { if (cc == '\n')        /* text rec terminated by LF x'0A' ? */
            break;
      }
  }

/*eject*/
/*Nov11/07 - code to recalc recs in file removed from here in getrect */
/*         - calcrecs() now called on every record for all file types */
/*         - at end of getrec() (after getrecf, getrect, etc)         */

/* increment record# - for varlth files                 */
/* (recalculated on each record for fixed lth files)    */
/*Nov12/07 - incrmnt only if fileptr < fileptr1s        */
/* - see fileptr1/fileptr1s logic in getcalc1 & getcalc2*/
if (fileptr < fileptr1s)
  { recnum1++;   /* increment recnum1 for display/print */
  }

if (rs1 < 0)
  { rs1 = 0;
  }

/* set switch 1 if any bytes < 0x20 or > 0x7E, except \n \r \t \f      */
/* to determine print mode, also depends on option 'h'                 */
/* h0 = auto determine, h1 = force chars only, h2 = force hex display  */
/* allow hex chars in last 2 bytes of record for CR/LF at end text recs*/
/* Feb20/03 - scan only to 3rd last byte (dont check for x'0D' or x'0A'*/
xchar = 0;
for (ii=0; ii < rs1-2; ii++)
  { uc = recb[ii];
    if ( uc >= ' ' && uc <= '~')
       continue;
    xchar = 1;
    break;
  }

/*Nov11/07 - recalc records in file based on current record size */
/*         - if recsize invalid (at EOF), default to 256         */
calcrecs(rs1,RDFLT);

/*Nov12/07 - if fileptr on last rec, set fileptr1 = last rec     */
/* - last rec if current fileptr + recsize >= filesize           */
/* - if arg1 recsize invalid, default to arg2                    */
setlast1(rs1,RDFLT);

return(rs1);
}

/*eject*/
/*----------------------------- getcbuf --------------------------------*/
/* get 1 byte at a time from a text file (includes LineFeeds)           */
/* - buffered by reading rszo (option r) bytes when buffer becomes empty*/
/* - returns 1 character (unsigned) or EOF (-1) at end of file          */

int getcbuf(void)
{
/* static char bufa[16384];        ** buffer read area            */
/* static char *bufp = bufa;       ** buffer pointer to next char */
/* static int bufn = 0;            ** buffer bytes remaining      */
/* May2001 - bufa,bufp,bufn  moved to global storage              */

if (bufn == 0)
  { bufn = read(fd1,bufa,rszo);
    bufp = bufa;
  }

bufn--;                         /* decrement bytes remaining */

if (bufn >= 0)
  { return (unsigned char) *bufp++;
  }
else
  { return EOF;
  }
}

/*eject*/
/*----------------------------- getrecv -------------------------------*/
/* get next IDXFORMAT8 length record (Micro Focus COBOL)               */
/*Feb05/09 - combine getrev & getrecx as getrecv using getrecx code    */
/* - called from getrec when option 'v' specified                      */
/* - fileptr should point to the 2 or 4 byte hdr of next record        */
/* - if fileptr < file header size, set fileptr = 0                    */
/* - read record header 2 bytes (or 4 bytes if max rcsz >= 4095)       */
/* - calc record size & slot size (hdr+data+fill to 4 byte boundary)   */
/* - reseek to begin rec hdr (to include hdr in display)               */
/* - test rec display optn v1,v2,v4,v8: filehdr, deleted, data, all    */
/*   (default v5 = filehdr + data recs)                                */

int getrecv(short bits)
{
int rs1,ss;             /* record size to be returned              */
char vrhdr[8];          /* work area to read 2 or 4 byte header    */
int ii;
char cc;

/* return point to read next rec if bypassing deleted recs or system recs*/
reread:
/*Nov12/07 - testlast1() has to follow reread, else hangup at EOF */
/* - due to 4 byte null record between data records & at EOF also */

/*Nov12/07 - test fileptr at begin last record (ifso return 0)    */
/*         - call testlast1() common to all filetypes             */
/* EOF test after getrec() call will reset fileptr1 to filesize   */
/* - causes null display, & last rec display, til user resets rec#*/
ss = testlast1(0);   /*Nov12/07 - try 0 vs vfhs, stuck on file hdr ? */
if (ss == 0)
  { return 0;
  }

/* read record header (2 or 4 bytes) to get record size               */
/* May2001: fseek changed to call fileseek (largefiles lseek/lseek64) */
/*          fread changed to read (since no fseek64)                  */
fileseek("getrecv");
rs1 = read(fd1,vrhdr,vrhs);        /* get record headr 2 or 4 bytes   */

switchx(vrhdr,vr4k);               /* calc & store rcsz,slotsz,& rectype*/
/* switchx stores vrsize & vrslot & applies max & min rules           */
/* switchx return value used only for min & max at file open          */

/* reseek to begin rec hdr & read record to be displayed slot size    */
/* May2001: fseek changed to call fileseek (largefiles lseek/lseek64) */
fileseek("getrecv");
rs1 = read(fd1,recb,vrslot);      /* get rechdr + data + any fill     */

/*eject*/
/* verify record type x'10' thru x'80'                              */
/* - if invalid we must have gone 'off the rails'                   */
/* - 'off the rails' displays partrecs or rereads til EOF reached   */
/* - try scanning to next data type record header x'40' 1st nibble  */
if ((vrtyp < 0x10) || (vrtyp > 0x80))
  { for (ii = vrhs; ii < vrslot; ii++)
      { cc = recb[ii] & 0xF0;     /* isolate 1st nibble             */
        if (cc == 0x40)           /* if possible data rec           */
          { fileptr += ii;        /* set fileptr to possible data rec*/
            goto reread;
          }
      }
  }

/* test rec display optn v1,v2,v4,v8: filehdr, deleted, data, all    */
if ((vrtyp == 0x30) && (opsi['v'-'a'] & 0x01) && (fileptr < vfhs))
  { goto process;
  }
else if ((vrtyp == 0x20) && (opsi['v'-'a'] & 0x02))
  { goto process;
  }
else if ((vrtyp == 0x40) && (opsi['v'-'a'] & 0x04))
  { goto process;
  }
/* sysrec (not filehdr) or other record - bypass unless option v8 (all) */
else if (opsi['v'-'a'] & 0x08)
  { goto process;
  }
else
  { /* current record not desired - bypass, return to get next         */
    fileptr += rs1;
    goto reread;
  }

/* record selected for process/display by calling mainline         */
process:
xchar = 1;      /* set switch to display record in hexadecimal   */

/* increment rec# - if we have read data (vs EOF return 0)          */
/*  & not inhibited by recnum1x=1 (user entered rec# vs null/browse)*/
/*Feb02/09 - & only if fileptr > fileptrLG (from Last Get)          */
if ((rs1 > 0) && (recnum1x == 0) && (fileptr < fileptr1s) 
                                 && (fileptr > fileptrLG))
  { recnum1++;               /* increment recnum1 for display/print */
  }

/*Nov11/07 - recalc records in file based on current record size */
/*         - if recsize invalid (at EOF), default to 256         */
calcrecs(rs1,RDFLT);

/*Nov12/07 - if fileptr on last rec, set fileptr1 = last rec     */
/* - last rec if current fileptr + recsize >= filesize           */
/* - if arg1 recsize invalid, default to arg2                    */
setlast1(rs1,RDFLT);

return (rs1);
}

/*eject*/
/*----------------------------- getrdw --------------------------------*/
/* get next RDW variable length record                                 */
/* - called from getrec when option 'z' specified                      */
/* - fileptr should point to the 2/4 byte hdr of next record           */
/*Jan24/09 - validate RDW prefix, ensure recsize < 16384             */
/* if recsize >= 16384, assume off the rails (due to using byte# ?)  */
/* - search for next byte with 2 topbits clear (possible RDW prefix) */
/* - if RDWz4 also require 3rd & 4tf byte = x'0000'                  */
/* - original getrdw chgd to getrdw1                                 */

int getrdw(short bits)
{
int rs1,ii;

rs1 = getrdw1(bits);         /* get RDW record via fileptr */
if (rs1 <= 0)                /* test EOF */
  { return(0);
  }

/*Jan26/09 - verify RDW prefix (z2 or z4) if on 1st record          */
/*         - if not on 1st rec, we scan to next possible RDW prefix */
if (fileptr == 0)
  { if (opsi['z'-'a'] & 0x02)
      { if (rs1 >= 16384)
        { printRV("\nInvalid RDWz2 (recsize > 16K), probably NOT RDWz2 file\n");
          exit(92);
        }
      }
    else
      { if ((rs1 >= 16384) || (recb[2]) || (recb[3]))
        { printRV("\nInvalid RDWz4 (recsize > 16K), probably NOT RDWz4 file\n");
          exit(94);
        }
      }
  }

/*eject*/
/*Jan24/09 - validate RDW prefix, ensure recsize < 16384             */
/* if recsize >= 16384, assume off the rails (due to using byte# ?)  */
/* - search for next byte with 2 topbits clear (possible RDW prefix) */
/* - if RDWz4 also require 3rd & 4tf byte = x'0000'                  */

if ((rs1 < 8) || (rs1 >= 16384))
  { while(1)
      { if (opsi['z'-'a'] & 0x02)
          { for (ii=0; ii < rs1; ii++)
              { if (recb[ii] & 0xC0)
                  { continue;
                  }
                else
                  { break;
                  }
              }
          }
        else
          { for (ii=0; ii < rs1; ii++)
              { if (recb[ii] & 0xC0)
                  { continue;
                  }
                if ((recb[ii+2] == '\0') && (recb[ii+3] == '\0'))
                  { break;
                  }
              }
          }
 
        /* found possible RDW prefix - up fileptr by index & retry get */
        fileptr += ii;
        rs1 = getrdw1(bits);        /* retry get RDW */
        if ((rs1 < 8) || (rs1 >= 16384))
          { fileptr++;
            continue;
          }
        else
          { break;
          } 
      }
  }
return(rs1);
}
/*eject*/
/*----------------------------- getrdw1 --------------------------------*/
/* get next variable length record (Micro Focus COBOL)                 */
/* - called from getrec when option 'z' specified                      */
/* - fileptr should point to the 4 byte hdr of next record             */
/* - read record header 4 bytes & store recsize/slotsize               */
/* - recsize in hdr includes 4 bytes + data size                       */
/* - reseek to begin rec hdr (to include hdr in display)               */

int getrdw1(short bits)
{
int rs1,ss;              /* record size to be returned            */

/*Nov12/07 - test fileptr at begin last record (ifso return 0)    */
/*         - call testlast1() common to all filetypes             */
/* EOF test after getrec() call will reset fileptr1 to filesize   */
/* - causes null display, & last rec display, til user resets rec#*/
ss = testlast1(4);
if (ss == 0)
  { return 0;
  }

/* fileseek & read record header (4 bytes) to get record size           */
/*Nov05/07 - variable length RDW option z verify z0,z2,z4,or z8         */
/* - now always assume recsize in 1st 2 bytes                           */
/* - option z now = bytes added to size in hdr to reach next record     */
/* - was reading 4 & assuming recsize total allowed 4                   */
fileseek("getrdw");
rs1 = read(fd1,rdwhdr,2);          /* get record size (1st 2 bytes)   */

memset(u1.cc,'\0',4);              /* clear work area union           */

/*Sep09/08 - provide option z1 for little-end RDW                */
/* z2=2bytehdr, z4=4bytehdr, z8=8bytehdr, z0 deflt z4,           */
if (opsi['z'-'a'] & 0x01)
  { memcpy(u1.cc,rdwhdr,2);      /* store little-end recsize on left*/
    /* switch little-end to Big-end if AIX,HP,SUN,etc (vs Intel)    */
#if (BEM)
    u2.cc[0] = u1.cc[3];
    u2.cc[1] = u1.cc[2];
    u2.cc[2] = u1.cc[1];
    u2.cc[3] = u1.cc[0];
#else
    memcpy(u2.cc,u1.cc,4);
#endif
  }
else
  { memcpy(u1.cc+2,rdwhdr,2);    /* store little-end recsize on left*/
/* switch big-end to little-end if INTEL or SCO (not SUN, HP, etc)  */
#if (LEM)
    u2.cc[0] = u1.cc[3];
    u2.cc[1] = u1.cc[2];
    u2.cc[2] = u1.cc[1];
    u2.cc[3] = u1.cc[0];
#else
    memcpy(u2.cc,u1.cc,4);
#endif
  }

/*eject*/
/* store record-size & slot-size */
vrsize = u2.ii;              /* store record size                 */
vrslot = (vrsize + rdwhx);   /* calc slot size hdrsize + 4 or 8   */
                             /* rdwhx = 2 for 'z2', 0 for z4 & z8 */
/*vrslot = (vrsize + rdwhs); <-- old code prior to Sep09/08       */

/* reseek to begin rec hdr & read record to be displayed slot size */
fileseek("getrdw");
rs1 = read(fd1,recb,vrslot);      /* get rechdr + data + any fill  */

/* increment rec# - if we have read data (vs EOF return 0)          */
/*  & not inhibited by recnum1x=1 (user entered rec# vs null/browse)*/
/*Feb02/09 - & only if fileptr > fileptrLG (from Last Get)          */
if ((rs1 > 0) && (recnum1x == 0) && (fileptr < fileptr1s) 
                                 && (fileptr > fileptrLG))
  { recnum1++;               /* increment recnum1 for display/print */
  }

xchar = 1;        /* set switch to display record in hexadecimal */

/*Nov11/07 - recalc records in file based on current record size */
/*         - if recsize invalid (at EOF), default to 256         */
calcrecs(rs1,RDFLT);

/*Nov12/07 - if fileptr on last rec, set fileptr1 = last rec     */
/* - last rec if current fileptr + recsize >= filesize           */
/* - if arg1 recsize invalid, default to arg2                    */
setlast1(rs1,RDFLT);

return (rs1);
}

/*eject*/
/*----------------------------- getlinr ------------------------------*/
/* get data for next display line (from record buffer)                */
/* - 64 bytes max, or 'lszo' if option 'l' spcfd                      */
/* - or less at EOR or EOF                                            */
/* - return number of bytes copied or 0 at EOR/EOF                    */

int getlinr(int lsize)
{
lii = 0;                               /* reset line index            */

rii1 = rii;                            /* save rec index b4 new segment*/

if (rii >= rsz1)                       /* EOR/EOF already reached ?   */
    return(0);

while ((lii < lsize) && (rii < rsz1))
  { linr[lii++] = recb[rii++];
  }

return(lii);
}

/*eject*/
/*----------------------------filterchar-------------------------------*/
/* subrtn to convert non-display characters to periods                 */
/*Apr04/06 - changed unprintable char rep from '.' period to '^' carat */
/*Apr04/06 - dont like it change it back                               */
void filterchar(unsigned char *ch, int lsize)
{
int i;
for (i=0; i < lsize; i++)
  {  if ((ch[i] < ' ') || ((ch[i] > 0x7e) && ((opsi['c'-'a'] & 0x01) == 0)))
         ch[i] = '.';
  }
}
/*Feb2003 - LMAX changed to lsize when LMAX increased to 1024      */
/*        - lsize now passed as argument (for screen & print diff) */
/*        - in filterchar, hexzone,& hexdigit                      */

/*--------------------------- hexzone ------------------------------*/
/* convert zones of char string to hex display                      */
void hexzone(char *hexzo, char *hexch, int lsize)
{
intU z;
int i;
for (i=0; i < lsize; i++)
  { z = (intU)(hexch[i] >> 4);
    z = z & 0x0f;
    if (z < 10)
        hexzo[i] = (char)(z + 0x30);
    else
        hexzo[i] = (char)(z + 0x37);
  }
hexzo[i] = '\0';
}

/*-------------------------- hexdigit ------------------------------*/
/* convert ddigits of char string for hex display                   */
void hexdigit(char *hd,char *hc, int lsize)
{
intU d;
int i;
for (i=0; i < lsize; i++)
  { d = (intU)(hc[i] & 0x0f);
    if (d < 10)
        hd[i] = (char)(d + 0x30);
    else
        hd[i] = (char)(d + 0x37);
  }
hd[i] = '\0';
}

/*eject*/
/*------------------------------- boldpat ----------------------------------*/
/* highlight last search patterns if in current line segment to be displayed*/
/*May2003 - enhanced to allow 3 search patterns to be highlighted           */
/*        - by using table of dsplcmnts to start & end highlighting         */

short boldpat(Uchar *linp, Uchar *linc)
{
int lincl;          /* length of input data                         */
int lincde;         /* dsplcmnt in record to end of current segment */
int p1d,p1l,p1de;   /* dsp,lth,end of 1st search pattern            */
int p2d,p2l,p2de;   /* dsp,lth,end of 2nd search pattern            */
int p3d,p3l,p3de;   /* dsp,lth,end of 3rd search pattern            */
int match;          /* match indicator considering 3 paterns AND/ORs*/
int ii,jj,tt;       /* indices for I/O data & table of start/stop dsps*/
int bt[8];          /* table of start/end dsplcmnts to be bolded    */
                    /* - will be sorted by start dsplcmnt           */
                    /* - unused entries disabled by dsp=262144      */

lincl = strlen((char*)linc);            /* calc length of input record */

/* copy input to output, presuming no search/no match in current segment*/
strcpy((char*)linp,(char*)linc);

/* bypass if inhibited via option b1 or if no search pattern stored    */
if ((opsi['b'-'a'] & 0x01) || (sargs.a2l == 0))
  { return(0);
  }

/* re-search to ensure current record contains data of last search    */
p1d = search2(recb,&sargs);
p1l = sargs.a2l;

if (sargs.a4l)               /* if 2nd search pattern specified ?     */
  { p2d = search3(recb,&sargs);
    p2l = sargs.a4l;
  }
else
  { p2d = 262144;             /* force match for 2nd pattern omitted    */
    p2l = 0;                 /* disable pattern 2                       */
  }                          /* 262144 inhibits BOLD when no op4 pattern*/

if (sargs.a6l)               /* if 3rd search pattern specified ?      */
  { p3d = search4(recb,&sargs);
    p3l = sargs.a6l;
  }
else
  { p3d = 262144;             /* force match for 2nd pattern omitted    */
    p3l = 0;                 /* disable pattern 3                       */
  }                          /* 262144 inhibits BOLD when no op4 pattern*/

/*eject*/
/* bypass if search conditions not met                               */
/* test results of 3 searches depending on conditions vs AND/ORs possible*/
/*May2003 - op4/op5 added to allow 3rd condition, also allowing AND/OR   */
match = 0;          /* presume no match */
if ((sargs.a3cc[0] != '|') && (sargs.a5cc[0] != '|'))
  { if ((p1d >= 0) && (p2d >= 0) && (p3d >= 0))
        match = 1;
  }
else if ((sargs.a3cc[0] != '|') && (sargs.a5cc[0] == '|'))
  { if ((p1d >= 0) && ((p2d >= 0) || (p3d >= 0)))
        match = 1;
  }
else if ((sargs.a3cc[0] == '|') && (sargs.a5cc[0] != '|'))
  { if ((p1d >= 0) || ((p2d >= 0) && (p3d >= 0)))
        match = 1;
  }
else if ((sargs.a3cc[0] == '|') && (sargs.a5cc[0] == '|'))
  { if ((p1d >= 0) || (p2d >= 0) || (p3d >= 0))
        match = 1;
  }
if (!match)
  { return(0);
  }

/* disable search patterns not within current segment                */
/* - by setting dsplcmnts high & length zero                         */
if ((p1d < rii1) || (p1d >= rii))
  { p1d = 262144;
    p1l = 0;
  }
if ((p2d < rii1) || (p2d >= rii))
  { p2d = 262144;
    p2l = 0;
  }
if ((p3d < rii1) || (p3d >= rii))
  { p3d = 262144;
    p3l = 0;
  }

/* if patterns spans segments truncate length to within current segment*/
lincde = rii1 + lincl;        /* calc dsplcmnt to end current segment */
p1de = p1d + p1l;             /* calc dsplcmnt to end pattern1        */
p2de = p2d + p2l;             /* calc dsplcmnt to end pattern2        */
p3de = p3d + p3l;             /* calc dsplcmnt to end pattern3        */

if ((p1de < 262144) && (p1de > lincde))
  { p1l = (lincde -p1d);
  }
if ((p2de < 262144) && (p2de > lincde))
  { p2l = (lincde -p2d);
  }
if ((p3de < 262144) && (p3de > lincde))
  { p3l = (lincde -p3d);
  }

/*eject*/
/* recalc end dsps in case of length change for patterns spanning segments*/
p1de = p1d + p1l;             /* recalc dsplcmnt to end pattern1          */
p2de = p2d + p2l;             /* recalc dsplcmnt to end pattern2          */
p3de = p3d + p3l;             /* recalc dsplcmnt to end pattern3          */

/* bypass if no search patterns within current line segment   */
if ((p1l == 0) && (p2l == 0) && (p3l == 0))
  { return(0);
  }

/* load table of start/end dsplcmnts to be bolded               */
/* - relative to beginning of current segment                   */
/* - so we can sort by start dsplcmnt                           */
bt[0] = p1d-rii1; bt[1] = p1de-rii1;
bt[2] = p2d-rii1; bt[3] = p2de-rii1;
bt[4] = p3d-rii1; bt[5] = p3de-rii1;
bt[6] = 262144; bt[7] = 262144; /* mark end table with high entries   */

sortint2(&bt[0],3);             /* sort table start/end pairs by start*/

/* disable any overlapping entries                                    */
/* - if next start dsplcmnt < current end dsplcmnt, disable next pair */
for (tt=0; tt < 6; tt+=2)
  { if (bt[tt+2] < bt[tt+1])
      { bt[tt+2] = 262144;   /* disable start via hi index never reached */
        bt[tt+3] = -1;       /* disable end via -1 to protect folwng pair*/
      }
  }

sortint2(&bt[0],3);          /* re-sort table to move any disableds high */

/*eject*/
/* init I/O & table indices to copy data inserting bold stop/start patterns*/
ii = 0;                      /* index for input data segment           */
jj = 0;                      /* index for output area                  */
tt = 0;                      /* index for table of start/end dsplcmnts */

/* use while loop to copy byte by byte                                 */
/* - testing current input dsplcmnt for match in table                 */
/*   of start/stop dsplcmnts to be bolded                              */
/* when start/stop dsplcmnts match,  insert start/stop bold patterns   */
while (ii < lincl)
  { if (ii == bt[tt])               /* current dsp = start bold dsp ?  */
      { memcpy(linp+jj,smso,smsol); /* insert start bold escape seq    */
        jj += smsol;                /* up output index by lth startbold*/
      }
    linp[jj++] = linc[ii++];        /* copy current byte & up indices  */
    if (ii == bt[tt+1])             /* current dsp = stop bold dsp ?   */
      { memcpy(linp+jj,rmso,rmsol); /* insert stop  bold escape seq    */
        jj += rmsol;                /* up output index by lth stopbold */
        tt += 2;                    /* up bold table to next pair      */
      }
  }

linp[jj] = '\0';                    /* null terminate            */

return(1);
}

/*eject*/
/*---------------------------------- sortint2 ---------------------------*/
/* sortint2 - buble sort for tables of integer pairs                     */
/*          - sorts on 1st integer & moves pairs as indicated            */

short sortint2(int *tbl, int ents)
{
int ii;              /* index to table of integer pairs    */
int s1,s2;           /* save areas for current pair switch */
int flips;           /* flip counter for current pass      */

while(1)
  { flips = 0;                      /* reset flip ctr for current pass */

    for (ii=0; ii < ents; ii+=2)
      { if (tbl[ii+2] < tbl[ii])
          { s1 = tbl[ii];            /* save current pair              */
            s2 = tbl[ii+1];
            tbl[ii] = tbl[ii+2];     /* move next pair to current pair */
            tbl[ii+1] = tbl[ii+3];
            tbl[ii+2] = s1;          /* move saved pair to next pair   */
            tbl[ii+3] = s2;
            flips++;                 /* count flips this pass          */
          }
      }
    /* end current pass - if no flips, we are done                      */
    if (flips == 0)
      { break;
      }
  }
return(1);
}

/*eject*/
/*------------------------------- parse --------------------------------*/
/* parse the command entered (in reply to prompt)                       */
/* - return 1 OK, 0 if errors discovered                                */
/*                                                                      */
/* type1 - 1 char command + optional numeric value                      */
/* r100                     - goto & display record #100                */
/*                                                                      */
/* type2 - cmd + op1,op2                                                */
/* s 0(20),='ABC'             - search for ABC within 1st 20 bytes      */
/* s 0(3),!'ABC'              - search for NOT ABC in cols 1-3          */
/*                              <!> search lth must match if lth > 1    */
/* p 0(3),>'800'              - print records with > 800 in 1st 3 bytes */
/* u 0(3),='ABC'              - update cols 1-3 of current rec w ABC    */
/*                                                                      */
/* s ='ABC'                   - search for ABC anywhere in entire record*/
/*                            - op1 dflts to 0(rcsz) if omitted         */
/*                                                                      */
/*9902 - provide 4 operands for commands (search, write, etc)           */
/*     - cmd + op1,op2,op3,op4   (allow 2 conditions in 2 diff fields)  */
/*                                                                      */
/* s 0(40),='ABC',40(40),='XYZ' - search for ABC in 1-40 & XYZ in 41-80 */
/*                                                                      */
/* s 0(1),='1',40(1),='A'   - search for 1 in byte 0 & A in byte 40     */
/* s 0(1),'1',&40(1),'A'    - same ('=' and '&' are the defaults)       */
/* s 0(1),'1',|40(1),'A'    - search for 1 in byte 0 OR A in byte 40    */
/*                                                                      */
/* s 0(80),<x'20',0(80),>x'7E' - search for non printable chars in 1-80 */
/*                                                                      */
/* s 0(1),'1',|,'2'         - op3 omitted defaults to op1 search area   */
/*                          - must specify OR (|) for = conditions      */
/* s 0(1),>'1',,<'9'        - search for 2-8 in byte 0 (range search)   */

int parse(void)
{
int sdi;                   /* dsplcmnt for sdscopy function           */
int ss;                    /* status of various cmnds                 */
char *pp;                  /* ptr returned by atol1                   */
int ee;                    /* end op1 (dsp + lth)                     */
int ii,jj;                 /* misc index                              */
char a3ccs[4];             /* save | or & from op4 for op3 and/or     */
char a5ccs[4];             /* save | or & from op6 for op5 and/or     */

/* clear argument storage from prior commands                           */
memset(cms,'\0',80);   memset(args,'\0',80);
memset(arg1a,'\0',80); memset(arg1b,'\0',80); memset(arg1c,'\0',80);
memset(arg2a,'\0',80); memset(arg2b,'\0',80); memset(arg2c,'\0',80);
memset(arg3a,'\0',80); memset(arg3b,'\0',80); memset(arg3c,'\0',80);
memset(arg4a,'\0',80); memset(arg4b,'\0',80); memset(arg4c,'\0',80);
memset(arg5a,'\0',80); memset(arg5b,'\0',80); memset(arg5c,'\0',80);
memset(arg6a,'\0',80); memset(arg6b,'\0',80); memset(arg6c,'\0',80);
memset(a3ccs,'\0',4);  memset(a5ccs,'\0',4);
arg1d = 0; arg1l = 0; arg2l = 0;
arg3d = 0; arg3l = 0; arg4l = 0;
arg5d = 0; arg5l = 0; arg6l = 0;
op1dflt = 0;

/*Jan21/09 - if no cmd entered - return now */
if (opcmd[0] <= ' ')
  { return(1);
  }
/*eject*/
/* if opcmd 1st byte numeric - default to the 'r' (rec#) command       */
if (isdigit(opcmd[0]))
  { strcpy(opcmd2,opcmd);               /* copy cmd to alt w/s         */
    opcmd[0] = 'r';                     /* insert 'r' command          */
    strcpy(opcmd+1,opcmd2);             /* append original cmd string  */
  }

/* separate command & arguments assuming: cmd op1,op2,op3,op4          */
/* s 0(40),='ABC',40(40),='XYZ'    - search ABC in 1-40 & XYZ in 41-80 */
sdscopy(cms,opcmd,80,&sdi,"~~ ~",0x1032);
sdscopy(args,opcmd,80,&sdi,"~~~~",0x2032);
strcpy(cops,cms);                          /* cmd+options               */

/*Nov09/07 - return now if cmd 'q' (quit) */
if (cops[0] == 'q')
  { return(9);        /* return > 0 to avoid reprompt */
  }

/* errchk - space after cmnd & prior to arguments                       */
/*        - should be no ( ) , ' " in cmnd string                       */
ss = searchcm(cms,"(),\'\"",20,5,0x03);
if (ss >= 0)
  { errmsg("command format - no space between cmd & args","","",0,0x10);
    return(0);
  }

/* process cmd options, 1st cnvrt non lower cmds - to prevent sortops err*/
if (cops[0] == '+') cops[0] = 'f';
if (cops[0] == '-') cops[0] = 'n';
if (cops[0] == '?') cops[0] = 'y';
if (cops[0] == ':') cops[0] = 'q';
if (cops[0] == 'X') cops[0] = 'o';
if (cops[0] == 'R') cops[0] = 'z';

/* sort/store options, but not if 2nd byte '=' (transfer last search cmd)*/
if (cops[1] != '=')
  { ss = sortops(cops,copsc,copsi,0x10);
    if (ss < 0)
       return(0);
  }

/* store any numeric value appended to the command                    */
/* - using option for cmd letter to avoid any option numeric values   */
cmn = copsi[cops[0] - 'a'];

/* separate args based on commas ie:  0(20),'ABC',40(20),'XYZ'         */
sdscopy(arg1a,args,80,&sdi,"~~,~",0x1012);
sdscopy(arg2a,args,80,&sdi,"~~,~",0x2012);
sdscopy(arg3a,args,80,&sdi,"~~,~",0x2012);
sdscopy(arg4a,args,80,&sdi,"~~,~",0x2012);
sdscopy(arg5a,args,80,&sdi,"~~,~",0x2012);
sdscopy(arg6a,args,80,&sdi,"~~~~",0x2002);

/*eject*/
/* allow op1 dsp(lth) to be omitted - default to 0(rcsz) entire record */
/* - if quote found in 1st 3 bytes of op1, assume op1 omitted          */
if (memchr(arg1a,'\'',3))
  { strcpy(arg6a,arg5a);               /* shift op5 to op6, etc        */
    strcpy(arg5a,arg4a);
    strcpy(arg4a,arg3a);
    strcpy(arg3a,arg2a);
    strcpy(arg2a,arg1a);
    sprintf(arg1a,"0(%d)",rsz1);       /* default op1 to 0(rcsz)       */
    op1dflt = 1;                       /* store indicator for scan/rep */
  }
/* allow op3 dsp(lth) to be omitted - default to op1 dsp(lth)          */
/* - if quote found in 1st 3 bytes of op3, assume op3 omitted          */
if (memchr(arg3a,'\'',3))
  { strcpy(arg6a,arg5a);               /* shift op5 to op6, etc        */
    strcpy(arg5a,arg4a);
    strcpy(arg4a,arg3a);
    strcpy(arg3a,arg1a);               /* default op3 to op1 dsp(lth)  */
  }
/* allow op5 dsp(lth) to be omitted - default to op3 dsp(lth)          */
/* - if quote found in 1st 3 bytes of op5, assume op5 omitted          */
if (memchr(arg5a,'\'',3))
  { strcpy(arg6a,arg5a);               /* shift op5 to op6             */
    strcpy(arg5a,arg3a);               /* default op5 to op3 dsp(lth)  */
  }

/* error check - arg1 (if coded), must include '('                   */
if ((arg1a[0]) && (strchr(arg1a,'(') == 0))
  { errmsg("command format - no '(' in arg1","","",0,0x10);
    return(0);
  }

/* default op3 same as op1 - if op3 omitted or only '|'              */
/* s 0(40),='ABC',|0(40),='XYZ'  - search ABC in 1-40 OR XYZ in 1-40 */
/* s 0(40),='ABC',|,='XYZ'       - same by default                   */
if (arg4a[0])
  { if (arg3a[0] < '0')
        strcpy(arg3a,arg1a);
    else if ((arg3a[0] == '|') && (arg3a[1] < '0'))
        strcat(arg3a,arg1a);
  }

/* default op5 same as op3 - if op5 omitted or only '|'              */
/* s 10(3),='AAA',|20(3),>'BBB',|30(3),<'CCC'                        */
/* s 10(3),='AAA',|,>'BBB',|,<'CCC'                                  */
if (arg6a[0])
  { if (arg5a[0] < '0')
        strcpy(arg5a,arg3a);
    else if ((arg5a[0] == '|') && (arg5a[1] < '0'))
        strcat(arg5a,arg3a);
  }

/*eject*/
/* standardize op2 constant in arg2b by defaulting omitted condtn/type*/
if (arg2a[0])                   /* if op2 constant specified          */
  { memcpy(arg2b,"=c",2);       /* setup dflts for condition & type   */
    ii = 0;
    if ((arg2a[ii] == '=') || (arg2a[ii] == '!') ||
        (arg2a[ii] == '<') || (arg2a[ii] == '>'))
      { arg2b[0] = arg2a[ii++];
      }
    if ((arg2a[ii] == 'x') || (arg2a[ii] == 'X') ||
        (arg2a[ii] == 'c') || (arg2a[ii] == 'C') ||
        (arg2a[ii] == 'e') || (arg2a[ii] == 'E'))
      { arg2b[1] = arg2a[ii++];
      }
    strcpy(&arg2b[2],&arg2a[ii]);   /* copy op2 constant '...data...' */

    /* error check - arg2b must now include quote in 3rd byte         */
    if (arg2b[2] != '\'')
      { errmsg("arg2 constant condition/type/open-quote invalid","","",0,0x10);
        return(0);
      }
  }

/* process arg1 - into displacement & length                          */
strcpy(arg1b,arg1a);
pp = atol1(&arg1d,arg1b,'(',6,0x05);
     atol1(&arg1l,pp,')',6,0x01);

/* process arg2 - strip quotes & cnvrt any hex rep to true hex        */
if ((arg2b[1] == 'x') || (arg2b[1] == 'X'))
  { arg2l = hex2data(arg2c,&arg2b[3],'\'',80,0x03);
  }
else
  { arg2l = stncopy(arg2c,&arg2b[3],'\'',80,0x03);
    /* test constant type 'e' convert ASCII to EBCDIC (for cmd line optn a)*/
    /* Dec2002 - assume EBCDIC e'---' if option 'a' spcfd on cmd line      */
    if ((arg2b[1] == 'e') || (arg2b[1] == 'E') || (opsc['a'-'a']))
      { toebcdic2((Uchar*)arg2c,arg2l,0x00);      /* cnvrt ASCII to EBCDIC */
      }
  }

/*eject*/
/* standardize op4 constant in arg4b by defaulting omitted condtn/type*/
if (arg4a[0])                   /* if op4 constant specified          */
  { memcpy(arg4b,"=c",2);       /* setup dflts for condition & type   */
    ii = 0;
    if ((arg4a[ii] == '|') || (arg4a[ii] == '&'))
      { a3ccs[0] = arg4a[ii++]; /* save |/& AND/OR indicator */
      }
    if ((arg4a[ii] == '=') || (arg4a[ii] == '!') ||
        (arg4a[ii] == '<') || (arg4a[ii] == '>'))
      { arg4b[0] = arg4a[ii++];
      }
    if ((arg4a[ii] == 'x') || (arg4a[ii] == 'X') ||
        (arg4a[ii] == 'c') || (arg4a[ii] == 'C') ||
        (arg4a[ii] == 'e') || (arg4a[ii] == 'E'))
      { arg4b[1] = arg4a[ii++];
      }
    strcpy(&arg4b[2],&arg4a[ii]);   /* copy op4 constant '...data...' */

    /* error check - arg4b must now include quote in 3rd byte         */
    if (arg4b[2] != '\'')
      { errmsg("arg4 constant condition/type/open-quote invalid","","",0,0x10);
        return(0);
      }
  }

/* process arg3 - into displacement & length                          */
strcpy(arg3b,arg3a);
pp = atol1(&arg3d,arg3b,'(',6,0x05);
     atol1(&arg3l,pp,')',6,0x01);

/* process arg4 - strip quotes & cnvrt any hex rep to true hex        */
if ((arg4b[1] == 'x') || (arg4b[1] == 'X'))
  { arg4l = hex2data(arg4c,&arg4b[3],'\'',80,0x03);
  }
else
  { arg4l = stncopy(arg4c,&arg4b[3],'\'',80,0x03);
    /* test constant type 'e' convert ASCII to EBCDIC (for cmd line optn a)*/
    /* Dec2002 - assume EBCDIC e'---' if option 'a' spcfd on cmd line      */
    if ((arg4b[1] == 'e') || (arg4b[1] == 'E') || (opsc['a'-'a']))
      { toebcdic2((Uchar*)arg4c,arg4l,0x00);      /* cnvrt ASCII to EBCDIC */
      }
  }

/*eject*/
/* standardize op6 constant in arg6b by defaulting omitted condtn/type*/
if (arg6a[0])                   /* if op6 constant specified          */
  { memcpy(arg6b,"=c",2);       /* setup dflts for condition & type   */
    ii = 0;
    if ((arg6a[ii] == '|') || (arg6a[ii] == '&'))
      { a5ccs[0] = arg6a[ii++]; /* save |/& AND/OR indicator */
      }
    if ((arg6a[ii] == '=') || (arg6a[ii] == '!') ||
        (arg6a[ii] == '<') || (arg6a[ii] == '>'))
      { arg6b[0] = arg6a[ii++];
      }
    if ((arg6a[ii] == 'x') || (arg6a[ii] == 'X') ||
        (arg6a[ii] == 'c') || (arg6a[ii] == 'C') ||
        (arg6a[ii] == 'e') || (arg6a[ii] == 'E'))
      { arg6b[1] = arg6a[ii++];
      }
    strcpy(&arg6b[2],&arg6a[ii]);   /* copy op6 constant '...data...' */

    /* error check - arg6b must now include quote in 3rd byte         */
    if (arg6b[2] != '\'')
      { errmsg("arg6 constant condition/type/open-quote invalid","","",0,0x10);
        return(0);
      }
  }

/* process arg5 - into displacement & length                          */
strcpy(arg5b,arg5a);
pp = atol1(&arg5d,arg5b,'(',6,0x05);
     atol1(&arg5l,pp,')',6,0x01);

/* process arg6 - strip quotes & cnvrt any hex rep to true hex        */
if ((arg6b[1] == 'x') || (arg6b[1] == 'X'))
  { arg6l = hex2data(arg6c,&arg6b[3],'\'',80,0x03);
  }
else
  { arg6l = stncopy(arg6c,&arg6b[3],'\'',80,0x03);
    /* test constant type 'e' convert ASCII to EBCDIC (for cmd line optn a)*/
    /* Dec2002 - assume EBCDIC e'---' if option 'a' spcfd on cmd line      */
    if ((arg6b[1] == 'e') || (arg6b[1] == 'E') || (opsc['a'-'a']))
      { toebcdic2((Uchar*)arg6c,arg6l,0x00);      /* cnvrt ASCII to EBCDIC */
      }
  }

/*Dec2002 - if option 'g1' on cmd line for 1 relative (vs 0 relative)  */
/*        - subtract 1 from dsplcmnts (force 0 if would be negative)   */
/*Aug16/06 - option changed to 'g' from 'z' (now RDW varlth filetype*/
if (opsi['g'-'a'] & 0x01)
  { arg1d--;
    arg3d--;
    arg5d--;
    if (arg1d < 0)
        arg1d = 0;
    if (arg3d < 0)
        arg3d = 0;
    if (arg5d < 0)
        arg5d = 0;
  }

/*eject*/
/* store results in a structure, for easy move depending on s,p,w,u   */
cargs = carg0;                        /* clear any prior data         */
strcpy(cargs.cmd,opcmd);
strcpy(cargs.cmc,cms);
cargs.cmv = cmn;
memcpy(cargs.a2c,arg2c,arg2l);
memcpy(cargs.a4c,arg4c,arg4l);
memcpy(cargs.a6c,arg6c,arg6l);
memcpy(cargs.a1cc,arg1b,3);
memcpy(cargs.a2cc,arg2b,3);
memcpy(cargs.a3cc,arg3b,3);
memcpy(cargs.a4cc,arg4b,3);
memcpy(cargs.a5cc,arg5b,3);
memcpy(cargs.a6cc,arg6b,3);

/* extract command options (dropping command & count) */
/* ex: if cms="t999999c15" then cmo="c15"             */
for (ii=1,jj=0; cms[ii]; ii++)
  { if (cms[ii] == cms[0])
        continue;
    if (!(isdigit(cms[ii])))
        break;
  }
while(cms[ii])
  { cargs.cmo[jj++] = cms[ii++];
  }

/* store any &/| AND/OR ccs saved from op4 for op3 or from op6 for op5 */
if (a3ccs[0])
    memcpy(cargs.a3cc,a3ccs,3);
if (a5ccs[0])
    memcpy(cargs.a5cc,a5ccs,3);
cargs.a1d = arg1d;
cargs.a1l = arg1l;
cargs.a2l = arg2l;
cargs.a3d = arg3d;
cargs.a3l = arg3l;
cargs.a4l = arg4l;
cargs.a5d = arg5d;
cargs.a5l = arg5l;
cargs.a6l = arg6l;
memcpy((char*)cargs.opi,(char*)copsi,104);
memcpy(cargs.opc,copsc,26);
cargs.op1dflt = op1dflt;
/*May16/08 - store indicator op1 dfltd to 0(rsz1)          */
/* - used by scan/rep to set op1lth = rsz1 for each record */

/*eject*/
/* calc op1 end dsp & check not > start + length                       */
ee = cargs.a1d + cargs.a1l;
/*Jun20/07 - op1 dsplcmnt check changed to rsz1 in case varlth files   */
/*Jun23/07 - disable check for varlth files                            */
if ((ftype == 'v') || (ftype == 'x') || (ftype == 'z') || (ftype == 't'))
  { ; }
else if (ee > rsz1)
  { errmsg("op1 dsplcmnt+length > rcsz","","",0,0x10);
    return(0);
  }

/* errcheck >!< compares,                                              */
/* if search pattern multibyte, search area lth must = pattern lth     */
if ((cargs.a2cc[0] == '>') || (cargs.a2cc[0] == '<') || (cargs.a2cc[0] == '!'))
 { if ((cargs.a2l > 1) && (cargs.a1l != cargs.a2l))
  { errmsg("compare <!> multi-byte pattern, search area lth must = pattern lth"
            ,"","",0,0x10);
    return(0);
  }
 }

return(1);
}

/*eject*/
/*---------------------------- search1 --------------------------------*/
/* search for a data pattern - forward or backward (via sb or ssb)     */
/*                                                                     */
/* s 0(20),'ABC',20(20),'XYZ'   - search for ABC within 1st 20 bytes   */
/*                                & XYZ in bytes 20-39                 */
/*                                                                     */
/* ss                           - repeat previous search               */
/* ssb                          - repeat previous search backward      */
/*                                                                     */
/* - set fileptr to 1st byte of record containing pattern & return 1   */
/* - if nofind set fileptr to last rec in file & return 0              */

int search1(void)
{
int rr;                        /* max records search ctr             */
int mm,nn,oo;                  /* field1,2,3 search find/nofind 0+/- */
int mmd,nnd,ood;               /* srch1,2,3 dsplcmnt in rec or file  */
int match;                     /* match results of 3 conditions 0/1  */
int nth;                       /* search for nth occurrence (optn n) */

rr = 0;                        /* reset max search ctr             */
nth = 0;                       /* reset nth occurrence match ctr   */

/* test for new search or repeat previous search                     */
/* note re pscd - op1 dsp prior match (for search continue)          */
/* cmd ss  - repeat search (from next record)                        */
/* cmd sss - repeat search (same record, incrmnt column)             */

if (cms[1] == 's')                  /* repeat search ?               */
  { if (cms[2] == 's')              /* repeat samerec incrementing col*/
      { sargs.pscd += 1;            /* yes - incrmnt dsp start       */
      }
    else
      { if (cms[2] == 'b')
           fileptr -= rszo;          /* backup to prior record        */
                    /* NOTE - rszo not rsz1 since EOF causes rsz1 = 0 */
        else
           fileptr += rsz1;         /* advance to next record        */
        sargs.pscd = 0;             /* ensure dsp extra clear        */
      }
  }
else
  { /* store new search args - but 1st verify (cargs here, sargs below)*/
    if ((cargs.a1l <= 0) || (cargs.a2l <= 0))
      { errmsg("search args invalid",cargs.cmd,"",0,0x10);
        return(0);
      }
    sargs = cargs;                  /* store new search args         */
  }

/*eject*/
/* verify search args - op1 lth & op2 constant lth > 0 ?             */
if ((sargs.a1l <= 0) || (sargs.a2l <= 0))
  { errmsg("search args invalid",sargs.cmd,"",0,0x10);
    return(0);
  }

/* search for pattern (char or hex) until found or EOF                */
while (1)
  { if (fileptr < 0)
      { printf("BOF w nomatch to: %s - try forward search ss ?\n",sargs.cmd);
        fileptr = 0;
        return(0);
      }
    rsz1 = getrec(0);                 /* get next record               */
    if (rsz1 <= 0)                    /* EOF ?                         */
      { fileptr = filesiz0;
        printf("EOF w nomatch to: %s - reset BOF (r0) & repeat(ss) ?\n"
               ,sargs.cmd);
        return(0);
      }

/*Aug07/08 - updt search arg1 lth with current recsize if op1 lth defaulted*/
if (op1dflt)
  { sargs.a1l = rsz1;
  }

/*eject*/
    /* search for patterns - char strings or hex patterns              */
    /*May2003 - 3 possible conditions = < > & AND/OR possible relations*/
    /*note - might have used 'search234' function to do this           */
    /*     - BUT we need to capture the dsplcmnts for reporting        */
    /*       (from returns of separate search2, search3,& search4)     */

    mm = search2(recb,&sargs);       /* 9701 search = > < !            */

    /* search for op4 pattern if specified                             */
    if (sargs.a4l)
      { nn = search3(recb,&sargs);  /* search for match on op4 constant*/
      }
    else
      { nn = 32767;                 /* force match if op4 omitted      */
      }
    /*Feb2003 - 32767 used so will not attempt BOLD when no op4 pattern*/

    /* search for op6 pattern if specified                             */
    if (sargs.a6l)
      { oo = search4(recb,&sargs);  /* search for match on op4 constant*/
      }
    else
      { oo = 32767;                 /* force match if op4 omitted      */
      }

    /* test results of 3 searches depending on conditions vs AND/ORs possible*/
    /*May2003 - op4/op5 added to allow 3rd condition, also allowing AND/OR   */

    match = 0;  /* presume no-match */
    if ((sargs.a3cc[0] != '|') && (sargs.a5cc[0] != '|'))
      { if ((mm >= 0) && (nn >= 0) && (oo >= 0))
            match = 1;
      }
    else if ((sargs.a3cc[0] != '|') && (sargs.a5cc[0] == '|'))
      { if ((mm >= 0) && ((nn >= 0) || (oo >= 0)))
            match = 1;
      }
    else if ((sargs.a3cc[0] == '|') && (sargs.a5cc[0] != '|'))
      { if ((mm >= 0) || ((nn >= 0) && (oo >= 0)))
            match = 1;
      }
    else if ((sargs.a3cc[0] == '|') && (sargs.a5cc[0] == '|'))
      { if ((mm >= 0) || (nn >= 0) || (oo >= 0))
            match = 1;
      }

/*eject*/
/* test result of all 3 possible search conditions & possible AND/ORs  */
/* if match - store fileptr & match dsp for possible ss following      */
/*          - store match found msg & break search loop                */
/*          - opmsg will be displayed after found screen displayed     */
/* nomatch  - clear prior search match dsplcmnt & increment            */
/*          - advance fileptr to next record & repeat search loop      */
    if (match)
      { sargs.psfp = fileptr;
        sargs.pscd = mm;

        /* show found pattern as dsplcmnt in rec or file depending optn d2*/
        if (opsi['d'-'a'] & 0x02)
          { mmd = fileptr+mm;
            nnd = fileptr+nn;
            ood = fileptr+oo;
          }
        else
          { mmd = mm;
            nnd = nn;
            ood = oo;
          }
        /* if 3 search fields, report all 3 found dsplcmnts            */
        if (sargs.a6l)
          { sprintf(opmsg,"found--> %s <--1st at %d, 2nd at %d, 3rd at %d\n",
                           sargs.cmd,mmd,nnd,ood);
          }
        /* if 2 search fields, report both found dsplcmnts             */
        else if (sargs.a4l)
          { sprintf(opmsg,"found--> %s <--1st at byte# %d & 2nd at byte %d\n",
                           sargs.cmd,mmd,nnd);
          }
        else
          { sprintf(opmsg,"found--> %s <--at byte# %d of record# %d\n"
                         ,sargs.cmd,mmd,recnum1);
          }

        /* ensure screen display will include found pattern      */
        /* calc rec index assume 320 bytes/screen                */
        rii = (mm/320) * 320;

        /* break while(1) loop on match & return +1              */
        /* But continue if option 'n' search < nth occurrence    */
        nth++;             /* increment search match occurrence# */
        if (nth >= sargs.opi['n'-'a'])
          { break;  
          }
        else
          { fileptr += rsz1;            /* advance to next record        */
            continue;
          }
      }

/*eject*/
    else
      { if ((cms[1] == 'b') || (cms[2] == 'b'))
          { fileptr -= rsz1;            /* backup to prior record        */
          }
        else
          { fileptr += rsz1;            /* advance to next record        */
          }
        sargs.pscd = 0;                /* ensure dsp extra clear        */
        rr++;                          /* count records searched        */
        if ((sargs.cmv) && (rr >= sargs.cmv))
          { sprintf(opmsg,"NOfind--> %s within limit spcfd\n",sargs.cmd);
            break;
          }
      }
  }
return(1);
}

/*eject*/
/*--------------------------- search2 --------------------------------*/
/* search a record for a match, as specified in the CA structure      */
/* - see the CA structure defined earlier in this program             */
/* - defines search start, length, pattern data, length,& cc = > < !  */
/* if match   - return dsplcmnt to matching data                      */
/* if nomatch - return -1                                             */

int search2(char *recb, struct CA *cap)
{
int ii,jj,kk;                  /* misc indices                       */
int bb = cap->a1d;             /* dsplcmnt in rec to begin search    */
int ee = cap->a1d+cap->a1l;    /* dsplcmnt in rec to end search      */
int ll = cap->a2l;             /* length of search pattern           */

/* to allow search continuation within same record (see search1 rtn)  */
/* - use prior search dsplcmnt (+1) if specified                      */
if (cap->pscd)
  { bb = cap->pscd;
  }

/* use different search rtns, depending on search condition > < = !    */
/*  & depending on search pattern length - 1 byte or multi-bytes       */
/*  <!> 1 byte compares    - will compare to each byte of search area  */
/* <!> multi-byte compares - search area length must = pattern length  */
/*                        - will compare corresponding bytes           */
if (cap->a2cc[0] == '>')
 { if (ll == 1)
    { for (ii=bb; ii < ee; ii++)
        { if ((Uchar) recb[ii] > (Uchar) cap->a2c[0])
              return(ii);
        }
    }
   else
    { if (memcmp(&recb[bb],&(cap->a2c[0]),ll) > 0)
              return(bb);
    }
 }

else if (cap->a2cc[0] == '<')
 { if (ll == 1)
    { for (ii=bb; ii < ee; ii++)
        { if ((Uchar) recb[ii] < (Uchar) cap->a2c[0])
              return(ii);
        }
    }
   else
    { if (memcmp(&recb[bb],&(cap->a2c[0]),ll) < 0)
              return(bb);
    }
 }

/*eject*/
else if (cap->a2cc[0] == '!')
 { if (ll == 1)
     { /*Mar2003 - sep logic for != search in 1 byte OR multi-byte fields    */
       if (cap->a1l == 1)
         { for (ii=bb; ii < ee; ii++)
             { if ((Uchar) recb[ii] != (Uchar) cap->a2c[0])
                   return(ii);
             }
         }
       else
         { /*Mar2003 - Multi-byte field search for != 1 byte pattern        */
           /* count&save dsp of any matches, at end return dsp to last match*/
            { for (ii=bb,jj=0,kk=0; ii < ee; ii++)
                { if ((Uchar) recb[ii] == (Uchar) cap->a2c[0])
                    { kk++;               /* count matches                  */
                      jj = ii;            /* save dsp to last == in field   */
                    }
                }
              if (kk == 0)                /* if No matches in field         */
                  return(bb);             /* return dsp to 1st byte of field*/
              /*  return(jj); return dsp to last == Mar2003 Bad logic fixed */
            }
         }
     }
   else
     { if (memcmp(&recb[bb],&(cap->a2c[0]),ll) != 0)
           return(bb);
     }
 }

else       /* must be = condition */
 { for (ii=bb; ii < ee; ii++)
     { for (jj=ii,kk=0; jj < ee; jj++,kk++)
         { if (recb[jj] != cap->a2c[kk])
               break;
           if (kk >= (ll-1))
               return(ii);
         }
     }
 }
  return(-1);
}

/*eject*/
/*--------------------------- search3 --------------------------------*/
/* search record for match to op4 constant, as specified in CA struct */
/* - see the CA structure defined earlier in this program             */
/* - defines search start, length, pattern data, length,& cc = > < !  */
/* if match   - return dsplcmnt to matching data                      */
/* if nomatch - return -1                                             */

int search3(char *recb, struct CA *cap)
{
int ii,jj,kk;                  /* misc indices                       */
int bb = cap->a3d;             /* dsplcmnt in rec to begin search    */
int ee = cap->a3d+cap->a3l;    /* dsplcmnt in rec to end search      */
int ll = cap->a4l;             /* length of search pattern           */


/* use different search rtns, depending on search condition > < = !    */
/*  & depending on search pattern length - 1 byte or multi-bytes       */
/*  <!> 1 byte compares    - will compare to each byte of search area  */
/* <!> multi-byte compares - search area length must = pattern length  */
/*                        - will compare corresponding bytes           */
if (cap->a4cc[0] == '>')
 { if (ll == 1)
    { for (ii=bb; ii < ee; ii++)
        { if ((Uchar) recb[ii] > (Uchar) cap->a4c[0])
              return(ii);
        }
    }
   else
    { if (memcmp(&recb[bb],&(cap->a4c[0]),ll) > 0)
              return(bb);
    }
 }

else if (cap->a4cc[0] == '<')
 { if (ll == 1)
    { for (ii=bb; ii < ee; ii++)
        { if ((Uchar) recb[ii] < (Uchar) cap->a4c[0])
              return(ii);
        }
    }
   else
    { if (memcmp(&recb[bb],&(cap->a4c[0]),ll) < 0)
              return(bb);
    }
 }

/*eject*/
else if (cap->a4cc[0] == '!')
 { if (ll == 1)
     { /*Mar2003 - sep logic for != search in 1 byte OR multi-byte fields    */
       if (cap->a3l == 1)
         { for (ii=bb; ii < ee; ii++)
             { if ((Uchar) recb[ii] != (Uchar) cap->a4c[0])
                   return(ii);
             }
         }
       else
         { /*Mar2003 - Multi-byte field search for != 1 byte pattern        */
           /* count&save dsp of any matches, at end return dsp to last match*/
            { for (ii=bb,jj=0,kk=0; ii < ee; ii++)
                { if ((Uchar) recb[ii] == (Uchar) cap->a4c[0])
                    { kk++;               /* count matches                  */
                      jj = ii;            /* save dsp to last == in field   */
                    }
                }
              if (kk == 0)                /* if No matches in field         */
                  return(bb);             /* return dsp to 1st byte of field*/
              /*  return(jj); return dsp to last == Mar2003 Bad logic fixed */
            }
         }
     }
   else
    { if (memcmp(&recb[bb],&(cap->a4c[0]),ll) != 0)
          return(bb);
    }
 }

else       /* must be = condition */
 { for (ii=bb; ii < ee; ii++)
     { for (jj=ii,kk=0; jj < ee; jj++,kk++)
         { if (recb[jj] != cap->a4c[kk])
               break;
           if (kk >= (ll-1))
               return(ii);
         }
     }
 }
  return(-1);
}

/*eject*/
/*--------------------------- search4 --------------------------------*/
/* search record for match to op6 constant, as specified in CA struct */
/* - see the CA structure defined earlier in this program             */
/* - defines search start, length, pattern data, length,& cc = > < !  */
/* if match: return dsplcmnt to matching data, if nomatch: return -1  */
/*May2003 - op5 & op6 added to allow 3rd condition on search, etc     */

int search4(char *recb, struct CA *cap)
{
int ii,jj,kk;                  /* misc indices                       */
int bb = cap->a5d;             /* dsplcmnt in rec to begin search    */
int ee = cap->a5d+cap->a5l;    /* dsplcmnt in rec to end search      */
int ll = cap->a6l;             /* length of search pattern           */


/* use different search rtns, depending on search condition > < = !    */
/*  & depending on search pattern length - 1 byte or multi-bytes       */
/*  <!> 1 byte compares    - will compare to each byte of search area  */
/* <!> multi-byte compares - search area length must = pattern length  */
/*                        - will compare corresponding bytes           */
if (cap->a6cc[0] == '>')
 { if (ll == 1)
    { for (ii=bb; ii < ee; ii++)
        { if ((Uchar) recb[ii] > (Uchar) cap->a6c[0])
              return(ii);
        }
    }
   else
    { if (memcmp(&recb[bb],&(cap->a6c[0]),ll) > 0)
              return(bb);
    }
 }

else if (cap->a6cc[0] == '<')
 { if (ll == 1)
    { for (ii=bb; ii < ee; ii++)
        { if ((Uchar) recb[ii] < (Uchar) cap->a6c[0])
              return(ii);
        }
    }
   else
    { if (memcmp(&recb[bb],&(cap->a6c[0]),ll) < 0)
              return(bb);
    }
 }

/*eject*/
else if (cap->a6cc[0] == '!')
 { if (ll == 1)
     { /*Mar2003 - sep logic for != search in 1 byte OR multi-byte fields    */
       if (cap->a5l == 1)
         { for (ii=bb; ii < ee; ii++)
             { if ((Uchar) recb[ii] != (Uchar) cap->a6c[0])
                   return(ii);
             }
         }
       else
         { /*Mar2003 - Multi-byte field search for != 1 byte pattern        */
           /* count&save dsp of any matches, at end return dsp to last match*/
            { for (ii=bb,jj=0,kk=0; ii < ee; ii++)
                { if ((Uchar) recb[ii] == (Uchar) cap->a6c[0])
                    { kk++;               /* count matches                  */
                      jj = ii;            /* save dsp to last == in field   */
                    }
                }
              if (kk == 0)                /* if No matches in field         */
                  return(bb);             /* return dsp to 1st byte of field*/
              /*  return(jj); return dsp to last == Mar2003 Bad logic fixed */
            }
         }
     }
   else
    { if (memcmp(&recb[bb],&(cap->a6c[0]),ll) != 0)
          return(bb);
    }
 }

else       /* must be = condition */
 { for (ii=bb; ii < ee; ii++)
     { for (jj=ii,kk=0; jj < ee; jj++,kk++)
         { if (recb[jj] != cap->a6c[kk])
               break;
           if (kk >= (ll-1))
               return(ii);
         }
     }
 }
  return(-1);
}

/*eject*/
/*--------------------------- search234 -------------------------------*/
/* search record for match to op3, op4,& op6 constants as per CA struct*/
/* - see the CA structure defined earlier in this program              */
/* - defines search start, length, pattern data, length,& cc = > < !   */
/* if match: return +1, if nomatch: return -1                          */
/*May2003 - subrtn renamed & extended to test results of search2,3,4   */

int search234(char *recb, struct CA *cap)
{
int mm,nn,oo;

mm = search2(recb,cap);             /* search for match on op2 constant*/

/* search for match on op4 constant if specified                      */
nn = 1;                             /* disable op4 consider if not spcfd*/
if (cap->a4l)
  { nn = search3(recb,cap);         /* search for match on op3 constant*/
  }

/* search for match on op6 constant if specified                      */
oo = 1;                             /* disable op6 consider if not spcfd*/
if (cap->a6l)
  { oo = search4(recb,cap);         /* search for match on op6 constant*/
  }

/* test results of 3 searches depending on conditions vs AND/ORs possible*/
/*May2003 - op4/op5 added to allow 3rd condition, also allowing AND/OR   */

if ((cap->a3cc[0] != '|') && (cap->a5cc[0] != '|'))
  { if ((mm >= 0) && (nn >= 0) && (oo >= 0))
        return(1);
  }
else if ((cap->a3cc[0] != '|') && (cap->a5cc[0] == '|'))
  { if ((mm >= 0) && ((nn >= 0) || (oo >= 0)))
        return(1);
  }
else if ((cap->a3cc[0] == '|') && (cap->a5cc[0] != '|'))
  { if ((mm >= 0) || ((nn >= 0) && (oo >= 0)))
        return(1);
  }
else if ((cap->a3cc[0] == '|') && (cap->a5cc[0] == '|'))
  { if ((mm >= 0) || (nn >= 0) || (oo >= 0))
        return(1);
  }

return(-1);
}

/*eject*/
/*---------------------------- search34 -------------------------------*/
/* search record for match to op3/op4,& op5/op6 constants per CA struct*/
/* - see the CA structure defined earlier in this program              */
/* - defines search start, length, pattern data, length,& cc = > < !   */
/* if match: return +1, if nomatch: return -1                          */
/*May2003 - op4/op5 3rd condition added                                */
/* search234 used for all 3 conditions (search,write,enumerate,print,iprint)*/
/* search34 used to test 2nd & 3rd conditions (update)                 */
/* - since op1/op2 (1st condition) used for update constant            */

int search34(char *recb, struct CA *cap)
{
int nn,oo;

/* search for match on op4 constant if specified                      */
nn = 1;                             /* disable op4 consider if not spcfd*/
if (cap->a4l)
  { nn = search3(recb,cap);         /* search for match on op3 constant*/
  }

/* search for match on op6 constant if specified                      */
oo = 1;                             /* disable op6 consider if not spcfd*/
if (cap->a6l)
  { oo = search4(recb,cap);         /* search for match on op6 constant*/
  }

/* test results of 2 searches depending on conditions vs AND/OR possible*/

if (cap->a5cc[0] == '|')
  { if ((nn >= 0) || (oo >= 0))
        return(1);
  }
else
  { if ((nn >= 0) && (oo >= 0))
        return(1);
  }

return(-1);
}

/*eject*/
/*---------------------------- print1 ---------------------------------*/
/* print specified number of records                                   */
/* - or records matching a pattern, to a specified maximum             */
/*                                                                     */
/* p5                        - print next 5 records                    */
/* p5l100                    - print 5 recs line width 100 bytes       */
/*                                                                     */
/* p5 0(20),='ABC'           - search for & print records with 'ABC'   */
/*                             within 1st 20 bytes to max 5 recs       */
/*                                                                     */
/* pp                        - repeat previous search                  */
/*                                                                     */
/* - if nofind set fileptr to last rec in file & return 0              */

int print1(int lsize)
{
int nn;                             /* max records search ctr       */
int mm;                             /* search find/nofind 0+/-      */
int ff;                             /* calc rmndr formfeed option f */
int ss;                             /* space option p or cmd line s */

/* test for 'p=s' transfer last search cmd to print cmd storage structure */
if ((cms[1] == '=') && (cms[2] == 's'))
  { pargs = sargs;              /* store print cmd from last search cmd */
    pargs.cmd[0] = 'p';         /* change cmd from 's' to 'p'           */
    pargs.cmc[0] = 'p';         /* change cmc from 's' to 'p'           */
    printf("print args stored from last search: %s \n",pargs.cmd);
    printf("execute 'pp' to print spcfd records from current to EOF --> ");
    return(2);
  }

/* test for new print command or repeat previous print command (pp)  */
if (cms[1] == 'p')                      /* repeat prior print ?      */
  { /* fileptr += rsz1; advance to next record <- disabled 980706    */
    /* allow value updt on repeat cmd - pp99                         */
    if (cargs.cmv)
        pargs.cmv = cargs.cmv;
  }
else
  { /* store new search args - but 1st verify (cargs here, pargs below)*/
    if ((cargs.cmv == 0) && (cargs.a2l == 0))
      { errmsg("print cmd must specify number or pattern",cargs.cmd,"",0,0x10);
        return(0);
      }
    pargs = cargs;                      /* store new print args      */
  }

/* store space option for printrec from s option on command line     */
/* - but override if s option spcfd on print command                 */
if (pargs.opc['s'-'a'])
  { ss = pargs.opi['s'-'a'];
  }
else
  { ss = opsi['s'-'a'];
  }

/*eject*/
/* verify print a number of records or search pattern                */
if ((pargs.cmv == 0) && (pargs.a2l == 0))
  { errmsg("print cmd must specify number or pattern",pargs.cmd,"",0,0x10);
    return(0);
  }

/*Feb05/09 - call function to open file on each print */
openfileX(pfname,"P",&pfptr,&pfopn,0x00);

fprintf(pfptr,"uvhd filename=%s\n",fn1a);
fprintf(pfptr,"options=%s lastmod=%s today=%s print=%s\n"
             ,opsu,fmdate,today,pargs.cmd);
fprintf(pfptr,"rec#=%d rcount=%d filesize=%s recsize=%d fsize%%rsize(remainder)=%d\n"
              ,recnum1,frecsp,fsedit,rsz1,remndr);
prints++;                       /* count print cmds (set switch)     */

/* modify line-width if option l spcfd (default 64 or cmd line optn l)*/
if (pargs.opi['l'-'a'])
    lsize = pargs.opi['l'-'a'];

/*Jan24/09 - correct recnum printed for RDW files (was 1 too high)     */
/*         - decrement here if RDW since getrec incrments for RDW files*/
if (opsc['z'-'a'])
  { recnum1--;
  } 

/*eject*/
/* print spcfd no of recs, or search by pattern & print to max recs  */
nn = 0; fpsv = 0;                       /* reset max print ctr        */
while (1)
 { rsz1 = getrec(0);
   if (rsz1 <= 0)                       /* EOF ?                       */
     { fileptr = fileptr1;
       break;
     }

   /* if search pattern spcfd - search for & print matching recs to max cnt*/
   if (pargs.a2l)
     { mm = search234(recb,&pargs);   /* 9701 search = > < !             */
       if (mm >= 0)
         { printrec(pfptr,lsize,ss);  /* print matching record           */
           nn++;                      /* count records printed (max test)*/
           fpsv = fileptr;            /* save fileptr of last matched rec*/
         }
     }
   else
     { printrec(pfptr,lsize,ss);      /* print current record            */
       nn++;                          /* count records printed (max test)*/
       fpsv = fileptr;                /* save fileptr of last matched rec*/
     }

   /* test max print count reached, if so display confirmation & return  */
   if ((pargs.cmv) && (nn >= pargs.cmv))
     { break;
     }

   /* if option f, write formfeed every f# records                  */
   if (copsi['f'-'a'])
     { ff = nn % copsi['f'-'a'];
       if (ff == 0)
          fprintf(pfptr,"\f");
     }

   /* increment fileptr to next record & repeat loop                 */
   fileptr += rsz1;                 /* advance to next record        */

 }

/*Feb05/09 - close file on each print (vs all prints to same file) */
closefileX(pfname,"P",&pfptr,&pfopn,0x00);

/* print loop ended by max count, or EOF + errmsg if no recs printed */
sprintf(opmsg,"%s --> %d recs printed %s\n",pargs.cmd,nn,pfname);

if (nn == 0)
  { printf("EOF reached with NO records printed \n");
    return(0);
  }
else
  { fileptr = fpsv;            /* so last printed rec will be displayed*/
  }
return(1);
}

/*eject*/
/*---------------------------- iprint1 --------------------------------*/
/* immediate print specified number of records                         */
/* - or records matching a pattern, to a specified maximum             */
/* i5                        - iprint next 5 records                   */
/* i5l100                    - print 5 recs line width 100 bytes       */
/* i5 0(20),='ABC'           - search for & print records with 'ABC'   */
/*                             within 1st 20 bytes to max 5 recs       */
/* ii                        - repeat previous search                  */
/* - if nofind set fileptr to last rec in file & return 0              */

int iprint1(int lsize)
{
int nn;                             /* max records search ctr       */
int mm;                             /* search find/nofind 0+/-      */
int ff;                             /* calc rmndr formfeed option f */
int ss;                             /* space option p or cmd line s */

/* test for 'i=s' transfer last search cmd to iprint cmd storage structure */
if ((cms[1] == '=') && (cms[2] == 's'))
  { iargs = sargs;              /* store iprint cmd from last search cmd */
    iargs.cmd[0] = 'i';         /* change cmd from 's' to 'i'           */
    iargs.cmc[0] = 'i';         /* change cmc from 's' to 'i'           */
    printf("iprint args stored from last search: %s \n",iargs.cmd);
    printf("execute 'ii' to iprint spcfd records from current to EOF --> ");
    return(2);
  }

/* test for new iprint command or repeat previous iprint command (pp)*/
if (cms[1] == 'i')                      /* repeat prior iprint ?     */
  { /* fileptr += rsz1; advance to next record <- disabled 980706    */
    /* allow value updt on repeat cmd - ii99                         */
    if (cargs.cmv)
        iargs.cmv = cargs.cmv;
  }
else
  { /* store new search args - but 1st verify (cargs here, iargs below)*/
    if ((cargs.cmv == 0) && (cargs.a2l == 0))
      { errmsg("iprint cmd must specify number or pattern",cargs.cmd,"",0,0x10);
        return(0);
      }
    iargs = cargs;                      /* store new iprint args     */
  }

/* store space option for printrec from s option on command line     */
/* - but override if s option spcfd on print command                 */
if (iargs.opc['s'-'a'])
  { ss = iargs.opi['s'-'a'];
  }
else
  { ss = opsi['s'-'a'];
  }

/*eject*/
/* verify iprint a number of records or search pattern               */
if ((iargs.cmv == 0) && (iargs.a2l == 0))
  { errmsg("iprint cmd must specify number or pattern",iargs.cmd,"",0,0x10);
    return(0);
  }

/*Feb05/09 - call function to open file on each iprint */
openfileX(ifname,"I",&ifptr,&ifopn,0x00);

fprintf(ifptr,"uvhd filename=%s\n",fn1a);
fprintf(ifptr,"options=%s lastmod=%s today=%s iprint=%s\n"
             ,opsu,fmdate,today,iargs.cmd);
fprintf(ifptr,"rec#=%d rcount=%d filesize=%s recsize=%d fsize%%rsize(remainder)=%d\n"
              ,recnum1,frecsp,fsedit,rsz1,remndr);
iprints++;                      /* count iprint cmds (set switch)    */

/* modify line-width if option l spcfd (default 64 or cmd line optn l)*/
if (iargs.opi['l'-'a'])
    lsize = iargs.opi['l'-'a'];

/*Jan24/09 - correct recnum printed for RDW files (was 1 too high)     */
/*         - decrement here if RDW since getrec incrments for RDW files*/
if (opsc['z'-'a'])
  { recnum1--;
  }

/*eject*/
/* iprint spcfd no of recs, or search by pattern & iprint to max recs */
nn = 0; fpsv = 0;                       /* reset max iprint ctr       */
while (1)
 { rsz1 = getrec(0);
   if (rsz1 <= 0)                       /* EOF ?                      */
     { fileptr = fileptr1;
       break;
     }
   /* if search pattern spcfd - search for & iprint matching recs to max cnt*/
   if (iargs.a2l)
     { mm = search234(recb,&iargs);   /* 9701 search = > < !              */
       if (mm >= 0)
         { printrec(ifptr,lsize,ss);  /* iprint matching record           */
           nn++;                      /* count records iprinted (max test)*/
           fpsv = fileptr;            /* save fileptr of last matched rec*/
         }
     }
   else
     { printrec(ifptr,lsize,ss);      /* iprint current record            */
       nn++;                          /* count records iprinted (max test)*/
       fpsv = fileptr;                /* save fileptr of last printed rec*/
     }

   /* test max iprint count reached, if so display confirmation & return  */
   if ((iargs.cmv) && (nn >= iargs.cmv))
     { break;
     }
   /* if option f, write formfeed every f# records                  */
   if (copsi['f'-'a'])
     { ff = nn % copsi['f'-'a'];
       if (ff == 0)
          fprintf(ifptr,"\f");
     }
   /* increment fileptr to next record & repeat loop                 */
   fileptr += rsz1;                 /* advance to next record        */
 }

/*eject*/
/* iprint loop ended by max count, or EOF + errmsg if no recs iprinted */
sprintf(opmsg,"%s --> %d recs iprinted %s\n",iargs.cmd,nn,ifname);

/*Feb05/09 - close file on each iprint (vs all prints to same file) */
closefileX(ifname,"I",&ifptr,&ifopn,0x00);

/* iprint cmd built at prgm init Portrait or Landscape if l optn > 68  */
if (iargs.opi['l'-'a'] > 68)
  { strcpy(prtcmdWfile,prtcmdW);       /* setup uvlp12L/lp for iprint LS  */
    strcat(prtcmdWfile," ");
    strcat(prtcmdWfile,ifname);        /* append iprint filename          */
    system(prtcmdWfile);         /* execute uvlp12L cmd (built at init) */
  }
else
  { strcpy(prtcmdfile,prtcmd);         /* setup uvlp/lp for iprint cmd   */
    strcat(prtcmdfile," ");
    strcat(prtcmdfile,ifname);         /* append iprint filename         */
    system(prtcmdfile);          /* execute uvlp12 cmd (built at init) */
  }

if (nn == 0)
  { printf("EOF reached with NO records iprinted \n");
    return(0);
  }
else
  { fileptr = fpsv;            /* so last printed rec will be displayed*/
  }
return(1);
}

/*eject*/
/*---------------------------- printrec -------------------------------*/
/* print current record to the output print file                       */
/* - not used for screen display (which must not exceed screen size)   */
/* - this subrtn will write formatted lines to the print file          */
/*   for the entire record length                                      */

int printrec(FILE *fptr,int lsize, int pspace)
{
/* output screen hdr record column scale, 0/1 relative by option g0/g1   */
sncopy(scalet2,scalet1,lsize,0x03);  /* copy & truncate tens scale lsize */
sncopy(scaleu2,scaleu1,lsize,0x03);  /* copy & truncate units scale lsize*/

/*May2001 - allow 32/64 bit fileptr, insert at begin scale1              */
/*        - edit fileptr rt justified in 8 or 11 bytes depending on value*/

/*Apr05/07 - test option o# to display scale every # records */
/*May15/07 - option changed from 'i' to 'o'                  */
/* calc remainder (recnum1 % optn o) only if optn o spcfd    */
recnum1o = 1;     /* presume no option o for test below      */
if ((opsi['o'-'a']) && (recnum1 > 1))
  { recnum1o = (recnum1 % opsi['o'-'a']);
  }
if (recnum1o == 1)
  { if (pspace & 0x01)           /* space before scale ? */
      { fprintf(fptr,"\n");
      }
    /* output screen hdr record column scale, 0/1 relative by option g0/g1 */
    fprintf(fptr,"            %s\n",scalet2);   /* tens scale fileptr removed*/
    fprintf(fptr,"r#%9ld %s\n",recnum1,scaleu2); /* units scale              */
    linctr += 2;                           /* incrmnt line ctr for scale   */
    if (pspace & 0x08)           /* space after scale ? */
      { fprintf(fptr,"\n");
      }
  }
else
  {  fprintf(fptr,"\n");     /* space 1 vs print scale */
     linctr++;
  }

/* begin loop to print 3 part vert hex lines, until end of record      */
/* getlinr - extracts next display segment from current record         */

while(1)
  { lsz1 = getlinr(lsize);
    if (lsz1 <= 0)
        break;

    memcpy(linc,linr,lsz1);          /* copy for translate/filter (LMAX)*/

    /* test option 'a' to translate character line from EBCDIC to ASCII */
    /* note - bit 0x04 converts non-printable EBCDIC chars to periods   */
    if (opsc['a'-'a'])                   /* option 'a' active ?         */
       toascii2((Uchar*)linc,lsz1,0x04); /* translate EBCDIC to ASCII (LMAX)*/

/*eject*/
    /* now convert data line to 3 lines for vertical hex display        */
    /* - presuming full 64 bytes per line (will compensate below)       */
    filterchar(linc,lsz1);           /* format line #1 - characters     */
    hexzone(linz,linr,lsz1);         /* format line #2 - hex zones      */
    hexdigit(lind,linr,lsz1);        /* format line #3 - hex digits     */

    /* now insert the array nulls again - based on length returned by   */
    /* getlinr which may be < 64 if optn 'l' or optn 'f' or eof         */
    linc[lsz1] = '\0';
    linz[lsz1] = '\0';
    lind[lsz1] = '\0';

    /* fprintf(fptr,"  %8ld %s\n",rii1,linc);                             */
    /*Nov2001 - fileptr removed on tens scale, now insert on data segments*/
    /*Dec2001 - always display fileptr on 1st segment of record           */
    /*        - then test option d0/d2 for following record segments      */
    /*     d0 - display record dsplcmnts on following segments of record  */
    /*     d2 - display file dsplcmnts on all segments of record          */
    if (rii1 == 0)
      { sprintf(fpedit,E64,fileptr);          /* edit allowing 32/64 bits */
      }
    else
      { if (opsi['d'-'a'] & 0x02)
          { sprintf(fpedit,E64,fileptr+rii1); /* file dsplcmnts on all segs*/
          }
        else
          { sprintf(fpedit,E64,rii1);         /* record dsplmnts on segs 2+*/
          }
      }

    if (pspace & 0x02)           /* test option to space before data lines */
      { fprintf(fptr,"\n");
      }

    /* display character line of current segment of current record         */
    fprintf(fptr,"%11s %s\n",fpedit,linc);

    /* display by option 'h' & any < 0x20 or > 0x7E not \n \r \t \f    */
    /* h0 auto determine by data, h1 force chars only, h2 force hex    */
    if (((opsi['h'-'a'] & 0x02) && (!(opsi['h'-'a'] & 0x01)))
        || (((opsi['h'-'a'] & 0x03) == 0) && (xchar > 0)))
      { fprintf(fptr,"            %s\n",linz);
        fprintf(fptr,"            %s\n",lind);
      }
  }

/* test option for extra space between records when printing */
if (pspace & 0x04)          /* space 3 option ?           */
  { fprintf(fptr,"\n");
  }
return(1);
}

/*eject*/
/*---------------------------- write1 ---------------------------------*/
/* write specified number of records                                   */
/* - or records matching a pattern, to a specified maximum             */
/*                                                                     */
/* w5                        - write next 5 records                    */
/*                                                                     */
/* w5e4x 0(20),='ABC'        - search for & write records with 'ABC'   */
/*                             within 1st 20 bytes to max 5 recs       */
/*                           - write Every 4th record (option e4)      */
/*                           - write other records to 2nd write file   */
/*                             (tmp/filename.datetimeX vs W)           */
/*                                                                     */
/* ww                        - repeat previous write                   */
/*                                                                     */
/* - if nofind set fileptr to last rec in file & return 0              */

int write1(void)
{
int nn;                             /* max records search ctr       */
int mm;                             /* search find/nofind 0+/-      */
int ii;                             /* recs dropped by DISAM flag   */
int tt;                             /* count for every nth select   */
int ww;                             /* set if current record written*/
int xx;                             /* count non-matched recs written*/
int yy;                             /* count non-matched recs til EOF*/

/* test for 'w=s' transfer last search cmd to write cmd storage structure */
if ((cms[1] == '=') && (cms[2] == 's'))
  { wargs = sargs;              /* store write cmd from last search cmd */
    wargs.cmd[0] = 'w';         /* change cmd from 's' to 'w'           */
    wargs.cmc[0] = 'w';         /* change cmc from 's' to 'w'           */
    printf("write args stored from last search: %s \n",wargs.cmd);
    printf("execute 'ww' to write spcfd records from current to EOF --> ");
    return(2);
  }

/* test for new write command or repeat previous write command (ww)  */
if (cms[1] == 'w')                      /* repeat prior write ?      */
  { /* fileptr += rsz1; advance to next record <- disabled 980706    */
    /* allow value updt on repeat cmd - ww99                         */
    if (cargs.cmv)
        wargs.cmv = cargs.cmv;
  }
else
  { /* store new args - but 1st default count (cargs here, wargs below)*/
    if (cargs.cmv == 0)
      { cargs.cmv = 999999999;
      }
    wargs = cargs;                      /* store new write args      */
  }

/* if count not specified - set to high number to ensure EOF reached  */
/* - while searching for patterns (presumably)                        */
if (wargs.cmv == 0)
  { wargs.cmv = 999999999;
  }

/*eject*/
/*Feb05/09 - common functions openfileX & closefileX for P,I,W,V,D files*/
/*         - chg P/I/W/V/D cmds to open & close output file on each cmd */
/*         - cmd line option w1 to write 1 combined file vs separate    */
/*         - filename format as follows:                                */
/* tmp/inputfilename_yymmdd_hhmmssX                     <-- X=P/I/W/V/D */
/* tmp/inputfilename_yymmdd_       <-- basefilename + time + X each open*/

/*Feb05/09 - call function to open file on 1st write (depending optn w1)*/
openfileX(wfname,"W",&wfptr,&wfopn,0x00);

writes++;                       /* count write cmds (set switch)     */

/* write spcfd no of recs, or search by pattern & write to max recs  */
nn=0; ii=0; fpsv=0; tt=0; xx=0; yy=0;   /* clear counters             */
while (1)
 { rsz1 = getrec(0);
   if (rsz1 <= 0)                       /* EOF ?                       */
     { fileptr = fileptr1;
       break;
     }
   ww = 0;                            /* reset switch cur rec written   */

   /* if option 'i' spcfd - drop if x'00' in last byte                  */
   if (wargs.opc['i'-'a'])
     { if (recb[rsz1-1] == '\0')
         { ii++;                      /* count recs dropped by ISAM flag*/
           fileptr += rsz1;           /* advance to next record         */
           continue;                  /* repeat loop to get next rec    */
         }
     }

   /* if search pattern spcfd - search for & write matching recs to max cnt*/
   if (wargs.a2l)
    { mm = search234(recb,&wargs);   /* 9701 search = > < !            */
      if (mm >= 0)
       { /* test option e to write every nth record                     */
         tt++;                       /* count all matches for every nth test*/
         if (wargs.opi['e'-'a'])
          { if ((tt % wargs.opi['e'-'a']) == 0)
             { writerec();           /* write matching record           */
               nn++;                 /* count records written (max test)*/
               ww++;                 /* set sw cur rec written          */
               fpsv = fileptr;       /* save fileptr of last matched rec*/
             }
          }
         else
          { writerec();              /* write matching record           */
            nn++;                    /* count records written (max test)*/
            ww++;                    /* set sw cur rec written          */
            fpsv = fileptr;          /* save fileptr of last matched rec*/
          }
       }
    }
/*eject*/
   else
    { /* test option e to write every nth record                     */
      tt++;                          /* count all matches for every nth test*/
      if (wargs.opi['e'-'a'])
       { if ((tt % wargs.opi['e'-'a']) == 0)
          { writerec();              /* write matching record           */
            nn++;                    /* count records written (max test)*/
            ww++;                    /* set sw cur rec written          */
            fpsv = fileptr;          /* save fileptr of last matched rec*/
          }
       }
      else
       { writerec();                 /* write current record            */
         nn++;                       /* count records written (max test)*/
         ww++;                       /* set sw cur rec written          */
         fpsv = fileptr;             /* save fileptr of last written rec*/
       }
    }

   /* option to write non-matched to 2nd file*/
   /*Feb05/09 - removed                      */
   /* if ((wargs.opc['x'-'a']) && (ww == 0)) */
   /*   { writerec2();                       */
   /*     xx++;                              */
   /*   }                                    */

   /* increment fileptr to next record & repeat loop                 */
   fileptr += rsz1;                 /* advance to next record        */

   /* test max write count reached, if so display confirmation & return */
   if ((wargs.cmv) && (nn >= wargs.cmv))
     { break;
     }
 }

/* write loop ended by max count, or EOF             */
/*Feb05/09 - suppress ISAM deleted recs dropped if 0 */
if (ii >= 1)
  { sprintf(opmsg,"%s %d written, %d dropped(ISAM), %s\n"
                 ,wargs.cmd,nn,ii,wfname);
  }
else
  { sprintf(opmsg,"%s %d records written to: %s\n",wargs.cmd,nn,wfname);
  }

/*eject*/
/* if option x2, continue to write 2nd file until EOF reached */
/*Feb05/09 - removed */

/*Feb05/09 - close file, inhibit if cmd line optn w1 for 1 file all writes*/
if ((opsi['w'-'a']) & 0x01)
  { ; }
else
  { closefileX(wfname,"W",&wfptr,&wfopn,0x00);
  }

if (nn == 0)
  { printf("EOF reached with NO records written \n");
    return(0);
  }
else
  { fileptr = fpsv;            /* so last written rec will be displayed*/
  }

return(1);
}

/*eject*/
/*---------------------------- writerec --------------------------------*/
/* write current record to output file                                  */

int writerec(void)
{
int ii;
int ss;
int nn;
int wrhs2;      /*Nov09/07 - recsize hdr lth 2 or 4 or 0 if option p   */
char wrsc[8];   /*Nov08/07 recsize numeric chars insert by option k1/k2*/

/* test options n1/n2 for 4 byte numeric recsize at begin/end data record */
wrsn1 = 0; wrsn2 = 0;
if (wargs.opi['n'-'a'] & 0x01)
  { wrsn1 = 4;
  }
else if (wargs.opi['n'-'a'] & 0x02)
  { wrsn2 = 4;
  }
wrsn3 = wrsn1 + wrsn2;  /* combine (4 or 0) for easier calcs below */

/*Feb07/09 - calc actual data size depending on input file type     */
/* - OK to subtract hdr sizes for both RDW & IDX (will be 0 if n/u) */
rsz1d = (rsz1 - rdwhs - vrhs);

/*eject*/
/* determine write rcsz - option w if spcfd, else current rcsz  */
if (wargs.opi['r'-'a'])
  { wrs1w = wargs.opi['r'-'a'];
    wrs1h = wargs.opi['r'-'a'];
  }
else
  { wrs1w = rsz1d;                       /* presume I/O write = data*/
    wrs1h = rsz1d;                       /* presume hdr size = data */
  }

/* test write options z2/z4 for RDW output            */
/* - set prefix size & store prefix in outrec area    */
if (wargs.opi['z'-'a'] & 0x02)
  { wrhs = 2;                        /* store RDW hdr size      */
    wrs1w += (2 + wrsn3);            /* I/O write size          */
    wrs1h += (0 + wrsn3);            /* value in RDW hdr same   */
    memcpy(wrecb,u2.cc,2);
  }
else if (wargs.opi['z'-'a'] & 0x04)
  { wrhs = 4;                        /* store RDW hdr size      */
    wrs1w += (4 + wrsn3);            /* I/O write size          */
    wrs1h += (4 + wrsn3);            /* value in RDW hdr same   */
    memcpy(wrecb,u2.cc,4);
  }
else
  { wrhs = 0;
  }

/*eject*/
/* create recsize prefixes in case of options z2/z4 with possible z1 */
/* - allow for BIG-end/littel-end machine                            */
/* - allow for option z1 to create little end format                 */
wrs1hs = wrs1h;                 /* move int recsize to short */
memset(u1.cc,'\0',4);           /* init union to switch ends */
memcpy(u1.cc,(char*)&wrs1hs,2); /* copy short into union     */
memcpy(u2.cc,u1.cc,4);          /* copy to output in case no switch req'd*/

/* if BEM - we are done (unlikely to use option z1) */
/* if LEM & option z1 we are done */
/* if LEM & not option z1, we need to switch ends   */
#if (LEM)
if (!(wargs.opi['z'-'a'] & 0x01))
  { u2.cc[0] = u1.cc[1];
    u2.cc[1] = u1.cc[0];
  }
#endif

/* calc input record data adrs = base adrs + any IDX or RDW prefix size */
irecd = (recb + vrhs + rdwhs);

/* calc adrs for out record data = base + z2/z4 offset + n1 offset */
wrecd = (wrecb + wrhs + wrsn1);

/*eject*/
/* clear wrec area to allow for variable length records  */
/* - clear min 2048 or rsz1 if larger                    */
if (rsz1 > 2048)
  { nn = rsz1; }
else
  { nn = 2048; }
/*Nov08/07 - clear to ASCII blank default, but EBCDIC blank if option a */
if (opsc['a'-'a'])
  { memset(wrecd,0x40,nn);       /* clear write area to EBCDIC blanks */
  }
else
  { memset(wrecd,0x20,nn);       /* clear write area to ASCII blanks  */
  }

/* store RDW hdr if any */
if (wargs.opi['z'-'a'] & 0x02)
  { memcpy(wrecb,u2.cc,2);
  }
else if (wargs.opi['z'-'a'] & 0x04)
  { memcpy(wrecb,u2.cc,4);
  }

/* copy input data to write output area */
/*Feb07/09 - copy just the data size from input record */
memcpy(wrecd,irecd,rsz1d);

/*Nov08/07 - test option a1 to translate write records to ASCII      */
if (wargs.opi['a'-'a'] & 0x01)
  { toascii2((Uchar*)wrecd,wrs1w,0x00);
  }
/*Apr16/08 (Uchar*) inserted for HP compile warning mismatch ptr types*/

/* translate any unprintable chars to '.' periods if option c1 */
if (wargs.opi['c'-'a'] & 0x01)
  { toprint2((Uchar*)wrecd,wrs1w,0x00);
  }

/*eject*/
/*Nov08/07 - insert recsize as 4 numeric characters if option n1/n2     */
/*           (n1 = insert at begin record, n2 = insert at end record)   */
/*Feb09/09 - fix write optn n1/n2, insert input size, not optn r outsize*/
/*         - assume user wants input data & not any optn r fixed size   */

/* calc adrs to insert option n1 numeric recsize (if any) */
wrecn1 = (wrecb + wrhs);

/* calc adrs for option n2 numeric recsize (if any) */
wrecn2 = (wrecb + wrs1w - 4);

/* decrement if option t1/t2 to insert CR/LF at end record */
if (wargs.opi['t'-'a'] & 0x01)
  { wrecn2--;
  }
if (wargs.opi['t'-'a'] & 0x02)
  { wrecn2--;
  }

sprintf(wrsc,"%04d",rsz1d);   /* convert input data size to digits */

/* if optin n1/n2 - insert write size at begin/end record */
if (wargs.opi['n'-'a'] & 0x01)
  { memcpy(wrecn1,wrsc,4); 
  }
else if (wargs.opi['n'-'a'] & 0x02)
  { memcpy(wrecn2,wrsc,4);
  }

/*eject*/
/* if option t4 - insert CR/LF terminator after last nonblank     */
/*Feb05/09 - optn y on cmd line chg to optn t on write cmd        */
/*Feb09/09 - fix write optn t4, scan back not writing shorter size*/
if (wargs.opi['t'-'a'] & 0x04)
  { for (ii=wrs1w-1; ii > 8; ii--)
      { if ((Uchar)wrecb[ii] <= 0x20)
          { ; }
        else
          { break; }
      }
    if (wargs.opi['t'-'a'] & 0x01)
      { wrecb[++ii] = '\r';
      }
    if (wargs.opi['t'-'a'] & 0x02)
      { wrecb[++ii] = '\n';
      }
    wrs1w = ii;       /*Feb09/09 - write size ending at LineFeed*/
  }
else /* not t4 after LNB - append CR/LF at end rec & increase recsz */
  {  /*Dec11/07 - ensure fixed size = optn w independent optns p,k  */
     /*         - but terminators extra depending optn t1,t2,t3     */
     /*Feb05/09 - terminator chgd from addon to within recsize      */
    if ((wargs.opi['t'-'a'] & 0x01) && (wargs.opi['t'-'a'] & 0x02))
      { wrecb[wrs1w-2] = '\r';
        wrecb[wrs1w-1] = '\n';
      }
    else if (wargs.opi['t'-'a'] & 0x01)
      { wrecb[wrs1w-1] = '\r';
      }
    else if (wargs.opi['t'-'a'] & 0x02)
      { wrecb[wrs1w-1] = '\n';
      }
  }

ss = fwrite(wrecb,1,wrs1w,wfptr);     /* write to output file#1     */
if (ss <= 0)
    errmsg("writing output file",wargs.cmd,wfname,0,0x40);

return(1);
}

/*Feb09/09 - CR/LR goes within record size, might clobber last bytes */
/* - might test optn 'r' OK within, else after & increase recsize    */
/* - OK if text file output, but not if RDW or IDX                   */
/* - would have to increase reclth in hdr                            */
/* - already calculated, switched ends, etc                          */

/*eject*/
/*----------------------------- update --------------------------------*/
/* update current record - return 1 if ok, 0 if any error              */
/*                   - sample commands follow:                         */
/*  u 0(3),='ABC'    - change 1st 3 bytes of current record to 'ABC'   */
/*  u 10(3),=x'0D0A' - change bytes 11&12 of current record to x'0D0A' */
/*  uu               - repeat prior update (on current record)         */

int update(void)
{
int uu;                         /* records updated counter          */
int nn;                         /* records read searching for updts */
int ss;                         /* op3/4 & op4/5 qualify status     */

uu = 0; nn = 0; fpsv = 0;       /* clear counters                   */

/* verify option u to allow update                                    */
if ((opsc['u'-'a']) == 0)
  { printf(">>>UPDATE requires option u on uvhd command line<<<\n");
    showhelp(help02,25,0x01);
    printf(">>>UPDATE requires option u on uvhd command line<<<\n");
    exit(9);
  }

/* save record for possible roll-back of last update to current record */
memcpy(recsav2,recb,rsz1);        /* save record for possible roll-back*/

/* save record for possible roll-back of multi updts to current record */
/* IE - save only if cur fileptr not = fileptr of last updt            */
if (fileptr != fplastup)
    memcpy(recsav1,recb,rsz1);   /* save rec for multi updt roll-back  */


/* test for new update or repeat previous update                      */
if (cms[1] == 'u')                      /* repeat prior update ?      */
  { /* trsfr any multi-rec count from uu999 to stored uargs structure */
    /* - to allow user to test update on 1 record, then repeat on all */
    uargs.cmv = cargs.cmv;              /* update multi-rec count     */
  }
else
  { /* store new search args - but 1st verify (cargs here, uargs below)*/
    if ((cargs.a1l <= 0) || (cargs.a2l <= 0))
      { errmsg("update args invalid",cargs.cmd,"",0,0x10);
        return(0);
      }
    if (cargs.a1l != cargs.a2l)
      { errmsg("update field lth NOT= constant lth",cargs.cmd,"",0,0x10);
        return(0);
      }
    uargs = cargs;                      /* store new search args      */
  }

/*eject*/
/* verify update args - op1 lth & op2 constant lth > 0 ?              */
if ((uargs.a1l <= 0) || (uargs.a2l <= 0))
  { errmsg("update args invalid",uargs.cmd,"",0,0x10);
    return(0);
  }

/* verify update args - op1 lth = op2 constant lth ?                 */
if (uargs.a1l != uargs.a2l)
  { errmsg("update field lth NOT= constant lth",uargs.cmd,"",0,0x10);
    return(0);
  }

/* instruction verified - test for multi-rec update or just current  */
/* 1 rec updt allows multi-field updts & roll-back, multirec does not*/
if (uargs.cmv == 0)
  {
    /* qualify record if op3/op4 &/or op5/op6 spcfd (may be AND/OR)       */
    ss = search34(recb,&uargs);         /* search for op3/4 &/or op5/6    */
    if (ss > 0)
      { /* move constant data into record                                 */
        sncopy(recb+uargs.a1d,uargs.a2c,uargs.a1l,0x00);

        /* rewrite the record & store fileptr for possible roll-back      */
        fileseek("update 1");

        writestat = write(fd1,recb,rsz1);
        if (writestat < 0)
            errmsg("updt write err",fn1,"",0,0x40);
        fplastup = fileptr;             /* save fileptr of last rec updtd */

        /* show update command - if uu vs original (already on screen)    */
        sprintf(opmsg,"above record updated - %s \n",uargs.cmd);
      }
  }

/*eject*/
else
 {/* multi-record update, from cur fileptr for count spcfd or til EOF*/
  /* u99 0(3),'ABC'                                                  */
  while(1)
   { rsz1 = getrec(0);
     if (rsz1 <= 0)
       { sprintf(opmsg,"EOF, %d records read, %d updated %s\n",nn,uu,uargs.cmd);
         /* if any records updated, set fileptr to last rec updated   */
         if (uu)
             fileptr = fpsv;
         else
             fileptr = fileptr1;
         break;
       }
     nn++;                              /* count recs read for max test*/

     /* qualify record if op3/op4 &/or op5/op6 spcfd (may be AND/OR)       */
     ss = search34(recb,&uargs);         /* search for op3/4 &/or op5/6    */

     if (ss < 0)
       { fileptr += rsz1;           /* up to next record in file   */
         continue;
       }

     /* update record - move constant data into record                 */
     sncopy(recb+uargs.a1d,uargs.a2c,uargs.a1l,0x00);

     /* seek to current record & rewrite                               */
     /* May2001: fseek changed to call fileseek (largefiles lseek/lseek64) */
     fileseek("update multiple");

     writestat = write(fd1,recb,rsz1); /*May2001 fwrite -> write for LFS */
     if (writestat < 0)
         errmsg("updt write err",fn1,"",0,0x40);

     uu++;                             /* count records updated           */
     fpsv = fileptr;                   /* save fileptr of last rec updated*/

     /* test for max record search limit reached                       */
     if (nn < uargs.cmv)
       { fileptr += rsz1;              /* up to next record in file    */
         continue;
       }
     else
       { sprintf(opmsg,"%d records read, %d updated %s\n",nn,uu,uargs.cmd);
         fileptr = fpsv;        /* so last updated rec will be displayed*/
         break;
       }
   }
 }
return(1);
}

/*eject*/
/*------------------------------- scanrep -------------------------------*/
/* command 'v' for variable length scan/replace                          */
/* scan an area for a pattern & replace with a 2nd pattern               */
/*                                                                       */
/* v 35(25),'ROAD','RD.'      - replace ROAD with RD. anywhere in 35-59  */
/* vv                         - repeat prior scan/rep (on current record)*/
/* v999 35(25),'STREET','ST.' - replace on next 999 records              */
/* v999 35(25),'AVE.','AVENUE',77(2),'BC' - replace only for BC          */
/* v999w 10(25),'x','x'       - option 'w' write sep file just cust names*/
/*                              tmp/custmas1_yymmdd_HHMMSSV              */

int scanrep(void)
{
int ss;                         /* scanrep return reps in current rec*/
int rr;                         /* total replacements made           */
int uu;                         /* records updated counter           */
int nn;                         /* records read searching for updts  */
int mm;                         /* op3/op4 search status             */
int oo;                         /* op5/op6 search status             */
int ii,jj,ll,ee;

rr=0; uu=0; nn=0; fpsv=0;         /* clear counters                   */

/* save record for possible roll-back of last update to current record */
memcpy(recsav2,recb,rsz1);        /* save record for possible roll-back*/

/* save record for possible roll-back of multi updts to current record */
/* IE - save only if cur fileptr not = fileptr of last updt            */
if (fileptr != fplastup)
    memcpy(recsav1,recb,rsz1);   /* save rec for multi updt roll-back  */


/* test for new scan/replace or repeat previous scan/replace           */
if (cms[1] == 'v')                      /* repeat prior scan/replace ? */
  { /* trsfr any multi-rec count from vv999 to stored vargs structure  */
    /* - to allow user to test scanrep on 1 record, then repeat on all */
    vargs.cmv = cargs.cmv;              /* update multi-rec count      */
  }
else
  { /* store new search args - but 1st verify (cargs here, vargs below)*/
    if ((cargs.a1l <= 0) || (cargs.a2l <= 0))
      { errmsg("scan/replace args invalid",cargs.cmd,"",0,0x10);
        return(0);
      }
    vargs = cargs;                      /* store new search args      */
  }

/*eject*/
/* verify option u to allow scan/replace */
/* - not required if option 'w' (write sep file) & count spcfd */
if ((vargs.opc['w'-'a']) && (vargs.cmv))
  { ; }
else if (!(opsc['u'-'a']))
  { printf(">>>scan/replace requires option u on uvhd command line<<<\n");
    showhelp(help02,25,0x01);
    printf(">>>scan/replace requires option u on uvhd command line<<<\n");
    exit(9);
  }

/* verify scan/replace args - op1 lth & op2 constant lth > 0 ?        */
if ((vargs.a1l <= 0) || (vargs.a2l <= 0))
  { errmsg("scan/replace args invalid",vargs.cmd,"",0,0x10);
    return(0);
  }

/* instruction verified - test for multi-rec scan/replace or just current  */
/* 1 rec updt allows multi-field updts & roll-back, multirec does not*/
if (vargs.cmv == 0)
 {
  /* call subfunction to perform scan/replace on op1 record field      */
  ss = scanrep1(recb,&vargs);

  /* rewrite updated record - but only if at least 1 replacement made  */
  if (ss)
    { fileseek("scan/replace 1");

      writestat = write(fd1,recb,rsz1); /*May2001 fwrite -> write for LFS */
      if (writestat < 0)
          errmsg("updt write err",fn1,"",0,0x40);
      fplastup = fileptr;               /* save fileptr of last rec updtd */

      /* show scan/replace command - if vv vs original (already on screen)*/
      sprintf(opmsg,"above record updated - %s \n",vargs.cmd);
    }
 }

/*eject*/
/* test for multi-record scan/replace (count spcfd v999)      */
/* v99 'ROAD','RD.' - replace ROAD with RD. anywhere in record*/

/*May16/08 - test option 'w' to write separate file*/
/* - most appropriate when processing a Text file  */
/* - search pattern lth != replace pattern lth     */
/* - multi-record scan/replace (already proven)    */
else if (vargs.opc['w'-'a']) 
  { /* open separate file for this scan/replace output */
    /* tmp/filename_yymmdd_HHMMSSV                     */

    /*Feb05/09 - call function to open file on each scan/replace */
    openfileX(vfname,"V",&vfptr,&vfopn,0x00);

    /* begin loop to get records, scan/replace,& write out */
    /* - until spcfd count reached or til EOF              */
    while(1)
     { rsz1 = getrec(0);
       if (rsz1 <= 0)
         { sprintf(opmsg,"EOF, %d reps in %d records, %s\n"
                        ,rr,uu,vargs.cmd);
           sprintf(opmsg2,"output file: %s\n",vfname);
           /*Feb05/09 - close file on each scan/replace */
           closefileX(vfname,"V",&vfptr,&vfopn,0x00);
           break;
         }
       nn++;                          /* count recs read for max test*/

       /*May16/08 - test indicator op1 dfltd to 0(rsz1)    */
       /*         - ifso set op1lth = rsz1 for each record */
       if (vargs.op1dflt)
         { vargs.a1l = rsz1;
         }

       /* qualify record - if op5/op6 patterns specified             */
       if (vargs.a6l)                 /* if op5/op6 pattern length   */
         { oo = search4(recb,&vargs);
           if (oo < 0)
             { fileptr += rsz1;       /* up to next record in file   */
               continue;
             }
         }

/*eject*/
      /*note - replacing unequal lths truncates or blank fills as reqd */
      memset(wa1,' ',8192);       /* clear work area to copy to        */
      ii = vargs.a1d;             /* init index to begin field dspt    */
      ee = vargs.a1d + vargs.a1l; /* calc end field index              */
      jj = 0;                     /* init index for work area copy     */
      ss = 0;                     /* init count scanreps this record   */

      /* begin loop to copy record field to blank filled work area      */
      /* - comparing each byte for match to search pattern on its length*/
      /* - replacing on matches & up I/O indices by respective lengths  */
      /* - else copy byte by byte until end of record field reached     */
      while(ii < ee)
        { if (memcmp(recb+ii,vargs.a2c,vargs.a2l) == 0)
            { memcpy(wa1+jj,vargs.a4c,vargs.a4l); /* copy replacement    */
              jj += vargs.a4l;                    /* up output by replth */
              ii += vargs.a2l;                    /* up input by search  */
              ss++;                               /* count reps this rec */
            }
          else
            { wa1[jj++] = recb[ii++];  /* copy current byte & up indices */
            }
        }

      ll = putlin0(vfptr,wa1,8192,'\n',0x0162);   /* write out */
      if (ll < 0)
        { errmsg("writing scan/replace output file",vfname,"",0,0x40);
        }
      uu++;                              /* count records written    */
      rr +=ss;                           /* count total replacements */

      /* test for max record search limit reached                    */
      if (nn < vargs.cmv)
        { fileptr += rsz1;              /* up to next record in file */
          continue;
        }
      else
        { sprintf(opmsg,"%d reps in %d records, %s\n"
                       ,rr,uu,vargs.cmd);
          sprintf(opmsg2,"output file: %s\n",vfname);
          closefileX(vfname,"V",&vfptr,&vfopn,0x00);
          break;
        }
    }
  }

/*eject*/
else
 {/* multi-record scan/replace, from cur fileptr for count spcfd or til EOF*/
  /* v99 35(25),'ROAD','RD.'      - replace ROAD with RD. anywhere in 35-59*/
  while(1)
   { rsz1 = getrec(0);
     if (rsz1 <= 0)
       { sprintf(opmsg,"EOF, %d read, %d reps in %d records, %s\n"
                      ,nn,rr,uu,vargs.cmd);
         /* if any records updated, set fileptr to last rec updated   */
         if (uu)
             fileptr = fpsv;
         else
             fileptr = fileptr1;
         break;
       }
     nn++;                              /* count recs read for max test*/

     /* qualify record - if op5/op6 patterns specified                 */
     if (vargs.a6l)                     /* if op5/op6 pattern length   */
       { oo = search4(recb,&vargs);
         if (oo < 0)
           { fileptr += rsz1;           /* up to next record in file   */
             continue;
           }
       }

     /* call subfunction to perform scan/replace on op1 record field      */
     ss = scanrep1(recb,&vargs);

     /* scanrep1 return status ss is replacements made in current record  */
     /* if any replacements made - seek to current record & rewrite       */
     if (ss)
       { fileseek("scan/replace multiple"); /* seek to current record  */
         writestat = write(fd1,recb,rsz1);  /* re-write current record */
         if (writestat < 0)
             errmsg("updt write err",fn1,"",0,0x40);
         fpsv = fileptr;                    /* save fileptr last rec updated*/
         uu++;                              /* count records updated        */
         rr +=ss;                           /* count total replacements     */
       }

     /* test for max record search limit reached                       */
     if (nn < vargs.cmv)
       { fileptr += rsz1;              /* up to next record in file    */
         continue;
       }
     else
       { sprintf(opmsg,"%d read, %d reps in %d records, %s\n"
                      ,nn,rr,uu,vargs.cmd);
         fileptr = fpsv;        /* so last updated rec will be displayed*/
         break;
       }
   }
 }
return(1);
}

/*eject*/
/*------------------------------- scanrep1 ------------------------------*/
/* scan an area for a pattern & replace with a 2nd pattern               */
/* - called twice by command 'v' for variable length scan/replace        */
/*                                                                       */
/* v 35(25),'ROAD','RD.'      <-- replace ROAD with RD. anywhere in 35-59*/
/*                                                                       */

int scanrep1(char *recb, struct CA *cap)
{
int ii,jj,ee,ss;

/*note - replacing unequal lengths will truncate or blank fill as reqd   */
memset(wa1,' ',cap->a1l);   /* clear work area to copy to                */
ii = cap->a1d;              /* init input index to begin field dsplcmnt  */
ee = cap->a1d + cap->a1l;   /* calc end field index                      */
jj = 0;                     /* init index for work area copy             */
ss = 0;                     /* init count of scanreps this rec (returned)*/

/* begin loop to copy record field to blank filled work area             */
/* - comparing from each byte for match to search pattern on its length  */
/* - replacing on matches & up I/O indices by respective lengths         */
/* - else copy byte by byte until end of record field reached            */
while(ii < ee)
  { if (memcmp(recb+ii,cap->a2c,cap->a2l) == 0)
      { memcpy(wa1+jj,cap->a4c,cap->a4l); /* copy replacement to w/a        */
        jj += cap->a4l;                   /* up output index by replace lth */
        ii += cap->a2l;                   /* up input index by search patlth*/
        ss++;                             /* count reps made this record    */
      }
    else
      { wa1[jj++] = recb[ii++];          /* copy current byte & up indices */
      }
  }

/* now copy work area back to record field */
memcpy(recb+cap->a1d,wa1,cap->a1l);

return(ss);                   /* return scanreps made in this record */
}

/*eject*/
/*--------------------- roll-back last update ------------------------*/
/* via oprtr cmd 'x'                                                  */

int back1(void)
{

/* verify current fileptr = fileptr of last updt                      */
if (fileptr != fplastup)
  { printf("rollback rejected - cur rec not same as last updt\n");
    return(0);
  }

/* May2001: fseek changed to call fileseek (largefiles lseek/lseek64) */
fileseek("rollback x");

writestat = write(fd1,recsav2,rsz1);   /* May2001 fwrite -> write for LFS*/
if (writestat <= 0)
    errmsg("rollback write err",fn1,"",0,0x40);

/* copy cur rec before rollback to rollback save area               */
/* - to allow toggling between updated & not updated                */
memcpy(recsav2,recb,rsz1);             /* update the save area      */

return(1);
}

/*eject*/
/*-------------------- roll-back all updts to cur rec ----------------*/
/* via oprtr cmd 'X'                                                  */

int backall(void)
{

/* verify current fileptr = fileptr of last updt                      */
if (fileptr != fplastup)
  { printf("rollback rejected - cur rec not same as last updt\n");
    return(0);
  }

/* May2001: fseek changed to call fileseek (largefiles lseek/lseek64) */
fileseek("rollback X");

writestat = write(fd1,recsav1,rsz1);    /*May2001 fwrite ->write LFS */
if (writestat <= 0)
    errmsg("rollback write err",fn1,"",0,0x40);

/* copy cur rec before rollback to rollback save area               */
/* - to allow toggling between updated & not updated                */
memcpy(recsav1,recb,rsz1);             /* update the save area      */

return(1);
}

/*eject*/
/*------------------------------ drop1 --------------------------------*/
/* drop records via specified pattern &/or by D-ISAM delete flag       */
/* - copies file to tmp/fname_yyyymmdd_hhmmssD dropping spcfd records  */
/*                                                                     */
/* d  8(1),'D'               - drop records with 'D' in column 9       */
/*                                                                     */
/* di 8(1),'D'               - drop records with 'D' in column 9       */
/*                             option 'i' to also drop ISAM deleteds   */
/*                                                                     */
/* d100i                     - copy 100 recs to tmp, dropping deleteds */
/*                                                                     */

int drop1(void)
{
int nn;                             /* max records search ctr       */
int mm;                             /* search find/nofind 0+/-      */
int dd,ii,ss;

/* test for 'd=s' transfer last search cmd to drop cmd storage structure */
if ((cms[1] == '=') && (cms[2] == 's'))
  { dargs = sargs;              /* store drop cmd from last search cmd */
    dargs.cmd[0] = 'd';         /* change cmd from 's' to 'd'           */
    dargs.cmc[0] = 'd';         /* change cmc from 's' to 'd'           */
    printf("drop args stored from last search: %s \n",dargs.cmd);
    printf("execute 'dd' to write to tmp dropping spcfd recs until EOF --> ");
    return(2);
  }

/* test for new drop command or repeat previous drop command (dd)    */
if (cms[1] == 'd')                      /* repeat prior drop ?       */
  { /* allow value updt on repeat cmd - dd99                         */
    if (cargs.cmv)
        dargs.cmv = cargs.cmv;
  }
else
  { /* store new drop args                                           */
    dargs = cargs;                      /* store new drop args       */
  }

/* create tmp dir if not already present                             */
if (stat("tmp",&tmpstat) == -1)
    system("mkdir tmp");

/* open drop output filename                                         */
/* - will overwrite if drop repeated (no collection like write)      */
openfileX(dfname,"D",&dfptr,&dfopn,0x00);
drops++;                         /* count drops cmds (set switch)    */

/*eject*/
/* copy to output file from current position                          */
/* - drop recs via specified pattern (if any)                         */
/* - drop recs via D-ISAM delete flag x'00' last byte (if optn 'i')   */
nn = 0; dd = 0; ii = 0;                /* clear record counters       */
while (1)
 { rsz1 = getrec(0);
   if (rsz1 <= 0)                       /* EOF ?                      */
     { fileptr = filesiz0;
       break;
     }

   /* if pattern spcfd - bypass matching records                      */
   if (dargs.a2l)
     { mm = search234(recb,&dargs);   /* 9701 search = > < !              */
       if (mm >= 0)
         { dd++;                      /* count records dropped by pattern*/
           fileptr += rsz1;           /* advance to next record          */
           continue;                  /* repeat loop to get next rec     */
         }
     }

   /* if option 'i' spcfd - drop if x'00' in last byte                    */
   if (dargs.opc['i'-'a'])
     { if (recb[rsz1-1] == '\0')
         { ii++;                      /* count recs dropped by ISAM flag*/
           fileptr += rsz1;           /* advance to next record         */
           continue;                  /* repeat loop to get next rec    */
         }
     }

   /* No delete flags - copy current record to tmp/drop file            */
   ss = fwrite(recb,1,rsz1,dfptr);    /* copy to tmp/drop file          */
   if (ss <= 0)
      errmsg("copying/dropping to output file",dargs.cmd,"",0,0x40);
   nn++;                              /* count output records          */

   /* test max copy count reached, if so display confirmation & return  */
   if ((dargs.cmv) && (nn >= dargs.cmv))
     { break;
     }

   /* increment fileptr to next record & repeat loop          */
   fileptr += rsz1;                 /* advance to next record */

 }

/* copy loop ended by max count, or EOF - display stats & close file */
sprintf(opmsg,"%d copied, %d dropped, %d dropped(ISAM), %s\n"
       ,nn,dd,ii,dfname);

closefileX(dfname,"D",&dfptr,&dfopn,0x00);  /* close drop output file */

return(1);
}

/*eject*/
/*------------------------------ check1 ---------------------------------*/
/* check sequence - display 1st record not in sequence                   */
/*  c  0(6)       - check sequence of cols 1-6                           */

int check1(void)
{
int cc,nn;
int dd,ll;       /* save seqnum field dsplcmnt & length */
int ii;          /* option i value */

/* test new chkseq cmd or repeat previously stored cmd (cc)             */
if (cms[1] == 'c')
   ;
else
  { /* store new chkseq args - 1st verify args                          */
    if (cargs.a1l <= 0)
      { errmsg("check sequence cmd must specify field dsplcmnt(lth)"
               ,cargs.cmd,"",0,0x10);
        return(0);
      }
    kargs = cargs;                 /* store chkseq args                 */
  }

/* verify chkseq args - op1 lth > 0                                     */
if (kargs.a1l <= 0)
  { errmsg("check sequence cmd must specify field dsplcmnt(lth)"
           ,kargs.cmd,"",0,0x10);
    return(0);
  }

/* init seq chk from current record util EOF (or seq err)              */
/* get current record & save seqchk field for compare to following rec */
rsz1 = getrec(0);                 /* get 1st rec for chk seq loop      */
if (rsz1 <=0)
  { fileptr = fileptr1;           /* ensure fileptr not past EOF       */
    errmsg("check seq at EOF already ?, reset to BOF & retry"
           ,kargs.cmd,"",0,0x10);
    return(0);
  }

dd = kargs.a1d;                    /* save seqnum field dsplcmnt    */
ll = kargs.a1l;                    /* save seqnum field length      */
memcpy(chksav1,recb+dd,ll);        /* save seqnum field from 1st rec*/

/*eject*/
/* begin loop to check sequence - until EOF (or seq err)               */
while (1)
  { fileptr += rsz1;                /* up fileptr to next record       */
    rsz1 = getrec(0);               /* get next rec for chk seq loop   */
    if (rsz1 <=0)
      { fileptr = fileptr1;         /* ensure fileptr not past EOF     */
        sprintf(opmsg,"%s --> sequence OK, reached EOF with no errors\n"
               ,kargs.cmd);
        return(1);
      }

    memcpy(chksav2,recb+dd,ll);     /* get seqnum from current record  */

    /* test option to check by specified increment - option i1,i10,etc */
    if (kargs.opc['i'-'a'] == 'i')
      { ii = kargs.opi['i'-'a'];    /* save seq# option increment */
        if (ii == 0)
          { ii = 1;                 /* default to 1 if unspcfd    */
          }
        chksav1i = atol2(chksav1,'~',ll,0x01); /* cnvt prior rec# to int*/
        chksav2i = atol2(chksav2,'~',ll,0x01); /* cnvt current rec# to int*/
        chksav1i += ii;                        /* prior val + incrmnt   */
        if (chksav1i != chksav2i)
          { break;
          }
      }
    else
      { /* character compare current record seq field to save area     */
        cc = memcmp(chksav2,chksav1,ll);
        if ((cc < 0) && (kargs.opc['d'-'a'] != 'd'))
            break;
        if ((cc > 0) && (kargs.opc['d'-'a'] == 'd'))
            break;
        if ((cc == 0) && (kargs.opc['e'-'a'] == 'e'))
            break;
      }

    /* current rec OK - save seq fld (for == chk) & repeat loop        */
    memcpy(chksav1,chksav2,ll); /* save current seqnum for next rec chk*/
  }

/* loop broken by out of sequence - display errmsg & return            */
sprintf(opmsg,"%s ERROR, 1st out of sequence record above\n",kargs.cmd);
return(1);                        /* return (1) to show next record    */
}

/*eject*/
/*---------------------------- enumerate -------------------------------*/
/* enumerate(count) records (from current record to EOF or within max spcfd*/
/* - or records matching a pattern, to a specified maximum              */
/*                                                                      */
/* t                 - count recs from current point to EOF             */
/*                                                                      */
/* t500 0(80),='ABC' - count records with 'ABC' anywhere in 1st 80 bytes*/
/*                     (count from current record for 500 max or EOF)   */
/*                                                                      */
/* tt                        - repeat previous count                    */
/*                                                                      */
/* - if nofind set fileptr to last rec in file & return 0               */

int enum1(void)
{
int nn;                             /* max records search ctr       */
int mm;                             /* search find/nofind 0+/-      */
int tt;                             /* enumerate counter            */

nn = 0; tt = 0; fpsv = 0;           /* clear counters               */

/* test for 'e=s' transfer last search cmd to enumerate cmd storage structure */
if ((cms[1] == '=') && (cms[2] == 's'))
  { eargs = sargs;              /* store enum cmd from last search cmd  */
    eargs.cmd[0] = 'e';         /* change cmd from 's' to 'e'           */
    eargs.cmc[0] = 'e';         /* change cmc from 's' to 'e'           */
    printf("enumerate args stored from last search: %s \n",eargs.cmd);
    printf("execute 'ee' to enumerate spcfd records from current to EOF --> ");
    return(2);
  }

/* test for new enumerate command or repeat previous enumerate command (ee)*/
if (cms[1] == 'e')                      /* repeat prior enumerate ?        */
  { /* allow value updt on repeat cmd - tt99                               */
    if (cargs.cmv)
        eargs.cmv = cargs.cmv;
  }
else
  { /* store new enumerate args                                          */
    eargs = cargs;                      /* store new enumerate args      */
  }

/*eject*/
/* enumerate (count) recs matching any spcfd patterns                  */
/* - within max recs (if spcfd), or until EOF                          */
while (1)
 { rsz1 = getrec(0);
   if (rsz1 <= 0)                     /* EOF ?                         */
     { fileptr = fileptr1;
       break;
     }
   nn++;                              /* count records read            */

   /* if enumerate pattern spcfd - search & count matching recs to max cnt*/
   if (eargs.a2l)
     { mm = search234(recb,&eargs);   /* search for patterns spcfd       */
       if (mm >= 0)
         { tt++;                      /* count matching records          */
           fpsv = fileptr;            /* save fileptr of last match      */
         }
     }
   else
     { tt++;                          /* count all recs (no pattern spcfd)*/
       fpsv = fileptr;                /* save fileptr of last counted    */
     }

   /* test max enumerate limit reached, if so break read loop now    */
   if ((eargs.cmv) && (nn >= eargs.cmv))
     { break;
     }

   /* increment fileptr to next record & repeat loop                 */
   fileptr += rsz1;                 /* advance to next record        */

 }

/* enumerate loop ended by max count, or EOF                         */
/* - store results msg to be displayed after last rec read           */
sprintf(opmsg,"%s --> %d recs counted of %d read\n",eargs.cmd,tt,nn);

/* if pattern search found any matches, set fileptr to last match    */
if (eargs.a2l && tt)
  { fileptr = fpsv;
  }

return(1);
}

/*eject*/
/*----------------------------- translate --------------------------------*/
/* translate current record - return 1 if ok, 0 if any error              */
/*                   - sample commands follow:                            */
/*  tt               - repeat prior translate (on current record)         */

int trans(void)
{
int tt;                         /* records translated counter       */
char *ss;

tt = 0; fpsv = 0;               /* clear counters                   */

/* verify command line option u to allow translate                    */
if ((opsc['u'-'a']) == 0)
  { printf(">>>Translate requires option u on uvhd command line<<<\n");
    showhelp(help02,25,0x01);
    printf(">>>Translate requires option u on uvhd command line<<<\n");
    exit(9);
  }

/* save record for possible roll-back of last update to current record */
memcpy(recsav2,recb,rsz1);        /* save record for possible roll-back*/

/* save record for possible roll-back of multi translates to current record */
/* IE - save only if cur fileptr not = fileptr of last translate            */
if (fileptr != fplastup)
    memcpy(recsav1,recb,rsz1);   /* save rec for multi translate roll-back  */


/* test for new translate or repeat previous translate                   */
if (cms[1] == 't')                      /* repeat prior translate ?      */
  { /* trsfr any multi-rec count from tt999 to stored targs structure    */
    /* - to allow user to test translate on 1 record, then repeat on all */
    targs.cmv = cargs.cmv;              /* translate multi-rec count     */
  }
else
  { /* default op1 to entire record if not specified */
    if (cargs.a1l <= 0)
      { cargs.a1l = rsz1;
      }
    targs = cargs;                      /* store new translate args  */
  }

/* verify translate option - present & 1 of a,e,u,l,c,p */
ss = strchr("aeulcp",targs.cmo[0]);
if ((targs.cmo[0]) && (ss))
  { ;
  }
else
  { errmsg("translate option must be 1 of a,e,u,l,c,p",targs.cmd,"",0,0x10);
    return(0);
  }

/*eject*/
/* test for multi-rec translate or just current                      */
/* 1 rec translate allows multi-field translates & roll-back, multi does not*/
if (targs.cmv <= 0)
  {
    tran1();                           /* translate current record   */
    tt++;                              /* count records translated(1)*/

    /* rewrite the record & store fileptr for possible roll-back     */
    fileseek("translate 1");

    writestat = write(fd1,recb,rsz1);
    if (writestat < 0)
        errmsg("translate write err",fn1,"",0,0x40);
    fplastup = fileptr;             /* save fileptr of last rec translated */

    /* show translate command - if tt vs original (already on screen) */
    sprintf(opmsg,"above record translated --> %s \n",targs.cmd);
  }

/*eject*/
else
 {/* multi-record translate, from cur fileptr for count spcfd or til EOF*/
  while(1)
   { rsz1 = getrec(0);
     if (rsz1 <= 0)
       { sprintf(opmsg,"EOF, %d records translated %s\n",tt,targs.cmd);
         /* if any records translated, set fileptr to last rec translated */
         if (tt)
             fileptr = fpsv;
         else
             fileptr = fileptr1;
         break;
       }
     tran1();                           /* translate current record    */
     tt++;                              /* count recs translated for max test*/

     /* seek to current record & rewrite                               */
     fileseek("translate multiple");

     writestat = write(fd1,recb,rsz1); /*May2001 fwrite -> write for LFS */
     if (writestat < 0)
         errmsg("translate write err",fn1,"",0,0x40);

     fpsv = fileptr;                   /* save fileptr of last rec translated*/

     /* test for max record translate limit reached                    */
     if (tt < targs.cmv)
       { fileptr += rsz1;              /* up to next record in file    */
         continue;
       }
     else
       { sprintf(opmsg,"%d records translated %s\n",tt,targs.cmd);
         fileptr = fpsv;        /* so last translate rec will be displayed*/
         break;
       }
   }
 }
return(1);
}

/*eject*/
/*------------------------------ tran1 ----------------------------------*/
/* translate current record - called by single & multiple translates     */

int tran1(void)
{
int dd,ll,ee;      /* op1 dsplcmnt,length,end from targs structure */
int opi;           /* numeric value (bits) from option 'c' or 'p'  */
char opc;          /* translate option a,e,u,l,c,p                 */
char clr;          /* unprintables clear char (blank or period)    */
int ii;

/* store dsplcmnt, length, options,& clear char from targs structure */
dd = targs.a1d;
ll = targs.a1l;
ee = dd + ll;
opc = targs.cmo[0];
if (opc == 'c')
  { opi = targs.opi['c'-'a'];
    clr = ' ';
  }
if (opc == 'p')
  { opi = targs.opi['p'-'a'];
    clr = '.';
  }

/* test translate option a,e,u,l,c,p */
if (opc == 'a')
  { toascii2((Uchar*)recb+dd,ll,0x00);
  }
else if (opc == 'e')
  { toebcdic2((Uchar*)recb+dd,ll,0x00);
  }
else if (opc == 'u')
  { toupper2(recb+dd,ll,0x00);
  }
else if (opc == 'l')
  { tolower2(recb+dd,ll,0x00);
  }

/*eject*/
else /* must be c or p */
  { /* copy neutral translate table to work area                      */
    /* & modify unprintables to blanks or periods depending on c or p */
    memcpy(trtc,trtn,256);
    memset(trtc,clr,32);
    memset(trtc+127,clr,129);

    /* restore LF,CR,FF,Tab depending on option bits                */
    if (opi & 0x01)
        trtc[10] = 0x0A;
    if (opi & 0x02)
        trtc[13] = 0x0D;
    if (opi & 0x04)
        trtc[12] = 0x0C;
    if (opi & 0x08)
        trtc[9] = 0x09;

    /* now translate the op1 area */
    for (ii = dd; ii < ee; ii++)
      { recb[ii] = trtc[(unsigned char)(recb[ii])];
      }
  }

return(1);
}

/*eject*/
/*----------------------------- seqgen --------------------------------*/
/* generate sequence number records - return 1 if ok, 0 if any error   */
/*                                                                     */
/*  g9999i10 70(6),'#1000_'   <-- example#1                            */
/*  - inserts any non-numeric constants before &/or after seqgen digits*/
/*  - starting sequence# may be specified                              */
/*  - will seq# in contiguos digit locations spcfd                     */
/*  - may specify desired increment with option i (10 above)           */
/*  - must specify max number of records to sequence# (else only 1)    */
/*                                                                     */
/*  g9999    70(6),'A00001',0(1),'A'  <-- example #2                   */
/*  - inserts seq# in records with 'A' in 1st byte                     */
/*                                                                     */
/*  gg               - repeat prior seqgen (on current record)         */

int seqgen(void)
{

int uu;                    /* records updated counter               */
int nn;                    /* records read searching for updts      */
int ss;                    /* op3/4 & op4/5 qualify status          */
int dd;                    /* dsplcmnt to 1st digit in op1 constant */
int ll;                    /* length of seqgen 1st digit to nondigit*/
int ii;
int seqgeni;               /* seqgen increment (dflt 1 or optn i)   */
int seqgenv;               /* seqgen ctr value integer              */
char seqgenc[15];          /* seqgen ctr value characters           */

uu = 0; nn = 0; fpsv = 0;         /* clear counters                   */

/* verify option u to allow seqgen                                    */
if ((opsc['u'-'a']) == 0)
  { printf(">>>generate seqnum requires option u on uvhd command line<<<\n");
    showhelp(help02,25,0x01);
    printf(">>>generate seqnum requires option u on uvhd command line<<<\n");
    exit(9);
  }

/* test for new seqgen or repeat previous seqgen                      */
if (cms[1] == 'g')                      /* repeat prior seqgen ?      */
  { /* trsfr any multi-rec count from gg999 to stored gargs structure */
    /* - to allow user to test seqgen on 1 record, then repeat on all */
    gargs.cmv = cargs.cmv;              /* seqgen multi-rec count     */
  }
else
  { /* store new search args - but 1st verify (cargs here, gargs below)*/
    if ((cargs.a1l <= 0) || (cargs.a2l <= 0))
      { errmsg("generate seqnum args invalid",cargs.cmd,"",0,0x10);
        return(0);
      }
    if (cargs.a1l != cargs.a2l)
      { errmsg("seqnum field lth NOT= constant lth",cargs.cmd,"",0,0x10);
        return(0);
      }
    gargs = cargs;                      /* store new search args      */
  }

/*eject*/
/* verify seqgen args - op1 lth & op2 constant lth > 0 ?              */
if ((gargs.a1l <= 0) || (gargs.a2l <= 0))
  { errmsg("seqnum args invalid",gargs.cmd,"",0,0x10);
    return(0);
  }

/* verify seqgen args - op1 lth = op2 constant lth ?                 */
if (gargs.a1l != gargs.a2l)
  { errmsg("generate seqnum field lth NOT= constant lth",gargs.cmd,"",0,0x10);
    return(0);
  }

/* store seqgen increment from option 'i' or default to 1          */
if (gargs.opi['i'-'a'])
  { seqgeni = gargs.opi['i'-'a'];  /* store seqgen inc from optn i */
  }
else
  { seqgeni = 1;                   /* default seqgen increment to 1*/
  }

/* extract initial value for seqgen from op1 constant          */
seqgenv = atol2(gargs.a2c,'~',15,0x01);

/* determine seqgen dsplcmnt & length by scanning op1 constant */
/* - for 1st numeric & then 1st non-numeric                    */
for (ii=0; gargs.a2c[ii]; ii++)
  { if (isdigit(gargs.a2c[ii]))
      { break;
      }
  }
dd = ii;       /* save dsplcmnt of 1st digit within op1 constant */

for ( ; gargs.a2c[ii]; ii++)
  { if (!isdigit(gargs.a2c[ii]))
      { break;
      }
  }
ll = ii - dd;  /* save lth of seqgen */

/* verify seqgen lth from 1 to 10 digits */
if ((ll > 10) || (ll < 1))
  { errmsg("sequence number digits min 1 max 10",gargs.cmd,"",0,0x10);
    return(0);
  }

/*eject*/
/* multi-record seqgen, from cur fileptr for count spcfd or til EOF*/
/* u99 0(3),'ABC'                                                  */
while(1)
  { rsz1 = getrec(0);
    if (rsz1 <= 0)
      { sprintf(opmsg,"EOF, %d records read, %d seqnumd %s\n",nn,uu,gargs.cmd);
        /* if any records seqgend, set fileptr to last rec seqgend   */
        if (uu)
            fileptr = fpsv;
        else
            fileptr = fileptr1;
        break;
      }
    nn++;                              /* count recs read for max test*/

    /* qualify record if op3/op4 &/or op5/op6 spcfd (may be AND/OR)       */
    ss = search34(recb,&gargs);         /* search for op3/4 &/or op5/6    */

    if (ss < 0)
      { fileptr += rsz1;           /* up to next record in file   */
        continue;
      }

    /* move any constant data before &/or after seqgen into record      */
    /* - then move the current value of seqgen ctr into digit positions */
    sncopy(recb+gargs.a1d,gargs.a2c,gargs.a1l,0x00);  /* store contants */
    sprintf(seqgenc,"%010d",seqgenv);          /* convert int to chars  */
    sncopy(recb+gargs.a1d+dd,seqgenc+10-ll,ll,0x00); /* insert seqgen digits*/

    /* seek to current record & rewrite                               */
    /* May2001: fseek changed to call fileseek (largefiles lseek/lseek64) */
    fileseek("seqgen multiple");

    writestat = write(fd1,recb,rsz1); /*May2001 fwrite -> write for LFS */
    if (writestat < 0)
      { errmsg("updt write err",fn1,"",0,0x40);
      }
    fpsv = fileptr;                   /* save fileptr of last rec seqgend */

    /* increment seqgen, count records update, & test for max search limit*/
    seqgenv += seqgeni;               /* increment by optn i or dflt 1    */
    uu++;                             /* count records seqgend            */

    /* test for max record search limit reached                       */
    if (nn < gargs.cmv)
      { fileptr += rsz1;              /* up to next record in file    */
        continue;
      }
    else
      { sprintf(opmsg,"%d records read, %d seqnumd %s\n",nn,uu,gargs.cmd);
        fileptr = fpsv;        /* so last seqgend rec will be displayed*/
        break;
      }
  }

return(1);
}

/*eject*/
/*----------------------------- switchb -------------------------------*/
/* return record slot size from ptr to 2 or 4 byte rec hdr             */
/* switch big-end to little-end if INTEL, SCO (not SUN, HP, etc)       */
/* - stores record-type, record-size,& slot-size                       */
/* - calc slot size = rcsz + 2 or +4 (if maxrcs>=4095) +3 &0x0000FFFC  */
/* - arg2 bits indicates 2 or 4 byte rec hdr                           */
/* - apply max & min rules to vrsize & vrslot                          */
/*note: switchb return value used only for min & max at file open      */

int switchb(char *vhdr, short bits)
{
/* work area unions to switch ends */
/* union INTC u1;  */
/* union INTC u2;  */
/*Mar2003 - unions u1 & u2 moved to global from switchb subfunction    */
/*        - was getting erroneous 'usage before def' msg from lcc-win32*/

int mm;

memset(u1.cc,'\0',4);          /* clear work area union                */

if (bits & 0x01)               /* if input rec hdr 4 vs 2 ?            */
  { memcpy(u1.cc,vhdr,4);      /* store 4 byte rec type & size         */
    vrtyp = u1.cc[0];          /* isolate rec type                     */
    vrtyp &= 0xF0;             /* clear digit (type in zone only)      */
    /* limit max rcsz to 65K (clear 1st 2 bytes of 4 byte rec size)    */
    u1.cc[0] = 0x00;
    u1.cc[1] = 0x00;
  }
else
  { memcpy(u1.cc+2,vhdr,2);    /* store 2 byte rec type & size         */
    vrtyp = u1.cc[2];          /* isolate rec type                     */
    vrtyp &= 0xF0;             /* clear digit (type in zone only)      */
    u1.cc[2] &= 0x0F;          /* clear zone (type) from rcsz          */
  }

/* switch big-end to little-end if INTEL or SCO (not SUN, HP, etc)     */
#if (LEM)
    u2.cc[0] = u1.cc[3];
    u2.cc[1] = u1.cc[2];
    u2.cc[2] = u1.cc[1];
    u2.cc[3] = u1.cc[0];
#else
    memcpy(u2.cc,u1.cc,4);
#endif

vrsize = u2.ii;                /* store record size                    */

/*Feb2003 - compiling on windows lcc-win32 get folwng errmsg ??        */
/* warning - uvhd.c line 3852 possible usage of u2 before definition   */
/*         - executable seems to work OK                               */

/*eject*/
/* enforce min & max rcsz */
/*Dec2002 - disable, problem on file hdr rec if max recsz < 128       */
/* if (vrsize < vrmin); vrsize = vrmin; */
/* if (vrsize > vrmax); vrsize = vrmax; */

/* calc slot size = rcsz + 2 or 4(if maxrcsz>=4095) + roundup to multiple 4*/
vrslot = vrsize + vrhs;
mm = (vrslot % 4);
if (mm)
  { vrslot += (4 - mm);
  }

return(u2.ii);
}

/*eject*/
/*----------------------------- switchx -------------------------------*/
/* return record slot size from ptr to 2 or 4 byte rec hdr             */
/* switch big-end to little-end if INTEL, SCO (not SUN, HP, etc)       */
/* - stores record-type, record-size,& slot-size                       */
/* - calc slot size = rcsz + 2/4 rechdrsize + roundup to multiple of 4 */
/* - arg2 bits indicates 2 or 4 byte rec hdr                           */
/* - apply max & min rules to vrsize & vrslot                          */
/* - switchx return value used for min & max at file open              */
/*Feb23/07 - fix reading IDXFORMAT8 switchx roundup multiple of 4 not 8*/

int switchx(char *vhdr, short bits)
{
/* work area unions to switch ends */
/* union INTC u1;  */
/* union INTC u2;  */
/*Mar2003 - unions u1 & u2 moved to global from switchx subfunction    */
/*        - was getting erroneous 'usage before def' msg from lcc-win32*/

int mm;

memset(u1.cc,'\0',4);          /* clear work area union                */

if (bits & 0x01)               /* if input rec hdr 4 vs 2 ?            */
  { memcpy(u1.cc,vhdr,4);      /* store 4 byte rec type & size         */
    vrtyp = u1.cc[0];          /* isolate rec type                     */
    vrtyp &= 0xF0;             /* clear digit (type in zone only)      */
    /* limit max rcsz to 65K (clear 1st 2 bytes of 4 byte rec size)    */
    u1.cc[0] = 0x00;
    u1.cc[1] = 0x00;
  }
else
  { memcpy(u1.cc+2,vhdr,2);    /* store 2 byte rec type & size         */
    vrtyp = u1.cc[2];          /* isolate rec type                     */
    vrtyp &= 0xF0;             /* clear digit (type in zone only)      */
    u1.cc[2] &= 0x0F;          /* clear zone (type) from rcsz          */
  }

/* switch big-end to little-end if INTEL or SCO (not SUN, HP, etc)     */
#if (LEM)
    u2.cc[0] = u1.cc[3];
    u2.cc[1] = u1.cc[2];
    u2.cc[2] = u1.cc[1];
    u2.cc[3] = u1.cc[0];
#else
    memcpy(u2.cc,u1.cc,4);
#endif

vrsize = u2.ii;                /* store record size                    */

/*Feb2003 - compiling on windows lcc-win32 get folwng errmsg ??        */
/* warning - uvhd.c line 3852 possible usage of u2 before definition   */
/*         - executable seems to work OK                               */

/*eject*/
/* enforce min & max rcsz */
/*Dec2002 - disable, problem on file hdr rec if max recsz < 128       */
/* if (vrsize < vrmin); vrsize = vrmin; */
/* if (vrsize > vrmax); vrsize = vrmax; */

/* calc slot size = rcsz + rechdrsize (2/4) + roundup to multiple 4   */
vrslot = vrsize + vrhs;
mm = (vrslot % 4);
if (mm)
  { vrslot += (4 - mm);
  }

return(u2.ii);
}

/*eject*/
/*----------------------------- trunc1 -----------------------------------*/
/* adjust filesize (truncate or extend with x'00's)                       */
/*                                                                        */
/* z100000   - set filesize to 100,000 bytes                              */
/*                                                                        */
/* - 'z' truncate function not handled by uvhd.exe (compiled by lcc-win32)*/
/* - truncate is defined by #include unistd.h on unix/linux (n/a windows) */
/* - bypassed (with errmsg) when compiled on windows by testing DWIN      */

int trunc1(void)
{
int ss;

if (!opsc['u'-'a'])
   errmsg("z (truncate) requires update option u on cmd line"
          ,cargs.cmd,fn1,0,0x21);

close(fd1);                    /*May2001 fclose -> close for LFS     */

/* store new filesize, calc recs & remndr, display results, continue ?*/
filesize = cmn;                /* store new filesize                  */
chkrcsz(rszo);                 /* check filesize/rcsz evenly divisible*/
sprintf(fsedit,E64,filesize);  /* re-edit allowing for 32/64 bits     */
printf("filesize=%s recsize=%d, fs/rs=%d, remndr=%d (s/b 0) OK ? y/n --> "
        ,fsedit,rszo,frecsp,remndr);
/* filesize=E64=%lld */
fgets(args,80,stdin);          /* wait for oprtr reply                */
if (args[0] != 'y')
  { errmsg("truncate aborted by operator request",cargs.cmd,"",0,0x21);
  }

#if defined(DWIN)              /* compiled on windows -DDWIN on cmd line*/
errmsg("truncate NOT supported on Windows version (uvhd.exe)"
       ,cargs.cmd,"",0,0x21);
ss = 0;                        /* - disable truncate on windows         */
#else
ss = truncate(fn1,cmn);        /* truncate file to specified size       */
#endif

if (ss < 0)
   errmsg("z (truncate) attempt returned fail status "
          ,cargs.cmd,fn1,0,0x21);

openfile();                    /* re-open the file                    */

chkrcsz(rszo);                 /* check filesize/rcsz evenly divisible*/
fileptr = fileptr1;            /* reset fileptr to last rec in file   */

return(1);
}

/*eject*/
/*--------------------------- open file -------------------------------*/
/* open input file, get file status, file size,& modification date     */
/* - coded as subfunction, since also used by 'z' truncate command     */
/* if filesize zero, set high - to allow uvhd on tape files            */

int openfile(void)
{
/* 1st get file size, using 'stat' function */
/*Dec07/07 - for lcc-win32, use _stati64    */
#if defined(LF64) && defined(DWIN)
statstat = _stati64(fn1,&fstat1);
#elif defined(LF64)
statstat = stat64(fn1,&fstat1);
#else
statstat = stat(fn1,&fstat1);
#endif

if (statstat < 0)
     errmsg("file status error",fn1,"",0,0x40);

filesize = fstat1.st_size;       /* store filesize                     */
if (filesize <= 0)               /* filesize 0 ?                       */
  { filesize = 999999999;        /* yes - set high to allow tape files */
  }

/*Nov12/07 - set fileptr1s=filesize to allow recnum1 inhibit until EOF */
/* - then to inhibit recnum1 incrmnt until user enters non-null cmd    */
fileptr1s = filesize;            /* allow recnum1 incrmnt until EOF    */

cnvdttm(fmdate,fstat1.st_mtime); /* store last mod date                */
fmdate[10] = '\0';               /* drop seconds                       */

/* set file flags depending on option u (update)                       */
if (opsc['u'-'a'])
    openflags = O_RDWR;          /* presume O_RDWR (2) update ?        */
else
    openflags = O_RDONLY;        /* no optn u - change O_RDONLY (0)    */

/*Mar2003 - DWIN version requires O_BINARY flag                        */
#ifdef DWIN
openflags |= O_BINARY;           /* add binary flag for lcc-win32      */
#endif

/*eject*/
/* open file                                                           */
/*May2001 - provide largefile support -D LF64                          */
/*        - fopen changed to open/open64 for lseek/lseek64 (no fopen64)*/
/*Dec07/07 - open64 N/R for lcc-win32 */
#if defined(LF64) && !defined(DWIN)
fd1 = open64(fn1,openflags,0);
#else
fd1 = open(fn1,openflags,0);
#endif

if (fd1 < 0)
     errmsg("cant open file",fn1,"",0,0x40);

/*May2001: get file descriptor from FILE ptr                          */
/* fd1 = fileno(fp1);  |* get filedes from fileptr <-- NOneed May2001 */

/* edit filesize allowing for LargeFiles 32/64 bit integers           */
/* - for convenience of other code displaying filesize                */
sprintf(fsedit,E64,filesize);

return(1);
}

/*eject*/
/*--------------------------- fileseek --------------------------------*/
/* May2001 - fileseek calls lseek or lseek64                           */
/* - always seeks to fileptr variable                                  */
/* - returns only if no error detected                                 */

int fileseek(char *emsg)
{
int sizeoffp;
UVi64 fileptrseek;

/* May 2001 - for largefiles, use lseek/lseek64 vs fseek           */
/* status = fseek(fp1,fileptr,0);    <-- fseek uses file PTR       */
/* status = lseek(fd1,fileptr,0);    <-- lseek uses file DSCRPTR   */
/* status = lseek64(fd1,fileptr,0);  <-- lseek uses file DSCRPTR   */

/*May9/2001 - having problems debuggung for largefiles             */
/* - for debug: add msg below displayed on lseek64 ??              */
/* sizeoffp = sizeof(fileptr); */
/* printf("DEBUG: lseek64 fileptr=%lld, sizeoffileptr=%d\n",fileptr,sizeoffp);*/

/*Dec07/07 - for lcc-win32, call lseeki64                 */
/*Dec07/07 - we bomb out when we reach the 2 gig limit    */
/*Dec09/07 - fixed by new lseeki64 modified from fseeki64 */
fileptrseek = fileptr;     /* debug break point */

#if defined(LF64) && defined(DWIN)
seekstat = lseeki64(fd1,fileptr,0);
#elif defined(LF64)
seekstat = lseek64(fd1,fileptr,0);
#else
seekstat = lseek(fd1,fileptr,0);
#endif

if (seekstat < 0)
  { sprintf(fsedit,E64,fileptr);
    printf("seek error at %s\n",fsedit);
    errmsg("seek error",fn1,emsg,0,0x40);
  }

return(1);
}

/*Feb04/09 - cmd history code removed (saved in src/uvhd_history.c)  */
/*         - 300 lines of code (histcode) removed                    */
/*         - sorthtdd, bublsort, inithist, writehist                 */

/*eject*/
/*------------------------------ printRV ------------------------------*/
/* display warning message in Reverse Video                            */

short printRV(char *warnmsg)
{

/*build Reverse Video warnmsg, allowing for inhibit via option b2 */
warnmsg2[0] = '\0';             /* init out area for inhibit option */
if ((opsi['b'-'a'] & 0x02) == 0)
    strcat(warnmsg2,smso);
strcat(warnmsg2,warnmsg);
if ((opsi['b'-'a'] & 0x02) == 0)
    strcat(warnmsg2,rmso);
printf(warnmsg2);

return(1);
}

/*eject*/
/*------------------------------- calcrsl --------------------------------*/
/* calc possible record size by decrementing from current recsize         */
/* - until we can divide recsize evenly into filesize                     */

int calcrsl(int rsiz, int fsiz)
{
int rsiz1;
int mod1;

/* inhibit calc if recsize <= 0 or >= filesize */
if ((rsiz <= 0) || (rsiz >= fsiz))
  { return (rsiz);
  }

for (rsiz1=rsiz; rsiz1 > 0; rsiz1--)
  { mod1 = (fsiz % rsiz1);
    if (mod1 == 0)
      { return(rsiz1);
      }
  }

return(0);
}

/*------------------------------- calcrsg --------------------------------*/
/* calc possible record size by incrementing from current recsize         */
/* - until we can divide recsize evenly into filesize                     */

int calcrsg(int rsiz, int fsiz)
{
int rsiz1;
int rsiz2;
int mod1;

/* inhibit calc if recsize <= 0 or >= filesize */
if ((rsiz <= 0) || (rsiz >= fsiz))
  { return (rsiz);
  }

rsiz2 = rsiz * 4;                 /* calc upper limit for increment test */

for (rsiz1=rsiz; rsiz1 <= rsiz2; rsiz1++)
  { mod1 = (fsiz % rsiz1);
    if (mod1 == 0)
      { return(rsiz1);
      }
  }

return(0);
}

/*eject*/
/*-------------------------------- showhelps ----------------------------*/
/* show helpscreens - arg1 points to a table of helpscreen pointers      */
/* - default to showing each in sequence until null pointer (end table)  */
/*   (see table of help screen ptrs stored up front in this program)     */
/* - allow user to enter help screen# to modify display sequence         */
/*   (or 'q' to quit help screens, 'qq' to quit help screens & program   */

int showhelps(char **hsp[], int hsmax)
{
int ii = 0;              /* index for table of help screen ptrs     */
int rr = 0;              /* user reply for next desired help screen */
char reply[80];          /* operator reply                          */

/* begin loop to display help screens until end of table, or user quit */
while (ii < hsmax)
  { showhelp(hsp[ii],25,0x01);    /* show current help screen          */
    if (ii < hsmax-1)
    { printf("enter null for next help screen, or help screen#, or q=quit--> ");
    }
    else
     { printf("End %d help screens, enter desired#, or Null to exit help --> "
           ,hsmax);
     }
    fgets(reply,80,stdin);     /* wait for operator reply           */
    if (reply[0] == 'q')
      { return(2);             /* return 2 to indicate oprtr quit   */
      }

    /* if operator entered valid index to desired help screen       */
    /* - use it, else display next sequential help screen           */
    rr = atol(reply);          /* convert reply to integer          */
    rr--;                      /* decrement for zero relative index */
    if ((rr >= 0) && (rr < hsmax))
      { ii = rr;               /* replace table index with users desire */
      }
    else
      { ii++;                  /* increment to next sequential help screen*/
      }
  }

return(1);
}

/*eject*/
/*------------------------------ openfileX ------------------------------*/
/*Feb05/09 - subfunction to open output files P,I,W,V,D                  */

int openfileX(char *xfname, char *xfid, FILE **xfptr, int *xfopn, short bits)
{
/* inhibit if file already open */
if (*xfopn)
  { return(0);
  }

/* base filename created at program init - now add time & ID (P/I/W/V/D) */
/* tmp/inputfilename_yymmdd_            <-- will append time & P/I/W/V/D */
/* tmp/inputfilename_yymmdd_hhmmssW     <-- example for Write command    */

getdatetime();                /* get current date & time */

/* if tmp/ subdir not present, create it now */
if (stat("tmp", &tmpstat) == -1)
  { system("mkdir tmp");
  }

/* create filename & open file */
strcpy(xfname,bfname);        /* base filename       */
strcat(xfname,time6);         /* append current time */
strcat(xfname,xfid);          /* append file ID      */

*xfptr = fopen(xfname,"wb");
if (*xfptr == (FILE*)0)
  { errmsg("opening file",xfname,"",0,0x40);
  }

/* ifptr = *xfptr;   Feb05/09 temp DEBUG */

*xfopn = 1;                  /* set file open switch */
return(1);
}

/*------------------------------ closefileX ------------------------------*/
/*Feb05/09 - subfunction to close output files P,I,W,V,D                  */

int closefileX(char *xfname, char *xfid, FILE **xfptr, int *xfopn, short bits)
{
/* test switch & close file if only if open */
if (*xfopn)
  { fclose(*xfptr);
  }

*xfopn = 0;                /* reset file open switch */
return(1);
}

/*eject*/
/*---------------------------------------------------------------------*/
/* 971107+ - following functions included in program for internet share*/
/*         - originally linked in from uvlib.a archive                 */

/*--------------------------- atol1 -------------------------------*/
/* convert ascii digit string to int integer                      */
/* - also see atol3 which has multiple stop chars vs atol1 1 only  */
/*                                                                 */
/* arg1 - ptr to int where result is to be stored                 */
/*                                                                 */
/* arg2 - pointer to 1st byte of data to be converted              */
/*                                                                 */
/* arg3 - stop character (in addition to NULL if bit 0x01)         */
/*        (if not needed code any dummy nonnumeric char)           */
/*                                                                 */
/* arg4 - max stop count                                           */
/*                                                                 */
/* arg5 - bit codes to modify procedure as follows:                */
/*        0x01 - stop on null, else by stopc or max count          */
/*        0x02 - stop on any nonnumeric except '-'                 */
/*             - return result of calculation so far + or -        */
/*        0x04 - bypass the stop character in ptr returned         */
/*        0x10 - recognize '-' anywhere in input & set result neg  */
/*               (otherwise ignore '-')                            */
/*        0x20 - validate string all numeric digits or '-' signs   */
/*             - if any other char found, return -1                */
/*             - if this bit not on, nonnumerics are bypassed      */
/*                                                                 */
/* return - ptr to the stop char that ends conversion              */
/*          or 1 past the stop char if bit 0x40 present            */
/*          or the current char when max lth is reached            */
/*        - may point to the null char at end of input sring       */

char *atol1(int *result, char *in, char stop, int max, short bits)
{
int nn=0;                /* result to be returned                 */
int ll;                  /* length ctr for max lth chk            */
short sign = 1;          /* neg indicator set -1 if '-' found     */

/*eject*/
/* for loop to search for ascii digits in string & accumulate result */

for (ll=0; ll < max; ll++, in++)
  {
    if ((*in == '\0') && (bits & 0x01))     /* end null reached ?     */
         break;

    if (*in == stop)                        /* stop char reached ?    */
      { if (bits & 0x04)
            in++;                           /* bypass the stop char   */
        break;
      }

    if (*in >= '0' && *in <= '9')
        nn = 10 * nn  + (*in - '0');
    else if ((*in != '-') && (bits & 0x02))
        break;
    else if ((*in == '-') && (bits & 0x10))
      { sign = -1;
        continue;
      }
    else if (bits & 0x20)
      { nn = -1;                     /* set result -1 for invalid digit */
        break;
      }
   }

/* make negative if - found & bit 0x10 & store result in users int     */
/* - return ptr to stop char (or stop+1), max lth char,or null char     */
*result = (nn * sign);
return(in);
}

/*eject*/
/*--------------------------- atol2 -------------------------------*/
/* atol2.c - convert ascii string to int integer                  */
/*  - older version, may phase out sometime                        */
/* arg1 - pointer to 1st byte of data to be converted              */
/* arg2 - alternate stop character (in addition to NULL)           */
/*        (if not needed code any dummy nonnumeric char)           */
/* arg3 - max stop count                                           */
/* arg4 - bit codes to modify procedure as follows:                */
/*        0x01 - stop on null, else by stopc or max count          */
/*        0x02 - stop on any nonnumeric except '-'                 */
/*             - return result of calculation so far + or -        */
/*        0x04 - recognize '-' anywhere in input & set result neg  */
/*               (otherwise ignore '-')                            */
/*        0x08 - validate string all numeric digits or '-' signs   */
/*             - if any other char found, return 0                 */
/*             - if this bit not on, nonnumerics are bypassed      */

int atol2(char *str, char stopc, int max, short bits)
{
int num=0;               /* result to be returned                 */
int j;                   /* work index                            */
short sign = 1;          /* neg indicator set -1 if '-' found     */

for (j=0; j < max && str[j] != stopc; j++)
   {
     if ((bits & 0x01) && (str[j] == '\0'))
          break;
     if (str[j] >= '0' && str[j] <= '9')
         num= 10 * num + (str[j] - '0');
     else if ((str[j] != '-') && (bits & 0x02))
              break;
     else if ((str[j] == '-') && (bits & 0x04))
           { sign = -1;
             continue;
           }
     else if (bits & 0x08)
             return(0);
   }
return(num * sign);
}

/*eject*/
/*--------------------------- atol3 -------------------------------*/
/* convert ascii digit string to integer Uvint (32 bits)           */
/* - also see atol1 which has only 1 stop char vs multiple         */
/*                                                                 */
/* arg1 - ptr to int where result is to be stored                 */
/* arg2 - pointer to 1st byte of data to be converted              */
/* arg3 - stop characters (in addition to NULL if bit 0x01)        */
/*        (if not needed code any dummy nonnumeric char)           */
/* arg4 - max stop count                                           */
/* arg5 - bit codes to modify procedure as follows:                */
/*        0x01 - stop on null, else by stopc or max count          */
/*        0x02 - stop on any nonnumeric except '-'                 */
/*             - return result of calculation so far + or -        */
/*        0x04 - bypass the stop character in ptr returned         */
/*        0x10 - recognize '-' anywhere in input & set result neg  */
/*               (otherwise ignore '-')                            */
/*        0x20 - validate string all numeric digits or '-' signs   */
/*             - if any other char found, return -1                */
/*             - if this bit not on, nonnumerics are bypassed      */

/* return - ptr to the stop char that ends conversion              */
/*          or 1 past the stop char if bit 0x40 present            */
/*          or the current char when max lth is reached            */
/*        - may point to the null char at end of input sring       */

char *atol3(int *result, char *in, char *stops, int max, short bits)
{
int nn=0;                /* result to be returned                 */
int ll;                  /* length ctr for max lth chk            */
int jj;                  /* index for stops char search           */
short sign = 1;          /* neg indicator set -1 if '-' found     */

/*eject*/
/* for loop to search for ascii digits in string & accumulate result */

for (ll=0; ll < max; ll++, in++)
  {
    if ((bits & 0x01) && (*in == '\0'))
         break;

    for (jj=0; stops[jj] > '\0'; jj++)
         if (stops[jj] == *in)
             break;
    if (stops[jj] != '\0')
      { if (bits & 0x04)
            in++;                               /* bypass the stop char   */
        break;
      }

    if (*in >= '0' && *in <= '9')
        nn = 10 * nn  + (*in - '0');
    else if ((*in != '-') && (bits & 0x02))
        break;
    else if ((*in == '-') && (bits & 0x10))
      { sign = -1;
        continue;
      }
    else if (bits & 0x20)
      { nn = -1;                     /* set result 0 for invalid digit */
        break;
      }
   }

/* make negative if - found & bit 0x10 & store result in users outfield */
/* - return ptr to stop char (or stop+1), max lth char,or null char     */
*result = ( nn * sign);
return(in);
}

/*eject*/
/*--------------------------- sncopy --------------------------*/
/* copy op2 to op1 until the op3 max count reached             */
/* or until a null is reached if op4 has a 1 bit               */
/* - null terminate the output if op4 has a 2 bit              */
/* - return the actual length copied                           */

int sncopy(
char out[],              /* output string           */
char in[],               /* input string            */
int max,                 /* no of chars to copy     */
short bits)              /* stop on null if 1 bit   */
{                        /* null terminate if 2 bit */
int i;

for (i=0; i < max; i++)
  {  if ((bits & 0x01) && (in[i] == '\0'))
         break;             /* stop on null if op4 1 bit  */
     out[i] = in[i];        /* copy the current character */
  }

if (bits & 0x02)            /* if indicated by op4 = 2      */
    out[i] = '\0';          /* null terminate output string */

return(i);
}

/*eject*/
/*--------------------------- stncopy ----------------------------*/
/* copy op2 to op1 until any of the following occurs:             */
/*     1 - an input character matches the op3 stop character      */
/*     2 - the op4 max count is reached                           */
/*     3 - a null is reached in the input (may inhibit by op5)    */
/* op5 is bit coded to control the following:                     */
/*     0x01 - also stop copy on input null (default does not)     */
/*     0x02 - null terminate the output (default does not)        */
/*     0x04 - if copy is stopped by op3 char include it in output */
/* - return the actual data byte count copied                     */

int stncopy(
char *out,                /* output string                    */
char *in,                 /* input string                     */
char stopc,               /* stop character                   */
int max,                  /* max bytes to copy                */
short bits)               /* options (see above)              */
{
int i;

for (i=0; i < max; i++)
   { if ((bits & 0x01) && (in[i] == '\0'))
         break;
     if (in[i] == stopc)
         break;
     out[i] = in[i];      /* copy the current character   */
   }

if ((bits & 0x04) && (in[i] == stopc))
      out[i++] = stopc;
if (bits & 0x02)
    out[i] = '\0';

return(i);              /* return actual bytes copied   */
}

/*eject*/
/*--------------------------- sdscopy -------------------------------*/
/* copy function controlled by dsplcmnt allowing bypass & stop chars */
/*                                                                   */
/* arg1 - the output area (should be as big as the input area)       */
/* arg2 - the input data string                                      */
/* arg3 - max length of input area                                   */
/* arg4 - pointer/displacement to current position in input area     */
/*      - updated to the stop char position or end op2 if nofind     */
/* arg5 - bypass or stop character string (always 4 chars)           */
/*      - 1,2 are bypass chars active if bit code 0x0300 present     */
/*      - 3,4 are stop chars always active, post bypass if 0x0030    */
/* arg6 - bit control codes                                          */
/*        0x1000 - init the displacement before this copy begins     */
/*        0x2000 - inhibit testing stop chars within single quotes   */
/*        0x0100 - bypass char#1 if present, then char#2 if present  */
/*        0x0200 - bypass multiples of #1, then multiples of #2      */
/*        0x0010 - if stopped by char #3, bypass it in dsplcmt update*/
/*               - then check for char #4,& bypass if present        */
/*        0x0020 - if stopped by char #3, bypass multiples to non #3 */
/*               - then bypass any multiple occurrences of char #4   */
/*        0x0002 - null terminate the output string                  */
/*        0x0001 - if stop search on input string null               */
/*                 dont set dsplcmnt to max                          */
/*                 (default sets dsplcmnt to max on nofind stopchar) */
/*                                                                   */
/* return - the length of the data copied                            */
/*        - note that arg4 dsplcmnt is also updated                  */
/*        - stop char nofinds, set the dsplcmnt to the max (arg3)    */

int sdscopy(char *out,char *in,int max,int *sds,char *stop,short bits)
{
int ii;
int sdi;            /* dsplcmnt retrvd to local, will update at end */
int qq=0;           /* quote counter (to inhibit stop within)       */

/* retrieve the dsplcmnt to a local var & init if bit 0x1000         */
sdi = *sds;                    /* retrieve current dsplcmnt to local */
if (bits & 0x1000)
    sdi = 0;

/* test bit controls for pre-copy bypass option single or multiple   */
if (bits & 0x0300)
  { while ((in[sdi] == stop[0]) && (sdi < max))
      { sdi++;
        if ((bits & 0x0200) == 0)       /* multi bypass bit ?        */
           break;
      }
    /* test pre-copy bypass for char #2 single or multiple           */
    while ((in[sdi] == stop[1]) && (sdi < max))
      { sdi++;
        if ((bits & 0x0200) == 0)       /* multi bypass bit ?        */
           break;
      }
  }

/*eject*/
/* copy input to output until NULL, max lth, or either stop char found*/
ii = 0;
while (sdi < max)
  { if (in[sdi] == '\'')
        qq++;                           /* count qts for stop inhibit */
    if ((bits & 0x2000) && (qq % 2))
      { ;
      }
    else
      { if (in[sdi] == stop[2])
           break;
        if (in[sdi] == stop[3])
           break;
      }
    if (in[sdi] == '\0')                /* null reached, no stop char   */
      { if ((bits & 0x0001) == 0)       /* stop null & dont set dsp max */
          { sdi = max;                  /* yes - set dsp max for nofind */
          }
        break;
      }
    out[ii++] = in[sdi++];              /* copy current byte, up to next*/
  }

/* bypass stop chars single or multiple depending on bit ctls          */
if (bits & 0x0030)                     /* bypass stop char requested ? */
  { while ((in[sdi] == stop[2] && sdi < max))
      { sdi++;
        if ((bits & 0x0020) == 0)
           break;
      }
    while ((in[sdi] == stop[3] && sdi < max))
      { sdi++;
        if ((bits & 0x0020) == 0)
           break;
      }
  }

/* null terminate the output if bit 0x0002                            */
if (bits & 0x0002)
   out[ii] = '\0';

/* store the new displacement & return lth of data copied            */
*sds = sdi;
return(ii);
}

/*eject*/
/*------------------------------ searchcm -----------------------------*/
/* search a string for 1st character that matches any character        */
/* within a 2nd string                                                 */
/* - return the index of the found char within the string              */
/*   or return -1 if nomatch found                                     */
/* arg1 - points to the string to be searched                          */
/* arg2 - points to the string of characters to be searched for        */
/* arg3 - max length of string 1 to search                             */
/* arg4 - max length of string 2 to search                             */
/* arg5 - bit codes                                                    */
/*        1 - stop on null in string 1 (else on max length)            */
/*        2 - stop on null in string 2 (else on max length)            */

int searchcm(char *s1, char *s2, int max1, int max2, short bits)
{
int i,j;

for (i=0; i < max1; i++)
  {  if ((bits & 0x01) && (s1[i] == '\0'))
         break;
     for (j=0; j < max2; j++)
       { if ((bits & 0x02) && (s2[j] == '\0'))
            return (-1);
         if (s2[j] == s1[i])
            return(i);
       }
  }
return(-1);
}

/*eject*/
/*---------------------------- errmsg.c ----------------------------*/
/* errmsg.c - display error message on stderr                       */
/* arg1 = msg text, may include \n if arg2 to be on 2nd line        */
/* arg2 = addtnl info such as filename or error data string         */
/* arg3 = addtnl info#2 (null if not needed)                        */
/*      - by convention msg2=logical & msg3=physical filename       */
/* arg4 = integer to be converted to ascii numeric                  */
/*        to replace underscores '_'s in the arg1 string (if any)   */
/*        or column# for * error marker to be displayed on next line*/
/*        if bitcode 0x04 present                                   */
/* arg5 = bitcodes to modify behaviour as follows:                  */
/*        0x000_ - info msg, display no prefix, return after msg    */
/*        0x001_ - display 'ERR -' prefix, return after msg         */
/*        0x002_ - display 'FATAL ERR -' prefix, exit program       */
/*        0x004_ - display 'SYSTEM ERR -' prefix, exit program      */
/*        0x00_1 - insert LF after msg1                             */
/*        0x00_2 - insert LF after msg2                             */
/*               - will always insert LF after msg3                 */
/*        0x00_4 - use arg4 as col# for '*' marker on next line     */
/*        0x0100 - wait for operator reply before returning         */

void errmsg(char *msg1, char *msg2, char *msg3, int num, short bits)
{
char prefix[16];
char msga[80];                   /* arg1 copied here for insert of num*/
char msgb[80];                   /* arg2,3 copied to ensure null term */
char msgc[80];
char numasc[8];                  /* arg4 converted to ascii numeric */
char lf1[2] = "\n";              /* LF or blank insert after msg1   */
char lf2[2] = "\n";              /* LF or blank insert after msg2   */
int i,j;

/* 1st setup prefix depending on severity bit code                  */
prefix[0] = '\0';               /* presume info, no prefix desired */
if (bits & 0x10)
    strcpy(prefix,"ERR -");
if (bits & 0x20)
    strcpy(prefix,"FATAL ERR -");
if (bits & 0x40)
    strcpy(prefix,"SYSTEM ERR -");

/*eject*/
/* setup LFs/blanks for insert before msg2/msg3   depending on bits*/
lf1[0] = '\0';                    /* presume blank                */
lf2[0] = '\0';
if (bits & 0x01)
    lf1[0] = '\n';
if (bits & 0x02)
    lf2[0] = '\n';

/* copy users strings to w/s & ensure null terminated             */
memset(msga,'\0',80);
memset(msgb,'\0',80);
memset(msgc,'\0',80);
sncopy(msga,msg1,79,1);
sncopy(msgb,msg2,79,1);
sncopy(msgc,msg3,79,1);

/* convert arg4 integer to ascii numeric to replace any '_'s in arg1*/
sprintf(numasc,"%6ld",num);
for (i=79,j=5; i >= 0; i--)
    if (msga[i] == '_')
        msga[i] = numasc[j--];

/* now display the constructed message                            */
printf("%s %s %s %s %s %s \n",prefix,msga,lf1,msgb,lf2,msgc);

/* display error marker line if bit 0x04 & arg4 col# > 0 & < 80   */
if ((bits & 0x04) && (num > 0) && (num < 78))
  { memset(msga,' ',78);           /* fill flagline with blanks   */
    msga[78] = '\n';               /* line feed at col 79         */
    msga[79] = '\0';               /* null term at col 80         */
    msga[num] = '*';               /* insert '*' at flag column#  */
    printf(msga);
  }

/*eject*/
/* if syserr bit 0x40 & system errno is valid - display errno alpha */
/* if ((bits & 0x40) && (errno > 0) && (errno < sys_nerr))          */
/*     printf("%d %s\n",errno,sys_errlist[errno]);                  */
/* above replaced by perror - July 93 (undefs on svr5r4)            */
if (bits & 0x40)
    perror("FATAL ERR ");

/* return to caller if severity bits 0 or 1, exit program if 2/6    */
if (bits & 0x60)
    exit(99);
else
  { /* wait for oprtr reply if bit 0x100                            */
    if (bits & 0x100)
      { printf(" - reply to continue (or kill job) \n");
        fgets(msgc,80,stdin);
      }
  }
return;
}

/*eject*/
/*--------------------------- showhelp ------------------------------*/
/* showhelp.c - display "help" screen - by O Townsend aug 92         */
/* - arg1 is ptr to array of char ptrs to strings                    */
/* - strings will be displayed on stderr until '!' is found          */
/*   in 1st char of any string or a null pointer is reached          */
/*Jan18/06 - OR until max lines displayed                            */
/*         - if bits 0x01, display blank lines up to max lines       */

int showhelp(char *msgs[], int max, short bits)
{
int ii,nn;
nn = 0;
for (ii=0; ii < max; ii++)
  { if ((!msgs[ii]) || (msgs[ii][0] == '!') || (msgs[ii][0] == '\0'))
      { if (bits & 0x01)
          { while (ii < max)
              { printf(" \n");
                ii++;
              }
          }
        break;
      }
    else
      { printf("%s\n",msgs[ii]);
      }
  }
return(ii);
}

/*eject*/
/*--------------------------- waitreply1 ------------------------------*/
/* wait for oprtr reply, allowing q to quit                            */
/* arg2 - determines action if oprtr enters q=quit                     */
/*      - 0x00 to return or 0x01 to exit program                       */
/*      - if quit, write history file if not already written           */
/*Oct31/06 - name chgd to 'waitreply1' since 'waitreply' is in showhelp*/
/*         - and does not include the writehist() below                */

int waitreply1(char *reply, short quitctl)
{
fgets(reply,80,stdin);      /* wait for operator reply         */
if ((reply[0] == 'q') && (reply[1] < '0'))
  { if (quitctl & 0x01)
      { /* if (histw < 1)   */
         /*  { writehist(); */
          /* }              */
        exit(2);
      }
    else
      { return(2);
      }
  }
return(1);
}

/*eject*/
/*------------- convert unix time to date/time string------------*/
/* arg2 points to a unix time (secs since 1970)                  */
/* converts unix time to "ccyymmddhhmmss" & stores in arg1       */
/* arg1 = ptr to 14 byte char array to receive "ccyymmddhhmmss"  */
/*        (not null terminated)                                  */

void cnvdttm(char *dtp, time_t unixtm)
{
struct tm *p;
int month;
char dtws[16];

p = localtime(&unixtm);
month = p->tm_mon + 1;

sprintf(dtws,"%04d%02d%02d%02d%02d%02d",
p->tm_year+1900,month,p->tm_mday,
p->tm_hour,p->tm_min,p->tm_sec);

memcpy(dtp,dtws,14);

return;
}

/*eject*/
/*--------------------------- hex2data -------------------------------*/
/* hex2data.c - function to convert hex representation to true data   */
/*            - by Owen Townsend, ARC, AUG92, updt NOV92              */
/* - returns count of output data length converted                    */
/* arg1 - points to output area  (input data will be twice as int)   */
/* arg2 - points to input hex rep (either upper or lower case a-f)    */
/* arg3 - optional stop char (if not needed code an unlikely char)    */
/* arg4 - max length of output (stop if reached)                      */
/* arg5 - bit options                                                 */
/*        0x01 - stop if null reached in input                        */
/*        0x02 - null terminate the output string                     */

int hex2data(char *out, char *in, char stopc, int max, short bits)
{
int i,j;
intU z,d;                              /* work areas for zone & digit */

for (i=0,j=0; j < max; i++,i++,j++)
 {  if ((bits & 0x01) && (in[i] == '\0'))
        break;
    if (in[i] == stopc)
        break;

   z = (intU)(in[i] & 0x7f);          /* remove any high bit of zone rep  */
   if (z > 'Z') z = z & 0x5f;         /* if upper, convert to lower       */
   if (z > '9') z = z -7;             /* if a-f convert low 4 bits to a-f */
                                      /* 0x41:0xf4 - 7 = 0x3a:0x3f        */
   z = z << 4;                        /* shift result to zone & clr digit */

   d = (intU)(in[i+1] & 0x7f);        /* remove any high bit of digit rep */
   if (d > 'Z') d = d & 0x5f;         /* if upper, convert to lower       */
   if (d > '9') d = d -7;             /* if a-f convert low 4 bits to a-f */
                                      /* 0x41:0xf4 - 7 = 0x3a:0x3f        */
   d = d & 0x0f;                      /* clear zone bits                  */

   out[j] = (char)(z | d);            /* combine zone & digit into output */
 }

if (bits & 0x02)                      /* option to null term output ?     */
    out[j] = '\0';

return(j);                            /* return the data length converted */
}

/*eject*/
/*---------------------------- sortops ---------------------------------*/
/* sortops.c - process options into alpha position                      */
/*               in character,& integer arrays                          */
/* - this subrtn is commonly used to process an option string from the  */
/*   command line into sorted forms easily accessed by the user programs*/
/* - options must be contiguous as in following examples:               */
/*                                                                      */
/*  abc                 - no spaces between option letters              */
/*  cba                 - not necessarily in sequence                   */
/*  b512e512r256        - option letters may have numeric args          */
/*  b512,e512,r256      - may use , separators if desired               */
/*                                                                      */
/* arg1 - input string                                                  */
/* arg2 - output sorted array of characters (or Nulls)                  */
/* arg3 - output sorted array of int ints                              */
/* arg4 - control bits - 0x00 arrays will be cleared to nulls           */
/*        0x01 - arrays will not be cleared (any prior optns remain)    */
/*        0x10 - return success/fail (+1/-1) indication to caller       */
/*               rather than errmsg&exit if invalid input detected      */

int sortops(char *opsu, char *opsc, int *opsi, short bits)
{
int ui;                    /* index to the input raw option string    */
char c;                    /* current option letter being processed   */
int ci;                    /* index to the character array            */
int j;                     /* misc index                              */
char nums[16];             /* numerics following current optn ltr     */
int ni;                    /* index to numeric array                  */

/* 1st clear the output arrays - unless inhibited by bits 0x01         */
if ((bits & 0x01) == 0)
  {  for (j=0; j < 26; j++)
        { opsc[j] = 0;             /* init the character array to nulls */
          opsi[j] = 0;             /* init integer array to zeros       */
        }
  }

/* bypass any leading '-' option identifier                             */
ui = 0;                        /* init index to 1st char                */
if (opsu[ui] == '-')
    ui++;                      /* bypass any leading '-'                */

/*eject*/
/* begin loop to extract each optn letter into opsc[] alpha sorted array*/
/* & to convert any appended digits to binary & store in opsi[] array   */
while (opsu[ui] > ' ')         /* until end string null/blank ?         */
  { c = opsu[ui++];            /* save current option letter            */
    if (c < 'a' || c > 'z')
       { printf("sortops invalid at byte# %d %s \n",ui,opsu);
         if (bits & 0x10)      /* return fail code or exit here ?       */
             return(-1);
         else
             exit(-1);
       }
    ci = c - 'a';              /* index into sorted options array       */
    opsc[ci] = c;              /* store optn letter into sorted array   */

    /* now isolate optional numeric string following alpha letter       */
    /*  convert to binary & store in integer array                      */
    ni = 0;
    while (opsu[ui] >= '0' && opsu[ui] <= '9')
           nums[ni++] = opsu[ui++];      /* store current digit        */
    nums[ni] = '\0';                     /* null terminate num string  */
    opsi[ci] = atol2(nums,'~',16,3);     /* cnvrt numstring to integer */

    /* bypass any comma ',' separator before next alpha option letter  */
    if (opsu[ui] == ',')
        ui++;
  }
return(1);
}

/*eject*/
/*---------------------------- sortops4 --------------------------------*/
/* sortops4.c - process options into alpha position                     */
/*               in character, integer,& string arrays                  */
/* This subrtn is commonly used to process an option string from the    */
/* command line into sorted forms easily accessed by the user programs  */
/* Options must be contiguous as in following examples:                 */
/*                                                                      */
/*  option input string - example description                           */
/*  ------------------------------------------------------              */
/*  abc                 - no spaces between option letters              */
/*  cba                 - not necessarily in sequence                   */
/*  b512e512-r256       - option letters may have numeric args&trlng sign*/
/*  b512,e512,r256      - may use , separators if desired               */
/*  b=512,e=512,r=256   - may use = separators if desired               */
/*                        (must use if any alpha in args)               */
/*  a=jkl,b=xyz         - must use = sep to assign non-numeric strings  */
/*  a='cat',b='c t'     - may enclose value in single quotes            */
/*                        (must do if any embedded blanks)              */
/*  a=^01ef^            - indicates hexadecimal string                  */
/*                      - converted to true hex when stored             */
/*                 note - must enclose hex strigs in '^'s (circumflexes)*/
/*                        because quotes are removed by the shell       */
/*                                                                      */
/* bit control arg#6                                                    */
/*     0x10 - return success/fail (+1/-1) indication to caller          */
/*            rather than exit here if invalid input detected           */
/*                                                                      */
/* note - also see sortops.c which provides only the char sorted array  */
/*        & the integer array, but not the string ptrs & string buffer  */
/*      - used by programs not requiring the strings (less overhead)    */

int sortops4(char*opsu,char*opsc,int*opsi,char**opsp,char*opsb,short bits)
{
/*       opsu[];           pointer to unsorted raw options input      */
/*       opsc[];           ptr to 26 sorted character options         */
/*       opsi[];           ptr to 26 integer options                  */
/*       *opsp[];          ptr to 26 ptrs to option strings           */
/*       opsb[];           buffer for option strings                  */

char *opsbp;               /* ptr to current position in buffer        */
int i;                     /* index to the input raw option string     */
char c;                    /* current option letter being processed    */
int cp;                    /* index to the character array             */
int j,k;                   /* work indexes                             */
char temp[128];            /* temp w/s if hex a=^hexchars^             */

/*eject*/
/* 1st init the output arrays */
for (j=0; j < 26; j++)
   { opsc[j] = 0;             /* init the sorted result to blanks      */
     opsp[j] = (char *) 0;    /* init string ptrs to null              */
     opsi[j] = 0;             /* init integer array to zeros           */
   }
opsbp = opsb;                 /* init buffer ptr to 1st byte           */

/* bypass any leading '-' option identifier                            */
i = 0;                         /* init index to 1st char                */
if (opsu[i] == '-') i++;       /* bypass any leading '-'                */

/* begin loop to extract optn letters into opsc[] alpha sorted array   */
/*  & store any following optn prmtr/args into opsb[] array            */
while (opsu[i] > ' ')          /* until end string null/blank ?        */
  { c = opsu[i++];             /* save current option letter            */
    if (c < 'a' || c > 'z')
       { printf("sortops4 invalid at byte# %d %s \n",i,opsu);
         if (bits & 0x10)
             return(-1);
         else
             exit(-1);
       }
    cp = c - 'a';              /* index into sorted options array         */
    opsc[cp] = c;              /* store optn letter into sorted array     */

/* 1st store the data following the option letter into the string buffer*/
/* (will then convert any numeric digits from strings into integer optns*/
/*  following data of 2 types - numeric or nonnumeric                   */
/* numeric data - stored until nonnum or non '-' sign reached           */
/* nonnum data  - must have begin with '=' sep & end with ',' sep       */
/* - may be enclosed in quotes for char 'xx x' or circs for hex ^xxxx^  */
  if (((opsu[i] >= '0') && (opsu[i] <= '9')) || (opsu[i] == '-'))
    { opsp[c-'a'] = opsbp;               /* store ptr to 1st digit      */
      while (((opsu[i] >= '0') && (opsu[i] <= '9')) || (opsu[i] == '-'))
             *opsbp++ = opsu[i++];        /* store numeric digits       */
      *opsbp++ = (char) 0;               /* null terminate              */
    }

/*eject*/
/* non-num follows optn ltr - test for '=' sep & char string            */
  else if (opsu[i] == '=' && opsu[i+1] != '^')  /* =charstring ?         */
    { i++;                              /* bypass the '=' separator     */
      if (opsu[i] == '\'')              /* if enclosed in quotes ?      */
          i++;                          /* bypass opening quote         */
      opsp[c-'a'] = opsbp;              /* store ptr to 1st char string */
      while ( opsu[i] != ',' && opsu[i] != '\0')
          *opsbp++ = opsu[i++];          /* store a/n string char       */
      *opsbp++ = '\0';                  /* null terminate               */
   }

/* non-num follows optn ltr - test for '=^' sep & hex string             */
  else if (opsu[i] == '=' && opsu[i+1] == '^')  /* =charstring ?         */
    { opsp[c-'a'] = opsbp;              /* store ptr to 1st char string */
      i++; i++;                         /* bypass the leading '=^' sep  */
      j = 0;                            /* init index for temp w/s copy */
      while ( opsu[i] != ','&& opsu[i] != '^' && opsu[i] != '\0')
          temp[j++] = opsu[i++];        /* copy hex chars to temp w/s   */
      temp[j] = '\0';                   /* null terminate temp w/s      */
      k = hex2data(opsbp,temp,'~',64,1);
                                        /* cnvrt to true hex in buffer  */
      opsbp = opsbp + k;                /* incrmnt buf ptr by hex lth   */
      *opsbp++ = '\0';                  /* null term hex data in buffer */
   }

  /* end current option letter - bypass any trailing separators b4 next */
   while (opsu[i] == ','|| opsu[i] == '\'' || opsu[i] == '^')
          i++;                         /* bypass , & ^ seps b4 next op  */
 }
/* we have completed extraction of optn characters & strings            */
/* - now convert any numeric strings to integers                        */
for (k=0; k < 26; k++)
   if ((opsp[k]) && (opsp[k][0] >= '0') && (opsp[k][0] <= '9'))
       opsi[k] = atol2(opsp[k],'~',16,0x07);
return(1);
}

/*eject*/
/*------------------------ toascii2 ---------------------------*/
/* convert a string from EBCDIC to ASCII                       */
/* arg1 - string to be translated                              */
/* arg2 - max length of string                                 */
/* arg3 - bit control codes                                    */
/*        0x01 - stop on NULL                                  */
/*        0x02 - terminate output string (if max with no null) */
/*        0x04 - convert non-printable EBCDIC chars to periods */
/* return -  length of string (or max count)                   */

/*Apr04/06 - changed 0x4B (EBCDIC period) to 0x5F (EBCDIC carat) */
/*Apr04/06 - dont like it change it back to period               */

int toascii2(unsigned char *str, int max, short bits )
{
int ii;
for (ii=0; ii < max; ii++)
  { if ((str[ii] == '\0') & (bits & 0x01))
        break;
    if (((unsigned char)str[ii] < 0x40) && (bits & 0x04))
        str[ii] = 0x4B;    /*Apr04/06 change 0x4B to 0x5F & back */
    str[ii] = ebc2asc[(unsigned char)(str[ii])];
  }
if ((ii == max) && (bits & 0x02))
    str[max] = '\0';
return(ii);                            /* return lth of string  */
}

/*------------------------ toebcdic2 ---------------------------*/
/* convert a string from ASCII to EBCDIC                       */
/* arg1 - string to be translated                              */
/* arg2 - max length of string                                 */
/* arg3 - bit control codes                                    */
/*        0x01 - stop on NULL                                  */
/*        0x02 - terminate output string (if max with no null) */
/* return -  length of string (or max count)                   */

int toebcdic2(unsigned char *str, int max, short bits )
{
int ii;
for (ii=0; ii < max; ii++)
  { if ((str[ii] == '\0') & (bits & 0x01))
        break;
    str[ii] = asc2ebc[(unsigned char)(str[ii])];
  }
if ((ii == max) && (bits & 0x02))
    str[max] = '\0';
return(ii);                            /* return lth of string  */
}

/*eject*/
/*------------------------- getline -----------------------------*/
/* get the next record from a file                               */
/* return the record size read or -1 if EOF or I/O error occurs  */
/* - return lth will be 1 higher than data lth if stopchar nofind*/
/*                                                               */
/* arg1 - the file pointer                                       */
/* arg2 - points to the 1st byte of data to be written           */
/* arg2 - the count to be written, or max if stops not found     */
/* arg4 - the stop character (usually a LineFeed)                */
/* arg5 - bitcodes to control procedures as follows              */
/*                                                               */
/*default - read the count specified (ignore NULLs, LFs,CRs)     */
/*        - dont convert any chars to blanks or NULLs            */
/*        - dont null terminate                                  */
/*                                                               */
/* stop read options - dflt stop on LF                           */
/* 0x0010 - bypass any LF's (use when stopchar is CR)            */
/* 0x0020 - stop reading when the stopchar (arg4) detected       */
/* 0x0040 - stop reading when a NULL is encountered              */
/* 0x0080 - n/u                                                  */
/*                                                               */
/* 0x0001 - convert CR's to blanks                               */
/* 0x0002 - convert LF's to blanks                               */
/* 0x0004 - convert NULL's to blanks                             */
/* 0x0008 - convert tabs to blanks (anywhere in record)          */
/*                                                               */
/* 0x0100 - remove any trailing blanks                           */
/* 0x0200 - terminate record with the stopchar (arg4)            */
/*          (stops read but is not put in data automatically)    */
/* 0x0400 - null terminate the record before returning           */
/*        - would follow the stopchar if 0x0200 on               */


int getlin0(FILE *fp, char *rec, int count, int stop, short bits)
{
int cc,ii,nn;

/*eject*/
/* stop read when max count reached if no stop chars spcfd (CR,LF,NULL) */
/*  if any stop char spcfd, read 1 extra byte                           */
/*  - if extra byte stop char - ok just return count requested          */
/*  - if extra byte not stop char return length count+1 to signal error */

ii = 0; nn = 0;
while (ii <= count)
   { cc = fgetc(fp);
     if (cc == EOF)
        return(-1);
     nn++;

     if ((bits & 0x0010) && (cc == '\n'))
         continue;

     if ((bits & 0x0020) && (cc == stop))
         break;
     if ((bits & 0x0040) && (cc == '\0'))
         break;

     if (((bits & 0x0060) == 0) && (nn == count))
         break;

     if ((bits & 0x0001) && (cc == '\r'))
         cc = ' ';
     if ((bits & 0x0002) && (cc == '\n'))
         cc = ' ';
     if ((bits & 0x0004) && (cc == '\0'))
         cc = ' ';
     if ((bits & 0x0008) && (cc == '\t'))
         cc = ' ';

     rec[ii++] = (char) cc;
   }

/*eject*/
/* remove trailing blanks or 0x00's if bitcode present                */
if (bits & 0x0100)
  { for (ii--; ii > 0; ii--)
      { if ((rec[ii] != ' ') && (rec[ii] != '\0'))
             break;
      }
    ii++;                    /* correct ii to byte after last data*/
  }

/* ensure we return at least 1 blank (EOF already checked above)   */
/* - converts lines with LF only to 1 blank (& LF maybe on output) */
/* - but only if stop-char is an LF                                */
/*   to allow copying binary files with alt stop chars             */
if ((ii == 0) && (stop == '\n'))
  { rec[ii++] = ' ';
  }

/* terminate with stopchar if indicated by bitcodes                */
if (bits & 0x0200)
    rec[ii++] = stop;

/* null terminate if indicated by bitcodes                         */
if (bits & 0x0400)
    rec[ii] = '\0';

return ii;
}

/*eject*/
/*-------------------------- putline ------------------------------*/
/* write a  record to a file                                       */
/* return the record size written or 0 if I/O error occurs         */
/*                                                                 */
/* arg1 - the file pointer                                         */
/* arg2 - points to the 1st byte of data to be written             */
/* arg3 - the count to be written or max if LF or null determined  */
/* arg4 - the stop-write-character (usually a LineFeed)            */
/* arg5 - bitcodes to control procedure as follows:                */
/*                                                                 */
/*default - write spcfd number of bytes (ignore NULLs,CRs,& LFs)   */
/*        - termination controlled by bitcodes & arg4 stop-char    */
/*                                                                 */
/* 0x0100 - remove any trailing blanks in the record (calc new rcsz)*/
/*                                                                 */
/* 0x0020 - stop writing on stop-character (arg4)                  */
/* 0x0040 - stop writing on NULL                                   */
/*                                                                 */
/*        - termination after max count written                    */
/* 0x0001 - write a CR after last data byte                        */
/* 0x0002 - write the stop-char (follows CR if 0x0001)             */
/* 0x0004 - write a NULL (follows CR & stopchar if indicated)      */

int putlin0(FILE *fp, char *rec, int count, int stop, short bits)
{
int ii,ll;

ll = count;                         /* presume count determines EOR */

/* 1st test for option to remove trailing blanks & adjust max count */
if (bits & 0x0100)
  { for (ll=count-1; ll > 0; ll--)
      { if (rec[ll] > ' ')
          { ll++;               /* include the last nonblank in count*/
            break;
          }
      }
    /* correct out lth for NULL records & force output to 1 blank    */
    /* - but only if stop-char is LineFeed                           */
    if ((ll == 0) && (stop == '\n'))
      { if (rec[ll] == '\0')
            rec[ll] = ' ';
        ll++;
      }
  }

/*eject*/
/* now write characters until any of following occurs:               */
/* - 1st CR   reached  & control bit present                         */
/* - 1st LF   reached  & control bit present                         */
/* - 1st NULL reached  & control bit present                         */
/* - max count (or last nonblank count) reached                      */
ii = 0;
while (ii < ll)
  { if ((bits & 0x0020) && (rec[ii] == stop))
        break;
    if ((bits & 0x0040) && (rec[ii] == '\0'))
        break;
    if (fputc(rec[ii++],fp) < 0)
        return(-1);                  /* return -1 if write err occurs*/
   }

/* append CR if indicated by bitcodes                               */
if (bits & 0x0001)
  { if (fputc('\r',fp) < 0)
        return(-1);                   /* return -1 if write err occurs*/
    ii++;
  }

/* append stop-char if indicated by bitcodes                        */
if (bits & 0x0002)
  { if (fputc(stop,fp) < 0)
        return(-1);                   /* return -1 if write err occurs*/
    ii++;
  }

/* append NULL if indicated by bitcodes                               */
if (bits & 0x0004)
  { if (fputc('\0',fp) < 0)
        return(-1);                   /* return -1 if write err occurs*/
    ii++;
  }

return(ii);                            /* return length written       */
}

/*eject*/
/*------------------------------ repchars -----------------------------*/
/* replace characters with alternates                                  */

short repchars(char *string, char *srcs, char *reps, int max, short bits)
{
int ii,jj,nn;

nn = 0;        /* clear counter of replacements for return value */

for (ii=0; (ii < max) && (string[ii]); ii++)
  { for (jj=0; srcs[jj]; jj++)
      { if (string[ii] == srcs[jj])
          { string[ii] = reps[jj];
            nn++;
            break;
          }
      }
  }
return(nn);
}

/*------------------------- getdatetime --------------------------*/
int getdatetime(void)
{
/* get system date & time & convert to "yyyymmdd_hhmm" format     */
tmsecs = time(0);              /* get time in secs since 1970     */
cnvdttm(today,tmsecs);         /* convert to "yyyymmddhhmm"       */
/* today[12] = '\0';   |* drop seconds  <-- disabled Feb18/07     */
sncopy(date6,today+2,6,0x03);  /* create today in 6 digits yymmdd */
sncopy(time6,today+8,6,0x03);  /* create time in 6 digits hhmmss  */

return(1);
}

/*eject*/
/*----------------------- tolower2 ----------------------------*/
/* convert a string to lower case                              */
/* arg1 - string to be translated                              */
/* arg2 - max length of string                                 */
/* arg3 - bit control codes                                    */
/*        0x01 - stop on NULL                                  */
/*        0x02 - terminate output string (if max with no null) */
/* return -  length of string (or max count)                   */

int tolower2(char *str, int max, short bits )
{
int ii;
for (ii=0; ii < max; ii++)
  { if ((str[ii] == '\0') & (bits & 0x01))
        break;
    str[ii] = (char) tolower(str[ii]);  /* translate to lower  */
  }
if ((ii == max) && (bits & 0x02))
    str[max] = '\0';
return(ii);                             /* return lth of string*/
}

/*------------------------ toupper2 ---------------------------*/
/* convert a string to upper case                              */
/* arg1 - string to be translated                              */
/* arg2 - max length of string                                 */
/* arg3 - bit control codes                                    */
/*        0x01 - stop on NULL                                  */
/*        0x02 - terminate output string (if max with no null) */
/* return -  length of string (or max count)                   */

int toupper2(char *str, int max, short bits )
{
int ii;
for (ii=0; ii < max; ii++)
  { if ((str[ii] == '\0') & (bits & 0x01))
        break;
    str[ii] = (char) toupper(str[ii]); /* translate to lower   */
  }
if ((ii == max) && (bits & 0x02))
    str[max] = '\0';
return(ii);                           /* return lth of string  */
}

/*eject*/
/*------------------------- toprint ---------------------------*/
/* convert a string to printable characters                    */
/* - replace any unprintable characters with '.' periods       */
/* arg1 - string to be translated                              */
/* arg2 - max length of string                                 */
/* arg3 - bit control codes                                    */
/*        0x01 - stop on NULL                                  */
/*        0x02 - terminate output string (if max with no null) */
/*        0x04 - convert non-numeric chars to periods          */
/* return -  length of string (or max count)                   */

int toprint2(Uchar *str, int max, short bits)
{
int ii;
for (ii=0; ii < max; ii++)
  { if ((str[ii] == '\0') & (bits & 0x01))
      { break;
      }
    if (!(isdigit(str[ii])) && (bits & 0x04))
      { str[ii] = '.';
      }
    str[ii] = trtprint[(unsigned char)(str[ii])];  
  }
if ((ii == max) && (bits & 0x02))
  { str[max] = '\0';
  }
return(ii);                            /* return lth of string  */
}

/*------------------------- end of uvhd.c program ---------------------*/
