Module Name: src
Committed By: mbalmer
Date: Sat Jul 25 16:18:09 UTC 2009
Modified Files:
src/usr.sbin/gpioctl: Makefile gpioctl.8 gpioctl.c
Added Files:
src/usr.sbin/gpioctl: strtonum.c
Log Message:
Reworked gpioctl(8) command to reflect recent gpio(4) changes.
To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 src/usr.sbin/gpioctl/Makefile
cvs rdiff -u -r1.4 -r1.5 src/usr.sbin/gpioctl/gpioctl.8
cvs rdiff -u -r1.5 -r1.6 src/usr.sbin/gpioctl/gpioctl.c
cvs rdiff -u -r0 -r1.1 src/usr.sbin/gpioctl/strtonum.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/usr.sbin/gpioctl/Makefile
diff -u src/usr.sbin/gpioctl/Makefile:1.3 src/usr.sbin/gpioctl/Makefile:1.4
--- src/usr.sbin/gpioctl/Makefile:1.3 Wed Apr 22 15:23:03 2009
+++ src/usr.sbin/gpioctl/Makefile Sat Jul 25 16:18:09 2009
@@ -1,7 +1,7 @@
-# $NetBSD: Makefile,v 1.3 2009/04/22 15:23:03 lukem Exp $
+# $NetBSD: Makefile,v 1.4 2009/07/25 16:18:09 mbalmer Exp $
PROG= gpioctl
-SRCS= gpioctl.c
+SRCS= gpioctl.c strtonum.c
MAN= gpioctl.8
Index: src/usr.sbin/gpioctl/gpioctl.8
diff -u src/usr.sbin/gpioctl/gpioctl.8:1.4 src/usr.sbin/gpioctl/gpioctl.8:1.5
--- src/usr.sbin/gpioctl/gpioctl.8:1.4 Wed Jan 9 15:56:27 2008
+++ src/usr.sbin/gpioctl/gpioctl.8 Sat Jul 25 16:18:09 2009
@@ -1,6 +1,6 @@
-.\" $NetBSD: gpioctl.8,v 1.4 2008/01/09 15:56:27 xtraeme Exp $
-.\" $OpenBSD: gpioctl.8,v 1.5 2004/12/02 05:11:40 grange Exp $
+.\" $NetBSD: gpioctl.8,v 1.5 2009/07/25 16:18:09 mbalmer Exp $
.\"
+.\" Copyright (c) 2009 Marc Balmer <[email protected]>
.\" Copyright (c) 2004 Alexander Yurchenko <[email protected]>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
@@ -15,66 +15,71 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd January 9, 2008
+.Dd July 15, 2009
.Dt GPIOCTL 8
.Os
.Sh NAME
.Nm gpioctl
.Nd control GPIO devices
.Sh SYNOPSIS
-.Nm
-.Op Fl hq
-.Op Fl d Ar device
-.Op Ar pin
+.Nm gpioctl
+.Op Fl q
+.Ar device
+attach
+.Ar device
+.Ar offset
+.Ar mask
+.Nm gpioctl
+.Op Fl q
+.Ar device
+detach
+.Ar device
+.Nm gpioctl
+.Op Fl q
+.Ar device
+.Ar pin
.Op Ar 0 | 1 | 2
-.Nm
-.Op Fl hq
-.Op Fl d Ar device
-.Fl c
+.Nm gpioctl
+.Op Fl q
+.Ar device
.Ar pin
+.Op Ar on | off | toggle
+.Nm gpioctl
+.Op Fl q
+.Ar device
+.Ar pin
+set
.Op Ar flags
+.Op Ar name
+.Nm gpioctl
+.Op Fl q
+.Ar device
+.Ar pin
+unset
.Sh DESCRIPTION
The
.Nm
-program allows manipulation of
-.Tn GPIO
+program allows manipulation of GPIO
(General Purpose Input/Output) device pins.
-Such devices can be either part of the chipset or embedded
-.Tn CPU ,
+Such devices can be either part of the chipset or embedded CPU,
or a separate chip.
-The usual way of using
-.Tn GPIO
-is to connect some simple devices such as LEDs, 1-wire thermal sensors,
-etc., to its pins.
+The usual way of using GPIO
+is to connect some simple devices such as LEDs and 1-wire thermal sensors
+to its pins.
.Pp
-Each
-.Tn GPIO
-device has an associated device file in the
+Each GPIO device has an associated device file in the
.Pa /dev
directory.
-By default
-.Nm
-uses
-.Pa /dev/gpio0 ,
-which corresponds to the first found
-.Tn GPIO
-device in the system.
-If more than one
-.Tn GPIO
-device is present, an alternative device file can be specified with the
-.Fl d
-option in order to access a particular
-.Tn GPIO
-device.
-.Pp
-When executed without any arguments,
-.Nm
-reads information about the
-.Tn GPIO
-device and displays it.
+.Ar device
+can be specified with or without the
+.Pa /dev
+prefix.
+For example,
+.Pa /dev/gpio0
+or
+.Pa gpio0 .
.Pp
-.Tn GPIO
-pins can be either
+GPIO pins can be either
.Dq read
or
.Dq written
@@ -82,9 +87,7 @@
If only a
.Ar pin
number is specified on the command line, the pin state will be read
-from the
-.Tn GPIO
-controller and displayed.
+from the GPIO controller and displayed.
To write to a pin, a value must be specified after the
.Ar pin
number.
@@ -92,15 +95,28 @@
A value of 2 has a special meaning: it
.Dq toggles
the pin, i.e. changes its state to the opposite.
+Instead of the numerical values, the word
+.Ar on ,
+.Ar off ,
+or
+.Ar toggle
+can be used.
.Pp
-Each pin can be configured with different flags with the
-.Fl c
-option.
-The following configuration flags are supported by the
-.Tn GPIO
-framework:
+Only pins that have been configured at securelevel 0, typically during system
+startup, are accessible once the securelevel has been raised.
+Pins can be given symbolic names for easier use.
+Besides using individual pins, device drivers that use GPIO pins can be
+attached to a
+.Xr gpio 4
+device using the
+.Nm
+command.
+.Pp
+The following configuration
+.Ar flags
+are supported by the GPIO framework:
.Pp
-.Bl -tag -width XXXXXXX -offset indent -compact
+.Bl -tag -width Ds -offset indent -compact
.It in
input direction
.It out
@@ -123,31 +139,23 @@
invert output
.El
.Pp
-Note that not all the flags can be supported by the particular
-.Tn GPIO
-controller.
-The list of supported flags is always displayed when executing
-.Nm
-with the
-.Fl c
-option.
-If only a
-.Ar pin
-number is specified on the command line, the current pin flags will be
-displayed.
-To change pin flags, a new flags set separated by spaces must be
-specified after the
-.Ar pin
-number.
+Note that not all the flags may be supported by the particular GPIO controller.
.Pp
-The
-.Fl q
-option causes
+When executed with only the
+.Xr gpio 4
+device name as argument,
.Nm
-to operate quietly i.e. nothing is printed to stdout.
-The
-.Fl h
-option displays a usage summary.
+reads information about the
+.Tn GPIO
+device and displays it.
+At securelevel 0 the number of physically available pins is displayed,
+at higher securelevels the number of configured (set) pins is displayed.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl q
+Operate quietly i.e. nothing is printed to stdout.
+.El
.Sh FILES
.Bl -tag -width "/dev/gpiou" -compact
.It /dev/gpio Ns Ar u
@@ -158,17 +166,33 @@
.Sh EXAMPLES
Configure pin 20 to have push-pull output:
.Pp
-.Dl # gpioctl -c 20 out pp
+.Dl # gpioctl gpio0 20 set out pp
.Pp
Write logical 1 to pin 20:
.Pp
-.Dl # gpioctl 20 1
+.Dl # gpioctl gpio0 20 1
+.Pp
+Attach a
+.Xr onewire 4
+bus on a
+.Xr gpioow 4
+device on pin 4:
+.Pp
+.Dl # gpioctl gpio0 attach gpioow 4 0x01
+.Pp
+Detach the gpioow0 device:
+.Pp
+.Dl # gpioctl gpio0 detach gpioow0
+.Pp
+Configure pin 5 as output and name it error_led:
+.Pp
+.Dl # gpioctl gpio0 5 set out error_led
+.Pp
+Toggle the error_led:
+.Pp
+.Dl # gpioctl gpio0 error_led 2
.Sh SEE ALSO
-.Xr elansc 4 ,
-.Xr gcscpcib 4 ,
-.Xr gpio 4 ,
-.Xr gscpcib 4 ,
-.Xr nsclpcsio 4
+.Xr gpio 4
.Sh HISTORY
The
.Nm
@@ -181,3 +205,5 @@
.Nm
program was written by
.An Alexander Yurchenko Aq [email protected] .
+Device attachment was added by
+.An Marc Balmer Aq [email protected] .
Index: src/usr.sbin/gpioctl/gpioctl.c
diff -u src/usr.sbin/gpioctl/gpioctl.c:1.5 src/usr.sbin/gpioctl/gpioctl.c:1.6
--- src/usr.sbin/gpioctl/gpioctl.c:1.5 Wed Jan 9 16:08:33 2008
+++ src/usr.sbin/gpioctl/gpioctl.c Sat Jul 25 16:18:09 2009
@@ -1,6 +1,7 @@
-/* $NetBSD: gpioctl.c,v 1.5 2008/01/09 16:08:33 xtraeme Exp $ */
-/* $OpenBSD: gpioctl.c,v 1.2 2004/08/08 00:05:09 deraadt Exp $ */
+/* $NetBSD: gpioctl.c,v 1.6 2009/07/25 16:18:09 mbalmer Exp $ */
+
/*
+ * Copyright (c) 2008 Marc Balmer <[email protected]>
* Copyright (c) 2004 Alexander Yurchenko <[email protected]>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -25,25 +26,34 @@
#include <sys/ioctl.h>
#include <err.h>
+#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
+#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#define _PATH_DEV_GPIO "/dev/gpio0"
-static const char *device = _PATH_DEV_GPIO;
-static int devfd = -1;
-static int quiet = 0;
-
-static void getinfo(void);
-static void pinread(int);
-static void pinwrite(int, int);
-static void pinctl(int, char *[], int);
-static void usage(void);
+char *dev;
+int devfd = -1;
+int quiet = 0;
+
+void getinfo(void);
+void gpioread(int, char *);
+void gpiowrite(int, char *, int);
+void gpioset(int pin, char *name, int flags, char *alias);
+void gpiounset(int pin, char *name);
+void devattach(char *, int, u_int32_t);
+void devdetach(char *);
+
+__dead void usage(void);
+
+extern long long strtonum(const char *numstr, long long minval,
+ long long maxval, const char **errstrp);
-static const struct bitstr {
+const struct bitstr {
unsigned int mask;
const char *string;
} pinflags[] = {
@@ -56,33 +66,28 @@
{ GPIO_PIN_PULLUP, "pu" },
{ GPIO_PIN_PULLDOWN, "pd" },
{ GPIO_PIN_INVIN, "iin" },
- { GPIO_PIN_INVOUT, "iiout" },
+ { GPIO_PIN_INVOUT, "iout" },
{ 0, NULL },
};
int
main(int argc, char *argv[])
{
- int ch;
+ const struct bitstr *bs;
+ int pin, ch, n, fl = 0, value = 0;
+ const char *errstr;
char *ep;
- int do_ctl = 0;
- int pin = 0, value = 0;
-
- setprogname(argv[0]);
+ int ga_offset = -1;
+ u_int32_t ga_mask = 0;
+ long lval;
+ char *nam = NULL;
+ char devn[32];
- while ((ch = getopt(argc, argv, "cd:hq")) != -1)
+ while ((ch = getopt(argc, argv, "q")) != -1)
switch (ch) {
- case 'c':
- do_ctl = 1;
- break;
- case 'd':
- device = optarg;
- break;
case 'q':
quiet = 1;
break;
- case 'h':
- case '?':
default:
usage();
/* NOTREACHED */
@@ -90,129 +95,199 @@
argc -= optind;
argv += optind;
- if (argc > 0) {
- pin = strtol(argv[0], &ep, 10);
- if (*argv[0] == '\0' || *ep != '\0' || pin < 0)
- errx(EXIT_FAILURE, "%s: invalid pin", argv[0]);
+ if (argc < 1)
+ usage();
+ dev = argv[0];
+
+ if (strncmp(_PATH_DEV, dev, sizeof(_PATH_DEV) - 1)) {
+ (void)snprintf(devn, sizeof(devn), "%s%s", _PATH_DEV, dev);
+ dev = devn;
}
- if ((devfd = open(device, O_RDWR)) == -1)
- err(EXIT_FAILURE, "%s", device);
+ if ((devfd = open(dev, O_RDWR)) == -1)
+ err(EXIT_FAILURE, "%s", dev);
- if (argc == 0 && !do_ctl) {
+ if (argc == 1) {
getinfo();
- } else if (argc == 1) {
- if (do_ctl)
- pinctl(pin, NULL, 0);
- else
- pinread(pin);
- } else if (argc > 1) {
- if (do_ctl) {
- pinctl(pin, argv + 1, argc - 1);
- } else {
- value = strtol(argv[1], &ep, 10);
- if (*argv[1] == '\0' || *ep != '\0')
- errx(EXIT_FAILURE, "%s: invalid value",
- argv[1]);
- pinwrite(pin, value);
- }
+ return EXIT_SUCCESS;
+ }
+
+ if (!strcmp(argv[1], "attach")) {
+ char *driver, *offset, *mask;
+
+ if (argc != 5)
+ usage();
+
+ driver = argv[2];
+ offset = argv[3];
+ mask = argv[4];
+
+ ga_offset = strtonum(offset, 0, INT_MAX, &errstr);
+ if (errstr)
+ errx(EXIT_FAILURE, "offset is %s: %s", errstr, offset);
+ lval = strtol(mask, &ep, 0);
+ if (*mask == '\0' || *ep != '\0')
+ errx(EXIT_FAILURE, "invalid mask (not a number)");
+ if ((errno == ERANGE && (lval == LONG_MAX
+ || lval == LONG_MIN)) || (unsigned long)lval > UINT_MAX)
+ errx(EXIT_FAILURE, "mask out of range");
+ ga_mask = lval;
+ devattach(driver, ga_offset, ga_mask);
+ return EXIT_SUCCESS;
+ } else if (!strcmp(argv[1], "detach")) {
+ if (argc != 3)
+ usage();
+ devdetach(argv[2]);
} else {
- usage();
- /* NOTREACHED */
+ char *nm = NULL;
+
+ /* expecting a pin number or name */
+ pin = strtonum(argv[1], 0, INT_MAX, &errstr);
+ if (errstr)
+ nm = argv[1]; /* try named pin */
+ if (argc > 2) {
+ if (!strcmp(argv[2], "set")) {
+ for (n = 3; n < argc; n++) {
+ for (bs = pinflags; bs->string != NULL;
+ bs++) {
+ if (!strcmp(argv[n],
+ bs->string)) {
+ fl |= bs->mask;
+ break;
+ }
+ }
+ if (bs->string == NULL)
+ nam = argv[n];
+ }
+ gpioset(pin, nm, fl, nam);
+ } else if (!strcmp(argv[2], "unset")) {
+ gpiounset(pin, nm);
+ } else {
+ value = strtonum(argv[2], INT_MIN, INT_MAX,
+ &errstr);
+ if (errstr) {
+ if (!strcmp(argv[2], "on"))
+ value = 1;
+ else if (!strcmp(argv[2], "off"))
+ value = 0;
+ else if (!strcmp(argv[2], "toggle"))
+ value = 2;
+ else
+ errx(EXIT_FAILURE,
+ "%s: invalid value",
+ argv[2]);
+ }
+ gpiowrite(pin, nm, value);
+ }
+ } else
+ gpioread(pin, nm);
}
return EXIT_SUCCESS;
}
-static void
+void
getinfo(void)
{
struct gpio_info info;
- memset(&info, 0, sizeof(info));
if (ioctl(devfd, GPIOINFO, &info) == -1)
err(EXIT_FAILURE, "GPIOINFO");
if (quiet)
return;
- printf("%s: %d pins\n", device, info.gpio_npins);
+ printf("%s: %d pins\n", dev, info.gpio_npins);
}
-static void
-pinread(int pin)
+void
+gpioread(int pin, char *gp_name)
{
- struct gpio_pin_op op;
+ struct gpio_req req;
- memset(&op, 0, sizeof(op));
- op.gp_pin = pin;
- if (ioctl(devfd, GPIOPINREAD, &op) == -1)
- err(EXIT_FAILURE, "GPIOPINREAD");
+ memset(&req, 0, sizeof(req));
+ if (gp_name != NULL)
+ strlcpy(req.gp_name, gp_name, sizeof(req.gp_name));
+ else
+ req.gp_pin = pin;
+
+ if (ioctl(devfd, GPIOREAD, &req) == -1)
+ err(EXIT_FAILURE, "GPIOREAD");
if (quiet)
return;
- printf("pin %d: state %d\n", pin, op.gp_value);
+ if (gp_name)
+ printf("pin %s: state %d\n", gp_name, req.gp_value);
+ else
+ printf("pin %d: state %d\n", pin, req.gp_value);
}
-static void
-pinwrite(int pin, int value)
+void
+gpiowrite(int pin, char *gp_name, int value)
{
- struct gpio_pin_op op;
+ struct gpio_req req;
if (value < 0 || value > 2)
errx(EXIT_FAILURE, "%d: invalid value", value);
- memset(&op, 0, sizeof(op));
- op.gp_pin = pin;
- op.gp_value = (value == 0 ? GPIO_PIN_LOW : GPIO_PIN_HIGH);
+ memset(&req, 0, sizeof(req));
+ if (gp_name != NULL)
+ strlcpy(req.gp_name, gp_name, sizeof(req.gp_name));
+ else
+ req.gp_pin = pin;
+ req.gp_value = (value == 0 ? GPIO_PIN_LOW : GPIO_PIN_HIGH);
if (value < 2) {
- if (ioctl(devfd, GPIOPINWRITE, &op) == -1)
- err(EXIT_FAILURE, "GPIOPINWRITE");
+ if (ioctl(devfd, GPIOWRITE, &req) == -1)
+ err(EXIT_FAILURE, "GPIOWRITE");
} else {
- if (ioctl(devfd, GPIOPINTOGGLE, &op) == -1)
- err(EXIT_FAILURE, "GPIOPINTOGGLE");
+ if (ioctl(devfd, GPIOTOGGLE, &req) == -1)
+ err(EXIT_FAILURE, "GPIOTOGGLE");
}
if (quiet)
return;
- printf("pin %d: state %d -> %d\n", pin, op.gp_value,
- (value < 2 ? value : 1 - op.gp_value));
+ if (gp_name)
+ printf("pin %s: state %d -> %d\n", gp_name, req.gp_value,
+ (value < 2 ? value : 1 - req.gp_value));
+ else
+ printf("pin %d: state %d -> %d\n", pin, req.gp_value,
+ (value < 2 ? value : 1 - req.gp_value));
}
-static void
-pinctl(int pin, char *flags[], int nflags)
+void
+gpioset(int pin, char *name, int fl, char *alias)
{
- struct gpio_pin_ctl ctl;
- int fl = 0;
+ struct gpio_set set;
const struct bitstr *bs;
- int i;
- memset(&ctl, 0, sizeof(ctl));
- ctl.gp_pin = pin;
- if (flags != NULL) {
- for (i = 0; i < nflags; i++)
- for (bs = pinflags; bs->string != NULL; bs++)
- if (strcmp(flags[i], bs->string) == 0) {
- fl |= bs->mask;
- break;
- }
- }
- ctl.gp_flags = fl;
- if (ioctl(devfd, GPIOPINCTL, &ctl) == -1)
- err(EXIT_FAILURE, "GPIOPINCTL");
+ memset(&set, 0, sizeof(set));
+ if (name != NULL)
+ strlcpy(set.gp_name, name, sizeof(set.gp_name));
+ else
+ set.gp_pin = pin;
+ set.gp_flags = fl;
+
+ if (alias != NULL)
+ strlcpy(set.gp_name2, alias, sizeof(set.gp_name2));
+
+ if (ioctl(devfd, GPIOSET, &set) == -1)
+ err(EXIT_FAILURE, "GPIOSET");
if (quiet)
return;
- printf("pin %d: caps:", pin);
+ if (name != NULL)
+ printf("pin %s: caps:", name);
+ else
+ printf("pin %d: caps:", pin);
for (bs = pinflags; bs->string != NULL; bs++)
- if (ctl.gp_caps & bs->mask)
+ if (set.gp_caps & bs->mask)
printf(" %s", bs->string);
printf(", flags:");
for (bs = pinflags; bs->string != NULL; bs++)
- if (ctl.gp_flags & bs->mask)
+ if (set.gp_flags & bs->mask)
printf(" %s", bs->string);
if (fl > 0) {
printf(" ->");
@@ -223,13 +298,58 @@
printf("\n");
}
-static void
+void
+gpiounset(int pin, char *name)
+{
+ struct gpio_set set;
+
+ memset(&set, 0, sizeof(set));
+ if (name != NULL)
+ strlcpy(set.gp_name, name, sizeof(set.gp_name));
+ else
+ set.gp_pin = pin;
+
+ if (ioctl(devfd, GPIOUNSET, &set) == -1)
+ err(EXIT_FAILURE, "GPIOUNSET");
+}
+
+void
+devattach(char *dvname, int offset, u_int32_t mask)
+{
+ struct gpio_attach attach;
+
+ memset(&attach, 0, sizeof(attach));
+ strlcpy(attach.ga_dvname, dvname, sizeof(attach.ga_dvname));
+ attach.ga_offset = offset;
+ attach.ga_mask = mask;
+ if (ioctl(devfd, GPIOATTACH, &attach) == -1)
+ err(EXIT_FAILURE, "GPIOATTACH");
+}
+
+void
+devdetach(char *dvname)
+{
+ struct gpio_attach attach;
+
+ memset(&attach, 0, sizeof(attach));
+ strlcpy(attach.ga_dvname, dvname, sizeof(attach.ga_dvname));
+ if (ioctl(devfd, GPIODETACH, &attach) == -1)
+ err(EXIT_FAILURE, "GPIODETACH");
+}
+
+void
usage(void)
{
- fprintf(stderr, "usage: %s [-hq] [-d device] [pin] [0 | 1 | 2]\n",
- getprogname());
- fprintf(stderr, " %s [-hq] [-d device] -c pin [flags]\n",
- getprogname());
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-q] device [pin] [0 | 1 | 2 | "
+ "on | off | toggle]\n", __progname);
+ fprintf(stderr, " %s [-q] device pin set [flags] [name]\n",
+ __progname);
+ fprintf(stderr, " %s [-q] device pin unset\n", __progname);
+ fprintf(stderr, " %s [-q] device attach device offset mask\n",
+ __progname);
+ fprintf(stderr, " %s [-q] device detach device\n", __progname);
exit(EXIT_FAILURE);
}
Added files:
Index: src/usr.sbin/gpioctl/strtonum.c
diff -u /dev/null src/usr.sbin/gpioctl/strtonum.c:1.1
--- /dev/null Sat Jul 25 16:18:09 2009
+++ src/usr.sbin/gpioctl/strtonum.c Sat Jul 25 16:18:09 2009
@@ -0,0 +1,69 @@
+/* $NetBSD: strtonum.c,v 1.1 2009/07/25 16:18:09 mbalmer Exp $ */
+/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */
+
+/*
+ * Copyright (c) 2004 Ted Unangst and Todd Miller
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#define INVALID 1
+#define TOOSMALL 2
+#define TOOLARGE 3
+
+long long strtonum(const char *numstr, long long minval, long long maxval,
+ const char **errstrp);
+
+long long
+strtonum(const char *numstr, long long minval, long long maxval,
+ const char **errstrp)
+{
+ long long ll = 0;
+ char *ep;
+ int error = 0;
+ struct errval {
+ const char *errstr;
+ int err;
+ } ev[4] = {
+ { NULL, 0 },
+ { "invalid", EINVAL },
+ { "too small", ERANGE },
+ { "too large", ERANGE },
+ };
+
+ ev[0].err = errno;
+ errno = 0;
+ if (minval > maxval)
+ error = INVALID;
+ else {
+ ll = strtoll(numstr, &ep, 10);
+ if (numstr == ep || *ep != '\0')
+ error = INVALID;
+ else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
+ error = TOOSMALL;
+ else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
+ error = TOOLARGE;
+ }
+ if (errstrp != NULL)
+ *errstrp = ev[error].errstr;
+ errno = ev[error].err;
+ if (error)
+ ll = 0;
+
+ return (ll);
+}