Logo Search packages:      
Sourcecode: acorn-fdisk version File versions  Download package

fdisk.c

/*
 * Changelog:
 *  25/03/1998    RMK   Added option for selecting partitioning scheme
 */

#include <assert.h>
#include <errno.h>
#include <ctype.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/mount.h>

#include "part/part.h"
#include "util/error.h"

#define NR_DEVICES            12
#define DEFAULT_DEVICE        "/dev/hda"
#define ALTERNATE_DEVICE      "/dev/sda"

static const char *part_devs[NR_DEVICES] = {
      "/dev/hda", "/dev/hdb", "/dev/hdc", "/dev/hdd",
      "/dev/sda", "/dev/sdb", "/dev/sdc", "/dev/sdd",
      "/dev/sde", "/dev/sdf", "/dev/sdg", "/dev/sdh"
};

#if 1
#include <getopt.h>

static const char *device;
static const char *progname;
static const char *dev_part_type = "";
static part_t *part;

typedef enum {
      mode_partition,
      mode_version,
      mode_help,
      mode_list,
      mode_size,
      mode_illegal
} prog_mode_t;

static struct option options[] = {
      { "help",   no_argument,            NULL, 'h' },
      { "list",   no_argument,            NULL, 'l' },
      { "size",   required_argument,      NULL, 's' },
      { "type",   required_argument,      NULL, 't' },
      { "version",      no_argument,            NULL, 'v' },
      { NULL,           no_argument,            NULL, 0   }
};

#define is_extended(x) ((x) == ptyp_dos_extended || (x) == ptyp_linux_extended)

static prog_mode_t
parse_args(int argc, char *argv[])
{
      prog_mode_t mode = mode_partition;
      int opt;

      do {
            opt = getopt_long(argc, argv, "hls:t:v", options, NULL);

            switch(opt) {
            case 'h':
                  mode = mode_help;
                  opt = EOF;
                  break;

            case 'v':
                  mode = mode_version;
                  opt = EOF;
                  break;

            case 'l':
                  mode = mode_list;
                  break;

            case 's':
                  mode = mode_size;
                  device = optarg;
                  break;

            case 't':
                  dev_part_type = optarg;
                  break;

            case EOF:
                  break;

            default:
                  mode = mode_illegal;
                  opt = EOF;
                  break;
            }
      } while (opt != EOF);

      return mode;
}

static char buf[16];

static const char *
read_line(const char *msg, ...)
{
      va_list ap;
      char *p;

      buf[0] = '\0';

      do {
            va_start(ap, msg);
            vprintf(msg, ap);
            va_end(ap);
            fgets(buf, sizeof(buf) - 1, stdin);
      } while (!feof(stdin) && buf[0] == '\0');

      p = strchr(buf, '\n');
      if (p)
            *p = '\0';

      return buf;
}

static char
read_char(const char *msg)
{
      return read_line(msg)[0];
}

static void
list_types(u_int parn, ptyp_t next)
{
      ptyp_t types[256], cur;
      u_int idx = 0, last[4], done = 0, nxt;
      int i;

      do {
            cur = next;
            next = part_nexttype(part, parn, cur, -1);
      } while (cur != next);

      do {
            cur = next;
            types[idx++] = cur;
            next = part_nexttype(part, parn, cur, +1);
      } while (cur != next);

      for (i = 3; i >= 0; i--)
            last[3 - i] = done += (idx + i - done) / (i + 1);

      i = nxt = done = 0;

      do {
            printf("%c%3x %-15.15s", i ? ' ' : '\n',
                   types[nxt], part_typename(part, types[nxt]));
            nxt = last[i++] + done;
            if (i > 3 || nxt >= last[i]) {
                  i = 0;
                  nxt = ++done;
            }
      } while (done < last[0]);
      putchar('\n');
            
}

static ptyp_t
read_type(u_int parn, ptyp_t old_type, const char *prompt)
{
      int hex;

      while (1) {
            const char *line = read_line(prompt);
            if (tolower(line[0]) == 'l')
                  list_types(parn, old_type);
            else if (isxdigit(line[0])) {
                  hex = (int)strtoul(line, NULL, 16);
                  break;
            }
      }
      return (ptyp_t)hex;
}


static u_int
get_part_nr(void)
{
      buf[0] = '\0';

      do {
            fputs("Which partition? ", stdout);
            fgets(buf, sizeof(buf) - 1, stdin);
      } while (!feof(stdin) && buf[0] == '\0');

      return atoi(buf);
}

static u_int display_factor = 1;    /* in units/sector */
static u_int full_bits = 0;         /* 1024 cylinders in sectors */
static bool_t unit_flag = 1;

static void
update_units(part_t *part)
{
      geometry_t geo;

      part_getgeometry(part, &geo);

      full_bits = 1024 * geo.heads * geo.sectors;
      if (unit_flag && full_bits)
            display_factor = full_bits >> 10;
      else
            display_factor = 1;
}

static void
change_units(part_t *part)
{
      unit_flag = !unit_flag;
      if (part)
            update_units(part);
      printf("Changing display/entry units to %ss\n",
            unit_flag ? "cylinder" : "sector");
}

static u_int
sectors_to_units(u_int sectors)
{
      return (sectors + (unit_flag ? display_factor - 1 : 0)) / display_factor;
}

static u_int
units_to_sectors(u_int units)
{
      return units * display_factor;
}

static inline u_int
calculate(part_t *part, u_int head, u_int sect, u_int cyl)
{
      geometry_t geo;

      part_getgeometry(part, &geo);

      return sect - 1 + geo.sectors * (head + geo.heads * cyl);
}

static void
delete_partition(void)
{
      partinfo_t info;
      u_int part_no, found = 0, partition = get_part_nr();

      for (part_no = 0; part_getpartinfo(part, part_no, &info); part_no += 1)
            if (info.kern_part_no == partition) {
                  found = 1;
                  break;
            }

      if (!found) {
            printf("Bad partition number `%d'\n", partition);
            return;
      }

      if (!(part_validops(part, part_no, &info) & VALIDOPS_DELETE)) {
            printf("Partition %d cannot be deleted.\n",
                   info.kern_part_no);
            return;
      }

      if (!part_delete(part, part_no))
            printf("Unable to delete partition `%d': %s\n",
                  partition, get_error());
      else
            printf("Partition `%d' deleted\n", partition);
}

struct regions {
      struct {
            u_int64_t start;
            u_int64_t end;
      } region[16];
      u_int nr;
};

static void
add_region(struct regions *region, partinfo_t *info)
{
      u_int i;

      for (i = 0; i < region->nr; i++) {
            if (region->region[i].start > info->blk_start) {
                  memmove(&region->region[i + 1],
                        &region->region[i],
                        (region->nr - i) * sizeof(region->region[0]));
                  region->region[i].start = info->blk_start;
                  region->region[i].end = info->blk_end + 1;
                  region->nr += 1;
                  goto added;
            }
      }

      region->region[region->nr].start = info->blk_start;
      region->region[region->nr].end = info->blk_end + 1;
      region->nr += 1;

added:
      for (i = 1; i < region->nr;) {
            if (region->region[i].start == region->region[i - 1].end) {
                  region->region[i - 1].end = region->region[i].end;
                  memmove(&region->region[i],
                        &region->region[i+1],
                        (region->nr - i) * sizeof(region->region[0]));
                  region->nr -= 1;
            } else
                  i += 1;
      }
}

static void
get_regions(struct regions *region)
{
      partinfo_t info;
      u_int parn = 0;

      region->nr = 0;

      while (part_getpartinfo(part, parn++, &info)) {
            if (!info.type)
                  continue;

            add_region(region, &info);
      }
}

static void
new_partition(void)
{
      partinfo_t info;
      geometry_t geo;
      struct regions regions;
      u_int parn, total, first = 1, i;
      char *units;

      update_units(part);

      part_getgeometry(part, &geo);
      total = geo.sectors * geo.heads * geo.cylinders;

      if (unit_flag)
            units = "cylinder";
      else
            units = "sector";

      printf("\nUnits are %ss of %d * %d bytes\nDisk space used:",
             units, display_factor, 512);

      get_regions(&regions);

      for (i = 0; i < regions.nr; i++)
            printf("%s %d - %d",
                  i ? "," : "",
                  sectors_to_units(regions.region[i].start),
                  sectors_to_units(regions.region[i].end));

      printf("\nTotal disk: %d\n\n", sectors_to_units(total));

      parn = part_allocate(part, &info);

      if (parn == PARN_ERROR) {
            printf("Unable to allocate a new partition slot: %s\n", get_error());
            return;
      }

      printf("Allocating partition %s%d\n\n", device, info.kern_part_no);

      do {
            const char *l;

            l = read_line(first ? "Start %s : " : "Start %s (%u) : ", units,
                        sectors_to_units(info.blk_start));

            if (isdigit(l[0]))
                  info.blk_start = units_to_sectors(strtoul(l, NULL, 10));
            else if (first || l[0] == 'q') {
                  printf("\nPartition not created\n");
                  return;
            }

            l = read_line(first ? "End %s   : " : "End %s (%u)   : ", units,
                        sectors_to_units(info.blk_end + 1));

            if (l[0] == '+')
                  info.blk_end = info.blk_start +
                        units_to_sectors(strtoul(l + 1, NULL, 10)) - 1;
            else if (isdigit(l[0]))
                  info.blk_end = units_to_sectors(strtoul(l, NULL, 10)) - 1;
            else if (first || l[0] == 'q') {
                  printf("\nPartition not created\n");
                  return;
            }

            info.chs_valid = 0;
            first = 0;

            if (info.blk_end > info.blk_start && info.blk_end < total) {
                  if (part_create(part, parn, &info)) {
                        printf("\nPartition %d created\n", parn);
                        return;
                  } else
                        printf("\nUnable to create partition: %s\n\n",
                               get_error());
            } else
                  printf("\nInvalid partition start/end\n\n");
      } while (1);
}

static void
change_type(void)
{
      partinfo_t info;
      u_int part_no, found = 0, partition = get_part_nr();
      ptyp_t old_type, new_type;

      for (part_no = 0; part_getpartinfo(part, part_no, &info); part_no += 1)
            if (info.kern_part_no == partition) {
                  found = 1;
                  break;
            }

      if (!found) {
            printf("Bad partition number `%d'\n", partition);
            return;
      }

      if (!(part_validops(part, part_no, &info) & VALIDOPS_UPDATE)) {
            printf("Partition %d cannot be altered.\n", info.kern_part_no);
            return;
      }

      old_type = info.type;
      if (is_extended(info.type)) {
            printf("Partition %d is extended.  Delete it\n", info.kern_part_no);
            return;
      }
      new_type = read_type(part_no, old_type, "Hex code (type L to list codes):");
      if (is_extended(new_type)) {
            printf("You may not change a partition "
                   "to be an extended partition\n");
            return;
      }
      info.type = new_type;

      if (!part_setpartinfo(part, part_no, &info))
            printf("Unable to change partition %d type: %s\n",
                   partition, get_error());
      else
            printf("Partition `%d' type changed from %X (%s) to %X (%s)\n",
                   partition,
                   old_type, part_typename(part, old_type),
                   new_type, part_typename(part, new_type));
}

typedef enum {
      LIST_NORMAL,
      LIST_EXTENDED_NORMAL,
      LIST_EXTENDED_EXTENDED
} list_t;

static inline int
is_displayable_partition(ptyp_t type)
{
      return type != ptyp_none && type != ptyp_pc_table;
}
                              
static void
list_table(list_t list_type)
{
      partinfo_t info;
      geometry_t geo;
      int i, w = 0;

      update_units(part);
      part_getgeometry(part, &geo);

      printf("\nDisk %s: %d heads, %d sectors, %d cylinders\n",
             device, geo.heads, geo.sectors, geo.cylinders);

      if (list_type == LIST_NORMAL) {
            printf("Units = %ss of ",
                   unit_flag ? "cylinder" : "sector");
            if (display_factor > 1)
                  printf("%d * ", display_factor);
            printf("512 bytes\n\n");

            w = strlen(device) + 1;
            if (w < 6)
                  w = 6;

            printf("%*s Boot   Begin    Start      End   Blocks     Id   System\n",
                   w, "Device");
      } else
            printf("Nr AF  Hd Sec  Cyl  Hd Sec  Cyl   Start    Size ID\n");

      i = 0;
      while (part_getpartinfo(part, i++, &info)) {
            if (list_type == LIST_NORMAL && !is_displayable_partition(info.type))
                  continue;

            if (list_type == LIST_NORMAL) {
                  printf("%*s%-2d  %c%9d%9d%9d%11llu%c  %4x  %s\n",
                         w - 1, device, info.kern_part_no, ' ',
                         sectors_to_units(calculate(part, info.chs_start.head,
                                          info.chs_start.sector,
                                          info.chs_start.cylinder)),
                         sectors_to_units(info.blk_start),
                         sectors_to_units(info.blk_end + (info.blk_end & 1 ? 0 : 1)),
                         (unsigned long long)((info.blk_end - info.blk_start + 1) / 2),
                         (info.blk_end - info.blk_start + 1) & 1 ? '+' : ' ',
                         info.type,
                         part_typename(part, info.type));
//                check_consistency (part, p);
            } else {
                  printf("%2d %02x%4d%4d%5d%4d%4d%5d%12llu%12llu %03x\n",
                         info.kern_part_no, 0, info.chs_start.head,
                         info.chs_start.sector, info.chs_start.cylinder,
                         info.chs_end.head, info.chs_end.sector,
                         info.chs_end.cylinder,
                         (unsigned long long)info.blk_start,
                         (unsigned long long)(info.blk_end - info.blk_start + 1),
                         info.type);
//                if (info.type != ptyp_none)
//                      check_consistency (part, p);
            }
      }
}

typedef enum {
      pps_ret_ok,
      pps_ret_open_error,
      pps_ret_not_found
} pps_ret_t;

static pps_ret_t
print_part_size(const char *device)
{
      pps_ret_t ret = pps_ret_not_found;
      char dev_nam[16], *p;
      part_t *part;
      int partition;

      strncpy(dev_nam, device, 15);
      dev_nam[15] = '\0';

      for (p = dev_nam; *p && !isdigit(*p); p++);

      partition = atoi(p);
      *p = '\0';

      part = part_open(dev_nam, dev_part_type);
      if (part) {
            int i = 0;
            partinfo_t info;

            while(part_getpartinfo(part, i, &info)) {
                  if (info.kern_part_no == partition) {
                        printf("%llu\n",
                            (unsigned long long)((info.blk_end -
                            info.blk_start + 1) / 2));
                        ret = pps_ret_ok;
                        break;
                  }
                  i += 1;
            }
            part = part_close(part);
      } else
            ret = pps_ret_not_found;

      return ret;
}

/* Prototype: int print_part_tables(int nr, const char **parts)
 * Purpose  : Display a list of partition tables
 * Params   : nr    - number of partition tables to display
 *          : parts - pointers to partition table names
 * Returns  : 0 if success
 */
static int
print_part_tables(int nr, const char **parts)
{
      int i, ret = 1;

      for(i = 0; i < nr; i++) {
            device = parts[i];
            part = part_open(device, dev_part_type);
            if (part) {
                  list_table(LIST_NORMAL);
                  part = part_close(part);
                  ret = 0;
            }
      }

      return ret;
}

static void
write_tables(void)
{
      const char *l;
      int i, error = 0;

      printf("\nWriting the partition tables is dangerous and"
             "\ncan result in loss of data.  Please confirm"
             "\nthat you really want to do this by typing 'YES'"
             "\nexactly as shown\n\n");

      l = read_line("Confirm: ");

      if (strcmp(l, "YES")) {
            printf("\nCONFIRMATION NOT GIVEN - not updating partition table\n");
            return;
      }

      if (!part_sync(part)) {
            printf("\nERROR occurred while updating partition tables: %s\n", get_error());
            return;
      }

      printf("The partition table has been altered!\n\n");
#ifdef BLKRRPART
      printf("Syncing disks...");
      fflush(stdout);
      sync();
      sleep(2);
      sync();

      printf("OK\nRe-reading the partition table...");
      fflush(stdout);

      i = ioctl(part->blkio->fd, BLKRRPART);
      if (i != 0)
            error = errno;
      else {
            printf(" resyncing...");
            fflush(stdout);
            sync();
            sleep(2);
            i = ioctl(part->blkio->fd, BLKRRPART);
            if (i != 0)
                  error = errno;
      }

      if (i < 0)
            printf("FAILED!\n");
#endif

      printf("Syncing disks...");
      fflush(stdout);
      sync();
      sleep(4);
      printf("OK\n");

#ifndef BLKRRPART
      printf("Note:\tre-reading the partition table is not supported\n"
          "\ton this operating system.\n");
      i = ENOSYS;
#endif

      if (i < 0)
            printf("\n*** Re-read table failed with error %d: %s ***\n"
                   "  Reboot your system to ensure the partition table is\n"
                   "  updated.\n", error, strerror(error));
      exit(0);
}

static void
show_main_menu (void)
{
      printf("\nCommand action\n"
             "   d    delete a partition\n"
//           "   l    list known partition types\n"
             "   m    print this menu\n"
             "   n    add a new partition\n"
             "   p    print the partition table\n"
             "   q    quit without saving changes\n"
             "   r    reopen partition, specifying type\n"
             "   t    change a partition's system id\n"
             "   u    change display/entry units\n"
//           "   v    verify the partition table\n"
             "   w    write table to disk and exit\n"
//           "   x    extra functionality (experts only)\n"
            );
}

/* Prototype: void main_menu(void)
 * Purpose  : display main menu & process user selections
 */
static void
main_menu(void)
{
      int quit = 0;
      part_t *new_part;
      const char *s;
      static char *scheme;

      show_main_menu();

      do {
            putchar('\n');
            switch (tolower(read_char("Command (m for help): "))) {
            case 'd':
                  if (part)
                        delete_partition();
                  else
                        goto unavailable;
                  break;
            case 'e':
                  if (part)
                        list_table(LIST_EXTENDED_NORMAL);
                  else
                        goto unavailable;
                  break;
//          case 'l':
//                if (part)
//                      list_types();
//                else
//                      goto unavailable;
//                break;
            case 'm':
            default:
                  show_main_menu();
                  break;
            case 'n':
                  if (part)
                        new_partition();
                  else
                        goto unavailable;
                  break;
            case 'p':
                  if (part)
                        list_table(LIST_NORMAL);
                  else
                        goto unavailable;
                  break;
            case 'q':
                  quit = 1;
                  break;
            case 'r':
                  s = read_line("Partitioning scheme: ");
                  new_part = part_open(device, s);
                  if (new_part) {
                        if (scheme)
                              free(scheme);
                        part_close(part);
                        part = new_part;
                        dev_part_type = scheme = strdup(s);
                        fprintf(stderr, "%s successfully reopened using %s scheme\n",
                              device, dev_part_type);
                  } else if (part)
                        fprintf(stderr, "%s: %s: unable to reopen partition as %s,\n"
                              "using %s instead.\n", progname, device, s, dev_part_type);
                  else
                        fprintf(stderr, "%s; %s: unable to reopen partition as %s\n",
                              progname, device, s);
                  break;
            case 't':
                  if (part)
                        change_type();
                  else
                        goto unavailable;
                  break;
            case 'u':
                  change_units(part);
                  break;
            case 'w':
                  if (part) {
                        write_tables();
                        break;
                  }
            unavailable:
                  fprintf(stderr, "%s: %s: option ignored - please re-open the device\n",
                        progname, device);
                  break;
            }
      } while (!quit);
}

/* Prototype: u_int edit_device(const char *dev_nam)
 * Purpose  : Edit partition tables on a device
 * Params   : dev_nam - special device name to edit
 * Returns  : 0 if success
 */
static u_int
edit_device(const char *dev_nam)
{
      u_int use_default = dev_nam == NULL;

      if (use_default) {
            device = DEFAULT_DEVICE;
            part = part_open(device, dev_part_type);
            if (!part) {
                  device = ALTERNATE_DEVICE;
                  part = part_open(ALTERNATE_DEVICE, dev_part_type);
            }
      } else {
            device = dev_nam;
            part = part_open(device, dev_part_type);
      }

      if (use_default)
            printf("\nUsing %s as default device\n", device);

      if (!part)
            printf("Unable to open device %s: %s\n\n"
                  "  Please use 'r' to open the partition as a specific type\n\n",
                  device, get_error());
      else
            printf("\nDevice %s is partitioned using %s scheme\n",
                  device, part_getscheme(part));

      main_menu();

      return 0;
}

int main(int argc, char *argv[])
{
      prog_mode_t mode;
      u_int ret = 1;

      progname = argv[0];
      mode = parse_args(argc, argv);

      switch(mode) {
      case mode_version:
            printf("arm-fdisk v"VERSION"\n");
            break;

      case mode_help:
            printf("Usage: %s [OPTION] [DEVICE]...\n", progname);
            printf("Display or edit partition tables on block devices.\n\n");
            printf("      --type SCHEME     specify the partitioning scheme to use on the device\n");
            printf("  -l, --list DEVICE...  list partition information on devices\n");
            printf("                        or all devices\n");
            printf("  -s, --size DEVICE     display size of partition\n");
            printf("      --help            display this help and exit\n");
            printf("      --version         output version information and exit\n\n");
            printf("With no DEVICE, partition /dev/hda or /dev/sda\n");
            break;

      case mode_size:
            switch(print_part_size(device)) {
            case pps_ret_ok:
                  ret = 0;
                  break;

            case pps_ret_open_error:
                  fprintf(stderr, "Cannot open %s: %s\n", device, get_error());
                  break;

            case pps_ret_not_found:
                  ret = 1;
                  break;
            }
            break;

      case mode_list:
            if (optind < argc)
                  ret = print_part_tables(argc - optind, (const char **)argv + optind);
            else
                  ret = print_part_tables(NR_DEVICES, part_devs);
            break;

      case mode_partition:
            if (optind < argc - 1) {
                  fprintf(stderr, "Partitioning is only allowed on a single device\n");
                  goto bad;
            }
            if (optind == argc)
                  ret = edit_device(NULL);
            else
                  ret = edit_device(argv[optind]);
            break;

      bad:
      case mode_illegal:
            fprintf(stderr, "Try `%s --help` for more information.\n", progname);
            ret = 1;
            break;
      }

      return ret;
}







#else
#include "partitions.h"

#define LINE_LENGTH           80
#define NR_DEF_DEVS           2

static const char *default_devs[NR_DEF_DEVS] = {
      DEFAULT_DEVICE,   ALTERNATE_DEVICE
};

static const char *usage = "Usage: fdisk [-l [device...]] [-v] [-s /dev/hdxn] [/dev/hdx]\n";
static char *prog_name;
static bool errorjump_set;
static jmp_buf errorjump;
unsigned int sector_offset = 1;
bool dos_compatible_flag = TRUE;

/*
 * Sundry functions...
 */
#ifdef __GNUC__
static volatile void fatal (const char *message, ...)
      __attribute__((noreturn));
#endif
static volatile void fatal (const char *message, ...)
{
      va_list ap;

      if (errorjump_set)
            longjmp (errorjump, 1);

      va_start (ap, message);
      fprintf (stderr, "%s: ", prog_name);
      vfprintf (stderr, message, ap);
      va_end (ap);
      exit (1);
}

static char line_buffer[LINE_LENGTH];
static char *line_ptr;                    /* interactive input */

char read_line (void)
{
      if (!fgets (line_buffer, LINE_LENGTH, stdin))
            return 0;
      line_ptr = line_buffer;
      while (*line_ptr && !isgraph (*line_ptr))
            line_ptr ++;
      return *line_ptr;
}

char read_char (char *mesg)
{
      do
            fputs (mesg, stdout);
      while (!read_line ());
      return *line_ptr;
}

#define hex_val(c) ({ char _c = (c); isdigit(_c) ? _c - '0' : tolower(_c) + 10 - 'a'; })

int read_hex (struct systypes *sys, int size)
{
      int hex;

      while (1) {
            read_char ("Hex code (type L to list codes): ");
            if (tolower (*line_ptr) == 'l')
                  part_list_types (sys, size);
            else if (isxdigit (*line_ptr)) {
                  hex = 0;
                  do
                        hex = hex << 4 | hex_val (*line_ptr++);
                  while (isxdigit (*line_ptr));
                  return hex;
            }
      }
}

enum offset {
      BASE_IGNORE, BASE_LOWER, BASE_UPPER, BASE_DEFAULT
};

int read_int (Partition_t *part, unsigned int low,
                         unsigned int dflt,
                         unsigned int high, enum offset base, char *mesg)
{
      unsigned int i, use_default = 1;
      char ms[70];

      switch (base) {
      case BASE_LOWER:
            sprintf (ms, "%s ([%d]-%d): ", mesg, low, high);
            break;
      case BASE_UPPER:
            sprintf (ms, "%s (%d-[%d]): ", mesg, low, high);
            break;
      case BASE_DEFAULT:
            sprintf (ms, "%s: (%d-[%d]-%d): ", mesg, low, dflt, high);
            break;
      default:
            sprintf (ms, "%s: (%d-%d): ", mesg, low, high);
            break;
      }

      while (1) {
            while (!isdigit (read_char (ms)) && (*line_ptr != '-' && *line_ptr != '+'))
                  continue;
            if (*line_ptr == '+' || *line_ptr == '-') {
                  if (*line_ptr == '+')
                        line_ptr ++;
                  i = atoi (line_ptr);
                  while (isdigit (*line_ptr)) {
                        line_ptr ++;
                        use_default = 0;
                  }
                  switch (*line_ptr) {
                  case 'c': case 'C':
                        if (!unit_flag)
                              i *= part->geo.heads * part->geo.sectors;
                        break;
                  case 'k': case 'K':
                        i = (i << 1) / display_factor;
                        break;
                  case 'm': case 'M':
                        i = (i << 11) / display_factor;
                        break;
                  default:
                        break;
                  }
                  switch (base) {
                  case BASE_LOWER:
                        i += low;
                        break;
                  case BASE_UPPER:
                        i += high;
                        break;
                  case BASE_DEFAULT:
                        i += dflt;
                  default:
                        break;
                  }
            } else {
                  i = atoi (line_ptr);
                  while (isdigit (*line_ptr)) {
                        line_ptr ++;
                        use_default = 0;
                  }
            }
            if (use_default)
                  printf ("Using default value %d\n", i = dflt);
            if (i >= low && i <= high)
                  break;
            else
                  printf ("Value out of range.\n");
      }
      return i;
}

int select_partition (Partition_t *partitions, bool warn, int min, int max)
{
      int i;

      if (!max) {
            min = 1;
            for (i = 0; i < partitions->nr_parts; i++)
                  if (max < partitions->parts[i].dev_index)
                        max = partitions->parts[i].dev_index;
      }

      max = read_int (partitions, min, max, max, BASE_IGNORE, "Partition number");

      if (warn) {
            for (i = 0; i < partitions->nr_parts; i++)
                  if (max == partitions->parts[i].dev_index)
                        break;
            if (i >= partitions->nr_parts)
                  printf ("Warning: partition %d has empty type\n", max);
      }
      return max;
}

void log2chs (Partition_t *part, unsigned long ls, unsigned int *c, unsigned int *h, unsigned int *s)
{
      int spc = part->geo.heads * part->geo.sectors;

      *c = ls / spc;
      ls = ls % spc;
      *h = ls / part->geo.sectors;
      *s = ls % part->geo.sectors + 1;
}

void check_bounds (Partition_t *part, unsigned long *first, unsigned long *last)
{
      int i;

      assert (part != NULL);
      assert (first != NULL);
      assert (last != NULL);

      for (i = 0; i < part->nr_parts; i++) {
            Part_t *p = &part->parts[i];

            first[i] = calculate (part, p->begin.heads, p->begin.sectors, p->begin.cylinders);
            last[i] = p->start + p->size - 1;
      }
}

static void check_consistency (Partition_t *part, Part_t *p)
{
      unsigned int pbc, pbh, pbs;
      unsigned int pec, peh, pes;
      unsigned int lbc, lbh, lbs;
      unsigned int lec, leh, les;

      /* physical beginning CHS */
      pbc = p->begin.cylinders;
      pbh = p->begin.heads;
      pbs = p->begin.sectors;
      /* physical end CHS */
      pec = p->end.cylinders;
      peh = p->end.heads;
      pes = p->end.sectors;
      /* logical beginning CHS */
      log2chs (part, p->start, &lbc, &lbh, &lbs);
      /* logical end CHS */
      log2chs (part, p->start + p->size - 1, &lec, &leh, &les);

      if (part->geo.cylinders <= 1024) {
            if (pbc != lbc || pbh != lbh || pbs != lbs) {
                  printf ("Partition %d has different physical/logical "
                        "beginnings (non-Linux?):\n", p->dev_index);
                  printf ("     phys=(%d, %d, %d) ", pbc, pbh, pbs);
                  printf (" logi=(%d, %d, %d)\n", lbc, lbh, lbs);
            }

            if (pec != lec || peh != leh || pes != les) {
                  printf ("Partition %d has different physical/logical "
                        "endings:\n", p->dev_index);
                  printf ("     phys=(%d, %d, %d) ", pec, peh, pes);
                  printf (" logi=(%d, %d, %d)\n", lec, leh, les);
            }
      }

      if (p->must_start_cyl && (pbh != 0/*!pbc*/ || pbs != 1)) {
            printf ("Partition %d does not start on a cylinder boundary:\n",
                  p->dev_index);
            printf ("     phys=(%d, %d, %d) ", pbc, pbh, pbs);
            printf ("should be (%d, %d, 1)\n", pbc, 0/*!pbc*/);
      }

      if (p->must_end_cyl && (peh != part->geo.heads - 1 || pes != part->geo.sectors)) {
            printf ("Partition %d does not end on a cylinder boundary:\n",
                  p->dev_index);
            printf ("     phys=(%d, %d, %d) ", pec, peh, pes);
            printf ("should be (%d, %d, %d)\n", pec, part->geo.heads - 1,
                        part->geo.sectors);
      }

      if (p->must_1mb && p->size % 1048576)
            printf ("Partition %d does not contain a whole number of MB\n",
                  p->dev_index);
}

void check (Partition_t *part, Part_t *p, unsigned long start)
{
/*    if (!total)
            printf ("Warning: partition %d contains sector 0\n", p->dev_index);
 */
      if (p->end.heads >= part->geo.heads)
            printf ("Partition %d: head %d is greater than maximum %d\n",
                  p->dev_index, p->end.heads, part->geo.heads - 1);

      if (p->end.sectors > part->geo.sectors)
            printf ("Partition %d; sector %d is greater than maximum %d\n",
                  p->dev_index, p->end.sectors, part->geo.sectors);

      if (p->end.cylinders >= part->geo.cylinders)
            printf ("Partition %d: cylinder %d is greater than maximum %d\n",
                  p->dev_index, p->end.cylinders, part->geo.cylinders - 1);
}

Part_t *get_part (Partition_t *part, int dev_index)
{
      int i;

      assert (part != NULL);

      for (i = 0; i < part->nr_parts; i++)
            if (part->parts[i].dev_index == dev_index)
                  break;

      if (i >= part->nr_parts)
            return NULL;

      return &part->parts[i];
}

/*
 * Other functions...
 */

static void move_begin (Partition_t *part, int dev_index)
{
      Part_t *p = get_part (part, dev_index);
      unsigned long first, new_first;

      if (!p)
            return;

      if (!p->sysid || !p->size || p->sysid == SYSID_EXTENDED) {
            printf ("Partition %d has no data area\n", dev_index);
            return;
      }

      first = calculate (part, p->begin.heads, p->begin.sectors, p->begin.cylinders);
      new_first = read_int (part, first, first, p->start + p->size - 1,
                        BASE_LOWER, "New beginning of data");
      if (new_first != p->size) {
            first = p->size + p->start - new_first;
            p->size = first;
            p->start = new_first;
            p->changed = TRUE;
      }
}

static void print_raw (Partition_t *part)
{
      printf ("Device: %s\n", part->device);
      part_dump (part);
}

static void toggle_active (Partition_t *part, int dev_index)
{
      Part_t *p = get_part (part, dev_index);

      if (!p)
            return;

      if (p->boot_flags)
            p->boot_flags = 0;
      else
            p->boot_flags = ACTIVE_FLAG;
      p->changed = TRUE;
}

static void toggle_dos (Partition_t *part)
{
      if ((dos_compatible_flag = !dos_compatible_flag))
            sector_offset = part->geo.sectors;
      else
            sector_offset = 1;

      printf ("DOS compatibility flag is %sset", dos_compatible_flag ? "" : "not ");
}

static int delete_partition (Partition_t *part, int dev_index)
{
      int i, j;

      assert (part != NULL);

      for (j = 0; j < part->nr_parts; j++)
            if (part->parts[j].dev_index == dev_index)
                  break;
      if (j >= part->nr_parts)
            return 0;

      if (part->parts[j].dev_index_owner == part->parts[j].dev_index) {
            printf ("Cannot delete partition %d\n", dev_index);
            return 1;
      }

      for (i = 0; i < part->nr_parts; i++)
            if (part->parts[i].dev_index_owner == dev_index &&
                  delete_partition (part, part->parts[i].dev_index))
                  return 1;

      part->parts[j].changed = TRUE;
      part->parts[j].boot_flags = 0;
      part->parts[j].begin.sectors = 0;
      part->parts[j].begin.heads = 0;
      part->parts[j].begin.cylinders = 0;
      part->parts[j].end.sectors = 0;
      part->parts[j].end.heads = 0;
      part->parts[j].end.cylinders = 0;
      part->parts[j].start = 0;
      part->parts[j].size = 0;
      part->parts[j].sysid = SYSID_EMPTY;
      return 0;
}

static int add_partition (Partition_t *part, int dev_index, int dev_index_owner, PType_t type)
{
      Part_t *p = get_part (part, dev_index);
      unsigned long *first, *last, start, stop = 0, limit;
      unsigned long temp;
      char mesg[48];
      int read, i;

      if (p && p->sysid) {
            printf ("Partition %d is already defined.  Delete "
                  "it before adding it.\n", dev_index);
            return 0;
      }

      first = malloc (sizeof (unsigned long) * part->nr_parts);
      if (!first)
            return -1;
      last = malloc (sizeof (unsigned long) * part->nr_parts);
      if (!last)
            return -1;

      check_bounds (part, first, last);
      if (unit_flag)
            for (i = 0; i < part->nr_parts; i++)
                  first[i] = (cround (first[i]) - 1) * display_factor;

      start = sector_offset;
      limit = part->geo.heads * part->geo.sectors * part->geo.cylinders - 1;
      if (type == PT_LOGICAL) {
            for (i = 0; i < part->nr_parts; i++)
                  if (part->parts[i].dev_index == dev_index_owner) {
                        start = part->parts[i].start + 1;
                        limit = part->parts[i].start + part->parts[i].size - 1;
                  }
      }

      sprintf (mesg, "First %s", unit_flag ? "cylinder" : "sector");

      read = 0;
      do {
            temp = start;
            for (i = 0; i < part->nr_parts; i++)
                  if (start >= first[i] && start <= last[i] &&
                      (part->parts[i].sysid != SYSID_EXTENDED || type != PT_LOGICAL))
                        start = last[i] + 1;
            if (start > limit)
                  break;
            if (start != temp && read) {
                  printf ("Sector %ld is already allocated\n", temp);
                  temp = start = stop;
                  read = 0;
            }
            if (!read && start == temp) {
                  unsigned long i;
                  i = stop = start;
                  start = read_int (part,
                                cround (i),
                                cround (i),
                                cround (limit),
                                BASE_IGNORE, mesg);
                  if (unit_flag) {
                        start = (start - 1) * display_factor;
                        if (start < i)
                              start = i;
                  }
                  read = 1;
            }
      } while (start != temp || !read);

      for (i = 0; i < part->nr_parts; i++) {
            if (start < first[i] && limit >= first[i] &&
                (part->parts[i].sysid != SYSID_EXTENDED || type != PT_LOGICAL))
                  limit = first[i] - 1;
      }
      if (start > limit) {
            printf ("No free sectors available\n");
            return 0;
      }
      if (cround (start) == cround (limit))
            stop = start;
      else {
            sprintf (mesg, "Last %s or +size or +sizeM or +sizeK",
                   unit_flag ? "cylinder" : "sector");
            stop = read_int (part, cround (start), cround (limit), cround (limit),
                         BASE_LOWER, mesg);
            if (unit_flag) {
                  stop = stop * display_factor - 1;
                  if (stop > limit)
                        stop = limit;
            }
      }

      if (!p) {
            p = realloc (part->parts, sizeof (Part_t) * (part->nr_parts + 1));
            if (!p)
                  return -1;

            part->parts = p;
            p = &part->parts[part->nr_parts++];
      }
      p->dev_index = dev_index;
      p->dev_index_owner = dev_index_owner;
      p->boot_flags = 0;
      p->sysid = type == PT_EXTENDED ? SYSID_EXTENDED : SYSID_LINUXNAT;
      p->start = start;
      p->size = stop - start + 1;
      p->begin.sectors = start % part->geo.sectors + 1;
      start /= part->geo.sectors;
      p->begin.heads = start % part->geo.heads;
      p->begin.cylinders = start / part->geo.heads;
      p->end.sectors = stop % part->geo.sectors + 1;
      stop /= part->geo.sectors;
      p->end.heads = stop % part->geo.heads;
      p->end.cylinders = stop / part->geo.heads;
      p->changed = TRUE;

      return 0;
}

static void new_partition (Partition_t *part)
{
      int partition_types, i;
      PType_t types[3];

      partition_types = part_getavailabletypes (part);
      if (!partition_types)
            printf ("All partitions are used.  Please delete some partitions "
                  "first\n");
      else {
            int lower, upper;
            i = 0;
            if (partition_types & 1)
                  types[i++] = PT_PRIMARY;
            if (partition_types & 2)
                  types[i++] = PT_EXTENDED;
            if (partition_types & 4)
                  types[i++] = PT_LOGICAL;
            if (i != 1) {
                  int j;
                  printf ("Command action\n");
                  for (j = 0; j < i; j++)
                        switch (types[j]) {
                        case PT_PRIMARY:
                              printf ("   p    primary\n");
                              break;
                        case PT_EXTENDED:
                              printf ("   e    extended\n");
                              break;
                        case PT_LOGICAL:
                              printf ("   l    logical\n");
                              break;
                        }
                  i = 0;
                  do {
                        char c;
                        c = tolower (read_char ("\nPartition type: "));
                        switch (c) {
                        case 'p':
                              if (partition_types & 1)
                                    types[i++] = PT_PRIMARY;
                              break;
                        case 'e':
                              if (partition_types & 2)
                                    types[i++] = PT_EXTENDED;
                              break;
                        case 'l':
                              if (partition_types & 4)
                                    types[i++] = PT_LOGICAL;
                        }
                  } while (!i);
            }

            part_getpartrange (part, types[0], &lower, &upper);
            add_partition (part, select_partition (part, FALSE, lower, upper), 2, types[0]);
      }
}

static void change_sysid (Partition_t *part, int dev_index)
{
      Part_t *p = get_part (part, dev_index);
      int sys;

      if (!p)
            return;

      if (p->sysid == SYSID_EXTENDED)
            printf ("Partition %d is extended.  Delete it\n", dev_index);
      else while (1) {
            sys = read_hex (NULL, 0);

            if (!sys) {
                  delete_partition (part, dev_index);
                  break;
            } else if (sys == SYSID_EXTENDED) {
                  printf ("You may not change a partition "
                        "to be an extended partition\n");
                  break;
            } else if (sys != p->sysid) {
                  p->sysid = sys;
                  printf ("Changed system type of partition %d to %x (%s)\n",
                        dev_index, sys, part_type (p->sysid));
                  p->changed = TRUE;
                  part->changed = 1;
                  break;
            }
      }
}

static void change_units (Partition_t *part)
{
      if ((unit_flag = !unit_flag))
            display_factor = 1;
      else
            display_factor = part->geo.heads * part->geo.sectors;
      update_units (part);
      printf ("Changing display/entry units to %ss\n",
            unit_flag ? "cylinder" : "sector");
}

static int verify (Partition_t *part)
{
      unsigned int total = 1;
      unsigned long *first, *last;
      int i, j;

      first = malloc (sizeof (unsigned long) * part->nr_parts);
      if (!first)
            return -1;
      last = malloc (sizeof (unsigned long) * part->nr_parts);
      if (!last)
            return -1;

      check_bounds (part, first, last);

      for (i = 0; i < part->nr_parts; i++) {
            Part_t *p = &part->parts[i];
            if (p->sysid != SYSID_EMPTY) {
                  if (p->sysid != SYSID_EXTENDED) {
                        check_consistency (part, p);
                        if (p->start < first[i])
                              printf ("Warning: bad start-of-data in partition %d.\n",
                                    p->dev_index);
                        check (part, p, last[i]);
                        total += last[i] + 1 - first[i];
                        if (p->dev_index_owner != p->dev_index) {
                              for (j = 0; j < part->nr_parts; j++)
                                    if (p->dev_index_owner == part->parts[j].dev_index) {
                                          if (first[i] <= first[j] + 1 ||
                                              last[i] > last[j])
                                                printf ("Logical partition %d not "
                                                      "entirely in partition %d\n",
                                                      p->dev_index, p->dev_index_owner);
                                          break;
                                    }
                              if (j >= part->nr_parts)
                                    printf ("Logical partition %d does not have a valid "
                                          "parent\n", p->dev_index);
                        }
                        for (j = 0; j < i; j++) {
                              if (part->parts[j].sysid == SYSID_EXTENDED)
                                    continue;
                              if ((first[i] >= first[j] && first[i] <= last[j]) ||
                                  (last[i] <= last[j] && last[i] >= first[j])) {
                                    printf ("Warning: partition %d overlaps partition %d.\n",
                                          part->parts[i].dev_index, part->parts[j].dev_index);
                                    total += first[i] >= first[j] ? first[i] : first[j];
                                    total -= last[i] <= last[j] ? last[i] : last[j];
                              }
                        }
                  } else {
                        total += 1;
                  }
            }
      }

      if (total > part->geo.heads * part->geo.sectors * part->geo.cylinders)
            printf ("Total allocated sectors %d greater than the maximum %d\n",
                  total, part->geo.heads * part->geo.sectors * part->geo.cylinders);
      else if ((total = part->geo.heads * part->geo.sectors * part->geo.cylinders - total))
            printf ("%d unallocated sectors\n", total);
      return 0;
}

static bool write_table (Partition_t *part)
{
      return part_write (part) >= 0;
}

#define REPORT_ERRORS 0
#define IGNORE_ERRORS 1
static void print_part_table (const char *dev, int errorstatus)
{
      static Partition_t *partitions;

      assert (dev != NULL);

      if (!errorjump_set && errorstatus == IGNORE_ERRORS) {
            errorjump_set = TRUE;
            if (setjmp (errorjump)) {
                  part_close (partitions);
                  partitions = NULL;
                  return;
            }
      }

      partitions = part_open (dev, READ_ONLY);
      if (!partitions)
            fatal ("Cannot open %s: %s\n", dev, get_error());

      list_table (partitions, LIST_NORMAL);
      part_close (partitions);
      partitions = NULL;
}

static int print_part_size (const char *dev)
{
      Partition_t *partitions;
      char device[9];
      int part_no, i;

      assert (dev != NULL);

      if (strlen (dev) <= 8 || (!(part_no = atoi (dev + 8))))
            fatal (usage);

      strncpy (device, dev, 8);
      device[8] = '\0';

      partitions = part_open (device, READ_ONLY);
      if (!partitions)
            fatal ("Cannot open %s: %s\n", device, get_error());

      for (i = 0; i < partitions->nr_parts; i++)
            if (partitions->parts[i].dev_index == part_no) {
                  printf ("%ld\n", partitions->parts[i].size / 2);
                  break;
            }
      i = i < partitions->nr_parts ? 0 : 1;

      part_close (partitions);

      return i;
}

static void show_extra_menu (void)
{
      puts ("\nCommand action\n"
            "   b    move beginning of data in a partition\n"
            "   c    change number of cylinders\n"
            "   d    print the raw data in the partition table\n"
            "   e    list extended partitions\n"
            "   h    change number of heads\n"
            "   m    print this menu\n"
            "   p    print the partition table\n"
            "   q    quit without saving changes\n"
            "   r    return to main menu\n"
            "   s    change number of sectors\n"
            "   v    verify the partition table\n"
            "   w    write table to disk and exit");
}

static bool extra_menu (Partition_t *partitions)
{
      bool quit = FALSE;
      static bool first = TRUE;

      if (first)
            show_extra_menu ();
      first = FALSE;

      do {
            putchar ('\n');
            switch (tolower (read_char ("Expert command (m for help): "))) {
                  case 'b':
                        move_begin (partitions, select_partition (partitions, TRUE, 0, 0));
                        break;
                  case 'c':
                        partitions->geo.cylinders = read_int (partitions, 1,
                                    partitions->geo.cylinders, 65535,
                                    BASE_DEFAULT, "Number of cylinders");
                        break;
                  case 'd':
                        print_raw (partitions);
                        break;
                  case 'e':
                        list_table (partitions, LIST_EXTENDED_EXTENDED);
                        break;
                  case 'h':
                        partitions->geo.heads = read_int (partitions, 1,
                                    partitions->geo.heads, 256, BASE_DEFAULT,
                                    "Number of heads");
                        update_units (partitions);
                        break;
                  case 'm':
                  default:
                        show_extra_menu ();
                        break;
                  case 'p':
                        list_table (partitions, LIST_EXTENDED_NORMAL);
                        break;
                  case 'q':
                        quit = TRUE;
                        break;
                  case 'r':
                        return FALSE;
                  case 's':
                        partitions->geo.sectors = read_int (partitions, 1,
                                    partitions->geo.sectors, 63, BASE_DEFAULT,
                                    "Number of sectors");
                        if (dos_compatible_flag) {
                              sector_offset = partitions->geo.sectors;
                              printf ("Warning: setting sector offset for DOS "
                                    "compatability\n");
                        }
                        update_units (partitions);
                        break;
                  case 'v':
                        verify (partitions);
                        break;
                  case 'w':
                        quit = write_table (partitions);
                        break;
            }
      } while (!quit);

      return TRUE;
}

static void show_main_menu (void)
{
      puts ("\nCommand action\n"
            "   a    toggle a bootable flag\n"
            "   b    edit bsd disklabel\n" /* bf */
            "   c    toggle the dos compatiblity flag\n"
            "   d    delete a partition\n"
            "   l    list known partition types\n"
            "   m    print this menu\n"
            "   n    add a new partition\n"
            "   p    print the partition table\n"
            "   q    quit without saving changes\n"
            "   t    change a partition's system id\n"
            "   u    change display/entry units\n"
            "   v    verify the partition table\n"
            "   w    write table to disk and exit\n"
            "   x    extra functionality (experts only)");
}

static void main_menu (const char *dev, part_open_mode_t open_mode)
{
      Partition_t *partitions;
      bool quit = FALSE;

      assert (dev != NULL);

      partitions = part_open (dev, open_mode);
      if (!partitions)
            fatal ("Cannot open %s: %s\n", dev, get_error());

      if (dos_compatible_flag)
            sector_offset = partitions->geo.sectors;

      update_units (partitions);

      show_main_menu ();

      do {
            putchar ('\n');
            switch (tolower (read_char ("Command (m for help): "))) {
                  case 'a':
                        toggle_active (partitions, select_partition (partitions, TRUE, 0, 0));
                        break;
                  case 'b':
                        break;
                  case 'c':
                        toggle_dos (partitions);
                        break;
                  case 'd':
                        delete_partition (partitions, select_partition (partitions, FALSE, 0, 0));
                        break;
                  case 'l':
                        part_list_types (NULL, 0);
                        break;
                  case 'm':
                  default:
                        show_main_menu ();
                        break;
                  case 'n':
                        new_partition (partitions);
                        break;
                  case 'p':
                        list_table (partitions, LIST_NORMAL);
                        break;
                  case 'q':
                        quit = TRUE;
                        break;
                  case 't':
                        change_sysid (partitions, select_partition (partitions, TRUE, 0, 0));
                        break;
                  case 'u':
                        change_units (partitions);
                        break;
                  case 'v':
                        verify (partitions);
                        break;
                  case 'w':
                        quit = write_table (partitions);
                        break;
                  case 'x':
                        quit = extra_menu (partitions);
                        break;
            }
      } while (!quit);

      part_close (partitions);
}

int main (int argc, char *argv[])
{
      const char *device = NULL;
      part_open_mode_t open_mode = READ_WRITE;
      int i;

      prog_name = strrchr (argv[0], '/');
      if (prog_name)
            prog_name ++;
      else
            prog_name = argv[0];
      if (argc > 1 && *argv[1] == '-') {
            switch (argv[1][1]) {
                  case 'v':
                        printf ("fdisk v" VERSION "\n");
                        exit (0);
                  case 'l':
                        if (argc > 2) {
                              for (i = 2; i < argc; i++)
                                    print_part_table (argv[i], REPORT_ERRORS);
                        } else {
                              for (i = 0; i < NR_DEVICES; i++)
                                    print_part_table (part_devs[i], IGNORE_ERRORS);
                        }
                        return 0;
                  case 's':
                        if (argc < 3)
                              fatal (usage);
                        return print_part_size (argv[2]);
                  default:
                        fprintf (stderr, "Option -%c not recognised\n", argv[1][1]);
                        exit (1);
            }
      }

      if (argc > 3)
            fatal (usage);

      if (argc > 1) {
            int fd;

            device = argv[argc - 1];

            fd = open (device, O_RDWR);
            if (fd < 0) {
                  open_mode = READ_ONLY;
                  fd = open (device, O_RDONLY);
                  if (fd < 0)
                        fatal ("can't open device '%s': %s\n", device, strerror (errno));
            }
            close (fd);
      } else {
            int fd, mode;

            for (mode = 0; mode < 2; mode++) {
                  for (i = 0; i < NR_DEF_DEVS; i++) {
                        fd = open (default_devs[i], open_mode == READ_WRITE ? O_RDWR : O_RDONLY);
                        if (fd >= 0)
                              break;
                  }
                  if (fd >= 0) {
                        device = default_devs[i];
                        close (fd);
                        break;
                  }
                  open_mode = READ_ONLY;
            }

            if (!device)
                  fatal ("can't open any device: %s\n", strerror (errno));

            printf ("Using %s as default device\n", device);
      }

      if (open_mode == READ_ONLY)
            printf ("Warning: can't get write permission on device '%s'\n", device);

      main_menu (device, open_mode);
      exit (0);
}
#endif

Generated by  Doxygen 1.6.0   Back to index