OSDN Git Service

initial commit
authorChih-Wei Huang <cwhuang@linux.org.tw>
Tue, 9 Mar 2010 08:54:42 +0000 (16:54 +0800)
committerChih-Wei Huang <cwhuang@linux.org.tw>
Tue, 9 Mar 2010 08:54:42 +0000 (16:54 +0800)
Source from Fedora 12: eject-2.1.5-15.fc12.src.rpm

AUTHORS [new file with mode: 0644]
Android.mk [new file with mode: 0644]
MODULE_LICENSE_GPL [new file with mode: 0644]
README [new file with mode: 0644]
eject.c [new file with mode: 0644]
i18n.h [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..42c406c
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Jeff Tranter <tranter@pobox.com>
diff --git a/Android.mk b/Android.mk
new file mode 100644 (file)
index 0000000..62cfb16
--- /dev/null
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := eject.c
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_CFLAGS := -DVERSION=\"2.1.5\" -DDEFAULTDEVICE=\"block/sr0\"
+
+LOCAL_MODULE := eject
+
+include $(BUILD_EXECUTABLE)
diff --git a/MODULE_LICENSE_GPL b/MODULE_LICENSE_GPL
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..3772258
--- /dev/null
+++ b/README
@@ -0,0 +1,64 @@
+Eject allows removable media (typically a CD-ROM, floppy disk, tape
+drive, or JAZ or ZIP disk) to be ejected under software control. The
+command can also control some multi-disc CD-ROM changers, the
+auto-eject feature supported by some devices, and close the disc tray
+of some CD-ROM drives.
+
+See the file INSTALL for instructions on how to compile and install
+the program.
+
+The following devices and configurations are known to work with eject:
+
+Devices:
+- 1GB and 2GB SCSI IOMEGA JAZ drives
+- Apple PowerMac floppy drive
+- HP1534 SCSI DAT tape drive
+- IDE/ATAPI CD-ROM
+- IDE/ATAPI IOMEGA 100MB ZIP drive
+- IDE/ATAPI LS120 120MB floppy drive
+- Parallel port IOMEGA 100MB ZIP drive
+- SCSI CD-R drive
+- SCSI CD-ROM
+- SCSI MO Drive
+- Sun SPARC floppy drive
+- Fujitsu M2512A magneto-optical disk drive (SCSI)
+
+Kernels:
+- 2.0.36 (and earlier 2.0.x)
+- 2.1.132 (and earlier 2.1.x)
+- 2.2.16 (and earlier 2.2.x)
+- 2.4.4 (and earlier 2.4.x)
+- 2.6.11 (and earlier 2.6.x)
+- with both the IDE, SCSI, and IDE SCSI emulation kernel drivers
+
+Platforms:
+- Intel (Red Hat)
+- SPARC
+- PowerPC
+- DEC Alpha
+
+It is loosely based on the SunOS eject command.
+
+If you have any problems or suggestions, drop me a line. I'm
+particularly interested in hearing if it does or doesn't work with
+media other than CD-ROM or when running Linux on non-Intel machines.
+
+Version 2.0 is mostly a rewrite of version 1.5 to clean up the source
+and add a number of new features (and take out some useless ones).
+Many thanks to the beta testers for version 2.0:
+
+Andrea Borgia  Darius Davis    David Wheeler   Jochen Stein
+Marko Makela   Mike Warnecke   Steve Linn      Tobias Richter
+Richard Simpson
+
+You may also find some of the following commands useful for managing
+removable media:
+
+gtkzip
+jazip
+mt
+mzip
+ziptool
+
+Jeff Tranter
+tranter@pobox.com
diff --git a/eject.c b/eject.c
new file mode 100644 (file)
index 0000000..300e3df
--- /dev/null
+++ b/eject.c
@@ -0,0 +1,1592 @@
+/********************************************************************
+ *
+ *             L I N U X   E J E C T   C O M M A N D
+ *
+ *               by Jeff Tranter (tranter@pobox.com)
+ *
+ ********************************************************************
+ *
+ * Copyright (C) 1994-2001 Jeff Tranter (tranter@pobox.com)
+ * Copyright (C) 2004, 2005 Frank Lichtenheld (djpig@debian.org)
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ ********************************************************************
+ *
+ * See the man page for a description of what this program does and what
+ * the requirements to run it are.
+ *
+ */
+
+#include "i18n.h"
+
+#ifndef DEFAULTDEVICE
+#error DEFAULTDEVICE not set, check Makefile
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <dirent.h>
+
+#ifdef GETOPTLONG
+#include <getopt.h>
+#endif /* GETOPTLONG */
+#include <errno.h>
+#include <regex.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+//#include <sys/mtio.h>
+#include <sys/mount.h>
+
+#if defined(__linux__)
+#include <linux/version.h>
+/* handy macro found in 2.1 kernels, but not in older ones */
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+#include <linux/mtio.h>
+#include <linux/types.h>
+#include <linux/cdrom.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+#include <linux/ucdrom.h>
+#endif
+#include <linux/fd.h>
+#include <scsi/scsi.h>
+#include <scsi/sg.h>
+#include <scsi/scsi_ioctl.h>
+#include <sys/time.h>
+
+/* Used by the ToggleTray() function. If ejecting the tray takes this
+ * time or less, the tray was probably already ejected, so we close it
+ * again.
+ */
+#define TRAY_WAS_ALREADY_OPEN_USECS  200000    /* about 0.2 seconds */
+
+
+#define CLOSE(fd) if (close(fd)==-1) { \
+    perror(programName); \
+    exit(1); \
+}
+
+#define FCLOSE(fd) if (fclose(fd)==-1) { \
+    perror(programName); \
+    exit(1); \
+}
+
+#define HAVE_EJECT_SCSI
+#define HAVE_EJECT_FLOPPY
+#define HAVE_EJECT_TAPE
+
+#elif defined(__FreeBSD_kernel__)
+#include <sys/cdio.h>
+#endif /* if defined(__linux__) */
+
+
+#define CLOSE(fd) if (close(fd)==-1) { \
+    perror(programName); \
+    exit(1); \
+}
+
+#define FCLOSE(fd) if (fclose(fd)==-1) { \
+    perror(programName); \
+    exit(1); \
+}
+
+/* Global Variables */
+static const char *version = VERSION; /* program version */
+int a_option = 0; /* command flags and arguments */
+int c_option = 0;
+int d_option = 0;
+int f_option = 0;
+int h_option = 0;
+int n_option = 0;
+int i_option = 0;
+int q_option = 0;
+int r_option = 0;
+int s_option = 0;
+int t_option = 0;
+int T_option = 0;
+int X_option = 0;
+int v_option = 0;
+int x_option = 0;
+#ifdef ANDROID
+int p_option = 1;
+#else
+int p_option = 0;
+#endif
+int m_option = 0;
+int a_arg = 0;
+int c_arg = 0;
+int x_arg = 0;
+int i_arg = 0;
+static char *programName; /* used in error messages */
+
+/*
+ * These are the basenames of devices which can have multiple
+ * partitions per device.
+ */
+static const char *partitionDevice[] = {
+       "hd",
+       "sd",
+       "xd",
+       "dos_hd",
+       "mfm",
+       "ad",
+       "ed",
+       "ftl",
+       "pd",
+       0};
+
+
+/* Display command usage on standard error and exit. */
+static void usage()
+{
+//    perror(_("%s: device is `%s'\n"));
+       fprintf(stderr,_(
+"Eject version %s by Jeff Tranter (tranter@pobox.com)\n"
+"Usage:\n"
+"  eject -h                            -- display command usage and exit\n"
+"  eject -V                            -- display program version and exit\n"
+"  eject [-vnrsfqpm] [<name>]          -- eject device\n"
+"  eject [-vn] -d                      -- display default device\n"
+"  eject [-vn] -a on|off|1|0 [<name>]  -- turn auto-eject feature on or off\n"
+"  eject [-vn] -c <slot> [<name>]      -- switch discs on a CD-ROM changer\n"
+"  eject [-vn] -t [<name>]             -- close tray\n"
+"  eject [-vn] -T [<name>]             -- toggle tray\n"
+"  eject [-vn] -i on|off|1|0 [<name>]  -- toggle manual eject protection on/off\n"
+"  eject [-vn] -x <speed> [<name>]     -- set CD-ROM max speed\n"
+"  eject [-vn] -X [<name>]             -- list CD-ROM available speeds\n"
+"Options:\n"
+"  -v\t-- enable verbose output\n"
+"  -n\t-- don't eject, just show device found\n"
+"  -r\t-- eject CD-ROM\n"
+#ifdef HAVE_EJECT_SCSI
+"  -s\t-- eject SCSI device\n"
+#endif
+#ifdef HAVE_EJECT_FLOPPY
+"  -f\t-- eject floppy\n"
+#endif
+#ifdef HAVE_EJECT_TAPE
+"  -q\t-- eject tape\n"
+#endif
+"  -p\t-- use /proc/mounts instead of /etc/mtab\n"
+"  -m\t-- do not unmount device even if it is mounted\n"
+)
+, version);
+#ifdef GETOPTLONG
+       fprintf(stderr,_(
+"Long options:\n"
+"  -h --help   -v --verbose      -d --default\n"
+"  -a --auto   -c --changerslot  -t --trayclose  -x --cdspeed\n"
+"  -r --cdrom"
+#ifdef HAVE_EJECT_SCSI
+"  -s --scsi"
+#endif
+#ifdef HAVE_EJECT_FLOPPY
+"         -f --floppy"
+#endif
+"     -X --listspeed"
+#ifdef HAVE_EJECT_TAPE
+"     -q --tape"
+#endif
+"\n"
+"  -n --noop   -V --version\n"
+"  -p --proc   -m --no-unmount   -T --traytoggle -i --manualeject\n"));
+#endif /* GETOPTLONG */
+       fprintf(stderr,_(
+"Parameter <name> can be a device file or a mount point.\n"
+"If omitted, name defaults to `%s'.\n"
+"By default tries -r, -s, -f, and -q in order until success.\n"),
+                       DEFAULTDEVICE);
+  exit(1);
+}
+
+
+/* Handle command line options. */
+static void parse_args(int argc, char **argv, char **device)
+{
+       const char *flags = "a:c:x:i:dfhnqrstTXvVpm";
+#ifdef GETOPTLONG
+       static struct option long_options[] =
+       {
+               {"help",        no_argument,       NULL, 'h'},
+               {"verbose",     no_argument,       NULL, 'v'},
+               {"default",     no_argument,       NULL, 'd'},
+               {"auto",        required_argument, NULL, 'a'},
+               {"changerslot", required_argument, NULL, 'c'},
+               {"manualeject", required_argument, NULL, 'i'},
+               {"trayclose",   no_argument,       NULL, 't'},
+               {"traytoggle",  no_argument,       NULL, 'T'},
+               {"cdspeed",     required_argument, NULL, 'x'},
+               {"listspeed",   no_argument,       NULL, 'X'},
+               {"noop",        no_argument,       NULL, 'n'},
+               {"cdrom",       no_argument,       NULL, 'r'},
+               {"scsi",        no_argument,       NULL, 's'},
+               {"floppy",      no_argument,       NULL, 'f'},
+               {"tape",        no_argument,       NULL, 'q'},
+               {"version",     no_argument,       NULL, 'V'},
+               {"proc",        no_argument,       NULL, 'p'},
+               {"no-unmount",  no_argument,       NULL, 'm'},
+               {0, 0, 0, 0}
+       };
+       int option_index;
+#endif /* GETOPTLONG */
+       int c;
+
+#ifdef GETOPTLONG
+       while ((c = getopt_long(argc, argv, flags, long_options, &option_index)) != EOF) {
+#else
+       while ((c = getopt(argc, argv, flags)) != EOF) {
+#endif /* GETOPTLONG */
+               switch (c) {
+                 case 'a':
+                         a_option = 1;
+                         if (!strcmp(optarg, "0"))
+                                 a_arg = 0;
+                         else if (!strcmp(optarg, "off"))
+                                 a_arg = 0;
+                         else if (!strcmp(optarg, "1"))
+                                 a_arg = 1;
+                         else if (!strcmp(optarg, "on"))
+                                 a_arg = 1;
+                         else {
+                                 fprintf(stderr, _("%s: invalid argument to --auto/-a option\n"), programName);
+                                 exit(1);
+                         }
+                         break;
+                 case 'c':
+                         c_option = 1;
+                         /* atoi() returns 0 on error, so "0" must be parsed separately */
+                         if (!strcmp(optarg, "0"))
+                                 c_arg = 0;
+                         else {
+                                 c_arg = atoi(optarg);
+                                 if (c_arg <= 0) {
+                                         fprintf(stderr, _("%s: invalid argument to --changerslot/-c option\n"), programName);
+                                         exit(1);
+                                 }
+                         }
+                         break;
+                 case 'x':
+                         x_option = 1;
+                         if (!strcmp(optarg, "0"))
+                                 x_arg = 0;
+                         else {
+                                 x_arg = atoi(optarg);
+                                 if (x_arg <= 0) {
+                                         fprintf(stderr, _("%s: invalid argument to --cdspeed/-x option\n"), programName);
+                                         exit(1);
+                                 }
+                         }
+                         break;
+                 case 'd':
+                         d_option = 1;
+                         break;
+                 case 'f':
+                         f_option = 1;
+                         break;
+                 case 'h':
+                         usage();
+                         exit(0);
+                         break;
+                 case 'i':
+                         i_option = 1;
+                         if (!strcmp(optarg, "0"))
+                                 i_arg = 0;
+                         else if (!strcmp(optarg, "off"))
+                                 i_arg = 0;
+                         else if (!strcmp(optarg, "1"))
+                                 i_arg = 1;
+                         else if (!strcmp(optarg, "on"))
+                                 i_arg = 1;
+                         else {
+                                 fprintf(stderr, _("%s: invalid argument to -i option\n"), programName);
+                                 exit(1);
+                         }
+                         break;
+                 case 'm':
+                         m_option = 1;
+                         break;
+                 case 'n':
+                         n_option = 1;
+                         break;
+                 case 'p':
+                         p_option = 1;
+                         break;
+                 case 'q':
+                         q_option = 1;
+                         break;
+                 case 'r':
+                         r_option = 1;
+                         break;
+                 case 's':
+                         s_option = 1;
+                         break;
+                 case 't':
+                         t_option = 1;
+                         break;
+                 case 'X':
+                         X_option = 1;
+                         break;
+                 case 'T':
+                         T_option = 1;
+                         break;
+                 case 'v':
+                         v_option = 1;
+                         break;
+                 case 'V':
+                         printf(_("eject version %s by Jeff Tranter (tranter@pobox.com)\n"), version);
+                         exit(0);
+                         break;
+                 case '?':
+                         exit(1);
+                         break;
+               }
+       }
+       /* check for a single additional argument */
+       if ((argc - optind) > 1) {
+               fprintf(stderr, _("%s: too many arguments\n"), programName);
+               exit(1);
+       }
+       if ((argc - optind) == 1) { /* one argument */
+               *device = strdup(argv[optind]);
+       }
+}
+
+
+/* Return 1 if file/device exists, 0 otherwise. */
+static int FileExists(const char *name, const int try, int *found)
+{
+
+        if (!found) return -1;
+        /*
+        * access() uses the UID, not the EUID. This way a normal user
+        * cannot find out if a file (say, /root/fubar) exists or not, even
+        * if eject is SUID root
+        */
+       if (access (name, F_OK) == 0) {
+         (*found)++;
+         if (try <= (*found))
+           return 1;
+         else
+           return 0;
+       } else {
+         return 0;
+       }
+}
+
+
+/*
+ * Linux mangles spaces in mount points by changing them to an octal string
+ * of '\040'.  So lets scan the mount point and fix it up by replacing all
+ * occurrences off '\0##' with the ASCII value of 0##.  Requires a writable
+ * string as input as we mangle in place.  Some of this was taken from the
+ * util-linux package.
+ */
+#define octalify(a) ((a) & 7)
+#define tooctal(s) (64*octalify(s[1]) + 8*octalify(s[2]) + octalify(s[3]))
+#define isoctal(a) (((a) & ~7) == '0')
+static char *DeMangleMount(char *s)
+{
+       char *tmp = s;
+       while ((tmp = strchr(tmp, '\\')) != NULL) {
+               if (isoctal(tmp[1]) && isoctal(tmp[2]) && isoctal(tmp[3])) {
+                       tmp[0] = tooctal(tmp);
+                       memmove(tmp+1, tmp+4, strlen(tmp)-3);
+               }
+               ++tmp;
+       }
+       return s;
+}
+
+
+/*
+ * Given name, such as foo, see if any of the following exist:
+ *
+ * foo (if foo starts with '.' or '/')
+ * /dev/foo
+ * /media/foo
+ * /mnt/foo
+ * /dev/cdroms/foo
+ * /dev/cdroms/foo0
+ * /dev/dsk/foo
+ * /dev/rdsk/foo
+ * ./foo
+ *
+ * If found, return the full path. If not found, return 0.
+ * Returns pointer to dynamically allocated string.
+ */
+static char *FindDevice(const char *name)
+{
+       char *buf;
+       static int try = 0;
+       int found = 0;
+
+       buf = (char *) malloc(strlen(name)+14); /* to allow for "/dev/cdroms/ + "0" + null */
+       if (buf==NULL) {
+               fprintf(stderr, _("%s: could not allocate memory\n"), programName);
+               exit(1);
+       }
+
+       if (try == INT_MAX) {
+         fprintf(stderr, _("%s: FindDevice called too often\n"), programName );;
+         exit(1);
+       } else
+         try++;
+
+       if ((name[0] == '.') || (name[0] == '/')) {
+               strcpy(buf, name);
+               if (FileExists(buf, try, &found))
+                       return buf;
+       }
+
+       strcpy(buf, "/dev/");
+       strcat(buf, name);
+       if (FileExists(buf, try, &found))
+               return buf;
+
+       strcpy(buf, "/media/");
+       strcat(buf, name);
+       if (FileExists(buf, try, &found))
+               return buf;
+
+       strcpy(buf, "/mnt/");
+       strcat(buf, name);
+       if (FileExists(buf, try, &found))
+               return buf;
+
+       /* for devfs under Linux */
+       strcpy(buf, "/dev/cdroms/");
+       strcat(buf, name);
+       if (FileExists(buf, try, &found))
+               return buf;
+
+       strcpy(buf, "/dev/cdroms/");
+       strcat(buf, name);
+       strcat(buf, "0");
+       if (FileExists(buf, try, &found))
+               return buf;
+
+       /* for devfs under Solaris */
+       strcpy(buf, "/dev/rdsk/");
+       strcat(buf, name);
+       if (FileExists(buf, try, &found))
+               return buf;
+
+       strcpy(buf, "/dev/dsk/");
+       strcat(buf, name);
+       if (FileExists(buf, try, &found))
+               return buf;
+
+       strcpy(buf, "./");
+       strcat(buf, name);
+       if (FileExists(buf, try, &found))
+               return buf;
+
+       free(buf);
+       buf = 0;
+       return 0;
+}
+
+
+/*
+ * Stops CDROM from opening on manual eject pressing the button.
+ * This can be useful when you carry your laptop
+ * in your bag while it's on and no CD inserted in it's drive.
+ * Implemented as found in Documentation/ioctl/cdrom.txt
+ *
+ * TODO: Maybe we should check this also:
+ * EDRIVE_CANT_DO_THIS   Door lock function not supported.
+ * EBUSY                 Attempt to unlock when multiple users
+ *                       have the drive open and not CAP_SYS_ADMIN
+ */
+static void ManualEject(int fd, int onOff)
+{
+       if (ioctl(fd, CDROM_LOCKDOOR, onOff) < 0) {
+               perror("ioctl on CDROM_LOCKDOOR");
+        } else {
+            if (onOff)
+               printf("CD-Drive may NOT be ejected with device button\n");
+            else
+                printf("CD-Drive may be ejected with device button\n");
+        }
+}
+
+
+/* Set or clear auto-eject mode. */
+static void AutoEject(int fd, int onOff)
+{
+       int status = -1;
+
+#if defined(CDROM_SET_OPTIONS) && defined(CDROM_CLEAR_OPTIONS)
+       if (onOff)
+               status = ioctl(fd, CDROM_SET_OPTIONS, CDO_AUTO_EJECT);
+       else
+               status = ioctl(fd, CDROM_CLEAR_OPTIONS, CDO_AUTO_EJECT);
+#else
+       errno = ENOSYS;
+#endif
+       if (status < 0) {
+               fprintf(stderr, _("%s: CD-ROM auto-eject command failed: %s\n"), programName, strerror(errno));
+               exit(1);
+       }
+}
+
+
+/*
+ * Changer select. CDROM_SELECT_DISC is preferred, older kernels used
+ * CDROMLOADFROMSLOT.
+ */
+static void ChangerSelect(int fd, int slot)
+{
+       int status;
+
+#ifdef CDROM_SELECT_DISC
+       status = ioctl(fd, CDROM_SELECT_DISC, slot);
+       if (status < 0) {
+               fprintf(stderr, _("%s: CD-ROM select disc command failed: %s\n"), programName, strerror(errno));
+               exit(1);
+       }
+#elif defined CDROMLOADFROMSLOT
+       status = ioctl(fd, CDROMLOADFROMSLOT, slot);
+       if (status != 0) {
+               fprintf(stderr, _("%s: CD-ROM load from slot command failed: %s\n"), programName, strerror(errno));
+               exit(1);
+       }
+#else
+    fprintf(stderr, _("%s: IDE/ATAPI CD-ROM changer not supported by this kernel\n"), programName);
+#endif
+}
+
+
+/*
+ * Close tray. Not supported by older kernels.
+ */
+static void CloseTray(int fd)
+{
+       int status;
+
+#if defined(CDROMCLOSETRAY) || defined(CDIOCCLOSE)
+#if defined(CDROMCLOSETRAY)
+       status = ioctl(fd, CDROMCLOSETRAY);
+#elif defined(CDIOCCLOSE)
+       status = ioctl(fd, CDIOCCLOSE);
+#endif
+       if (status != 0) {
+               fprintf(stderr, _("%s: CD-ROM tray close command failed: %s\n"), programName, strerror(errno));
+               exit(1);
+       }
+#else
+    fprintf(stderr, _("%s: CD-ROM tray close command not supported by this kernel\n"), programName);
+#endif
+}
+
+/*
+ * Toggle tray.
+ *
+ * Written by Benjamin Schwenk <benjaminschwenk@yahoo.de> and
+ * Sybren Stuvel <sybren@thirdtower.com>
+ *
+ * Not supported by older kernels because it might use
+ * CloseTray().
+ *
+ */
+static void ToggleTray(int fd)
+{
+       struct timeval time_start, time_stop;
+       int time_elapsed;
+
+#ifdef CDROMCLOSETRAY
+
+       /* Try to open the CDROM tray and measure the time needed.
+        * In my experience the function needs less than 0.05
+        * seconds if the tray was already open, and at least 1.5 seconds
+        * if it was closed.
+        */
+       gettimeofday(&time_start, NULL);
+
+       /* Send the CDROMEJECT command to the device. */
+       if (ioctl(fd, CDROMEJECT, 0) < 0) {
+               perror("ioctl");
+               exit(1);
+       }
+
+       /* Get the second timestamp, to measure the time needed to open
+        * the tray.  */
+       gettimeofday(&time_stop, NULL);
+
+       time_elapsed = (time_stop.tv_sec * 1000000 + time_stop.tv_usec) -
+               (time_start.tv_sec * 1000000 + time_start.tv_usec);
+
+       /* If the tray "opened" too fast, we can be nearly sure, that it
+        * was already open. In this case, close it now. Else the tray was
+        * closed before. This would mean that we are done.  */
+       if (time_elapsed < TRAY_WAS_ALREADY_OPEN_USECS)
+               CloseTray(fd);
+
+#else
+    fprintf(stderr, _("%s: CD-ROM tray toggle command not supported by this kernel\n"), programName);
+#endif
+
+}
+
+/*
+ * Select Speed of CD-ROM drive.
+ * Thanks to Roland Krivanek (krivanek@fmph.uniba.sk)
+ * http://dmpc.dbp.fmph.uniba.sk/~krivanek/cdrom_speed/
+ */
+static void SelectSpeedCdrom(int fd, int speed)
+{
+       int status;
+
+#ifdef CDROM_SELECT_SPEED
+       status = ioctl(fd, CDROM_SELECT_SPEED, speed);
+       if (status != 0) {
+               fprintf(stderr, _("%s: CD-ROM select speed command failed: %s\n"), programName, strerror(errno));
+               exit(1);
+       }
+#else
+    fprintf(stderr, _("%s: CD-ROM select speed command not supported by this kernel\n"), programName);
+#endif
+}
+
+/*
+ * Read Speed of CD-ROM drive. From Linux 2.6.13, the current speed is correctly reported
+ */
+static int ReadSpeedCdrom(const char *shortName)
+{
+       char line[512];
+       char *str_speed, *str_name;
+       int drive_number = -1, i;
+       FILE *f = fopen("/proc/sys/dev/cdrom/info", "r");
+
+       if (f == NULL) {
+               fprintf(stderr, _("%s: unable to read the speed from /proc/sys/dev/cdrom/info\n"), programName);
+               exit(1);
+       }
+
+       while (!feof(f)) {
+               fgets(line, sizeof(line), f);
+
+               /* find drive number from shortName in line "drive name" */
+               if (drive_number == -1) {
+                       if (strncmp(line, "drive name:", 11) == 0) {
+                               str_name = strtok(&line[11], "\t ");
+                               drive_number = 0;
+                               while (strncmp(shortName, str_name, strlen(shortName)) != 0) {
+                                       drive_number++;
+                                       str_name = strtok(NULL, "\t ");
+                                       if (str_name == NULL) {
+                                               fprintf(stderr, _("%s: error while finding CD-ROM name\n"), programName);
+                                               exit(1);
+                                       }
+                               }
+                       }
+               /* find line "drive speed" and read the correct speed */
+               } else {
+                       if (strncmp(line, "drive speed:", 12) == 0) {
+                               str_speed = strtok(&line[12], "\t ");
+                               for (i = 1; i < drive_number; i++)
+                                       str_speed = strtok(NULL, "\t ");
+
+                               if (str_speed == NULL) {
+                                       fprintf(stderr, _("%s: error while reading speed\n"), programName);
+                                       exit(1);
+                               }
+                               return atoi(str_speed);
+                       }
+               }
+       }
+
+       fprintf(stderr, _("%s: error while reading speed\n"), programName);
+       exit(1);
+       return -1;
+}
+
+
+/*
+ * List Speed of CD-ROM drive.
+ */
+static void ListSpeedCdrom(const char *fullName, int fd)
+{
+#ifdef CDROM_SELECT_SPEED
+       int max_speed, curr_speed = 0, prev_speed;
+       char *shortName = strrchr(fullName, '/') + 1;
+
+       SelectSpeedCdrom(fd, 0);
+       max_speed = ReadSpeedCdrom(shortName);
+       while (curr_speed < max_speed) {
+               prev_speed = curr_speed;
+               SelectSpeedCdrom(fd, prev_speed + 1);
+               curr_speed = ReadSpeedCdrom(shortName);
+               if (curr_speed > prev_speed)
+                       printf("%d ", curr_speed);
+               else
+                       curr_speed = prev_speed + 1;
+       }
+
+       printf("\n");
+#else
+       fprintf(stderr, _("%s: CD-ROM select speed command not supported by this kernel\n"), programName);
+#endif
+}
+
+/*
+ * Eject using CDROMEJECT ioctl. Return 1 if successful, 0 otherwise.
+ */
+static int EjectCdrom(int fd)
+{
+       int status = -1;
+
+#if defined(CDROMEJECT)
+       status = ioctl(fd, CDROMEJECT);
+#elif defined(CDIOCEJECT)
+       status = ioctl(fd, CDIOCEJECT);
+#else
+/* Some kernels implement cdrom-eject only, but I don't think any kernel in the
+   world would implement eject only for non-cdrom drives.  Let's die. */
+# error
+#endif
+       return (status == 0);
+}
+
+#ifdef HAVE_EJECT_SCSI
+/*
+ * Eject using SCSI SG_IO commands. Return 1 if successful, 0 otherwise.
+ */
+static int EjectScsi(int fd)
+{
+       int status, k;
+       sg_io_hdr_t io_hdr;
+       unsigned char allowRmBlk[6] = {ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 0, 0};
+       unsigned char startStop1Blk[6] = {START_STOP, 0, 0, 0, 1, 0};
+       unsigned char startStop2Blk[6] = {START_STOP, 0, 0, 0, 2, 0};
+       unsigned char inqBuff[2];
+       unsigned char sense_buffer[32];
+
+       if ((ioctl(fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
+         if (v_option) {
+                 printf(_("not an sg device, or old sg driver\n"));
+         }
+         return 0;
+       }
+
+       memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+       io_hdr.interface_id = 'S';
+       io_hdr.cmd_len = 6;
+       io_hdr.mx_sb_len = sizeof(sense_buffer);
+       io_hdr.dxfer_direction = SG_DXFER_NONE;
+       io_hdr.dxfer_len = 0;
+       io_hdr.dxferp = inqBuff;
+       io_hdr.sbp = sense_buffer;
+       io_hdr.timeout = 10000;
+
+       io_hdr.cmdp = allowRmBlk;
+       status = ioctl(fd, SG_IO, (void *)&io_hdr);
+       if (status < 0)
+               return 0;
+
+       io_hdr.cmdp = startStop1Blk;
+       status = ioctl(fd, SG_IO, (void *)&io_hdr);
+       if (status < 0)
+               return 0;
+
+       io_hdr.cmdp = startStop2Blk;
+       status = ioctl(fd, SG_IO, (void *)&io_hdr);
+       if (status < 0)
+               return 0;
+
+       /* force kernel to reread partition table when new disc inserted */
+       status = ioctl(fd, BLKRRPART);
+       return 1;
+}
+#endif
+
+
+#ifdef HAVE_EJECT_FLOPPY
+/*
+ * Eject using FDEJECT ioctl. Return 1 if successful, 0 otherwise.
+ */
+static int EjectFloppy(int fd)
+{
+       int status;
+
+       status = ioctl(fd, FDEJECT);
+       return (status >= 0);
+}
+#endif
+
+
+#ifdef HAVE_EJECT_TAPE
+/*
+ * Eject using tape ioctl. Return 1 if successful, 0 otherwise.
+ */
+static int EjectTape(int fd)
+{
+       int status;
+       struct mtop op;
+
+       op.mt_op = MTOFFL; /* rewind and eject */
+       op.mt_count = 0;   /* not used */
+       status = ioctl(fd, MTIOCTOP, &op);
+       return (status >= 0);
+}
+#endif
+
+
+/* Unmount a device. */
+static void Unmount(const char *fullName)
+{
+       int status;
+
+       switch (fork()) {
+         case 0: /* child */
+                 setuid(getuid()); /* reduce likelyhood of security holes when running setuid */
+                 if(p_option) {
+                         execlp("pumount", "pumount", fullName, "-n", NULL);
+                         execlp("umount", "umount", fullName, "-n", NULL);
+                 } else {
+                         execlp("pumount", "pumount", fullName, NULL);
+                         execlp("umount", "umount", fullName, NULL);
+                 }
+                 fprintf(stderr, _("%s: unable to exec umount of `%s': %s\n"),
+                                 programName, fullName, strerror(errno));
+                 exit(1);
+                 break;
+         case -1:
+                 fprintf(stderr, _("%s: unable to fork: %s\n"), programName, strerror(errno));
+                 break;
+         default: /* parent */
+                 wait(&status);
+                 if (WIFEXITED(status) == 0) {
+                         fprintf(stderr, _("%s: unmount of `%s' did not exit normally\n"), programName, fullName);
+                         exit(1);
+                 }
+                 if (WEXITSTATUS(status) != 0) {
+                         fprintf(stderr, _("%s: unmount of `%s' failed\n"), programName, fullName);
+                         exit(1);
+                 }
+                 break;
+       }
+}
+
+
+/* Open a device file. Try opening first read/write, and if that fails then read only. */
+static int OpenDevice(const char *fullName)
+{
+       int fd;
+
+       fd = open(fullName, O_RDWR|O_NONBLOCK);
+       if (fd != -1) {
+               return fd;
+       }
+
+       fd = open(fullName, O_RDONLY|O_NONBLOCK);
+       if (fd == -1) {
+               fprintf(stderr, _("%s: unable to open `%s'\n"), programName, fullName);
+               exit(1);
+       }
+       return fd;
+}
+
+
+/*
+ * Get major and minor device numbers for a device file name, so we
+ * can check for duplicate devices.
+ */
+static int GetMajorMinor(const char *name, int *maj, int *min)
+{
+       struct stat sstat;
+       if (maj) *maj = -1;
+       if (min) *min = -1;
+       if (stat(name, &sstat) == -1)
+               return -1;
+       if (! S_ISBLK(sstat.st_mode) && ! S_ISCHR(sstat.st_mode))
+               return -1;
+       if (maj) *maj = major(sstat.st_rdev);
+       if (min) *min = minor(sstat.st_rdev);
+       return 0;
+}
+
+
+/*
+ * See if device has been mounted by looking in mount table.  If so, set
+ * device name and mount point name, and return 1, otherwise return 0.
+ */
+static int MountedDevice(const char *name, char **mountName, char **deviceName)
+{
+       FILE *fp;
+       char line[1024];
+       char s1[1024];
+       char s2[1024];
+       int rc;
+
+       int maj;
+       int min;
+
+       GetMajorMinor(name, &maj, &min);
+
+       fp = fopen((p_option ? "/proc/mounts" : "/etc/mtab"), "r");
+       if (fp == NULL)
+       {
+               fprintf(stderr, _("unable to open %s: %s\n"), (p_option ? "/proc/mounts" : "/etc/mtab"), strerror(errno));
+               exit(1);
+       }
+
+       while (fgets(line, sizeof(line), fp) != 0) {
+               rc = sscanf(line, "%1023s %1023s", s1, s2);
+               if (rc >= 2) {
+                       int mtabmaj, mtabmin;
+                       DeMangleMount(s1);
+                       DeMangleMount(s2);
+                       GetMajorMinor(s1, &mtabmaj, &mtabmin);
+                       if (((strcmp(s1, name) == 0) || (strcmp(s2, name) == 0)) ||
+                               ((maj != -1) && (maj == mtabmaj) && (min == mtabmin))) {
+                               FCLOSE(fp);
+                               *deviceName = strdup(s1);
+                               *mountName = strdup(s2);
+                               return 1;
+                       }
+               }
+       }
+       *deviceName = 0;
+       *mountName = 0;
+       FCLOSE(fp);
+       return 0;
+}
+
+
+/*
+ * See if device can be mounted by looking in /etc/fstab.
+ * If so, set device name and mount point name, and return 1,
+ * otherwise return 0.
+ */
+static int MountableDevice(const char *name, char **mountName, char **deviceName)
+{
+       FILE *fp;
+       char line[1024];
+       char s1[1024];
+       char s2[1024];
+       int rc;
+
+       fp = fopen("/etc/fstab", "r");
+       if (fp == NULL) {
+/*
+ * /etc/fstab may be unreadable in some situations due to passwords in the
+ * file.
+ */
+/*             fprintf(stderr, _("%s: unable to open /etc/fstab: %s\n"), programName, strerror(errno));
+               exit(1);*/
+               if (v_option) {
+                       printf( _("%s: unable to open /etc/fstab: %s\n"), programName, strerror(errno));
+               }
+               return -1;
+       }
+
+       while (fgets(line, sizeof(line), fp) != 0) {
+               rc = sscanf(line, "%1023s %1023s", s1, s2);
+               DeMangleMount(s1);
+               DeMangleMount(s2);
+               if (rc >= 2 && s1[0] != '#' && strcmp(s2, name) == 0) {
+                       FCLOSE(fp);
+                       *deviceName = strdup(s1);
+                       *mountName = strdup(s2);
+                       return 1;
+               }
+       }
+       FCLOSE(fp);
+       return 0;
+}
+
+
+#ifndef ANDROID
+/*
+ * Step through mount table and unmount all devices that match a regular
+ * expression.
+ */
+static void UnmountDevices(const char *pattern)
+{
+       regex_t preg;
+       FILE *fp;
+       char s1[1024];
+       char s2[1024];
+       char line[1024];
+       int status;
+
+       if (regcomp(&preg, pattern, REG_EXTENDED)!=0) {
+               perror(programName);
+               exit(1);
+       }
+
+       fp = fopen((p_option ? "/proc/mounts" : "/etc/mtab"), "r");
+       if (fp == NULL)
+       {
+               fprintf(stderr, _("unable to open %s: %s\n"),(p_option ? "/proc/mounts" : "/etc/mtab"), strerror(errno));
+               exit(1);
+       }
+
+       while (fgets(line, sizeof(line), fp) != 0) {
+               status = sscanf(line, "%1023s %1023s", s1, s2);
+               if (status >= 2) {
+                       status = regexec(&preg, s1, 0, 0, 0);
+                       if (status == 0) {
+                               if (v_option)
+                                       printf(_("%s: unmounting `%s'\n"), programName, s2);
+                               Unmount(s2);
+                       }
+               }
+       }
+       regfree(&preg);
+       FCLOSE(fp);
+}
+#endif
+
+
+/* Check if name is a symbolic link. If so, return what it points to. */
+static char *SymLink(const char *name)
+{
+       int status;
+       char s1[PATH_MAX];
+       char s2[PATH_MAX];
+       char s4[PATH_MAX];
+       char result[PATH_MAX];
+       char *s3;
+
+       memset(s1, 0, sizeof(s1));
+       memset(s2, 0, sizeof(s2));
+       memset(s4, 0, sizeof(s4));
+       memset(result, 0, sizeof(result));
+
+       status = readlink(name, s1, sizeof(s1) - 1);
+
+       if (status == -1)
+               return 0;
+
+       s1[status] = 0;
+       if (s1[0] == '/') { /* absolute link */
+               return strdup(s1);
+       } else { /* relative link, add base name */
+               strncpy(s2, name, sizeof(s2)-1);
+               s3 = strrchr(s2, '/');
+               if (s3 != 0) {
+                       s3[1] = 0;
+                       snprintf(result, sizeof(result)-1, "%s%s", s2, s1);
+               }
+       }
+       realpath(result, s4);
+       return strdup(s4);
+}
+
+
+#ifndef ANDROID
+/*
+ * Given a name, see if it matches a pattern for a device that can have
+ * multiple partitions.  If so, return a regular expression that matches
+ * partitions for that device, otherwise return 0.
+ */
+static char *MultiplePartitions(const char *name)
+{
+       int i = 0;
+       int status;
+       regex_t preg;
+       char pattern[256];
+       char *result = 0;
+
+       for (i = 0; partitionDevice[i] != 0; i++) {
+               /* look for ^/dev/foo[a-z]([0-9]?[0-9])?$, e.g. /dev/hda1 */
+               strcpy(pattern, "^/dev/");
+               strcat(pattern, partitionDevice[i]);
+               strcat(pattern, "[a-z]([0-9]?[0-9])?$");
+               if (regcomp(&preg, pattern, REG_EXTENDED|REG_NOSUB) != 0) {
+                       perror(programName);
+                       exit(1);
+               }
+               status = regexec(&preg, name, 1, 0, 0);
+               regfree(&preg);
+               if (status == 0) {
+                       result = (char *) malloc(strlen(name) + 25);
+                       if (result == NULL) {
+                               fprintf(stderr, _("%s: could not allocate memory\n"), programName);
+                               exit(1);
+                       }
+                       strcpy(result, name);
+                       result[strlen(partitionDevice[i]) + 6] = 0;
+                       strcat(result, "([0-9]?[0-9])?$");
+                       if (v_option)
+                               printf(_("%s: `%s' is a multipartition device\n"), programName, name);
+                       return result;
+               }
+       }
+       if (v_option)
+               printf(_("%s: `%s' is not a multipartition device\n"), programName, name);
+       return 0;
+}
+#endif
+
+/*
+ * Find device name in /sys/block/. Returns NULL if not
+ * found. The returned pointer must be free()'d.
+ */
+static char* FindDeviceSysBlock(const char* deviceName)
+{
+       DIR *dir = opendir("/sys/block");
+       struct dirent *d;
+       const char *baseName = strrchr(deviceName, '/');
+       char *device;
+       int len;
+
+       baseName = baseName ? baseName + 1 : deviceName;
+       if (!dir) {
+               fprintf(stderr, _("%s: can not open directory /sys/block/"), programName);
+               return NULL;
+       }
+       while ((d = readdir(dir)) != NULL) {
+               if (d->d_type != DT_DIR && d->d_type != DT_LNK && d->d_type != DT_UNKNOWN)
+                       continue;
+               len = strlen(d->d_name);
+               if (!strncmp(baseName, d->d_name, len)) {
+                       if ((*(baseName+len) >= '0' &&
+                            *(baseName+len) <= '9') ||
+                            *(baseName+len) == '\0') {
+                               device = strdup(d->d_name);
+                               closedir(dir);
+                               return device;
+                       }
+               }
+       }
+       closedir(dir);
+       return NULL;
+}
+
+/*
+ * From given path gets a subsystem. Returns subsystem if any found
+ * otherwise returns NULL. Returned value must not be free()'d
+ */
+static char *GetSubSystem(const char *sysfspath)
+{
+       static char subsystem[PATH_MAX];
+       char link_subsystem[PATH_MAX];
+       struct stat buf;
+       char *pos;
+
+       snprintf(link_subsystem, sizeof(link_subsystem), "%s/subsystem", sysfspath);
+
+       if (lstat(link_subsystem, &buf) == -1)
+               return NULL;
+       if (!S_ISLNK(buf.st_mode))
+               return NULL;
+       if (readlink(link_subsystem, subsystem, sizeof(subsystem)) == -1)
+               return NULL;
+       if ((pos = strrchr(subsystem, '/')) == NULL)
+               return NULL;
+       strncpy(subsystem, pos+1, sizeof(subsystem));
+
+       return subsystem;
+}
+
+/*
+ * Check content of /sys/block/<dev>/removable. Returns 1 if the file
+ * contains '1' otherwise returns 0.
+ */
+static int CheckRemovable(const char* deviceName)
+{
+       FILE *fp;
+       int removable = 0;
+       char *device;
+       char path[PATH_MAX];
+
+       if ((device = FindDeviceSysBlock(deviceName)) == NULL) {
+               fprintf(stderr,
+                               _("%s: did not find a device %s in /sys/block/\n"),
+                               programName, deviceName);
+               exit(1);
+       }
+       snprintf(path, sizeof(path), "/sys/block/%s/removable", device);
+       free(device);
+       if((fp = fopen(path, "r")) == NULL)
+               return removable;
+       if (fgetc(fp) == '1')
+               removable = 1;
+
+       fclose(fp);
+       return removable;
+}
+
+/* Check if a device is on hotpluggable subsystem. Returns 1 if is
+ * otherwise returns 0.
+ */
+static int CheckHotpluggable(const char* deviceName)
+{
+       int hotpluggable = 0;
+       char *device;
+       char path[PATH_MAX];
+       char *device_chain;
+       struct stat buf;
+       char *subsystem;
+       char *pos;
+
+       if ((device = FindDeviceSysBlock(deviceName)) == NULL) {
+               fprintf(stderr, _("%s: did not find a device %s in /sys/block/\n"),
+                               programName, deviceName);
+               exit(1);
+       }
+       snprintf(path, sizeof(path), "/sys/block/%s/device", device);
+       free(device);
+
+       if (lstat(path, &buf) == -1)
+               return hotpluggable;
+       if (!S_ISLNK(buf.st_mode))
+               return hotpluggable;
+       if ((device_chain = SymLink(path)) == NULL)
+               return hotpluggable;
+       while ( strncmp(device_chain, "", sizeof(device_chain) != 0)) {
+               subsystem = GetSubSystem(device_chain);
+               if (subsystem) {
+                       /* as hotpluggable we assume devices on these buses  */
+                       if (strncmp("usb", subsystem, sizeof("usb")) == 0 ||
+                           strncmp("ieee1394", subsystem, sizeof("ieee1394")) == 0 ||
+                           strncmp("pcmcia", subsystem, sizeof("pcmcia")) == 0 ||
+                           strncmp("mmc", subsystem, sizeof("mmc")) == 0 ||
+                           strncmp("ccw", subsystem, sizeof("ccw")) == 0) {
+                               hotpluggable = 1;
+                               break;
+                       }
+               }
+               /* remove one member from devicechain */
+               pos = strrchr(device_chain, '/');
+               if (pos)
+                       pos[0] = '\0';
+               else
+                       device_chain[0] = '\0';
+       }
+
+       return hotpluggable;
+}
+
+/* handle -x option */
+static void HandleXOption(char *deviceName)
+{
+       int fd;            /* file descriptor for device */
+       if (x_option) {
+               if (v_option)
+               {
+                       if (x_arg == 0)
+                               printf(_("%s: setting CD-ROM speed to auto\n"), programName);
+                       else
+                               printf(_("%s: setting CD-ROM speed to %dX\n"), programName, x_arg);
+               }
+               fd = OpenDevice(deviceName);
+               SelectSpeedCdrom(fd, x_arg);
+               exit(0);
+       }
+}
+
+
+/* main program */
+int main(int argc, char **argv)
+{
+       const char *defaultDevice = DEFAULTDEVICE;  /* default if no name passed by user */
+       int worked = 0;    /* set to 1 when successfully ejected */
+       char *device = 0;  /* name passed from user */
+       char *fullName;    /* expanded name */
+       char *fullNameOrig;/* expanded name (links not resolved) */
+       char *deviceName;  /* name of device */
+       char *linkName;    /* name of device's symbolic link */
+       char *mountName;   /* name of device's mount point */
+       int fd;            /* file descriptor for device */
+       int mounted = 0;   /* true if device is mounted */
+       int mountable = 0; /* true if device is in /etc/fstab */
+       int result = 0;    /* store the result of a operation */
+       char *pattern = 0; /* regex for device if multiple partitions */
+       int ld = 6;        /* symbolic link max depth */
+
+       I18NCODE
+
+       /* program name is global variable used by other procedures */
+       programName = strdup(argv[0]);
+
+       /* parse the command line arguments */
+       parse_args(argc, argv, &device);
+
+
+       /* handle -d option */
+       if (d_option) {
+               printf(_("%s: default device: `%s'\n"), programName, defaultDevice);
+               exit(0);
+       }
+
+       /* if no device, use default */
+       if (device == 0) {
+               device = strdup(defaultDevice);
+               if (v_option)
+                       printf(_("%s: using default device `%s'\n"), programName, device);
+       }
+
+       /* Strip any trailing slash from name in case user used bash/tcsh
+          style filename completion (e.g. /mnt/cdrom/) */
+       if (device[strlen(device)-1] == '/')
+               device[strlen(device)-1] = 0;
+
+       if (v_option)
+               printf(_("%s: device name is `%s'\n"), programName, device);
+
+       do {
+         /* figure out full device or mount point name */
+         fullName = FindDevice(device);
+         if (fullName == 0) {
+           fprintf(stderr, _("%s: unable to find or open device for: `%s'\n"),
+                   programName, device);
+           exit(1);
+         }
+         if (v_option)
+           printf(_("%s: expanded name is `%s'\n"), programName, fullName);
+
+         /* check for a symbolic link */
+         /* /proc/mounts doesn't resolve symbolic links */
+         fullNameOrig = strdup(fullName);
+         linkName = strdup(fullName); /* ensure linkName is initialized */
+         if (!p_option) {
+           while ((linkName = SymLink(fullName)) && (ld > 0)) {
+             if (v_option)
+               printf(_("%s: `%s' is a link to `%s'\n"), programName,
+                      fullName, linkName);
+             free(fullName);
+             fullName = strdup(linkName);
+             free(linkName);
+             linkName = 0;
+             ld--;
+           }
+         }
+         /* handle max depth exceeded option */
+         if (ld <= 0) {
+           printf(_("%s: maximum symbolic link depth exceeded: `%s'\n"), programName, fullName);
+           exit(1);
+         }
+
+         /* if mount point, get device name */
+         mounted = MountedDevice(fullName, &mountName, &deviceName);
+         if (v_option) {
+           if (mounted)
+             printf(_("%s: `%s' is mounted at `%s'\n"), programName,
+                    deviceName, mountName);
+           else
+             printf(_("%s: `%s' is not mounted\n"), programName, fullName);
+         }
+         if (!mounted) {
+           deviceName = strdup(fullName);
+         }
+
+         /* if not currently mounted, see if it is a possible mount point */
+         if (!mounted) {
+           mountable = MountableDevice(fullName, &mountName, &deviceName);
+           /* if return value -1 then fstab could not be read */
+           if (v_option && mountable >= 0) {
+             if (mountable)
+               printf(_("%s: `%s' can be mounted at `%s'\n"), programName, deviceName, mountName);
+             else
+               printf(_("%s: `%s' is not a mount point\n"), programName, fullName);
+           }
+         }
+
+         result = GetMajorMinor(deviceName, NULL, NULL);
+         if (result == -1) {
+             fprintf(stderr,
+                     _("%s: tried to use `%s' as device name but it is no block device\n"),
+                     programName, deviceName);
+         }
+
+       } while (result == -1);
+
+       /* handle -n option */
+       if (n_option) {
+               printf(_("%s: device is `%s'\n"), programName, deviceName);
+               if (v_option)
+                       printf(_("%s: exiting due to -n/--noop option\n"), programName);
+               exit(0);
+       }
+
+       /* Check if device has removable flag*/
+       if (v_option)
+               printf(_("%s: checking if device \"%s\" has a removable or hotpluggable flag\n"),
+                               programName, deviceName);
+       if (!CheckRemovable(deviceName) && !CheckHotpluggable(deviceName))
+       {
+               fprintf(stderr, _("%s: device \"%s\" doesn't have a removable or hotpluggable flag\n"),
+                               programName, deviceName);
+               exit(1);
+       }
+
+       /* handle -i option */
+       if (i_option) {
+               fd = OpenDevice(deviceName);
+               ManualEject(fd, i_arg);
+               exit(0);
+       }
+
+       /* handle -a option */
+       if (a_option) {
+               if (v_option) {
+                       if (a_arg)
+                               printf(_("%s: enabling auto-eject mode for `%s'\n"), programName, deviceName);
+                       else
+                               printf(_("%s: disabling auto-eject mode for `%s'\n"), programName, deviceName);
+               }
+               fd = OpenDevice(deviceName);
+               AutoEject(fd, a_arg);
+               exit(0);
+       }
+
+       /* handle -t option */
+       if (t_option) {
+               if (v_option)
+                       printf(_("%s: closing tray\n"), programName);
+               fd = OpenDevice(deviceName);
+               CloseTray(fd);
+               HandleXOption(deviceName);
+               exit(0);
+       }
+
+       /* handle -X option */
+       if (X_option) {
+               if (v_option)
+                       printf(_("%s: listing CD-ROM speed\n"), programName);
+               fd = OpenDevice(deviceName);
+               ListSpeedCdrom(deviceName, fd);
+               exit(0);
+       }
+
+       /* handle -x option only */
+       if (!c_option) HandleXOption(deviceName);
+
+       /* unmount device if mounted */
+       if ((m_option != 1) && mounted) {
+               if (v_option)
+                       printf(_("%s: unmounting device `%s' from `%s'\n"), programName, deviceName, mountName);
+               Unmount(mountName);
+       }
+
+#ifndef ANDROID
+       /* if it is a multipartition device, unmount any other partitions on
+          the device */
+       pattern = MultiplePartitions(deviceName);
+       if ((m_option != 1) && (pattern != 0))
+               UnmountDevices(pattern);
+#endif
+
+       /* handle -T option */
+       if (T_option) {
+               if (v_option)
+                       printf(_("%s: toggling tray\n"), programName);
+               fd = OpenDevice(deviceName);
+               ToggleTray(fd);
+               HandleXOption(deviceName);
+               exit(0);
+       }
+
+       /* handle -c option */
+       if (c_option) {
+               if (v_option)
+                       printf(_("%s: selecting CD-ROM disc #%d\n"), programName, c_arg);
+               fd = OpenDevice(deviceName);
+               ChangerSelect(fd, c_arg);
+               HandleXOption(deviceName);
+               exit(0);
+       }
+
+       /* if user did not specify type of eject, try all four methods */
+       if ((r_option + s_option + f_option + q_option) == 0) {
+               r_option = s_option = f_option = q_option = 1;
+       }
+
+       /* open device */
+       fd = OpenDevice(deviceName);
+
+       /* try various methods of ejecting until it works */
+       if (r_option) {
+               if (v_option)
+                       printf(_("%s: trying to eject `%s' using CD-ROM eject command\n"), programName, deviceName);
+               worked = EjectCdrom(fd);
+               if (v_option) {
+                       if (worked)
+                               printf(_("%s: CD-ROM eject command succeeded\n"), programName);
+                       else
+                               printf(_("%s: CD-ROM eject command failed\n"), programName);
+               }
+       }
+
+#ifdef HAVE_EJECT_SCSI
+       if (s_option && !worked) {
+               if (v_option)
+                       printf(_("%s: trying to eject `%s' using SCSI commands\n"), programName, deviceName);
+               worked = EjectScsi(fd);
+               if (v_option) {
+                       if (worked)
+                               printf(_("%s: SCSI eject succeeded\n"), programName);
+                       else
+                               printf(_("%s: SCSI eject failed\n"), programName);
+               }
+       }
+#endif
+
+#ifdef HAVE_EJECT_FLOPPY
+       if (f_option && !worked) {
+               if (v_option)
+                       printf(_("%s: trying to eject `%s' using floppy eject command\n"), programName, deviceName);
+               worked = EjectFloppy(fd);
+               if (v_option) {
+                       if (worked)
+                               printf(_("%s: floppy eject command succeeded\n"), programName);
+                       else
+                               printf(_("%s: floppy eject command failed\n"), programName);
+               }
+       }
+#endif
+
+#ifdef HAVE_EJECT_TAPE
+       if (q_option && !worked) {
+               if (v_option)
+                       printf(_("%s: trying to eject `%s' using tape offline command\n"), programName, deviceName);
+               worked = EjectTape(fd);
+               if (v_option) {
+                       if (worked)
+                               printf(_("%s: tape offline command succeeded\n"), programName);
+                       else
+                               printf(_("%s: tape offline command failed\n"), programName);
+               }
+       }
+#endif
+
+       if (!worked) {
+               fprintf(stderr, _("%s: unable to eject, last error: %s\n"), programName, strerror(errno));
+               exit(1);
+       }
+
+       /* cleanup */
+       CLOSE(fd);
+       free(device);
+       free(deviceName);
+       free(fullName);
+       free(fullNameOrig);
+       free(linkName);
+       free(mountName);
+       free(pattern);
+       exit(0);
+}
diff --git a/i18n.h b/i18n.h
new file mode 100644 (file)
index 0000000..75f140a
--- /dev/null
+++ b/i18n.h
@@ -0,0 +1,26 @@
+/*
+    i18nized by:  KUN-CHUNG, HSIEH <linuxer@coventive.com>
+                 Taiwan
+
+    Homepage: http://www.geocities.com/linux4tw/
+
+    程式國際化設計:  謝崑中
+*/
+
+#include <features.h>
+#ifndef __i18n__
+# ifdef USE_NLS
+   #define PKG "eject"
+   #define __i18n__
+   #define LOCALEDIR "/usr/share/locale"
+   #include <locale.h>
+   #include <libintl.h>
+   #define _(str) gettext (str)
+   #define N_(str) (str)
+   #define I18NCODE setlocale(LC_ALL,""); textdomain(PKG); bindtextdomain(PKG,LOCALEDIR);
+# else
+   #define _(str) (str)
+   #define N_(str) (str)
+   #define I18NCODE
+# endif
+#endif