home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / unix / volume27 / encode / part01 < prev    next >
Encoding:
Text File  |  1993-09-12  |  35.7 KB  |  1,242 lines

  1. Newsgroups: comp.sources.unix
  2. From: tcl@hellfudge.asd.sgi.com (Tom Lawrence)
  3. Subject: v27i035: encode - utilities encode/decode binary files in ascii format, Part01/01
  4. Message-id: <1.747861129.4245@gw.home.vix.com>
  5. Sender: unix-sources-moderator@gw.home.vix.com
  6. Approved: vixie@gw.home.vix.com
  7.  
  8. Submitted-By: tcl@hellfudge.asd.sgi.com (Tom Lawrence)
  9. Posting-Number: Volume 27, Issue 35
  10. Archive-Name: encode/part01
  11.  
  12.  ----------- What are encode/decode?
  13.  
  14. Encode and decode are utilities which encode binary data into printable
  15. format suitable for transmission via email, posting to usenet, etc. They are
  16. intended to replace the aging uuencode and uudecode.
  17.  
  18.  ----------- Features:
  19.  
  20. Encode features a very flexible encoding scheme which allows the user to
  21. specify exactly which printable characters to use in the output.  The
  22. default is to use all 95 printable characters in the encoding process, as
  23. this produces the least expansion of the input data.  However, for cases
  24. such as file transfer to a mainframe or to a foreign country where some
  25. characters may be modified en route, these characters can simply be removed
  26. from the output character set.  Encoding is possible with as few as 2
  27. characters in the output character set.
  28.  
  29. Regardless of how many characters are specified in the output character set,
  30. encode only expands the data by a factor very close to the theoretical limit
  31. for that number of characters. (see next section)
  32.  
  33. The implementation is simple (less than 500 lines total without comments) and
  34. efficient (runs at a speed comparable to uuencode/uudecode)
  35.  
  36. #! /bin/sh
  37. # This is a shell archive.  Remove anything before this line, then unpack
  38. # it by saving it into a file and typing "sh file".  To overwrite existing
  39. # files, type "sh file -c".  You can also feed this as standard input via
  40. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  41. # will see the following message at the end:
  42. #        "End of archive 1 (of 1)."
  43. # Contents:  MANIFEST Makefile README codes.c codes.h decode.1 decode.c
  44. #   encode.1 encode.c
  45. # Wrapped by vixie@gw.home.vix.com on Sun Sep 12 12:10:53 1993
  46. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  47. if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  48.   echo shar: Will not clobber existing file \"'MANIFEST'\"
  49. else
  50. echo shar: Extracting \"'MANIFEST'\" \(393 characters\)
  51. sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
  52. X   File Name        Archive #    Description
  53. X-----------------------------------------------------------
  54. X MANIFEST                   1    This shipping list
  55. X Makefile                   1    
  56. X README                     1    
  57. X codes.c                    1    
  58. X codes.h                    1    
  59. X decode.1                   1    
  60. X decode.c                   1    
  61. X encode.1                   1    
  62. X encode.c                   1    
  63. END_OF_FILE
  64. if test 393 -ne `wc -c <'MANIFEST'`; then
  65.     echo shar: \"'MANIFEST'\" unpacked with wrong size!
  66. fi
  67. # end of 'MANIFEST'
  68. fi
  69. if test -f 'Makefile' -a "${1}" != "-c" ; then 
  70.   echo shar: Will not clobber existing file \"'Makefile'\"
  71. else
  72. echo shar: Extracting \"'Makefile'\" \(601 characters\)
  73. sed "s/^X//" >'Makefile' <<'END_OF_FILE'
  74. X# $Header: /d/tcl/src/uutar/RCS/Makefile,v 1.2.1.2 1993/09/10 21:39:24 tcl Exp $
  75. X
  76. XCC = cc 
  77. XCFLAGS = -O
  78. X
  79. XCOMMON_SRCS = codes.c
  80. XCOMMON_BINARIES = ${COMMON_SRCS:.c=.o}
  81. X
  82. XENCODE_SRCS = encode.c
  83. XENCODE_BINARIES = ${ENCODE_SRCS:.c=.o}
  84. X
  85. XDECODE_SRCS = decode.c
  86. XDECODE_BINARIES = ${DECODE_SRCS:.c=.o}
  87. X
  88. Xdefault: encode decode
  89. X
  90. Xencode:        $(COMMON_BINARIES) $(ENCODE_BINARIES)
  91. X        $(CC) -o encode $(COMMON_BINARIES) $(ENCODE_BINARIES)
  92. X
  93. Xdecode:        $(COMMON_BINARIES) $(DECODE_BINARIES)
  94. X        $(CC) -o decode $(COMMON_BINARIES) $(DECODE_BINARIES)
  95. X
  96. Xclean:
  97. X        @touch bunk.o bunk~ encode decode
  98. X        /bin/rm -f *.o *~ encode decode
  99. END_OF_FILE
  100. if test 601 -ne `wc -c <'Makefile'`; then
  101.     echo shar: \"'Makefile'\" unpacked with wrong size!
  102. fi
  103. # end of 'Makefile'
  104. fi
  105. if test -f 'README' -a "${1}" != "-c" ; then 
  106.   echo shar: Will not clobber existing file \"'README'\"
  107. else
  108. echo shar: Extracting \"'README'\" \(8859 characters\)
  109. sed "s/^X//" >'README' <<'END_OF_FILE'
  110. X$Header: /usr/people/tcl/src/uutar/RCS/README,v 1.3 1993/09/12 00:40:52 tcl Exp $
  111. X
  112. X----------- What are encode/decode?
  113. X
  114. XEncode and decode are utilities which encode binary data into
  115. Xprintable format suitable for transmission via email, posting to
  116. Xusenet, etc. They are intended to replace the aging uuencode and
  117. Xuudecode.
  118. X
  119. X----------- Features:
  120. X
  121. XEncode features a very flexible encoding scheme which allows the user
  122. Xto specify exactly which printable characters to use in the output.
  123. XThe default is to use all 95 printable characters in the encoding
  124. Xprocess, as this produces the least expansion of the input data.
  125. XHowever, for cases such as file transfer to a mainframe or to a
  126. Xforeign country where some characters may be modified en route, these
  127. Xcharacters can simply be removed from the output character set.
  128. XEncoding is possible with as few as 2 characters in the output
  129. Xcharacter set.
  130. X
  131. XRegardless of how many characters are specified in the output
  132. Xcharacter set, encode only expands the data by a factor very close to
  133. Xthe theoretical limit for that number of characters. (see next
  134. Xsection)
  135. X
  136. XMy implementation is simple (less than 500 lines total without
  137. Xcomments) and efficient (runs at a speed comparable to
  138. Xuuencode/uudecode)
  139. X
  140. X----------- Some theory on file expansion during encoding:
  141. X
  142. XThe number of bits required to encode n distinct values is log2(n)
  143. X(log base 2 of n). For example, to encode 256 distinct values, you
  144. Xneed log2(256) = 8 bits. Let's think of the input file before encoding
  145. Xas a raw stream of bits without byte boundaries. If we want to
  146. Xrepresent this data with 256 distinct characters, we will consume 8
  147. Xbits of the input bitstream per output character. This is how files
  148. Xare normally encoded. However, if we can't use all 256 output
  149. Xcharacters, we will consume fewer than 8 input bits per output
  150. Xcharacter, and thus we will require more output characters to
  151. Xrepresent the input bitstream than if we had 256 output characters.
  152. XThus, the process of encoding a binary file in printable format will
  153. Xnecessarily expand the file. For example if we use the 95 printable
  154. Xcharacters, we'll consume an average of log2(95) = 6.57 bits in the
  155. Xinput stream for each output character. Thus the file will be expanded
  156. Xby a factor of log2(256)/log2(95) = log(256)/log(95) = 1.217 or 21.7%.
  157. XNote that this is a theoretical figure. In practice, we can't
  158. Xsubdivide bits, but this figure does provide a theoretical estimate of
  159. Xthe smallest amount of expansion we can hope to get with n output
  160. Xcharacters. In practice some coding schemes should be able to do
  161. Xbetter for select cases, but for a very large sample space of random
  162. Xdata, no encoding scheme should ever be able to do better than this
  163. Xtheoretical limit.
  164. X
  165. XUuencode maps 3 input characters to 4 output characters for an
  166. Xexpansion of 33% (not including control information). Lately several
  167. Xencoding schemes which map 4 input characters to 5 output characters
  168. Xhave popped up, for an expansion of 25%.
  169. X
  170. XAn analysis of encode shows that the average expansion over a very
  171. Xlarge input file of random data is 
  172. X8 / (pb - 2 + 2n/p)
  173. Xwhere n is the number of output characters, p is the smallest power of
  174. X2 greater than or equal to n, and pb is log2(p), or the number of bits
  175. Xneeded to represent p values. A graph of this function for values of n
  176. Xfrom 2 to 256 shows a very close approximation of the theoretical
  177. Xexpansion of log(256)/log(n). For example, for n = 95, the expansion
  178. Xfactor is
  179. X8 / (7 - 2 + 2*95/128) = 1.234 or 23.4%
  180. X
  181. XNote that all expansion factors given above fail to take into account
  182. Xthe addition of newline characters to limit output width.
  183. X
  184. X----------- The encoding process:
  185. X
  186. XThe encoding process used by encode is simply to throw away the byte
  187. Xboundaries in the input bitstream and insert new byte boundaries in
  188. Xsuch a manner that there are only n distinct "tokens" in the input
  189. Xstream where n is the number of output characters. These tokens can
  190. Xthen be mapped one-to-one with the output characters, both during
  191. Xencoding and decoding. A good example of this process is uuencode,
  192. Xwhich discards the byte boundaries which occur every 8 bits and
  193. Xinserts byte boundaries every 6 bits. The result is a series of tokens
  194. Xwith a maximum of 64 possible values, each of which is mapped
  195. Xone-to-one with the output character set of 64 printable characters.
  196. XThis process is trivial for any n which is a power of two, you simply
  197. Xinsert byte boundaries every log2(n) bits. When n is not a power of 2,
  198. Xhowever, the process is somewhat more complicated.
  199. X
  200. XWe can no longer insert the byte boundaries at regular intervals of b
  201. Xbits, since this would imply 2^b output characters. If we select b
  202. Xsuch that 2^b < n, then we aren't using all n output characters, and
  203. Xwe're expanding the file more than necessary. On the other hand if we
  204. Xselect b such that 2^b > n, we don't have enough output characters to
  205. Xencode the data. The solution is to start with the smallest b such
  206. Xthat 2^b >= n and then eliminate some of the input tokens until there
  207. Xare exactly n of them, then we can map one-to-one with the output
  208. Xcharacters. Input tokens can be eliminated by taking two input tokens
  209. Xand combining them to form a single, shorter token. This is best
  210. Xexplained by giving an example.
  211. X
  212. XLet's say we have 6 output characters. We start with 8 input tokens:
  213. X000,001,010,011,100,101,110,111
  214. XThis set of tokens has the property that any input bitstream can
  215. Xbe broken down to a series of these tokens in exactly one way.
  216. XNow let's combine two of the tokens. The tokens to be combined must
  217. Xhave identical bits except for the last bit, and the process of
  218. Xcombining strips that bit from the tokens. e.g. 110 and 111 can be
  219. Xcombined into the token 11, so we now have the token set
  220. X000,001,010,011,100,101,11
  221. XIf we combine two more tokens, 100 and 101 -> 10, we get
  222. X000,001,010,011,10,11
  223. XThis token set still has the property that any input bitstream can be
  224. Xbroken down into a series of these tokens in exactly one way, and
  225. Xsince there are 6 of them, we can map one-to-one with the output
  226. Xcharacter set.
  227. X
  228. XThe standard for the generation of these tokens will be as follows:
  229. XStart with 2^b distinct tokens of length b bits, where b is the
  230. Xsmallest integer such that 2^b >= n, where n is the number of output
  231. Xcharacters. Then, as above, while there are more than n tokens of any
  232. Xlength, replace the two numerically greatest b length tokens with a
  233. Xsingle b-1 length token such that the b-1 length token is equivalent
  234. Xto the b-1 most significant bits of either b length token. (It is
  235. Xasserted that at any time in the procedure, the two numerically
  236. Xgreatest b length tokens differ only in the least significant bit).
  237. X
  238. XThe standard for the one-to-one mapping between tokens and output
  239. Xcharacters will be as follows: tokens will be sorted such that all b
  240. Xlength tokens come first, in numerical order, followed by all b-1
  241. Xlength tokens, in numerical order. Output characters will be sorted by
  242. Xascii code in numerical order. A one-to-one mapping will be
  243. Xestablished between these two sets.
  244. X
  245. XThe standard for the checksum will be as follows: The checksum will be
  246. Xcomputed on the decoded data. It will be 32 bits wide. For each
  247. Xcharacter read from the input file during encoding or written to the
  248. Xoutput file diring decoding, the checksum will first be rolled 7 bits
  249. Xto the left (the 7 bits which slide off the MSB end will be reinserted
  250. Xinto the LSB end) and then the character will be xor'd onto the low
  251. Xorder 8 bits of the checksum.
  252. X
  253. X----------- Implementation:
  254. X
  255. XDecoding with this scheme is trivial: you simply map the printable
  256. Xcharacter from the input to the corresponding variable length token,
  257. Xand then append that token to the decoded bitstream.
  258. X
  259. XEncoding is a bit more tricky however, since the token length is
  260. Xvariable, and the input bitstream has no token boundaries in it. The
  261. Xsolution is to set up a 256 element array which is indexed by the next
  262. X8 bits in the input bitstream. Note that these 8 bits are not
  263. Xnecessarily byte-aligned in the input file. The indexed element in the
  264. Xarray will indicate how many bits should be consumed in the input, and
  265. Xwhat printable character to append to the output. For example, in
  266. Xorder to recognize the token 010, all elements of the array whose
  267. Xindex is 010xxxxx for all xxxxx should be set up to indicate that 3
  268. Xbits were seen and give the printable character that maps to 010. The
  269. Xinput bitstream will then be advanced by 3 bits and the operation is
  270. Xrepeated, using the next 8 bits to index the array again.
  271. X
  272. XMy implementation of this encoding process is fairly simplistic and
  273. Xincorporates no more than the basic functionality provided by
  274. Xuuencode/uudecode. It is intended primarily to introduce this encoding
  275. Xscheme to the public in the hopes that it will be widely adopted.
  276. XShould such adoption occur, this file should be used as a standard
  277. Xreference for the encoding algorithm.
  278. X
  279. END_OF_FILE
  280. if test 8859 -ne `wc -c <'README'`; then
  281.     echo shar: \"'README'\" unpacked with wrong size!
  282. fi
  283. # end of 'README'
  284. fi
  285. if test -f 'codes.c' -a "${1}" != "-c" ; then 
  286.   echo shar: Will not clobber existing file \"'codes.c'\"
  287. else
  288. echo shar: Extracting \"'codes.c'\" \(5195 characters\)
  289. sed "s/^X//" >'codes.c' <<'END_OF_FILE'
  290. X/*
  291. X * $Header: /usr/people/tcl/src/uutar/RCS/codes.c,v 1.1.1.4 1993/09/11 22:42:44 tcl Exp $
  292. X * Tom Lawrence
  293. X * tcl@sgi.com
  294. X */
  295. X
  296. X#include <stdio.h>
  297. X#include <stdlib.h>
  298. X#include "codes.h"
  299. X
  300. X/* see codes.h */
  301. Xint numchars;
  302. Xstruct code codes[256];
  303. X
  304. X/* initialize a subset of the codes array. val and len define a variable
  305. X * length bitfield. The array elements will be set up so that any element
  306. X * of the array whose index is a left-aligned superset of this bitfield
  307. X * will contain the given output ascii character. E.g. if the bitfield is
  308. X * 10010, then all elements in the array with subscript 10010xxx for all
  309. X * xxx, will store the given ascii code, and the length of the bitfield,
  310. X * in this case 5.
  311. X */
  312. Xstatic void
  313. Xinit_encodeval(codes, val, len, ascii)
  314. X    struct code *codes;
  315. X    int val;
  316. X    int len;
  317. X    int ascii;
  318. X{
  319. X    int shift, stop;
  320. X
  321. X    /* determine how far the code must be shifted to be 
  322. X     * MSB justified in the byte
  323. X     */
  324. X    shift = 8 - len;
  325. X
  326. X    /* calculate the upper bound of indices which this bitfield
  327. X     * will match
  328. X     */
  329. X    stop = (val + 1) << shift;
  330. X
  331. X    /* shift the code over to the left edge of the byte */
  332. X    val <<= shift;
  333. X
  334. X    /* thus, for every index in the 256 element array which has
  335. X     * this code as a prefix
  336. X     */
  337. X    for(; val < stop; val++) {
  338. X    /* store the code length and the printable character 
  339. X     * it represents
  340. X     */
  341. X    codes[val].len = (char)len;
  342. X    codes[val].code = (char)ascii;
  343. X    }
  344. X}
  345. X
  346. X/* convert an ascii character code to an integer. The character code may
  347. X * be in decimal, hex or octal, or it may be an actual character escaped
  348. X * with a back-slash
  349. X */
  350. Xstatic int
  351. Xstr2val(str)
  352. X    char *str;
  353. X{
  354. X    int val;
  355. X    char *end;
  356. X
  357. X    while(*str == ' ' || *str == '\t')
  358. X    str++;
  359. X
  360. X    /* check if this is an escaped character */
  361. X    if (*str == '\\') {
  362. X    str++;
  363. X    if (*str == 0) {
  364. X        fprintf(stderr, "missing character in alphabet\n");
  365. X        exit(1);
  366. X    }
  367. X    return((int)*str);
  368. X    }
  369. X
  370. X    val = (int)strtol(str, &end, 0);
  371. X    if (end == str) {
  372. X    if (*str)
  373. X        fprintf(stderr, "invalid char \'%c\' in alphabet\n", *str);
  374. X    else
  375. X        fprintf(stderr, "empty numerical field in alphabet\n");
  376. X    exit(1);
  377. X    }
  378. X    return(val);
  379. X}
  380. X
  381. X/* parse a range of characters for the output character set and mark each
  382. X * character as in use in the codes array. A range is either in the form
  383. X * num-num or just num
  384. X */
  385. Xstatic void
  386. Xparse_charval_range(range)
  387. X    char *range;
  388. X{
  389. X    char *c, savec = 0;
  390. X    int start, end, x;
  391. X
  392. X    for(c = range; *c && *c != '-'; c++);
  393. X    savec = *c;
  394. X    *c = 0;
  395. X
  396. X    start = str2val(range);
  397. X    if (savec == '-') {
  398. X    end = str2val(c + 1);
  399. X    *c = savec;
  400. X    for(x = start; x <= end; x++)
  401. X        codes[x].inuse = 1;
  402. X    }
  403. X    else
  404. X    codes[start].inuse = 1;
  405. X}
  406. X
  407. X/* parse a list of character ranges for the output character set and then
  408. X * parse each range found. A list is of the form range,range,...
  409. X */
  410. Xvoid
  411. Xparse_charval_list(list)
  412. X    char *list;
  413. X{
  414. X    char *c1, *c2, savec2;
  415. X    int x;
  416. X
  417. X    for(x = 0; x < 256; x++)
  418. X    codes[x].inuse = 0;
  419. X
  420. X    c1 = list;
  421. X    while(*c1) {
  422. X    while(*c1 == ',')
  423. X        c1++;
  424. X    if (*c1 == 0)
  425. X        return;
  426. X    for(c2 = c1; *c2 && *c2 != ','; c2++);
  427. X    savec2 = *c2;
  428. X    *c2 = 0;
  429. X    parse_charval_range(c1);
  430. X    *c2 = savec2;
  431. X    c1 = c2;
  432. X    }
  433. X    return;
  434. X}
  435. X
  436. X/* print out the character set in the form of a list of ranges, encoded
  437. X * in decimal
  438. X */
  439. Xvoid
  440. Xprint_charval_list(fp)
  441. X    FILE *fp;
  442. X{
  443. X    int x, usecomma;
  444. X
  445. X    usecomma = 0;
  446. X    for(x = 0; x < 256; x++) {
  447. X    if (codes[x].inuse) {
  448. X        if (usecomma)
  449. X        putc(',', fp);
  450. X        fprintf(fp, "%d", x);
  451. X        usecomma = 1;
  452. X        if (x < 255 && codes[x+1].inuse) {
  453. X        putc('-', fp);
  454. X        while(++x < 256 && codes[x].inuse);
  455. X        fprintf(fp, "%d", x-1);
  456. X        }
  457. X    }
  458. X    }
  459. X}
  460. X
  461. X/*
  462. X * Initialize the tables for encoding or decoding depending on the given
  463. X * direction. 
  464. X */
  465. Xvoid
  466. Xinit_codes(direction)
  467. X    int direction;
  468. X{
  469. X    int x, code, numchars;
  470. X    int pof2, pof2len, half, whole;
  471. X
  472. X    /* count how big our character set is */
  473. X    numchars = 0;
  474. X    for(x = 0; x < 256; x++)
  475. X    if (codes[x].inuse)
  476. X        numchars++;
  477. X
  478. X    if (numchars < 2) {
  479. X    fprintf(stderr,
  480. X        "uutar: alphabet doesn't contain enough characters.\n");
  481. X    exit(1);
  482. X    }
  483. X
  484. X    /* determine the lowest power of 2 that is >= numchars, and the number
  485. X     * of bits needed to store that many values.
  486. X     */
  487. X    for(pof2 = 2, pof2len = 1; pof2 < numchars;
  488. X    pof2 <<= 1, pof2len++);
  489. X
  490. X    /* compute how many half codes we need */
  491. X    half = pof2 - numchars;
  492. X
  493. X    /* compute how many whole codes we need */
  494. X    whole = numchars - half;
  495. X
  496. X    /* create a variable length code for each valid entry */
  497. X    code = 0;
  498. X    x = -1;
  499. X
  500. X    /* create the whole codes */
  501. X    while(whole--) {
  502. X    /* get next slot */
  503. X    do x++; while(codes[x].inuse == 0);
  504. X
  505. X    if (direction == DECODE) {
  506. X        codes[x].code = (char)code;
  507. X        codes[x].len = (char)pof2len;
  508. X    }
  509. X    else
  510. X        init_encodeval(codes, code, pof2len, x);
  511. X    code++;
  512. X    }
  513. X    
  514. X    /* chop off LSB to form the half codes */
  515. X    code >>= 1;
  516. X    pof2len--;
  517. X    
  518. X    /* create the half codes */
  519. X    while(half--) {
  520. X    do x++; while(codes[x].inuse == 0);
  521. X
  522. X    if (direction == DECODE) {
  523. X        codes[x].code = (char)code;
  524. X        codes[x].len = (char)pof2len;
  525. X    }
  526. X    else
  527. X        init_encodeval(codes, code, pof2len, x);
  528. X    code++;
  529. X    }
  530. X}
  531. END_OF_FILE
  532. if test 5195 -ne `wc -c <'codes.c'`; then
  533.     echo shar: \"'codes.c'\" unpacked with wrong size!
  534. fi
  535. # end of 'codes.c'
  536. fi
  537. if test -f 'codes.h' -a "${1}" != "-c" ; then 
  538.   echo shar: Will not clobber existing file \"'codes.h'\"
  539. else
  540. echo shar: Extracting \"'codes.h'\" \(1188 characters\)
  541. sed "s/^X//" >'codes.h' <<'END_OF_FILE'
  542. X/*
  543. X * $Header: /usr/people/tcl/src/uutar/RCS/codes.h,v 1.1.1.2 1993/09/11 18:41:46 tcl Exp $
  544. X * Tom Lawrence
  545. X * tcl@sgi.com
  546. X */
  547. X
  548. X/* number of printable characters in output character set */
  549. Xextern int numchars;
  550. X
  551. X/* encoding/decoding table. inuse indicates whether or not the character
  552. X * whose ascii code is the offset into this array is part of the output 
  553. X * printable character set.
  554. X *
  555. X * When encoding, the next 8 bits (not necessarily byte aligned) in the
  556. X * input binary bitstream are used to index into this array. The code
  557. X * field then indicates the printable output character to append to the
  558. X * output, and the len field indicates how many of the input 8 bits
  559. X * should be comsumed by this operation, i.e. the input bitstream is
  560. X * advanced by len bits.
  561. X *
  562. X * When decoding, the input printable ascii character is used to index
  563. X * into this array. The variable length (8 bits or less) bitfield stored
  564. X * in code and whose length is len, is appended to the output binary
  565. X * bitstream.
  566. X */
  567. Xextern struct code {
  568. X    char inuse;
  569. X    char code;
  570. X    char len;
  571. X} codes[256];
  572. X
  573. Xvoid init_codes();
  574. Xvoid parse_charval_list();
  575. Xvoid print_charval_list();
  576. X
  577. X#define ENCODE 0
  578. X#define DECODE 1
  579. END_OF_FILE
  580. if test 1188 -ne `wc -c <'codes.h'`; then
  581.     echo shar: \"'codes.h'\" unpacked with wrong size!
  582. fi
  583. # end of 'codes.h'
  584. fi
  585. if test -f 'decode.1' -a "${1}" != "-c" ; then 
  586.   echo shar: Will not clobber existing file \"'decode.1'\"
  587. else
  588. echo shar: Extracting \"'decode.1'\" \(717 characters\)
  589. sed "s/^X//" >'decode.1' <<'END_OF_FILE'
  590. X.\" $Header: /usr/people/tcl/src/uutar/RCS/decode.1,v 1.1 1993/09/11 20:06:09 tcl Exp $
  591. X.TH decode 1 "11 Sept 1993"
  592. X.SH NAME
  593. Xdecode \- decode a file encoded with the encode(1) utility
  594. X.SH SYNOPSIS
  595. X.B decode
  596. X[
  597. X.B \-i \c
  598. X.I <inputfile>
  599. X]
  600. X[
  601. X.B \-o \c
  602. X.I <outputfile>
  603. X]
  604. X.SH DESCRIPTION
  605. X.LP
  606. XDecode decodes a file which has been encoded in printable format with
  607. Xthe encode(1) utility.
  608. X.SH OPTIONS
  609. X.TP
  610. X.B \-i\c
  611. X.I <inputfile>
  612. X.br
  613. Xspecifies the file to read input from. If this argument is omitted,
  614. Xstdin is used.
  615. X.TP
  616. X.B \-o\c
  617. X.I <outputfile>
  618. X.br
  619. Xspecifies the file to write output to. If this argument is omitted,
  620. Xthe name of the output file is obtained from the first line of the
  621. Xinput file.
  622. X.SH "SEE ALSO"
  623. X.BR encode (1),
  624. END_OF_FILE
  625. if test 717 -ne `wc -c <'decode.1'`; then
  626.     echo shar: \"'decode.1'\" unpacked with wrong size!
  627. fi
  628. # end of 'decode.1'
  629. fi
  630. if test -f 'decode.c' -a "${1}" != "-c" ; then 
  631.   echo shar: Will not clobber existing file \"'decode.c'\"
  632. else
  633. echo shar: Extracting \"'decode.c'\" \(5584 characters\)
  634. sed "s/^X//" >'decode.c' <<'END_OF_FILE'
  635. X/*
  636. X * $Header: /usr/people/tcl/src/uutar/RCS/decode.c,v 1.1.1.3 1993/09/11 18:42:17 tcl Exp $
  637. X * Tom Lawrence
  638. X * tcl@sgi.com
  639. X */
  640. X
  641. X#include <stdio.h>
  642. X#include <fcntl.h>
  643. X#include <stdlib.h>
  644. X#include <string.h>
  645. X#include <strings.h>
  646. X#include <ctype.h>
  647. X#include "codes.h"
  648. X
  649. X/*
  650. X * given a string with n tokens separated by white space in it, and a
  651. X * pointer to a char vector, create a vector with each pointer pointing
  652. X * to a successive token and null terminate the tokens.  Return the
  653. X * number of tokens or -1 on error.  This routine is destructive to the
  654. X * passed string
  655. X */
  656. X#define IS_WHITE_SPACE(c) (c == ' ' || c == '\t')
  657. X#define VECLEN 10
  658. X
  659. XFILE *infp, *outfp;
  660. X
  661. Xstatic int
  662. Xtokenize(string, vector)
  663. X    char *string;
  664. X    char **vector;
  665. X{
  666. X    int tokens;
  667. X    enum {
  668. X    WHITE_SPACE,
  669. X    TOKEN
  670. X    } state;
  671. X    char *c;
  672. X
  673. X    /* scan through the string setting up the vector pointers and
  674. X     * null terminating the tokens
  675. X     */
  676. X    tokens = 0;
  677. X    state = WHITE_SPACE;
  678. X
  679. X    for(c = string; *c; c++) {
  680. X    if (state == WHITE_SPACE && !IS_WHITE_SPACE(*c)) {
  681. X        /* just hit beginning of a token */
  682. X        vector[tokens] = c;
  683. X        tokens++;
  684. X        state = TOKEN;
  685. X
  686. X        if (tokens >= VECLEN) {
  687. X        fprintf(stderr, "too many tokens in input\n");
  688. X        exit(1);
  689. X        }
  690. X    }
  691. X    else if (state == TOKEN && IS_WHITE_SPACE(*c)) {
  692. X        /* just ended a token */
  693. X        *c = 0;
  694. X        state = WHITE_SPACE;
  695. X    }
  696. X    }
  697. X    return(tokens);
  698. X}
  699. X
  700. X/* normally I'd use strtol for this, but strtol can't handle
  701. X * unsigned values greater than 0x7FFFFFFF on some machines.
  702. X */
  703. Xstatic unsigned int
  704. Xhex2long(str)
  705. X    char *str;
  706. X{
  707. X    unsigned long ret = 0;
  708. X    char *c, c1;
  709. X
  710. X    for(c = str; *c; c++) {
  711. X    c1 = *c;
  712. X    if (c1 >= '0' && c1 <= '9')
  713. X        c1 -= '0';
  714. X    else if (c1 >= 'a' && c1 <= 'f')
  715. X        c1 -= ('a' - 10);
  716. X    else if (c1 >= 'A' && c1 <= 'F')
  717. X        c1 -= ('A' - 10);
  718. X    ret = (ret << 4) + c1;
  719. X    }
  720. X    return(ret);
  721. X}
  722. X
  723. Xstatic void
  724. Xusage()
  725. X{
  726. X    printf("options:\n");
  727. X    printf("-i <inputfile>\n");
  728. X    printf("-o <outputfile>\n");
  729. X    exit(1);
  730. X}
  731. X
  732. X/* parse command line arguments */
  733. Xstatic void
  734. Xparse(argc, argv)
  735. X    int argc;
  736. X    char **argv;
  737. X{
  738. X    char *infile, *outfile;
  739. X
  740. X    infile = outfile = 0;
  741. X
  742. X    while(--argc) {
  743. X    argv++;
  744. X    if (!strcmp(*argv, "-i")) {
  745. X        if (argc < 2)
  746. X        usage();
  747. X        argc--;
  748. X        argv++;
  749. X        infile = *argv;
  750. X    }
  751. X    else if (!strcmp(*argv, "-o")) {
  752. X        if (argc < 2)
  753. X        usage();
  754. X        argc--;
  755. X        argv++;
  756. X        outfile = *argv;
  757. X    }
  758. X    else
  759. X        usage();
  760. X    }
  761. X
  762. X    /* open input stream */
  763. X    if (infile) {
  764. X    if ((infp = fopen(infile, "r")) == 0) {
  765. X        perror(infile);
  766. X        exit(1);
  767. X    }
  768. X    }
  769. X    else
  770. X    infp = stdin;
  771. X
  772. X    /* open output stream or leave it for later if no output file
  773. X     * was specified 
  774. X     */
  775. X    if (outfile) {
  776. X    if ((outfp = fopen(outfile, "w")) == 0) {
  777. X        perror(outfile);
  778. X        exit(1);
  779. X    }
  780. X    }
  781. X    else
  782. X    outfp = 0;
  783. X}
  784. X
  785. Xmain(argc, argv)
  786. X    int argc;
  787. X    char **argv;
  788. X{
  789. X    char buffer[1024], *tokens[VECLEN], *c, out;
  790. X    int state, numtokens, outfd, buf_offset, lookforend;
  791. X    unsigned int cksum;
  792. X    unsigned short buf;
  793. X
  794. X    /* parse command line arguments */
  795. X    parse(argc, argv);
  796. X
  797. X    state = 0;
  798. X
  799. X    /* clear the output buffer */
  800. X    buf = 0;
  801. X    buf_offset = 16;
  802. X
  803. X    cksum = 0;
  804. X    lookforend = 0;
  805. X
  806. X    /* scan the input file */
  807. X    while(fgets(buffer, sizeof(buffer), infp)) {
  808. X    /* remove any newlines */
  809. X    if (c = index(buffer, '\n'))
  810. X        *c = 0;
  811. X
  812. X    /* if this line is blank, check for and END keyword
  813. X     * on the next line
  814. X     */
  815. X    if (*buffer == 0) {
  816. X        lookforend = 1;
  817. X        continue;
  818. X    }
  819. X
  820. X    /* state 0 == haven't seen BEGIN yet */
  821. X    if (state == 0) {
  822. X        if (!strncmp(buffer, "BEGIN ", 6)) {
  823. X        state = 1;
  824. X        numtokens = tokenize(buffer, tokens);
  825. X        if (numtokens < 4) {
  826. X            fprintf(stderr, "incomplete BEGIN line in encoded file\n");
  827. X            exit(1);
  828. X        }
  829. X
  830. X        /* if output file wasn't specified on command line, use the
  831. X         * one encoded in the input file
  832. X         */
  833. X        if (outfp == 0) {
  834. X            /* use open() so we can specify the mode */
  835. X            if ((outfd = open(tokens[2], O_WRONLY | O_CREAT | O_TRUNC, 
  836. X                      strtol(tokens[1], 0, 8))) < 0) {
  837. X            perror(tokens[2]);
  838. X            exit(1);
  839. X            }
  840. X            outfp = fdopen(outfd, "w");
  841. X        }
  842. X        /* parse the character set and initialize the
  843. X         * codes accordingly
  844. X         */
  845. X        parse_charval_list(tokens[3]);
  846. X        init_codes(DECODE);
  847. X        }
  848. X    }
  849. X
  850. X    /* state != 0 and we're looking for the END token */
  851. X    else if (lookforend && !strncmp(buffer, "END ", 4)) {
  852. X        numtokens = tokenize(buffer, tokens);
  853. X
  854. X        /* issue checksum error if there's a mismatch */
  855. X        if (numtokens < 2 || hex2long(tokens[1]) != cksum) {
  856. X        fprintf(stderr, "checksum error.\n");
  857. X        fprintf(stderr, "saw %X, computed %X\n",
  858. X            hex2long(tokens[1]), cksum);
  859. X        exit(1);
  860. X        }
  861. X        exit(0);
  862. X    }
  863. X
  864. X    /* state != 0 so this is a data line. Decode it */
  865. X    else {
  866. X        for(c = buffer; *c; c++) {
  867. X
  868. X        /* check for garbage characters in the input */
  869. X        if (!codes[*c].inuse) {
  870. X            fprintf(stderr, "invalid char ");
  871. X            if (isprint(*c))
  872. X            fprintf(stderr, "\'%c\' ", *c);
  873. X            fprintf(stderr, "(%d) in input", *c);
  874. X            exit(1);
  875. X        }
  876. X
  877. X        /* append the variable length bitfield that maps to
  878. X         * this input character to the output bitstream
  879. X         */
  880. X        buf_offset -= codes[*c].len;
  881. X        buf |= (((unsigned short)(codes[*c].code) << buf_offset));
  882. X
  883. X        /* if we've got an entire byte available in the output
  884. X         * buffer, append it to the output file
  885. X         */
  886. X        if (buf_offset < 9) {
  887. X            out = (char)(buf >> 8);
  888. X            putc(out, outfp);
  889. X            cksum = ((cksum << 7) | (cksum >> 25)) ^
  890. X            (unsigned char)out;
  891. X
  892. X            /* advance the output buffer */
  893. X            buf_offset += 8;
  894. X            buf <<= 8;
  895. X        }
  896. X        }
  897. X    }
  898. X    lookforend = 0;
  899. X    }
  900. X}
  901. END_OF_FILE
  902. if test 5584 -ne `wc -c <'decode.c'`; then
  903.     echo shar: \"'decode.c'\" unpacked with wrong size!
  904. fi
  905. # end of 'decode.c'
  906. fi
  907. if test -f 'encode.1' -a "${1}" != "-c" ; then 
  908.   echo shar: Will not clobber existing file \"'encode.1'\"
  909. else
  910. echo shar: Extracting \"'encode.1'\" \(2628 characters\)
  911. sed "s/^X//" >'encode.1' <<'END_OF_FILE'
  912. X.\" $Header: /usr/people/tcl/src/uutar/RCS/encode.1,v 1.2 1993/09/11 22:21:31 tcl Exp $
  913. X.TH encode 1 "11 Sept 1993"
  914. X.SH NAME
  915. Xencode \- encode binary files into printable format
  916. X.SH SYNOPSIS
  917. X.B encode
  918. X[
  919. X.B \-i \c
  920. X.I <inputfile>
  921. X]
  922. X[
  923. X.B \-o \c
  924. X.I <outputfile>
  925. X]
  926. X[
  927. X.B \-n \c
  928. X.I <name>
  929. X]
  930. X[
  931. X.B \-c \c
  932. X.I <charset>
  933. X]
  934. X.SH DESCRIPTION
  935. X.LP
  936. XEncode takes a binary file as input and encodes it into a printable
  937. Xformat that can be transferred via email. 
  938. X.SH OPTIONS
  939. X.TP
  940. X.B \-i\c
  941. X.I <inputfile>
  942. X.br
  943. Xspecifies the file to read input from. If this argument is omitted,
  944. Xstdin is used.
  945. X.TP
  946. X.B \-o\c
  947. X.I <outputfile>
  948. X.br
  949. Xspecifies the file to write output to. If this argument is omitted,
  950. Xstdout is used.
  951. X.TP
  952. X.B \-n\c
  953. X.I <name>
  954. X.br
  955. Xspecifies the filename to store in the output file. This filename will
  956. Xbe the default filename used to create the decoded file. If this
  957. Xargument is omitted, the name of the input file is used. If the input
  958. Xfile is stdin, the string "stdin" is used.
  959. X.TP
  960. X.B \-c\c
  961. X.I <charset>
  962. X.br
  963. X
  964. Xspecifies the character set to encode with. A character set is
  965. Xspecified as a list of ranges. A range is either a single character
  966. Xcode or two character codes separated by a hyphen, e.g. 23 or 45-51. A
  967. Xlist of ranges is 1 or more ranges separated by commas, e.g. 23,45-51.
  968. XOverlaps in ranges are not a problem; each character is counted only
  969. Xonce.  A character code is any valid number between 0 and 255 decimal,
  970. Xor the equivalent in octal, hex, or raw escaped characters. Note,
  971. Xhowever, that it only makes sense to use printable characters in the
  972. Xrange 32-126.  Octal codes must be preceeded by a 0, e.g. 023. Hex
  973. Xcodes must be preceeded by 0x, e.g. 0x6e. Raw escaped character codes
  974. Xmay be specified with a backslash followed by the character itself,
  975. Xe.g. \\t.  If this argument is omitted, the entire set of printable
  976. Xcharacters, 32-126, is used. The character set is included in the
  977. Xencoded file in decimal notation with any overlaps removed regardless
  978. Xof how it is specified on the command line.
  979. X
  980. X.SH OUTPUT FORMAT
  981. X.LP
  982. XThe first line of the output contains the keyword BEGIN followed by
  983. Xthe file mode of the input file in octal, the filename to be used when
  984. Xcreating the decoded file, and the character set used. Immediately
  985. Xfollowing this line is the encoded data, using only the characters in
  986. Xthe specified character set. Output width is limited to 79 columns by
  987. Xinserting a newline every 79 characters. The encoded data terminates
  988. Xwhen two consecutive newlines are seen. Immediately following the
  989. Xsecond newline is a line containing the keyword END and a 32 bit
  990. Xchecksum of the input file in hex.
  991. X.SH "SEE ALSO"
  992. X.BR decode (1),
  993. END_OF_FILE
  994. if test 2628 -ne `wc -c <'encode.1'`; then
  995.     echo shar: \"'encode.1'\" unpacked with wrong size!
  996. fi
  997. # end of 'encode.1'
  998. fi
  999. if test -f 'encode.c' -a "${1}" != "-c" ; then 
  1000.   echo shar: Will not clobber existing file \"'encode.c'\"
  1001. else
  1002. echo shar: Extracting \"'encode.c'\" \(4700 characters\)
  1003. sed "s/^X//" >'encode.c' <<'END_OF_FILE'
  1004. X/*
  1005. X * $Header: /usr/people/tcl/src/uutar/RCS/encode.c,v 1.1.1.5 1993/09/11 22:42:56 tcl Exp $
  1006. X * Tom Lawrence
  1007. X * tcl@sgi.com
  1008. X */
  1009. X
  1010. X#include <stdio.h>
  1011. X#include <sys/types.h>
  1012. X#include <sys/stat.h>
  1013. X#include <stdlib.h>
  1014. X#include <string.h>
  1015. X#include "codes.h"
  1016. X
  1017. Xstatic FILE *infp, *outfp;
  1018. Xstatic char *name, *charset;
  1019. Xstatic mode_t inmode;
  1020. X
  1021. Xstatic void
  1022. Xusage()
  1023. X{
  1024. X    printf("options:\n");
  1025. X    printf("-i <inputfile>\n");
  1026. X    printf("-o <outputfile>\n");
  1027. X    printf("-n <name>\n");
  1028. X    printf("-c <charset>\n");
  1029. X    exit(1);
  1030. X}
  1031. X
  1032. X/* parse command line arguments */
  1033. Xstatic void
  1034. Xparse(argc, argv)
  1035. X    int argc;
  1036. X    char **argv;
  1037. X{
  1038. X    char *infile, *outfile;
  1039. X    struct stat statbuf;
  1040. X
  1041. X    infile = outfile = 0;
  1042. X    name = charset = 0;
  1043. X
  1044. X    while(--argc) {
  1045. X    argv++;
  1046. X    if (!strcmp(*argv, "-i")) {
  1047. X        if (argc < 2)
  1048. X        usage();
  1049. X        argc--;
  1050. X        argv++;
  1051. X        infile = *argv;
  1052. X    }
  1053. X    else if (!strcmp(*argv, "-o")) {
  1054. X        if (argc < 2)
  1055. X        usage();
  1056. X        argc--;
  1057. X        argv++;
  1058. X        outfile = *argv;
  1059. X    }
  1060. X    else if (!strcmp(*argv, "-n")) {
  1061. X        if (argc < 2)
  1062. X        usage();
  1063. X        argc--;
  1064. X        argv++;
  1065. X        name = *argv;
  1066. X    }
  1067. X    else if (!strcmp(*argv, "-c")) {
  1068. X        if (argc < 2)
  1069. X        usage();
  1070. X        argc--;
  1071. X        argv++;
  1072. X        charset = *argv;
  1073. X    }
  1074. X    else
  1075. X        usage();
  1076. X    }
  1077. X
  1078. X    /* open the input stream */
  1079. X    if (infile) {
  1080. X    if ((infp = fopen(infile, "r")) == 0) {
  1081. X        perror(infile);
  1082. X        exit(1);
  1083. X    }
  1084. X    if (stat(infile, &statbuf) < 0) {
  1085. X        perror(infile);
  1086. X        exit(1);
  1087. X    }
  1088. X    inmode = statbuf.st_mode & 0777;
  1089. X    }
  1090. X    else {
  1091. X    infp = stdin;
  1092. X    inmode = 0666;
  1093. X    }
  1094. X
  1095. X    /* open the output stream */
  1096. X    if (outfile) {
  1097. X    if ((outfp = fopen(outfile, "w")) == 0) {
  1098. X        perror(outfile);
  1099. X        exit(1);
  1100. X    }
  1101. X    }
  1102. X    else
  1103. X    outfp = stdout;
  1104. X
  1105. X    /* get the filename to store in the encoded file */
  1106. X    if (name == 0) {
  1107. X    if (infile == 0)
  1108. X        name = "stdin";
  1109. X    else
  1110. X        name = infile;
  1111. X    }
  1112. X
  1113. X    /* set default character set if none was specified */
  1114. X    if (charset == 0)
  1115. X    charset = "32-126";
  1116. X}
  1117. X
  1118. Xmain(argc, argv)
  1119. X    int argc;
  1120. X    char **argv;
  1121. X{
  1122. X    int c;
  1123. X    unsigned short buf;
  1124. X    int buf_offset, inlen, cols = 0, pattern;
  1125. X    unsigned int cksum;
  1126. X
  1127. X    /* parse command line arguments */
  1128. X    parse(argc, argv);
  1129. X
  1130. X    /* parse the supplied character set specification and initialize
  1131. X     * tables based on that set
  1132. X     */
  1133. X    parse_charval_list(charset);
  1134. X    init_codes(ENCODE);
  1135. X
  1136. X    fprintf(outfp, "BEGIN %o %s ", inmode, name);
  1137. X    print_charval_list(outfp);
  1138. X    putc('\n', outfp);
  1139. X
  1140. X    /* clear the sliding input buffer */
  1141. X    buf = 0;
  1142. X    buf_offset = 16;
  1143. X    
  1144. X    cksum = 0;
  1145. X
  1146. X    /* read in the input file */
  1147. X    while((c = getc(infp)) != EOF) {
  1148. X
  1149. X    /* compute a checksum on the input file */
  1150. X    cksum = ((cksum << 7) | (cksum >> 25)) ^ (unsigned)c;
  1151. X
  1152. X    /* shift the byte just read in into our sliding buffer */
  1153. X    buf_offset -= 8;
  1154. X    buf |= ((unsigned short)c << buf_offset);
  1155. X
  1156. X    /* see if there are any complete variable length bitfields
  1157. X     * in the input buffer. If so, output their corresponding
  1158. X     * printable output character and advance the input buffer
  1159. X     * by their length in bits
  1160. X     */
  1161. X    while (1) {
  1162. X
  1163. X        /* grab the next 8 bits in the input bitstream */
  1164. X        pattern = (int)(buf >> 8);
  1165. X
  1166. X        /* determine how many of those bits we will need
  1167. X         * to extract from the sliding buffer
  1168. X         */
  1169. X        inlen = codes[pattern].len;
  1170. X
  1171. X        /* if there are not enough bits in the sliding
  1172. X         * buffer, stop for now. (interestingly, you don't need
  1173. X         * to have all of the needed bits in order to determine
  1174. X         * that you don't have all of the needed bits)
  1175. X         */
  1176. X        if (inlen > (16 - buf_offset))
  1177. X        break;
  1178. X
  1179. X        /* output the printable character associated with
  1180. X         * the variable length bitfield recognized in the
  1181. X         * input bitstream
  1182. X         */
  1183. X        putc(codes[pattern].code, outfp);
  1184. X
  1185. X        /* limit our width */
  1186. X        if (++cols == 79) {
  1187. X        cols = 0;
  1188. X        putc('\n', outfp);
  1189. X        }
  1190. X
  1191. X        /* advance the input bitstream by the length of the bitfield
  1192. X         * just recognized
  1193. X         */
  1194. X        buf_offset += inlen;
  1195. X        buf <<= inlen;
  1196. X    }
  1197. X    }
  1198. X
  1199. X    /* flush the buffer. The last byte read in may still have some
  1200. X     * of its bits in the sliding buffer. If so, print out one more
  1201. X     * output character. This will necessarily append some garbage
  1202. X     * bits to the output but what can we do? we can't write files
  1203. X     * at a finer granularity that the byte. The decoder will ignore
  1204. X     * them so it's ok
  1205. X     */
  1206. X    if (buf_offset < 16) {
  1207. X    putc(codes[pattern].code, outfp);
  1208. X    cols++;
  1209. X    }
  1210. X
  1211. X    /* indicate end of encoded data by 2 consecutive newlines followed
  1212. X     * by the keyword END. This is necessary since the END line itself
  1213. X     * is potentially valid encoded data
  1214. X     */
  1215. X    if (cols)
  1216. X    putc('\n', outfp);
  1217. X    fprintf(outfp, "\nEND %X\n", cksum);
  1218. X}
  1219. END_OF_FILE
  1220. if test 4700 -ne `wc -c <'encode.c'`; then
  1221.     echo shar: \"'encode.c'\" unpacked with wrong size!
  1222. fi
  1223. # end of 'encode.c'
  1224. fi
  1225. echo shar: End of archive 1 \(of 1\).
  1226. cp /dev/null ark1isdone
  1227. MISSING=""
  1228. for I in 1 ; do
  1229.     if test ! -f ark${I}isdone ; then
  1230.     MISSING="${MISSING} ${I}"
  1231.     fi
  1232. done
  1233. if test "${MISSING}" = "" ; then
  1234.     echo You have the archive.
  1235.     rm -f ark[1-9]isdone
  1236. else
  1237.     echo You still need to unpack the following archives:
  1238.     echo "        " ${MISSING}
  1239. fi
  1240. ##  End of shell archive.
  1241. exit 0
  1242.