text: /*
   * This file is Copyright (c) 2010 by the GPSD project
   * SPDX-License-Identifier: BSD-2-clause
   */
  #include <string.h>
  #include <ctype.h>
  #include <assert.h>
  
  #include "gpsd.h"
  
  /*
   * gpsd_packetdump()
   *
   * if binbuf is printable, just return a copy of it
   * otherwise) convert a binary string to a hex string and return that
   *
   * *scbuf   -- the buffer to fill with hex
   * scbuflen -- sizeof(scbuf)
   * *binbuf  -- the binary to convert to hex and place in scbuf
   * binbuflen -- sizeof(binbuf)
   *
   * scbuflen needs to be 2x binbuflen to hold the hex conversion
   */
  
  const char *gpsd_packetdump(char *scbuf, size_t scbuflen,
  			    char *binbuf, size_t binbuflen)
  {
      char *cp;
      bool printable = true;
  
      assert(binbuf != NULL);
      for (cp = binbuf; cp < binbuf + binbuflen; cp++)
  	if (!isprint((unsigned char) *cp) && !isspace((unsigned char) *cp)) {
  	    printable = false;
  	    break;	/* no need to keep iterating */
          }
      if (printable)
  	return binbuf;
      else
  	return gpsd_hexdump(scbuf, scbuflen, binbuf, binbuflen);
  }
  
  const char *gpsd_hexdump(char *scbuf, size_t scbuflen,
  					  char *binbuf, size_t binbuflen)
  {
  #ifndef SQUELCH_ENABLE
      size_t i, j = 0;
      size_t len =
  	(size_t) ((binbuflen >
  		   MAX_PACKET_LENGTH) ? MAX_PACKET_LENGTH : binbuflen);
      const char *ibuf = (const char *)binbuf;
      const char *hexchar = "0123456789abcdef";
  
      if (NULL == binbuf || 0 == binbuflen)
  	return "";
  
      for (i = 0; i < len && j < (scbuflen - 3); i++) {
  	scbuf[j++] = hexchar[(ibuf[i] & 0xf0) >> 4];
  	scbuf[j++] = hexchar[ibuf[i] & 0x0f];
      }
      scbuf[j] = '\0';
  #else /* SQUELCH defined */
      scbuf[0] = '\0';
      (void)scbuflen;
      (void)binbuf;
      (void)binbuflen;
  #endif /* SQUELCH_ENABLE */
      return scbuf;
  }
  
  static int hex2bin(const char *s)
  {
      int a, b;
  
      a = s[0] & 0xff;
      b = s[1] & 0xff;
  
      if ((a >= 'a') && (a <= 'f'))
  	a = a + 10 - 'a';
      else if ((a >= 'A') && (a <= 'F'))
  	a = a + 10 - 'A';
      else if ((a >= '0') && (a <= '9'))
  	a -= '0';
      else
  	return -1;
  
      if ((b >= 'a') && (b <= 'f'))
  	b = b + 10 - 'a';
      else if ((b >= 'A') && (b <= 'F'))
  	b = b + 10 - 'A';
      else if ((b >= '0') && (b <= '9'))
  	b -= '0';
      else
  	return -1;
  
      return ((a << 4) + b);
  }
  
  int gpsd_hexpack(const char *src, char *dst, size_t len)
  /* hex2bin source string to destination - destination can be same as source */
  {
      int i, j;
  
      j = (int)(strlen(src) / 2);
      if ((j < 1) || ((size_t) j > len))
  	return -2;
  
      for (i = 0; i < j; i++) {
  	int k;
  	if ((k = hex2bin(src + i * 2)) != -1)
  	    dst[i] = (char)(k & 0xff);
  	else
  	    return -1;
      }
      (void)memset(dst + i, '\0', (size_t) (len - i));
      return j;
  }
  
  
  ssize_t hex_escapes(char *cooked, const char *raw)
  /* interpret C-style hex escapes */
  {
      char c, *cookend;
  
      for (cookend = cooked; *raw != '\0'; raw++)
  	if (*raw != '\\')
  	    *cookend++ = *raw;
  	else {
  	    switch (*++raw) {
  	    case 'b':
  		*cookend++ = '\b';
  		break;
  	    case 'e':
  		*cookend++ = '\x1b';
  		break;
  	    case 'f':
  		*cookend++ = '\f';
  		break;
  	    case 'n':
  		*cookend++ = '\n';
  		break;
  	    case 'r':
  		*cookend++ = '\r';
  		break;
  	    case 't':
  		*cookend++ = '\r';
  		break;
  	    case 'v':
  		*cookend++ = '\v';
  		break;
  	    case 'x':
  		switch (*++raw) {
  		case '0':
  		    c = (char)0x00;
  		    break;
  		case '1':
  		    c = (char)0x10;
  		    break;
  		case '2':
  		    c = (char)0x20;
  		    break;
  		case '3':
  		    c = (char)0x30;
  		    break;
  		case '4':
  		    c = (char)0x40;
  		    break;
  		case '5':
  		    c = (char)0x50;
  		    break;
  		case '6':
  		    c = (char)0x60;
  		    break;
  		case '7':
  		    c = (char)0x70;
  		    break;
  		case '8':
  		    c = (char)0x80;
  		    break;
  		case '9':
  		    c = (char)0x90;
  		    break;
  		case 'A':
  		case 'a':
  		    c = (char)0xa0;
  		    break;
  		case 'B':
  		case 'b':
  		    c = (char)0xb0;
  		    break;
  		case 'C':
  		case 'c':
  		    c = (char)0xc0;
  		    break;
  		case 'D':
  		case 'd':
  		    c = (char)0xd0;
  		    break;
  		case 'E':
  		case 'e':
  		    c = (char)0xe0;
  		    break;
  		case 'F':
  		case 'f':
  		    c = (char)0xf0;
  		    break;
  		default:
  		    return -1;
  		}
  		switch (*++raw) {
  		case '0':
  		    c += 0x00;
  		    break;
  		case '1':
  		    c += 0x01;
  		    break;
  		case '2':
  		    c += 0x02;
  		    break;
  		case '3':
  		    c += 0x03;
  		    break;
  		case '4':
  		    c += 0x04;
  		    break;
  		case '5':
  		    c += 0x05;
  		    break;
  		case '6':
  		    c += 0x06;
  		    break;
  		case '7':
  		    c += 0x07;
  		    break;
  		case '8':
  		    c += 0x08;
  		    break;
  		case '9':
  		    c += 0x09;
  		    break;
  		case 'A':
  		case 'a':
  		    c += 0x0a;
  		    break;
  		case 'B':
  		case 'b':
  		    c += 0x0b;
  		    break;
  		case 'C':
  		case 'c':
  		    c += 0x0c;
  		    break;
  		case 'D':
  		case 'd':
  		    c += 0x0d;
  		    break;
  		case 'E':
  		case 'e':
  		    c += 0x0e;
  		    break;
  		case 'F':
  		case 'f':
  		    c += 0x0f;
  		    break;
  		default:
  		    return -2;
  		}
  		*cookend++ = c;
  		break;
  	    case '\\':
  		*cookend++ = '\\';
  		break;
  	    default:
  		return -3;
  	    }
  	}
      return (ssize_t) (cookend - cooked);
  }
  