"Fossies" - the Fresh Open Source Software Archive

Member "termcap-1.3.1/termcap.c" (25 Feb 2002, 17655 Bytes) of package /linux/misc/old/termcap-1.3.1.tar.gz:


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 /* Work-alike for termcap, plus extra features.
    2    Copyright (C) 1985, 86, 93, 94, 95, 2000, 2001
    3    Free Software Foundation, Inc.
    4 
    5 This program is free software; you can redistribute it and/or modify
    6 it under the terms of the GNU General Public License as published by
    7 the Free Software Foundation; either version 2, or (at your option)
    8 any later version.
    9 
   10 This program is distributed in the hope that it will be useful,
   11 but WITHOUT ANY WARRANTY; without even the implied warranty of
   12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13 GNU General Public License for more details.
   14 
   15 You should have received a copy of the GNU General Public License
   16 along with this program; see the file COPYING.  If not, write to
   17 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   18 Boston, MA 02111-1307, USA.  */
   19 
   20 /* Emacs config.h may rename various library functions such as malloc.  */
   21 #ifdef HAVE_CONFIG_H
   22 #include <config.h>
   23 #endif
   24 
   25 #ifdef emacs
   26 
   27 #include <lisp.h>       /* xmalloc is here */
   28 /* Get the O_* definitions for open et al.  */
   29 #include <sys/file.h>
   30 #ifdef HAVE_FCNTL_H
   31 #include <fcntl.h>
   32 #endif
   33 #ifdef HAVE_UNISTD_H
   34 #include <unistd.h>
   35 #endif
   36 
   37 #else /* not emacs */
   38 
   39 #ifdef STDC_HEADERS
   40 #include <stdlib.h>
   41 #include <string.h>
   42 #else
   43 char *getenv ();
   44 char *malloc ();
   45 char *realloc ();
   46 #endif
   47 
   48 /* Do this after the include, in case string.h prototypes bcopy.  */
   49 #if (defined(HAVE_STRING_H) || defined(STDC_HEADERS)) && !defined(bcopy)
   50 #define bcopy(s, d, n) memcpy ((d), (s), (n))
   51 #endif
   52 
   53 #ifdef HAVE_UNISTD_H
   54 #include <unistd.h>
   55 #endif
   56 #ifdef _POSIX_VERSION
   57 #include <fcntl.h>
   58 #endif
   59 
   60 #endif /* not emacs */
   61 
   62 #ifndef NULL
   63 #define NULL (char *) 0
   64 #endif
   65 
   66 #ifndef O_RDONLY
   67 #define O_RDONLY 0
   68 #endif
   69 
   70 /* BUFSIZE is the initial size allocated for the buffer
   71    for reading the termcap file.
   72    It is not a limit.
   73    Make it large normally for speed.
   74    Make it variable when debugging, so can exercise
   75    increasing the space dynamically.  */
   76 
   77 #ifndef BUFSIZE
   78 #ifdef DEBUG
   79 #define BUFSIZE bufsize
   80 
   81 int bufsize = 128;
   82 #else
   83 #define BUFSIZE 2048
   84 #endif
   85 #endif
   86 
   87 #ifndef TERMCAP_FILE
   88 #define TERMCAP_FILE "/etc/termcap"
   89 #endif
   90 
   91 #ifndef emacs
   92 static void
   93 memory_out ()
   94 {
   95   write (2, "virtual memory exhausted\n", 25);
   96   exit (1);
   97 }
   98 
   99 static char *
  100 xmalloc (size)
  101      unsigned size;
  102 {
  103   register char *tem = malloc (size);
  104 
  105   if (!tem)
  106     memory_out ();
  107   return tem;
  108 }
  109 
  110 static char *
  111 xrealloc (ptr, size)
  112      char *ptr;
  113      unsigned size;
  114 {
  115   register char *tem = realloc (ptr, size);
  116 
  117   if (!tem)
  118     memory_out ();
  119   return tem;
  120 }
  121 #endif /* not emacs */
  122 
  123 /* Looking up capabilities in the entry already found.  */
  124 
  125 /* The pointer to the data made by tgetent is left here
  126    for tgetnum, tgetflag and tgetstr to find.  */
  127 static char *term_entry;
  128 
  129 static char *tgetst1 ();
  130 
  131 /* Search entry BP for capability CAP.
  132    Return a pointer to the capability (in BP) if found,
  133    0 if not found.  */
  134 
  135 static char *
  136 find_capability (bp, cap)
  137      register char *bp, *cap;
  138 {
  139   for (; *bp; bp++)
  140     if (bp[0] == ':'
  141     && bp[1] == cap[0]
  142     && bp[2] == cap[1])
  143       return &bp[4];
  144   return NULL;
  145 }
  146 
  147 int
  148 tgetnum (cap)
  149      char *cap;
  150 {
  151   register char *ptr = find_capability (term_entry, cap);
  152   if (!ptr || ptr[-1] != '#')
  153     return -1;
  154   return atoi (ptr);
  155 }
  156 
  157 int
  158 tgetflag (cap)
  159      char *cap;
  160 {
  161   register char *ptr = find_capability (term_entry, cap);
  162   return ptr && ptr[-1] == ':';
  163 }
  164 
  165 /* Look up a string-valued capability CAP.
  166    If AREA is non-null, it points to a pointer to a block in which
  167    to store the string.  That pointer is advanced over the space used.
  168    If AREA is null, space is allocated with `malloc'.  */
  169 
  170 char *
  171 tgetstr (cap, area)
  172      char *cap;
  173      char **area;
  174 {
  175   register char *ptr = find_capability (term_entry, cap);
  176   if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~'))
  177     return NULL;
  178   return tgetst1 (ptr, area);
  179 }
  180 
  181 #ifdef IS_EBCDIC_HOST
  182 /* Table, indexed by a character in range 0200 to 0300 with 0200 subtracted,
  183    gives meaning of character following \, or a space if no special meaning.
  184    Sixteen characters per line within the string.  */
  185 
  186 static char esctab[]
  187   = " \057\026  \047\014         \
  188      \025   \015      \
  189    \005 \013          \
  190                 ";
  191 #else
  192 /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
  193    gives meaning of character following \, or a space if no special meaning.
  194    Eight characters per line within the string.  */
  195 
  196 static char esctab[]
  197   = " \007\010  \033\014 \
  198       \012 \
  199   \015 \011 \013 \
  200         ";
  201 #endif
  202 
  203 /* PTR points to a string value inside a termcap entry.
  204    Copy that value, processing \ and ^ abbreviations,
  205    into the block that *AREA points to,
  206    or to newly allocated storage if AREA is NULL.
  207    Return the address to which we copied the value,
  208    or NULL if PTR is NULL.  */
  209 
  210 static char *
  211 tgetst1 (ptr, area)
  212      char *ptr;
  213      char **area;
  214 {
  215   register char *p, *r;
  216   register int c;
  217   register int size;
  218   char *ret;
  219   register int c1;
  220 
  221   if (!ptr)
  222     return NULL;
  223 
  224   /* `ret' gets address of where to store the string.  */
  225   if (!area)
  226     {
  227       /* Compute size of block needed (may overestimate).  */
  228       p = ptr;
  229       while ((c = *p++) && c != ':' && c != '\n')
  230     ;
  231       ret = (char *) xmalloc (p - ptr + 1);
  232     }
  233   else
  234     ret = *area;
  235 
  236   /* Copy the string value, stopping at null or colon.
  237      Also process ^ and \ abbreviations.  */
  238   p = ptr;
  239   r = ret;
  240   while ((c = *p++) && c != ':' && c != '\n')
  241     {
  242       if (c == '^')
  243     {
  244       c = *p++;
  245       if (c == '?')
  246         c = 0177;
  247       else
  248         c &= 037;
  249     }
  250       else if (c == '\\')
  251     {
  252       c = *p++;
  253       if (c >= '0' && c <= '7')
  254         {
  255           c -= '0';
  256           size = 0;
  257 
  258           while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7')
  259         {
  260           c *= 8;
  261           c += c1 - '0';
  262           p++;
  263         }
  264         }
  265 #ifdef IS_EBCDIC_HOST
  266       else if (c >= 0200 && c < 0360)
  267         {
  268           c1 = esctab[(c & ~0100) - 0200];
  269           if (c1 != ' ')
  270         c = c1;
  271         }
  272 #else
  273       else if (c >= 0100 && c < 0200)
  274         {
  275           c1 = esctab[(c & ~040) - 0100];
  276           if (c1 != ' ')
  277         c = c1;
  278         }
  279 #endif
  280     }
  281       *r++ = c;
  282     }
  283   *r = '\0';
  284   /* Update *AREA.  */
  285   if (area)
  286     *area = r + 1;
  287   return ret;
  288 }
  289 
  290 /* Outputting a string with padding.  */
  291 
  292 #ifndef emacs
  293 short ospeed;
  294 /* If OSPEED is 0, we use this as the actual baud rate.  */
  295 int tputs_baud_rate;
  296 #endif
  297 char PC;
  298 
  299 #ifndef emacs
  300 /* Actual baud rate if positive;
  301    - baud rate / 100 if negative.  */
  302 
  303 static int speeds[] =
  304   {
  305 #ifdef VMS
  306     0, 50, 75, 110, 134, 150, -3, -6, -12, -18,
  307     -20, -24, -36, -48, -72, -96, -192
  308 #else /* not VMS */
  309     0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
  310     -18, -24, -48, -96, -192, -288, -384, -576, -1152
  311 #endif /* not VMS */
  312   };
  313 
  314 #endif /* not emacs */
  315 
  316 void
  317 tputs (str, nlines, outfun)
  318      register char *str;
  319      int nlines;
  320      register int (*outfun) ();
  321 {
  322   register int padcount = 0;
  323   register int speed;
  324 
  325 #ifdef emacs
  326   extern int baud_rate;
  327   speed = baud_rate;
  328   /* For quite high speeds, convert to the smaller
  329      units to avoid overflow.  */
  330   if (speed > 10000)
  331     speed = - speed / 100;
  332 #else
  333   if (ospeed == 0)
  334     speed = tputs_baud_rate;
  335   else
  336     speed = speeds[ospeed];
  337 #endif
  338   
  339   if (!str)
  340     return;
  341 
  342   while (*str >= '0' && *str <= '9')
  343     {
  344       padcount += *str++ - '0';
  345       padcount *= 10;
  346     }
  347   if (*str == '.')
  348     {
  349       str++;
  350       padcount += *str++ - '0';
  351     }
  352   if (*str == '*')
  353     {
  354       str++;
  355       padcount *= nlines;
  356     }
  357   while (*str)
  358     (*outfun) (*str++);
  359 
  360   /* PADCOUNT is now in units of tenths of msec.
  361      SPEED is measured in characters per 10 seconds
  362      or in characters per .1 seconds (if negative).
  363      We use the smaller units for larger speeds to avoid overflow.  */
  364   padcount *= speed;
  365   padcount += 500;
  366   padcount /= 1000;
  367   if (speed < 0)
  368     padcount = -padcount;
  369   else
  370     {
  371       padcount += 50;
  372       padcount /= 100;
  373     }
  374 
  375   while (padcount-- > 0)
  376     (*outfun) (PC);
  377 }
  378 
  379 /* Finding the termcap entry in the termcap data base.  */
  380 
  381 struct termcap_buffer
  382   {
  383     char *beg;
  384     int size;
  385     char *ptr;
  386     int ateof;
  387     int full;
  388   };
  389 
  390 /* Forward declarations of static functions.  */
  391 
  392 static int scan_file ();
  393 static char *gobble_line ();
  394 static int compare_contin ();
  395 static int name_match ();
  396 
  397 #ifdef VMS
  398 
  399 #include <rmsdef.h>
  400 #include <fab.h>
  401 #include <nam.h>
  402 
  403 static int
  404 valid_filename_p (fn)
  405      char *fn;
  406 {
  407   struct FAB fab = cc$rms_fab;
  408   struct NAM nam = cc$rms_nam;
  409   char esa[NAM$C_MAXRSS];
  410 
  411   fab.fab$l_fna = fn;
  412   fab.fab$b_fns = strlen(fn);
  413   fab.fab$l_nam = &nam;
  414   fab.fab$l_fop = FAB$M_NAM;
  415 
  416   nam.nam$l_esa = esa;
  417   nam.nam$b_ess = sizeof esa;
  418 
  419   return SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL;
  420 }
  421 
  422 #else /* !VMS */
  423 
  424 #ifdef MSDOS /* MW, May 1993 */
  425 static int
  426 valid_filename_p (fn)
  427      char *fn;
  428 {
  429   return *fn == '/' || fn[1] == ':';
  430 }
  431 #else
  432 #define valid_filename_p(fn) (*(fn) == '/')
  433 #endif
  434 
  435 #endif /* !VMS */
  436 
  437 /* Find the termcap entry data for terminal type NAME
  438    and store it in the block that BP points to.
  439    Record its address for future use.
  440 
  441    If BP is null, space is dynamically allocated.
  442 
  443    Return -1 if there is some difficulty accessing the data base
  444    of terminal types,
  445    0 if the data base is accessible but the type NAME is not defined
  446    in it, and some other value otherwise.  */
  447 
  448 int
  449 tgetent (bp, name)
  450      char *bp, *name;
  451 {
  452   register char *termcap_name;
  453   register int fd;
  454   struct termcap_buffer buf;
  455   register char *bp1;
  456   char *tc_search_point;
  457   char *term;
  458   int malloc_size = 0;
  459   register int c;
  460   char *tcenv = NULL;       /* TERMCAP value, if it contains :tc=.  */
  461   char *indirect = NULL;    /* Terminal type in :tc= in TERMCAP value.  */
  462   int filep;
  463 
  464 #ifdef INTERNAL_TERMINAL
  465   /* For the internal terminal we don't want to read any termcap file,
  466      so fake it.  */
  467   if (!strcmp (name, "internal"))
  468     {
  469       term = INTERNAL_TERMINAL;
  470       if (!bp)
  471     {
  472       malloc_size = 1 + strlen (term);
  473       bp = (char *) xmalloc (malloc_size);
  474     }
  475       strcpy (bp, term);
  476       goto ret;
  477     }
  478 #endif /* INTERNAL_TERMINAL */
  479 
  480   /* For compatibility with programs like `less' that want to
  481      put data in the termcap buffer themselves as a fallback.  */
  482   if (bp)
  483     term_entry = bp;
  484 
  485   termcap_name = getenv ("TERMCAP");
  486   if (termcap_name && *termcap_name == '\0')
  487     termcap_name = NULL;
  488 #if defined (MSDOS) && !defined (TEST)
  489   if (termcap_name && (*termcap_name == '\\'
  490                || *termcap_name == '/'
  491                || termcap_name[1] == ':'))
  492     dostounix_filename(termcap_name);
  493 #endif
  494 
  495   filep = termcap_name && valid_filename_p (termcap_name);
  496 
  497   /* If termcap_name is non-null and starts with / (in the un*x case, that is),
  498      it is a file name to use instead of /etc/termcap.
  499      If it is non-null and does not start with /,
  500      it is the entry itself, but only if
  501      the name the caller requested matches the TERM variable.  */
  502 
  503   if (termcap_name && !filep && !strcmp (name, getenv ("TERM")))
  504     {
  505       indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0);
  506       if (!indirect)
  507     {
  508       if (!bp)
  509         bp = termcap_name;
  510       else
  511         strcpy (bp, termcap_name);
  512       goto ret;
  513     }
  514       else
  515     {           /* It has tc=.  Need to read /etc/termcap.  */
  516       tcenv = termcap_name;
  517       termcap_name = NULL;
  518     }
  519     }
  520 
  521   if (!termcap_name || !filep)
  522     termcap_name = TERMCAP_FILE;
  523 
  524   /* Here we know we must search a file and termcap_name has its name.  */
  525 
  526 #ifdef MSDOS
  527   fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
  528 #else
  529   fd = open (termcap_name, O_RDONLY, 0);
  530 #endif
  531   if (fd < 0)
  532     return -1;
  533 
  534   buf.size = BUFSIZE;
  535   /* Add 1 to size to ensure room for terminating null.  */
  536   buf.beg = (char *) xmalloc (buf.size + 1);
  537   term = indirect ? indirect : name;
  538 
  539   if (!bp)
  540     {
  541       malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
  542       bp = (char *) xmalloc (malloc_size);
  543     }
  544   tc_search_point = bp1 = bp;
  545 
  546   if (indirect)
  547     /* Copy the data from the environment variable.  */
  548     {
  549       strcpy (bp, tcenv);
  550       bp1 += strlen (tcenv);
  551     }
  552 
  553   while (term)
  554     {
  555       /* Scan the file, reading it via buf, till find start of main entry.  */
  556       if (scan_file (term, fd, &buf) == 0)
  557     {
  558       close (fd);
  559       free (buf.beg);
  560       if (malloc_size)
  561         free (bp);
  562       return 0;
  563     }
  564 
  565       /* Free old `term' if appropriate.  */
  566       if (term != name)
  567     free (term);
  568 
  569       /* If BP is malloc'd by us, make sure it is big enough.  */
  570       if (malloc_size)
  571     {
  572       int offset1 = bp1 - bp, offset2 = tc_search_point - bp;
  573       malloc_size = offset1 + buf.size;
  574       bp = termcap_name = (char *) xrealloc (bp, malloc_size);
  575       bp1 = termcap_name + offset1;
  576       tc_search_point = termcap_name + offset2;
  577     }
  578 
  579       /* Copy the line of the entry from buf into bp.  */
  580       termcap_name = buf.ptr;
  581       while ((*bp1++ = c = *termcap_name++) && c != '\n')
  582     /* Drop out any \ newline sequence.  */
  583     if (c == '\\' && *termcap_name == '\n')
  584       {
  585         bp1--;
  586         termcap_name++;
  587       }
  588       *bp1 = '\0';
  589 
  590       /* Does this entry refer to another terminal type's entry?
  591      If something is found, copy it into heap and null-terminate it.  */
  592       tc_search_point = find_capability (tc_search_point, "tc");
  593       term = tgetst1 (tc_search_point, (char **) 0);
  594     }
  595 
  596   close (fd);
  597   free (buf.beg);
  598 
  599   if (malloc_size)
  600     bp = (char *) xrealloc (bp, bp1 - bp + 1);
  601 
  602  ret:
  603   term_entry = bp;
  604   return 1;
  605 }
  606 
  607 /* Given file open on FD and buffer BUFP,
  608    scan the file from the beginning until a line is found
  609    that starts the entry for terminal type STR.
  610    Return 1 if successful, with that line in BUFP,
  611    or 0 if no entry is found in the file.  */
  612 
  613 static int
  614 scan_file (str, fd, bufp)
  615      char *str;
  616      int fd;
  617      register struct termcap_buffer *bufp;
  618 {
  619   register char *end;
  620 
  621   bufp->ptr = bufp->beg;
  622   bufp->full = 0;
  623   bufp->ateof = 0;
  624   *bufp->ptr = '\0';
  625 
  626   lseek (fd, 0L, 0);
  627 
  628   while (!bufp->ateof)
  629     {
  630       /* Read a line into the buffer.  */
  631       end = NULL;
  632       do
  633     {
  634       /* if it is continued, append another line to it,
  635          until a non-continued line ends.  */
  636       end = gobble_line (fd, bufp, end);
  637     }
  638       while (!bufp->ateof && end[-2] == '\\');
  639 
  640       if (*bufp->ptr != '#'
  641       && name_match (bufp->ptr, str))
  642     return 1;
  643 
  644       /* Discard the line just processed.  */
  645       bufp->ptr = end;
  646     }
  647   return 0;
  648 }
  649 
  650 /* Return nonzero if NAME is one of the names specified
  651    by termcap entry LINE.  */
  652 
  653 static int
  654 name_match (line, name)
  655      char *line, *name;
  656 {
  657   register char *tem;
  658 
  659   if (!compare_contin (line, name))
  660     return 1;
  661   /* This line starts an entry.  Is it the right one?  */
  662   for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
  663     if (*tem == '|' && !compare_contin (tem + 1, name))
  664       return 1;
  665 
  666   return 0;
  667 }
  668 
  669 static int
  670 compare_contin (str1, str2)
  671      register char *str1, *str2;
  672 {
  673   register int c1, c2;
  674   while (1)
  675     {
  676       c1 = *str1++;
  677       c2 = *str2++;
  678       while (c1 == '\\' && *str1 == '\n')
  679     {
  680       str1++;
  681       while ((c1 = *str1++) == ' ' || c1 == '\t');
  682     }
  683       if (c2 == '\0')
  684     {
  685       /* End of type being looked up.  */
  686       if (c1 == '|' || c1 == ':')
  687         /* If end of name in data base, we win.  */
  688         return 0;
  689       else
  690         return 1;
  691         }
  692       else if (c1 != c2)
  693     return 1;
  694     }
  695 }
  696 
  697 /* Make sure that the buffer <- BUFP contains a full line
  698    of the file open on FD, starting at the place BUFP->ptr
  699    points to.  Can read more of the file, discard stuff before
  700    BUFP->ptr, or make the buffer bigger.
  701 
  702    Return the pointer to after the newline ending the line,
  703    or to the end of the file, if there is no newline to end it.
  704 
  705    Can also merge on continuation lines.  If APPEND_END is
  706    non-null, it points past the newline of a line that is
  707    continued; we add another line onto it and regard the whole
  708    thing as one line.  The caller decides when a line is continued.  */
  709 
  710 static char *
  711 gobble_line (fd, bufp, append_end)
  712      int fd;
  713      register struct termcap_buffer *bufp;
  714      char *append_end;
  715 {
  716   register char *end;
  717   register int nread;
  718   register char *buf = bufp->beg;
  719   register char *tem;
  720 
  721   if (!append_end)
  722     append_end = bufp->ptr;
  723 
  724   while (1)
  725     {
  726       end = append_end;
  727       while (*end && *end != '\n') end++;
  728       if (*end)
  729         break;
  730       if (bufp->ateof)
  731     return buf + bufp->full;
  732       if (bufp->ptr == buf)
  733     {
  734       if (bufp->full == bufp->size)
  735         {
  736           bufp->size *= 2;
  737           /* Add 1 to size to ensure room for terminating null.  */
  738           tem = (char *) xrealloc (buf, bufp->size + 1);
  739           bufp->ptr = (bufp->ptr - buf) + tem;
  740           append_end = (append_end - buf) + tem;
  741           bufp->beg = buf = tem;
  742         }
  743     }
  744       else
  745     {
  746       append_end -= bufp->ptr - buf;
  747       bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
  748       bufp->ptr = buf;
  749     }
  750       if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
  751     bufp->ateof = 1;
  752       bufp->full += nread;
  753       buf[bufp->full] = '\0';
  754     }
  755   return end + 1;
  756 }
  757 
  758 #ifdef TEST
  759 
  760 #ifdef NULL
  761 #undef NULL
  762 #endif
  763 
  764 #include <stdio.h>
  765 
  766 main (argc, argv)
  767      int argc;
  768      char **argv;
  769 {
  770   char *term;
  771   char *buf;
  772 
  773   term = argv[1];
  774   printf ("TERM: %s\n", term);
  775 
  776   buf = (char *) tgetent (0, term);
  777   if ((int) buf <= 0)
  778     {
  779       printf ("No entry.\n");
  780       return 0;
  781     }
  782 
  783   printf ("Entry: %s\n", buf);
  784 
  785   tprint ("cm");
  786   tprint ("AL");
  787 
  788   printf ("co: %d\n", tgetnum ("co"));
  789   printf ("am: %d\n", tgetflag ("am"));
  790 }
  791 
  792 tprint (cap)
  793      char *cap;
  794 {
  795   char *x = tgetstr (cap, 0);
  796   register char *y;
  797 
  798   printf ("%s: ", cap);
  799   if (x)
  800     {
  801       for (y = x; *y; y++)
  802     if (*y <= ' ' || *y == 0177)
  803       printf ("\\%0o", *y);
  804     else
  805       putchar (*y);
  806       free (x);
  807     }
  808   else
  809     printf ("none");
  810   putchar ('\n');
  811 }
  812 
  813 #endif /* TEST */