home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / modem / rzsz.arc / ZM.C < prev    next >
C/C++ Source or Header  |  1987-08-16  |  14KB  |  735 lines

  1. /*
  2.  *   Z M . C
  3.  *    ZMODEM protocol primitives
  4.  *    07-28-87  Chuck Forsberg Omen Technology Inc
  5.  *
  6.  * Entry point Functions:
  7.  *    zsbhdr(type, hdr) send binary header
  8.  *    zshhdr(type, hdr) send hex header
  9.  *    zgethdr(hdr, eflag) receive header - binary or hex
  10.  *    zsdata(buf, len, frameend) send data
  11.  *    zrdata(buf, len) receive data
  12.  *    stohdr(pos) store position data in Txhdr
  13.  *    long rclhdr(hdr) recover position offset from header
  14.  */
  15.  
  16. #ifndef CANFDX
  17. #include "zmodem.h"
  18. int Rxtimeout = 100;        /* Tenths of seconds to wait for something */
  19. #endif
  20.  
  21. #ifndef UNSL
  22. #define UNSL
  23. #endif
  24.  
  25.  
  26. /* Globals used by ZMODEM functions */
  27. int Rxframeind;        /* ZBIN ZBIN32, or ZHEX type of frame received */
  28. int Rxtype;        /* Type of header received */
  29. int Rxcount;        /* Count of data bytes received */
  30. char Rxhdr[4];        /* Received header */
  31. char Txhdr[4];        /* Transmitted header */
  32. long Rxpos;        /* Received file position */
  33. long Txpos;        /* Transmitted file position */
  34. int Txfcs32;        /* TURE means send binary frames with 32 bit FCS */
  35. int Crc32t;        /* Display flag indicating 32 bit CRC being sent */
  36. int Crc32;        /* Display flag indicating 32 bit CRC being received */
  37. int Znulls;        /* Number of nulls to send at beginning of ZDATA hdr */
  38. char Attn[ZATTNLEN+1];    /* Attention string rx sends to tx on err */
  39.  
  40.  
  41. static char *frametypes[] = {
  42.     "Carrier Lost",        /* -3 */
  43.     "TIMEOUT",        /* -2 */
  44.     "ERROR",        /* -1 */
  45. #define FTOFFSET 3
  46.     "ZRQINIT",
  47.     "ZRINIT",
  48.     "ZSINIT",
  49.     "ZACK",
  50.     "ZFILE",
  51.     "ZSKIP",
  52.     "ZNAK",
  53.     "ZABORT",
  54.     "ZFIN",
  55.     "ZRPOS",
  56.     "ZDATA",
  57.     "ZEOF",
  58.     "ZFERR",
  59.     "ZCRC",
  60.     "ZCHALLENGE",
  61.     "ZCOMPL",
  62.     "ZCAN",
  63.     "ZFREECNT",
  64.     "ZCOMMAND",
  65.     "ZSTDERR",
  66.     "xxxxx"
  67. #define FRTYPES 22    /* Total number of frame types in this array */
  68.             /*  not including psuedo negative entries */
  69. };
  70.  
  71. /* Send ZMODEM binary header hdr of type type */
  72. zsbhdr(type, hdr)
  73. register char *hdr;
  74. {
  75.     register n;
  76.     register unsigned short crc;
  77.  
  78.     vfile("zsbhdr: %s %lx", frametypes[type+FTOFFSET], rclhdr(hdr));
  79.     if (type == ZDATA)
  80.         for (n = Znulls; --n >=0; )
  81.             zsendline(0);
  82.  
  83.     xsendline(ZPAD); xsendline(ZDLE);
  84.  
  85.     if (Crc32t=Txfcs32)
  86.         zsbh32(hdr, type);
  87.     else {
  88.         xsendline(ZBIN); zsendline(type); crc = updcrc(type, 0);
  89.  
  90.         for (n=4; --n >= 0; ++hdr) {
  91.             zsendline(*hdr);
  92.             crc = updcrc((0377& *hdr), crc);
  93.         }
  94.         crc = updcrc(0,updcrc(0,crc));
  95.         zsendline(crc>>8);
  96.         zsendline(crc);
  97.     }
  98.     if (type != ZDATA)
  99.         flushmo();
  100. }
  101.  
  102.  
  103. /* Send ZMODEM binary header hdr of type type */
  104. zsbh32(hdr, type)
  105. register char *hdr;
  106. {
  107.     register n;
  108.     register UNSL long crc;
  109.  
  110.     xsendline(ZBIN32);  zsendline(type);
  111.     crc = 0xFFFFFFFFL; crc = UPDC32(type, crc);
  112.  
  113.     for (n=4; --n >= 0; ++hdr) {
  114.         crc = UPDC32((0377 & *hdr), crc);
  115.         zsendline(*hdr);
  116.     }
  117.     crc = ~crc;
  118.     for (n=4; --n >= 0;) {
  119.         zsendline((int)crc);
  120.         crc >>= 8;
  121.     }
  122. }
  123.  
  124. /* Send ZMODEM HEX header hdr of type type */
  125. zshhdr(type, hdr)
  126. register char *hdr;
  127. {
  128.     register n;
  129.     register unsigned short crc;
  130.  
  131.     vfile("zshhdr: %s %lx", frametypes[type+FTOFFSET], rclhdr(hdr));
  132.     sendline(ZPAD); sendline(ZPAD); sendline(ZDLE); sendline(ZHEX);
  133.     zputhex(type);
  134.     Crc32t = 0;
  135.  
  136.     crc = updcrc(type, 0);
  137.     for (n=4; --n >= 0; ++hdr) {
  138.         zputhex(*hdr); crc = updcrc((0377 & *hdr), crc);
  139.     }
  140.     crc = updcrc(0,updcrc(0,crc));
  141.     zputhex(crc>>8); zputhex(crc);
  142.  
  143.     /* Make it printable on remote machine */
  144.     sendline(015); sendline(012);
  145.     /*
  146.      * Uncork the remote in case a fake XOFF has stopped data flow
  147.      */
  148.     if (type != ZFIN && type != ZACK)
  149.         sendline(021);
  150.     flushmo();
  151. }
  152.  
  153. /*
  154.  * Send binary array buf of length length, with ending ZDLE sequence frameend
  155.  */
  156. zsdata(buf, length, frameend)
  157. register char *buf;
  158. {
  159.     register unsigned short crc;
  160.  
  161.     vfile("zsdata: length=%d end=%x", length, frameend);
  162.     if (Crc32t)
  163.         zsda32(buf, length, frameend);
  164.     else {
  165.         crc = 0;
  166.         for (;--length >= 0; ++buf) {
  167.             zsendline(*buf); crc = updcrc((0377 & *buf), crc);
  168.         }
  169.         xsendline(ZDLE); xsendline(frameend);
  170.         crc = updcrc(frameend, crc);
  171.  
  172.         crc = updcrc(0,updcrc(0,crc));
  173.         zsendline(crc>>8); zsendline(crc);
  174.     }
  175.     if (frameend == ZCRCW) {
  176.         xsendline(XON);  flushmo();
  177.     }
  178. }
  179.  
  180. zsda32(buf, length, frameend)
  181. register char *buf;
  182. {
  183.     register UNSL long crc;
  184.  
  185.     crc = 0xFFFFFFFFL;
  186.     for (;--length >= 0;++buf) {
  187.         crc = UPDC32((0377 & *buf), crc);
  188.         zsendline(*buf);
  189.     }
  190.     xsendline(ZDLE); xsendline(frameend);
  191.     crc = UPDC32(frameend, crc);
  192.  
  193.     crc = ~crc;
  194.     for (length=4; --length >= 0;) {
  195.         zsendline((int)crc);  crc >>= 8;
  196.     }
  197. }
  198.  
  199. /*
  200.  * Receive array buf of max length with ending ZDLE sequence
  201.  *  and CRC.  Returns the ending character or error code.
  202.  *  NB: On errors may store length+1 bytes!
  203.  */
  204. zrdata(buf, length)
  205. register char *buf;
  206. {
  207.     register c;
  208.     register unsigned short crc;
  209.     register char *end;
  210.     register d;
  211.  
  212.     if (Rxframeind == ZBIN32)
  213.         return zrdat32(buf, length);
  214.  
  215.     crc = Rxcount = 0;  end = buf + length;
  216.     while (buf <= end) {
  217.         if ((c = zdlread()) & ~0377) {
  218. crcfoo:
  219.             switch (c) {
  220.             case GOTCRCE:
  221.             case GOTCRCG:
  222.             case GOTCRCQ:
  223.             case GOTCRCW:
  224.                 crc = updcrc((d=c)&0377, crc);
  225.                 if ((c = zdlread()) & ~0377)
  226.                     goto crcfoo;
  227.                 crc = updcrc(c, crc);
  228.                 if ((c = zdlread()) & ~0377)
  229.                     goto crcfoo;
  230.                 crc = updcrc(c, crc);
  231.                 if (crc & 0xFFFF) {
  232.                     zperr("Bad data CRC");
  233.                     return ERROR;
  234.                 }
  235.                 Rxcount = length - (end - buf);
  236.                 vfile("zrdata: cnt = %d ret = %x", Rxcount, d);
  237.                 return d;
  238.             case GOTCAN:
  239.                 zperr("Sender Canceled");
  240.                 return ZCAN;
  241.             case TIMEOUT:
  242.                 zperr("TIMEOUT");
  243.                 return c;
  244.             default:
  245.                 zperr("Bad data subpacket");
  246.                 return c;
  247.             }
  248.         }
  249.         *buf++ = c;
  250.         crc = updcrc(c, crc);
  251.     }
  252.     zperr("Data subpacket too long");
  253.     return ERROR;
  254. }
  255.  
  256. zrdat32(buf, length)
  257. register char *buf;
  258. {
  259.     register c;
  260.     register UNSL long crc;
  261.     register char *end;
  262.     register d;
  263.  
  264.     crc = 0xFFFFFFFFL;  Rxcount = 0;  end = buf + length;
  265.     while (buf <= end) {
  266.         if ((c = zdlread()) & ~0377) {
  267. crcfoo:
  268.             switch (c) {
  269.             case GOTCRCE:
  270.             case GOTCRCG:
  271.             case GOTCRCQ:
  272.             case GOTCRCW:
  273.                 d = c;  c &= 0377;
  274.                 crc = UPDC32(c, crc);
  275.                 if ((c = zdlread()) & ~0377)
  276.                     goto crcfoo;
  277.                 crc = UPDC32(c, crc);
  278.                 if ((c = zdlread()) & ~0377)
  279.                     goto crcfoo;
  280.                 crc = UPDC32(c, crc);
  281.                 if ((c = zdlread()) & ~0377)
  282.                     goto crcfoo;
  283.                 crc = UPDC32(c, crc);
  284.                 if ((c = zdlread()) & ~0377)
  285.                     goto crcfoo;
  286.                 crc = UPDC32(c, crc);
  287.                 if (crc != 0xDEBB20E3) {
  288.                     zperr("Bad data CRC");
  289.                     return ERROR;
  290.                 }
  291.                 Rxcount = length - (end - buf);
  292.                 vfile("zrdat32: cnt = %d ret = %x", Rxcount, d);
  293.                 return d;
  294.             case GOTCAN:
  295.                 zperr("Sender Canceled");
  296.                 return ZCAN;
  297.             case TIMEOUT:
  298.                 zperr("TIMEOUT");
  299.                 return c;
  300.             default:
  301.                 zperr("Bad data subpacket");
  302.                 return c;
  303.             }
  304.         }
  305.         *buf++ = c;
  306.         crc = UPDC32(c, crc);
  307.     }
  308.     zperr("Data subpacket too long");
  309.     return ERROR;
  310. }
  311.  
  312.  
  313. /*
  314.  * Read a ZMODEM header to hdr, either binary or hex.
  315.  *  eflag controls local display of non zmodem characters:
  316.  *    0:  no display
  317.  *    1:  display printing characters only
  318.  *    2:  display all non ZMODEM characters
  319.  *  On success, set Zmodem to 1, set Rxpos and return type of header.
  320.  *   Otherwise return negative on error.
  321.  *   Return ERROR instantly if ZCRCW sequence, for fast error recovery.
  322.  */
  323. zgethdr(hdr, eflag)
  324. char *hdr;
  325. {
  326.     register c, n, cancount;
  327.  
  328.     n = Zrwindow + Baudrate;    /* Max bytes before start of frame */
  329.     Rxframeind = Rxtype = 0;
  330.  
  331. startover:
  332.     cancount = 5;
  333. again:
  334.     /* Return immediate ERROR if ZCRCW sequence seen */
  335.     switch (c = readline(Rxtimeout)) {
  336.     case RCDO:
  337.     case TIMEOUT:
  338.         goto fifi;
  339.     case CAN:
  340. gotcan:
  341.         if (--cancount <= 0) {
  342.             c = ZCAN; goto fifi;
  343.         }
  344.         switch (c = readline(1)) {
  345.         case TIMEOUT:
  346.             goto again;
  347.         case ZCRCW:
  348.             c = ERROR;
  349.         /* **** FALL THRU TO **** */
  350.         case RCDO:
  351.             goto fifi;
  352.         default:
  353.             break;
  354.         case CAN:
  355.             if (--cancount <= 0) {
  356.                 c = ZCAN; goto fifi;
  357.             }
  358.             goto again;
  359.         }
  360.     /* **** FALL THRU TO **** */
  361.     default:
  362. agn2:
  363.         if ( --n == 0) {
  364.             zperr("Garbage count exceeded");
  365.             return(ERROR);
  366.         }
  367.         if (eflag && ((c &= 0177) & 0140))
  368.             bttyout(c);
  369.         else if (eflag > 1)
  370.             bttyout(c);
  371.         goto startover;
  372.     case ZPAD|0200:        /* This is what we want. */
  373.     case ZPAD:        /* This is what we want. */
  374.         break;
  375.     }
  376.     cancount = 5;
  377. splat:
  378.     switch (c = noxrd7()) {
  379.     case ZPAD:
  380.         goto splat;
  381.     case RCDO:
  382.     case TIMEOUT:
  383.         goto fifi;
  384.     default:
  385.         goto agn2;
  386.     case ZDLE:        /* This is what we want. */
  387.         break;
  388.     }
  389.  
  390.     switch (c = noxrd7()) {
  391.     case RCDO:
  392.     case TIMEOUT:
  393.         goto fifi;
  394.     case ZBIN:
  395.         Rxframeind = ZBIN;  Crc32 = FALSE;
  396.         c =  zrbhdr(hdr);
  397.         break;
  398.     case ZBIN32:
  399.         Crc32 = Rxframeind = ZBIN32;
  400.         c =  zrbhdr32(hdr);
  401.         break;
  402.     case ZHEX:
  403.         Rxframeind = ZHEX;  Crc32 = FALSE;
  404.         c =  zrhhdr(hdr);
  405.         break;
  406.     case CAN:
  407.         goto gotcan;
  408.     default:
  409.         goto agn2;
  410.     }
  411.     Rxpos = hdr[ZP3] & 0377;
  412.     Rxpos = (Rxpos<<8) + (hdr[ZP2] & 0377);
  413.     Rxpos = (Rxpos<<8) + (hdr[ZP1] & 0377);
  414.     Rxpos = (Rxpos<<8) + (hdr[ZP0] & 0377);
  415. fifi:
  416.     switch (c) {
  417.     case GOTCAN:
  418.         c = ZCAN;
  419.     /* **** FALL THRU TO **** */
  420.     case ZNAK:
  421.     case ZCAN:
  422.     case ERROR:
  423.     case TIMEOUT:
  424.     case RCDO:
  425.         zperr("Got %s", frametypes[c+FTOFFSET]);
  426.     /* **** FALL THRU TO **** */
  427.     default:
  428.         if (c >= -3 && c <= FRTYPES)
  429.             vfile("zgethdr: %s %lx", frametypes[c+FTOFFSET], Rxpos);
  430.         else
  431.             vfile("zgethdr: %d %lx", c, Rxpos);
  432.     }
  433.     return c;
  434. }
  435.  
  436. /* Receive a binary style header (type and position) */
  437. zrbhdr(hdr)
  438. register char *hdr;
  439. {
  440.     register c, n;
  441.     register unsigned short crc;
  442.  
  443.     if ((c = zdlread()) & ~0377)
  444.         return c;
  445.     Rxtype = c;
  446.     crc = updcrc(c, 0);
  447.  
  448.     for (n=4; --n >= 0; ++hdr) {
  449.         if ((c = zdlread()) & ~0377)
  450.             return c;
  451.         crc = updcrc(c, crc);
  452.         *hdr = c;
  453.     }
  454.     if ((c = zdlread()) & ~0377)
  455.         return c;
  456.     crc = updcrc(c, crc);
  457.     if ((c = zdlread()) & ~0377)
  458.         return c;
  459.     crc = updcrc(c, crc);
  460.     if (crc & 0xFFFF) {
  461.         zperr("Bad Header CRC"); return ERROR;
  462.     }
  463.     Zmodem = 1;
  464.     return Rxtype;
  465. }
  466.  
  467. /* Receive a binary style header (type and position) with 32 bit FCS */
  468. zrbhdr32(hdr)
  469. register char *hdr;
  470. {
  471.     register c, n;
  472.     register UNSL long crc;
  473.  
  474.     if ((c = zdlread()) & ~0377)
  475.         return c;
  476.     Rxtype = c;
  477.     crc = 0xFFFFFFFFL; crc = UPDC32(c, crc);
  478. #ifdef DEBUGZ
  479.     vfile("zrbhdr32 c=%X  crc=%lX", c, crc);
  480. #endif
  481.  
  482.     for (n=4; --n >= 0; ++hdr) {
  483.         if ((c = zdlread()) & ~0377)
  484.             return c;
  485.         crc = UPDC32(c, crc);
  486.         *hdr = c;
  487. #ifdef DEBUGZ
  488.         vfile("zrbhdr32 c=%X  crc=%lX", c, crc);
  489. #endif
  490.     }
  491.     for (n=4; --n >= 0;) {
  492.         if ((c = zdlread()) & ~0377)
  493.             return c;
  494.         crc = UPDC32(c, crc);
  495. #ifdef DEBUGZ
  496.         vfile("zrbhdr32 c=%X  crc=%lX", c, crc);
  497. #endif
  498.     }
  499.     if (crc != 0xDEBB20E3) {
  500.         zperr("Bad Header CRC"); return ERROR;
  501.     }
  502.     Zmodem = 1;
  503.     return Rxtype;
  504. }
  505.  
  506.  
  507. /* Receive a hex style header (type and position) */
  508. zrhhdr(hdr)
  509. char *hdr;
  510. {
  511.     register c;
  512.     register unsigned short crc;
  513.     register n;
  514.  
  515.     if ((c = zgethex()) < 0)
  516.         return c;
  517.     Rxtype = c;
  518.     crc = updcrc(c, 0);
  519.  
  520.     for (n=4; --n >= 0; ++hdr) {
  521.         if ((c = zgethex()) < 0)
  522.             return c;
  523.         crc = updcrc(c, crc);
  524.         *hdr = c;
  525.     }
  526.     if ((c = zgethex()) < 0)
  527.         return c;
  528.     crc = updcrc(c, crc);
  529.     if ((c = zgethex()) < 0)
  530.         return c;
  531.     crc = updcrc(c, crc);
  532.     if (crc & 0xFFFF) {
  533.         zperr("Bad Header CRC"); return ERROR;
  534.     }
  535.     if (readline(1) == '\r')    /* Throw away possible cr/lf */
  536.         readline(1);
  537.     Zmodem = 1; return Rxtype;
  538. }
  539.  
  540. /* Send a byte as two hex digits */
  541. zputhex(c)
  542. register c;
  543. {
  544.     static char    digits[]    = "0123456789abcdef";
  545.  
  546.     if (Verbose>8)
  547.         vfile("zputhex: %02X", c);
  548.     sendline(digits[(c&0xF0)>>4]);
  549.     sendline(digits[(c)&0xF]);
  550. }
  551.  
  552. /*
  553.  * Send character c with ZMODEM escape sequence encoding.
  554.  *  Escape XON, XOFF. Escape CR following @ (Telenet net escape)
  555.  */
  556. zsendline(c)
  557. register c;
  558. {
  559.     static lastsent;
  560.  
  561.     switch (c &= 0377) {
  562.     case ZDLE:
  563.         xsendline(ZDLE);
  564.         xsendline (lastsent = (c ^= 0100));
  565.         break;
  566.     case 015:
  567.     case 0215:
  568.         if (!Zctlesc && (lastsent & 0177) != '@')
  569.             goto sendit;
  570.     /* **** FALL THRU TO **** */
  571.     case 020:
  572.     case 021:
  573.     case 023:
  574.     case 0220:
  575.     case 0221:
  576.     case 0223:
  577.         xsendline(ZDLE);
  578.         c ^= 0100;
  579. sendit:
  580.         xsendline(lastsent = c);
  581.         break;
  582.     default:
  583.         if (Zctlesc && ! (c & 0140)) {
  584.             xsendline(ZDLE);
  585.             c ^= 0100;
  586.         }
  587.         xsendline(lastsent = c);
  588.     }
  589. }
  590.  
  591. /* Decode two lower case hex digits into an 8 bit byte value */
  592. zgethex()
  593. {
  594.     register c;
  595.  
  596.     c = zgeth1();
  597.     if (Verbose>8)
  598.         vfile("zgethex: %02X", c);
  599.     return c;
  600. }
  601. zgeth1()
  602. {
  603.     register c, n;
  604.  
  605.     if ((c = noxrd7()) < 0)
  606.         return c;
  607.     n = c - '0';
  608.     if (n > 9)
  609.         n -= ('a' - ':');
  610.     if (n & ~0xF)
  611.         return ERROR;
  612.     if ((c = noxrd7()) < 0)
  613.         return c;
  614.     c -= '0';
  615.     if (c > 9)
  616.         c -= ('a' - ':');
  617.     if (c & ~0xF)
  618.         return ERROR;
  619.     c += (n<<4);
  620.     return c;
  621. }
  622.  
  623. /*
  624.  * Read a byte, checking for ZMODEM escape encoding
  625.  *  including CAN*5 which represents a quick abort
  626.  */
  627. zdlread()
  628. {
  629.     register c;
  630.  
  631. again:
  632.     switch (c = readline(Rxtimeout)) {
  633.     case ZDLE:
  634.         break;
  635.     case 023:
  636.     case 0223:
  637.     case 021:
  638.     case 0221:
  639.         goto again;
  640.     default:
  641.         if (Zctlesc && !(c & 0140)) {
  642.             goto again;
  643.         }
  644.         return c;
  645.     }
  646. again2:
  647.     if ((c = readline(Rxtimeout)) < 0)
  648.         return c;
  649.     if (c == CAN && (c = readline(Rxtimeout)) < 0)
  650.         return c;
  651.     if (c == CAN && (c = readline(Rxtimeout)) < 0)
  652.         return c;
  653.     if (c == CAN && (c = readline(Rxtimeout)) < 0)
  654.         return c;
  655.     switch (c) {
  656.     case CAN:
  657.         return GOTCAN;
  658.     case ZCRCE:
  659.     case ZCRCG:
  660.     case ZCRCQ:
  661.     case ZCRCW:
  662.         return (c | GOTOR);
  663.     case ZRUB0:
  664.         return 0177;
  665.     case ZRUB1:
  666.         return 0377;
  667.     case 023:
  668.     case 0223:
  669.     case 021:
  670.     case 0221:
  671.         goto again2;
  672.     default:
  673.         if (Zctlesc && ! (c & 0140)) {
  674.             goto again2;
  675.         }
  676.         if ((c & 0140) ==  0100)
  677.             return (c ^ 0100);
  678.         break;
  679.     }
  680.     zperr("Bad escape sequence %x", c);
  681.     return ERROR;
  682. }
  683.  
  684. /*
  685.  * Read a character from the modem line with timeout.
  686.  *  Eat parity, XON and XOFF characters.
  687.  */
  688. noxrd7()
  689. {
  690.     register c;
  691.  
  692.     for (;;) {
  693.         if ((c = readline(Rxtimeout)) < 0)
  694.             return c;
  695.         switch (c &= 0177) {
  696.         case XON:
  697.         case XOFF:
  698.             continue;
  699.         default:
  700.             if (Zctlesc && !(c & 0140))
  701.                 continue;
  702.         case '\r':
  703.         case '\n':
  704.         case ZDLE:
  705.             return c;
  706.         }
  707.     }
  708. }
  709.  
  710. /* Store long integer pos in Txhdr */
  711. stohdr(pos)
  712. long pos;
  713. {
  714.     Txhdr[ZP0] = pos;
  715.     Txhdr[ZP1] = pos>>8;
  716.     Txhdr[ZP2] = pos>>16;
  717.     Txhdr[ZP3] = pos>>24;
  718. }
  719.  
  720. /* Recover a long integer from a header */
  721. long
  722. rclhdr(hdr)
  723. register char *hdr;
  724. {
  725.     register long l;
  726.  
  727.     l = (hdr[ZP3] & 0377);
  728.     l = (l << 8) | (hdr[ZP2] & 0377);
  729.     l = (l << 8) | (hdr[ZP1] & 0377);
  730.     l = (l << 8) | (hdr[ZP0] & 0377);
  731.     return l;
  732. }
  733.  
  734. /* End of zm.c */
  735.