.
[gnulib.git] / lib / save-cwd.c
1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4
5 #include <stdio.h>
6
7 #ifdef STDC_HEADERS
8 # include <stdlib.h>
9 #endif
10
11 #ifdef HAVE_UNISTD_H
12 # include <unistd.h>
13 #endif
14
15 #ifdef HAVE_FCNTL_H
16 # include <fcntl.h>
17 #else
18 # include <sys/file.h>
19 #endif
20
21 #include <errno.h>
22 # ifndef errno
23 extern int errno;
24 #endif
25
26 #include "save-cwd.h"
27 #include "error.h"
28
29 char *xgetcwd __P((void));
30
31 /* Record the location of the current working directory in CWD so that
32    the program may change to other directories and later use restore_cwd
33    to return to the recorded location.  This function may allocate
34    space using malloc (via xgetcwd) or leave a file descriptor open;
35    use free_cwd to perform the necessary free or close.  Upon failure,
36    no memory is allocated, any locally opened file descriptors are
37    closed;  return non-zero -- in that case, free_cwd need not be
38    called, but doing so is ok.  Otherwise, return zero.  */
39
40 int
41 save_cwd (cwd)
42      struct saved_cwd *cwd;
43 {
44   static int have_working_fchdir = 1;
45
46   cwd->desc = -1;
47   cwd->name = NULL;
48
49   if (have_working_fchdir)
50     {
51 #ifdef HAVE_FCHDIR
52       cwd->desc = open (".", O_RDONLY);
53       if (cwd->desc < 0)
54         {
55           error (0, errno, "cannot open current directory");
56           return 1;
57         }
58
59 # if __sun__ || sun
60       /* On SunOS 4, fchdir returns EINVAL if accounting is enabled,
61          so we have to fall back to chdir.  */
62       if (fchdir (cwd->desc))
63         {
64           if (errno == EINVAL)
65             {
66               close (cwd->desc);
67               cwd->desc = -1;
68               have_working_fchdir = 0;
69             }
70           else
71             {
72               error (0, errno, "current directory");
73               close (cwd->desc);
74               cwd->desc = -1;
75               return 1;
76             }
77         }
78 # endif /* __sun__ || sun */
79 #else
80 #define fchdir(x) (abort (), 0)
81       have_working_fchdir = 0;
82 #endif
83     }
84
85   if (!have_working_fchdir)
86     {
87       cwd->name = xgetcwd ();
88       if (cwd->name == NULL)
89         {
90           error (0, errno, "cannot get current directory");
91           return 1;
92         }
93     }
94   return 0;
95 }
96
97 /* Change to recorded location, CWD, in directory hierarchy.
98    If "saved working directory", NULL))
99    */
100
101 int
102 restore_cwd (cwd, dest, from)
103      const struct saved_cwd *cwd;
104      const char *dest;
105      const char *from;
106 {
107   int fail = 0;
108   if (cwd->desc >= 0)
109     {
110       if (fchdir (cwd->desc))
111         {
112           error (0, errno, "cannot return to %s%s%s",
113                  (dest ? dest : "saved working directory"),
114                  (from ? " from " : ""),
115                  (from ? from : ""));
116           fail = 1;
117         }
118     }
119   else if (chdir (cwd->name) < 0)
120     {
121       error (0, errno, "%s", cwd->name);
122       fail = 1;
123     }
124   return fail;
125 }
126
127 void
128 free_cwd (cwd)
129      struct saved_cwd *cwd;
130 {
131   if (cwd->desc >= 0)
132     close (cwd->desc);
133   if (cwd->name)
134     free (cwd->name);
135 }
136