home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / CONTRIB / MBASE / MBASE51.TAR / mbase51 / src / mb_util.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-04  |  15.8 KB  |  778 lines

  1. /*
  2.  * METALBASE 5.1
  3.  *
  4.  * Released January 1st, 1993 by Huan-Ti [ t-richj@microsoft.com ]
  5.  *
  6.  */
  7.  
  8. #include <mbase.h>
  9. #include "internal.h"
  10.  
  11.  
  12. /*
  13.  * PROTOTYPES -----------------------------------------------------------------
  14.  *
  15.  */
  16.  
  17.    static mb_err  _balance      XARGS( (relation *, long, int, int) );
  18.  
  19.    static int     _comp_fld     XARGS( (relation *, dataptr, dataptr, int) );
  20.    static int     _comp_short   XARGS( (short  *, short  *) );
  21.    static int     _comp_ushort  XARGS( (ushort *, ushort *) );
  22.    static int     _comp_long    XARGS( (long   *, long   *) );
  23.    static int     _comp_ulong   XARGS( (ulong  *, ulong  *) );
  24.    static int     _comp_float   XARGS( (float  *, float  *) );
  25.    static int     _comp_double  XARGS( (double *, double *) );
  26.    static int     _comp_string  XARGS( (char   *, char   *, int) );
  27.    static int     _comp_byte    XARGS( (dataptr,  dataptr,  int) );
  28.    static int     _comp_phone   XARGS( (mb_phone *, mb_phone *) );
  29.  
  30.  
  31. /*
  32.  * ROUTINES -------------------------------------------------------------------
  33.  *
  34.  */
  35.  
  36. byte *
  37. numcpy  (new, old, num)
  38. byte    *new,*old;
  39. int                num;
  40. {
  41.    if (new)
  42.       {
  43.       if (old)
  44.          {
  45.          for ( ; num; num--)
  46.             *new++ = *old++;
  47.          }
  48.       else
  49.          {
  50.          for ( ; num; num--)
  51.             *new++ = (byte)0;
  52.          }
  53.       }
  54.    return new;
  55. }
  56.  
  57. charptr
  58. strzcpy (new, old, num)
  59. charptr  new, old;
  60. int                num;
  61. {
  62.    if (new && old)
  63.       {
  64.       for ( ; num && *old; num--)
  65.          *new++ = *old++;
  66.       *new = 0;
  67.       }
  68.  
  69.    return new;
  70. }
  71.  
  72. int
  73. fldnum   (rel, name)
  74. relation *rel;
  75. charptr        name;
  76. {
  77.    int  fld;
  78.  
  79.    for (fld = 0; fld < rel->nFld; fld++)
  80.       if (! strcmp (name, rel->fldName[fld]))
  81.          break;
  82.  
  83.    if (fld == rel->nFld)
  84.       {
  85.       SetError (MB_BAD_FLD);
  86.       return -1;
  87.       }
  88.  
  89.    SetError (MB_OKAY);
  90.    return fld;
  91. }
  92.  
  93. int
  94. idxnum   (rel, name)
  95. relation *rel;
  96. charptr        name;
  97. {
  98.    int  idx;
  99.  
  100.    for (idx = 0; idx < rel->nIdx; idx++)
  101.       if (! strcmp (name, rel->idxName[idx]))
  102.          break;
  103.  
  104.    if (idx == rel->nIdx)
  105.       {
  106.       SetError (MB_BAD_IDX);
  107.       return -1;
  108.       }
  109.  
  110.    SetError (MB_OKAY);
  111.    return idx;
  112. }
  113.  
  114.  
  115. /*
  116.  * SERVICE ROUTINES -----------------------------------------------------------
  117.  *
  118.  */
  119.  
  120. int
  121. _identify (rel)
  122. relation  *rel;
  123. {
  124.    register int  i;
  125.  
  126.    if (rel == RNULL || !fStarted)
  127.       return -1;
  128.  
  129.    for (i = 0; i < MAX_REL; i++)
  130.       if (aRel[i] == rel)
  131.          break;
  132.  
  133.    if (i == MAX_REL)
  134.       return -1;
  135.  
  136.    if (rel->ver < verLOWEST || rel->ver > verCURRENT)
  137.       return -2;
  138.  
  139.    return i;
  140. }
  141.  
  142. mb_err
  143. _drop    (rel, rcd, idx, top)  /* CACHED */
  144. relation *rel;
  145. long           rcd,      top;
  146. int                 idx;
  147. {
  148.    dataptr  a, b;
  149.    cache   *ptr;
  150.    long     pos, par;
  151.    int      dir;
  152.    char     temp;
  153.  
  154.    ptr = _read_cache (rel, top, idx);
  155.    if (top == 0L)
  156.       {
  157.       pos = ptr->num;
  158.       par = 0L;
  159.       }
  160.    else
  161.       {
  162.       par = ptr->parent;
  163.       pos = top;
  164.       }
  165.  
  166.    if ((a = (dataptr)malloc (rel->cbRecord +1)) == (dataptr)0)
  167.       {
  168.       Error (MB_NO_MEMORY);
  169.       }
  170.    if ((b = (dataptr)malloc (rel->cbRecord +1)) == (dataptr)0)
  171.       {
  172.       Error_2 (MB_NO_MEMORY);
  173.       }
  174.  
  175.    _memrec (rel, a, rcd);
  176.  
  177.    for ( ; pos != 0L; )
  178.       {
  179.       ptr = _read_cache (rel, pos, idx);
  180.       temp = ptr->parbal;
  181.  
  182.       _memrec (rel, b, pos);
  183.  
  184.       if ((dir = _compare (rel, a, b, idx)) == 0)
  185.          {
  186.          dir = ((temp & BAL) > BAL_EV) ? -1 : 1;
  187.          }
  188.       temp += dir;
  189.  
  190.       _change_cache (ptr, parbal, temp);
  191.  
  192.       par = pos;
  193.       pos = _cache_field (ptr, dir);
  194.       }
  195.  
  196.    if (par == 0L)
  197.       {
  198.       ptr = _read_cache (rel, 0L, idx);
  199.       _change_cache (ptr, num, rcd);
  200.       }
  201.    else
  202.       {
  203.       temp = (char)(((dir==1) ? PARDIR:0) | BAL_EV);
  204.  
  205.       ptr = _read_cache (rel, rcd, idx);
  206.       _change_cache (ptr, parbal, temp);
  207.  
  208.       ptr = _read_cache (rel, par, idx);
  209.       if (dir == -1)      _changeqcache (ptr, left, rcd);
  210.       else if (dir == 0)  _changeqcache (ptr, parent, rcd);
  211.       else                _changeqcache (ptr, right, rcd);
  212.       ptr->changed = 1;
  213.       }
  214.  
  215.    ptr = _read_cache (rel, rcd, idx);
  216.    _change_cache (ptr, parent, par);
  217.  
  218.    SetError (MB_OKAY);
  219.  
  220.    free (b);
  221.  
  222. lblERROR_2:
  223.    free (a);
  224.  
  225. lblERROR:
  226.    return mb_errno;
  227. }
  228.  
  229. mb_err
  230. _check   (rel, st, ed, idx)  /* CACHED */
  231. relation *rel;
  232. long           st, ed;
  233. int                    idx;
  234. {
  235.    cache *ptr;
  236.    long   pos, tmp;
  237.  
  238.    for (pos = st; pos != ed; )
  239.       {
  240.       if (pos == 0L)  break;
  241.  
  242.       ptr = _read_cache (rel, pos, idx);
  243.       tmp = ptr->parent;
  244.  
  245.       if (! BALANCED ( (ptr->parbal & BAL) ))
  246.          if (_balance (rel, pos, idx, (int)(ptr->parbal & BAL)))
  247.             {
  248.             Error (mb_errno);
  249.             }
  250.       pos = tmp;
  251.       }
  252.  
  253.    SetError (MB_OKAY);
  254.  
  255. lblERROR:
  256.    return mb_errno;
  257. }
  258.  
  259.  
  260. /*
  261.  * COMPARISON FUNCTIONS -------------------------------------------------------
  262.  *
  263.  */
  264.  
  265. int
  266. compare  (rel, ptra, ptrb, idx)  /* -1 = ptra <  ptrb */
  267. relation *rel;                   /*  1 = ptra  > ptrb */
  268. dataptr        ptra, ptrb;       /*  0 = ptra == ptrb */
  269. int                        idx;  /* -2 = error        */
  270. {
  271.    int   x = -2;  /* Error condition */
  272.  
  273.  
  274.    if (_identify (rel) < 0)          Error (MB_BAD_REL);
  275.  
  276.    if (idx < 0 || idx >= rel->nIdx)  Error (MB_BAD_IDX);
  277.  
  278.    if (ptra == ptrb)                 Error (MB_OKAY);
  279.  
  280.  
  281. /*
  282.  * _compare() expects the records it compares to be encrypted, so encrypt and
  283.  * decrypt the records we get, around the function call.  Note that we do not
  284.  * assign temp files to multi-length fields; since indices can't be placed on
  285.  * that kind of field, there's no reason to worry about 'em here.
  286.  *
  287.  */
  288.  
  289.    _encrypt (rel, ptra);
  290.    _encrypt (rel, ptrb);
  291.    x = _compare (rel, ptra, ptrb, idx);
  292.    _decrypt (rel, ptra);
  293.    _decrypt (rel, ptrb);
  294.  
  295.    SetError (MB_OKAY);
  296.  
  297. lblERROR:
  298.    return x;
  299. }
  300.  
  301. int
  302. _compare (rel, ptra, ptrb, idx)   /* -1 = ptra <  ptrb */
  303. relation *rel;                    /*  1 = ptra  > ptrb */
  304. dataptr        ptra, ptrb;        /*  0 = ptra == ptrb */
  305. int                        idx;
  306. {
  307.    register int  i;
  308.    int           n;
  309.  
  310.    for (i = 0; i < rel->nIdxFld[idx]; i++)
  311.       {
  312.       if ((n = _comp_fld (rel, ptra, ptrb, rel->idxFld[idx][i])) != 0)
  313.          {
  314.          return n;
  315.          }
  316.       }
  317.  
  318.    return 0;
  319. }
  320.  
  321. static int
  322. _comp_fld (rel, ptra, ptrb, fld)
  323. relation  *rel;
  324. dataptr         ptra, ptrb;
  325. int                         fld;
  326. {
  327.    charptr  a, b;
  328.    int      n;
  329.  
  330.    n = rel->cbLen[fld];
  331.    a = (charptr)ptra + rel->cbStart[fld];
  332.    b = (charptr)ptrb + rel->cbStart[fld];
  333.  
  334.    _decryptf (a, n, rel->mask);
  335.    _decryptf (b, n, rel->mask);
  336.  
  337.    switch (rel->fldType[fld])
  338.       {
  339.       case T_SHORT:   n = _comp_short  ((short  *)a, (short  *)b);     break;
  340.       case T_USHORT:  n = _comp_ushort ((ushort *)a, (ushort *)b);     break;
  341.       case T_LONG:    n = _comp_long   ((long   *)a, (long   *)b);     break;
  342.       case T_ULONG:   n = _comp_ulong  ((ulong  *)a, (ulong  *)b);     break;
  343.       case T_FLOAT:   n = _comp_float  ((float  *)a, (float  *)b);     break;
  344.       case T_DOUBLE:  n = _comp_double ((double *)a, (double *)b);     break;
  345.       case T_MONEY:   n = _comp_double ((double *)a, (double *)b);     break;
  346.       case T_TIME:    n = _comp_long   ((long   *)a, (long   *)b);     break;
  347.       case T_DATE:    n = _comp_long   ((long   *)a, (long   *)b);     break;
  348.       case T_SERIAL:  n = _comp_long   ((long   *)a, (long   *)b);     break;
  349.       case T_BYTE:    n = _comp_byte   ((byte   *)a, (byte   *)b, n);  break;
  350.       case T_PHONE:   n = _comp_phone  ((mb_phone *)a, (mb_phone *)b); break;
  351.       default:        n = _comp_string (a, b, n);                      break;
  352.       }
  353.  
  354.    _encryptf (b, rel->cbLen[fld], rel->mask);
  355.    _encryptf (a, rel->cbLen[fld], rel->mask);
  356.  
  357.    return n;
  358. }
  359.  
  360. static int
  361. _comp_byte (a, b, n)
  362. byte       *a,*b;
  363. int               n;
  364. {
  365.    for ( ; n; n--)
  366.       {
  367.       if (*a < *b)  return -1;
  368.       if (*a > *b)  return  1;
  369.       a++;
  370.       b++;
  371.       }
  372.    return 0;
  373. }
  374.  
  375. static int
  376. _comp_short (a, b)
  377. short       *a,*b;
  378. {
  379.    return ((*a < *b) ? -1 : (*a > *b) ? 1 : 0);
  380. }
  381.  
  382. static int
  383. _comp_ushort (a, b)
  384. ushort       *a,*b;
  385. {
  386.    return ((*a < *b) ? -1 : (*a > *b) ? 1 : 0);
  387. }
  388.  
  389. static int
  390. _comp_long (a, b)
  391. long       *a,*b;
  392. {
  393.    return ((*a < *b) ? -1 : (*a > *b) ? 1 : 0);
  394. }
  395.  
  396. static int
  397. _comp_ulong (a, b)
  398. ulong       *a,*b;
  399. {
  400.    return ((*a < *b) ? -1 : (*a > *b) ? 1 : 0);
  401. }
  402.  
  403. static int
  404. _comp_float (a, b)
  405. float       *a,*b;
  406. {
  407.    return ((*a < *b) ? -1 : (*a > *b) ? 1 : 0);
  408. }
  409.  
  410. static int
  411. _comp_double (a, b)
  412. double       *a,*b;
  413. {
  414.    return ((*a < *b) ? -1 : (*a > *b) ? 1 : 0);
  415. }
  416.  
  417. static int
  418. _comp_string (a, b, n)
  419. char         *a,*b;
  420. int                 n;
  421. {
  422.    int  i;
  423.    i = strncmp (a,b,n);            /* THIS WON'T RETURN -1,0,1!!  It returns */
  424.    return (i < 0) ? -1 : (i > 0);  /* different amounts on different systems */
  425. }
  426.  
  427. static int
  428. _comp_phone (a, b)
  429. mb_phone    *a,*b;
  430. {
  431.    if (a->area   != b->area)    return ((a->area   < b->area)   ? -1 : 1);
  432.    if (a->prefix != b->prefix)  return ((a->prefix < b->prefix) ? -1 : 1);
  433.    if (a->number != b->number)  return ((a->number < b->number) ? -1 : 1);
  434.    if (a->ext    != b->ext)     return ((a->ext    < b->ext)    ? -1 : 1);
  435.  
  436.    return 0;
  437. }
  438.  
  439.  
  440. /*
  441.  * ENCRYPTION -----------------------------------------------------------------
  442.  *
  443.  */
  444.  
  445. void
  446. _crypt   (rel, rec)
  447. relation *rel;
  448. dataptr        rec;
  449. {
  450. #ifdef NO_ENCRYPT
  451.    (void)rel;  /* Reference these arguments, so the compiler */
  452.    (void)rec;  /* won't complain.                            */
  453. #else
  454.    register int  i;
  455.  
  456.    if (rel->mask)
  457.       {
  458.       for (i = 0; i < rel->nFld; i++)
  459.          {
  460.          if (rel->fldType[i] != T_MCHAR && rel->fldType[i] != T_MBYTE)
  461.             {
  462.             _cryptf ((charptr)rec +rel->cbStart[i], rel->cbLen[i], rel->mask);
  463.             }
  464.          }
  465.       }
  466. #endif
  467. }
  468.  
  469. void
  470. _cryptf (rec, siz, mask)
  471. charptr  rec;
  472. int           siz, mask;
  473. {
  474. #ifdef NO_ENCRYPT
  475.    (void)rec;  /* Reference these arguments, so the compiler */
  476.    (void)siz;  /* won't complain.                            */
  477.    (void)mask;
  478. #else
  479.    register int  i;
  480.  
  481.    if (mask != 0)
  482.       {
  483.       for (i = 0; i < siz; i++)
  484.          {
  485.          *rec ^= mask;
  486.          mask  = (mask + 1) & (int)0xFF;
  487.          rec++;
  488.          }
  489.       }
  490. #endif
  491. }
  492.  
  493.  
  494. /*
  495.  * SERVICE ROUTINES -----------------------------------------------------------
  496.  *
  497.  */
  498.  
  499. static mb_err
  500. _balance (rel, rcd, idx, bal)  /* CACHED */
  501. relation *rel;
  502. long           rcd;
  503. int                 idx, bal;
  504. {
  505.    long   rep, rp;
  506.    cache *ptr;
  507.  
  508.    if (! (rep = _find_seq (rel, 0L, rcd, idx, NUM_BAL(bal))) )
  509.       {
  510.       Error (MB_CORRUPT);
  511.       }
  512.  
  513.    ptr = _read_cache (rel, rep, idx);
  514.    rp = ptr->parent;
  515.  
  516.    _dislink (rel, rep, idx, rcd);
  517.    _replace (rel, rcd, rep, idx);
  518.  
  519.    if (_drop (rel, rcd, idx, rep) != MB_OKAY)
  520.       {
  521.       Error (mb_errno);
  522.       }
  523.  
  524.    if (rp != rcd)
  525.       {
  526.       if (_check (rel, rp, rep, idx) != MB_OKAY)
  527.          {
  528.          Error (mb_errno);
  529.          }
  530.       }
  531.  
  532.    ptr = _read_cache (rel, rcd, idx);
  533.    rp = ptr->parent;
  534.  
  535.    if (_check (rel, rp, rep, idx) != MB_OKAY)
  536.       {
  537.       Error (mb_errno);
  538.       }
  539.  
  540.    SetError (MB_OKAY);
  541.  
  542. lblERROR:
  543.    return mb_errno;
  544. }
  545.  
  546. void
  547. _dislink (rel, pos, idx, end)  /* CACHED */
  548. relation *rel;
  549. long           pos,      end;
  550. int                 idx;
  551. {
  552.    char   temp;
  553.    long   ch, par, tmp;
  554.    int    dir = 0, tdir;
  555.    cache *ptr;
  556.  
  557.    ptr = _read_cache (rel, pos, idx); /* There will be one child, at most */
  558.    ch = ptr->left;
  559.    if (ch)
  560.       dir = -1;
  561.    else
  562.       {
  563.       ch = ptr->right;     /* We already read in this record! */
  564.       dir = (ch ? 1 : 0);
  565.       }
  566.  
  567.    par = ptr->parent;
  568.    temp = (char)((int)ptr->parbal & PARDIR);
  569.    tdir = temp?1:-1;
  570.  
  571.    ptr = _read_cache (rel, par, idx);
  572.    ptr->changed = 1;
  573.    if (par == 0L)
  574.       {
  575.       _changeqcache (ptr, num, ch);
  576.       }
  577.    else
  578.       {
  579.       if (temp) _changeqcache (ptr, right, ch);
  580.       else      _changeqcache (ptr, left,  ch);
  581.       }
  582.  
  583.    if (ch)
  584.       {
  585.       ptr = _read_cache (rel, ch, idx);
  586.       _change_cache (ptr, parent, par);
  587.       temp = ptr->parbal;
  588.       temp = (char)((int)(temp & BAL) | (tdir == 1 ? PARDIR : 0));
  589.  
  590.       _change_cache (ptr, parbal, temp);
  591.       }
  592.  
  593.    for (tmp=par, dir=tdir; tmp != 0L; )   /* Update balances: */
  594.       {
  595.       ptr = _read_cache (rel, tmp, idx);
  596.       temp = ptr->parbal;
  597.       temp = (char)((int)(temp & PARDIR) | ((temp & BAL) - dir));
  598.  
  599.       _change_cache (ptr, parbal, temp);
  600.  
  601.       dir = (temp & PARDIR) ? 1 : -1;
  602.       if (tmp == end)  break;
  603.  
  604.       tmp = ptr->parent;
  605.       }
  606. }
  607.  
  608. void
  609. _replace (rel, old, new, idx)  /* CACHED */
  610. relation *rel;
  611. long           old, new;
  612. int                      idx;
  613. {
  614.    char   pba;
  615.    long   lef, rig, par;
  616.    cache *ptr;
  617.  
  618.    ptr = _read_cache (rel, old, idx);
  619.       lef = ptr->left;
  620.       rig = ptr->right;
  621.       par = ptr->parent;
  622.       pba = ptr->parbal;
  623.    ptr = _read_cache (rel, new, idx);
  624.       _change_cache (ptr, left,   lef);
  625.       _changeqcache (ptr, right,  rig);
  626.       _changeqcache (ptr, parent, par);
  627.       _changeqcache (ptr, parbal, pba);
  628.  
  629.    if (par == 0L)   /* Parent */
  630.       {
  631.       ptr = _read_cache (rel, 0L, idx);
  632.       _change_cache (ptr, num, new);
  633.       }
  634.    else
  635.       {
  636.       ptr = _read_cache (rel, par, idx);
  637.       if (pba & PARDIR)  _changeqcache (ptr, right, new);
  638.       else               _changeqcache (ptr, left,  new);
  639.       ptr->changed = 1;
  640.       }
  641.  
  642.    if (lef != 0L)  /* Left child */
  643.       {
  644.       ptr = _read_cache (rel, lef, idx);
  645.       _change_cache (ptr, parent, new);
  646.       }
  647.  
  648.    if (rig != 0L)  /* Right child */
  649.       {
  650.       ptr = _read_cache (rel, rig, idx);
  651.       _change_cache (ptr, parent, new);
  652.       }
  653.  
  654.    ptr = _read_cache (rel, old, idx);
  655.    _change_cache (ptr, parbal, BAL_EV);
  656.    _changeqcache (ptr, left,   0L);
  657.    _changeqcache (ptr, right,  0L);
  658.    _changeqcache (ptr, parent, 0L);
  659. }
  660.  
  661. long
  662. _find_seq (rel, top, rcd, idx, dir)  /* CACHED */
  663. relation  *rel;
  664. long            top, rcd;
  665. int                       idx, dir;
  666. {
  667.    char   temp[5];
  668.    long   pos, tmp;
  669.    cache *ptr;
  670.  
  671.    _strobe (rel, 0);  /* Don't let anyone think we're dead */
  672.  
  673.    ptr = _read_cache (rel, rcd, idx);
  674.    pos = _cache_field (ptr, dir);
  675.  
  676.    if (pos == 0L)
  677.       {
  678.       if (rcd == top)  return 0L;      /* hit top=no sequential available */
  679.       for (pos = rcd; ; pos = tmp)
  680.          {
  681.          ptr = _read_cache (rel, pos, idx);
  682.          tmp = ptr->parent;
  683.  
  684.          if (tmp == top)  return 0L;    /* hit top=no sequential available */
  685.  
  686.          temp[0] = ptr->parbal;
  687.          if (dir == ((temp[0] & PARDIR) ? -1 : 1))  return tmp;
  688.          } 
  689.       } 
  690.  
  691.    for (dir = 0-dir; ; pos = tmp)
  692.       {
  693.       ptr = _read_cache (rel, pos, idx);
  694.       tmp = _cache_field (ptr, dir);
  695.       if (tmp == 0L)  return pos;
  696.       }
  697. }
  698.  
  699. /*
  700.  * HEX DISPLAY ----------------------------------------------------------------
  701.  *
  702.  */
  703.  
  704. static char HexString[] = "0123456789ABCDEF";
  705.  
  706. void
  707. strtohex (buf, str, num)
  708. dataptr   buf;
  709. charptr        str;
  710. int                 num;
  711. {
  712.    byte *ptr;
  713.    bool  fHigh;
  714.    int   val;
  715.  
  716.    for (ptr = buf, fHigh = TRUE; num && *str; str++)
  717.       {
  718.       for (val = 0; val < 16; val++)
  719.          if (HexString[val] == toupper(*str))
  720.             break;
  721.       if (val == 16)
  722.          {
  723.          if (! fHigh)  /* If we just found 1 hex digit */
  724.             {
  725.             *ptr >>= 4;
  726.             ptr++;
  727.             num--;
  728.             }
  729.          fHigh = TRUE;
  730.          continue;
  731.          }
  732.  
  733.       if (fHigh)
  734.          {
  735.          *ptr = (byte)((int)val << 4);
  736.          }
  737.       else
  738.          {
  739.          *ptr |= (byte)val;
  740.          ptr ++;
  741.          num--;
  742.          }
  743.       fHigh = (fHigh) ? FALSE : TRUE;
  744.       }
  745.  
  746.    if (! fHigh)
  747.       {
  748.       *ptr >>= 4;
  749.       ptr++;
  750.       num--;
  751.       }
  752.  
  753.    for ( ; num; ptr++,num--)
  754.       {
  755.       *ptr = 0;
  756.       }
  757. }
  758.  
  759. void
  760. hextostr (str, buf, num)  /* Requires ( 3*num -1 ) characters */
  761. charptr   str;
  762. dataptr        buf;
  763. int                 num;
  764. {
  765.    byte  *ptr;
  766.  
  767.    for (ptr = (byte *)buf; num; num--,ptr++)
  768.       {
  769.       if (ptr != (byte *)buf)
  770.          *str++ = ' ';
  771.       *str++ = HexString[ ((*ptr & 0xF0) >> 4) ];
  772.       *str++ = HexString[  (*ptr & 0x0F) ];
  773.       }
  774.  
  775.    *str = 0;
  776. }
  777.  
  778.