tilde.c: avoid a warning
[libguestfs.git] / fish / tilde.c
1 /* guestfish - the filesystem interactive shell
2  * Copyright (C) 2009 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <ctype.h>
26 #include <assert.h>
27 #include <pwd.h>
28 #include <sys/types.h>
29
30 #include "fish.h"
31
32 static char *expand_home (const char *);
33 static const char *find_home_for_username (const char *, size_t);
34
35 /* This is called from the script loop if we find a candidate for
36  * ~username (tilde-expansion).
37  */
38 char *
39 try_tilde_expansion (char *str)
40 {
41   assert (str[0] == '~');
42
43   /* Expand current user's home directory.  By simple experimentation
44    * I found out that bash always uses $HOME.
45    */
46   if (str[1] == '\0')           /* ~ */
47     return expand_home (NULL);
48   else if (str[1] == '/')       /* ~/... */
49     return expand_home (&str[1]);
50
51   /* Try expanding the part up to the following '\0' or '/' as a
52    * username from the password file.
53    */
54   else {
55     const char *home, *rest;
56     size_t len = strcspn (&str[1], "/");
57     rest = &str[1+len];
58
59     home = find_home_for_username (&str[1], len);
60
61     if (home) {
62       len = strlen (home) + strlen (rest);
63       str = malloc (len);
64       if (str == NULL) {
65         perror ("malloc");
66         exit (1);
67       }
68       strcpy (str, home);
69       strcat (str, rest);
70       return str;
71     }
72   }
73
74   /* No match, return the orignal string. */
75   return str;
76 }
77
78 /* Return $HOME + append string. */
79 static char *
80 expand_home (const char *append)
81 {
82   const char *home;
83   int len;
84   char *str;
85
86   home = getenv ("HOME");
87   if (!home) home = "~";
88
89   len = strlen (home) + (append ? strlen (append) : 0);
90   str = malloc (len);
91   if (str == NULL) {
92     perror ("malloc");
93     exit (1);
94   }
95
96   strcpy (str, home);
97   if (append)
98     strcat (str, append);
99
100   return str;
101 }
102
103 /* Lookup username (of length ulen), return home directory if found,
104  * or NULL if not found.
105  */
106 static const char *
107 find_home_for_username (const char *username, size_t ulen)
108 {
109   struct passwd *pw;
110
111   setpwent ();
112   while ((pw = getpwent ()) != NULL) {
113     if (strlen (pw->pw_name) == ulen &&
114         strncmp (username, pw->pw_name, ulen) == 0)
115       return pw->pw_dir;
116   }
117
118   return NULL;
119 }