/*
 * mknod64(1) - 64-bit mknod using mknod64(2)
 *
 * 18-Apr-2003: initial version
 * Robert Love	<rml@tech9.net>
 *
 * Based on mknod(1), by David MacKenzie, part of the GNU coreutils
 * Copyright (C) 90, 91, 1995-2002 Free Software Foundation, Inc.
 *
 * This program is licensed under the GNU Public License version 2
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <getopt.h>

/*
 * glibc is not yet updated for mknod64(2) as of 2.3.2 (18-Apr-2003)...
 * so setup the syscalls ourselves
 */
#ifndef DONT_NEED_MKNOD64_SETUP
#include <linux/unistd.h>
#if defined(__i386__)
#define __NR_mknod64	268
#endif
extern long mknod64(const char *filename, int mode,
		    unsigned int devhi, unsigned int devlo);
_syscall4(long, mknod64, const char *, filename, int, mode,
	  unsigned int, devhi, unsigned int, devlo);
#endif /* DONT_NEED_MKNOD64_SETUP */

#define VERSION		"0.1"

void print_usage(const char *progname, int retval)
{
	fprintf(stderr, "usage: %s [OPTION] NAME TYPE [MAJOR MINOR]\n",
		progname);
	fprintf(stderr, "Create the special file NAME of the given TYPE\n\n");

	fprintf(stderr, "  -h, --help       display this help and exit\n");
	fprintf(stderr, "  -V, --version    display version info and exit\n\n");

	fprintf(stderr, "TYPE is one of b, c, u, or p for block (buffered)\n" \
			"device, character or unbuffered device, or FIFO,\n" \
			"respectively.  For types b, c, and u the MAJOR and\n" \
			"MINOR values must be specified.  For type p, they\n" \
			"must not be specified.\n");
	exit(retval);
}

int main(int argc, char **argv)
{
	mode_t newmode;
	int optc;
	mode_t node_type;
	unsigned int major, minor;

	static struct option const longopts[] = {
		{"help", 0, NULL, 'h' },
		{"version", 0, NULL, 'V' },
		{NULL, 0, NULL, 0}
	};

	while ((optc = getopt_long(argc, argv, "hV", longopts, NULL)) != -1) {
		switch (optc) {
		case 0:
			break;
		case 'h':
			print_usage(argv[0], 0);
			break;
		case 'V':
			fprintf(stderr, "mknod64 " VERSION "\n");
			fprintf(stderr, "Written by Robert Love\n");
			return 0;
			break;
		default:
			print_usage(argv[0], -1);
		}
	}

	newmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);

	if (argc - optind != 2 && argc - optind != 4) {
		fprintf(stderr, "incorrect number of arguments\n");
		print_usage(argv[0], -1);
	}

	switch (argv[optind + 1][0]) {
	case 'b':	/* block or buffered */
		node_type = S_IFBLK;	/* set this here too to avoid warning */
	case 'c':	/* character */
	case 'u':	/* unbuffered */
		node_type = S_IFCHR;
		if (argv[optind + 1][0] == 'b')
			node_type = S_IFBLK;

		if (argc - optind != 4) {
			fprintf(stderr, "you must specify a major and minor" \
				" device number!\n");
			return -1;
		}

		errno = 0;
		major = strtoul(argv[optind + 2], NULL, 0);
		if (errno) {
			perror("strtoul");
			fprintf(stderr, "error getting major number!\n");
			return -1;
		}

		minor = strtoul(argv[optind + 3], NULL, 0);
		if (errno) {
		perror("strtoul");
			fprintf(stderr, "error getting minor number!\n");
			return -1;
		}

#if 0
		printf("filename=%s type=%d(%c) major=%u minor=%u\n",
			argv[optind], node_type, argv[optind + 1][0],
			major, minor);
#endif
		if (mknod64(argv[optind], newmode | node_type, major, minor)) {
			perror("mknod64");
			fprintf(stderr, "failed to create device!\n");
			return -1;
		}

		break;

	case 'p':	/* fifo */
		if (argc - optind != 2) {
			fprintf(stderr,
				"do not specify device number for fifos!\n");
			return -1;
		}
		if (mkfifo(argv[optind], newmode))
			perror("mkfifo");
			fprintf(stderr, "failed to make fifo!\n");
			return -1;
		break;

	default:
		fprintf(stderr, "invalid device type\n!");
		return -1;
	}

	return 0;
}
