Don't export STREQ and friends in <guestfs.h>
[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 <assert.h>
26 #include <pwd.h>
27 #include <sys/types.h>
28
29 #include "fish.h"
30
31 static char *expand_home (const char *);
32 static const char *find_home_for_username (const char *, size_t);
33
34 /* This is called from the script loop if we find a candidate for
35  * ~username (tilde-expansion).
36  */
37 char *
38 try_tilde_expansion (char *str)
39 {
40   assert (str[0] == '~');
41
42   /* Expand current user's home directory.  By simple experimentation
43    * I found out that bash always uses $HOME.
44    */
45   if (str[1] == '\0')           /* ~ */
46     return expand_home (NULL);
47   else if (str[1] == '/')       /* ~/... */
48     return expand_home (&str[1]);
49
50   /* Try expanding the part up to the following '\0' or '/' as a
51    * username from the password file.
52    */
53   else {
54     const char *home, *rest;
55     size_t len = strcspn (&str[1], "/");
56     rest = &str[1+len];
57
58     home = find_home_for_username (&str[1], len);
59
60     if (home) {
61       len = strlen (home) + strlen (rest);
62       str = malloc (len);
63       if (str == NULL) {
64         perror ("malloc");
65         exit (1);
66       }
67       strcpy (str, home);
68       strcat (str, rest);
69       return str;
70     }
71   }
72
73   /* No match, return the orignal string. */
74   return str;
75 }
76
77 /* Return $HOME + append string. */
78 static char *
79 expand_home (const char *append)
80 {
81   const char *home;
82   int len;
83   char *str;
84
85   home = getenv ("HOME");
86   if (!home) home = "~";
87
88   len = strlen (home) + (append ? strlen (append) : 0);
89   str = malloc (len);
90   if (str == NULL) {
91     perror ("malloc");
92     exit (1);
93   }
94
95   strcpy (str, home);
96   if (append)
97     strcat (str, append);
98
99   return str;
100 }
101
102 /* Lookup username (of length ulen), return home directory if found,
103  * or NULL if not found.
104  */
105 static const char *
106 find_home_for_username (const char *username, size_t ulen)
107 {
108   struct passwd *pw;
109
110   setpwent ();
111   while ((pw = getpwent ()) != NULL) {
112     if (strlen (pw->pw_name) == ulen &&
113         STREQLEN (username, pw->pw_name, ulen))
114       return pw->pw_dir;
115   }
116
117   return NULL;
118 }