fsck.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 1993 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/vfstab.h>
#include <sys/mntent.h>
#include <locale.h>
#include <libintl.h>
#define ARGV_MAX 16
#define FSTYPE_MAX 8
#define VFS_PATH "/usr/lib/fs"
#define VFS_PATH2 "/etc/fs"
#define CHECK(xx, yy)\
if (xx == (yy)-1) {\
fprintf(stderr, gettext("%s: too many arguments\n"), myname); \
usage(); \
}
#define OPTION(flag)\
options++; \
nargv[nargc++] = flag; \
CHECK(nargc, ARGV_MAX); \
break
#define OPTARG(flag)\
nargv[nargc++] = flag; \
CHECK(nargc, ARGV_MAX); \
if (optarg) {\
nargv[nargc++] = optarg; \
CHECK(nargc, ARGV_MAX); \
}\
break
int nrun, ndisks;
int maxrun = 8; /* should be based on the machine resources */
extern char *optarg;
extern int optind;
extern char *default_fstype();
int nargc = 2;
int options = 0;
int mnt_passno = 0;
int exitstat = 0;
char *nargv[ARGV_MAX];
char *myname, *fstype;
char *malloc();
char vfstab[] = VFSTAB;
char pflg = 0, Vflg = 0;
/*
* Keep an idea of the last device arg type as a hint to the
* type of the next arg. In the case of mountall, it's very likely
* to be the same type and the next entry in the file. This should
* help speed vfstab lookups.
*/
enum dev_arg_t { UNKNOWN, SPECIAL, FSCKDEV, MOUNTPT };
enum dev_arg_t arg_hint = UNKNOWN;
static struct devlist {
char *name;
char *fsname;
pid_t pid;
struct devlist *nxt;
} *newdev(), *getdev();
/*
* private copy vfstab functions
*/
static struct vfstab vfsave = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
static int
vfdup(struct vfstab *vp)
{
if (vfsave.vfs_special != NULL) {
free(vfsave.vfs_special);
vfsave.vfs_special = NULL;
}
if ((vp->vfs_special != NULL) &&
((vfsave.vfs_special = strdup(vp->vfs_special)) == NULL)) {
perror(myname);
return (4); /* XXX */
}
if (vfsave.vfs_fsckdev != NULL) {
free(vfsave.vfs_fsckdev);
vfsave.vfs_fsckdev = NULL;
}
if ((vp->vfs_fsckdev != NULL) &&
((vfsave.vfs_fsckdev = strdup(vp->vfs_fsckdev)) == NULL)) {
perror(myname);
return (4); /* XXX */
}
if (vfsave.vfs_mountp != NULL) {
free(vfsave.vfs_mountp);
vfsave.vfs_mountp = NULL;
}
if ((vp->vfs_mountp != NULL) &&
((vfsave.vfs_mountp = strdup(vp->vfs_mountp)) == NULL)) {
perror(myname);
return (4); /* XXX */
}
if (vfsave.vfs_fstype != NULL) {
free(vfsave.vfs_fstype);
vfsave.vfs_fstype = NULL;
}
if ((vp->vfs_fstype != NULL) &&
((vfsave.vfs_fstype = strdup(vp->vfs_fstype)) == NULL)) {
perror(myname);
return (4); /* XXX */
}
if (vfsave.vfs_fsckpass != NULL) {
free(vfsave.vfs_fsckpass);
vfsave.vfs_fsckpass = NULL;
}
if ((vp->vfs_fsckpass != NULL) &&
((vfsave.vfs_fsckpass = strdup(vp->vfs_fsckpass)) == NULL)) {
perror(myname);
return (4); /* XXX */
}
if (vfsave.vfs_automnt != NULL) {
free(vfsave.vfs_automnt);
vfsave.vfs_automnt = NULL;
}
if ((vp->vfs_automnt != NULL) &&
((vfsave.vfs_automnt = strdup(vp->vfs_automnt)) == NULL)) {
perror(myname);
return (4); /* XXX */
}
if (vfsave.vfs_mntopts != NULL) {
free(vfsave.vfs_mntopts);
vfsave.vfs_mntopts = NULL;
}
if ((vp->vfs_mntopts != NULL) &&
((vfsave.vfs_mntopts = strdup(vp->vfs_mntopts)) == NULL)) {
perror(myname);
return (4); /* XXX */
}
*vp = vfsave;
return (0);
}
static int
mygetvfsent(FILE *fp, struct vfstab *vp)
{
int error;
if ((error = getvfsent(fp, vp)) != 0)
return (error);
return (vfdup(vp));
}
static int
mygetvfsany(FILE *fp, struct vfstab *vp, struct vfstab *vrefp)
{
int error;
if ((error = getvfsany(fp, vp, vrefp)) != 0)
return (error);
return (vfdup(vp));
}
main(argc, argv)
int argc;
char *argv[];
{
int cc, ret, other_than_ufs = 0;
int questflg = 0, Fflg = 0, Vflg = 0, sanity = 0;
char *subopt;
FILE *fd = NULL;
struct vfstab vget, vref;
int preencnt = 0;
struct devlist *dp, *devs = NULL;
int status;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
#endif
(void) textdomain(TEXT_DOMAIN);
myname = strrchr(argv[0], '/');
if (myname)
myname++;
else
myname = argv[0];
while ((cc = getopt(argc, argv, "?F:mnNo:VyY")) != -1) {
switch (cc) {
case '?':
questflg++;
if (questflg > 1)
usage();
nargv[nargc++] = "-?";
CHECK(nargc, ARGV_MAX);
break;
case 'F':
Fflg++;
/* check for more that one -F */
if (Fflg > 1) {
fprintf(stderr,
gettext("%s: more than one fstype specified\n"),
myname);
usage();
}
fstype = optarg;
if (strlen(fstype) > (size_t) FSTYPE_MAX) {
fprintf(stderr,
gettext("%s: Fstype %s exceeds %d characters\n"),
myname, fstype, FSTYPE_MAX);
exit(1);
}
break;
case 'm':
sanity++;
OPTION("-m");
case 'n':
OPTION("-n");
case 'N':
OPTION("-N");
case 'o':
subopt = optarg;
while (*subopt != '\0') {
if (*subopt == 'p') {
pflg++;
break;
}
subopt++;
}
OPTARG("-o");
case 'V':
Vflg++;
if (Vflg > 1)
usage();
break;
case 'y':
OPTION("-y");
case 'Y':
OPTION("-Y");
}
optarg = NULL;
}
/* copy '--' to specific */
if (strcmp(argv[optind-1], "--") == 0) {
nargv[nargc++] = argv[optind-1];
CHECK(nargc, ARGV_MAX);
}
if (questflg) {
if (Fflg) {
nargc = 2;
nargv[nargc++] = "-?";
nargv[nargc] = NULL;
do_exec(fstype, nargv);
}
usage();
}
if ((sanity) && (options > 1)) {
usage();
}
if (optind == argc) { /* no device name is specified */
if (fstype == NULL) {
if ((argc > 2) && (sanity)) {
usage();
}
}
/*
* Try to check UFS filesystems first, then check other
* filesystems if they exist.
* Note: Parallel checking is only available in UFS for now.
*/
if (fstype == NULL || strcmp(fstype, MNTTYPE_UFS) == 0) {
if ((fd = fopen(vfstab, "r")) == NULL) {
fprintf(stderr,
gettext("%s: cannot open vfstab\n"),
myname);
exit(1);
}
while ((ret = mygetvfsent(fd, &vget)) == 0) {
if (strcmp(vget.vfs_fstype, MNTTYPE_UFS) &&
numbers(vget.vfs_fsckpass)) {
other_than_ufs ++;
continue;
}
if (numbers(vget.vfs_fsckpass))
mnt_passno = atoi(vget.vfs_fsckpass);
else
continue;
if (mnt_passno < 1)
continue;
if (pflg == 0 || mnt_passno == 1) {
status = execute(vget.vfs_fsckdev,
MNTTYPE_UFS, Vflg, fd);
/* return the highest exit code */
if (status > exitstat)
exitstat = status;
} else if (preen_addev(vget.vfs_fsckdev) == 0) {
preencnt++;
dp = newdev(&vget);
dp->nxt = devs;
devs = dp;
} else {
/*
* preening setup failed, so
* execute serially here...
*/
fprintf(stderr,
gettext("%s: preen_addev error\n"),
myname);
status = execute(vget.vfs_fsckdev,
MNTTYPE_UFS, Vflg, fd);
/* return the highest exit code */
if (status > exitstat)
exitstat = status;
}
}
fclose(fd);
if (ret > 0)
vfserror(ret);
if (pflg && exitstat == 0) {
fsck_dopreen(&devs, preencnt);
}
}
else
other_than_ufs = 1;
if (other_than_ufs) {
if ((fd = fopen(vfstab, "r")) == NULL) {
fprintf(stderr,
gettext("%s: cannot open vfstab\n"),
myname);
exit(1);
}
while ((ret = mygetvfsent(fd, &vget)) == 0)
if (strcmp(vget.vfs_fstype, MNTTYPE_UFS) &&
numbers(vget.vfs_fsckpass) &&
vget.vfs_fsckdev != NULL &&
(fstype == NULL ||
strcmp(fstype, vget.vfs_fstype) == 0)) {
status = execute(vget.vfs_fsckdev,
vget.vfs_fstype, Vflg, fd);
/* return the highest exit code */
if (status > exitstat)
exitstat = status;
}
fclose(fd);
if (ret > 0)
vfserror(ret);
}
} else { /* device name is specified */
if (fstype == NULL && (fd = fopen(vfstab, "r")) == NULL) {
fprintf(stderr, gettext("%s: cannot open vfstab\n"),
myname);
exit(1);
}
while (optind < argc) {
/*
* If "-F FStype" is specified, use that fs type.
* Otherwise, determine the fs type from /etc/vfstab
* if the entry exists. Otherwise, determine the
* local or remote fs type from /etc/default/df
* or /etc/dfs/fstypes respectively.
*/
if (fstype == NULL) {
if ((argc > 3) && (sanity)) {
usage();
}
/* must check for both special && raw devices */
vfsnull(&vref);
/*
* Find the vfstab entry for this device.
* arg_hint tells us what to try to match,
* based on the type of the last arg. If
* arg_hint equals UNKNOWN, then we're not
* sure of the type and need to fallthrough
* all 3 possibilities for vfstab lookup.
* Try it as a mountpt first, since that's
* what mountall gives us.
*/
try_again:
switch (arg_hint) {
case UNKNOWN:
/* FALLTHROUGH */
case MOUNTPT:
vref.vfs_mountp = argv[optind];
if ((ret = mygetvfsany(fd, &vget,
&vref)) == -1 ||
vget.vfs_fstype == NULL) {
vref.vfs_mountp = NULL;
rewind(fd);
if (arg_hint == MOUNTPT) {
arg_hint = UNKNOWN;
goto try_again;
}
/* FALLTHROUGH */
} else {
/* Found it */
if (vget.vfs_fsckdev != NULL) {
argv[optind] =
vget.vfs_fsckdev;
}
arg_hint = MOUNTPT;
break;
}
case FSCKDEV:
vref.vfs_fsckdev = argv[optind];
if ((ret = mygetvfsany(fd, &vget,
&vref)) == -1 ||
vget.vfs_fstype == NULL) {
vref.vfs_fsckdev = NULL;
rewind(fd);
if (arg_hint == FSCKDEV) {
arg_hint = UNKNOWN;
goto try_again;
}
/* FALLTHROUGH */
} else {
/* Found it */
arg_hint = FSCKDEV;
break;
}
case SPECIAL:
vref.vfs_special = argv[optind];
if ((ret = mygetvfsany(fd, &vget,
&vref)) == -1 ||
vget.vfs_fstype == NULL) {
vref.vfs_special = NULL;
rewind(fd);
if (arg_hint == SPECIAL) {
arg_hint = UNKNOWN;
goto try_again;
}
/* FALLTHROUGH */
} else {
/* Found it */
arg_hint = SPECIAL;
break;
}
}
if (ret == 0 && vget.vfs_fstype) {
if ((pflg) && (strcmp(vget.vfs_fstype,
MNTTYPE_UFS) == 0) && (preen_addev(
vget.vfs_fsckdev) == 0)) {
preencnt++;
dp = newdev(&vget);
dp->nxt = devs;
devs = dp;
} else {
status = execute(argv[optind],
vget.vfs_fstype, Vflg, fd);
if (status > exitstat)
exitstat = status;
}
} else if (ret == -1 ||
vget.vfs_fstype == NULL) {
fstype =
default_fstype(argv[optind]);
status = execute(argv[optind], fstype,
Vflg, fd);
/* return the highest exit code */
if (status > exitstat)
exitstat = status;
} else
vfserror(ret);
} else {
status = execute(argv[optind], fstype,
Vflg, NULL);
/* return the highest exit code */
if (status > exitstat)
exitstat = status;
}
optind++;
}
if (fd != NULL)
fclose(fd);
if ((pflg) && (exitstat == 0)) {
fsck_dopreen(&devs, preencnt);
}
}
exit(exitstat);
}
static
fsck_dopreen(devp, ndevs)
struct devlist **devp;
int ndevs;
{
char name[1024];
int rc;
register int i;
struct devlist *bl, *bdp;
struct devlist *badlist;
bl = badlist = NULL;
while (ndevs > 0) {
if (nrun > maxrun)
waiter(&bl, &badlist);
rc = preen_getdev(name);
switch (rc) {
case 0:
break;
case 1:
bdp = getdev(name, devp);
if (bdp == NULL) {
fprintf(stderr,
gettext("%s: unknown dev: `%s'\n"),
myname, name);
exit(1);
}
bdp->nxt = bl;
bl = bdp;
startdisk(bdp);
ndevs--;
break;
case 2:
waiter(&bl, &badlist);
break;
default:
fprintf(stderr,
gettext("%s: bad return `%d' from preen_getdev\n"),
myname, rc);
break;
}
}
while (bl != NULL) {
waiter(&bl, &badlist);
}
if (badlist != NULL)
print_badlist(badlist);
}
static
startdisk(dp)
struct devlist *dp;
{
pid_t pid;
nrun++;
if ((pid = fork()) == -1) {
perror("fork");
exit(1);
} else if (pid == 0) {
exitstat = execute(dp->name, MNTTYPE_UFS, Vflg, NULL);
exit(exitstat);
} else {
dp->pid = pid;
}
}
static
waiter(blp, badlist)
struct devlist **blp;
struct devlist **badlist;
{
pid_t curpid;
int status;
register struct devlist *bdp, *pbdp;
curpid = wait(&status);
if (curpid == -1) {
perror("wait");
exit(1);
}
for (pbdp = NULL, bdp = *blp; bdp != NULL; pbdp = bdp, bdp = bdp->nxt) {
if (bdp->pid == curpid) {
break;
}
}
if (bdp == NULL)
return;
nrun--;
if (pbdp)
pbdp->nxt = bdp->nxt;
else
*blp = bdp->nxt;
preen_releasedev(bdp->name);
if (WTERMSIG(status)) {
printf(gettext("%s (%s): EXITED WITH SIGNAL %d\n"),
bdp->name, bdp->fsname, WTERMSIG(status));
status = status&0377 | 8<<8;
}
if (WHIBYTE(status) != 0) {
if (WHIBYTE(status) > exitstat)
exitstat = WHIBYTE(status);
while (*badlist != NULL)
badlist = &(*badlist)->nxt;
*badlist = bdp;
bdp->nxt = NULL;
}
}
static
print_badlist(lp)
struct devlist *lp;
{
int x, len;
printf(
gettext("\nTHE FOLLOWING FILE SYSTEM(S) HAD AN UNEXPECTED INCONSISTENCY:"));
for (x = 3; lp != NULL; lp = lp->nxt) {
len = strlen(lp->name) + strlen(lp->fsname) + 5;
x += len;
if (x >= 80) {
printf("\n ");
x = len + 3;
} else {
printf(" ");
}
printf("%s (%s)%s", lp->name, lp->fsname,
lp->nxt ? "," : "\n");
}
}
/*
* allocate and initialize a `devlist' structure
*/
static
struct devlist *
newdev(vfsp)
struct vfstab *vfsp;
{
struct devlist *dp;
extern char *strdup();
dp = (struct devlist *)malloc(sizeof (struct devlist));
if (dp == NULL) {
fprintf(stderr, gettext("%s: out of memory\n"), myname);
exit(1);
}
dp->name = strdup(vfsp->vfs_fsckdev);
dp->fsname = strdup(vfsp->vfs_mountp);
if (dp->name == NULL || dp->fsname == NULL) {
fprintf(stderr, gettext("%s: out of memory\n"), myname);
exit(1);
}
return (dp);
}
/*
* locate the devlist structure in the given list that matches `name'.
* If found, the structure is removed from the list, and a pointer to
* it is returned. If not, NULL is returned.
*/
static
struct devlist *
getdev(name, list)
char *name;
struct devlist **list;
{
register struct devlist *p, *lp;
for (lp = NULL, p = *list; p != NULL; lp = p, p = p->nxt) {
if (strcmp(p->name, name) == 0)
break;
}
if (p != NULL) {
if (lp != NULL)
lp->nxt = p->nxt;
else
*list = p->nxt;
}
return (p);
}
/* see if all numbers */
numbers(yp)
char *yp;
{
if (yp == NULL)
return (0);
while ('0' <= *yp && *yp <= '9')
yp++;
if (*yp)
return (0);
return (1);
}
execute(fsckdev, fstype, Vflg, fd)
char *fsckdev, *fstype;
int Vflg;
FILE *fd;
{
int st;
pid_t fk;
char full_path[PATH_MAX];
char *vfs_path = VFS_PATH;
int status = 0;
nargv[nargc] = fsckdev;
if (Vflg) {
prnt_cmd(stdout, fstype);
return (0);
}
if (fd)
fcntl(fileno(fd), F_SETFD, 1); /* close on exec */
if ((fk = fork()) == (pid_t)-1) {
fprintf(stderr,
gettext("%s: cannot fork. Try again later\n"),
myname);
perror(myname);
exit(1);
}
if (fk == 0) {
/* Try to exec the fstype dependent portion of the fsck. */
do_exec(fstype, nargv);
} else {
/* parent waits for child */
if (wait(&st) == (pid_t)-1) {
fprintf(stderr, gettext("%s: bad wait\n"), myname);
perror(myname);
exit(1);
}
if ((st & 0xff) == 0x7f) {
fprintf(stderr,
gettext("%s: warning: the following command"
" (process %d) was stopped by signal %d\n"),
myname, fk, (st >> 8) & 0xff);
prnt_cmd(stderr, fstype);
status = ((st >> 8) & 0xff) | 0x80;
} else if (st & 0xff) {
if (st & 0x80)
fprintf(stderr,
gettext("%s: warning: the following command"
" (process %d) was terminated by signal %d"
" and dumped core\n"),
myname, fk, st & 0x7f);
else
fprintf(stderr,
gettext("%s: warning: the following command"
" (process %d) was terminated by signal %d\n"),
myname, fk, st & 0x7f);
prnt_cmd(stderr, fstype);
status = ((st & 0xff) | 0x80);
} else if (st & 0xff00)
status = (st >> 8) & 0xff;
}
return (status);
}
do_exec(fstype, nargv)
char *fstype, *nargv[];
{
char full_path[PATH_MAX];
char *vfs_path = VFS_PATH;
if (strlen(fstype) > (size_t) FSTYPE_MAX) {
fprintf(stderr,
gettext("%s: Fstype %s exceeds %d characters\n"),
myname, fstype, FSTYPE_MAX);
exit(1);
}
/* build the full pathname of the fstype dependent command. */
sprintf(full_path, "%s/%s/%s", vfs_path, fstype, myname);
/* set the new argv[0] to the filename */
nargv[1] = myname;
/* Try to exec the fstype dependent portion of the fsck. */
execv(full_path, &nargv[1]);
if (errno == EACCES) {
fprintf(stderr,
gettext("%s: cannot execute %s - permission denied\n"),
myname, full_path);
}
if (errno == ENOEXEC) {
nargv[0] = "sh";
nargv[1] = full_path;
execv("/sbin/sh", &nargv[0]);
}
/* second path to try */
vfs_path = VFS_PATH2;
/* build the full pathname of the fstype dependent command. */
sprintf(full_path, "%s/%s/%s", vfs_path, fstype, myname);
/* set the new argv[0] to the filename */
nargv[1] = myname;
/* Try to exec the second fstype dependent portion of the fsck. */
execv(full_path, &nargv[1]);
if (errno == EACCES) {
fprintf(stderr,
gettext("%s: cannot execute %s - permission denied\n"),
myname, full_path);
exit(1);
}
if (errno == ENOEXEC) {
nargv[0] = "sh";
nargv[1] = full_path;
execv("/sbin/sh", &nargv[0]);
}
fprintf(stderr,
gettext("%s: operation not applicable to FSType %s\n"),
myname, fstype);
exit(1);
}
prnt_cmd(fd, fstype)
FILE *fd;
char *fstype;
{
char **argp;
fprintf(fd, "%s -F %s", myname, fstype);
for (argp = &nargv[2]; *argp; argp++)
fprintf(fd, " %s", *argp);
fprintf(fd, "\n");
}
vfserror(flag)
int flag;
{
switch (flag) {
case VFS_TOOLONG:
fprintf(stderr,
gettext("%s: line in vfstab exceeds %d characters\n"),
myname, VFS_LINE_MAX-2);
break;
case VFS_TOOFEW:
fprintf(stderr,
gettext("%s: line in vfstab has too few entries\n"),
myname);
break;
case VFS_TOOMANY:
fprintf(stderr,
gettext("%s: line in vfstab has too many entries\n"),
myname);
break;
}
exit(1);
}
int opterr = 1, optind = 1, optopt = 0;
char *optarg = 0;
int
getopt(int argc, char * const *argv, const char *opts)
{
static int sp = 1;
register int c;
register char *cp;
if (sp == 1)
if (optind >= argc ||
argv[optind][0] != '-' || argv[optind][1] == '\0')
return (-1);
else if (strcmp(argv[optind], "--") == 0) {
optind++;
return (-1);
}
optopt = c = argv[optind][sp];
if (c == ':' || (cp = strchr(opts, c)) == 0) {
if (opterr)
fprintf(stderr,
gettext("%s: illegal option -- %c\n"),
*argv, c);
if (argv[optind][++sp] == '\0') {
optind++;
sp = 1;
}
return ('?');
}
if (*++cp == ':') {
if (argv[optind][sp+1] != '\0')
optarg = &argv[optind++][sp+1];
else if (++optind >= argc) {
if (opterr)
fprintf(stderr,
gettext("%s: option requires an argument -- %c\n"), *argv, c);
sp = 1;
return ('?');
} else
optarg = argv[optind++];
sp = 1;
} else if (*cp == ';') {
if (argv[optind][++sp] != '\0')
if (isoptarg(c, &argv[optind][sp])) {
optarg = &argv[optind++][sp];
sp = 1;
} else
optarg = NULL;
else {
sp = 1;
if (++optind >= argc || !isoptarg(c, &argv[optind][0]))
optarg = NULL;
else
optarg = argv[optind++];
}
} else {
if (argv[optind][++sp] == '\0') {
sp = 1;
optind++;
}
optarg = NULL;
}
return (c);
}
isoptarg(cc, arg)
int cc;
char *arg;
{
if (cc == 's' || cc == 'S') {
while (*arg >= '0' && *arg <= '9')
arg++;
if (*arg++ != ':')
return (0);
while (*arg >= '0' && *arg <= '9')
arg++;
if (*arg)
return (0);
return (1);
}
return (0);
}
usage()
{
fprintf(stderr,
gettext("Usage:\n%s [-F FSType] [-V] [-m] [special ...]\n"
"%s [-F FSType] [-V] [-y|Y|n|N]"
" [-o specific_options] [special ...]\n"),
myname, myname);
exit(1);
}