fdwalk should return 0 on an empty directory
[kopensolaris-gnu/glibc.git] / misc / mntent_r.c
1 /* Utilities for reading/writing fstab, mtab, etc.
2    Copyright (C) 1995-2000, 2001, 2002, 2003, 2006
3    Free Software Foundation, Inc.
4    This file is part of the GNU C Library.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 #include <alloca.h>
22 #include <mntent.h>
23 #include <stdio.h>
24 #include <stdio_ext.h>
25 #include <string.h>
26 #include <sys/types.h>
27
28 #ifdef USE_IN_LIBIO
29 # define flockfile(s) _IO_flockfile (s)
30 # define funlockfile(s) _IO_funlockfile (s)
31 #endif
32
33 #undef __setmntent
34 #undef __endmntent
35 #undef __getmntent_r
36
37 /* Prepare to begin reading and/or writing mount table entries from the
38    beginning of FILE.  MODE is as for `fopen'.  */
39 FILE *
40 __setmntent (const char *file, const char *mode)
41 {
42   /* Extend the mode parameter with "c" to disable cancellation in the
43      I/O functions.  */
44   size_t modelen = strlen (mode);
45   char newmode[modelen + 2];
46   memcpy (mempcpy (newmode, mode, modelen), "c", 2);
47   FILE *result = fopen (file, newmode);
48
49   if (result != NULL)
50     /* We do the locking ourselves.  */
51     __fsetlocking (result, FSETLOCKING_BYCALLER);
52
53   return result;
54 }
55 INTDEF(__setmntent)
56 weak_alias (__setmntent, setmntent)
57
58
59 /* Close a stream opened with `setmntent'.  */
60 int
61 __endmntent (FILE *stream)
62 {
63   if (stream)           /* SunOS 4.x allows for NULL stream */
64     fclose (stream);
65   return 1;             /* SunOS 4.x says to always return 1 */
66 }
67 INTDEF(__endmntent)
68 weak_alias (__endmntent, endmntent)
69
70
71 /* Since the values in a line are separated by spaces, a name cannot
72    contain a space.  Therefore some programs encode spaces in names
73    by the strings "\040".  We undo the encoding when reading an entry.
74    The decoding happens in place.  */
75 static char *
76 decode_name (char *buf)
77 {
78   char *rp = buf;
79   char *wp = buf;
80
81   do
82     if (rp[0] == '\\' && rp[1] == '0' && rp[2] == '4' && rp[3] == '0')
83       {
84         /* \040 is a SPACE.  */
85         *wp++ = ' ';
86         rp += 3;
87       }
88     else if (rp[0] == '\\' && rp[1] == '0' && rp[2] == '1' && rp[3] == '1')
89       {
90         /* \011 is a TAB.  */
91         *wp++ = '\t';
92         rp += 3;
93       }
94     else if (rp[0] == '\\' && rp[1] == '0' && rp[2] == '1' && rp[3] == '2')
95       {
96         /* \012 is a NEWLINE.  */
97         *wp++ = '\n';
98         rp += 3;
99       }
100     else if (rp[0] == '\\' && rp[1] == '\\')
101       {
102         /* We have to escape \\ to be able to represent all characters.  */
103         *wp++ = '\\';
104         rp += 1;
105       }
106     else if (rp[0] == '\\' && rp[1] == '1' && rp[2] == '3' && rp[3] == '4')
107       {
108         /* \134 is also \\.  */
109         *wp++ = '\\';
110         rp += 3;
111       }
112     else
113       *wp++ = *rp;
114   while (*rp++ != '\0');
115
116   return buf;
117 }
118
119
120 /* Read one mount table entry from STREAM.  Returns a pointer to storage
121    reused on the next call, or null for EOF or error (use feof/ferror to
122    check).  */
123 struct mntent *
124 __getmntent_r (FILE *stream, struct mntent *mp, char *buffer, int bufsiz)
125 {
126   char *cp;
127   char *head;
128
129   flockfile (stream);
130   do
131     {
132       char *end_ptr;
133
134       if (fgets_unlocked (buffer, bufsiz, stream) == NULL)
135         {
136           funlockfile (stream);
137           return NULL;
138         }
139
140       end_ptr = strchr (buffer, '\n');
141       if (end_ptr != NULL)      /* chop newline */
142         *end_ptr = '\0';
143       else
144         {
145           /* Not the whole line was read.  Do it now but forget it.  */
146           char tmp[1024];
147           while (fgets_unlocked (tmp, sizeof tmp, stream) != NULL)
148             if (strchr (tmp, '\n') != NULL)
149               break;
150         }
151
152       head = buffer + strspn (buffer, " \t");
153       /* skip empty lines and comment lines:  */
154     }
155   while (head[0] == '\0' || head[0] == '#');
156
157   cp = __strsep (&head, " \t");
158   mp->mnt_fsname = cp != NULL ? decode_name (cp) : (char *) "";
159   if (head)
160     head += strspn (head, " \t");
161   cp = __strsep (&head, " \t");
162   mp->mnt_dir = cp != NULL ? decode_name (cp) : (char *) "";
163   if (head)
164     head += strspn (head, " \t");
165   cp = __strsep (&head, " \t");
166   mp->mnt_type = cp != NULL ? decode_name (cp) : (char *) "";
167   if (head)
168     head += strspn (head, " \t");
169   cp = __strsep (&head, " \t");
170   mp->mnt_opts = cp != NULL ? decode_name (cp) : (char *) "";
171   switch (head ? sscanf (head, " %d %d ", &mp->mnt_freq, &mp->mnt_passno) : 0)
172     {
173     case 0:
174       mp->mnt_freq = 0;
175     case 1:
176       mp->mnt_passno = 0;
177     case 2:
178       break;
179     }
180   funlockfile (stream);
181
182   return mp;
183 }
184 INTDEF(__getmntent_r)
185 weak_alias (__getmntent_r, getmntent_r)
186
187
188 /* We have to use an encoding for names if they contain spaces or tabs.
189    To be able to represent all characters we also have to escape the
190    backslash itself.  This "function" must be a macro since we use
191    `alloca'.  */
192 #define encode_name(name) \
193   do {                                                                        \
194     const char *rp = name;                                                    \
195                                                                               \
196     while (*rp != '\0')                                                       \
197       if (*rp == ' ' || *rp == '\t' || *rp == '\\')                           \
198         break;                                                                \
199       else                                                                    \
200         ++rp;                                                                 \
201                                                                               \
202     if (*rp != '\0')                                                          \
203       {                                                                       \
204         /* In the worst case the length of the string can increase to         \
205            founr times the current length.  */                                \
206         char *wp;                                                             \
207                                                                               \
208         rp = name;                                                            \
209         name = wp = (char *) alloca (strlen (name) * 4 + 1);                  \
210                                                                               \
211         do                                                                    \
212           if (*rp == ' ')                                                     \
213             {                                                                 \
214               *wp++ = '\\';                                                   \
215               *wp++ = '0';                                                    \
216               *wp++ = '4';                                                    \
217               *wp++ = '0';                                                    \
218             }                                                                 \
219           else if (*rp == '\t')                                               \
220             {                                                                 \
221               *wp++ = '\\';                                                   \
222               *wp++ = '0';                                                    \
223               *wp++ = '1';                                                    \
224               *wp++ = '1';                                                    \
225             }                                                                 \
226           else if (*rp == '\n')                                               \
227             {                                                                 \
228               *wp++ = '\\';                                                   \
229               *wp++ = '0';                                                    \
230               *wp++ = '1';                                                    \
231               *wp++ = '2';                                                    \
232             }                                                                 \
233           else if (*rp == '\\')                                               \
234             {                                                                 \
235               *wp++ = '\\';                                                   \
236               *wp++ = '\\';                                                   \
237             }                                                                 \
238           else                                                                \
239             *wp++ = *rp;                                                      \
240         while (*rp++ != '\0');                                                \
241       }                                                                       \
242   } while (0)
243
244
245 /* Write the mount table entry described by MNT to STREAM.
246    Return zero on success, nonzero on failure.  */
247 int
248 __addmntent (FILE *stream, const struct mntent *mnt)
249 {
250   struct mntent mntcopy = *mnt;
251   if (fseek (stream, 0, SEEK_END))
252     return 1;
253
254   /* Encode spaces and tabs in the names.  */
255   encode_name (mntcopy.mnt_fsname);
256   encode_name (mntcopy.mnt_dir);
257   encode_name (mntcopy.mnt_type);
258   encode_name (mntcopy.mnt_opts);
259
260   return (fprintf (stream, "%s %s %s %s %d %d\n",
261                    mntcopy.mnt_fsname,
262                    mntcopy.mnt_dir,
263                    mntcopy.mnt_type,
264                    mntcopy.mnt_opts,
265                    mntcopy.mnt_freq,
266                    mntcopy.mnt_passno)
267           < 0 ? 1 : 0);
268 }
269 weak_alias (__addmntent, addmntent)
270
271
272 /* Search MNT->mnt_opts for an option matching OPT.
273    Returns the address of the substring, or null if none found.  */
274 char *
275 __hasmntopt (const struct mntent *mnt, const char *opt)
276 {
277   const size_t optlen = strlen (opt);
278   char *rest = mnt->mnt_opts, *p;
279
280   while ((p = strstr (rest, opt)) != NULL)
281     {
282       if ((p == rest || p[-1] == ',')
283           && (p[optlen] == '\0' || p[optlen] == '=' || p[optlen] == ','))
284         return p;
285
286       rest = strchr (p, ',');
287       if (rest == NULL)
288         break;
289       ++rest;
290     }
291
292   return NULL;
293 }
294 weak_alias (__hasmntopt, hasmntopt)