summaryrefslogtreecommitdiffstats
path: root/usr/utils
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:06:04 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:06:04 +0000
commit2f0649f6fe411d7e07c8d56cf8ea56db53536da8 (patch)
tree778611fb52176dce1ad06c68e87b2cb348ca0f7b /usr/utils
parentInitial commit. (diff)
downloadklibc-2f0649f6fe411d7e07c8d56cf8ea56db53536da8.tar.xz
klibc-2f0649f6fe411d7e07c8d56cf8ea56db53536da8.zip
Adding upstream version 2.0.13.upstream/2.0.13upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--usr/utils/Kbuild86
-rw-r--r--usr/utils/cat.c309
-rw-r--r--usr/utils/chroot.c30
-rw-r--r--usr/utils/cpio.c1079
-rw-r--r--usr/utils/dd.c540
-rw-r--r--usr/utils/dmesg.c79
-rw-r--r--usr/utils/false.c4
-rw-r--r--usr/utils/file_mode.c143
-rw-r--r--usr/utils/file_mode.h6
-rw-r--r--usr/utils/halt.c64
-rw-r--r--usr/utils/insmod.c140
-rw-r--r--usr/utils/kill.c33
-rw-r--r--usr/utils/ln.c77
-rw-r--r--usr/utils/losetup.c400
-rw-r--r--usr/utils/ls.c223
-rw-r--r--usr/utils/minips.c511
-rw-r--r--usr/utils/mkdir.c154
-rw-r--r--usr/utils/mkfifo.c70
-rw-r--r--usr/utils/mknod.c84
-rw-r--r--usr/utils/mount_main.c156
-rw-r--r--usr/utils/mount_opts.c102
-rw-r--r--usr/utils/mount_opts.h26
-rw-r--r--usr/utils/mv.c69
-rw-r--r--usr/utils/nuke.c121
-rw-r--r--usr/utils/pivot_root.c19
-rw-r--r--usr/utils/readlink.c57
-rw-r--r--usr/utils/sleep.c26
-rw-r--r--usr/utils/sync.c7
-rw-r--r--usr/utils/true.c4
-rw-r--r--usr/utils/umount.c51
-rw-r--r--usr/utils/uname.c155
31 files changed, 4825 insertions, 0 deletions
diff --git a/usr/utils/Kbuild b/usr/utils/Kbuild
new file mode 100644
index 0000000..a389c2a
--- /dev/null
+++ b/usr/utils/Kbuild
@@ -0,0 +1,86 @@
+#
+# Kbuild file for klib utils
+#
+
+progs := chroot dd mkdir mkfifo mknod mount pivot_root umount
+progs += true false sleep ln mv nuke minips cat ls losetup
+progs += insmod uname halt kill readlink cpio sync dmesg
+
+static-y := $(addprefix static/, $(progs))
+shared-y := $(addprefix shared/, $(progs))
+
+# The binary is placed in a subdir, so we need to tell kbuild this
+static/chroot-y := chroot.o
+shared/chroot-y := chroot.o
+static/dd-y := dd.o
+shared/dd-y := dd.o
+static/dmesg-y := dmesg.o
+shared/dmesg-y := dmesg.o
+static/mkdir-y := mkdir.o file_mode.o
+shared/mkdir-y := mkdir.o file_mode.o
+static/mkfifo-y := mkfifo.o file_mode.o
+shared/mkfifo-y := mkfifo.o file_mode.o
+static/mknod-y := mknod.o file_mode.o
+shared/mknod-y := mknod.o file_mode.o
+static/mount-y := mount_main.o mount_opts.o
+shared/mount-y := mount_main.o mount_opts.o
+static/pivot_root-y := pivot_root.o
+shared/pivot_root-y := pivot_root.o
+static/umount-y := umount.o
+shared/umount-y := umount.o
+static/true-y := true.o
+shared/true-y := true.o
+static/false-y := false.o
+shared/false-y := false.o
+static/sleep-y := sleep.o
+shared/sleep-y := sleep.o
+static/ln-y := ln.o
+shared/ln-y := ln.o
+static/ls-y := ls.o
+shared/ls-y := ls.o
+static/mv-y := mv.o
+shared/mv-y := mv.o
+static/nuke-y := nuke.o
+shared/nuke-y := nuke.o
+static/minips-y := minips.o
+shared/minips-y := minips.o
+static/cat-y := cat.o
+shared/cat-y := cat.o
+static/insmod-y := insmod.o
+shared/insmod-y := insmod.o
+static/uname-y := uname.o
+shared/uname-y := uname.o
+static/halt-y := halt.o
+shared/halt-y := halt.o
+static/kill-y := kill.o
+shared/kill-y := kill.o
+static/readlink-y := readlink.o
+shared/readlink-y := readlink.o
+static/cpio-y := cpio.o
+shared/cpio-y := cpio.o
+static/sync-y := sync.o
+shared/sync-y := sync.o
+static/losetup-y := losetup.o
+shared/losetup-y := losetup.o
+
+# Additionally linked targets
+always := static/reboot static/poweroff
+ifdef KLIBCSHAREDFLAGS
+always += shared/reboot shared/poweroff
+endif
+
+$(obj)/static/reboot $(obj)/static/poweroff: $(obj)/static/halt
+ $(call cmd,ln)
+$(obj)/shared/reboot $(obj)/shared/poweroff: $(obj)/shared/halt
+ $(call cmd,ln)
+
+# Clean deletes the static and shared dir
+clean-dirs := static shared
+
+# install the shared binaries by preference
+ifdef KLIBCSHAREDFLAGS
+install-y := $(shared-y)
+else
+install-y := $(static-y)
+endif
+install-link-y := reboot=halt poweroff=halt
diff --git a/usr/utils/cat.c b/usr/utils/cat.c
new file mode 100644
index 0000000..7465148
--- /dev/null
+++ b/usr/utils/cat.c
@@ -0,0 +1,309 @@
+/* $NetBSD: cat.c,v 1.43 2004/01/04 03:31:28 jschauma Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kevin Fall.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __COPYRIGHT
+#define __COPYRIGHT(arg)
+#endif
+#ifndef __RCSID
+#define __RCSID(arg)
+#endif
+
+#if !defined(lint)
+__COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n");
+#if 0
+static char sccsid[] = "@(#)cat.c 8.2 (Berkeley) 4/27/95";
+#else
+__RCSID("$NetBSD: cat.c,v 1.43 2004/01/04 03:31:28 jschauma Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int bflag, eflag, fflag, lflag, nflag, sflag, tflag, vflag;
+int rval;
+const char *filename;
+
+int main(int, char *[]);
+void cook_args(char *argv[]);
+void cook_buf(FILE *);
+void raw_args(char *argv[]);
+void raw_cat(int);
+
+int main(int argc, char *argv[])
+{
+ int ch;
+ struct flock stdout_lock;
+
+ while ((ch = getopt(argc, argv, "beflnstuv")) != -1)
+ switch (ch) {
+ case 'b':
+ bflag = nflag = 1; /* -b implies -n */
+ break;
+ case 'e':
+ eflag = vflag = 1; /* -e implies -v */
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'l':
+ lflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 't':
+ tflag = vflag = 1; /* -t implies -v */
+ break;
+ case 'u':
+ /* unimplemented */
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ case '?':
+ (void)fprintf(stderr,
+ "usage: cat [-beflnstuv] [-] [file ...]\n");
+ exit(1);
+ /* NOTREACHED */
+ }
+ argv += optind;
+
+ if (lflag) {
+ stdout_lock.l_len = 0;
+ stdout_lock.l_start = 0;
+ stdout_lock.l_type = F_WRLCK;
+ stdout_lock.l_whence = SEEK_SET;
+ if (fcntl(STDOUT_FILENO, F_SETLKW, &stdout_lock) == -1) {
+ perror("fcntl");
+ exit(1);
+ }
+ }
+
+ if (bflag || eflag || nflag || sflag || tflag || vflag)
+ cook_args(argv);
+ else
+ raw_args(argv);
+ if (fclose(stdout)) {
+ perror("fclose");
+ exit(1);
+ }
+ exit(rval);
+ /* NOTREACHED */
+}
+
+void cook_args(char **argv)
+{
+ FILE *fp;
+
+ fp = stdin;
+ filename = "stdin";
+ do {
+ if (*argv) {
+ if (!strcmp(*argv, "-"))
+ fp = stdin;
+ else if ((fp = fopen(*argv,
+ fflag ? "rf" : "r")) == NULL) {
+ perror("fopen");
+ rval = 1;
+ ++argv;
+ continue;
+ }
+ filename = *argv++;
+ }
+ cook_buf(fp);
+ if (fp != stdin)
+ (void)fclose(fp);
+ } while (*argv);
+}
+
+void cook_buf(FILE * fp)
+{
+ int ch, gobble, line, prev;
+ int stdout_err = 0;
+
+ line = gobble = 0;
+ for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
+ if (prev == '\n') {
+ if (ch == '\n') {
+ if (sflag) {
+ if (!gobble && putchar(ch) == EOF)
+ break;
+ gobble = 1;
+ continue;
+ }
+ if (nflag) {
+ if (!bflag) {
+ if (fprintf(stdout,
+ "%6d\t",
+ ++line) < 0) {
+ stdout_err++;
+ break;
+ }
+ } else if (eflag) {
+ if (fprintf(stdout,
+ "%6s\t", "") < 0) {
+ stdout_err++;
+ break;
+ }
+ }
+ }
+ } else if (nflag) {
+ if (fprintf(stdout, "%6d\t", ++line) < 0) {
+ stdout_err++;
+ break;
+ }
+ }
+ }
+ gobble = 0;
+ if (ch == '\n') {
+ if (eflag)
+ if (putchar('$') == EOF)
+ break;
+ } else if (ch == '\t') {
+ if (tflag) {
+ if (putchar('^') == EOF || putchar('I') == EOF)
+ break;
+ continue;
+ }
+ } else if (vflag) {
+ if (!isascii(ch)) {
+ if (putchar('M') == EOF || putchar('-') == EOF)
+ break;
+ ch = (ch) & 0x7f;
+ }
+ if (iscntrl(ch)) {
+ if (putchar('^') == EOF ||
+ putchar(ch == '\177' ? '?' :
+ ch | 0100) == EOF)
+ break;
+ continue;
+ }
+ }
+ if (putchar(ch) == EOF)
+ break;
+ }
+ if (stdout_err) {
+ perror(filename);
+ rval = 1;
+ }
+}
+
+void raw_args(char **argv)
+{
+ int fd;
+
+ fd = fileno(stdin);
+ filename = "stdin";
+ do {
+ if (*argv) {
+ if (!strcmp(*argv, "-"))
+ fd = fileno(stdin);
+ else if (fflag) {
+ struct stat st;
+ fd = open(*argv, O_RDONLY | O_NONBLOCK, 0);
+ if (fd < 0)
+ goto skip;
+
+ if (fstat(fd, &st) == -1) {
+ close(fd);
+ goto skip;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ close(fd);
+ errno = EINVAL;
+ goto skipnomsg;
+ }
+ } else if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
+ skip:
+ perror(*argv);
+ skipnomsg:
+ rval = 1;
+ ++argv;
+ continue;
+ }
+ filename = *argv++;
+ }
+ raw_cat(fd);
+ if (fd != fileno(stdin))
+ (void)close(fd);
+ } while (*argv);
+}
+
+void raw_cat(int rfd)
+{
+ static char *buf;
+ static char fb_buf[BUFSIZ];
+ static size_t bsize;
+
+ struct stat sbuf;
+ ssize_t nr, nw, off;
+ int wfd;
+
+ wfd = fileno(stdout);
+ if (buf == NULL) {
+ if (fstat(wfd, &sbuf) == 0) {
+ bsize = sbuf.st_blksize > BUFSIZ ?
+ sbuf.st_blksize : BUFSIZ;
+ buf = malloc(bsize);
+ }
+ if (buf == NULL) {
+ buf = fb_buf;
+ bsize = BUFSIZ;
+ }
+ }
+ while ((nr = read(rfd, buf, bsize)) > 0)
+ for (off = 0; nr; nr -= nw, off += nw)
+ if ((nw = write(wfd, buf + off, (size_t) nr)) < 0) {
+ perror("write");
+ exit(1);
+ }
+ if (nr < 0) {
+ fprintf(stderr, "%s: invalid length\n", filename);
+ rval = 1;
+ }
+}
diff --git a/usr/utils/chroot.c b/usr/utils/chroot.c
new file mode 100644
index 0000000..bc1b94f
--- /dev/null
+++ b/usr/utils/chroot.c
@@ -0,0 +1,30 @@
+/*
+ * by rmk
+ */
+#include <unistd.h>
+#include <stdio.h>
+
+int main(int argc, char *argv[], char *envp[])
+{
+ if (argc < 3) {
+ fprintf(stderr, "Usage: %s newroot command...\n", argv[0]);
+ return 1;
+ }
+
+ if (chroot(argv[1]) == -1) {
+ perror("chroot");
+ return 1;
+ }
+
+ if (chdir("/") == -1) {
+ perror("chdir");
+ return 1;
+ }
+
+ if (execvp(argv[2], argv + 2) == -1) {
+ perror("execvp");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/usr/utils/cpio.c b/usr/utils/cpio.c
new file mode 100644
index 0000000..9b0b6ae
--- /dev/null
+++ b/usr/utils/cpio.c
@@ -0,0 +1,1079 @@
+/* copyin.c - extract or list a cpio archive
+ Copyright (C) 1990,1991,1992,2001,2002,2003,2004 Free Software Foundation, Inc.
+
+ 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, 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. */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <malloc.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+#include <utime.h>
+#include <fnmatch.h>
+
+# ifndef DIRECTORY_SEPARATOR
+# define DIRECTORY_SEPARATOR '/'
+# endif
+
+# ifndef ISSLASH
+# define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR)
+# endif
+
+/* Return 1 if an array of N objects, each of size S, cannot exist due
+ to size arithmetic overflow. S must be positive and N must be
+ nonnegative. This is a macro, not an inline function, so that it
+ works correctly even when SIZE_MAX < N.
+
+ By gnulib convention, SIZE_MAX represents overflow in size
+ calculations, so the conservative dividend to use here is
+ SIZE_MAX - 1, since SIZE_MAX might represent an overflowed value.
+ However, malloc (SIZE_MAX) fails on all known hosts where
+ sizeof (ptrdiff_t) <= sizeof (size_t), so do not bother to test for
+ exactly-SIZE_MAX allocations on such hosts; this avoids a test and
+ branch when S is known to be 1. */
+# define xalloc_oversized(n, s) \
+ ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n))
+
+#define DISK_IO_BLOCK_SIZE (512)
+
+char *progname = NULL;
+
+/* If true, print a . for each file processed. (-V) */
+char dot_flag = false;
+
+/* Input and output buffers. */
+char *input_buffer, *output_buffer;
+
+/* The size of the input buffer. */
+long input_buffer_size;
+
+/* Current locations in `input_buffer' and `output_buffer'. */
+char *in_buff, *out_buff;
+
+/* Current number of bytes stored at `input_buff' and `output_buff'. */
+long input_size, output_size;
+
+/* Block size value, initially 512. -B sets to 5120. */
+int io_block_size = 512;
+
+struct new_cpio_header {
+ unsigned short c_magic;
+ union {
+ struct {
+ unsigned long c_ino;
+ unsigned long c_mode;
+ unsigned long c_uid;
+ unsigned long c_gid;
+ unsigned long c_nlink;
+ unsigned long c_mtime;
+ unsigned long c_filesize;
+ long c_dev_maj;
+ long c_dev_min;
+ long c_rdev_maj;
+ long c_rdev_min;
+ unsigned long c_namesize;
+ unsigned long c_chksum;
+ };
+ unsigned long c_hdr[13];
+ };
+ char *c_name;
+ char *c_tar_linkname;
+};
+
+/* Total number of bytes read and written for all files.
+ * Now that many tape drives hold more than 4Gb we need more than 32
+ * bits to hold input_bytes and output_bytes.
+ */
+long long input_bytes, output_bytes;
+
+/* Allocate N bytes of memory dynamically, with error checking. */
+
+static void *xmalloc(size_t n)
+{
+ void *p;
+ if (xalloc_oversized(n, 1) || (!(p = malloc(n)) && n != 0)) {
+ fprintf(stderr, "%s: memory exhausted\n", progname);
+ exit(1);
+ }
+ return p;
+/* return xnmalloc_inline (n, 1); */
+}
+
+/* Clone STRING. */
+
+static char *xstrdup(char const *string)
+{
+ size_t s = strlen(string) + 1;
+ return memcpy(xmalloc(s), string, s);
+/* return xmemdup_inline (string, strlen (string) + 1); */
+}
+
+/* Copy NUM_BYTES of buffer `in_buff' into IN_BUF.
+ `in_buff' may be partly full.
+ When `in_buff' is exhausted, refill it from file descriptor IN_DES. */
+
+static void tape_fill_input_buffer(int in_des, int num_bytes)
+{
+ in_buff = input_buffer;
+ num_bytes = (num_bytes < io_block_size) ? num_bytes : io_block_size;
+ input_size = read(in_des, input_buffer, num_bytes);
+ if (input_size < 0) {
+ fprintf(stderr, "%s: read error: %s\n", progname,
+ strerror(errno));
+ exit(1);
+ }
+ if (input_size == 0) {
+ fprintf(stderr, "%s: premature end of file\n", progname);
+ exit(1);
+ }
+ input_bytes += input_size;
+}
+
+/* Write `output_size' bytes of `output_buffer' to file
+ descriptor OUT_DES and reset `output_size' and `out_buff'.
+ If `swapping_halfwords' or `swapping_bytes' is set,
+ do the appropriate swapping first. Our callers have
+ to make sure to only set these flags if `output_size'
+ is appropriate (a multiple of 4 for `swapping_halfwords',
+ 2 for `swapping_bytes'). The fact that DISK_IO_BLOCK_SIZE
+ must always be a multiple of 4 helps us (and our callers)
+ insure this. */
+
+static void disk_empty_output_buffer(int out_des)
+{
+ int bytes_written;
+
+ bytes_written = write(out_des, output_buffer, output_size);
+
+ if (bytes_written != output_size) {
+ fprintf(stderr, "%s: write error: %s\n",
+ progname, strerror(errno));
+ exit(1);
+ }
+ output_bytes += output_size;
+ out_buff = output_buffer;
+ output_size = 0;
+}
+
+/* Copy NUM_BYTES of buffer IN_BUF to `out_buff', which may be partly full.
+ When `out_buff' fills up, flush it to file descriptor OUT_DES. */
+
+static void disk_buffered_write(char *in_buf, int out_des, long num_bytes)
+{
+ register long bytes_left = num_bytes; /* Bytes needing to be copied. */
+ register long space_left; /* Room left in output buffer. */
+
+ while (bytes_left > 0) {
+ space_left = DISK_IO_BLOCK_SIZE - output_size;
+ if (space_left == 0)
+ disk_empty_output_buffer(out_des);
+ else {
+ if (bytes_left < space_left)
+ space_left = bytes_left;
+ memmove(out_buff, in_buf, (unsigned)space_left);
+ out_buff += space_left;
+ output_size += space_left;
+ in_buf += space_left;
+ bytes_left -= space_left;
+ }
+ }
+}
+
+/* Copy a file using the input and output buffers, which may start out
+ partly full. After the copy, the files are not closed nor the last
+ block flushed to output, and the input buffer may still be partly
+ full. If `crc_i_flag' is set, add each byte to `crc'.
+ IN_DES is the file descriptor for input;
+ OUT_DES is the file descriptor for output;
+ NUM_BYTES is the number of bytes to copy. */
+
+static void copy_files_tape_to_disk(int in_des, int out_des, long num_bytes)
+{
+ long size;
+
+ while (num_bytes > 0) {
+ if (input_size == 0)
+ tape_fill_input_buffer(in_des, io_block_size);
+ size = (input_size < num_bytes) ? input_size : num_bytes;
+ disk_buffered_write(in_buff, out_des, size);
+ num_bytes -= size;
+ input_size -= size;
+ in_buff += size;
+ }
+}
+
+/* if IN_BUF is NULL, Skip the next NUM_BYTES bytes of file descriptor IN_DES. */
+static void tape_buffered_read(char *in_buf, int in_des, long num_bytes)
+{
+ register long bytes_left = num_bytes; /* Bytes needing to be copied. */
+ register long space_left; /* Bytes to copy from input buffer. */
+
+ while (bytes_left > 0) {
+ if (input_size == 0)
+ tape_fill_input_buffer(in_des, io_block_size);
+ if (bytes_left < input_size)
+ space_left = bytes_left;
+ else
+ space_left = input_size;
+ if (in_buf != NULL) {
+ memmove(in_buf, in_buff, (unsigned)space_left);
+ in_buf += space_left;
+ }
+ in_buff += space_left;
+ input_size -= space_left;
+ bytes_left -= space_left;
+ }
+}
+
+/* Skip the next NUM_BYTES bytes of file descriptor IN_DES. */
+#define tape_toss_input(in_des,num_bytes) \
+(tape_buffered_read(NULL,(in_des),(num_bytes)))
+
+struct deferment {
+ struct deferment *next;
+ struct new_cpio_header header;
+};
+
+static struct deferment *create_deferment(struct new_cpio_header *file_hdr)
+{
+ struct deferment *d;
+ d = (struct deferment *)xmalloc(sizeof(struct deferment));
+ d->header = *file_hdr;
+ d->header.c_name = (char *)xmalloc(strlen(file_hdr->c_name) + 1);
+ strcpy(d->header.c_name, file_hdr->c_name);
+ return d;
+}
+
+static void free_deferment(struct deferment *d)
+{
+ free(d->header.c_name);
+ free(d);
+}
+
+static int link_to_name(char *link_name, char *link_target)
+{
+ int res = link(link_target, link_name);
+ return res;
+}
+
+struct inode_val {
+ unsigned long inode;
+ unsigned long major_num;
+ unsigned long minor_num;
+ char *file_name;
+};
+
+/* Inode hash table. Allocated by first call to add_inode. */
+static struct inode_val **hash_table = NULL;
+
+/* Size of current hash table. Initial size is 47. (47 = 2*22 + 3) */
+static int hash_size = 22;
+
+/* Number of elements in current hash table. */
+static int hash_num;
+
+/* Do the hash insert. Used in normal inserts and resizing the hash
+ table. It is guaranteed that there is room to insert the item.
+ NEW_VALUE is the pointer to the previously allocated inode, file
+ name association record. */
+
+static void hash_insert(struct inode_val *new_value)
+{
+ int start; /* Home position for the value. */
+ int temp; /* Used for rehashing. */
+
+ /* Hash function is node number modulo the table size. */
+ start = new_value->inode % hash_size;
+
+ /* Do the initial look into the table. */
+ if (hash_table[start] == NULL) {
+ hash_table[start] = new_value;
+ return;
+ }
+
+ /* If we get to here, the home position is full with a different inode
+ record. Do a linear search for the first NULL pointer and insert
+ the new item there. */
+ temp = (start + 1) % hash_size;
+ while (hash_table[temp] != NULL)
+ temp = (temp + 1) % hash_size;
+
+ /* Insert at the NULL. */
+ hash_table[temp] = new_value;
+}
+
+/* Associate FILE_NAME with the inode NODE_NUM. (Insert into hash table.) */
+
+static void
+add_inode(unsigned long node_num, char *file_name, unsigned long major_num,
+ unsigned long minor_num)
+{
+ struct inode_val *temp;
+
+ /* Create new inode record. */
+ temp = (struct inode_val *)xmalloc(sizeof(struct inode_val));
+ temp->inode = node_num;
+ temp->major_num = major_num;
+ temp->minor_num = minor_num;
+ temp->file_name = xstrdup(file_name);
+
+ /* Do we have to increase the size of (or initially allocate)
+ the hash table? */
+ if (hash_num == hash_size || hash_table == NULL) {
+ struct inode_val **old_table; /* Pointer to old table. */
+ int i; /* Index for re-insert loop. */
+
+ /* Save old table. */
+ old_table = hash_table;
+ if (old_table == NULL)
+ hash_num = 0;
+
+ /* Calculate new size of table and allocate it.
+ Sequence of table sizes is 47, 97, 197, 397, 797, 1597, 3197, 6397 ...
+ where 3197 and most of the sizes after 6397 are not prime. The other
+ numbers listed are prime. */
+ hash_size = 2 * hash_size + 3;
+ hash_table = (struct inode_val **)
+ xmalloc(hash_size * sizeof(struct inode_val *));
+ memset(hash_table, 0, hash_size * sizeof(struct inode_val *));
+
+ /* Insert the values from the old table into the new table. */
+ for (i = 0; i < hash_num; i++)
+ hash_insert(old_table[i]);
+
+ free(old_table);
+ }
+
+ /* Insert the new record and increment the count of elements in the
+ hash table. */
+ hash_insert(temp);
+ hash_num++;
+}
+
+static char *find_inode_file(unsigned long node_num, unsigned long major_num,
+ unsigned long minor_num)
+{
+ int start; /* Initial hash location. */
+ int temp; /* Rehash search variable. */
+
+ if (hash_table != NULL) {
+ /* Hash function is node number modulo the table size. */
+ start = node_num % hash_size;
+
+ /* Initial look into the table. */
+ if (hash_table[start] == NULL)
+ return NULL;
+ if (hash_table[start]->inode == node_num
+ && hash_table[start]->major_num == major_num
+ && hash_table[start]->minor_num == minor_num)
+ return hash_table[start]->file_name;
+
+ /* The home position is full with a different inode record.
+ Do a linear search terminated by a NULL pointer. */
+ for (temp = (start + 1) % hash_size;
+ hash_table[temp] != NULL && temp != start;
+ temp = (temp + 1) % hash_size) {
+ if (hash_table[temp]->inode == node_num
+ && hash_table[start]->major_num == major_num
+ && hash_table[start]->minor_num == minor_num)
+ return hash_table[temp]->file_name;
+ }
+ }
+ return NULL;
+}
+
+/* Try and create a hard link from FILE_NAME to another file
+ with the given major/minor device number and inode. If no other
+ file with the same major/minor/inode numbers is known, add this file
+ to the list of known files and associated major/minor/inode numbers
+ and return -1. If another file with the same major/minor/inode
+ numbers is found, try and create another link to it using
+ link_to_name, and return 0 for success and -1 for failure. */
+
+static int
+link_to_maj_min_ino(char *file_name, int st_dev_maj, int st_dev_min, int st_ino)
+{
+ int link_res;
+ char *link_name;
+ link_res = -1;
+ /* Is the file a link to a previously copied file? */
+ link_name = find_inode_file(st_ino, st_dev_maj, st_dev_min);
+ if (link_name == NULL)
+ add_inode(st_ino, file_name, st_dev_maj, st_dev_min);
+ else
+ link_res = link_to_name(file_name, link_name);
+ return link_res;
+}
+
+static void copyin_regular_file(struct new_cpio_header *file_hdr,
+ int in_file_des);
+
+static void warn_junk_bytes(long bytes_skipped)
+{
+ fprintf(stderr, "%s: warning: skipped %ld byte(s) of junk\n",
+ progname, bytes_skipped);
+}
+
+/* Skip the padding on IN_FILE_DES after a header or file,
+ up to the next header.
+ The number of bytes skipped is based on OFFSET -- the current offset
+ from the last start of a header (or file) -- and the current
+ header type. */
+
+static void tape_skip_padding(int in_file_des, int offset)
+{
+ int pad;
+ pad = (4 - (offset % 4)) % 4;
+
+ if (pad != 0)
+ tape_toss_input(in_file_des, pad);
+}
+
+static int
+try_existing_file(struct new_cpio_header *file_hdr, int in_file_des,
+ int *existing_dir)
+{
+ struct stat file_stat;
+
+ *existing_dir = false;
+ if (lstat(file_hdr->c_name, &file_stat) == 0) {
+ if (S_ISDIR(file_stat.st_mode)
+ && ((file_hdr->c_mode & S_IFMT) == S_IFDIR)) {
+ /* If there is already a directory there that
+ we are trying to create, don't complain about
+ it. */
+ *existing_dir = true;
+ return 0;
+ } else if (S_ISDIR(file_stat.st_mode)
+ ? rmdir(file_hdr->c_name)
+ : unlink(file_hdr->c_name)) {
+ fprintf(stderr, "%s: cannot remove current %s: %s\n",
+ progname, file_hdr->c_name, strerror(errno));
+ tape_toss_input(in_file_des, file_hdr->c_filesize);
+ tape_skip_padding(in_file_des, file_hdr->c_filesize);
+ return -1; /* Go to the next file. */
+ }
+ }
+ return 0;
+}
+
+/* The newc and crc formats store multiply linked copies of the same file
+ in the archive only once. The actual data is attached to the last link
+ in the archive, and the other links all have a filesize of 0. When a
+ file in the archive has multiple links and a filesize of 0, its data is
+ probably "attatched" to another file in the archive, so we can't create
+ it right away. We have to "defer" creating it until we have created
+ the file that has the data "attatched" to it. We keep a list of the
+ "defered" links on deferments. */
+
+struct deferment *deferments = NULL;
+
+/* Add a file header to the deferments list. For now they all just
+ go on one list, although we could optimize this if necessary. */
+
+static void defer_copyin(struct new_cpio_header *file_hdr)
+{
+ struct deferment *d;
+ d = create_deferment(file_hdr);
+ d->next = deferments;
+ deferments = d;
+ return;
+}
+
+/* We just created a file that (probably) has some other links to it
+ which have been defered. Go through all of the links on the deferments
+ list and create any which are links to this file. */
+
+static void create_defered_links(struct new_cpio_header *file_hdr)
+{
+ struct deferment *d;
+ struct deferment *d_prev;
+ int ino;
+ int maj;
+ int min;
+ int link_res;
+ ino = file_hdr->c_ino;
+ maj = file_hdr->c_dev_maj;
+ min = file_hdr->c_dev_min;
+ d = deferments;
+ d_prev = NULL;
+ while (d != NULL) {
+ if ((d->header.c_ino == ino) && (d->header.c_dev_maj == maj)
+ && (d->header.c_dev_min == min)) {
+ struct deferment *d_free;
+ link_res =
+ link_to_name(d->header.c_name, file_hdr->c_name);
+ if (link_res < 0) {
+ fprintf(stderr,
+ "%s: cannot link %s to %s: %s\n",
+ progname, d->header.c_name,
+ file_hdr->c_name, strerror(errno));
+ }
+ if (d_prev != NULL)
+ d_prev->next = d->next;
+ else
+ deferments = d->next;
+ d_free = d;
+ d = d->next;
+ free_deferment(d_free);
+ } else {
+ d_prev = d;
+ d = d->next;
+ }
+ }
+}
+
+/* If we had a multiply linked file that really was empty then we would
+ have defered all of its links, since we never found any with data
+ "attached", and they will still be on the deferment list even when
+ we are done reading the whole archive. Write out all of these
+ empty links that are still on the deferments list. */
+
+static void create_final_defers(void)
+{
+ struct deferment *d;
+ int link_res;
+ int out_file_des;
+ struct utimbuf times; /* For setting file times. */
+ /* Initialize this in case it has members we don't know to set. */
+ memset(&times, 0, sizeof(struct utimbuf));
+
+ for (d = deferments; d != NULL; d = d->next) {
+ /* Debian hack: A line, which could cause an endless loop, was
+ removed (97/1/2). It was reported by Ronald F. Guilmette to
+ the upstream maintainers. -BEM */
+ /* Debian hack: This was reported by Horst Knobloch. This bug has
+ been reported to "bug-gnu-utils@prep.ai.mit.edu". (99/1/6) -BEM
+ */
+ link_res = link_to_maj_min_ino(d->header.c_name,
+ d->header.c_dev_maj,
+ d->header.c_dev_min,
+ d->header.c_ino);
+ if (link_res == 0) {
+ continue;
+ }
+ out_file_des = open(d->header.c_name, O_CREAT | O_WRONLY, 0600);
+ if (out_file_des < 0) {
+ fprintf(stderr, "%s: open %s: %s\n",
+ progname, d->header.c_name, strerror(errno));
+ continue;
+ }
+
+ /* File is now copied; set attributes. */
+ if ((fchown(out_file_des, d->header.c_uid, d->header.c_gid) < 0)
+ && errno != EPERM)
+ fprintf(stderr, "%s: fchown %s: %s\n",
+ progname, d->header.c_name, strerror(errno));
+ /* chown may have turned off some permissions we wanted. */
+ if (fchmod(out_file_des, (int)d->header.c_mode) < 0)
+ fprintf(stderr, "%s: fchmod %s: %s\n",
+ progname, d->header.c_name, strerror(errno));
+
+ if (close(out_file_des) < 0)
+ fprintf(stderr, "%s: close %s: %s\n",
+ progname, d->header.c_name, strerror(errno));
+
+ }
+}
+
+static void
+copyin_regular_file(struct new_cpio_header *file_hdr, int in_file_des)
+{
+ int out_file_des; /* Output file descriptor. */
+
+ /* Can the current file be linked to a previously copied file? */
+ if (file_hdr->c_nlink > 1) {
+ int link_res;
+ if (file_hdr->c_filesize == 0) {
+ /* The newc and crc formats store multiply linked copies
+ of the same file in the archive only once. The
+ actual data is attached to the last link in the
+ archive, and the other links all have a filesize
+ of 0. Since this file has multiple links and a
+ filesize of 0, its data is probably attatched to
+ another file in the archive. Save the link, and
+ process it later when we get the actual data. We
+ can't just create it with length 0 and add the
+ data later, in case the file is readonly. We still
+ lose if its parent directory is readonly (and we aren't
+ running as root), but there's nothing we can do about
+ that. */
+ defer_copyin(file_hdr);
+ tape_toss_input(in_file_des, file_hdr->c_filesize);
+ tape_skip_padding(in_file_des, file_hdr->c_filesize);
+ return;
+ }
+ /* If the file has data (filesize != 0), then presumably
+ any other links have already been defer_copyin'ed(),
+ but GNU cpio version 2.0-2.2 didn't do that, so we
+ still have to check for links here (and also in case
+ the archive was created and later appeneded to). */
+ /* Debian hack: (97/1/2) This was reported by Ronald
+ F. Guilmette to the upstream maintainers. -BEM */
+ link_res = link_to_maj_min_ino(file_hdr->c_name,
+ file_hdr->c_dev_maj,
+ file_hdr->c_dev_min,
+ file_hdr->c_ino);
+ if (link_res == 0) {
+ tape_toss_input(in_file_des, file_hdr->c_filesize);
+ tape_skip_padding(in_file_des, file_hdr->c_filesize);
+ return;
+ }
+ }
+
+ /* If not linked, copy the contents of the file. */
+ out_file_des = open(file_hdr->c_name, O_CREAT | O_WRONLY, 0600);
+
+ if (out_file_des < 0) {
+ fprintf(stderr, "%s: open %s: %s\n",
+ progname, file_hdr->c_name, strerror(errno));
+ tape_toss_input(in_file_des, file_hdr->c_filesize);
+ tape_skip_padding(in_file_des, file_hdr->c_filesize);
+ return;
+ }
+
+ copy_files_tape_to_disk(in_file_des, out_file_des,
+ file_hdr->c_filesize);
+ disk_empty_output_buffer(out_file_des);
+
+ if (close(out_file_des) < 0)
+ fprintf(stderr, "%s: close %s: %s\n",
+ progname, file_hdr->c_name, strerror(errno));
+
+ /* File is now copied; set attributes. */
+ if ((chown(file_hdr->c_name, file_hdr->c_uid, file_hdr->c_gid) < 0)
+ && errno != EPERM)
+ fprintf(stderr, "%s: chown %s: %s\n",
+ progname, file_hdr->c_name, strerror(errno));
+
+ /* chown may have turned off some permissions we wanted. */
+ if (chmod(file_hdr->c_name, (int)file_hdr->c_mode) < 0)
+ fprintf(stderr, "%s: chmod %s: %s\n",
+ progname, file_hdr->c_name, strerror(errno));
+
+ tape_skip_padding(in_file_des, file_hdr->c_filesize);
+ if (file_hdr->c_nlink > 1) {
+ /* (see comment above for how the newc and crc formats
+ store multiple links). Now that we have the data
+ for this file, create any other links to it which
+ we defered. */
+ create_defered_links(file_hdr);
+ }
+}
+
+/* In general, we can't use the builtin `basename' function if available,
+ since it has different meanings in different environments.
+ In some environments the builtin `basename' modifies its argument.
+
+ Return the address of the last file name component of NAME. If
+ NAME has no file name components because it is all slashes, return
+ NAME if it is empty, the address of its last slash otherwise. */
+
+static char *base_name(char const *name)
+{
+ char const *base = name;
+ char const *p;
+
+ for (p = base; *p; p++) {
+ if (ISSLASH(*p)) {
+ /* Treat multiple adjacent slashes like a single slash. */
+ do
+ p++;
+ while (ISSLASH(*p));
+
+ /* If the file name ends in slash, use the trailing slash as
+ the basename if no non-slashes have been found. */
+ if (!*p) {
+ if (ISSLASH(*base))
+ base = p - 1;
+ break;
+ }
+
+ /* *P is a non-slash preceded by a slash. */
+ base = p;
+ }
+ }
+
+ return (char *)base;
+}
+
+/* Return the length of of the basename NAME. Typically NAME is the
+ value returned by base_name. Act like strlen (NAME), except omit
+ redundant trailing slashes. */
+
+static size_t base_len(char const *name)
+{
+ size_t len;
+
+ for (len = strlen(name); 1 < len && ISSLASH(name[len - 1]); len--)
+ continue;
+
+ return len;
+}
+
+/* Remove trailing slashes from PATH.
+ Return true if a trailing slash was removed.
+ This is useful when using filename completion from a shell that
+ adds a "/" after directory names (such as tcsh and bash), because
+ the Unix rename and rmdir system calls return an "Invalid argument" error
+ when given a path that ends in "/" (except for the root directory). */
+
+static bool strip_trailing_slashes(char *path)
+{
+ char *base = base_name(path);
+ char *base_lim = base + base_len(base);
+ bool had_slash = (*base_lim != '\0');
+ *base_lim = '\0';
+ return had_slash;
+}
+
+static void copyin_directory(struct new_cpio_header *file_hdr, int existing_dir)
+{
+ int res; /* Result of various function calls. */
+
+ /* Strip any trailing `/'s off the filename; tar puts
+ them on. We might as well do it here in case anybody
+ else does too, since they cause strange things to happen. */
+ strip_trailing_slashes(file_hdr->c_name);
+
+ /* Ignore the current directory. It must already exist,
+ and we don't want to change its permission, ownership
+ or time. */
+ if (file_hdr->c_name[0] == '.' && file_hdr->c_name[1] == '\0') {
+ return;
+ }
+
+ if (!existing_dir)
+ {
+ res = mkdir(file_hdr->c_name, file_hdr->c_mode);
+ } else
+ res = 0;
+ if (res < 0) {
+ /* In some odd cases where the file_hdr->c_name includes `.',
+ the directory may have actually been created by
+ create_all_directories(), so the mkdir will fail
+ because the directory exists. If that's the case,
+ don't complain about it. */
+ struct stat file_stat;
+ if ((errno != EEXIST) ||
+ (lstat(file_hdr->c_name, &file_stat) != 0) ||
+ !(S_ISDIR(file_stat.st_mode))) {
+ fprintf(stderr, "%s: lstat %s: %s\n",
+ progname, file_hdr->c_name, strerror(errno));
+ return;
+ }
+ }
+ if ((chown(file_hdr->c_name, file_hdr->c_uid, file_hdr->c_gid) < 0)
+ && errno != EPERM)
+ fprintf(stderr, "%s: chown %s: %s\n",
+ progname, file_hdr->c_name, strerror(errno));
+ /* chown may have turned off some permissions we wanted. */
+ if (chmod(file_hdr->c_name, (int)file_hdr->c_mode) < 0)
+ fprintf(stderr, "%s: chmod %s: %s\n",
+ progname, file_hdr->c_name, strerror(errno));
+}
+
+static void copyin_device(struct new_cpio_header *file_hdr)
+{
+ int res; /* Result of various function calls. */
+
+ if (file_hdr->c_nlink > 1) {
+ int link_res;
+ /* Debian hack: This was reported by Horst
+ Knobloch. This bug has been reported to
+ "bug-gnu-utils@prep.ai.mit.edu". (99/1/6) -BEM */
+ link_res = link_to_maj_min_ino(file_hdr->c_name,
+ file_hdr->c_dev_maj,
+ file_hdr->c_dev_min,
+ file_hdr->c_ino);
+ if (link_res == 0) {
+ return;
+ }
+ }
+
+ res = mknod(file_hdr->c_name, file_hdr->c_mode,
+ makedev(file_hdr->c_rdev_maj, file_hdr->c_rdev_min));
+ if (res < 0) {
+ fprintf(stderr, "%s: mknod %s: %s\n", progname,
+ file_hdr->c_name, strerror(errno));
+ return;
+ }
+ if ((chown(file_hdr->c_name, file_hdr->c_uid, file_hdr->c_gid) < 0)
+ && errno != EPERM)
+ fprintf(stderr, "%s: chown %s: %s\n", progname,
+ file_hdr->c_name, strerror(errno));
+ /* chown may have turned off some permissions we wanted. */
+ if (chmod(file_hdr->c_name, file_hdr->c_mode) < 0)
+ fprintf(stderr, "%s: chmod %s: %s\n", progname,
+ file_hdr->c_name, strerror(errno));
+}
+
+static void copyin_link(struct new_cpio_header *file_hdr, int in_file_des)
+{
+ char *link_name = NULL; /* Name of hard and symbolic links. */
+ int res; /* Result of various function calls. */
+
+ link_name = (char *)xmalloc(file_hdr->c_filesize + 1);
+ link_name[file_hdr->c_filesize] = '\0';
+ tape_buffered_read(link_name, in_file_des, file_hdr->c_filesize);
+ tape_skip_padding(in_file_des, file_hdr->c_filesize);
+
+ res = symlink(link_name, file_hdr->c_name);
+ if (res < 0) {
+ fprintf(stderr, "%s: symlink %s: %s\n",
+ progname, file_hdr->c_name, strerror(errno));
+ free(link_name);
+ return;
+ }
+ if ((lchown(file_hdr->c_name, file_hdr->c_uid, file_hdr->c_gid) < 0)
+ && errno != EPERM) {
+ fprintf(stderr, "%s: lchown %s: %s\n",
+ progname, file_hdr->c_name, strerror(errno));
+ }
+ free(link_name);
+}
+
+static void copyin_file(struct new_cpio_header *file_hdr, int in_file_des)
+{
+ int existing_dir;
+
+ if (try_existing_file(file_hdr, in_file_des, &existing_dir) < 0)
+ return;
+
+ /* Do the real copy or link. */
+ switch (file_hdr->c_mode & S_IFMT) {
+ case S_IFREG:
+ copyin_regular_file(file_hdr, in_file_des);
+ break;
+
+ case S_IFDIR:
+ copyin_directory(file_hdr, existing_dir);
+ break;
+
+ case S_IFCHR:
+ case S_IFBLK:
+ case S_IFSOCK:
+ case S_IFIFO:
+ copyin_device(file_hdr);
+ break;
+
+ case S_IFLNK:
+ copyin_link(file_hdr, in_file_des);
+ break;
+
+ default:
+ fprintf(stderr, "%s: %s: unknown file type\n",
+ progname, file_hdr->c_name);
+ tape_toss_input(in_file_des, file_hdr->c_filesize);
+ tape_skip_padding(in_file_des, file_hdr->c_filesize);
+ }
+}
+
+/* Fill in FILE_HDR by reading a new-format ASCII format cpio header from
+ file descriptor IN_DES, except for the magic number, which is
+ already filled in. */
+
+static void read_in_new_ascii(struct new_cpio_header *file_hdr, int in_des)
+{
+ char ascii_header[13*8], *ah, hexbuf[9];
+ int i;
+
+ tape_buffered_read(ascii_header, in_des, 13*8);
+ ah = ascii_header;
+ hexbuf[8] = '\0';
+ for (i = 0; i < 13; i++) {
+ memcpy(hexbuf, ah, 8);
+ file_hdr->c_hdr[i] = strtoul(hexbuf, NULL, 16);
+ ah += 8;
+ }
+
+ /* Sizes > LONG_MAX can currently result in integer overflow
+ in various places. Fail if name is too large. */
+ if (file_hdr->c_namesize > LONG_MAX) {
+ fprintf(stderr, "%s: name size out of range\n",
+ progname);
+ exit(1);
+ }
+
+ /* Read file name from input. */
+ free(file_hdr->c_name);
+ file_hdr->c_name = (char *)xmalloc(file_hdr->c_namesize);
+ tape_buffered_read(file_hdr->c_name, in_des,
+ (long)file_hdr->c_namesize);
+
+ /* In SVR4 ASCII format, the amount of space allocated for the header
+ is rounded up to the next long-word, so we might need to drop
+ 1-3 bytes. */
+ tape_skip_padding(in_des, file_hdr->c_namesize + 110);
+
+ /* Fail if file is too large. We could check this earlier
+ but it's helpful to report the name. */
+ if (file_hdr->c_filesize > LONG_MAX) {
+ fprintf(stderr, "%s: %s: file size out of range\n",
+ progname, file_hdr->c_name);
+ exit(1);
+ }
+}
+
+/* Return 16-bit integer I with the bytes swapped. */
+#define swab_short(i) ((((i) << 8) & 0xff00) | (((i) >> 8) & 0x00ff))
+
+/* Read the header, including the name of the file, from file
+ descriptor IN_DES into FILE_HDR. */
+
+static void read_in_header(struct new_cpio_header *file_hdr, int in_des)
+{
+ long bytes_skipped = 0; /* Bytes of junk found before magic number. */
+
+ /* Search for a valid magic number. */
+
+ file_hdr->c_tar_linkname = NULL;
+
+ tape_buffered_read((char *)file_hdr, in_des, 6L);
+ while (1) {
+ if (!strncmp((char *)file_hdr, "070702", 6)
+ || !strncmp((char *)file_hdr, "070701", 6))
+ {
+ if (bytes_skipped > 0)
+ warn_junk_bytes(bytes_skipped);
+
+ read_in_new_ascii(file_hdr, in_des);
+ break;
+ }
+ bytes_skipped++;
+ memmove((char *)file_hdr, (char *)file_hdr + 1, 5);
+ tape_buffered_read((char *)file_hdr + 5, in_des, 1L);
+ }
+}
+
+/* Read the collection from standard input and create files
+ in the file system. */
+
+static void process_copy_in(void)
+{
+ char done = false; /* True if trailer reached. */
+ struct new_cpio_header file_hdr; /* Output header information. */
+ int in_file_des; /* Input file descriptor. */
+
+ /* Initialize the copy in. */
+ file_hdr.c_name = NULL;
+
+ /* only from stdin */
+ in_file_des = 0;
+
+ /* While there is more input in the collection, process the input. */
+ while (!done) {
+ /* Start processing the next file by reading the header. */
+ read_in_header(&file_hdr, in_file_des);
+
+ /* Is this the header for the TRAILER file? */
+ if (strcmp("TRAILER!!!", file_hdr.c_name) == 0) {
+ done = true;
+ break;
+ }
+
+ /* Copy the input file into the directory structure. */
+
+ copyin_file(&file_hdr, in_file_des);
+
+ if (dot_flag)
+ fputc('.', stderr);
+ }
+
+ if (dot_flag)
+ fputc('\n', stderr);
+
+ create_final_defers();
+
+}
+
+/* Initialize the input and output buffers to their proper size and
+ initialize all variables associated with the input and output
+ buffers. */
+
+static void initialize_buffers(void)
+{
+ int in_buf_size, out_buf_size;
+
+ /* Make sure the input buffer can always hold 2 blocks and that it
+ is big enough to hold 1 tar record (512 bytes) even if it
+ is not aligned on a block boundary. The extra buffer space
+ is needed by process_copyin and peek_in_buf to automatically
+ figure out what kind of archive it is reading. */
+ if (io_block_size >= 512)
+ in_buf_size = 2 * io_block_size;
+ else
+ in_buf_size = 1024;
+ out_buf_size = DISK_IO_BLOCK_SIZE;
+
+ input_buffer = (char *)xmalloc(in_buf_size);
+ in_buff = input_buffer;
+ input_buffer_size = in_buf_size;
+ input_size = 0;
+ input_bytes = 0;
+
+ output_buffer = (char *)xmalloc(out_buf_size);
+ out_buff = output_buffer;
+ output_size = 0;
+ output_bytes = 0;
+
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+ int extract_flag = false;
+
+ progname = argv[0];
+
+ do {
+ c = getopt(argc, argv, "iV");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'V':
+ dot_flag = true;
+ break;
+
+ case 'i':
+ extract_flag = true;
+ break;
+ case '?':
+ fprintf(stderr,
+ "%s: not implemented or invalid option -%c\n",
+ progname, optopt);
+ exit(1);
+
+ }
+ } while (1);
+
+ if (extract_flag) {
+ initialize_buffers();
+
+ process_copy_in();
+ } else {
+ fprintf(stderr, "Usage: %s [-V] -i [< archive]\n", progname);
+ exit(1);
+ }
+
+ return 0;
+}
diff --git a/usr/utils/dd.c b/usr/utils/dd.c
new file mode 100644
index 0000000..706b8c3
--- /dev/null
+++ b/usr/utils/dd.c
@@ -0,0 +1,540 @@
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+static char *progname;
+
+struct option {
+ const char *opt;
+ char *str;
+ char *arg;
+};
+
+struct conv {
+ const char str[8];
+ unsigned int set;
+ unsigned int exclude;
+};
+
+#define CONV_BLOCK (1<<0)
+#define CONV_UNBLOCK (1<<1)
+
+#define CONV_LCASE (1<<2)
+#define CONV_UCASE (1<<3)
+
+#define CONV_SWAB (1<<4)
+#define CONV_NOERROR (1<<5)
+#define CONV_NOTRUNC (1<<6)
+#define CONV_SYNC (1<<7)
+
+static struct option options[] = {
+ {"bs", NULL, NULL},
+#define OPT_BS (&options[0])
+ {"cbs", NULL, NULL},
+#define OPT_CBS (&options[1])
+ {"conv", NULL, NULL},
+#define OPT_CONV (&options[2])
+ {"count", NULL, NULL},
+#define OPT_COUNT (&options[3])
+ {"ibs", NULL, NULL},
+#define OPT_IBS (&options[4])
+ {"if", NULL, NULL},
+#define OPT_IF (&options[5])
+ {"obs", NULL, NULL},
+#define OPT_OBS (&options[6])
+ {"of", NULL, NULL},
+#define OPT_OF (&options[7])
+ {"seek", NULL, NULL},
+#define OPT_SEEK (&options[8])
+ {"skip", NULL, NULL}
+#define OPT_SKIP (&options[9])
+};
+
+static const struct conv conv_opts[] = {
+ {"block", CONV_BLOCK, CONV_UNBLOCK},
+ {"unblock", CONV_UNBLOCK, CONV_BLOCK},
+ {"lcase", CONV_LCASE, CONV_UCASE},
+ {"ucase", CONV_UCASE, CONV_LCASE},
+ {"swab", CONV_SWAB, 0},
+ {"noerror", CONV_NOERROR, 0},
+ {"notrunc", CONV_NOTRUNC, 0},
+ {"sync", CONV_SYNC, 0},
+};
+
+static size_t cbs;
+static unsigned int conv;
+static unsigned int count;
+static size_t ibs = 512;
+static size_t obs = 512;
+static unsigned int seek;
+static unsigned int skip;
+static char *in_buf;
+static char *out_buf;
+
+static size_t parse_bs(struct option *opt)
+{
+ unsigned long val, realval = 1;
+ char *str = opt->str;
+ int err = 0;
+
+ do {
+ char *s = str;
+ val = strtoul(str, &str, 10);
+ if (s == str || (val == ULONG_MAX && errno == ERANGE)) {
+ err = 1;
+ break;
+ }
+
+ /*
+ * This option may be followed by
+ * 'b', 'k' or 'x'
+ */
+ if (*str == 'b') {
+ val *= 512;
+ str++;
+ } else if (*str == 'k') {
+ val *= 1024;
+ str++;
+ }
+ realval *= val;
+ if (*str != 'x')
+ break;
+ str++;
+ } while (1);
+
+ if (*str != '\0')
+ err = 1;
+
+ if (err) {
+ fprintf(stderr, "%s: bad operand `%s'\n", progname, opt->arg);
+ exit(1);
+ }
+
+ return (size_t) realval;
+}
+
+static unsigned int parse_num(struct option *opt)
+{
+ unsigned long val;
+ char *str = opt->str;
+
+ val = strtoul(str, &str, 10);
+ if (str == opt->str || (val == ULONG_MAX && errno == ERANGE) ||
+ val > UINT_MAX) {
+ fprintf(stderr, "%s: bad operand `%s'\n", progname, opt->arg);
+ exit(1);
+ }
+
+ return (unsigned int)val;
+}
+
+static int parse_options(int argc, char *argv[])
+{
+ unsigned int i;
+ char *p, *s;
+ int arg;
+
+ /*
+ * We cheat here; we don't parse the operand values
+ * themselves here. We merely split the operands
+ * up. This means that bs=foo bs=1 won't produce
+ * an error.
+ */
+ for (arg = 1; arg < argc; arg++) {
+ unsigned int len;
+
+ s = strchr(argv[arg], '=');
+ if (!s)
+ s = argv[arg]; /* don't recognise this arg */
+
+ len = s - argv[arg];
+ for (i = 0; i < ARRAY_SIZE(options); i++) {
+ if (strncmp(options[i].opt, argv[arg], len) != 0)
+ continue;
+
+ options[i].str = s + 1;
+ options[i].arg = argv[arg];
+ break;
+ }
+
+ if (i == ARRAY_SIZE(options)) {
+ fprintf(stderr, "%s: bad operand `%s'\n",
+ progname, argv[arg]);
+ return 1;
+ }
+ }
+
+ /*
+ * Translate numeric operands.
+ */
+ if (OPT_IBS->str)
+ ibs = parse_bs(OPT_IBS);
+ if (OPT_OBS->str)
+ obs = parse_bs(OPT_OBS);
+ if (OPT_CBS->str)
+ cbs = parse_bs(OPT_CBS);
+ if (OPT_COUNT->str)
+ count = parse_num(OPT_COUNT);
+ if (OPT_SEEK->str)
+ seek = parse_num(OPT_SEEK);
+ if (OPT_SKIP->str)
+ skip = parse_num(OPT_SKIP);
+
+ /*
+ * If bs= is specified, it overrides ibs= and obs=
+ */
+ if (OPT_BS->str)
+ ibs = obs = parse_bs(OPT_BS);
+
+ /*
+ * And finally conv=
+ */
+ if (OPT_CONV->str) {
+ p = OPT_CONV->str;
+
+ while ((s = strsep(&p, ",")) != NULL) {
+ for (i = 0; i < ARRAY_SIZE(conv_opts); i++) {
+ if (strcmp(s, conv_opts[i].str) != 0)
+ continue;
+ conv &= ~conv_opts[i].exclude;
+ conv |= conv_opts[i].set;
+ break;
+ }
+
+ if (i == ARRAY_SIZE(conv_opts)) {
+ fprintf(stderr, "%s: bad conversion `%s'\n",
+ progname, s);
+ return 1;
+ }
+ }
+ }
+
+ if (conv & (CONV_BLOCK | CONV_UNBLOCK) && cbs == 0) {
+ fprintf(stderr, "%s: block/unblock conversion with zero cbs\n",
+ progname);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int safe_read(int fd, void *buf, size_t size)
+{
+ int ret, count = 0;
+ char *p = buf;
+
+ while (size) {
+ ret = read(fd, p, size);
+
+ /*
+ * If we got EINTR, go again.
+ */
+ if (ret == -1 && errno == EINTR)
+ continue;
+
+ /*
+ * If we encountered an error condition
+ * or read 0 bytes (EOF) return what we
+ * have.
+ */
+ if (ret == -1 || ret == 0)
+ return count ? count : ret;
+
+ /*
+ * We read some bytes.
+ */
+ count += ret;
+ size -= ret;
+ p += ret;
+ }
+
+ return count;
+}
+
+static int skip_blocks(int fd, void *buf, unsigned int blks, size_t size)
+{
+ unsigned int blk;
+ int ret = 0;
+
+ /*
+ * Try to seek.
+ */
+ for (blk = 0; blk < blks; blk++) {
+ ret = lseek(fd, size, SEEK_CUR);
+ if (ret == -1)
+ break;
+ }
+
+ /*
+ * If we failed to seek, read instead.
+ * FIXME: we don't handle short reads here, or
+ * EINTR correctly.
+ */
+ if (blk == 0 && ret == -1 && errno == ESPIPE) {
+ for (blk = 0; blk < blks; blk++) {
+ ret = safe_read(fd, buf, size);
+ if (ret != (int)size)
+ break;
+ }
+ }
+
+ if (ret == -1) {
+ perror("seek/skip");
+ return 1;
+ }
+ return 0;
+}
+
+struct stats {
+ unsigned int in_full;
+ unsigned int in_partial;
+ unsigned int out_full;
+ unsigned int out_partial;
+ unsigned int truncated;
+};
+
+static int do_dd(int rd, int wr, struct stats *stats)
+{
+ unsigned int i;
+ int ret;
+ int fill_val = 0;
+ size_t out_size = 0;
+ size_t in_size;
+ char *buf;
+
+ if (conv & (CONV_BLOCK | CONV_UNBLOCK))
+ fill_val = ' ';
+
+ while (!OPT_COUNT->str || count-- != 0) {
+ buf = in_buf;
+
+ /*
+ * 1. read ibs-sized buffer
+ */
+ in_size = ret = read(rd, in_buf, ibs);
+ if (ret == -1 || (ret == 0 && (conv & CONV_NOERROR) == 0))
+ break;
+
+ if (in_size == ibs) {
+ stats->in_full++;
+ } else {
+ stats->in_partial++;
+
+ /*
+ * 2. zero (or append spaces)
+ */
+ if (conv & CONV_SYNC) {
+ memset(in_buf + in_size, fill_val,
+ ibs - in_size);
+ in_size = ibs;
+ }
+ }
+
+ /*
+ * 4. swab conversion. With an odd number of bytes,
+ * last byte does not get swapped.
+ */
+ if (conv & CONV_SWAB) {
+ char c;
+
+ for (i = 1; i < in_size; i += 2) {
+ c = in_buf[i - 1];
+ in_buf[i - 1] = in_buf[i];
+ in_buf[i] = c;
+ }
+ }
+
+ /*
+ * 5. remaining conversions.
+ */
+ if (conv & CONV_LCASE)
+ for (i = 0; i < in_size; i++)
+ in_buf[i] = tolower(in_buf[i]);
+
+ if (conv & CONV_UCASE)
+ for (i = 0; i < in_size; i++)
+ in_buf[i] = toupper(in_buf[i]);
+
+ /* block/unblock ? */
+
+ /*
+ * 6. Aggregate into obs sized buffers.
+ * If the in_size is obs-sized and we have no
+ * data waiting, just write "buf" to the output.
+ */
+ if (out_size == 0 && in_size == obs) {
+ write(wr, buf, obs);
+ stats->out_full++;
+ } else {
+ /*
+ * We had data waiting, or we didn't have an
+ * obs-sized input block. We need to append
+ * the input data to the output buffer.
+ */
+ unsigned int space;
+ char *in_ptr = in_buf;
+
+ do {
+ space = obs - out_size;
+ if (space > in_size)
+ space = in_size;
+
+ memcpy(out_buf + out_size, in_ptr, space);
+ out_size += space;
+ in_size -= space;
+ in_ptr += space;
+
+ if (out_size == obs) {
+ write(wr, out_buf, obs);
+ stats->out_full++;
+ out_size = 0;
+ }
+ } while (out_size == 0 && in_size);
+
+ if (in_size) {
+ memcpy(out_buf, in_ptr, in_size);
+ out_size = in_size;
+ }
+ }
+ }
+
+ if (out_size) {
+ write(wr, out_buf, out_size);
+ stats->out_partial++;
+ }
+
+ return 0;
+}
+
+static sigjmp_buf jmp;
+
+static void sigint_handler(int sig)
+{
+ siglongjmp(jmp, -sig);
+}
+
+static int dd(int rd_fd, int wr_fd, struct stats *stats)
+{
+ int ret;
+
+ ret = sigsetjmp(jmp, 1);
+ if (ret == 0) {
+ sysv_signal(SIGINT, sigint_handler);
+ ret = do_dd(rd_fd, wr_fd, stats);
+ }
+
+ sysv_signal(SIGINT, SIG_DFL);
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ struct stats stats;
+ int ret;
+ int rd_fd = 0, wr_fd = 1;
+
+ progname = argv[0];
+
+ ret = parse_options(argc, argv);
+ if (ret)
+ return ret;
+
+ if (conv & (CONV_BLOCK | CONV_UNBLOCK)) {
+ fprintf(stderr, "%s: block/unblock not implemented\n",
+ progname);
+ return 1;
+ }
+
+ in_buf = malloc(ibs);
+ if (!in_buf) {
+ perror("malloc ibs");
+ return 1;
+ }
+
+ out_buf = malloc(obs);
+ if (!out_buf) {
+ perror("malloc obs");
+ return 1;
+ }
+
+ /*
+ * Open the input file, if specified.
+ */
+ if (OPT_IF->str) {
+ rd_fd = open(OPT_IF->str, O_RDONLY);
+ if (rd_fd == -1) {
+ perror("open input file");
+ return 1;
+ }
+ }
+
+ /*
+ * Open the output file, if specified.
+ */
+ if (OPT_OF->str) {
+ int flags = O_WRONLY|O_CREAT;
+ flags |= (conv & CONV_NOTRUNC) ? 0 : O_TRUNC;
+ wr_fd = open(OPT_OF->str, flags, 0666);
+ if (wr_fd == -1) {
+ perror("open output file");
+ close(rd_fd);
+ return 1;
+ }
+ }
+
+ /*
+ * Skip obs-sized blocks of output file.
+ */
+ if (OPT_SEEK->str && skip_blocks(wr_fd, out_buf, seek, obs)) {
+ close(rd_fd);
+ close(wr_fd);
+ return 1;
+ }
+
+ /*
+ * Skip ibs-sized blocks of input file.
+ */
+ if (OPT_SKIP->str && skip_blocks(rd_fd, in_buf, skip, ibs)) {
+ close(rd_fd);
+ close(wr_fd);
+ return 1;
+ }
+
+ memset(&stats, 0, sizeof(stats));
+
+ /*
+ * Do the real work
+ */
+ ret = dd(rd_fd, wr_fd, &stats);
+
+ if (close(rd_fd) == -1)
+ perror(OPT_IF->str ? OPT_IF->str : "stdin");
+ if (close(wr_fd) == -1)
+ perror(OPT_OF->str ? OPT_OF->str : "stdout");
+
+ fprintf(stderr, "%u+%u records in\n", stats.in_full, stats.in_partial);
+ fprintf(stderr, "%u+%u records out\n",
+ stats.out_full, stats.out_partial);
+ if (stats.truncated)
+ fprintf(stderr, "%u truncated record%s\n",
+ stats.truncated, stats.truncated == 1 ? "" : "s");
+
+ /*
+ * ret will be -SIGINT if we got a SIGINT. Raise
+ * the signal again to cause us to terminate with
+ * SIGINT status.
+ */
+ if (ret == -SIGINT)
+ raise(SIGINT);
+
+ return ret;
+}
diff --git a/usr/utils/dmesg.c b/usr/utils/dmesg.c
new file mode 100644
index 0000000..1960713
--- /dev/null
+++ b/usr/utils/dmesg.c
@@ -0,0 +1,79 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <sys/klog.h>
+
+static void usage(char *name)
+{
+ fprintf(stderr, "usage: %s [-c]\n", name);
+}
+
+int main(int argc, char *argv[])
+{
+ char *buf = NULL;
+ const char *p;
+ int c;
+ int bufsz = 0;
+ int cmd = 3; /* Read all messages remaining in the ring buffer */
+ int len = 0;
+ int opt;
+ int newline;
+
+ while ((opt = getopt(argc, argv, "c")) != -1) {
+ switch (opt) {
+ /* Read and clear all messages remaining in the ring buffer */
+ case 'c':
+ cmd = 4;
+ break;
+ case '?':
+ default:
+ usage(argv[0]);
+ exit(1);
+ }
+ }
+
+ if (!bufsz) {
+ len = klogctl(10, NULL, 0); /* Get size of log buffer */
+ if (len > 0)
+ bufsz = len;
+ }
+
+ if (bufsz) {
+ int sz = bufsz + 8;
+
+ buf = (char *)malloc(sz);
+ len = klogctl(cmd, buf, sz);
+ }
+
+ if (len < 0) {
+ perror("klogctl");
+ exit(1);
+ }
+
+ newline = 1;
+ p = buf;
+ while ((c = *p)) {
+ switch (c) {
+ case '\n':
+ newline = 1;
+ putchar(c);
+ p++;
+ break;
+ case '<':
+ if (newline && isdigit(p[1]) && p[2] == '>') {
+ p += 3;
+ break;
+ }
+ /* else fall through */
+ default:
+ newline = 0;
+ putchar(c);
+ p++;
+ }
+ }
+ if (!newline)
+ putchar('\n');
+
+ return 0;
+}
diff --git a/usr/utils/false.c b/usr/utils/false.c
new file mode 100644
index 0000000..2c3243a
--- /dev/null
+++ b/usr/utils/false.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+ return 1;
+}
diff --git a/usr/utils/file_mode.c b/usr/utils/file_mode.c
new file mode 100644
index 0000000..48f7f43
--- /dev/null
+++ b/usr/utils/file_mode.c
@@ -0,0 +1,143 @@
+#include <sys/stat.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "file_mode.h"
+
+extern char *progname;
+
+mode_t parse_file_mode(char *arg, mode_t mode, mode_t sumask)
+{
+ char *clause;
+
+ if (isdigit(*arg) && *arg < '8') {
+ unsigned long num;
+
+ num = strtoul(arg, NULL, 8);
+ if ((num == ULONG_MAX && errno == ERANGE) || num > 07777) {
+ fprintf(stderr, "%s: invalid mode `%s'\n", progname,
+ arg);
+ exit(255);
+ }
+ return (mode_t) num;
+ }
+
+ while ((clause = strsep(&arg, ",")) != NULL) {
+ mode_t who = 0;
+ int action;
+ char *p = clause;
+
+ /*
+ * Parse the who list. Optional.
+ */
+ while (1) {
+ switch (*p++) {
+ case 'u':
+ who |= S_IRWXU | S_ISUID;
+ continue;
+ case 'g':
+ who |= S_IRWXG | S_ISGID;
+ continue;
+ case 'o':
+ who |= S_IRWXO | S_ISVTX;
+ continue;
+ case 'a':
+ who =
+ S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID |
+ S_ISGID | S_ISVTX;
+ continue;
+ }
+ /* undo the increment above */
+ p--;
+ break;
+ }
+
+ if (who == 0)
+ who = (~sumask) | S_ISVTX;
+
+ /*
+ * Parse an action list. Must be at least one action.
+ */
+ while (*p) {
+ mode_t perm = 0;
+
+ /*
+ * Parse the action
+ */
+ action = *p;
+ if (action == '+' || action == '-' || action == '=')
+ p++;
+
+ /*
+ * Parse perm
+ */
+ while (*p) {
+ switch (*p++) {
+ case 'r':
+ perm |= S_IRUSR | S_IRGRP | S_IROTH;
+ continue;
+ case 'w':
+ perm |= S_IWUSR | S_IWGRP | S_IWOTH;
+ continue;
+ case 'x':
+ perm |= S_IXUSR | S_IXGRP | S_IXOTH;
+ continue;
+ case 'X':
+ perm |= S_ISVTX;
+ continue;
+ case 's':
+ perm |= S_ISUID | S_ISGID;
+ continue;
+ case 'u':
+ perm = mode & S_IRWXU;
+ perm |= perm >> 3 | perm >> 6;
+ if (mode & S_ISUID)
+ perm |= S_ISGID;
+ continue;
+ case 'g':
+ perm = mode & S_IRWXG;
+ perm |= perm << 3 | perm >> 3;
+ if (mode & S_ISGID)
+ perm |= S_ISUID;
+ continue;
+ case 'o':
+ perm = mode & S_IRWXO;
+ perm |= perm << 6 | perm << 3;
+ continue;
+ }
+ /* undo the increment above */
+ p--;
+ break;
+ }
+
+ perm &= who;
+
+ switch (action) {
+ case '+':
+ mode |= perm;
+ continue;
+
+ case '-':
+ mode &= ~perm;
+ continue;
+
+ case '=':
+ mode &= ~who;
+ mode |= perm;
+ continue;
+ }
+
+ if (!action)
+ break;
+ fprintf(stderr, "%s: invalid mode `%s'\n", progname,
+ clause);
+ exit(255);
+ }
+ }
+
+ return mode;
+}
diff --git a/usr/utils/file_mode.h b/usr/utils/file_mode.h
new file mode 100644
index 0000000..364f603
--- /dev/null
+++ b/usr/utils/file_mode.h
@@ -0,0 +1,6 @@
+#ifndef UTILS_FILE_MODE_H
+#define UTILS_FILE_MODE_H
+
+mode_t parse_file_mode(char *arg, mode_t mode, mode_t sumask);
+
+#endif /* UTILS_FILE_MODE_H */
diff --git a/usr/utils/halt.c b/usr/utils/halt.c
new file mode 100644
index 0000000..368f095
--- /dev/null
+++ b/usr/utils/halt.c
@@ -0,0 +1,64 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/reboot.h>
+#include <klibc/compiler.h>
+
+static __noreturn usage(void)
+{
+ static char mesg[] = "Usage: {halt|reboot|poweroff} [-n] [reboot-arg]\n";
+ write(2, mesg, sizeof(mesg) - 1);
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ int cmd = 0; /* initalize to shut gcc up */
+ int do_sync = 1;
+ char *ptr, *ptr2;
+ char *reboot_arg = NULL;
+
+ /* Which action (program name)? */
+ ptr2 = ptr = argv[0];
+ while (*ptr2)
+ if (*ptr2++ == '/')
+ ptr = ptr2;
+ if (*ptr == 'r')
+ cmd = LINUX_REBOOT_CMD_RESTART;
+ else if (*ptr == 'h')
+ cmd = LINUX_REBOOT_CMD_HALT;
+ else if (*ptr == 'p')
+ cmd = LINUX_REBOOT_CMD_POWER_OFF;
+ else
+ usage();
+
+ /* Walk options */
+ while (*++argv)
+ if (**argv == '-') {
+ switch (*++*argv) {
+ case 'f':
+ break; /* -f assumed */
+ case 'n':
+ do_sync = 0;
+ break;
+ default:
+ usage();
+ }
+ } else if (cmd == LINUX_REBOOT_CMD_RESTART) {
+ reboot_arg = *argv;
+ cmd = LINUX_REBOOT_CMD_RESTART2;
+ } else {
+ usage(); /* args, not reboot == error */
+ }
+
+ if (do_sync)
+ sync();
+ reboot(LINUX_REBOOT_CMD_CAD_OFF, NULL); /* Enable CTRL+ALT+DEL */
+ if (!reboot(cmd, reboot_arg)) {
+ /* Success. Currently, CMD_HALT returns, so stop the world */
+ /* kill(-1, SIGSTOP); */
+ kill(getpid(), SIGSTOP);
+ }
+ write(2, "failed.\n", 8);
+ return 1;
+}
diff --git a/usr/utils/insmod.c b/usr/utils/insmod.c
new file mode 100644
index 0000000..47b5880
--- /dev/null
+++ b/usr/utils/insmod.c
@@ -0,0 +1,140 @@
+/* insmod.c: insert a module into the kernel.
+ Copyright (C) 2001 Rusty Russell.
+ Copyright (C) 2002 Rusty Russell, IBM Corporation.
+
+ 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
+*/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#define streq(a,b) (strcmp((a),(b)) == 0)
+
+/* This really needs to be in a header file... */
+extern long init_module(void *, unsigned long, const char *);
+
+static void print_usage(const char *progname)
+{
+ fprintf(stderr, "Usage: %s filename [args]\n", progname);
+ exit(1);
+}
+
+/* We use error numbers in a loose translation... */
+static const char *moderror(int err)
+{
+ switch (err) {
+ case ENOEXEC:
+ return "Invalid module format";
+ case ENOENT:
+ return "Unknown symbol in module";
+ case ESRCH:
+ return "Module has wrong symbol version";
+ case EINVAL:
+ return "Invalid parameters";
+ default:
+ return strerror(err);
+ }
+}
+
+static void *grab_file(const char *filename, unsigned long *size)
+{
+ unsigned int max = 16384;
+ int ret, fd;
+ void *buffer = malloc(max);
+
+ if (streq(filename, "-"))
+ fd = dup(STDIN_FILENO);
+ else
+ fd = open(filename, O_RDONLY, 0);
+
+ if (fd < 0)
+ return NULL;
+
+ *size = 0;
+ while ((ret = read(fd, buffer + *size, max - *size)) > 0) {
+ *size += ret;
+ if (*size == max)
+ buffer = realloc(buffer, max *= 2);
+ }
+ if (ret < 0) {
+ free(buffer);
+ buffer = NULL;
+ }
+ close(fd);
+ return buffer;
+}
+
+int main(int argc, char *argv[])
+{
+ int i;
+ long int ret;
+ unsigned long len;
+ void *file;
+ char *filename, *options = strdup("");
+ char *progname = argv[0];
+
+ if (argv[1] && (streq(argv[1], "--version") || streq(argv[1], "-V"))) {
+ puts("klibc insmod");
+ exit(0);
+ }
+
+ /* Ignore old options, for backwards compat. */
+ while (argv[1] && (streq(argv[1], "-p")
+ || streq(argv[1], "-s")
+ || streq(argv[1], "-f"))) {
+ argv++;
+ argc--;
+ }
+
+ filename = argv[1];
+ if (!filename)
+ print_usage(progname);
+
+ /* Rest is options */
+ for (i = 2; i < argc; i++) {
+ options = realloc(options,
+ strlen(options) + 2 + strlen(argv[i]) + 2);
+ /* Spaces handled by "" pairs, but no way of escaping
+ quotes */
+ if (strchr(argv[i], ' '))
+ strcat(options, "\"");
+ strcat(options, argv[i]);
+ if (strchr(argv[i], ' '))
+ strcat(options, "\"");
+ strcat(options, " ");
+ }
+
+ file = grab_file(filename, &len);
+ if (!file) {
+ fprintf(stderr, "insmod: can't read '%s': %s\n",
+ filename, strerror(errno));
+ exit(1);
+ }
+
+ ret = init_module(file, len, options);
+ if (ret != 0) {
+ fprintf(stderr, "insmod: error inserting '%s': %li %s\n",
+ filename, ret, moderror(errno));
+ exit(1);
+ }
+ exit(0);
+}
diff --git a/usr/utils/kill.c b/usr/utils/kill.c
new file mode 100644
index 0000000..188f1b5
--- /dev/null
+++ b/usr/utils/kill.c
@@ -0,0 +1,33 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+
+char *progname;
+
+static __noreturn usage(void)
+{
+ fprintf(stderr, "Usage: %s pid\n", progname);
+ exit(1);
+}
+int main(int argc, char *argv[])
+{
+ long pid;
+ char *endp;
+
+ progname = argv[0];
+ if (argc != 2)
+ usage();
+
+ pid = strtol(argv[1], &endp, 10);
+ if (*endp != '\0') {
+ perror("pid");
+ usage();
+ }
+
+ if (kill(pid, SIGTERM) == -1) {
+ perror("kill");
+ exit(-1);
+ }
+ exit(0);
+}
diff --git a/usr/utils/ln.c b/usr/utils/ln.c
new file mode 100644
index 0000000..e826eb8
--- /dev/null
+++ b/usr/utils/ln.c
@@ -0,0 +1,77 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <linux/limits.h>
+
+int main(int argc, char *argv[])
+{
+ int c, s, f;
+ char *p;
+ struct stat sb;
+
+ s = f = 0;
+ do {
+ c = getopt(argc, argv, "sf");
+ if (c == EOF)
+ break;
+
+ switch (c) {
+
+ case 's':
+ s = 1;
+ break;
+ case 'f':
+ f = 1;
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ return 1;
+ }
+
+ } while (1);
+
+ if (optind == argc) {
+ fprintf(stderr, "Usage: %s [-s] [-f] target link\n", argv[0]);
+ return 1;
+ }
+
+ memset(&sb, 0, sizeof(struct stat));
+ if (stat(argv[argc - 1], &sb) < 0 && argc - optind > 2) {
+ if (!(S_ISDIR(sb.st_mode))) {
+ fprintf(stderr,
+ "multiple targets and %s is not a directory\n",
+ argv[argc - 1]);
+ return 1;
+ }
+ }
+
+ for (c = optind; c < argc - 1; c++) {
+ char target[PATH_MAX];
+
+ p = strrchr(argv[c], '/');
+ p++;
+
+ if (S_ISDIR(sb.st_mode))
+ snprintf(target, PATH_MAX, "%s/%s", argv[argc - 1], p);
+ else
+ snprintf(target, PATH_MAX, "%s", argv[argc - 1]);
+
+ if (f)
+ unlink(target);
+
+ if (s) {
+ if (symlink(argv[c], target) == -1)
+ perror(target);
+ } else {
+ if (link(argv[c], target) == -1)
+ perror(target);
+ }
+ }
+
+ return 0;
+}
diff --git a/usr/utils/losetup.c b/usr/utils/losetup.c
new file mode 100644
index 0000000..16159eb
--- /dev/null
+++ b/usr/utils/losetup.c
@@ -0,0 +1,400 @@
+/* Originally from Ted's losetup.c */
+
+#define LOOPMAJOR 7
+
+/*
+ * losetup.c - setup and control loop devices
+ */
+
+/* We want __u64 to be unsigned long long */
+#define __SANE_USERSPACE_TYPES__
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/sysmacros.h>
+#include <stdarg.h>
+#include <linux/loop.h>
+
+extern int verbose;
+extern char *progname;
+extern char *xstrdup (const char *s); /* not: #include "sundries.h" */
+extern void error (const char *fmt, ...); /* idem */
+
+/* caller guarantees n > 0 */
+void xstrncpy(char *dest, const char *src, size_t n)
+{
+ strncpy(dest, src, n-1);
+ dest[n-1] = 0;
+}
+
+
+static int show_loop(char *device)
+{
+ struct loop_info64 loopinfo64;
+ int fd, errsv;
+
+ if ((fd = open(device, O_RDONLY)) < 0) {
+ int errsv = errno;
+ fprintf(stderr, "loop: can't open device %s: %s\n",
+ device, strerror (errsv));
+ return 2;
+ }
+
+ if (ioctl(fd, LOOP_GET_STATUS64, &loopinfo64) == 0) {
+
+ loopinfo64.lo_file_name[LO_NAME_SIZE-2] = '*';
+ loopinfo64.lo_file_name[LO_NAME_SIZE-1] = 0;
+ loopinfo64.lo_crypt_name[LO_NAME_SIZE-1] = 0;
+
+ printf("%s: [%04llx]:%llu (%s)",
+ device, loopinfo64.lo_device, loopinfo64.lo_inode,
+ loopinfo64.lo_file_name);
+
+ if (loopinfo64.lo_offset)
+ printf(", offset %lld", loopinfo64.lo_offset);
+
+ if (loopinfo64.lo_sizelimit)
+ printf(", sizelimit %lld", loopinfo64.lo_sizelimit);
+
+ if (loopinfo64.lo_encrypt_type ||
+ loopinfo64.lo_crypt_name[0]) {
+ const char *e = (const char *)loopinfo64.lo_crypt_name;
+
+ if (*e == 0 && loopinfo64.lo_encrypt_type == 1)
+ e = "XOR";
+ printf(", encryption %s (type %d)",
+ e, loopinfo64.lo_encrypt_type);
+ }
+ printf("\n");
+ close (fd);
+ return 0;
+ }
+
+ errsv = errno;
+ fprintf(stderr, "loop: can't get info on device %s: %s\n",
+ device, strerror (errsv));
+ close (fd);
+ return 1;
+}
+
+int
+is_loop_device (const char *device) {
+ struct stat statbuf;
+
+ return (stat(device, &statbuf) == 0 &&
+ S_ISBLK(statbuf.st_mode) &&
+ major(statbuf.st_rdev) == LOOPMAJOR);
+}
+
+#define SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+char * find_unused_loop_device (void)
+{
+ char dev[20];
+ int fd, rc;
+
+ fd = open("/dev/loop-control", O_RDWR);
+ if (fd < 0) {
+ error("%s: could not open /dev/loop-control. Maybe this kernel "
+ "does not know\n"
+ " about the loop device? (If so, recompile or "
+ "`modprobe loop'.)", progname);
+ return NULL;
+ }
+ rc = ioctl(fd, LOOP_CTL_GET_FREE, 0);
+ close(fd);
+ if (rc < 0) {
+ error("%s: could not find any free loop device", progname);
+ return NULL;
+ }
+
+ sprintf(dev, "/dev/loop%d", rc);
+ return xstrdup(dev);
+}
+
+/*
+ * A function to read the passphrase either from the terminal or from
+ * an open file descriptor.
+ */
+static char * xgetpass(int pfd, const char *prompt)
+{
+ char *pass;
+ int buflen, i;
+
+ pass = NULL;
+ buflen = 0;
+ for (i=0; ; i++) {
+ if (i >= buflen-1) {
+ /* we're running out of space in the buffer.
+ * Make it bigger: */
+ char *tmppass = pass;
+ buflen += 128;
+ pass = realloc(tmppass, buflen);
+ if (pass == NULL) {
+ /* realloc failed. Stop reading. */
+ error("Out of memory while reading passphrase");
+ pass = tmppass; /* the old buffer hasn't changed */
+ break;
+ }
+ }
+ if (read(pfd, pass+i, 1) != 1 ||
+ pass[i] == '\n' || pass[i] == 0)
+ break;
+ }
+
+ if (pass == NULL)
+ return "";
+
+ pass[i] = 0;
+ return pass;
+}
+
+static int digits_only(const char *s)
+{
+ while (*s)
+ if (!isdigit(*s++))
+ return 0;
+ return 1;
+}
+
+int set_loop(const char *device, const char *file, unsigned long long offset,
+ const char *encryption, int pfd, int *loopro) {
+ struct loop_info64 loopinfo64;
+ int fd, ffd, mode, i;
+ char *pass;
+
+ mode = (*loopro ? O_RDONLY : O_RDWR);
+ if ((ffd = open(file, mode)) < 0) {
+ if (!*loopro && errno == EROFS)
+ ffd = open(file, mode = O_RDONLY);
+ if (ffd < 0) {
+ perror(file);
+ return 1;
+ }
+ }
+ if ((fd = open(device, mode)) < 0) {
+ perror (device);
+ return 1;
+ }
+ *loopro = (mode == O_RDONLY);
+
+ memset(&loopinfo64, 0, sizeof(loopinfo64));
+
+ xstrncpy((char *)loopinfo64.lo_file_name, file, LO_NAME_SIZE);
+
+ if (encryption && *encryption) {
+ if (digits_only(encryption)) {
+ loopinfo64.lo_encrypt_type = atoi(encryption);
+ } else {
+ loopinfo64.lo_encrypt_type = LO_CRYPT_CRYPTOAPI;
+ snprintf((char *)loopinfo64.lo_crypt_name, LO_NAME_SIZE,
+ "%s", encryption);
+ }
+ }
+
+ loopinfo64.lo_offset = offset;
+
+
+ switch (loopinfo64.lo_encrypt_type) {
+ case LO_CRYPT_NONE:
+ loopinfo64.lo_encrypt_key_size = 0;
+ break;
+ case LO_CRYPT_XOR:
+ pass = xgetpass(pfd, "Password: ");
+ goto gotpass;
+ default:
+ pass = xgetpass(pfd, "Password: ");
+ gotpass:
+ memset(loopinfo64.lo_encrypt_key, 0, LO_KEY_SIZE);
+ xstrncpy((char *)loopinfo64.lo_encrypt_key, pass, LO_KEY_SIZE);
+ memset(pass, 0, strlen(pass));
+ loopinfo64.lo_encrypt_key_size = LO_KEY_SIZE;
+ }
+
+ if (ioctl(fd, LOOP_SET_FD, (void *)(size_t)ffd) < 0) {
+ perror("ioctl: LOOP_SET_FD");
+ return 1;
+ }
+ close (ffd);
+
+ i = ioctl(fd, LOOP_SET_STATUS64, &loopinfo64);
+ if (i)
+ perror("ioctl: LOOP_SET_STATUS64");
+ memset(&loopinfo64, 0, sizeof(loopinfo64));
+
+ if (i) {
+ ioctl (fd, LOOP_CLR_FD, 0);
+ close (fd);
+ return 1;
+ }
+ close (fd);
+
+ if (verbose > 1)
+ printf("set_loop(%s,%s,%llu): success\n",
+ device, file, offset);
+ return 0;
+}
+
+int del_loop (const char *device)
+{
+ int fd;
+
+ if ((fd = open (device, O_RDONLY)) < 0) {
+ int errsv = errno;
+ fprintf(stderr, "loop: can't delete device %s: %s\n",
+ device, strerror (errsv));
+ return 1;
+ }
+ if (ioctl (fd, LOOP_CLR_FD, 0) < 0) {
+ perror ("ioctl: LOOP_CLR_FD");
+ close (fd);
+ return 1;
+ }
+ close (fd);
+ if (verbose > 1)
+ printf("del_loop(%s): success\n", device);
+ return 0;
+}
+
+
+int verbose = 0;
+char *progname;
+
+static void usage(FILE *f)
+{
+ fprintf(f, "usage:\n\
+ %s loop_device # give info\n\
+ %s -d loop_device # delete\n\
+ %s -f # find unused\n\
+ %s -h # this help\n\
+ %s [-e encryption] [-o offset] {-f|loop_device} file # setup\n",
+ progname, progname, progname, progname, progname);
+ exit(f == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+char * xstrdup (const char *s) {
+ char *t;
+
+ if (s == NULL)
+ return NULL;
+
+ t = strdup (s);
+
+ if (t == NULL) {
+ fprintf(stderr, "not enough memory");
+ exit(1);
+ }
+
+ return t;
+}
+
+void error (const char *fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vfprintf (stderr, fmt, args);
+ va_end (args);
+ fprintf (stderr, "\n");
+}
+
+int main(int argc, char **argv)
+{
+ char *p, *offset, *encryption, *passfd, *device, *file;
+ int delete, find, c;
+ int res = 0;
+ int ro = 0;
+ int pfd = -1;
+ unsigned long long off;
+
+
+ delete = find = 0;
+ off = 0;
+ offset = encryption = passfd = NULL;
+
+ progname = argv[0];
+ if ((p = strrchr(progname, '/')) != NULL)
+ progname = p+1;
+
+ while ((c = getopt(argc, argv, "de:E:fho:p:v")) != -1) {
+ switch (c) {
+ case 'd':
+ delete = 1;
+ break;
+ case 'E':
+ case 'e':
+ encryption = optarg;
+ break;
+ case 'f':
+ find = 1;
+ break;
+ case 'h':
+ usage(stdout);
+ break;
+ case 'o':
+ offset = optarg;
+ break;
+ case 'p':
+ passfd = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage(stderr);
+ }
+ }
+
+ if (argc == 1) {
+ usage(stderr);
+ } else if (delete) {
+ if (argc != optind+1 || encryption || offset || find)
+ usage(stderr);
+ } else if (find) {
+ if (argc < optind || argc > optind+1)
+ usage(stderr);
+ } else {
+ if (argc < optind+1 || argc > optind+2)
+ usage(stderr);
+ }
+
+ if (find) {
+ device = find_unused_loop_device();
+ if (device == NULL)
+ return -1;
+ if (verbose)
+ printf("Loop device is %s\n", device);
+ if (argc == optind) {
+ printf("%s\n", device);
+ return 0;
+ }
+ file = argv[optind];
+ } else {
+ device = argv[optind];
+ if (argc == optind+1)
+ file = NULL;
+ else
+ file = argv[optind+1];
+ }
+
+ if (delete)
+ res = del_loop(device);
+ else if (file == NULL)
+ res = show_loop(device);
+ else {
+ if (offset && sscanf(offset, "%llu", &off) != 1)
+ usage(stderr);
+ if (passfd && sscanf(passfd, "%d", &pfd) != 1)
+ usage(stderr);
+ res = set_loop(device, file, off, encryption, pfd, &ro);
+ }
+ return res;
+}
diff --git a/usr/utils/ls.c b/usr/utils/ls.c
new file mode 100644
index 0000000..50af434
--- /dev/null
+++ b/usr/utils/ls.c
@@ -0,0 +1,223 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+
+#define STAT_ISSET(mode, mask) (((mode) & mask) == mask)
+
+static size_t max_linksiz = 128;
+static int max_nlinks = 1;
+static int max_size = 1;
+static int max_uid = 1;
+static int max_gid = 1;
+static int max_min = 1;
+static int max_maj = 1;
+
+static void do_preformat(const struct stat *st)
+{
+ int bytes;
+
+ bytes = snprintf(NULL, 0, "%ju", (uintmax_t) st->st_nlink);
+ if (bytes > max_nlinks)
+ max_nlinks = bytes;
+
+ bytes = snprintf(NULL, 0, "%ju", (uintmax_t) st->st_uid);
+ if (bytes > max_uid)
+ max_uid = bytes;
+
+ bytes = snprintf(NULL, 0, "%ju", (uintmax_t) st->st_gid);
+ if (bytes > max_gid)
+ max_gid = bytes;
+
+ if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
+ bytes = snprintf(NULL, 0, "%u", major(st->st_rdev));
+ if (bytes > max_maj)
+ max_maj = bytes;
+
+ bytes = snprintf(NULL, 0, "%u", minor(st->st_rdev));
+ if (bytes > max_min)
+ max_min = bytes;
+
+ max_size = max_maj + max_min + 1;
+ } else {
+ bytes = snprintf(NULL, 0, "%ju", (uintmax_t) st->st_size);
+ if (bytes > max_size)
+ max_size = bytes;
+ }
+ return;
+}
+
+static void do_stat(const struct stat *st, int dir_fd, const char *path)
+{
+ char *fmt, *link_name;
+ int rc;
+
+ switch (st->st_mode & S_IFMT) {
+ case S_IFBLK:
+ putchar('b');
+ break;
+ case S_IFCHR:
+ putchar('c');
+ break;
+ case S_IFDIR:
+ putchar('d');
+ break;
+ case S_IFIFO:
+ putchar('p');
+ break;
+ case S_IFLNK:
+ putchar('l');
+ break;
+ case S_IFSOCK:
+ putchar('s');
+ break;
+ case S_IFREG:
+ putchar('-');
+ break;
+ default:
+ putchar('?');
+ break;
+ }
+ putchar(STAT_ISSET(st->st_mode, S_IRUSR) ? 'r' : '-');
+ putchar(STAT_ISSET(st->st_mode, S_IWUSR) ? 'w' : '-');
+
+ !STAT_ISSET(st->st_mode, S_ISUID) ?
+ putchar(STAT_ISSET(st->st_mode, S_IXUSR) ? 'x' : '-') :
+ putchar('S');
+
+ putchar(STAT_ISSET(st->st_mode, S_IRGRP) ? 'r' : '-');
+ putchar(STAT_ISSET(st->st_mode, S_IWGRP) ? 'w' : '-');
+
+ !STAT_ISSET(st->st_mode, S_ISGID) ?
+ putchar(STAT_ISSET(st->st_mode, S_IXGRP) ? 'x' : '-') :
+ putchar('S');
+
+ putchar(STAT_ISSET(st->st_mode, S_IROTH) ? 'r' : '-');
+ putchar(STAT_ISSET(st->st_mode, S_IWOTH) ? 'w' : '-');
+
+ !STAT_ISSET(st->st_mode, S_ISVTX) ?
+ putchar(STAT_ISSET(st->st_mode, S_IXOTH) ? 'x' : '-') :
+ putchar(S_ISDIR(st->st_mode) ? 't' : 'T');
+
+ if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
+ rc = asprintf(&fmt, " %%%dju %%%dju %%%dju %%%du,%%%du %%s",
+ max_nlinks, max_uid, max_gid, max_maj, max_min);
+ if (rc == -1) {
+ perror("asprintf");
+ exit(1);
+ }
+ fprintf(stdout, fmt,
+ (uintmax_t) st->st_nlink,
+ (uintmax_t) st->st_uid,
+ (uintmax_t) st->st_gid,
+ major(st->st_rdev),
+ minor(st->st_rdev),
+ path);
+ } else {
+ rc = asprintf(&fmt, " %%%dju %%%dju %%%dju %%%dju %%s",
+ max_nlinks, max_uid, max_gid, max_size);
+ if (rc == -1) {
+ perror("asprintf");
+ exit(1);
+ }
+ fprintf(stdout, fmt,
+ (uintmax_t) st->st_nlink,
+ (uintmax_t) st->st_uid,
+ (uintmax_t) st->st_gid,
+ (uintmax_t) st->st_size,
+ path);
+ }
+ free(fmt);
+
+ if (S_ISLNK(st->st_mode)) {
+ link_name = malloc(max_linksiz);
+ if (link_name == NULL) {
+ perror("malloc");
+ exit(1);
+ }
+ rc = readlinkat(dir_fd, path, link_name, max_linksiz);
+ if (rc == -1) {
+ free(link_name);
+ perror("readlink");
+ exit(1);
+ }
+ link_name[rc] = '\0';
+ fprintf(stdout, " -> %s", link_name);
+ free(link_name);
+ }
+
+ putchar('\n');
+ return;
+}
+
+static void do_dir(const char *path, int preformat)
+{
+ DIR *dir;
+ int dir_fd;
+ struct dirent *dent;
+ struct stat st;
+
+ dir = opendir(path);
+ if (dir == NULL) {
+ perror(path);
+ exit(1);
+ }
+ dir_fd = dirfd(dir);
+
+ while ((dent = readdir(dir)) != NULL) {
+ if (fstatat(dir_fd, dent->d_name, &st,
+ AT_SYMLINK_NOFOLLOW)) {
+ perror(dent->d_name);
+ exit(1);
+ }
+ (preformat) ?
+ do_preformat(&st) :
+ do_stat(&st, dir_fd, dent->d_name);
+ }
+
+ closedir(dir);
+}
+
+int main(int argc, char *argv[])
+{
+ int i;
+ struct stat st;
+
+ if (argc == 1) {
+ do_dir(".", 1);
+ do_dir(".", 0);
+ return 0;
+ }
+
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-' && argv[i][1] == 'h') {
+ fprintf(stdout, "Usage: ls [-h] [FILE ...]\n");
+ return 0;
+ }
+
+ if (lstat(argv[i], &st)) {
+ perror(argv[i]);
+ exit(1);
+ }
+
+ S_ISDIR(st.st_mode) ?
+ do_dir(argv[i], 1) :
+ do_preformat(&st);
+ }
+
+ for (i = 1; i < argc; i++) {
+ if (lstat(argv[i], &st)) {
+ perror(argv[i]);
+ exit(1);
+ }
+
+ S_ISDIR(st.st_mode) ?
+ do_dir(argv[i], 0) :
+ do_stat(&st, AT_FDCWD, argv[i]);
+ }
+
+ return 0;
+}
diff --git a/usr/utils/minips.c b/usr/utils/minips.c
new file mode 100644
index 0000000..f48505f
--- /dev/null
+++ b/usr/utils/minips.c
@@ -0,0 +1,511 @@
+/*
+ * Copyright 1998 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * 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 Library General Public License for more details.
+ */
+
+/* This is a minimal /bin/ps, designed to be smaller than the old ps
+ * while still supporting some of the more important features of the
+ * new ps. (for total size, note that this ps does not need libproc)
+ * It is suitable for Linux-on-a-floppy systems only.
+ *
+ * Maintainers: do not compile or install for normal systems.
+ * Anyone needing this will want to tweak their compiler anyway.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include <asm/param.h> /* HZ */
+
+static int P_euid;
+static int P_pid;
+static char P_cmd[16];
+static char P_state;
+static int P_ppid, P_pgrp, P_session, P_tty, P_tpgid;
+static unsigned long P_flags, P_min_flt, P_cmin_flt, P_maj_flt, P_cmaj_flt,
+ P_utime, P_stime;
+static long P_cutime, P_cstime, P_priority, P_nice, P_timeout, P_it_real_value;
+static unsigned long P_start_time, P_vsize;
+static long P_rss;
+static unsigned long P_rss_rlim, P_start_code, P_end_code, P_start_stack,
+ P_kstk_esp, P_kstk_eip;
+static unsigned P_signal, P_blocked, P_sigignore, P_sigcatch;
+static unsigned long P_wchan, P_nswap, P_cnswap;
+
+#if 0
+static int screen_cols = 80;
+static int w_count;
+#endif
+
+static int want_one_pid;
+static const char *want_one_command;
+static int select_notty;
+static int select_all;
+
+static int ps_format;
+static int old_h_option;
+
+/* we only pretend to support this */
+static int show_args; /* implicit with -f and all BSD options */
+static int bsd_c_option; /* this option overrides the above */
+
+static int ps_argc; /* global argc */
+static char **ps_argv; /* global argv */
+static int thisarg; /* index into ps_argv */
+static char *flagptr; /* current location in ps_argv[thisarg] */
+
+#ifndef HZ
+#warning HZ not defined, assuming it is 100
+#define HZ 100
+#endif
+
+int page_shift; /* Page size as shift count */
+
+static void usage(void)
+{
+ fprintf(stderr,
+ "-C select by command name (minimal ps only accepts one)\n"
+ "-p select by process ID (minimal ps only accepts one)\n"
+ "-e all processes (same as ax)\n"
+ "a all processes w/ tty, including other users\n"
+ "x processes w/o controlling ttys\n"
+ "-f full format\n"
+ "-j,j job control format\n"
+ "v virtual memory format\n"
+ "-l,l long format\n"
+ "u user-oriented format\n"
+ "-o user-defined format (limited support, only \"ps -o pid=\")\n"
+ "h no header\n"
+/*
+ "-A all processes (same as ax)\n"
+ "c true command name\n"
+ "-w,w wide output\n"
+*/
+ );
+ exit(1);
+}
+
+/*
+ * Return the next argument, or call the usage function.
+ * This handles both: -oFOO -o FOO
+ */
+static const char *get_opt_arg(void)
+{
+ const char *ret;
+ ret = flagptr + 1; /* assume argument is part of ps_argv[thisarg] */
+ if (*ret)
+ return ret;
+ if (++thisarg >= ps_argc)
+ usage(); /* there is nothing left */
+ /* argument is the new ps_argv[thisarg] */
+ ret = ps_argv[thisarg];
+ if (!ret || !*ret)
+ usage();
+ return ret;
+}
+
+/* return the PID, or 0 if nothing good */
+static void parse_pid(const char *str)
+{
+ char *endp;
+ int num;
+ if (!str)
+ goto bad;
+ num = strtol(str, &endp, 0);
+ if (*endp != '\0')
+ goto bad;
+ if (num < 1)
+ goto bad;
+ if (want_one_pid)
+ goto bad;
+ want_one_pid = num;
+ return;
+ bad:
+ usage();
+}
+
+/***************** parse SysV options, including Unix98 *****************/
+static void parse_sysv_option(void)
+{
+ do {
+ switch (*flagptr) {
+ /**** selection ****/
+ case 'C': /* end */
+ if (want_one_command)
+ usage();
+ want_one_command = get_opt_arg();
+ return; /* can't have any more options */
+ case 'p': /* end */
+ parse_pid(get_opt_arg());
+ return; /* can't have any more options */
+ case 'A':
+ case 'e':
+ select_all++;
+ select_notty++;
+ case 'w': /* here for now, since the real one is not used */
+ break;
+ /**** output format ****/
+ case 'f':
+ show_args = 1;
+ /* FALL THROUGH */
+ case 'j':
+ case 'l':
+ if (ps_format)
+ usage();
+ ps_format = *flagptr;
+ break;
+ case 'o': /* end */
+ /* We only support a limited form: "ps -o pid=" (yes, just "pid=") */
+ if (strcmp(get_opt_arg(), "pid="))
+ usage();
+ if (ps_format)
+ usage();
+ ps_format = 'o';
+ old_h_option++;
+ return; /* can't have any more options */
+ /**** other stuff ****/
+#if 0
+ case 'w':
+ w_count++;
+ break;
+#endif
+ default:
+ usage();
+ } /* switch */
+ } while (*++flagptr);
+}
+
+/************************* parse BSD options **********************/
+static void parse_bsd_option(void)
+{
+ do {
+ switch (*flagptr) {
+ /**** selection ****/
+ case 'a':
+ select_all++;
+ break;
+ case 'x':
+ select_notty++;
+ break;
+ case 'p': /* end */
+ parse_pid(get_opt_arg());
+ return; /* can't have any more options */
+ /**** output format ****/
+ case 'j':
+ case 'l':
+ case 'u':
+ case 'v':
+ if (ps_format)
+ usage();
+ ps_format = 0x80 | *flagptr; /* use 0x80 to tell BSD from SysV */
+ break;
+ /**** other stuff ****/
+ case 'c':
+ bsd_c_option++;
+#if 0
+ break;
+#endif
+ case 'w':
+#if 0
+ w_count++;
+#endif
+ break;
+ case 'h':
+ old_h_option++;
+ break;
+ default:
+ usage();
+ } /* switch */
+ } while (*++flagptr);
+}
+
+#if 0
+/* not used yet */
+static void choose_dimensions(void)
+{
+ struct winsize ws;
+ char *columns;
+ /* screen_cols is 80 by default */
+ if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 30)
+ screen_cols = ws.ws_col;
+ columns = getenv("COLUMNS");
+ if (columns && *columns) {
+ long t;
+ char *endptr;
+ t = strtol(columns, &endptr, 0);
+ if (!*endptr && (t > 30) && (t < (long)999999999))
+ screen_cols = (int)t;
+ }
+ if (w_count && (screen_cols < 132))
+ screen_cols = 132;
+ if (w_count > 1)
+ screen_cols = 999999999;
+}
+#endif
+
+static void arg_parse(int argc, char *argv[])
+{
+ int sel = 0; /* to verify option sanity */
+ ps_argc = argc;
+ ps_argv = argv;
+ thisarg = 0;
+ /**** iterate over the args ****/
+ while (++thisarg < ps_argc) {
+ flagptr = ps_argv[thisarg];
+ switch (*flagptr) {
+ case '0'...'9':
+ show_args = 1;
+ parse_pid(flagptr);
+ break;
+ case '-':
+ flagptr++;
+ parse_sysv_option();
+ break;
+ default:
+ show_args = 1;
+ parse_bsd_option();
+ break;
+ }
+ }
+ /**** sanity check and clean-up ****/
+ if (want_one_pid)
+ sel++;
+ if (want_one_command)
+ sel++;
+ if (select_notty || select_all)
+ sel++;
+ if (sel > 1 || select_notty > 1 || select_all > 1 || bsd_c_option > 1
+ || old_h_option > 1)
+ usage();
+ if (bsd_c_option)
+ show_args = 0;
+}
+
+/* return 1 if it works, or 0 for failure */
+static int stat2proc(int pid)
+{
+ char buf[800]; /* about 40 fields, 64-bit decimal is about 20 chars */
+ int num;
+ int fd;
+ char *tmp;
+ struct stat sb; /* stat() used to get EUID */
+
+ snprintf(buf, 32, "/proc/%d/stat", pid);
+ fd = open(buf, O_RDONLY, 0);
+ if (fd == -1)
+ return 0;
+ num = read(fd, buf, sizeof buf - 1);
+ fstat(fd, &sb);
+ P_euid = sb.st_uid;
+ close(fd);
+ if (num < 80)
+ return 0;
+ buf[num] = '\0';
+ tmp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
+ *tmp = '\0'; /* replace trailing ')' with NUL */
+ /* parse these two strings separately, skipping the leading "(". */
+ memset(P_cmd, 0, sizeof P_cmd); /* clear */
+ sscanf(buf, "%d (%15c", &P_pid, P_cmd); /* comm[16] in kernel */
+ num = sscanf(tmp + 2, /* skip space after ')' too */
+ "%c " "%d %d %d %d %d " "%lu %lu %lu %lu %lu %lu %lu " "%ld %ld %ld %ld %ld %ld " "%lu %lu " "%ld " "%lu %lu %lu %lu %lu %lu " "%u %u %u %u " /* no use for RT signals */
+ "%lu %lu %lu",
+ &P_state,
+ &P_ppid, &P_pgrp, &P_session, &P_tty, &P_tpgid,
+ &P_flags, &P_min_flt, &P_cmin_flt, &P_maj_flt, &P_cmaj_flt,
+ &P_utime, &P_stime, &P_cutime, &P_cstime, &P_priority,
+ &P_nice, &P_timeout, &P_it_real_value, &P_start_time,
+ &P_vsize, &P_rss, &P_rss_rlim, &P_start_code, &P_end_code,
+ &P_start_stack, &P_kstk_esp, &P_kstk_eip, &P_signal,
+ &P_blocked, &P_sigignore, &P_sigcatch, &P_wchan, &P_nswap,
+ &P_cnswap);
+/* fprintf(stderr, "stat2proc converted %d fields.\n",num); */
+ P_vsize /= 1024;
+ P_rss <<= page_shift - 10;
+ if (num < 30)
+ return 0;
+ if (P_pid != pid)
+ return 0;
+ return 1;
+}
+
+static const char *do_time(unsigned long t)
+{
+ int hh, mm, ss;
+ static char buf[32];
+ int cnt = 0;
+ t /= HZ;
+ ss = t % 60;
+ t /= 60;
+ mm = t % 60;
+ t /= 60;
+ hh = t % 24;
+ t /= 24;
+ if (t)
+ cnt = snprintf(buf, sizeof buf, "%d-", (int)t);
+ snprintf(cnt + buf, sizeof(buf) - cnt, "%02d:%02d:%02d", hh, mm, ss);
+ return buf;
+}
+
+static void print_proc(void)
+{
+ char tty[16];
+ snprintf(tty, sizeof tty, "%3d,%-3d", (P_tty >> 8) & 0xff,
+ P_tty & 0xff);
+ switch (ps_format) {
+ case 0:
+ printf("%5d %s %s", P_pid, tty, do_time(P_utime + P_stime));
+ break;
+ case 'o':
+ printf("%d\n", P_pid);
+ return; /* don't want the command */
+ case 'l':
+ printf("%03x %c %5d %5d %5d - %3d %3d - "
+ "%5ld %06x %s %s",
+ (unsigned)P_flags & 0x777, P_state, P_euid, P_pid,
+ P_ppid, (int)P_priority, (int)P_nice,
+ P_vsize >> (page_shift - 10),
+ (unsigned)(P_wchan & 0xffffff), tty,
+ do_time(P_utime + P_stime)
+ );
+ break;
+ case 'f':
+ printf("%5d %5d %5d - - %s %s",
+ P_euid, P_pid, P_ppid, tty, do_time(P_utime + P_stime)
+ );
+ break;
+ case 'j':
+ printf("%5d %5d %5d %s %s",
+ P_pid, P_pgrp, P_session, tty, do_time(P_utime + P_stime)
+ );
+ break;
+ case 'u' | 0x80:
+ printf("%5d %5d - - %5ld %5ld %s %c - %s",
+ P_euid, P_pid, P_vsize, P_rss, tty, P_state,
+ do_time(P_utime + P_stime)
+ );
+ break;
+ case 'v' | 0x80:
+ printf("%5d %s %c %s %6d - - %5d -",
+ P_pid, tty, P_state, do_time(P_utime + P_stime),
+ (int)P_maj_flt, (int)P_rss);
+ break;
+ case 'j' | 0x80:
+ printf("%5d %5d %5d %5d %s %5d %c %5d %s",
+ P_ppid, P_pid, P_pgrp, P_session, tty, P_tpgid, P_state,
+ P_euid, do_time(P_utime + P_stime)
+ );
+ break;
+ case 'l' | 0x80:
+ printf("%03x %5d %5d %5d %3d %3d "
+ "%5ld %4ld %06x %c %s %s",
+ (unsigned)P_flags & 0x777, P_euid, P_pid, P_ppid,
+ (int)P_priority, (int)P_nice, P_vsize, P_rss,
+ (unsigned)(P_wchan & 0xffffff), P_state, tty,
+ do_time(P_utime + P_stime)
+ );
+ break;
+ default:
+ break;
+ }
+ if (show_args)
+ printf(" [%s]\n", P_cmd);
+ else
+ printf(" %s\n", P_cmd);
+}
+
+int main(int argc, char *argv[])
+{
+ arg_parse(argc, argv);
+
+ page_shift = __getpageshift();
+
+#if 0
+ choose_dimensions();
+#endif
+ if (!old_h_option) {
+ const char *head;
+ switch (ps_format) {
+ default: /* can't happen */
+ case 0:
+ head = " PID TTY TIME CMD";
+ break;
+ case 'l':
+ head =
+ " F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD";
+ break;
+ case 'f':
+ head =
+ " UID PID PPID C STIME TTY TIME CMD";
+ break;
+ case 'j':
+ head = " PID PGID SID TTY TIME CMD";
+ break;
+ case 'u' | 0x80:
+ head =
+ " UID PID %CPU %MEM VSZ RSS TTY S START TIME COMMAND";
+ break;
+ case 'v' | 0x80:
+ head =
+ " PID TTY S TIME MAJFL TRS DRS RSS %MEM COMMAND";
+ break;
+ case 'j' | 0x80:
+ head =
+ " PPID PID PGID SID TTY TPGID S UID TIME COMMAND";
+ break;
+ case 'l' | 0x80:
+ head =
+ " F UID PID PPID PRI NI VSZ RSS WCHAN S TTY TIME COMMAND";
+ break;
+ }
+ printf("%s\n", head);
+ }
+ if (want_one_pid) {
+ if (stat2proc(want_one_pid))
+ print_proc();
+ else
+ exit(1);
+ } else {
+ struct dirent *ent; /* dirent handle */
+ DIR *dir;
+ int ouruid;
+ int found_a_proc;
+ found_a_proc = 0;
+ ouruid = getuid();
+ dir = opendir("/proc");
+ if (!dir)
+ exit(1);
+ while ((ent = readdir(dir))) {
+ if (*ent->d_name < '0' || *ent->d_name > '9')
+ continue;
+ if (!stat2proc(atoi(ent->d_name)))
+ continue;
+ if (want_one_command) {
+ if (strcmp(want_one_command, P_cmd))
+ continue;
+ } else {
+ if (!select_notty && P_tty == -1)
+ continue;
+ if (!select_all && P_euid != ouruid)
+ continue;
+ }
+ found_a_proc++;
+ print_proc();
+ }
+ closedir(dir);
+ exit(!found_a_proc);
+ }
+ return 0;
+}
diff --git a/usr/utils/mkdir.c b/usr/utils/mkdir.c
new file mode 100644
index 0000000..af241ef
--- /dev/null
+++ b/usr/utils/mkdir.c
@@ -0,0 +1,154 @@
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "file_mode.h"
+
+static mode_t leaf_mode, subdir_mode;
+static int p_flag;
+
+char *progname;
+
+static __noreturn usage(void)
+{
+ fprintf(stderr, "Usage: %s [-p] [-m mode] dir...\n", progname);
+ exit(1);
+}
+
+static int make_one_dir(char *dir, mode_t mode)
+{
+ struct stat stbuf;
+
+ if (mkdir(dir, mode) == -1) {
+ int err = errno;
+
+ /*
+ * Ignore the error if it all of the following
+ * are satisfied:
+ * - error was EEXIST
+ * - -p was specified
+ * - stat indicates that its a directory
+ */
+ if (p_flag && errno == EEXIST &&
+ stat(dir, &stbuf) == 0 && S_ISDIR(stbuf.st_mode))
+ return 1;
+ errno = err;
+ fprintf(stderr, "%s: ", progname);
+ perror(dir);
+ return -1;
+ }
+ return 0;
+}
+
+static int make_dir(char *dir)
+{
+ int ret;
+
+ if (p_flag) {
+ char *s, *p;
+
+ /*
+ * Recurse each directory, trying to make it
+ * as we go. Should we check to see if it
+ * exists, and if so if it's a directory
+ * before calling mkdir?
+ */
+ s = dir;
+ while ((p = strchr(s, '/')) != NULL) {
+ /*
+ * Ignore the leading /
+ */
+ if (p != dir) {
+ *p = '\0';
+
+ /*
+ * Make the intermediary directory. POSIX
+ * says that these directories are created
+ * with umask,u+wx
+ */
+ if (make_one_dir(dir, subdir_mode) == -1)
+ return -1;
+
+ *p = '/';
+ }
+ s = p + 1;
+ }
+ }
+
+ /*
+ * Make the final target. Only complain if the
+ * target already exists if -p was not specified.
+ * This is created with the asked for mode & ~umask
+ */
+ ret = make_one_dir(dir, leaf_mode);
+ if (ret == -1)
+ return -1;
+
+ /*
+ * We might not set all the permission bits. Do that
+ * here (but only if we did create it.)
+ */
+ if (ret == 0 && chmod(dir, leaf_mode) == -1) {
+ int err_save = errno;
+
+ /*
+ * We failed, remove the directory we created
+ */
+ rmdir(dir);
+ errno = err_save;
+ fprintf(stderr, "%s: ", progname);
+ perror(dir);
+ return -1;
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int c, ret = 0;
+ mode_t saved_umask;
+
+ progname = argv[0];
+
+ saved_umask = umask(0);
+ leaf_mode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~saved_umask;
+ subdir_mode = (saved_umask ^ (S_IRWXU | S_IRWXG | S_IRWXO))
+ | S_IWUSR | S_IXUSR;
+
+ do {
+ c = getopt(argc, argv, "pm:");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'm':
+ leaf_mode =
+ parse_file_mode(optarg, leaf_mode, saved_umask);
+ break;
+ case 'p':
+ p_flag = 1;
+ break;
+
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ progname, optopt);
+ usage();
+ }
+ } while (1);
+
+ if (optind == argc)
+ usage();
+
+ while (optind < argc) {
+ if (make_dir(argv[optind]))
+ ret = 255; /* seems to be what gnu mkdir does */
+ optind++;
+ }
+
+ return ret;
+}
diff --git a/usr/utils/mkfifo.c b/usr/utils/mkfifo.c
new file mode 100644
index 0000000..5a758b2
--- /dev/null
+++ b/usr/utils/mkfifo.c
@@ -0,0 +1,70 @@
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "file_mode.h"
+
+static mode_t leaf_mode;
+
+char *progname;
+
+static int make_fifo(char *dir)
+{
+ if (mkfifo(dir, leaf_mode)) {
+ /*
+ * We failed, remove the directory we created.
+ */
+ fprintf(stderr, "%s: ", progname);
+ perror(dir);
+ return -1;
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int c, ret = 0;
+ mode_t saved_umask;
+
+ progname = argv[0];
+
+ saved_umask = umask(0);
+ leaf_mode =
+ (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) &
+ ~saved_umask;
+
+ do {
+ c = getopt(argc, argv, "m:");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'm':
+ leaf_mode =
+ parse_file_mode(optarg, leaf_mode, saved_umask);
+ break;
+
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ progname, optopt);
+ exit(1);
+ }
+ } while (1);
+
+ if (optind == argc) {
+ fprintf(stderr, "Usage: %s [-m mode] file...\n", progname);
+ exit(1);
+ }
+
+ while (optind < argc) {
+ if (make_fifo(argv[optind]))
+ ret = 255; /* seems to be what gnu mkdir does */
+ optind++;
+ }
+
+ return ret;
+}
diff --git a/usr/utils/mknod.c b/usr/utils/mknod.c
new file mode 100644
index 0000000..fa7ac7a
--- /dev/null
+++ b/usr/utils/mknod.c
@@ -0,0 +1,84 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+char *progname;
+
+static __noreturn usage(void)
+{
+ fprintf(stderr, "Usage: %s [-m mode] name {b|c|p} major minor\n",
+ progname);
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ char *name, *type, typec, *endp;
+ unsigned int major_num, minor_num;
+ mode_t mode, mode_set = 0;
+ dev_t dev;
+
+ progname = *argv++;
+ if (argc == 1)
+ usage();
+
+ if (argv[0][0] == '-' && argv[0][1] == 'm' && !argv[0][2]) {
+ mode_set = strtoul(argv[1], &endp, 8);
+ argv += 2;
+ }
+
+ name = *argv++;
+ if (!name)
+ usage();
+
+ type = *argv++;
+ if (!type || !type[0] || type[1])
+ usage();
+ typec = type[0];
+
+ mode = 0;
+ switch (typec) {
+ case 'c':
+ mode = S_IFCHR;
+ break;
+ case 'b':
+ mode = S_IFBLK;
+ break;
+ case 'p':
+ mode = S_IFIFO;
+ break;
+ default:
+ usage();
+ }
+
+ if (mode == S_IFIFO) {
+ dev = 0;
+ } else {
+ if (!argv[0] || !argv[1])
+ usage();
+
+ major_num = strtol(*argv++, &endp, 0);
+ if (*endp != '\0')
+ usage();
+ minor_num = strtol(*argv++, &endp, 0);
+ if (*endp != '\0')
+ usage();
+ dev = makedev(major_num, minor_num);
+ }
+
+ if (*argv)
+ usage();
+
+ if (mknod(name, mode|0666, dev) == -1) {
+ perror("mknod");
+ exit(1);
+ }
+
+ if (mode_set && chmod(name, mode_set)) {
+ perror("chmod");
+ exit(1);
+ }
+
+ exit(0);
+}
diff --git a/usr/utils/mount_main.c b/usr/utils/mount_main.c
new file mode 100644
index 0000000..0d299c4
--- /dev/null
+++ b/usr/utils/mount_main.c
@@ -0,0 +1,156 @@
+/*
+ * by rmk
+ */
+#include <sys/mount.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <mntent.h>
+
+#include "mount_opts.h"
+
+#define _PATH_MOUNTED "/etc/mtab"
+#define _PATH_PROC_MOUNTS "/proc/mounts"
+
+char *progname;
+
+static struct extra_opts extra;
+static unsigned long rwflag;
+
+static __noreturn usage(void)
+{
+ fprintf(stderr, "Usage: %s [-r] [-w] [-o options] [-t type] [-f] [-i] "
+ "[-n] device directory\n", progname);
+ exit(1);
+}
+
+static __noreturn print_mount(char *type)
+{
+ FILE *mfp;
+ struct mntent *mnt;
+
+ mfp = setmntent(_PATH_PROC_MOUNTS, "r");
+ if (!mfp)
+ mfp = setmntent(_PATH_MOUNTED, "r");
+ if (!mfp)
+ perror("setmntent");
+
+ while ((mnt = getmntent(mfp)) != NULL) {
+ if (mnt->mnt_fsname && !strncmp(mnt->mnt_fsname, "no", 2))
+ continue;
+ if (type && mnt->mnt_type && strcmp(type, mnt->mnt_type))
+ continue;
+ printf("%s on %s", mnt->mnt_fsname, mnt->mnt_dir);
+ if (mnt->mnt_type != NULL && *mnt->mnt_type != '\0')
+ printf(" type %s", mnt->mnt_type);
+ if (mnt->mnt_opts != NULL && *mnt->mnt_opts != '\0')
+ printf(" (%s)", mnt->mnt_opts);
+ printf("\n");
+ }
+ endmntent(mfp);
+ exit(0);
+}
+
+static int
+do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data)
+{
+ char *s;
+ int error = 0;
+
+ while ((s = strsep(&type, ",")) != NULL) {
+retry:
+ if (mount(dev, dir, s, rwflag, data) == -1) {
+ error = errno;
+ /*
+ * If the filesystem is not found, or the
+ * superblock is invalid, try the next.
+ */
+ if (error == ENODEV || error == EINVAL)
+ continue;
+
+ /*
+ * If we get EACCESS, and we're trying to
+ * mount readwrite and this isn't a remount,
+ * try read only.
+ */
+ if (error == EACCES &&
+ (rwflag & (MS_REMOUNT | MS_RDONLY)) == 0) {
+ rwflag |= MS_RDONLY;
+ goto retry;
+ }
+ } else {
+ error = 0;
+ }
+ break;
+ }
+
+ if (error) {
+ errno = error;
+ perror("mount");
+ return 255;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ char *type = NULL;
+ int c;
+
+ progname = argv[0];
+ rwflag = MS_VERBOSE;
+
+ do {
+ c = getopt(argc, argv, "fhino:rt:w");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'f':
+ /* we can't edit /etc/mtab yet anyway; exit */
+ exit(0);
+ case 'i':
+ /* ignore for now; no support for mount helpers */
+ break;
+ case 'h':
+ usage();
+ case 'n':
+ /* no mtab writing */
+ break;
+ case 'o':
+ rwflag = parse_mount_options(optarg, rwflag, &extra);
+ break;
+ case 'r':
+ rwflag |= MS_RDONLY;
+ break;
+ case 't':
+ type = optarg;
+ break;
+ case 'w':
+ rwflag &= ~MS_RDONLY;
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ progname, optopt);
+ exit(1);
+ }
+ } while (1);
+
+ if (optind == argc)
+ print_mount(type);
+
+ /*
+ * If remount, bind or move was specified, then we don't
+ * have a "type" as such. Use the dummy "none" type.
+ */
+ if (rwflag & MS_TYPE)
+ type = "none";
+
+ if (optind + 2 != argc || type == NULL)
+ usage();
+
+ return do_mount(argv[optind], argv[optind + 1], type, rwflag,
+ extra.str);
+}
diff --git a/usr/utils/mount_opts.c b/usr/utils/mount_opts.c
new file mode 100644
index 0000000..bb26c7d
--- /dev/null
+++ b/usr/utils/mount_opts.c
@@ -0,0 +1,102 @@
+/*
+ * by rmk
+ *
+ * Decode mount options.
+ */
+#include <sys/mount.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mount_opts.h"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+static const struct mount_opts options[] = {
+ /* name mask set noset */
+ {"async", MS_SYNCHRONOUS, 0, MS_SYNCHRONOUS},
+ {"atime", MS_NOATIME, 0, MS_NOATIME},
+ {"bind", MS_TYPE, MS_BIND, 0,},
+ {"dev", MS_NODEV, 0, MS_NODEV},
+ {"diratime", MS_NODIRATIME, 0, MS_NODIRATIME},
+ {"dirsync", MS_DIRSYNC, MS_DIRSYNC, 0},
+ {"exec", MS_NOEXEC, 0, MS_NOEXEC},
+ {"move", MS_TYPE, MS_MOVE, 0},
+ {"nodev", MS_NODEV, MS_NODEV, 0},
+ {"noexec", MS_NOEXEC, MS_NOEXEC, 0},
+ {"nosuid", MS_NOSUID, MS_NOSUID, 0},
+ {"recurse", MS_REC, MS_REC, 0},
+ {"remount", MS_TYPE, MS_REMOUNT, 0},
+ {"ro", MS_RDONLY, MS_RDONLY, 0},
+ {"rw", MS_RDONLY, 0, MS_RDONLY},
+ {"suid", MS_NOSUID, 0, MS_NOSUID},
+ {"sync", MS_SYNCHRONOUS, MS_SYNCHRONOUS, 0},
+ {"verbose", MS_VERBOSE, MS_VERBOSE, 0},
+};
+
+static void add_extra_option(struct extra_opts *extra, char *s)
+{
+ int len = strlen(s);
+ int newlen = extra->used_size + len;
+
+ if (extra->str)
+ len++; /* +1 for ',' */
+
+ if (newlen >= extra->alloc_size) {
+ char *new;
+
+ new = realloc(extra->str, newlen + 1); /* +1 for NUL */
+ if (!new)
+ return;
+
+ extra->str = new;
+ extra->end = extra->str + extra->used_size;
+ extra->alloc_size = newlen;
+ }
+
+ if (extra->used_size) {
+ *extra->end = ',';
+ extra->end++;
+ }
+ strcpy(extra->end, s);
+ extra->used_size += len;
+
+}
+
+unsigned long
+parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra)
+{
+ char *s;
+
+ while ((s = strsep(&arg, ",")) != NULL) {
+ char *opt = s;
+ unsigned int i;
+ int res, no = s[0] == 'n' && s[1] == 'o';
+
+ if (no)
+ s += 2;
+
+ for (i = 0, res = 1; i < ARRAY_SIZE(options); i++) {
+ res = strcmp(s, options[i].str);
+
+ if (res == 0) {
+ rwflag &= ~options[i].rwmask;
+ if (no)
+ rwflag |= options[i].rwnoset;
+ else
+ rwflag |= options[i].rwset;
+ }
+ if (res <= 0)
+ break;
+ }
+
+ if (res != 0 && s[0]) {
+ if (!strcmp(opt, "defaults"))
+ rwflag &= ~(MS_RDONLY|MS_NOSUID|MS_NODEV|
+ MS_NOEXEC|MS_SYNCHRONOUS);
+ else
+ add_extra_option(extra, opt);
+ }
+ }
+
+ return rwflag;
+}
diff --git a/usr/utils/mount_opts.h b/usr/utils/mount_opts.h
new file mode 100644
index 0000000..cf47cae
--- /dev/null
+++ b/usr/utils/mount_opts.h
@@ -0,0 +1,26 @@
+#ifndef UTILS_MOUNT_OPTS_H
+#define UTILS_MOUNT_OPTS_H
+
+struct mount_opts {
+ const char str[8];
+ unsigned long rwmask;
+ unsigned long rwset;
+ unsigned long rwnoset;
+};
+
+struct extra_opts {
+ char *str;
+ char *end;
+ int used_size;
+ int alloc_size;
+};
+
+/*
+ * These options define the function of "mount(2)".
+ */
+#define MS_TYPE (MS_REMOUNT|MS_BIND|MS_MOVE)
+
+unsigned long
+parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra);
+
+#endif /* UTILS_MOUNT_OPTS_H */
diff --git a/usr/utils/mv.c b/usr/utils/mv.c
new file mode 100644
index 0000000..e3f38ed
--- /dev/null
+++ b/usr/utils/mv.c
@@ -0,0 +1,69 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <linux/limits.h>
+
+int main(int argc, char *argv[])
+{
+ int c, f;
+ char *p;
+ struct stat sb;
+
+ f = 0;
+ do {
+ c = getopt(argc, argv, "f");
+ if (c == EOF)
+ break;
+
+ switch (c) {
+
+ case 'f':
+ f = 1;
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ return 1;
+ }
+
+ } while (1);
+
+ if (optind == argc) {
+ fprintf(stderr, "Usage: %s [-f] source dest\n", argv[0]);
+ return 1;
+ }
+
+ memset(&sb, 0, sizeof(struct stat));
+ if (stat(argv[argc - 1], &sb) < 0 && argc - optind > 2) {
+ if (!(S_ISDIR(sb.st_mode))) {
+ fprintf(stderr,
+ "multiple targets and %s is not a directory\n",
+ argv[argc - 1]);
+ return 1;
+ }
+ }
+
+ for (c = optind; c < argc - 1; c++) {
+ char target[PATH_MAX];
+
+ p = strrchr(argv[c], '/');
+ p++;
+
+ if (S_ISDIR(sb.st_mode))
+ snprintf(target, PATH_MAX, "%s/%s", argv[argc - 1], p);
+ else
+ snprintf(target, PATH_MAX, "%s", argv[argc - 1]);
+
+ if (f)
+ unlink(target);
+
+ if (rename(argv[c], target) == -1)
+ perror(target);
+ }
+
+ return 0;
+}
diff --git a/usr/utils/nuke.c b/usr/utils/nuke.c
new file mode 100644
index 0000000..93a04af
--- /dev/null
+++ b/usr/utils/nuke.c
@@ -0,0 +1,121 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2004-2006 H. Peter Anvin - All Rights Reserved
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * Simple program which does the same thing as rm -rf, except it takes
+ * no options and can therefore not get confused by filenames starting
+ * with -. Similarly, an empty list of inputs is assumed to mean don't
+ * do anything.
+ */
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static const char *program;
+
+static int nuke(const char *what);
+
+static int nuke_dirent(int len, const char *dir, const char *name)
+{
+ int bytes = len + strlen(name) + 2;
+ char path[bytes];
+ int xlen;
+
+ xlen = snprintf(path, bytes, "%s/%s", dir, name);
+ assert(xlen < bytes);
+
+ return nuke(path);
+}
+
+/* Wipe the contents of a directory, but not the directory itself */
+static int nuke_dir(const char *what)
+{
+ int len = strlen(what);
+ DIR *dir;
+ struct dirent *d;
+ int err = 0;
+
+ dir = opendir(what);
+ if (!dir) {
+ /* EACCES means we can't read it. Might be empty and removable;
+ if not, the rmdir() in nuke() will trigger an error. */
+ return (errno == EACCES) ? 0 : errno;
+ }
+
+ while ((d = readdir(dir))) {
+ /* Skip . and .. */
+ if (d->d_name[0] == '.' &&
+ (d->d_name[1] == '\0' ||
+ (d->d_name[1] == '.' && d->d_name[2] == '\0')))
+ continue;
+
+ err = nuke_dirent(len, what, d->d_name);
+ if (err) {
+ closedir(dir);
+ return err;
+ }
+ }
+
+ closedir(dir);
+
+ return 0;
+}
+
+static int nuke(const char *what)
+{
+ int rv;
+ int err = 0;
+
+ rv = unlink(what);
+ if (rv < 0) {
+ if (errno == EISDIR) {
+ /* It's a directory. */
+ err = nuke_dir(what);
+ if (!err)
+ rmdir(what);
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int i;
+
+ program = argv[0];
+
+ for (i = 1; i < argc; i++)
+ nuke(argv[i]);
+
+ return 0;
+}
diff --git a/usr/utils/pivot_root.c b/usr/utils/pivot_root.c
new file mode 100644
index 0000000..41b2ea0
--- /dev/null
+++ b/usr/utils/pivot_root.c
@@ -0,0 +1,19 @@
+/* Change the root file system */
+
+/* Written 2000 by Werner Almesberger */
+
+#include <stdio.h>
+#include <sys/mount.h>
+
+int main(int argc, const char **argv)
+{
+ if (argc != 3) {
+ fprintf(stderr, "Usage: %s new_root put_old\n", argv[0]);
+ return 1;
+ }
+ if (pivot_root(argv[1], argv[2]) < 0) {
+ perror("pivot_root");
+ return 1;
+ }
+ return 0;
+}
diff --git a/usr/utils/readlink.c b/usr/utils/readlink.c
new file mode 100644
index 0000000..1f16c02
--- /dev/null
+++ b/usr/utils/readlink.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+const char *progname;
+
+static __noreturn usage(void)
+{
+ fprintf(stderr, "Usage: %s [-f] link...\n", progname);
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ int c, f_flag = 0;
+ const char *name;
+ char link_name[PATH_MAX];
+ int rv;
+
+ progname = argv[0];
+
+ do {
+ c = getopt(argc, argv, "f");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'f':
+ f_flag = 1;
+ break;
+
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ progname, optopt);
+ usage();
+ }
+ } while (1);
+
+ if (optind == argc)
+ usage();
+
+ argv += optind;
+ while ((name = *argv++)) {
+ if (f_flag)
+ rv = realpath(name, link_name) ? strlen(link_name) : -1;
+ else
+ rv = readlink(name, link_name, sizeof link_name - 1);
+ if (rv < 0) {
+ perror(name);
+ exit(1);
+ }
+ link_name[rv] = '\n';
+ _fwrite(link_name, rv+1, stdout);
+ }
+
+ return 0;
+}
diff --git a/usr/utils/sleep.c b/usr/utils/sleep.c
new file mode 100644
index 0000000..991cdbe
--- /dev/null
+++ b/usr/utils/sleep.c
@@ -0,0 +1,26 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <errno.h>
+
+int main(int argc, char *argv[])
+{
+ struct timespec ts;
+ char *p;
+
+ if (argc != 2)
+ goto err;
+
+ p = strtotimespec(argv[1], &ts);
+ if (*p)
+ goto err;
+
+ while (nanosleep(&ts, &ts) == -1 && errno == EINTR)
+ ;
+
+ return 0;
+
+err:
+ fprintf(stderr, "Usage: %s seconds[.fraction]\n", argv[0]);
+ return 1;
+}
diff --git a/usr/utils/sync.c b/usr/utils/sync.c
new file mode 100644
index 0000000..de3093c
--- /dev/null
+++ b/usr/utils/sync.c
@@ -0,0 +1,7 @@
+#include <unistd.h>
+
+int main(void)
+{
+ sync();
+ return 0;
+}
diff --git a/usr/utils/true.c b/usr/utils/true.c
new file mode 100644
index 0000000..31dbf45
--- /dev/null
+++ b/usr/utils/true.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+ return 0;
+}
diff --git a/usr/utils/umount.c b/usr/utils/umount.c
new file mode 100644
index 0000000..41275f7
--- /dev/null
+++ b/usr/utils/umount.c
@@ -0,0 +1,51 @@
+/*
+ * by rmk
+ */
+#include <sys/mount.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+char *progname;
+
+int main(int argc, char *argv[])
+{
+ int c, flag = 0;
+
+ progname = argv[0];
+
+ do {
+ c = getopt(argc, argv, "fli");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'f':
+ flag |= MNT_FORCE;
+ break;
+ case 'l':
+ flag |= MNT_DETACH;
+ break;
+ case 'i':
+ /* ignore for now; no support for umount helpers */
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ progname, optopt);
+ exit(1);
+ }
+ } while (1);
+
+ if (optind + 1 != argc) {
+ fprintf(stderr, "Usage: %s [-f] [-l] [-i] mntpoint\n",
+ progname);
+ return 1;
+ }
+
+ if (umount2(argv[optind], flag) == -1) {
+ perror("umount2");
+ return 255;
+ }
+
+ return 0;
+}
diff --git a/usr/utils/uname.c b/usr/utils/uname.c
new file mode 100644
index 0000000..6ea4dbe
--- /dev/null
+++ b/usr/utils/uname.c
@@ -0,0 +1,155 @@
+/*
+ * by tlh
+ *
+ * The uname program for system information: kernel name, kernel
+ * release, kernel release, machine, processor, platform, os and
+ * hostname.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/utsname.h>
+
+enum uname_fields {
+ UN_SYSNAME,
+ UN_NODENAME,
+ UN_RELEASE,
+ UN_VERSION,
+ UN_MACHINE,
+#if NOT_IMPLEMENTED_PROCESSOR
+ UN_PROCESSOR,
+#endif
+ UN_HARDWARE,
+#if NOT_IMPLEMENTED_OS
+ UN_OS,
+#endif
+ UN_NR_FIELDS
+};
+
+static void usage(FILE *stream, const char *progname)
+{
+ fprintf(stream,
+ "Usage: %s [OPTION] . . .\n"
+ "Print system information, No options defaults to -s.\n"
+ "\n"
+ " -a print all the information in the same order as follows below\n"
+ " -s kernel name\n"
+ " -n network node name (hostname)\n"
+ " -r kernel release\n"
+ " -v kernel version\n" " -m machine hardware name\n"
+#if NOT_IMPLEMENTED_PROCESSOR
+ " -p processor type\n"
+#endif
+ " -i hardware platform\n"
+#if NOT_IMPLEMENTED_OS
+ " -o operating system\n"
+#endif
+ "\n" " -h help/usage\n" "\n", progname);
+}
+
+static char *make_hardware(const char *machine)
+{
+ char *hardware;
+
+ hardware = strdup(machine);
+ if (!hardware) {
+ fprintf(stderr, "strdup() failed: %s\n", strerror(errno));
+ goto end;
+ }
+ if (strlen(hardware) == 4
+ && hardware[0] == 'i' && hardware[2] == '8' && hardware[3] == '6') {
+ hardware[1] = '3';
+ }
+end:
+ return hardware;
+}
+
+int main(int argc, char *argv[])
+{
+ int ec = 1;
+ int opt;
+ int i;
+ int nr_pr;
+ struct utsname buf;
+ char *uname_fields[UN_NR_FIELDS] = { NULL };
+
+ if (-1 == uname(&buf)) {
+ fprintf(stderr, "uname() failure: %s\n", strerror(errno));
+ goto end;
+ }
+
+ if (1 == argc)
+ /* no options given - default to -s */
+ uname_fields[UN_SYSNAME] = buf.sysname;
+
+ while ((opt = getopt(argc, argv, "asnrvmpioh")) != -1) {
+ switch (opt) {
+ case 'a':
+ uname_fields[UN_SYSNAME] = buf.sysname;
+ uname_fields[UN_NODENAME] = buf.nodename;
+ uname_fields[UN_RELEASE] = buf.release;
+ uname_fields[UN_VERSION] = buf.version;
+ uname_fields[UN_MACHINE] = buf.machine;
+ uname_fields[UN_HARDWARE] = make_hardware(buf.machine);
+ if (!uname_fields[UN_HARDWARE])
+ goto end;
+ break;
+ case 's':
+ uname_fields[UN_SYSNAME] = buf.sysname;
+ break;
+ case 'n':
+ uname_fields[UN_NODENAME] = buf.nodename;
+ break;
+ case 'r':
+ uname_fields[UN_RELEASE] = buf.release;
+ break;
+ case 'v':
+ uname_fields[UN_VERSION] = buf.version;
+ break;
+ case 'm':
+ uname_fields[UN_MACHINE] = buf.machine;
+ break;
+#if NOT_IMPLEMENTED_PROCESSOR
+ case 'p':
+ break;
+#endif
+ case 'i':
+ uname_fields[UN_HARDWARE] = make_hardware(buf.machine);
+ if (!uname_fields[UN_HARDWARE])
+ goto end;
+ break;
+#if NOT_IMPLEMENTED_OS
+ case 'o':
+ break;
+#endif
+ case 'h':
+ usage(stdout, argv[0]);
+ ec = 0;
+ goto end;
+ break;
+ default:
+ usage(stderr, argv[0]);
+ goto end;
+ break;
+ }
+ }
+
+ for (nr_pr = 0, i = UN_SYSNAME; i < UN_NR_FIELDS; i++) {
+ if (!uname_fields[i])
+ continue;
+ if (nr_pr)
+ fputc(' ', stdout);
+ fputs(uname_fields[i], stdout);
+ nr_pr++;
+ }
+ fputc('\n', stdout);
+
+ ec = 0;
+
+end:
+ if (uname_fields[UN_HARDWARE])
+ free(uname_fields[UN_HARDWARE]);
+ return ec;
+}