others
/* $Id$ */
							/* Copyright (c) 2007-2015 Pierre Pronchery <khorben@defora.org> */
							/* This file is part of DeforaOS Unix others */
							/* This program is free software: you can redistribute it and/or modify
							 * it under the terms of the GNU General Public License as published by
							 * the Free Software Foundation, version 3 of the License.
							 *
							 * This program is distributed in the hope that it will be useful,
							 * but WITHOUT ANY WARRANTY; without even the implied warranty of
							 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
							 * GNU General Public License for more details.
							 *
							 * You should have received a copy of the GNU General Public License
							 * along with this program.  If not, see <http://www.gnu.org/licenses/>. */
							#include <sys/statvfs.h>
							#ifdef ST_WAIT /* NetBSD */
							# include <sys/param.h>
							#endif
							#include <sys/mount.h>
							#include <ctype.h>
							#include <unistd.h>
							#include <stdlib.h>
							#include <stdio.h>
							#include <string.h>
							#ifndef PROGNAME
							# define PROGNAME "umount"
							#endif
							/* umount */
							/* private */
							/* types */
							typedef int Prefs;
							#define PREFS_a 0x1
							#define PREFS_f 0x2
							/* prototypes */
							static int _umount(Prefs * prefs, int pathc, char * pathv[]);
							static int _umount_error(char const * message, int ret);
							static int _umount_usage(void);
							/* functions */
							/* umount */
							static int _umount_all(Prefs * prefs);
							static int _umount_do(Prefs * prefs, char const * pathname);
							static int _umount(Prefs * prefs, int pathc, char * pathv[])
							{
								int ret = 0;
								int i;
								if(*prefs & PREFS_a && pathc == 0)
									return _umount_all(prefs);
								for(i = 0; i < pathc; i++)
									ret |= _umount_do(prefs, pathv[i]);
								return ret;
							}
							#ifdef ST_WAIT
							static int _umount_all(Prefs * prefs)
							{
								int ret = 0;
								int cnt;
								struct statvfs * f;
								int i;
								if((cnt = getvfsstat(NULL, 0, ST_WAIT)) < 0)
									return _umount_error("getvfsstat", 1);
								if((f = malloc(sizeof(*f) * cnt)) == NULL)
									return _umount_error("malloc", 1);
								if((cnt = getvfsstat(f, sizeof(*f) * cnt, ST_WAIT)) < 0)
								{
									free(f);
									return _umount_error("getvfsstat", 1);
								}
								for(i = cnt - 1; i >= 0; i--)
									if(strcmp("/", f[i].f_mntonname) == 0)
										continue;
									else
										ret |= _umount_do(prefs, f[i].f_mntonname);
								free(f);
								return ret;
							#else
							# include <errno.h>
							static int _umount_all(Prefs * prefs)
							{
								int ret = 0;
								char const path[] = "/proc/mounts";
								FILE * fp;
								unsigned char buf[1024];
								size_t i;
								size_t j;
								int c;
								if((fp = fopen(path, "r")) == NULL)
									return -_umount_error(path, 1);
								while(fgets(buf, sizeof(buf), fp) != NULL)
								{
									/* skip device */
									for(i = 0; buf[i] != '\0' && !isspace((c = buf[i])); i++);
									for(; isspace((c = buf[i])); i++);
									/* determine mountpoint */
									for(j = i; buf[j] != '\0' && !isspace((c = buf[j])); j++);
									if(j > i && buf[j] != '\0' && strncmp(&buf[i], "/", j - i) != 0)
									{
										buf[j++] = '\0';
										ret |= _umount_do(prefs, &buf[i]);
									}
									for(; buf[j] != '\0' && buf[j] != '\n'; j++);
									if(buf[j] == '\n')
										continue;
									/* flush longer lines */
									for(c = fgetc(fp); c != EOF && c != '\n'; c = fgetc(fp));
								}
								if(ferror(fp))
									ret |= -_umount_error(path, 1);
								fclose(fp);
								return ret;
							#endif
							}
							static int _umount_do(Prefs * prefs, char const * pathname)
							{
								int flags = 0;
							#ifdef MNT_FORCE
								if(*prefs & PREFS_f)
									flags |= MNT_FORCE;
							#endif
							#ifdef MS_RDONLY /* Linux */
								if(umount(pathname) == 0)
							#else
								if(unmount(pathname, flags) == 0)
							#endif
									return 0;
								return _umount_error(pathname, 1);
							}
							/* umount_error */
							static int _umount_error(char const * message, int ret)
							{
								fputs(PROGNAME ": ", stderr);
								perror(message);
								return ret;
							}
							/* umount_usage */
							static int _umount_usage(void)
							{
								fputs("Usage: " PROGNAME " -a [-f]\n"
							"       " PROGNAME " [-f] special | node ...\n", stderr);
								return 1;
							}
							/* public */
							/* functions */
							/* main */
							int main(int argc, char * argv[])
							{
								Prefs prefs;
								int o;
								prefs = 0;
								while((o = getopt(argc, argv, "af")) != -1)
									switch(o)
									{
										case 'a':
											prefs |= PREFS_a;
											break;
										case 'f':
											prefs |= PREFS_f;
											break;
										default:
											return _umount_usage();
									}
								if(optind == argc && (prefs & PREFS_a) != PREFS_a)
									return _umount_usage();
								return (_umount(&prefs, argc - optind, &argv[optind]) == 0) ? 0 : 2;
							}
							