#if HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#endif
#include <sys/cdefs.h>
#ifdef __RCSID
__RCSID("$NetBSD: main.c,v 1.20 2026/03/28 20:43:34 palle Exp $");
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <ctype.h>
#include <sys/stat.h>
#ifndef HAVE_NBTOOL_CONFIG_H
#include <util.h>
#endif
#include "map.h"
#include "gpt.h"
#include "gpt_private.h"
static const struct gpt_cmd c_null = { 0 };
static const struct gpt_cmd *cmdsw[] = {
&c_add,
#ifndef HAVE_NBTOOL_CONFIG_H
&c_backup,
#endif
&c_biosboot,
&c_create,
&c_destroy,
&c_header,
&c_label,
&c_migrate,
&c_recover,
&c_remove,
&c_resize,
&c_resizedisk,
#ifndef HAVE_NBTOOL_CONFIG_H
&c_restore,
#endif
&c_set,
&c_show,
&c_type,
&c_unset,
&c_uuid,
&c_null,
};
__dead static void
usage(void)
{
const char *p = getprogname();
const char *f =
"[-nrqv] [-m mediasize] [-s sectorsize] [-T timestamp]";
size_t i;
if (strcmp(p, "gpt") == 0)
fprintf(stderr,
"Usage: %s %s command device\n", p, f);
else
fprintf(stderr,
"Usage: %s %s device command\n", p, f);
fprintf(stderr, "Commands:\n");
for (i = 0; i < __arraycount(cmdsw); i++)
gpt_usage("\t", cmdsw[i]);
exit(EXIT_FAILURE);
}
static void
prefix(const char *cmd)
{
char *pfx;
if (asprintf(&pfx, "%s %s", getprogname(), cmd) < 0)
pfx = NULL;
else
setprogname(pfx);
}
static time_t
get_tstamp(const char *b)
{
struct stat st;
char *eb;
long long l;
if (stat(b, &st) != -1)
return (time_t)st.st_mtime;
errno = 0;
l = strtoll(b, &eb, 0);
if (b == eb || *eb || errno) {
#ifndef HAVE_NBTOOL_CONFIG_H
time_t when;
errno = 0;
if ((when = parsedate(b, NULL, NULL)) != -1 || errno == 0)
return when;
#endif
errx(EXIT_FAILURE, "Can't parse timestamp `%s'", b);
}
return (time_t)l;
}
int
main(int argc, char *argv[])
{
char *cmd, *p, *dev = NULL;
int ch, i;
u_int secsz = 0;
uintmax_t mediasz = 0;
int flags = 0;
int verbose = 0;
int sectormult = 0;
time_t timestamp = 0;
gpt_t gpt;
setprogname(argv[0]);
if (strcmp(getprogname(), "gpt") == 0) {
if (argc < 3)
usage();
dev = argv[--argc];
}
#ifdef __GLIBC__
#define GETOPT_BE_POSIX "+"
#else
#define GETOPT_BE_POSIX ""
#endif
while ((ch = getopt(argc, argv, GETOPT_BE_POSIX "Hm:nqrs:T:v")) != -1) {
switch(ch) {
case 'H':
flags |= GPT_HYBRID;
break;
case 'm':
if (mediasz > 0)
usage();
errno = 0;
mediasz = strtoull(optarg, &p, 10);
if (errno != 0 || mediasz < 1)
usage();
while (isspace(*(unsigned char *)p))
p++;
ch = *(unsigned char *)p;
if (islower(ch))
ch = toupper(ch);
switch (ch) {
case 'S':
if (mediasz > UINTMAX_MAX /
(secsz ? secsz : 512))
errx(EXIT_FAILURE,
"Media size too large");
if (secsz != 0) {
mediasz *= secsz;
} else {
mediasz *= 512;
sectormult = 1;
}
continue;
case 'K':
if (mediasz >= UINTMAX_MAX >> 10)
usage();
mediasz <<= 10;
p++;
break;
case 'M':
if (mediasz >= UINTMAX_MAX >> 20)
usage();
mediasz <<= 20;
p++;
break;
case 'G':
if (mediasz >= UINTMAX_MAX >> 30)
usage();
mediasz <<= 30;
p++;
break;
case 'T':
if (mediasz >= UINTMAX_MAX >> 40)
usage();
mediasz <<= 40;
p++;
break;
case 'P':
if (mediasz >= UINTMAX_MAX >> 50)
usage();
mediasz <<= 50;
p++;
break;
}
if (*p == 'i' && (p[1] == 'B' || p[1] == 'b'))
p++;
if (*p == 'B' || *p == 'b')
p++;
if (*p != '\0')
usage();
break;
case 'n':
flags |= GPT_NOSYNC;
break;
case 'r':
flags |= GPT_READONLY;
break;
case 'q':
flags |= GPT_QUIET;
break;
case 's':
if (gpt_scaled_uint_get(NULL, &secsz) == -1)
usage();
if (sectormult) {
if (mediasz / 512 > UINTMAX_MAX / secsz)
usage();
mediasz = mediasz / 512 * secsz;
sectormult = 0;
}
break;
case 'T':
flags |= GPT_TIMESTAMP;
timestamp = get_tstamp(optarg);
break;
case 'v':
verbose++;
break;
default:
usage();
}
}
if (argc == optind)
usage();
if (dev == NULL)
dev = argv[optind++];
if (argc == optind)
usage();
cmd = argv[optind++];
for (i = 0; cmdsw[i]->name != NULL && strcmp(cmd, cmdsw[i]->name); i++)
continue;
if (cmdsw[i]->fptr == NULL)
errx(EXIT_FAILURE, "Unknown command: %s", cmd);
prefix(cmd);
if (*dev != '-') {
gpt = gpt_open(dev, flags | cmdsw[i]->flags,
verbose, (off_t)mediasz, secsz, timestamp);
if (gpt == NULL)
return EXIT_FAILURE;
if (sectormult && mediasz == (uintmax_t)gpt->mediasz) {
if (mediasz / 512 > UINTMAX_MAX / gpt->secsz)
errx(EXIT_FAILURE, "media size too large");
gpt->mediasz /= 512;
gpt->mediasz *= gpt->secsz;
}
} else {
if ((cmdsw[i]->flags & GPT_OPTDEV) == 0)
errx(EXIT_FAILURE,
"Command %s needs a device parameter", cmd);
argc++;
gpt = NULL;
}
if ((*cmdsw[i]->fptr)(gpt, argc, argv) == -1)
return EXIT_FAILURE;
if (gpt)
gpt_close(gpt);
return EXIT_SUCCESS;
}