home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / osr5 / sco / scripts / asc < prev    next >
Encoding:
AWK Script  |  1997-08-26  |  39.1 KB  |  1,086 lines

  1. #!/usr/local/bin/gawk -f
  2. #!/usr/bin/awk -f
  3. # @(#) asc.gawk 1.3 97/06/27
  4. # asc: print ascii values of characters
  5. # Use gawk because its printf "%c" works the way we want.
  6. # Yes, this is pretty gross...
  7. # 91/10/17 john h. dubois iii (john@armory.com)
  8. # 92/02/16 added help
  9. # 92/05/01 changed to #!gawk script
  10. # 92/08/04 changed to use a function
  11. # 94/01/01 Added all options.
  12. # 94/01/08 Read from stdin if no data given on cmd line.
  13. # 94/09/15 Added w option
  14. # 94/10/15 Fixed h and r options.  Added l option.  Made work for 0 values.
  15. # 95/05/07 Added z option.
  16. # 95/07/26 Added pdsS options.
  17. # 97/06/18 Added y option.
  18. # 97/06/27 Added YR options.
  19.  
  20. BEGIN {
  21.     Name = "asc"
  22.     Usage = \
  23. "Usage: " Name " [-hbowxlRyYz] [-p<bits>] [-[sS]<num-chars>] [-r<radix>]\n"\
  24. "             [-d<digits>] <characters> ..."
  25.     ARGC = Opts(Name,Usage,"bd>hlop>r>Rs>S>wxyYz",0,"","",0,"",0,"",
  26.     "y,s,S,p,w,l;b,o,x,r;sSpwlboxrdz,Y,R")
  27.     if ("h" in Options) {
  28.     printf \
  29. "%s: print the ASCII values of characters.\n"\
  30. "%s\n"\
  31. "The values of the characters in each word are printed together on a line,\n"\
  32. "separated by spaces.  If more than one word is given, the values for each\n"\
  33. "are printed on separate lines.  If no characters are given on the command\n"\
  34. "line, they are read from the standard input.  A blank line is printed\n"\
  35. "between the converted output for each line of input.\n"\
  36. "Options:\n"\
  37. "-h: Print this help.\n"\
  38. "Input conversion:\n"\
  39. "-s<numchars>: Read the standard input as a stream, and convert each\n"\
  40. "    <numchars> characters to a word as a single value, with the first\n"\
  41. "    character of each set read setting the low 8 bits, etc.  <numchars>\n"\
  42. "    must be in the range 1..4.  Due to gawk limitations, if the last\n"\
  43. "    character of the file is a newline, the last word may be incorrect.\n"\
  44. "-S<numchars>: Like -s, except that the last character of each set read\n"\
  45. "    sets the low 8 bits, etc.\n"\
  46. "-p<bits>: Take each word to be an unsigned fixed-point number, with the\n"\
  47. "    low <bits> bits being to the right of the point.\n"\
  48. "-w: Convert the characters in each word as a single value.  The rightmost\n"\
  49. "    character of each word sets the low 8 bits, etc.  If a word is longer\n"\
  50. "    than 4 characters, each 4 characters (grouped from the right) are\n"\
  51. "    converted separately.  Due to limitations of gawk arithmetic, only\n"\
  52. "    values up to 2^31-1 may be converted.\n"\
  53. "-l: Like -w, except that the leftmost character of each word sets the low\n"\
  54. "    8 bits, etc.\n"\
  55. "-y: Interpret certain input combinations as a symbolic value.  Currently,\n"\
  56. "    the only conversion done is that the sequence ^X will be interpreted\n"\
  57. "    as control-X, where X is any character from the set: @A-Za-z[\]^_?\n"\
  58. "Output formatting:\n"\
  59. "-b: Print values in binary.\n"\
  60. "-o: Print values in octal.\n"\
  61. "-x: Print values in hex.\n"\
  62. "-r<radix>: Print values in radix (base) <radix>\n"\
  63. "-d<digits>: Specify the number of output-base digits to print after the\n"\
  64. "    decimal point.  Used with -p only.\n"\
  65. "-z: Print values with leading zeros, so they occupy a fixed field width.\n"\
  66. "-Y: Instead of printing numbers, produce output in which the characters\n"\
  67. "    with ASCII values 32-126 are printed directly, characters 0-31 are\n"\
  68. "    printed as ^@ through ^, and characters 128-255 are printed in octal\n"\
  69. "    (\0xxx) form.  May not be used with any of the multi-character\n"\
  70. "    conversion options.\n"\
  71. "-R: Raw output; no conversion at all.  Normally only used with -y.\n"\
  72. "    May not be used with any of the multi-character conversion options.\n",
  73.     Name,Usage
  74.     exit(0)
  75.     }
  76.     SymIn = "y" in Options
  77.     SymOut = "Y" in Options
  78.     rawOut = "R" in Options
  79.     if ("b" in Options)
  80.     Base = 2
  81.     else if ("o" in Options)
  82.     Base = 8
  83.     else if ("x" in Options)
  84.     Base = 16
  85.     else if ("r" in Options)
  86.     Base = Options["r"]+0
  87.     else
  88.     Base = 10
  89.     if ("p" in Options) {
  90.     Scale = 2^(Options["p"])
  91.     # Convert the integer value of the maximum fractional part to a
  92.     # string to estimate how many fraction digits are needed to
  93.     # represent the value.
  94.     if ("d" in Options)
  95.         FracDig = Options["d"]
  96.     else
  97.         FracDig = length(itoa(Scale-1,Base))
  98.     }
  99.     else
  100.     Scale = 0
  101.     WordLen = 1
  102.     MaxVal = 255
  103.     if ("w" in Options || "l" in Options) {
  104.     WordLen = 4
  105.     MaxVal = 2^31-1
  106.     Left = "l" in Options
  107.     }
  108.     if (Stream = ("s" in Options || "S" in Options)) {
  109.     if ("s" in Options)
  110.         WordLen = Options["s"]
  111.     else
  112.         WordLen = Options["S"]
  113.     if (WordLen > 4) {
  114.         print \
  115.         "Number of characters given with -s and -S must be <= 4" \
  116.         > "/dev/stderr"
  117.         exit(1)
  118.     }
  119.     if (WordLen < 4)
  120.         MaxVal = 256^WordLen - 1
  121.     else
  122.         MaxVal = 2^31-1
  123.     Left = "s" in Options
  124.     }
  125.     if ("z" in Options) {
  126.     if (Scale)
  127.         nDig = length(itoa(int(MaxVal/Scale),Base)) + 1 + FracDig
  128.     else
  129.         nDig = length(itoa(MaxVal,Base))
  130.     }
  131.     else
  132.     nDig = 0
  133.  
  134.     if (Stream) {
  135.     # Since we can't tell whether RS occurs after the last record,
  136.     # guarantee that it does by adding it after the end of the input
  137.     # stream.  Note: this won't work until the bug wherein gawk closes
  138.     # stdin for pipes to getline is fixed.
  139.     # Cmd = "cat; echo ''"
  140.     # while ((Cmd | getline) == 1) {
  141.     # For now, just ignore the final RS... sigh.
  142.     while (getline == 1) {
  143.         S = S Sep $0   # Every line except the first had to come after a \n
  144.         Start = 1
  145.         for (len = length(S); (Start+WordLen-1) >= len; Start += WordLen)
  146.         ConvertWord(substr(S,Start,WordLen),Base,WordLen,Left,nDig,
  147.         Scale,FracDig)
  148.         S = substr(S,Start)
  149.         Sep = "\n"
  150.     }
  151.     # close(Cmd)
  152.     if (S != "")
  153.         ConvertWord(S,Base,WordLen,Left,nDig,Scale,FracDig)
  154.     }
  155.     else if (ARGC < 2) {
  156.     while (getline == 1) {
  157.         printf newline    # print newline before each group except first
  158.         for (i = 1; i <= NF; i++)
  159.         ConvertWord($i,Base,WordLen,Left,nDig,Scale,FracDig,SymIn,
  160.         SymOut,rawOut)
  161.         newline = "\n"
  162.     }
  163.     }
  164.     else {
  165.     for (i = 1; i < ARGC; i++)
  166.         ConvertWord(ARGV[i],Base,WordLen,Left,nDig,Scale,FracDig,SymIn,
  167.         SymOut,rawOut)
  168.     }
  169. }
  170.  
  171. # Convert character sequences of the form ^X to control character X
  172. function ToControl(S,  len,i,c0,c,newS) {
  173.     len = length(S)
  174.     if (!("@" in _ToControl_Map)) {
  175.     _ToControl_Map["@"] = "\000"
  176.     _ToControl_Map["["] = "\033"
  177.     _ToControl_Map["\\"] = "\034"
  178.     _ToControl_Map["]"] = "\035"
  179.     _ToControl_Map["^"] = "\036"
  180.     _ToControl_Map["_"] = "\037"
  181.     _ToControl_Map["?"] = "\177"
  182.     for (i = 1; i <= 26; i++) {
  183.         _ToControl_Map[sprintf("%c",i+64)] = sprintf("%c",i)
  184.         _ToControl_Map[sprintf("%c",i+96)] = sprintf("%c",i)
  185.     }
  186.     }
  187.     
  188.     for (i = 1; i <= len; i++)
  189.     if ((c0 = substr(S,i,1)) == "^" && \
  190.     (c = substr(S,i+1,1)) in _ToControl_Map) {
  191.         newS = newS _ToControl_Map[c]
  192.         i += 1
  193.     }
  194.     else
  195.         newS = newS c0
  196.     return newS
  197. }
  198.  
  199. # Convert the chars of string s to their ASCII integer values and print them.
  200. # Each WordLen chars of s are converted as a unit.
  201. # Base is the output radix.
  202. # When WordLen is more than 1, Left determines which end of the substring
  203. # sets which bits of the output word.
  204. # If nDig is nonzero, values are printed with leading zeros to make the
  205. # numeric part be nDig digits long, and a space is printed before the
  206. # numeric value if it is positive.
  207. # If Scale is nonzero, the number is taken to be a fixed-point number; it
  208. # is divided by Scale and converted, with FracDig digits printed after the 
  209. # decimal point.
  210. function ConvertWord(s,Base,WordLen,Left,nDig,Scale,FracDig,SymIn,SymOut,
  211. rawOut,
  212. rem,len,n) {
  213.     if (SymIn)
  214.     s = ToControl(s)
  215.  
  216.     if (SymOut) {
  217.     print Uncontrol(s)
  218.     return
  219.     }
  220.     if (rawOut) {
  221.     print s
  222.     return
  223.     }
  224.  
  225.  
  226.     len = length(s)    # Find number of chars to convert
  227.     
  228.     if (Left)
  229.     n = 1    # If converting left to right, start with char pos 1
  230.     else {
  231.     rem = len % WordLen
  232.     # Do non-integral-word-length prefix first, if any
  233.     if (rem)
  234.         if (Scale)
  235.         printf ftoa(ascW(substr(s,1,rem))/Scale,Base,nDig,FracDig) " "
  236.         else
  237.         printf itoa(ascW(substr(s,1,rem)),Base,nDig) " "
  238.     n = rem + 1
  239.     }
  240.     for (; n <= len; n += WordLen)
  241.     if (Scale)
  242.         printf \
  243.         ftoa(ascW(substr(s,n,WordLen),Left)/Scale,Base,nDig,FracDig) " "
  244.     else
  245.         printf itoa(ascW(substr(s,n,WordLen),Left),Base,nDig) " "
  246.     print ""
  247. }
  248.  
  249. function asc(InC,  c,ascval,b) {
  250.     ascval = 128
  251.     b = 128
  252.     while ((c = sprintf("%c",ascval)) != InC)
  253.     if (c < InC)
  254.         ascval += (b /= 2)
  255.     else
  256.         ascval -= (b /= 2)
  257.     # use int() because if value is InC, the last division of b will give 0.5
  258.     return int(ascval)
  259. }
  260.  
  261. # Convert word of up to 4 chars to big ascii val & return it as an integer.
  262. # If Left is false, rightmost char sets low 8 bits, etc.
  263. # If Left is true, leftmost char sets low 8 bits, etc.
  264. function ascW(S,Left,  l,tot,i) {
  265.     l = length(S)
  266.     if (Left)
  267.     for (i = l; i >= 1; i--)
  268.         tot = tot * 256 + asc(substr(S,i,1))
  269.     else
  270.     for (i = 1; i <= l; i++)
  271.         tot = tot * 256 + asc(substr(S,i,1))
  272.     return tot
  273. }
  274.  
  275. ### start of ntoa lib
  276. # @(#) ntoa 1.0 94/01/01
  277. # Converts integer inval to string representation in base radix & returns it.
  278. # inval is taken to be a signed value; the result is preceded by a minus
  279. # sign if negative.
  280. # If numDig is nonzero, the result (before the minus sign, if any, is added)
  281. # is padded on the left with zeros to make it numDig digits long.
  282. # Then, either the minus sign or (if the result is positive) a space is added.
  283. # This means that the result will always be numDig+1 characters long.
  284. # If the result is longer than numDig before padding, it is left alone.
  285. # If numDig is zero, the leading space is not printed.
  286. # Null is returned on error.
  287. function itoa(inval,radix,numDig,  Buf,value,neg,dig,Sign) {
  288.  
  289.     if (!(2 <= radix && radix <= 36))
  290.     return ""
  291.     if (neg = (inval < 0))
  292.     value = -inval
  293.     else
  294.     value = inval
  295.     if (value == 0)
  296.     Buf = "0"
  297.     while (value > 0) {
  298.     if ((dig = value % radix) > 9)
  299.         # Add digit value to 'a' - 10
  300.         Buf = sprintf("%c",dig + 87) Buf
  301.     else
  302.         # Add digit value to '0'
  303.         Buf = sprintf("%c",dig + 48) Buf
  304.     value = int(value / radix)
  305.     }
  306.     if (neg)
  307.     Sign "-"
  308.     else if (numDig)    # Do this before zeroing numDig
  309.     Sign = " "
  310.     if (numDig)
  311.     for (numDig -= length(Buf); numDig > 0; numDig--)
  312.         Buf = "0" Buf
  313.     return Sign Buf
  314. }
  315.  
  316. # ftoa: Convert a floating-point number to ASCII.
  317. # Converts inval to string representation in base radix & returns it.
  318. # inval is taken to be a signed value; the result is preceded by a minus
  319. # sign if negative.
  320. # fracDig is the number of digits in the output radix to print after the
  321. # decimal point.
  322. # If nDig is nonzero, the result (before the minus sign, if any, is added)
  323. # is padded on the left with zeros to make it nDig digits long.
  324. # Then, either the minus sign or (if the result is positive) a space is added.
  325. # This means that the result will always be nDig+1 characters long.
  326. # If the result is longer than nDig before padding, it is left alone.
  327. # Null is returned on error.
  328. function ftoa(inval,radix,nDig,fracDig,
  329. Buf,value,dig,intPart) {
  330.  
  331.     if (!(2 <= radix && radix <= 36))
  332.     return ""
  333.     intPart = int(inval)
  334.     value = abs(inval - intPart)
  335.     Buf = itoa(int(inval),radix,nDig ? nDig - fracDig - 1 : 0) "."
  336.     for (; fracDig; fracDig--) {
  337.     dig = int(value *= radix)
  338.     # Add digit value to 'a' - 10 or to '0'
  339.     Buf = Buf sprintf("%c",dig + (dig > 9 ? 87 : 48))
  340.     value -= dig
  341.     }
  342.     return Buf
  343. }
  344. ### end of ntoa lib
  345. ### Start of ProcArgs library
  346. # @(#) ProcArgs 1.11 96/12/08
  347. # 92/02/29 john h. dubois iii (john@armory.com)
  348. # 93/07/18 Added "#" arg type
  349. # 93/09/26 Do not count -h against MinArgs
  350. # 94/01/01 Stop scanning at first non-option arg.  Added ">" option type.
  351. #          Removed meaning of "+" or "-" by itself.
  352. # 94/03/08 Added & option and *()< option types.
  353. # 94/04/02 Added NoRCopt to Opts()
  354. # 94/06/11 Mark numeric variables as such.
  355. # 94/07/08 Opts(): Do not require any args if h option is given.
  356. # 95/01/22 Record options given more than once.  Record option num in argv.
  357. # 95/06/08 Added ExclusiveOptions().
  358. # 96/01/20 Let rcfiles be a colon-separated list of filenames.
  359. #          Expand $VARNAME at the start of its filenames.
  360. #          Let varname=0 and -option- turn off an option.
  361. # 96/05/05 Changed meaning of 7th arg to Opts; now can specify exactly how many
  362. #          of the vars should be searched for in the environment.
  363. #          Check for duplicate rcfiles.
  364. # 96/05/13 Return more specific error values.  Note: ProcArgs() and InitOpts()
  365. #          now return various negatives values on error, not just -1, and
  366. #          Opts() may set Err to various positive values, not just 1.
  367. #          Added AllowUnrecOpt.
  368. # 96/05/23 Check type given for & option
  369. # 96/06/15 Re-port to awk
  370. # 96/10/01 Moved file-reading code into ReadConfFile(), so that it can be
  371. #          used by other functions.
  372. # 96/10/15 Added OptChars
  373. # 96/11/01 Added exOpts arg to Opts()
  374. # 96/11/16 Added ; type
  375. # 96/12/08 Added Opt2Set() & Opt2Sets()
  376. # 96/12/27 Added CmdLineOpt()
  377.  
  378. # optlist is a string which contains all of the possible command line options.
  379. # A character followed by certain characters indicates that the option takes
  380. # an argument, with type as follows:
  381. # :    String argument
  382. # ;    Non-empty string argument
  383. # *    Floating point argument
  384. # (    Non-negative floating point argument
  385. # )    Positive floating point argument
  386. # #    Integer argument
  387. # <    Non-negative integer argument
  388. # >    Positive integer argument
  389. # The only difference the type of argument makes is in the runtime argument
  390. # error checking that is done.
  391.  
  392. # The & option is a special case used to get numeric options without the
  393. # user having to give an option character.  It is shorthand for [-+.0-9].
  394. # If & is included in optlist and an option string that begins with one of
  395. # these characters is seen, the value given to "&" will include the first
  396. # char of the option.  & must be followed by a type character other than ":"
  397. # or ";".
  398. # Note that if e.g. &> is given, an option of -.5 will produce an error.
  399.  
  400. # Strings in argv[] which begin with "-" or "+" are taken to be
  401. # strings of options, except that a string which consists solely of "-"
  402. # or "+" is taken to be a non-option string; like other non-option strings,
  403. # it stops the scanning of argv and is left in argv[].
  404. # An argument of "--" or "++" also stops the scanning of argv[] but is removed.
  405. # If an option takes an argument, the argument may either immediately
  406. # follow it or be given separately.
  407. # "-" and "+" options are treated the same.  "+" is allowed because most awks
  408. # take any -options to be arguments to themselves.  gawk 2.15 was enhanced to
  409. # stop scanning when it encounters an unrecognized option, though until 2.15.5
  410. # this feature had a flaw that caused problems in some cases.  See the OptChars
  411. # parameter to explicitly set the option-specifier characters.
  412.  
  413. # If an option that does not take an argument is given,
  414. # an index with its name is created in Options and its value is set to the
  415. # number of times it occurs in argv[].
  416.  
  417. # If an option that does take an argument is given, an index with its name is
  418. # created in Options and its value is set to the value of the argument given
  419. # for it, and Options[option-name,"count"] is (initially) set to the 1.
  420. # If an option that takes an argument is given more than once,
  421. # Options[option-name,"count"] is incremented, and the value is assigned to
  422. # the index (option-name,instance) where instance is 2 for the second occurance
  423. # of the option, etc.
  424. # In other words, the first time an option with a value is encountered, the
  425. # value is assigned to an index consisting only of its name; for any further
  426. # occurances of the option, the value index has an extra (count) dimension.
  427.  
  428. # The sequence number for each option found in argv[] is stored in
  429. # Options[option-name,"num",instance], where instance is 1 for the first
  430. # occurance of the option, etc.  The sequence number starts at 1 and is
  431. # incremented for each option, both those that have a value and those that
  432. # do not.  Options set from a config file have a value of 0 assigned to this.
  433.  
  434. # Options and their arguments are deleted from argv.
  435. # Note that this means that there may be gaps left in the indices of argv[].
  436. # If compress is nonzero, argv[] is packed by moving its elements so that
  437. # they have contiguous integer indices starting with 0.
  438. # Option processing will stop with the first unrecognized option, just as
  439. # though -- was given except that unlike -- the unrecognized option will not be
  440. # removed from ARGV[].  Normally, an error value is returned in this case.
  441. # If AllowUnrecOpt is true, it is not an error for an unrecognized option to
  442. # be found, so the number of remaining arguments is returned instead.
  443. # If OptChars is not a null string, it is the set of characters that indicate
  444. # that an argument is an option string if the string begins with one of the
  445. # characters.  A string consisting solely of two of the same option-indicator
  446. # characters stops the scanning of argv[].  The default is "-+".
  447. # argv[0] is not examined.
  448. # The number of arguments left in argc is returned.
  449. # If an error occurs, the global string OptErr is set to an error message
  450. # and a negative value is returned.
  451. # Current error values:
  452. # -1: option that required an argument did not get it.
  453. # -2: argument of incorrect type supplied for an option.
  454. # -3: unrecognized (invalid) option.
  455. function ProcArgs(argc,argv,OptList,Options,compress,AllowUnrecOpt,OptChars,
  456. ArgNum,ArgsLeft,Arg,ArgLen,ArgInd,Option,Pos,NumOpt,Value,HadValue,specGiven,
  457. NeedNextOpt,GotValue,OptionNum,Escape,dest,src,count,c,OptTerm,OptCharSet)
  458. {
  459. # ArgNum is the index of the argument being processed.
  460. # ArgsLeft is the number of arguments left in argv.
  461. # Arg is the argument being processed.
  462. # ArgLen is the length of the argument being processed.
  463. # ArgInd is the position of the character in Arg being processed.
  464. # Option is the character in Arg being processed.
  465. # Pos is the position in OptList of the option being processed.
  466. # NumOpt is true if a numeric option may be given.
  467.     ArgsLeft = argc
  468.     NumOpt = index(OptList,"&")
  469.     OptionNum = 0
  470.     if (OptChars == "")
  471.     OptChars = "-+"
  472.     while (OptChars != "") {
  473.     c = substr(OptChars,1,1)
  474.     OptChars = substr(OptChars,2)
  475.     OptCharSet[c]
  476.     OptTerm[c c]
  477.     }
  478.     for (ArgNum = 1; ArgNum < argc; ArgNum++) {
  479.     Arg = argv[ArgNum]
  480.     if (length(Arg) < 2 || !((specGiven = substr(Arg,1,1)) in OptCharSet))
  481.         break    # Not an option; quit
  482.     if (Arg in OptTerm) {
  483.         delete argv[ArgNum]
  484.         ArgsLeft--
  485.         break
  486.     }
  487.     ArgLen = length(Arg)
  488.     for (ArgInd = 2; ArgInd <= ArgLen; ArgInd++) {
  489.         Option = substr(Arg,ArgInd,1)
  490.         if (NumOpt && Option ~ /[-+.0-9]/) {
  491.         # If this option is a numeric option, make its flag be & and
  492.         # its option string flag position be the position of & in
  493.         # the option string.
  494.         Option = "&"
  495.         Pos = NumOpt
  496.         # Prefix Arg with a char so that ArgInd will point to the
  497.         # first char of the numeric option.
  498.         Arg = "&" Arg
  499.         ArgLen++
  500.         }
  501.         # Find position of flag in option string, to get its type (if any).
  502.         # Disallow & as literal flag.
  503.         else if (!(Pos = index(OptList,Option)) || Option == "&") {
  504.         if (AllowUnrecOpt) {
  505.             Escape = 1
  506.             break
  507.         }
  508.         else {
  509.             OptErr = "Invalid option: " specGiven Option
  510.             return -3
  511.         }
  512.         }
  513.  
  514.         # Find what the value of the option will be if it takes one.
  515.         # NeedNextOpt is true if the option specifier is the last char of
  516.         # this arg, which means that if the option requires a value it is
  517.         # the next arg.
  518.         if (NeedNextOpt = (ArgInd >= ArgLen)) { # Value is the next arg
  519.         if (GotValue = ArgNum + 1 < argc)
  520.             Value = argv[ArgNum+1]
  521.         }
  522.         else {    # Value is included with option
  523.         Value = substr(Arg,ArgInd + 1)
  524.         GotValue = 1
  525.         }
  526.  
  527.         if (HadValue = AssignVal(Option,Value,Options,
  528.         substr(OptList,Pos + 1,1),GotValue,"",++OptionNum,!NeedNextOpt,
  529.         specGiven)) {
  530.         if (HadValue < 0)    # error occured
  531.             return HadValue
  532.         if (HadValue == 2)
  533.             ArgInd++    # Account for the single-char value we used.
  534.         else {
  535.             if (NeedNextOpt) {    # option took next arg as value
  536.             delete argv[++ArgNum]
  537.             ArgsLeft--
  538.             }
  539.             break    # This option has been used up
  540.         }
  541.         }
  542.     }
  543.     if (Escape)
  544.         break
  545.     # Do not delete arg until after processing of it, so that if it is not
  546.     # recognized it can be left in ARGV[].
  547.     delete argv[ArgNum]
  548.     ArgsLeft--
  549.     }
  550.     if (compress != 0) {
  551.     dest = 1
  552.     src = argc - ArgsLeft + 1
  553.     for (count = ArgsLeft - 1; count; count--) {
  554.         ARGV[dest] = ARGV[src]
  555.         dest++
  556.         src++
  557.     }
  558.     }
  559.     return ArgsLeft
  560. }
  561.  
  562. # Assignment to values in Options[] occurs only in this function.
  563. # Option: Option specifier character.
  564. # Value: Value to be assigned to option, if it takes a value.
  565. # Options[]: Options array to return values in.
  566. # ArgType: Argument type specifier character.
  567. # GotValue: Whether any value is available to be assigned to this option.
  568. # Name: Name of option being processed.
  569. # OptionNum: Number of this option (starting with 1) if set in argv[],
  570. #     or 0 if it was given in a config file or in the environment.
  571. # SingleOpt: true if the value (if any) that is available for this option was
  572. #     given as part of the same command line arg as the option.  Used only for
  573. #     options from the command line.
  574. # specGiven is the option specifier character use, if any (e.g. - or +),
  575. # for use in error messages.
  576. # Global variables: OptErr
  577. # Return value: negative value on error, 0 if option did not require an
  578. # argument, 1 if it did & used the whole arg, 2 if it required just one char of
  579. # the arg.
  580. # Current error values:
  581. # -1: Option that required an argument did not get it.
  582. # -2: Value of incorrect type supplied for option.
  583. # -3: Bad type given for option &
  584. function AssignVal(Option,Value,Options,ArgType,GotValue,Name,OptionNum,
  585. SingleOpt,specGiven,  UsedValue,Err,NumTypes) {
  586.     # If option takes a value...    [
  587.     NumTypes = "*()#<>]"
  588.     if (Option == "&" && ArgType !~ "[" NumTypes) {    # ]
  589.     OptErr = "Bad type given for & option"
  590.     return -3
  591.     }
  592.  
  593.     if (UsedValue = (ArgType ~ "[:;" NumTypes)) {    # ]
  594.     if (!GotValue) {
  595.         if (Name != "")
  596.         OptErr = "Variable requires a value -- " Name
  597.         else
  598.         OptErr = "option requires an argument -- " Option
  599.         return -1
  600.     }
  601.     if ((Err = CheckType(ArgType,Value,Option,Name,specGiven)) != "") {
  602.         OptErr = Err
  603.         return -2
  604.     }
  605.     # Mark this as a numeric variable; will be propogated to Options[] val.
  606.     if (ArgType != ":" && ArgType != ";")
  607.         Value += 0
  608.     if ((Instance = ++Options[Option,"count"]) > 1)
  609.         Options[Option,Instance] = Value
  610.     else
  611.         Options[Option] = Value
  612.     }
  613.     # If this is an environ or rcfile assignment & it was given a value...
  614.     else if (!OptionNum && Value != "") {
  615.     UsedValue = 1
  616.     # If the value is "0" or "-" and this is the first instance of it,
  617.     # do not set Options[Option]; this allows an assignment in an rcfile to
  618.     # turn off an option (for the simple "Option in Options" test) in such
  619.     # a way that it cannot be turned on in a later file.
  620.     if (!(Option in Options) && (Value == "0" || Value == "-"))
  621.         Instance = 1
  622.     else
  623.         Instance = ++Options[Option]
  624.     # Save the value even though this is a flag
  625.     Options[Option,Instance] = Value
  626.     }
  627.     # If this is a command line flag and has a - following it in the same arg,
  628.     # it is being turned off.
  629.     else if (OptionNum && SingleOpt && substr(Value,1,1) == "-") {
  630.     UsedValue = 2
  631.     if (Option in Options)
  632.         Instance = ++Options[Option]
  633.     else
  634.         Instance = 1
  635.     Options[Option,Instance]
  636.     }
  637.     # If this is a flag assignment without a value, increment the count for the
  638.     # flag unless it was turned off.  The indicator for a flag being turned off
  639.     # is that the flag index has not been set in Options[] but it has an
  640.     # instance count.
  641.     else if (Option in Options || !((Option,1) in Options))
  642.     # Increment number of times this flag seen; will inc null value to 1
  643.     Instance = ++Options[Option]
  644.     Options[Option,"num",Instance] = OptionNum
  645.     return UsedValue
  646. }
  647.  
  648. # Option is the option letter
  649. # Value is the value being assigned
  650. # Name is the var name of the option, if any
  651. # ArgType is one of:
  652. # :    String argument
  653. # ;    Non-null string argument
  654. # *    Floating point argument
  655. # (    Non-negative floating point argument
  656. # )    Positive floating point argument
  657. # #    Integer argument
  658. # <    Non-negative integer argument
  659. # >    Positive integer argument
  660. # specGiven is the option specifier character use, if any (e.g. - or +),
  661. # for use in error messages.
  662. # Returns null on success, err string on error
  663. function CheckType(ArgType,Value,Option,Name,specGiven,  Err,ErrStr) {
  664.     if (ArgType == ":")
  665.     return ""
  666.     if (ArgType == ";") {
  667.     if (Value == "")
  668.         Err = "must be a non-empty string"
  669.     }
  670.     # A number begins with optional + or -, and is followed by a string of
  671.     # digits or a decimal with digits before it, after it, or both
  672.     else if (Value !~ /^[-+]?([0-9]+|[0-9]*\.[0-9]+|[0-9]+\.)$/)
  673.     Err = "must be a number"
  674.     else if (ArgType ~ "[#<>]" && Value ~ /\./)
  675.     Err = "may not include a fraction"
  676.     else if (ArgType ~ "[()<>]" && Value < 0)
  677.     Err = "may not be negative"
  678.     # (
  679.     else if (ArgType ~ "[)>]" && Value == 0)
  680.     Err = "must be a positive number"
  681.     if (Err != "") {
  682.     ErrStr = "Bad value \"" Value "\".  Value assigned to "
  683.     if (Name != "")
  684.         return ErrStr "variable " substr(Name,1,1) " " Err
  685.     else {
  686.         if (Option == "&")
  687.         Option = Value
  688.         return ErrStr "option " specGiven substr(Option,1,1) " " Err
  689.     }
  690.     }
  691.     else
  692.     return ""
  693. }
  694.  
  695. # Note: only the above functions are needed by ProcArgs.
  696. # The rest of these functions call ProcArgs() and also do other
  697. # option-processing stuff.
  698.  
  699. # Opts: Process command line arguments.
  700. # Opts processes command line arguments using ProcArgs()
  701. # and checks for errors.  If an error occurs, a message is printed
  702. # and the program is exited.
  703. #
  704. # Input variables:
  705. # Name is the name of the program, for error messages.
  706. # Usage is a usage message, for error messages.
  707. # OptList the option description string, as used by ProcArgs().
  708. # MinArgs is the minimum number of non-option arguments that this
  709. # program should have, non including ARGV[0] and +h.
  710. # If the program does not require any non-option arguments,
  711. # MinArgs should be omitted or given as 0.
  712. # rcFiles, if given, is a colon-seprated list of filenames to read for
  713. # variable initialization.  If a filename begins with ~/, the ~ is replaced
  714. # by the value of the environment variable HOME.  If a filename begins with
  715. # $, the part from the character after the $ up until (but not including)
  716. # the first character not in [a-zA-Z0-9_] will be searched for in the
  717. # environment; if found its value will be substituted, if not the filename will
  718. # be discarded.
  719. # rcfiles are read in the order given.
  720. # Values given in them will not override values given on the command line,
  721. # and values given in later files will not override those set in earlier
  722. # files, because AssignVal() will store each with a different instance index.
  723. # The first instance of each variable, either on the command line or in an
  724. # rcfile, will be stored with no instance index, and this is the value
  725. # normally used by programs that call this function.
  726. # VarNames is a comma-separated list of variable names to map to options,
  727. # in the same order as the options are given in OptList.
  728. # If EnvSearch is given and nonzero, the first EnvSearch variables will also be
  729. # searched for in the environment.  If set to -1, all values will be searched
  730. # for in the environment.  Values given in the environment will override
  731. # those given in the rcfiles but not those given on the command line.
  732. # NoRCopt, if given, is an additional letter option that if given on the
  733. # command line prevents the rcfiles from being read.
  734. # See ProcArgs() for a description of AllowUnRecOpt and optChars, and
  735. # ExclusiveOptions() for a description of exOpts.
  736. # Special options:
  737. # If x is made an option and is given, some debugging info is output.
  738. # h is assumed to be the help option.
  739.  
  740. # Global variables:
  741. # The command line arguments are taken from ARGV[].
  742. # The arguments that are option specifiers and values are removed from
  743. # ARGV[], leaving only ARGV[0] and the non-option arguments.
  744. # The number of elements in ARGV[] should be in ARGC.
  745. # After processing, ARGC is set to the number of elements left in ARGV[].
  746. # The option values are put in Options[].
  747. # On error, Err is set to a positive integer value so it can be checked for in
  748. # an END block.
  749. # Return value: The number of elements left in ARGV is returned.
  750. # Must keep OptErr global since it may be set by InitOpts().
  751. function Opts(Name,Usage,OptList,MinArgs,rcFiles,VarNames,EnvSearch,NoRCopt,
  752. AllowUnrecOpt,optChars,exOpts,  ArgsLeft,e) {
  753.     if (MinArgs == "")
  754.     MinArgs = 0
  755.     ArgsLeft = ProcArgs(ARGC,ARGV,OptList NoRCopt,Options,1,AllowUnrecOpt,
  756.     optChars)
  757.     if (ArgsLeft < (MinArgs+1) && !("h" in Options)) {
  758.     if (ArgsLeft >= 0) {
  759.         OptErr = "Not enough arguments"
  760.         Err = 4
  761.     }
  762.     else
  763.         Err = -ArgsLeft
  764.     printf "%s: %s.\nUse -h for help.\n%s\n",
  765.     Name,OptErr,Usage > "/dev/stderr"
  766.     exit 1
  767.     }
  768.     if (rcFiles != "" && (NoRCopt == "" || !(NoRCopt in Options)) &&
  769.     (e = InitOpts(rcFiles,Options,OptList,VarNames,EnvSearch)) < 0)
  770.     {
  771.     print Name ": " OptErr ".\nUse -h for help." > "/dev/stderr"
  772.     Err = -e
  773.     exit 1
  774.     }
  775.     if ((exOpts != "") && ((OptErr = ExclusiveOptions(exOpts,Options)) != ""))
  776.     {
  777.     printf "%s: Error: %s\n",Name,OptErr > "/dev/stderr"
  778.     Err = 1
  779.     exit 1
  780.     }
  781.     return ArgsLeft
  782. }
  783.  
  784. # ReadConfFile(): Read a file containing var/value assignments, in the form
  785. # <variable-name><assignment-char><value>.
  786. # Whitespace (spaces and tabs) around a variable (leading whitespace on the
  787. # line and whitespace between the variable name and the assignment character) 
  788. # is stripped.  Lines that do not contain an assignment operator or which
  789. # contain a null variable name are ignored, other than possibly being noted in
  790. # the return value.  If more than one assignment is made to a variable, the
  791. # first assignment is used.
  792. # Input variables:
  793. # File is the file to read.
  794. # Comment is the line-comment character.  If it is found as the first non-
  795. #     whitespace character on a line, the line is ignored.
  796. # Assign is the assignment string.  The first instance of Assign on a line
  797. #     separates the variable name from its value.
  798. # If StripWhite is true, whitespace around the value (whitespace between the
  799. #     assignment char and trailing whitespace on the line) is stripped.
  800. # VarPat is a pattern that variable names must match.  
  801. #     Example: "^[a-zA-Z][a-zA-Z0-9]+$"
  802. # If FlagsOK is true, variables are allowed to be "set" by being put alone on
  803. #     a line; no assignment operator is needed.  These variables are set in
  804. #     the output array with a null value.  Lines containing nothing but
  805. #     whitespace are still ignored.
  806. # Output variables:
  807. # Values[] contains the assignments, with the indexes being the variable names
  808. #     and the values being the assigned values.
  809. # Lines[] contains the line number that each variable occured on.  A flag set
  810. #     is record by giving it an index in Lines[] but not in Values[].
  811. # Return value:
  812. # If any errors occur, a string consisting of descriptions of the errors
  813. # separated by newlines is returned.  In no case will the string start with a
  814. # numeric value.  If no errors occur,  the number of lines read is returned.
  815. function ReadConfigFile(Values,Lines,File,Comment,Assign,StripWhite,VarPat,
  816. FlagsOK,
  817. Line,Status,Errs,AssignLen,LineNum,Var,Val) {
  818.     if (Comment != "")
  819.     Comment = "^" Comment
  820.     AssignLen = length(Assign)
  821.     if (VarPat == "")
  822.     VarPat = "."    # null varname not allowed
  823.     while ((Status = (getline Line < File)) == 1) {
  824.     LineNum++
  825.     sub("^[ \t]+","",Line)
  826.     if (Line == "")        # blank line
  827.         continue
  828.     if (Comment != "" && Line ~ Comment)
  829.         continue
  830.     if (Pos = index(Line,Assign)) {
  831.         Var = substr(Line,1,Pos-1)
  832.         Val = substr(Line,Pos+AssignLen)
  833.         if (StripWhite) {
  834.         sub("^[ \t]+","",Val)
  835.         sub("[ \t]+$","",Val)
  836.         }
  837.     }
  838.     else {
  839.         Var = Line    # If no value, var is entire line
  840.         Val = ""
  841.     }
  842.     if (!FlagsOK && Val == "") {
  843.         Errs = Errs \
  844.         sprintf("\nBad assignment on line %d of file %s: %s",
  845.         LineNum,File,Line)
  846.         continue
  847.     }
  848.     sub("[ \t]+$","",Var)
  849.     if (Var !~ VarPat) {
  850.         Errs = Errs sprintf("\nBad variable name on line %d of file %s: %s",
  851.         LineNum,File,Var)
  852.         continue
  853.     }
  854.     if (!(Var in Lines)) {
  855.         Lines[Var] = LineNum
  856.         if (Pos)
  857.         Values[Var] = Val
  858.     }
  859.     }
  860.     if (Status)
  861.     Errs = Errs "\nCould not read file " File
  862.     close(File)
  863.     return Errs == "" ? LineNum : substr(Errs,2)    # Skip first newline
  864. }
  865.  
  866. # Variables:
  867. # Data is stored in Options[].
  868. # rcFiles, OptList, VarNames, and EnvSearch are as as described for Opts().
  869. # Global vars:
  870. # Sets OptErr.  Uses ENVIRON[].
  871. # If anything is read from any of the rcfiles, sets READ_RCFILE to 1.
  872. function InitOpts(rcFiles,Options,OptList,VarNames,EnvSearch,
  873. Line,Var,Pos,Vars,Map,CharOpt,NumVars,TypesInd,Types,Type,Ret,i,rcFile,
  874. fNames,numrcFiles,filesRead,Err,Values,retStr) {
  875.     split("",filesRead,"")    # make awk know this is an array
  876.     NumVars = split(VarNames,Vars,",")
  877.     TypesInd = Ret = 0
  878.     if (EnvSearch == -1)
  879.     EnvSearch = NumVars
  880.     for (i = 1; i <= NumVars; i++) {
  881.     Var = Vars[i]
  882.     CharOpt = substr(OptList,++TypesInd,1)
  883.     if (CharOpt ~ "^[:;*()#<>&]$")
  884.         CharOpt = substr(OptList,++TypesInd,1)
  885.     Map[Var] = CharOpt
  886.     Types[Var] = Type = substr(OptList,TypesInd+1,1)
  887.     # Do not overwrite entries from environment
  888.     if (i <= EnvSearch && Var in ENVIRON &&
  889.     (Err = AssignVal(CharOpt,ENVIRON[Var],Options,Type,1,Var,0)) < 0)
  890.         return Err
  891.     }
  892.  
  893.     numrcFiles = split(rcFiles,fNames,":")
  894.     for (i = 1; i <= numrcFiles; i++) {
  895.     rcFile = fNames[i]
  896.     if (rcFile ~ "^~/")
  897.         rcFile = ENVIRON["HOME"] substr(rcFile,2)
  898.     else if (rcFile ~ /^\$/) {
  899.         rcFile = substr(rcFile,2)
  900.         match(rcFile,"^[a-zA-Z0-9_]*")
  901.         envvar = substr(rcFile,1,RLENGTH)
  902.         if (envvar in ENVIRON)
  903.         rcFile = ENVIRON[envvar] substr(rcFile,RLENGTH+1)
  904.         else
  905.         continue
  906.     }
  907.     if (rcFile in filesRead)
  908.         continue
  909.     # rcfiles are liable to be given more than once, e.g. UHOME and HOME
  910.     # may be the same
  911.     filesRead[rcFile]
  912.     if ("x" in Options)
  913.         printf "Reading configuration file %s\n",rcFile > "/dev/stderr"
  914.     retStr = ReadConfigFile(Values,Lines,rcFile,"#","=",0,"",1)
  915.     if (retStr > 0)
  916.         READ_RCFILE = 1
  917.     else if (ret != "") {
  918.         OptErr = retStr
  919.         Ret = -1
  920.     }
  921.     for (Var in Lines)
  922.         if (Var in Map) {
  923.         if ((Err = AssignVal(Map[Var],
  924.         Var in Values ? Values[Var] : "",Options,Types[Var],
  925.         Var in Values,Var,0)) < 0)
  926.             return Err
  927.         }
  928.         else {
  929.         OptErr = sprintf(\
  930.         "Unknown var \"%s\" assigned to on line %d\nof file %s",Var,
  931.         Lines[Var],rcFile)
  932.         Ret = -1
  933.         }
  934.     }
  935.  
  936.     if ("x" in Options)
  937.     for (Var in Map)
  938.         if (Map[Var] in Options)
  939.         printf "(%s) %s=%s\n",Map[Var],Var,Options[Map[Var]] > \
  940.         "/dev/stderr"
  941.         else
  942.         printf "(%s) %s not set\n",Map[Var],Var > "/dev/stderr"
  943.     return Ret
  944. }
  945.  
  946. # OptSets is a semicolon-separated list of sets of option sets.
  947. # Within a list of option sets, the option sets are separated by commas.  For
  948. # each set of sets, if any option in one of the sets is in Options[] AND any
  949. # option in one of the other sets is in Options[], an error string is returned.
  950. # If no conflicts are found, nothing is returned.
  951. # Example: if OptSets = "ab,def,g;i,j", an error will be returned due to
  952. # the exclusions presented by the first set of sets (ab,def,g) if:
  953. # (a or b is in Options[]) AND (d, e, or f is in Options[]) OR
  954. # (a or b is in Options[]) AND (g is in Options) OR
  955. # (d, e, or f is in Options[]) AND (g is in Options)
  956. # An error will be returned due to the exclusions presented by the second set
  957. # of sets (i,j) if: (i is in Options[]) AND (j is in Options[]).
  958. # todo: make options given on command line unset options given in config file
  959. # todo: that they conflict with.
  960. function ExclusiveOptions(OptSets,Options,
  961. Sets,SetSet,NumSets,Pos1,Pos2,Len,s1,s2,c1,c2,ErrStr,L1,L2,SetSets,NumSetSets,
  962. SetNum,OSetNum) {
  963.     NumSetSets = split(OptSets,SetSets,";")
  964.     # For each set of sets...
  965.     for (SetSet = 1; SetSet <= NumSetSets; SetSet++) {
  966.     # NumSets is the number of sets in this set of sets.
  967.     NumSets = split(SetSets[SetSet],Sets,",")
  968.     # For each set in a set of sets except the last...
  969.     for (SetNum = 1; SetNum < NumSets; SetNum++) {
  970.         s1 = Sets[SetNum]
  971.         L1 = length(s1)
  972.         for (Pos1 = 1; Pos1 <= L1; Pos1++)
  973.         # If any of the options in this set was given, check whether
  974.         # any of the options in the other sets was given.  Only check
  975.         # later sets since earlier sets will have already been checked
  976.         # against this set.
  977.         if ((c1 = substr(s1,Pos1,1)) in Options)
  978.             for (OSetNum = SetNum+1; OSetNum <= NumSets; OSetNum++) {
  979.             s2 = Sets[OSetNum]
  980.             L2 = length(s2)
  981.             for (Pos2 = 1; Pos2 <= L2; Pos2++)
  982.                 if ((c2 = substr(s2,Pos2,1)) in Options)
  983.                 ErrStr = ErrStr "\n"\
  984.                 sprintf("Cannot give both %s and %s options.",
  985.                 c1,c2)
  986.             }
  987.     }
  988.     }
  989.     if (ErrStr != "")
  990.     return substr(ErrStr,2)
  991.     return ""
  992. }
  993.  
  994. # The value of each instance of option Opt that occurs in Options[] is made an
  995. # index of Set[].
  996. # The return value is the number of instances of Opt in Options.
  997. function Opt2Set(Options,Opt,Set,  count) {
  998.     if (!(Opt in Options))
  999.     return 0
  1000.     Set[Options[Opt]]
  1001.     count = Options[Opt,"count"]
  1002.     for (; count > 1; count--)
  1003.     Set[Options[Opt,count]]
  1004.     return count
  1005. }
  1006.  
  1007. # The value of each instance of option Opt that occurs in Options[] that
  1008. # begins with "!" is made an index of nSet[] (with the ! stripped from it).
  1009. # Other values are made indexes of Set[].
  1010. # The return value is the number of instances of Opt in Options.
  1011. function Opt2Sets(Options,Opt,Set,nSet,  count,aSet,ret) {
  1012.     ret = Opt2Set(Options,Opt,aSet)
  1013.     for (value in aSet)
  1014.     if (substr(value,1,1) == "!")
  1015.         nSet[substr(value,2)]
  1016.     else
  1017.         Set[value]
  1018.     return ret
  1019. }
  1020.  
  1021. # Returns true if option Opt was given on the command line.
  1022. function CmdLineOpt(Options,Opt,  i) {
  1023.     for (i = 1; (Opt,"num",i) in Options; i++)
  1024.     if (Options[Opt,"num",i] != 0)
  1025.         return 1
  1026.     return 0
  1027. }
  1028. ### End of ProcArgs library
  1029.  
  1030. function abs(value) {
  1031.     if (value >= 0)
  1032.     return value
  1033.     else
  1034.     return -value
  1035. }
  1036. ### Begin UnControl routines
  1037.  
  1038. # @(#) uncontrol.awk 1.1 96/05/29
  1039. # 92/11/09 john h. dubois iii (john@armory.com)
  1040. # 96/05/29 Added octal-only conversion.
  1041.  
  1042. # Uncontrol(S): Convert control characters in S to symbolic form.
  1043. # Characters in S with values < 32 and with value 127 are converted to the form
  1044. # ^X.  Characters with value >= 128 are converted to the octal form \0nnn,
  1045. # where nnn is the octal value of the character.
  1046. # The resulting string is returned.
  1047. # If OctalOnly is true, octal numbers are used for all symbolic values instead
  1048. # of ^X.
  1049. # Global variables: UncTable[] and char2octal[].
  1050. function Uncontrol(S,OctalOnly,  i,len,Output) {
  1051.     len = length(S)
  1052.     Output = ""
  1053.     if (!("a" in UncTable))
  1054.     MakeUncontrolTable()
  1055.     for (i = 1; i <= len; i++)
  1056.     Output = Output \
  1057.     (OctalOnly ? char2octal[substr(S,i,1)] : UncTable[substr(S,i,1)])
  1058.     return Output
  1059. }
  1060.  
  1061. # MakeUncontrolTable: Make tables for use by Uncontrol().
  1062. # Global variables:
  1063. # UncTable[] is made into a character -> symbolic character lookup table
  1064. # with characters with values < 32 and with value 127 converted to the form
  1065. # ^X, and characters with value >= 128 are converted to the octal form \0nnn.
  1066. # char2octal[] is made into a similar table but with all non-printing chars
  1067. # in the form \0nnn.
  1068. function MakeUncontrolTable(  i,c) {
  1069.     for (i = 0; i < 32; i++) {
  1070.     UncTable[c = sprintf("%c",i)] = "^" sprintf("%c",i + 64)
  1071.     char2octal[c] = "\\" sprintf("%03o",i)
  1072.     }
  1073.     for (i = 32; i < 127; i++) {
  1074.     c = sprintf("%c",i)
  1075.     char2octal[c]  = UncTable[c] = sprintf("%c",i)
  1076.     }
  1077.     UncTable[c = sprintf("%c",127)] = "^?"
  1078.     char2octal[c] = "\\0177"
  1079.     for (i = 128; i < 256; i++) {
  1080.     UncTable[c = sprintf("%c",i)] = "\\" sprintf("%03o",i)
  1081.     char2octal[c] = "\\" sprintf("%03o",i)
  1082.     }
  1083. }
  1084.  
  1085. ### End UnControl routines
  1086.