13e6cd8ff2410e98421ee92b7cdc5e2b2f8ae75d
[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 *, int);
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     int len;
56     const char *home, *rest;
57
58     len = strcspn (&str[1], "/");
59     rest = &str[1+len];
60
61     home = find_home_for_username (&str[1], len);
62
63     if (home) {
64       len = strlen (home) + strlen (rest);
65       str = malloc (len);
66       if (str == NULL) {
67         perror ("malloc");
68         exit (1);
69       }
70       strcpy (str, home);
71       strcat (str, rest);
72       return str;
73     }
74   }
75
76   /* No match, return the orignal string. */
77   return str;
78 }
79
80 /* Return $HOME + append string. */
81 static char *
82 expand_home (const char *append)
83 {
84   const char *home;
85   int len;
86   char *str;
87
88   home = getenv ("HOME");
89   if (!home) home = "~";
90
91   len = strlen (home) + (append ? strlen (append) : 0);
92   str = malloc (len);
93   if (str == NULL) {
94     perror ("malloc");
95     exit (1);
96   }
97
98   strcpy (str, home);
99   if (append)
100     strcat (str, append);
101
102   return str;
103 }
104
105 /* Lookup username (of length ulen), return home directory if found,
106  * or NULL if not found.
107  */
108 static const char *
109 find_home_for_username (const char *username, int ulen)
110 {
111   struct passwd *pw;
112
113   setpwent ();
114   while ((pw = getpwent ()) != NULL) {
115     if (strlen (pw->pw_name) == ulen &&
116         strncmp (username, pw->pw_name, ulen) == 0)
117       return pw->pw_dir;
118   }
119
120   return NULL;
121 }