"Fossies" - the Fresh Open Source Software Archive

Member "schily-2021-09-18/tartest/tartest.c" (20 Aug 2021, 15540 Bytes) of package /linux/privat/schily-2021-09-18.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /* @(#)tartest.c    1.24 21/08/20 Copyright 2002-2021 J. Schilling */
    2 #include <schily/mconfig.h>
    3 #ifndef lint
    4 static  UConst char sccsid[] =
    5     "@(#)tartest.c  1.24 21/08/20 Copyright 2002-2021 J. Schilling";
    6 #endif
    7 /*
    8  *  Copyright (c) 2002-2021 J. Schilling
    9  */
   10 /*
   11  * The contents of this file are subject to the terms of the
   12  * Common Development and Distribution License, Version 1.0 only
   13  * (the "License").  You may not use this file except in compliance
   14  * with the License.
   15  *
   16  * See the file CDDL.Schily.txt in this distribution for details.
   17  * A copy of the CDDL is also available via the Internet at
   18  * http://www.opensource.org/licenses/cddl1.txt
   19  *
   20  * When distributing Covered Code, include this CDDL HEADER in each
   21  * file and include the License file CDDL.Schily.txt from this distribution.
   22  */
   23 
   24 #include <schily/stdio.h>
   25 #include <schily/stdlib.h>
   26 
   27 #include "star.h"
   28 #include <schily/standard.h>
   29 #include <schily/string.h>
   30 #include <schily/getargs.h>
   31 #define GT_COMERR       /* #define comerr gtcomerr */
   32 #define GT_ERROR        /* #define error gterror   */
   33 #include <schily/schily.h>
   34 
   35 #include <schily/fcntl.h>           /* O_BINARY */
   36 #include <schily/io.h>              /* for setmode() prototype */
   37 #include <schily/nlsdefs.h>
   38 
   39 LOCAL   void    usage       __PR((int ret));
   40 EXPORT  int main        __PR((int ac, char *av[]));
   41 LOCAL   BOOL    doit        __PR((FILE *f));
   42 LOCAL   BOOL    checkhdr    __PR((TCB *ptb));
   43 LOCAL   BOOL    checkoctal  __PR((char *ptr, int len, char *text));
   44 LOCAL   BOOL    checktype   __PR((TCB *ptb));
   45 LOCAL   BOOL    checkid     __PR((char *ptr, char *text));
   46 LOCAL   BOOL    checkmagic  __PR((char *ptr));
   47 LOCAL   BOOL    checkvers   __PR((char *ptr));
   48 EXPORT  void    stolli      __PR((char *s, Ullong *ull, int len));
   49 LOCAL   Ulong   checksum    __PR((TCB *ptb));
   50 LOCAL   void    pretty_char __PR((char *p, unsigned c));
   51 
   52 LOCAL   BOOL    verbose;
   53 LOCAL   BOOL    signedcksum;
   54 LOCAL   BOOL    is_posix_2001;
   55 
   56 LOCAL void
   57 usage(ret)
   58     int ret;
   59 {
   60     error("Usage:\t%s [options] < file\n", get_progname());
   61     error("Options:\n");
   62     error("\t-help\t\tprint this help\n");
   63     error("\t-version\tPrint version number.\n");
   64     error("\t-v\t\tprint all filenames during verification\n");
   65     error("\n%s checks stdin fore compliance with the POSIX.1-1990 TAR standard\n", get_progname());
   66     exit(ret);
   67     /* NOTREACHED */
   68 }
   69 
   70 EXPORT int
   71 main(ac, av)
   72     int ac;
   73     char    *av[];
   74 {
   75     int     cac = ac;
   76     char    *const *cav = av;
   77     BOOL        help = FALSE;
   78     BOOL        prversion = FALSE;
   79 
   80     save_args(ac, av);
   81 
   82     (void) setlocale(LC_ALL, "");
   83 
   84 #ifdef  USE_NLS
   85 #if !defined(TEXT_DOMAIN)   /* Should be defined by cc -D */
   86 #define TEXT_DOMAIN "tartest"   /* Use this only if it weren't */
   87 #endif
   88     { char  *dir;
   89     dir = searchfileinpath("share/locale", F_OK,
   90                     SIP_ANY_FILE|SIP_NO_PATH, NULL);
   91     if (dir)
   92         (void) bindtextdomain(TEXT_DOMAIN, dir);
   93     else
   94 #if defined(PROTOTYPES) && defined(INS_BASE)
   95     (void) bindtextdomain(TEXT_DOMAIN, INS_BASE "/share/locale");
   96 #else
   97     (void) bindtextdomain(TEXT_DOMAIN, "/usr/share/locale");
   98 #endif
   99     (void) textdomain(TEXT_DOMAIN);
  100     }
  101 #endif  /* USE_NLS */
  102     cac--;
  103     cav++;
  104     if (getallargs(&cac, &cav, "help,h,version,v", &help, &help,
  105                         &prversion, &verbose) < 0) {
  106         errmsgno(EX_BAD, "Bad Option: '%s'.\n", cav[0]);
  107         usage(EX_BAD);
  108     }
  109     if (help)
  110         usage(0);
  111 
  112     printf("tartest %s (%s-%s-%s)\n\n", "1.24",
  113                     HOST_CPU, HOST_VENDOR, HOST_OS);
  114     gtprintf("Copyright (C) 2002-2021 %s\n", _("Jörg Schilling"));
  115     gtprintf("This is free software; see the source for copying conditions.  There is NO\n");
  116     gtprintf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
  117     if (prversion)
  118         exit(0);
  119 
  120     gtprintf("\nTesting for POSIX.1-1990 TAR compliance...\n");
  121 
  122     setmode(fileno(stdin), O_BINARY);
  123 
  124     if (!doit(stdin)) {
  125         gtprintf(">>> Archive is not POSIX.1-1990 TAR standard compliant.\n");
  126         return (1);
  127     }
  128     gtprintf("No deviations from POSIX.1-1990 TAR standard found.\n");
  129     return (0);
  130 }
  131 
  132 LOCAL BOOL
  133 doit(f)
  134     FILE    *f;
  135 {
  136     BOOL    ret = TRUE;
  137     BOOL    r;
  138     TCB tcb;
  139     TCB *ptb;
  140     char    name[257];
  141     char    lname[101];
  142     Ullong  checks;
  143     Ullong  hsum;
  144     Ullong  size;
  145     Ullong  blockno = 0;
  146     int i;
  147 
  148     ptb = &tcb;
  149     for (;;) {
  150         r = TRUE;
  151         fillbytes(ptb, TBLOCK, '\0');
  152         if (fileread(f, ptb, TBLOCK) != TBLOCK) {
  153             gtprintf("Hard EOF at block %lld\n", blockno);
  154             return (FALSE);
  155         }
  156 
  157         stolli(ptb->ustar_dbuf.t_chksum, &checks, 8);
  158         hsum = checksum(ptb);
  159         if (hsum == 0) {
  160             /*
  161              * Check EOF
  162              */
  163             gtprintf("Found 1st EOF block at %lld\n", blockno);
  164             blockno++;
  165             if (fileread(f, ptb, TBLOCK) != TBLOCK) {
  166                 gtprintf(
  167                 "Hard EOF at block %lld (second EOF block missing)\n",
  168                     blockno);
  169                 return (FALSE);
  170             }
  171             hsum = checksum(ptb);
  172             if (hsum != 0) {
  173                 gtprintf(
  174                     "Second EOF block missing at %lld\n",
  175                     blockno);
  176                 return (FALSE);
  177             }
  178             gtprintf("Found 2nd EOF block at %lld\n", blockno);
  179             return (ret);
  180         }
  181         if (checks != hsum) {
  182             gtprintf("Bad Checksum %0llo != %0llo at block %lld\n",
  183                             checks, hsum, blockno);
  184             signedcksum = TRUE;
  185             hsum = checksum(ptb);
  186             if (checks == hsum) {
  187                 gtprintf("Warning: archive uses signed checksums.\n");
  188                 return (FALSE);
  189             }
  190             if (blockno != 0) {
  191                 if (is_posix_2001) {
  192                     gtprintf(
  193                     "The archive may either be corrupted or using the POSIX.1-2001 size field.\n");
  194                 } else {
  195                     gtprintf(
  196                     "Warning: Corrupted TAR archive.\n");
  197                 }
  198             }
  199             return (FALSE);
  200         }
  201         blockno++;
  202 
  203         stolli(ptb->ustar_dbuf.t_size, &size, 12);
  204 
  205         if (ptb->ustar_dbuf.t_prefix[0]) {
  206             js_snprintf(name, sizeof (name), "%.155s/%.100s",
  207                 ptb->ustar_dbuf.t_prefix,
  208                 ptb->ustar_dbuf.t_name);
  209         } else {
  210             strncpy(name, ptb->ustar_dbuf.t_name, 100);
  211             name[100] = '\0';
  212         }
  213         strncpy(lname, ptb->ustar_dbuf.t_linkname, 100);
  214         lname[100] = '\0';
  215 
  216         r = checkhdr(ptb);
  217         if (!r)
  218             ret = FALSE;
  219 
  220         /*
  221          * Handle the size field acording to POSIX.1-1990.
  222          */
  223         i = tarblocks(size);
  224         switch ((int)(ptb->ustar_dbuf.t_typeflag & 0xFF)) {
  225 
  226         case '\0':  /* Old plain file */
  227         case '0':   /* Ustar plain file */
  228         case '7':   /* Contiguous file */
  229             break;
  230 
  231         case '1':   /* Hard link */
  232         case '2':   /* Symbolic link */
  233             if (i != 0) {
  234                 gtprintf(
  235                 "Warning: t_size field: %0llu, should be 0 for %s link\n",
  236                     size,
  237                     ptb->ustar_dbuf.t_typeflag == '1'?
  238                     "hard":"symbolic");
  239                 ret = r = FALSE;
  240             }
  241             i = 0;
  242             break;
  243         case '3':   /* Character special */
  244         case '4':   /* Block special */
  245         case '5':   /* Directory */
  246         case '6':   /* FIFO  (named pipe) */
  247             i = 0;
  248             break;
  249         }
  250 
  251         if (!r || verbose) {
  252             gtprintf("*** %sFilename '%s'\n",
  253                         r == FALSE ?
  254                         "Failing ":"", name);
  255             if (lname[0]) {
  256                 gtprintf("*** %sLinkname '%s'\n",
  257                         r == FALSE ?
  258                         "Failing ":"", lname);
  259             }
  260         }
  261 
  262         /*
  263          * Skip file content.
  264          */
  265         while (--i >= 0) {
  266             if (fileread(f, ptb, TBLOCK) != TBLOCK) {
  267                 gtprintf("Hard EOF at block %lld\n", blockno);
  268                 return (FALSE);
  269             }
  270             blockno++;
  271         }
  272     }
  273 
  274 }
  275 
  276 LOCAL BOOL
  277 checkhdr(ptb)
  278     TCB *ptb;
  279 {
  280     BOOL    ret = TRUE;
  281     int errs = 0;
  282     Ullong  ll;
  283 
  284     if (ptb->ustar_dbuf.t_name[  0] == '\0') {
  285         gtprintf("Warning: t_name[  0] is a null character.\n");
  286         errs++;
  287     }
  288     if (ptb->ustar_dbuf.t_name[  0] != '\0' &&
  289         ptb->ustar_dbuf.t_name[ 99] != '\0' &&
  290         /* LINTED */
  291         ptb->ndbuf.t_name[100] == '\0') {
  292         gtprintf("Warning: t_name[100] is a null character.\n");
  293         errs++;
  294     }
  295     if (ptb->ustar_dbuf.t_linkname[  0] != '\0' &&
  296         ptb->ustar_dbuf.t_linkname[ 99] != '\0' &&
  297         /* LINTED */
  298         ptb->ndbuf.t_linkname[100] == '\0') {
  299         gtprintf("Warning: t_linkname[100] is a null character.\n");
  300         errs++;
  301     }
  302 
  303     if (!checkoctal(ptb->ustar_dbuf.t_mode, 8, "t_mode"))
  304         errs++;
  305 
  306     stolli(ptb->ustar_dbuf.t_mode, &ll, 8);
  307     if (ll & ~07777) {
  308         gtprintf(
  309         "Warning: too many bits in t_mode field: 0%llo, should be 0%llo\n",
  310             ll, ll & 07777);
  311         errs++;
  312     }
  313 
  314     if (!checkoctal(ptb->ustar_dbuf.t_uid, 8, "t_uid"))
  315         errs++;
  316 
  317     if (!checkoctal(ptb->ustar_dbuf.t_gid, 8, "t_gid"))
  318         errs++;
  319 
  320     if (!checkoctal(ptb->ustar_dbuf.t_size, 12, "t_size"))
  321         errs++;
  322 
  323     if (!checkoctal(ptb->ustar_dbuf.t_mtime, 12, "t_mtime"))
  324         errs++;
  325 
  326     if (!checkoctal(ptb->ustar_dbuf.t_chksum, 8, "t_chksum"))
  327         errs++;
  328 
  329     if (!checktype(ptb))
  330         errs++;
  331 
  332     if (!checkmagic(ptb->ustar_dbuf.t_magic))
  333         errs++;
  334 
  335     if (!checkvers(ptb->ustar_dbuf.t_version))
  336         errs++;
  337 
  338     if (!checkid(ptb->ustar_dbuf.t_uname, "t_uname"))
  339         errs++;
  340     if (!checkid(ptb->ustar_dbuf.t_gname, "t_gname"))
  341         errs++;
  342 
  343     if (!checkoctal(ptb->ustar_dbuf.t_devmajor, 8, "t_devmajor"))
  344         errs++;
  345 
  346     if (!checkoctal(ptb->ustar_dbuf.t_devminor, 8, "t_devminor"))
  347         errs++;
  348 
  349 #ifdef  __needed__
  350     /*
  351      * The POSIX.1 TAR standard does not mention the last 12 bytes in the
  352      * TAR header. They may have any value...
  353      */
  354     if (cmpnullbytes(ptb->ustar_dbuf.t_mfill, 12) < 12) {
  355         gtprintf(
  356         "Warning: non null character in last 12 bytes of header\n");
  357         errs++;
  358     }
  359 #endif
  360 
  361     if (errs)
  362         ret = FALSE;
  363     return (ret);
  364 }
  365 
  366 /*
  367  * Check whether octal numeric fields are according to POSIX.1-1990.
  368  */
  369 LOCAL BOOL
  370 checkoctal(ptr, len, text)
  371     char    *ptr;
  372     int len;
  373     char    *text;
  374 {
  375     BOOL    ret = TRUE;
  376     BOOL    foundoctal = FALSE;
  377     int i;
  378     int endoff = 0;
  379 #ifdef      END_ALL_THESAME
  380     char    endc = '\0';
  381 #endif
  382     char    cs[4];
  383 
  384     for (i = 0; i < len; i++) {
  385 #ifdef  CHECKOCTAL_DEBUG
  386         error("%d '%c'\n", i, ptr[i]);
  387 #endif
  388 
  389 #ifdef      END_ALL_THESAME
  390         if (endoff > 0 && ptr[i] != endc) {
  391 #else
  392         /*
  393          * Ugly, but the standard seems to allow mixins space and null
  394          * characters at the end of an octal numeric field.
  395          */
  396         if (endoff > 0 && (ptr[i] != ' ' && ptr[i] != '\0')) {
  397 #endif
  398             pretty_char(cs, ptr[i] & 0xFF);
  399             gtprintf(
  400             "Warning: illegal end character '%s' (0x%02X) found in field '%s[%d]'\n",
  401                     cs,
  402                     ptr[i] & 0xFF,
  403                     text, i);
  404             ret = FALSE;
  405         }
  406         if (endoff > 0)
  407             continue;
  408         if (ptr[i] == ' ' || ptr[i] == '\0') {
  409             if (foundoctal) {
  410                 endoff = i;
  411 #ifdef      END_ALL_THESAME
  412                 endc = ptr[i];
  413 #endif
  414                 continue;
  415             }
  416         }
  417         if (!isoctal(ptr[i])) {
  418             pretty_char(cs, ptr[i] & 0xFF);
  419             gtprintf(
  420             "Warning: non octal character '%s' (0x%02X) found in field '%s[%d]'\n",
  421                     cs,
  422                     ptr[i] & 0xFF,
  423                     text, i);
  424             ret = FALSE;
  425         } else {
  426             foundoctal = TRUE;
  427         }
  428     }
  429     if (foundoctal && endoff == 0) {
  430         gtprintf("Warning: no end character found in field '%s'\n",
  431             text);
  432         ret = FALSE;
  433     }
  434     return (ret);
  435 }
  436 
  437 /*
  438  * Check whether the POSIX.1-1990 'typeflag' field contains a valid character.
  439  */
  440 LOCAL BOOL
  441 checktype(ptb)
  442     TCB *ptb;
  443 {
  444     BOOL    ret = TRUE;
  445     char    cs[4];
  446 
  447     switch ((int)(ptb->ustar_dbuf.t_typeflag & 0xFF)) {
  448 
  449     case '\0':  /* Old plain file */
  450     case '0':   /* Ustar plain file */
  451     case '1':   /* Hard link */
  452     case '2':   /* Symbolic link */
  453     case '3':   /* Character special */
  454     case '4':   /* Block special */
  455     case '5':   /* Directory */
  456     case '6':   /* FIFO  (named pipe) */
  457     case '7':   /* Contiguous file */
  458         break;
  459 
  460     case 'g':
  461     case 'x':
  462         if (!is_posix_2001) {
  463             gtprintf("Warning: Archive uses POSIX.1-2001 extensions.\n");
  464             gtprintf("Warning: The correctness of the size field cannot be checked for this reason.\n");
  465             is_posix_2001 = TRUE;
  466         }
  467         break;
  468 
  469     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
  470     case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
  471     case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
  472     case 'V': case 'W': case 'X': case 'Y': case 'Z':
  473         { static char vend[256];
  474             if (vend[ptb->ustar_dbuf.t_typeflag & 0xFF] == 0) {
  475                 gtprintf(
  476                 "Warning: Archive uses Vendor specific extension file type '%c'.\n",
  477                 ptb->ustar_dbuf.t_typeflag & 0xFF);
  478                 vend[ptb->ustar_dbuf.t_typeflag & 0xFF] = 1;
  479             }
  480         }
  481         break;
  482 
  483     default:
  484         pretty_char(cs, ptb->ustar_dbuf.t_typeflag & 0xFF);
  485         gtprintf(
  486         "Warning: Archive uses illegal file type '%s' (0x%02X).\n",
  487                 cs,
  488                 ptb->ustar_dbuf.t_typeflag & 0xFF);
  489         ret = FALSE;
  490     }
  491     return (ret);
  492 }
  493 
  494 /*
  495  * Check whether the POSIX.1-1990 'uid' or 'gid' field contains
  496  * reasonable things.
  497  */
  498 LOCAL BOOL
  499 checkid(ptr, text)
  500     char    *ptr;
  501     char    *text;
  502 {
  503     BOOL    ret = TRUE;
  504     char    cs[4];
  505     int len = 32;
  506     int i;
  507 
  508     if (ptr[0] == '\0') {
  509         for (i = 0; i < len; i++) {
  510             if (ptr[i] != '\0') {
  511                 pretty_char(cs, ptr[i] & 0xFF);
  512                 gtprintf(
  513                 "Warning: non null character '%s' (0x%02X) found in field '%s[%d]'\n",
  514                         cs,
  515                         ptr[i] & 0xFF,
  516                         text, i);
  517                 ret = FALSE;
  518             }
  519         }
  520         return (ret);
  521     }
  522     i = len - 1;
  523     if (ptr[i] != '\0') {
  524         pretty_char(cs, ptr[i] & 0xFF);
  525         gtprintf(
  526         "Warning: non null string terminator character '%s' (0x%02X) found in field '%s[%d]'\n",
  527                 cs,
  528                 ptr[i] & 0xFF,
  529                 text, i);
  530         ret = FALSE;
  531     }
  532     return (ret);
  533 }
  534 
  535 /*
  536  * Check whether the POSIX.1-1990 'magic' field contains the
  537  * two string "ustar" (5 characters and a null byte).
  538  */
  539 LOCAL BOOL
  540 checkmagic(ptr)
  541     char    *ptr;
  542 {
  543     BOOL    ret = TRUE;
  544     char    *mag = "ustar";
  545     char    cs[4];
  546     int i;
  547 
  548     for (i = 0; i < 6; i++) {
  549         if (ptr[i] != mag[i]) {
  550             pretty_char(cs, ptr[i] & 0xFF);
  551             gtprintf(
  552             "Warning: illegal character '%s' (0x%02X) found in field 't_magic[%d]'\n",
  553                     cs,
  554                     ptr[i] & 0xFF, i);
  555             ret = FALSE;
  556         }
  557     }
  558     return (ret);
  559 }
  560 
  561 /*
  562  * Check whether the POSIX.1-1990 'version' field contains the
  563  * two characters "00".
  564  */
  565 LOCAL BOOL
  566 checkvers(ptr)
  567     char    *ptr;
  568 {
  569     BOOL    ret = TRUE;
  570     char    *vers = "00";
  571     char    cs[4];
  572     int i;
  573 
  574     for (i = 0; i < 2; i++) {
  575         if (ptr[i] != vers[i]) {
  576             pretty_char(cs, ptr[i] & 0xFF);
  577             gtprintf(
  578             "Warning: illegal character '%s' (0x%02X) found in field 't_version[%d]'\n",
  579                     cs,
  580                     ptr[i] & 0xFF, i);
  581             ret = FALSE;
  582         }
  583     }
  584     return (ret);
  585 }
  586 
  587 
  588 /*
  589  * Convert string -> long long int
  590  * This is the debug version that stops at "len" size to be safe against
  591  * field overflow.
  592  */
  593 EXPORT void
  594 stolli(s, ull, len)
  595     register char   *s;
  596         Ullong  *ull;
  597         int len;
  598 {
  599     register Ullong ret = (Ullong)0;
  600     register char   c;
  601     register int    t;
  602 
  603     while (*s == ' ') {
  604         if (--len < 0)
  605             break;
  606         s++;
  607     }
  608 
  609     for (;;) {
  610         if (--len < 0)
  611             break;
  612         c = *s++;
  613 #ifdef  STOLLI_DEBUG
  614         error("'%c'\n", c);
  615 #endif
  616         if (isoctal(c))
  617             t = c - '0';
  618         else
  619             break;
  620         ret *= 8;
  621         ret += t;
  622     }
  623     *ull = ret;
  624 #ifdef  STOLLI_DEBUG
  625     error("len: %d\n", len);
  626 #endif
  627 }
  628 
  629 /*
  630  * Checsum function.
  631  * Returns 0 if the block contains nothing but null characters.
  632  */
  633 #define CHECKS  sizeof (ptb->ustar_dbuf.t_chksum)
  634 /*
  635  * We know, that sizeof (TCP) is 512 and therefore has no
  636  * reminder when dividing by 8
  637  *
  638  * CHECKS is known to be 8 too, use loop unrolling.
  639  */
  640 #define DO8(a)  a; a; a; a; a; a; a; a;
  641 
  642 LOCAL Ulong
  643 checksum(ptb)
  644     register    TCB *ptb;
  645 {
  646     register    int i;
  647     register    Ulong   sum = 0;
  648     register    Uchar   *us;
  649 
  650     if (signedcksum) {
  651         register    char    *ss;
  652 
  653         ss = (char *)ptb;
  654         for (i = sizeof (*ptb)/8; --i >= 0; ) {
  655             DO8(sum += *ss++);
  656         }
  657         if (sum == 0L)      /* Block containing 512 nul's */
  658             return (sum);
  659 
  660         ss = (char *)ptb->ustar_dbuf.t_chksum;
  661         DO8(sum -= *ss++);
  662         sum += CHECKS*' ';
  663     } else {
  664         us = (Uchar *)ptb;
  665         for (i = sizeof (*ptb)/8; --i >= 0; ) {
  666             DO8(sum += *us++);
  667         }
  668         if (sum == 0L)      /* Block containing 512 nul's */
  669             return (sum);
  670 
  671         us = (Uchar *)ptb->ustar_dbuf.t_chksum;
  672         DO8(sum -= *us++);
  673         sum += CHECKS*' ';
  674     }
  675     return (sum);
  676 }
  677 
  678 /*
  679  * Pretty print one character.
  680  * Quote anything that is not a printable 7 bit ASCII character.
  681  */
  682 #define SP  ' '
  683 #define DEL '\177'
  684 #define SP8 (SP | 0x80)
  685 #define DEL8    (DEL | 0x80)
  686 
  687 LOCAL void
  688 pretty_char(p, c)
  689     char        *p;
  690     unsigned    c;
  691 {
  692     c &= 0xFF;
  693 
  694     if (c < SP || c == DEL) {           /* ctl char */
  695         *p++ = '^';
  696         *p++ = c ^ 0100;
  697     } else if ((c > DEL && c < SP8) || c == DEL8) {  /* 8 bit ctl */
  698         *p++ = '~';
  699         *p++ = '^';
  700         *p++ = c ^ 0300;
  701     } else if (c >= SP8) {              /* 8 bit char */
  702         *p++ = '~';
  703         *p++ = c & 0177;
  704     } else {                    /* normal char */
  705         *p++ = c;
  706     }
  707     *p = '\0';
  708 }