update comments
[gnulib.git] / lib / mountlist.c
1 /* mountlist.c -- return a list of mounted filesystems
2    Copyright (C) 1991, 1992, 1997, 1998, 1999 Free Software Foundation, 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, or (at your option)
7    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 Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 #if HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include "mountlist.h"
25
26 #ifdef STDC_HEADERS
27 # include <stdlib.h>
28 #else
29 void free ();
30 #endif
31 #if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
32 # include <string.h>
33 #else
34 # include <strings.h>
35 #endif
36
37 #ifndef strstr
38 char *strstr ();
39 #endif
40 char *xmalloc ();
41 char *xrealloc ();
42 char *xstrdup ();
43 void error ();
44
45 #include <errno.h>
46 #ifndef errno
47 extern int errno;
48 #endif
49
50 #ifdef HAVE_FCNTL_H
51 # include <fcntl.h>
52 #endif
53
54 #ifdef HAVE_UNISTD_H
55 # include <unistd.h>
56 #endif
57
58 #if HAVE_SYS_PARAM_H
59 # include <sys/param.h>
60 #endif
61
62 #if defined (MOUNTED_GETFSSTAT) /* __alpha running OSF_1 */
63 # include <sys/mount.h>
64 # include <sys/fs_types.h>
65 #endif /* MOUNTED_GETFSSTAT */
66
67 #ifdef MOUNTED_GETMNTENT1       /* 4.3BSD, SunOS, HP-UX, Dynix, Irix.  */
68 # include <mntent.h>
69 # if !defined(MOUNTED)
70 #  if defined(MNT_MNTTAB)       /* HP-UX.  */
71 #   define MOUNTED MNT_MNTTAB
72 #  endif
73 #  if defined(MNTTABNAME)       /* Dynix.  */
74 #   define MOUNTED MNTTABNAME
75 #  endif
76 # endif
77 #endif
78
79 #ifdef MOUNTED_GETMNTINFO       /* 4.4BSD.  */
80 # include <sys/mount.h>
81 #endif
82
83 #ifdef MOUNTED_GETMNT           /* Ultrix.  */
84 # include <sys/mount.h>
85 # include <sys/fs_types.h>
86 #endif
87
88 #ifdef MOUNTED_FREAD            /* SVR2.  */
89 # include <mnttab.h>
90 #endif
91
92 #ifdef MOUNTED_FREAD_FSTYP      /* SVR3.  */
93 # include <mnttab.h>
94 # include <sys/fstyp.h>
95 # include <sys/statfs.h>
96 #endif
97
98 #ifdef MOUNTED_LISTMNTENT
99 # include <mntent.h>
100 #endif
101
102 #ifdef MOUNTED_GETMNTENT2       /* SVR4.  */
103 # include <sys/mnttab.h>
104 #endif
105
106 #ifdef MOUNTED_VMOUNT           /* AIX.  */
107 # include <fshelp.h>
108 # include <sys/vfs.h>
109 #endif
110
111 #ifdef DOLPHIN
112 /* So special that it's not worth putting this in autoconf.  */
113 # undef MOUNTED_FREAD_FSTYP
114 # define MOUNTED_GETMNTTBL
115 #endif
116
117 #if HAVE_SYS_MNTENT_H
118 /* This is to get MNTOPT_IGNORE on e.g. SVR4.  */
119 # include <sys/mntent.h>
120 #endif
121
122 #if defined (MNTOPT_IGNORE) && defined (HAVE_HASMNTOPT)
123 # define MNT_IGNORE(M) hasmntopt ((M), MNTOPT_IGNORE)
124 #else
125 # define MNT_IGNORE(M) 0
126 #endif
127
128 #ifdef MOUNTED_GETMNTENT1       /* 4.3BSD, SunOS, HP-UX, Dynix, Irix.  */
129 /* Return the value of the hexadecimal number represented by CP.
130    No prefix (like '0x') or suffix (like 'h') is expected to be
131    part of CP. */
132 /* FIXME: this can overflow */
133
134 static int
135 xatoi (char *cp)
136 {
137   int val;
138
139   val = 0;
140   while (*cp)
141     {
142       if (*cp >= 'a' && *cp <= 'f')
143         val = val * 16 + *cp - 'a' + 10;
144       else if (*cp >= 'A' && *cp <= 'F')
145         val = val * 16 + *cp - 'A' + 10;
146       else if (*cp >= '0' && *cp <= '9')
147         val = val * 16 + *cp - '0';
148       else
149         break;
150       cp++;
151     }
152   return val;
153 }
154
155 /* Convert, in place, each unambiguous `\040' sequence in the NUL-terminated
156    string, STR, to a single space.  `unambiguous' means that it must not be
157    immediately preceded by an odd number of backslash characters.  */
158 /* FIXME: should any other backslash-escaped sequences be translated?  */
159 /* FIXME: is the backslash counting necessary?  */
160
161 static void
162 translate_040_to_space (char *str)
163 {
164   while (1)
165     {
166       char *p;
167       char *backslash = strstr (str, "\\040");
168       unsigned int backslash_count = 0;
169
170       if (backslash == NULL)
171         break;
172
173       /* Count preceding backslashes, going no further than str.  */
174       for (p = backslash - 1; p >= str && *p == '\\'; p--)
175         ++backslash_count;
176
177       if (backslash_count % 2 == 1)
178         {
179           /* The backslash is escaped;  advance past the 040 and
180              continue searching.  */
181           str = backslash + 4;
182           continue;
183         }
184
185       /* We found an unambiguous `\040'.  Replace the `/' with a space
186          and shift the string after `040' so that it starts where the
187          first zero was.  The source and destination regions may overlap,
188          so use memmove.  */
189       *backslash = ' ';
190       str = backslash + 1;
191       /* Be sure to copy the trailing NUL byte, too.  */
192       memmove (str, backslash + 4, strlen (backslash + 4) + 1);
193     }
194 }
195
196 #endif /* MOUNTED_GETMNTENT1.  */
197
198 #if MOUNTED_GETMNTINFO
199
200 # if ! HAVE_F_FSTYPENAME_IN_STATFS
201 static char *
202 fstype_to_string (short t)
203 {
204   switch (t)
205     {
206 #  ifdef MOUNT_PC
207     case MOUNT_PC:
208       return "pc";
209 #  endif
210 #  ifdef MOUNT_MFS
211     case MOUNT_MFS:
212       return "mfs";
213 #  endif
214 #  ifdef MOUNT_LO
215     case MOUNT_LO:
216       return "lo";
217 #  endif
218 #  ifdef MOUNT_TFS
219     case MOUNT_TFS:
220       return "tfs";
221 #  endif
222 #  ifdef MOUNT_TMP
223     case MOUNT_TMP:
224       return "tmp";
225 #  endif
226 #  ifdef MOUNT_UFS
227    case MOUNT_UFS:
228      return "ufs" ;
229 #  endif
230 #  ifdef MOUNT_NFS
231    case MOUNT_NFS:
232      return "nfs" ;
233 #  endif
234 #  ifdef MOUNT_MSDOS
235    case MOUNT_MSDOS:
236      return "msdos" ;
237 #  endif
238 #  ifdef MOUNT_LFS
239    case MOUNT_LFS:
240      return "lfs" ;
241 #  endif
242 #  ifdef MOUNT_LOFS
243    case MOUNT_LOFS:
244      return "lofs" ;
245 #  endif
246 #  ifdef MOUNT_FDESC
247    case MOUNT_FDESC:
248      return "fdesc" ;
249 #  endif
250 #  ifdef MOUNT_PORTAL
251    case MOUNT_PORTAL:
252      return "portal" ;
253 #  endif
254 #  ifdef MOUNT_NULL
255    case MOUNT_NULL:
256      return "null" ;
257 #  endif
258 #  ifdef MOUNT_UMAP
259    case MOUNT_UMAP:
260      return "umap" ;
261 #  endif
262 #  ifdef MOUNT_KERNFS
263    case MOUNT_KERNFS:
264      return "kernfs" ;
265 #  endif
266 #  ifdef MOUNT_PROCFS
267    case MOUNT_PROCFS:
268      return "procfs" ;
269 #  endif
270 #  ifdef MOUNT_AFS
271    case MOUNT_AFS:
272      return "afs" ;
273 #  endif
274 #  ifdef MOUNT_CD9660
275    case MOUNT_CD9660:
276      return "cd9660" ;
277 #  endif
278 #  ifdef MOUNT_UNION
279    case MOUNT_UNION:
280      return "union" ;
281 #  endif
282 #  ifdef MOUNT_DEVFS
283    case MOUNT_DEVFS:
284      return "devfs" ;
285 #  endif
286 #  ifdef MOUNT_EXT2FS
287    case MOUNT_EXT2FS:
288      return "ext2fs" ;
289 #  endif
290     default:
291       return "?";
292     }
293 }
294 # endif /* ! HAVE_F_FSTYPENAME_IN_STATFS */
295
296 /* __NetBSD__ || BSD_NET2 || __OpenBSD__ */
297 static char *
298 fsp_to_string (const struct statfs *fsp)
299 {
300 # if defined HAVE_F_FSTYPENAME_IN_STATFS
301   return fsp->f_fstypename;
302 # else
303   return fstype_to_string (fsp->f_type);
304 # endif
305 }
306
307 #endif /* MOUNTED_GETMNTINFO */
308
309 #ifdef MOUNTED_VMOUNT           /* AIX.  */
310 static char *
311 fstype_to_string (int t)
312 {
313   struct vfs_ent *e;
314
315   e = getvfsbytype (t);
316   if (!e || !e->vfsent_name)
317     return "none";
318   else
319     return e->vfsent_name;
320 }
321 #endif /* MOUNTED_VMOUNT */
322
323 /* Return a list of the currently mounted filesystems, or NULL on error.
324    Add each entry to the tail of the list so that they stay in order.
325    If NEED_FS_TYPE is nonzero, ensure that the filesystem type fields in
326    the returned list are valid.  Otherwise, they might not be.  */
327
328 struct mount_entry *
329 read_filesystem_list (int need_fs_type)
330 {
331   struct mount_entry *mount_list;
332   struct mount_entry *me;
333   struct mount_entry **mtail = &mount_list;
334
335 #ifdef MOUNTED_LISTMNTENT
336   {
337     struct tabmntent *mntlist, *p;
338     struct mntent *mnt;
339     struct mount_entry *me;
340
341     /* the third and fourth arguments could be used to filter mounts,
342        but Crays doesn't seem to have any mounts that we want to
343        remove. Specifically, automount create normal NFS mounts.
344        */
345
346     if(listmntent(&mntlist, KMTAB, NULL, NULL) < 0)
347       return NULL;
348     for (p = mntlist; p; p = p->next) {
349       mnt = p->ment;
350       me = (struct mount_entry*) xmalloc(sizeof (struct mount_entry));
351       me->me_devname = xstrdup(mnt->mnt_fsname);
352       me->me_mountdir = xstrdup(mnt->mnt_dir);
353       me->me_type = xstrdup(mnt->mnt_type);
354       me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
355       me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
356       me->me_dev = -1;
357       *mtail = me;
358       mtail = &me->me_next;
359     }
360     freemntlist(mntlist);
361   }
362 #endif
363
364 #ifdef MOUNTED_GETMNTENT1       /* 4.3BSD, SunOS, HP-UX, Dynix, Irix.  */
365   {
366     struct mntent *mnt;
367     char *table = MOUNTED;
368     FILE *fp;
369     char *devopt;
370
371     fp = setmntent (table, "r");
372     if (fp == NULL)
373       return NULL;
374
375     while ((mnt = getmntent (fp)))
376       {
377         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
378         me->me_devname = xstrdup (mnt->mnt_fsname);
379         me->me_mountdir = xstrdup (mnt->mnt_dir);
380         me->me_type = xstrdup (mnt->mnt_type);
381         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
382         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
383         devopt = strstr (mnt->mnt_opts, "dev=");
384         if (devopt)
385           {
386             if (devopt[4] == '0' && (devopt[5] == 'x' || devopt[5] == 'X'))
387               me->me_dev = xatoi (devopt + 6);
388             else
389               me->me_dev = xatoi (devopt + 4);
390           }
391         else
392           me->me_dev = (dev_t) -1;      /* Magic; means not known yet. */
393
394         /* FIXME: do the conversion only if we're using some version of
395            GNU libc -- which one?  */
396         /* Convert each `\040' string to a space.  */
397         translate_040_to_space (me->me_mountdir);
398
399         /* Add to the linked list. */
400         *mtail = me;
401         mtail = &me->me_next;
402       }
403
404     if (endmntent (fp) == 0)
405       goto free_then_fail;
406   }
407 #endif /* MOUNTED_GETMNTENT1. */
408
409 #ifdef MOUNTED_GETMNTINFO       /* 4.4BSD.  */
410   {
411     struct statfs *fsp;
412     int entries;
413
414     entries = getmntinfo (&fsp, MNT_NOWAIT);
415     if (entries < 0)
416       return NULL;
417     for (; entries-- > 0; fsp++)
418       {
419         char *fs_type = fsp_to_string (fsp);
420
421         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
422         me->me_devname = xstrdup (fsp->f_mntfromname);
423         me->me_mountdir = xstrdup (fsp->f_mntonname);
424         me->me_type = fs_type;
425         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
426         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
427         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
428
429         /* Add to the linked list. */
430         *mtail = me;
431         mtail = &me->me_next;
432       }
433   }
434 #endif /* MOUNTED_GETMNTINFO */
435
436 #ifdef MOUNTED_GETMNT           /* Ultrix.  */
437   {
438     int offset = 0;
439     int val;
440     struct fs_data fsd;
441
442     while (errno = 0,
443            0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
444                               (char *) 0)))
445       {
446         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
447         me->me_devname = xstrdup (fsd.fd_req.devname);
448         me->me_mountdir = xstrdup (fsd.fd_req.path);
449         me->me_type = gt_names[fsd.fd_req.fstype];
450         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
451         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
452         me->me_dev = fsd.fd_req.dev;
453
454         /* Add to the linked list. */
455         *mtail = me;
456         mtail = &me->me_next;
457       }
458     if (val < 0)
459       goto free_then_fail;
460   }
461 #endif /* MOUNTED_GETMNT. */
462
463 #if defined (MOUNTED_GETFSSTAT) /* __alpha running OSF_1 */
464   {
465     int numsys, counter, bufsize;
466     struct statfs *stats;
467
468     numsys = getfsstat ((struct statfs *)0, 0L, MNT_WAIT);
469     if (numsys < 0)
470       return (NULL);
471
472     bufsize = (1 + numsys) * sizeof (struct statfs);
473     stats = (struct statfs *)xmalloc (bufsize);
474     numsys = getfsstat (stats, bufsize, MNT_WAIT);
475
476     if (numsys < 0)
477       {
478         free (stats);
479         return (NULL);
480       }
481
482     for (counter = 0; counter < numsys; counter++)
483       {
484         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
485         me->me_devname = xstrdup (stats[counter].f_mntfromname);
486         me->me_mountdir = xstrdup (stats[counter].f_mntonname);
487         me->me_type = mnt_names[stats[counter].f_type];
488         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
489         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
490         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
491
492         /* Add to the linked list. */
493         *mtail = me;
494         mtail = &me->me_next;
495       }
496
497     free (stats);
498   }
499 #endif /* MOUNTED_GETFSSTAT */
500
501 #if defined (MOUNTED_FREAD) || defined (MOUNTED_FREAD_FSTYP) /* SVR[23].  */
502   {
503     struct mnttab mnt;
504     char *table = "/etc/mnttab";
505     FILE *fp;
506
507     fp = fopen (table, "r");
508     if (fp == NULL)
509       return NULL;
510
511     while (fread (&mnt, sizeof mnt, 1, fp) > 0)
512       {
513         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
514 # ifdef GETFSTYP                        /* SVR3.  */
515         me->me_devname = xstrdup (mnt.mt_dev);
516 # else
517         me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
518         strcpy (me->me_devname, "/dev/");
519         strcpy (me->me_devname + 5, mnt.mt_dev);
520 # endif
521         me->me_mountdir = xstrdup (mnt.mt_filsys);
522         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
523         me->me_type = "";
524 # ifdef GETFSTYP                        /* SVR3.  */
525         if (need_fs_type)
526           {
527             struct statfs fsd;
528             char typebuf[FSTYPSZ];
529
530             if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
531                 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
532               me->me_type = xstrdup (typebuf);
533           }
534 # endif
535         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
536         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
537
538         /* Add to the linked list. */
539         *mtail = me;
540         mtail = &me->me_next;
541       }
542
543     if (ferror (fp))
544       {
545         int saved_errno = errno;
546         fclose (fp);
547         errno = saved_errno;
548         goto free_then_fail;
549       }
550
551     if (fclose (fp) == EOF)
552       goto free_then_fail;
553   }
554 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP.  */
555
556 #ifdef MOUNTED_GETMNTTBL        /* DolphinOS goes it's own way */
557   {
558     struct mntent **mnttbl=getmnttbl(),**ent;
559     for (ent=mnttbl;*ent;ent++)
560       {
561         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
562         me->me_devname = xstrdup ( (*ent)->mt_resource);
563         me->me_mountdir = xstrdup( (*ent)->mt_directory);
564         me->me_type =  xstrdup ((*ent)->mt_fstype);
565         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
566         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
567         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
568
569         /* Add to the linked list. */
570         *mtail = me;
571         mtail = &me->me_next;
572       }
573     endmnttbl();
574   }
575 #endif
576
577 #ifdef MOUNTED_GETMNTENT2       /* SVR4.  */
578   {
579     struct mnttab mnt;
580     char *table = MNTTAB;
581     FILE *fp;
582     int ret;
583     int lockfd = -1;
584
585 # if defined F_RDLCK && defined F_SETLKW
586     /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
587        e.g. Solaris 2.6.  If the SVR4 folks ever define a macro
588        for this file name, we should use their macro name instead.
589        (Why not just lock MNTTAB directly?  We don't know.)  */
590 #  ifndef MNTTAB_LOCK
591 #   define MNTTAB_LOCK "/etc/.mnttab.lock"
592 #  endif
593     lockfd = open (MNTTAB_LOCK, O_RDONLY);
594     if (0 <= lockfd)
595       {
596         struct flock flock;
597         flock.l_type = F_RDLCK;
598         flock.l_whence = SEEK_SET;
599         flock.l_start = 0;
600         flock.l_len = 0;
601         while (fcntl (lockfd, F_SETLKW, &flock) == -1)
602           if (errno != EINTR)
603             {
604               int saved_errno = errno;
605               close (lockfd);
606               errno = saved_errno;
607               return NULL;
608             }
609       }
610     else if (errno != ENOENT)
611       return NULL;
612 # endif
613
614     errno = 0;
615     fp = fopen (table, "r");
616     if (fp == NULL)
617       ret = errno;
618     else
619       {
620         while ((ret = getmntent (fp, &mnt)) == 0)
621           {
622             me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
623             me->me_devname = xstrdup (mnt.mnt_special);
624             me->me_mountdir = xstrdup (mnt.mnt_mountp);
625             me->me_type = xstrdup (mnt.mnt_fstype);
626             me->me_dummy = MNT_IGNORE (&mnt) != 0;
627             me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
628             me->me_dev = (dev_t) -1;    /* Magic; means not known yet. */
629
630             /* Add to the linked list. */
631             *mtail = me;
632             mtail = &me->me_next;
633           }
634
635         ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
636       }
637
638     if (0 <= lockfd && close (lockfd) != 0)
639       ret = errno;
640
641     if (0 <= ret)
642       {
643         errno = ret;
644         goto free_then_fail;
645       }
646   }
647 #endif /* MOUNTED_GETMNTENT2.  */
648
649 #ifdef MOUNTED_VMOUNT           /* AIX.  */
650   {
651     int bufsize;
652     char *entries, *thisent;
653     struct vmount *vmp;
654
655     /* Ask how many bytes to allocate for the mounted filesystem info.  */
656     mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize);
657     entries = xmalloc (bufsize);
658
659     /* Get the list of mounted filesystems.  */
660     mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
661
662     for (thisent = entries; thisent < entries + bufsize;
663          thisent += vmp->vmt_length)
664       {
665         vmp = (struct vmount *) thisent;
666         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
667         if (vmp->vmt_flags & MNT_REMOTE)
668           {
669             char *host, *path;
670
671             me->me_remote = 1;
672             /* Prepend the remote pathname.  */
673             host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
674             path = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
675             me->me_devname = xmalloc (strlen (host) + strlen (path) + 2);
676             strcpy (me->me_devname, host);
677             strcat (me->me_devname, ":");
678             strcat (me->me_devname, path);
679           }
680         else
681           {
682             me->me_remote = 0;
683             me->me_devname = xstrdup (thisent +
684                                       vmp->vmt_data[VMT_OBJECT].vmt_off);
685           }
686         me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
687         me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
688         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
689         me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want.  */
690
691         /* Add to the linked list. */
692         *mtail = me;
693         mtail = &me->me_next;
694       }
695     free (entries);
696   }
697 #endif /* MOUNTED_VMOUNT. */
698
699   *mtail = NULL;
700   return mount_list;
701
702
703  free_then_fail:
704   {
705     int saved_errno = errno;
706     *mtail = NULL;
707
708     while (mount_list)
709       {
710         me = mount_list->me_next;
711         free (mount_list->me_devname);
712         free (mount_list->me_mountdir);
713         /* FIXME: me_type is not always malloced.  */
714         free (mount_list);
715         mount_list = me;
716       }
717
718     errno = saved_errno;
719     return NULL;
720   }
721 }
722
723 #ifdef TEST
724 int
725 main (int argc, char **argv)
726 {
727   int i;
728   for (i = 1; i < argc; i++)
729     {
730       char *p = xstrdup (argv[i]);
731       translate_040_to_space (p);
732       printf ("%s: %s\n", argv[i], p);
733     }
734   exit (0);
735 }
736 #endif