xref: /openbsd-src/gnu/lib/libreadline/tilde.c (revision 94bc1d69862d967191a734cf52efb58597d05e23)
11acd27e7Smillert /* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */
21acd27e7Smillert 
31acd27e7Smillert /* Copyright (C) 1988,1989 Free Software Foundation, Inc.
41acd27e7Smillert 
51acd27e7Smillert    This file is part of GNU Readline, a library for reading lines
61acd27e7Smillert    of text with interactive input and history editing.
71acd27e7Smillert 
81acd27e7Smillert    Readline is free software; you can redistribute it and/or modify it
91acd27e7Smillert    under the terms of the GNU General Public License as published by the
101acd27e7Smillert    Free Software Foundation; either version 2, or (at your option) any
111acd27e7Smillert    later version.
121acd27e7Smillert 
131acd27e7Smillert    Readline is distributed in the hope that it will be useful, but
141acd27e7Smillert    WITHOUT ANY WARRANTY; without even the implied warranty of
151acd27e7Smillert    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
161acd27e7Smillert    General Public License for more details.
171acd27e7Smillert 
181acd27e7Smillert    You should have received a copy of the GNU General Public License
191acd27e7Smillert    along with Readline; see the file COPYING.  If not, write to the Free
201acd27e7Smillert    Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
211acd27e7Smillert 
221acd27e7Smillert #if defined (HAVE_CONFIG_H)
231acd27e7Smillert #  include <config.h>
241acd27e7Smillert #endif
251acd27e7Smillert 
261acd27e7Smillert #if defined (HAVE_UNISTD_H)
271acd27e7Smillert #  ifdef _MINIX
281acd27e7Smillert #    include <sys/types.h>
291acd27e7Smillert #  endif
301acd27e7Smillert #  include <unistd.h>
311acd27e7Smillert #endif
321acd27e7Smillert 
331acd27e7Smillert #if defined (HAVE_STRING_H)
341acd27e7Smillert #  include <string.h>
351acd27e7Smillert #else /* !HAVE_STRING_H */
361acd27e7Smillert #  include <strings.h>
371acd27e7Smillert #endif /* !HAVE_STRING_H */
381acd27e7Smillert 
391acd27e7Smillert #if defined (HAVE_STDLIB_H)
401acd27e7Smillert #  include <stdlib.h>
411acd27e7Smillert #else
421acd27e7Smillert #  include "ansi_stdlib.h"
431acd27e7Smillert #endif /* HAVE_STDLIB_H */
441acd27e7Smillert 
451acd27e7Smillert #include <sys/types.h>
461acd27e7Smillert #include <pwd.h>
471acd27e7Smillert 
481acd27e7Smillert #include "tilde.h"
491acd27e7Smillert 
501acd27e7Smillert #if defined (TEST) || defined (STATIC_MALLOC)
511acd27e7Smillert static char *xmalloc (), *xrealloc ();
521acd27e7Smillert #else
531acd27e7Smillert #  if defined __STDC__
541acd27e7Smillert extern char *xmalloc (int);
551acd27e7Smillert extern char *xrealloc (void *, int);
561acd27e7Smillert #  else
571acd27e7Smillert extern char *xmalloc (), *xrealloc ();
581acd27e7Smillert #  endif /* !__STDC__ */
591acd27e7Smillert #endif /* TEST || STATIC_MALLOC */
601acd27e7Smillert 
611acd27e7Smillert #if !defined (HAVE_GETPW_DECLS)
621acd27e7Smillert extern struct passwd *getpwuid (), *getpwnam ();
631acd27e7Smillert #endif /* !HAVE_GETPW_DECLS */
641acd27e7Smillert 
651acd27e7Smillert #if !defined (savestring)
661acd27e7Smillert #  ifndef strcpy
671acd27e7Smillert extern char *strcpy ();
681acd27e7Smillert #  endif
691acd27e7Smillert #define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x))
701acd27e7Smillert #endif /* !savestring */
711acd27e7Smillert 
721acd27e7Smillert #if !defined (NULL)
731acd27e7Smillert #  if defined (__STDC__)
741acd27e7Smillert #    define NULL ((void *) 0)
751acd27e7Smillert #  else
761acd27e7Smillert #    define NULL 0x0
771acd27e7Smillert #  endif /* !__STDC__ */
781acd27e7Smillert #endif /* !NULL */
791acd27e7Smillert 
801acd27e7Smillert /* If being compiled as part of bash, these will be satisfied from
811acd27e7Smillert    variables.o.  If being compiled as part of readline, they will
821acd27e7Smillert    be satisfied from shell.o. */
831acd27e7Smillert extern char *get_home_dir __P((void));
841acd27e7Smillert extern char *get_env_value __P((char *));
851acd27e7Smillert 
861acd27e7Smillert /* The default value of tilde_additional_prefixes.  This is set to
871acd27e7Smillert    whitespace preceding a tilde so that simple programs which do not
881acd27e7Smillert    perform any word separation get desired behaviour. */
891acd27e7Smillert static char *default_prefixes[] =
901acd27e7Smillert   { " ~", "\t~", (char *)NULL };
911acd27e7Smillert 
921acd27e7Smillert /* The default value of tilde_additional_suffixes.  This is set to
931acd27e7Smillert    whitespace or newline so that simple programs which do not
941acd27e7Smillert    perform any word separation get desired behaviour. */
951acd27e7Smillert static char *default_suffixes[] =
961acd27e7Smillert   { " ", "\n", (char *)NULL };
971acd27e7Smillert 
981acd27e7Smillert /* If non-null, this contains the address of a function that the application
991acd27e7Smillert    wants called before trying the standard tilde expansions.  The function
1001acd27e7Smillert    is called with the text sans tilde, and returns a malloc()'ed string
1011acd27e7Smillert    which is the expansion, or a NULL pointer if the expansion fails. */
1021acd27e7Smillert CPFunction *tilde_expansion_preexpansion_hook = (CPFunction *)NULL;
1031acd27e7Smillert 
1041acd27e7Smillert /* If non-null, this contains the address of a function to call if the
1051acd27e7Smillert    standard meaning for expanding a tilde fails.  The function is called
1061acd27e7Smillert    with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
1071acd27e7Smillert    which is the expansion, or a NULL pointer if there is no expansion. */
1081acd27e7Smillert CPFunction *tilde_expansion_failure_hook = (CPFunction *)NULL;
1091acd27e7Smillert 
1101acd27e7Smillert /* When non-null, this is a NULL terminated array of strings which
1111acd27e7Smillert    are duplicates for a tilde prefix.  Bash uses this to expand
1121acd27e7Smillert    `=~' and `:~'. */
1131acd27e7Smillert char **tilde_additional_prefixes = default_prefixes;
1141acd27e7Smillert 
1151acd27e7Smillert /* When non-null, this is a NULL terminated array of strings which match
1161acd27e7Smillert    the end of a username, instead of just "/".  Bash sets this to
1171acd27e7Smillert    `:' and `=~'. */
1181acd27e7Smillert char **tilde_additional_suffixes = default_suffixes;
1191acd27e7Smillert 
1201acd27e7Smillert /* Find the start of a tilde expansion in STRING, and return the index of
1211acd27e7Smillert    the tilde which starts the expansion.  Place the length of the text
1221acd27e7Smillert    which identified this tilde starter in LEN, excluding the tilde itself. */
1231acd27e7Smillert static int
1241acd27e7Smillert tilde_find_prefix (string, len)
1251acd27e7Smillert      char *string;
1261acd27e7Smillert      int *len;
1271acd27e7Smillert {
1281acd27e7Smillert   register int i, j, string_len;
1291acd27e7Smillert   register char **prefixes;
1301acd27e7Smillert 
1311acd27e7Smillert   prefixes = tilde_additional_prefixes;
1321acd27e7Smillert 
1331acd27e7Smillert   string_len = strlen (string);
1341acd27e7Smillert   *len = 0;
1351acd27e7Smillert 
1361acd27e7Smillert   if (*string == '\0' || *string == '~')
1371acd27e7Smillert     return (0);
1381acd27e7Smillert 
1391acd27e7Smillert   if (prefixes)
1401acd27e7Smillert     {
1411acd27e7Smillert       for (i = 0; i < string_len; i++)
1421acd27e7Smillert 	{
1431acd27e7Smillert 	  for (j = 0; prefixes[j]; j++)
1441acd27e7Smillert 	    {
1451acd27e7Smillert 	      if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
1461acd27e7Smillert 		{
1471acd27e7Smillert 		  *len = strlen (prefixes[j]) - 1;
1481acd27e7Smillert 		  return (i + *len);
1491acd27e7Smillert 		}
1501acd27e7Smillert 	    }
1511acd27e7Smillert 	}
1521acd27e7Smillert     }
1531acd27e7Smillert   return (string_len);
1541acd27e7Smillert }
1551acd27e7Smillert 
1561acd27e7Smillert /* Find the end of a tilde expansion in STRING, and return the index of
1571acd27e7Smillert    the character which ends the tilde definition.  */
1581acd27e7Smillert static int
1591acd27e7Smillert tilde_find_suffix (string)
1601acd27e7Smillert      char *string;
1611acd27e7Smillert {
1621acd27e7Smillert   register int i, j, string_len;
1631acd27e7Smillert   register char **suffixes;
1641acd27e7Smillert 
1651acd27e7Smillert   suffixes = tilde_additional_suffixes;
1661acd27e7Smillert   string_len = strlen (string);
1671acd27e7Smillert 
1681acd27e7Smillert   for (i = 0; i < string_len; i++)
1691acd27e7Smillert     {
1701acd27e7Smillert #if defined (__MSDOS__)
1711acd27e7Smillert       if (string[i] == '/' || string[i] == '\\' /* || !string[i] */)
1721acd27e7Smillert #else
1731acd27e7Smillert       if (string[i] == '/' /* || !string[i] */)
1741acd27e7Smillert #endif
1751acd27e7Smillert 	break;
1761acd27e7Smillert 
1771acd27e7Smillert       for (j = 0; suffixes && suffixes[j]; j++)
1781acd27e7Smillert 	{
1791acd27e7Smillert 	  if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
1801acd27e7Smillert 	    return (i);
1811acd27e7Smillert 	}
1821acd27e7Smillert     }
1831acd27e7Smillert   return (i);
1841acd27e7Smillert }
1851acd27e7Smillert 
1861acd27e7Smillert /* Return a new string which is the result of tilde expanding STRING. */
1871acd27e7Smillert char *
1881acd27e7Smillert tilde_expand (string)
1891acd27e7Smillert      char *string;
1901acd27e7Smillert {
1911acd27e7Smillert   char *result;
1921acd27e7Smillert   int result_size, result_index;
1931acd27e7Smillert 
1941acd27e7Smillert   result_index = result_size = 0;
1951acd27e7Smillert   if (result = strchr (string, '~'))
1961acd27e7Smillert     result = xmalloc (result_size = (strlen (string) + 16));
1971acd27e7Smillert   else
1981acd27e7Smillert     result = xmalloc (result_size = (strlen (string) + 1));
1991acd27e7Smillert 
2001acd27e7Smillert   /* Scan through STRING expanding tildes as we come to them. */
2011acd27e7Smillert   while (1)
2021acd27e7Smillert     {
2031acd27e7Smillert       register int start, end;
2041acd27e7Smillert       char *tilde_word, *expansion;
2051acd27e7Smillert       int len;
2061acd27e7Smillert 
2071acd27e7Smillert       /* Make START point to the tilde which starts the expansion. */
2081acd27e7Smillert       start = tilde_find_prefix (string, &len);
2091acd27e7Smillert 
2101acd27e7Smillert       /* Copy the skipped text into the result. */
2111acd27e7Smillert       if ((result_index + start + 1) > result_size)
2121acd27e7Smillert 	result = xrealloc (result, 1 + (result_size += (start + 20)));
2131acd27e7Smillert 
2141acd27e7Smillert       strncpy (result + result_index, string, start);
2151acd27e7Smillert       result_index += start;
2161acd27e7Smillert 
2171acd27e7Smillert       /* Advance STRING to the starting tilde. */
2181acd27e7Smillert       string += start;
2191acd27e7Smillert 
2201acd27e7Smillert       /* Make END be the index of one after the last character of the
2211acd27e7Smillert 	 username. */
2221acd27e7Smillert       end = tilde_find_suffix (string);
2231acd27e7Smillert 
2241acd27e7Smillert       /* If both START and END are zero, we are all done. */
2251acd27e7Smillert       if (!start && !end)
2261acd27e7Smillert 	break;
2271acd27e7Smillert 
2281acd27e7Smillert       /* Expand the entire tilde word, and copy it into RESULT. */
2291acd27e7Smillert       tilde_word = xmalloc (1 + end);
2301acd27e7Smillert       strncpy (tilde_word, string, end);
2311acd27e7Smillert       tilde_word[end] = '\0';
2321acd27e7Smillert       string += end;
2331acd27e7Smillert 
2341acd27e7Smillert       expansion = tilde_expand_word (tilde_word);
2351acd27e7Smillert       free (tilde_word);
2361acd27e7Smillert 
2371acd27e7Smillert       len = strlen (expansion);
2381acd27e7Smillert #ifdef __CYGWIN32__
2391acd27e7Smillert       /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
2401acd27e7Smillert          $HOME for `user' is /.  On cygwin, // denotes a network drive. */
2411acd27e7Smillert       if (len > 1 || *expansion != '/' || *string != '/')
2421acd27e7Smillert #endif
2431acd27e7Smillert 	{
2441acd27e7Smillert 	  if ((result_index + len + 1) > result_size)
2451acd27e7Smillert 	    result = xrealloc (result, 1 + (result_size += (len + 20)));
2461acd27e7Smillert 
2471acd27e7Smillert 	  strcpy (result + result_index, expansion);
2481acd27e7Smillert 	  result_index += len;
2491acd27e7Smillert 	}
2501acd27e7Smillert       free (expansion);
2511acd27e7Smillert     }
2521acd27e7Smillert 
2531acd27e7Smillert   result[result_index] = '\0';
2541acd27e7Smillert 
2551acd27e7Smillert   return (result);
2561acd27e7Smillert }
2571acd27e7Smillert 
2581acd27e7Smillert /* Take FNAME and return the tilde prefix we want expanded.  If LENP is
2591acd27e7Smillert    non-null, the index of the end of the prefix into FNAME is returned in
2601acd27e7Smillert    the location it points to. */
2611acd27e7Smillert static char *
2621acd27e7Smillert isolate_tilde_prefix (fname, lenp)
2631acd27e7Smillert      char *fname;
2641acd27e7Smillert      int *lenp;
2651acd27e7Smillert {
2661acd27e7Smillert   char *ret;
2671acd27e7Smillert   int i;
2681acd27e7Smillert 
2691acd27e7Smillert   ret = xmalloc (strlen (fname));
2701acd27e7Smillert #if defined (__MSDOS__)
2711acd27e7Smillert   for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
2721acd27e7Smillert #else
2731acd27e7Smillert   for (i = 1; fname[i] && fname[i] != '/'; i++)
2741acd27e7Smillert #endif
2751acd27e7Smillert     ret[i - 1] = fname[i];
2761acd27e7Smillert   ret[i - 1] = '\0';
2771acd27e7Smillert   if (lenp)
2781acd27e7Smillert     *lenp = i;
2791acd27e7Smillert   return ret;
2801acd27e7Smillert }
2811acd27e7Smillert 
2821acd27e7Smillert /* Return a string that is PREFIX concatenated with SUFFIX starting at
2831acd27e7Smillert    SUFFIND. */
2841acd27e7Smillert static char *
2851acd27e7Smillert glue_prefix_and_suffix (prefix, suffix, suffind)
2861acd27e7Smillert      char *prefix, *suffix;
2871acd27e7Smillert      int suffind;
2881acd27e7Smillert {
2891acd27e7Smillert   char *ret;
2901acd27e7Smillert   int plen, slen;
2911acd27e7Smillert 
2921acd27e7Smillert   plen = (prefix && *prefix) ? strlen (prefix) : 0;
2931acd27e7Smillert   slen = strlen (suffix + suffind);
2941acd27e7Smillert   ret = xmalloc (plen + slen + 1);
2951acd27e7Smillert   if (plen)
2961acd27e7Smillert     strcpy (ret, prefix);
2971acd27e7Smillert   strcpy (ret + plen, suffix + suffind);
2981acd27e7Smillert   return ret;
2991acd27e7Smillert }
3001acd27e7Smillert 
3011acd27e7Smillert /* Do the work of tilde expansion on FILENAME.  FILENAME starts with a
3021acd27e7Smillert    tilde.  If there is no expansion, call tilde_expansion_failure_hook.
3031acd27e7Smillert    This always returns a newly-allocated string, never static storage. */
3041acd27e7Smillert char *
3051acd27e7Smillert tilde_expand_word (filename)
3061acd27e7Smillert      char *filename;
3071acd27e7Smillert {
3081acd27e7Smillert   char *dirname, *expansion, *username;
3091acd27e7Smillert   int user_len;
3101acd27e7Smillert   struct passwd *user_entry;
3111acd27e7Smillert 
3121acd27e7Smillert   if (filename == 0)
3131acd27e7Smillert     return ((char *)NULL);
3141acd27e7Smillert 
3151acd27e7Smillert   if (*filename != '~')
3161acd27e7Smillert     return (savestring (filename));
3171acd27e7Smillert 
3181acd27e7Smillert   /* A leading `~/' or a bare `~' is *always* translated to the value of
3191acd27e7Smillert      $HOME or the home directory of the current user, regardless of any
3201acd27e7Smillert      preexpansion hook. */
3211acd27e7Smillert   if (filename[1] == '\0' || filename[1] == '/')
3221acd27e7Smillert     {
3231acd27e7Smillert       /* Prefix $HOME to the rest of the string. */
3241acd27e7Smillert       expansion = get_env_value ("HOME");
3251acd27e7Smillert 
3261acd27e7Smillert       /* If there is no HOME variable, look up the directory in
3271acd27e7Smillert 	 the password database. */
328*94bc1d69Smillert       if (expansion == 0 || *expansion == '\0')
3291acd27e7Smillert 	expansion = get_home_dir ();
3301acd27e7Smillert 
3311acd27e7Smillert       return (glue_prefix_and_suffix (expansion, filename, 1));
3321acd27e7Smillert     }
3331acd27e7Smillert 
3341acd27e7Smillert   username = isolate_tilde_prefix (filename, &user_len);
3351acd27e7Smillert 
3361acd27e7Smillert   if (tilde_expansion_preexpansion_hook)
3371acd27e7Smillert     {
3381acd27e7Smillert       expansion = (*tilde_expansion_preexpansion_hook) (username);
3391acd27e7Smillert       if (expansion)
3401acd27e7Smillert 	{
3411acd27e7Smillert 	  dirname = glue_prefix_and_suffix (expansion, filename, user_len);
3421acd27e7Smillert 	  free (username);
3431acd27e7Smillert 	  free (expansion);
3441acd27e7Smillert 	  return (dirname);
3451acd27e7Smillert 	}
3461acd27e7Smillert     }
3471acd27e7Smillert 
3481acd27e7Smillert   /* No preexpansion hook, or the preexpansion hook failed.  Look in the
3491acd27e7Smillert      password database. */
3501acd27e7Smillert   dirname = (char *)NULL;
3511acd27e7Smillert   user_entry = getpwnam (username);
3521acd27e7Smillert   if (user_entry == 0)
3531acd27e7Smillert     {
3541acd27e7Smillert       /* If the calling program has a special syntax for expanding tildes,
3551acd27e7Smillert 	 and we couldn't find a standard expansion, then let them try. */
3561acd27e7Smillert       if (tilde_expansion_failure_hook)
3571acd27e7Smillert 	{
3581acd27e7Smillert 	  expansion = (*tilde_expansion_failure_hook) (username);
3591acd27e7Smillert 	  if (expansion)
3601acd27e7Smillert 	    {
3611acd27e7Smillert 	      dirname = glue_prefix_and_suffix (expansion, filename, user_len);
3621acd27e7Smillert 	      free (expansion);
3631acd27e7Smillert 	    }
3641acd27e7Smillert 	}
3651acd27e7Smillert       free (username);
3661acd27e7Smillert       /* If we don't have a failure hook, or if the failure hook did not
3671acd27e7Smillert 	 expand the tilde, return a copy of what we were passed. */
3681acd27e7Smillert       if (dirname == 0)
3691acd27e7Smillert 	dirname = savestring (filename);
3701acd27e7Smillert     }
3711acd27e7Smillert   else
3721acd27e7Smillert     {
3731acd27e7Smillert       free (username);
3741acd27e7Smillert       dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
3751acd27e7Smillert     }
3761acd27e7Smillert 
3771acd27e7Smillert   endpwent ();
3781acd27e7Smillert   return (dirname);
3791acd27e7Smillert }
3801acd27e7Smillert 
3811acd27e7Smillert 
3821acd27e7Smillert #if defined (TEST)
3831acd27e7Smillert #undef NULL
3841acd27e7Smillert #include <stdio.h>
3851acd27e7Smillert 
3861acd27e7Smillert main (argc, argv)
3871acd27e7Smillert      int argc;
3881acd27e7Smillert      char **argv;
3891acd27e7Smillert {
3901acd27e7Smillert   char *result, line[512];
3911acd27e7Smillert   int done = 0;
3921acd27e7Smillert 
3931acd27e7Smillert   while (!done)
3941acd27e7Smillert     {
3951acd27e7Smillert       printf ("~expand: ");
3961acd27e7Smillert       fflush (stdout);
3971acd27e7Smillert 
3981acd27e7Smillert       if (!gets (line))
3991acd27e7Smillert 	strcpy (line, "done");
4001acd27e7Smillert 
4011acd27e7Smillert       if ((strcmp (line, "done") == 0) ||
4021acd27e7Smillert 	  (strcmp (line, "quit") == 0) ||
4031acd27e7Smillert 	  (strcmp (line, "exit") == 0))
4041acd27e7Smillert 	{
4051acd27e7Smillert 	  done = 1;
4061acd27e7Smillert 	  break;
4071acd27e7Smillert 	}
4081acd27e7Smillert 
4091acd27e7Smillert       result = tilde_expand (line);
4101acd27e7Smillert       printf ("  --> %s\n", result);
4111acd27e7Smillert       free (result);
4121acd27e7Smillert     }
4131acd27e7Smillert   exit (0);
4141acd27e7Smillert }
4151acd27e7Smillert 
4161acd27e7Smillert static void memory_error_and_abort ();
4171acd27e7Smillert 
4181acd27e7Smillert static char *
4191acd27e7Smillert xmalloc (bytes)
4201acd27e7Smillert      int bytes;
4211acd27e7Smillert {
4221acd27e7Smillert   char *temp = (char *)malloc (bytes);
4231acd27e7Smillert 
4241acd27e7Smillert   if (!temp)
4251acd27e7Smillert     memory_error_and_abort ();
4261acd27e7Smillert   return (temp);
4271acd27e7Smillert }
4281acd27e7Smillert 
4291acd27e7Smillert static char *
4301acd27e7Smillert xrealloc (pointer, bytes)
4311acd27e7Smillert      char *pointer;
4321acd27e7Smillert      int bytes;
4331acd27e7Smillert {
4341acd27e7Smillert   char *temp;
4351acd27e7Smillert 
4361acd27e7Smillert   if (!pointer)
4371acd27e7Smillert     temp = (char *)malloc (bytes);
4381acd27e7Smillert   else
4391acd27e7Smillert     temp = (char *)realloc (pointer, bytes);
4401acd27e7Smillert 
4411acd27e7Smillert   if (!temp)
4421acd27e7Smillert     memory_error_and_abort ();
4431acd27e7Smillert 
4441acd27e7Smillert   return (temp);
4451acd27e7Smillert }
4461acd27e7Smillert 
4471acd27e7Smillert static void
4481acd27e7Smillert memory_error_and_abort ()
4491acd27e7Smillert {
4501acd27e7Smillert   fprintf (stderr, "readline: out of virtual memory\n");
4511acd27e7Smillert   abort ();
4521acd27e7Smillert }
4531acd27e7Smillert 
4541acd27e7Smillert /*
4551acd27e7Smillert  * Local variables:
4561acd27e7Smillert  * compile-command: "gcc -g -DTEST -o tilde tilde.c"
4571acd27e7Smillert  * end:
4581acd27e7Smillert  */
4591acd27e7Smillert #endif /* TEST */
460