1 1.8 dholland /* $NetBSD: boot.c,v 1.8 2016/06/11 06:58:42 dholland Exp $ */ 2 1.1 nonaka 3 1.1 nonaka /* 4 1.1 nonaka * Copyright (c) 2009 NONAKA Kimihiro <nonaka (at) netbsd.org> 5 1.1 nonaka * All rights reserved. 6 1.1 nonaka * 7 1.1 nonaka * Redistribution and use in source and binary forms, with or without 8 1.1 nonaka * modification, are permitted provided that the following conditions 9 1.1 nonaka * are met: 10 1.1 nonaka * 1. Redistributions of source code must retain the above copyright 11 1.1 nonaka * notice, this list of conditions and the following disclaimer. 12 1.1 nonaka * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 nonaka * notice, this list of conditions and the following disclaimer in the 14 1.1 nonaka * documentation and/or other materials provided with the distribution. 15 1.1 nonaka * 16 1.1 nonaka * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 1.1 nonaka * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 1.1 nonaka * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 1.1 nonaka * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 1.1 nonaka * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 1.1 nonaka * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 1.1 nonaka * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 1.1 nonaka * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 1.1 nonaka * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 1.1 nonaka * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 1.1 nonaka */ 27 1.1 nonaka 28 1.1 nonaka #include <sys/types.h> 29 1.1 nonaka #include <sys/bootblock.h> 30 1.1 nonaka #include <sys/boot_flag.h> 31 1.1 nonaka 32 1.1 nonaka #include "boot.h" 33 1.1 nonaka #include "bootinfo.h" 34 1.1 nonaka #include "bootmenu.h" 35 1.1 nonaka #include "disk.h" 36 1.1 nonaka #include "unixdev.h" 37 1.1 nonaka #include "pathnames.h" 38 1.1 nonaka 39 1.1 nonaka #include <lib/libsa/loadfile.h> 40 1.1 nonaka #include <lib/libsa/ufs.h> 41 1.1 nonaka 42 1.1 nonaka #include "compat_linux.h" 43 1.1 nonaka 44 1.1 nonaka static const char * const names[][2] = { 45 1.1 nonaka { "netbsd", "netbsd.gz" }, 46 1.1 nonaka { "netbsd.old", "netbsd.old.gz", }, 47 1.1 nonaka { "onetbsd", "onetbsd.gz" }, 48 1.1 nonaka }; 49 1.1 nonaka 50 1.1 nonaka char *default_devname; 51 1.1 nonaka uint default_unit, default_partition; 52 1.1 nonaka const char *default_filename; 53 1.1 nonaka int default_timeout = 5; 54 1.1 nonaka 55 1.1 nonaka static char probed_disks[256]; 56 1.5 nonaka static char bootconfpath[1024]; 57 1.1 nonaka 58 1.1 nonaka static void bootcmd_help(char *); 59 1.1 nonaka static void bootcmd_ls(char *); 60 1.1 nonaka static void bootcmd_quit(char *); 61 1.1 nonaka static void bootcmd_boot(char *); 62 1.1 nonaka static void bootcmd_disk(char *); 63 1.1 nonaka #ifdef SUPPORT_CONSDEV 64 1.1 nonaka static void bootcmd_consdev(char *); 65 1.1 nonaka #endif 66 1.1 nonaka 67 1.1 nonaka static const struct bootblk_command { 68 1.1 nonaka const char *c_name; 69 1.1 nonaka void (*c_fn)(char *arg); 70 1.1 nonaka } bootcmds[] = { 71 1.1 nonaka { "help", bootcmd_help }, 72 1.1 nonaka { "?", bootcmd_help }, 73 1.1 nonaka { "quit", bootcmd_quit }, 74 1.1 nonaka { "ls", bootcmd_ls }, 75 1.1 nonaka { "boot", bootcmd_boot }, 76 1.1 nonaka { "disk", bootcmd_disk }, 77 1.1 nonaka #ifdef SUPPORT_CONSDEV 78 1.1 nonaka { "consdev", bootcmd_consdev }, 79 1.1 nonaka #endif 80 1.1 nonaka { NULL, NULL }, 81 1.1 nonaka }; 82 1.1 nonaka 83 1.1 nonaka static struct btinfo_howto bi_howto; 84 1.1 nonaka 85 1.1 nonaka static void print_banner(void); 86 1.1 nonaka static int exec_netbsd(const char *file, int howto); 87 1.1 nonaka 88 1.1 nonaka int 89 1.1 nonaka parsebootfile(const char *fname, char **fsname, char **devname, 90 1.1 nonaka uint *unit, uint *partition, const char **file) 91 1.1 nonaka { 92 1.1 nonaka const char *col; 93 1.1 nonaka 94 1.1 nonaka *fsname = "ufs"; 95 1.1 nonaka *devname = default_devname; 96 1.1 nonaka *unit = default_unit; 97 1.1 nonaka *partition = default_partition; 98 1.1 nonaka *file = default_filename; 99 1.1 nonaka 100 1.1 nonaka if (fname == NULL) 101 1.1 nonaka return 0; 102 1.1 nonaka 103 1.1 nonaka if ((col = strchr(fname, ':'))) { /* device given */ 104 1.1 nonaka static char savedevname[MAXDEVNAME+1]; 105 1.1 nonaka int devlen; 106 1.1 nonaka unsigned int u = 0, p = 0; 107 1.1 nonaka int i = 0; 108 1.1 nonaka 109 1.1 nonaka devlen = col - fname; 110 1.1 nonaka if (devlen > MAXDEVNAME) 111 1.1 nonaka return EINVAL; 112 1.1 nonaka 113 1.1 nonaka #define isvalidname(c) ((c) >= 'a' && (c) <= 'z') 114 1.1 nonaka if (!isvalidname(fname[i])) 115 1.1 nonaka return EINVAL; 116 1.1 nonaka do { 117 1.1 nonaka savedevname[i] = fname[i]; 118 1.1 nonaka i++; 119 1.1 nonaka } while (isvalidname(fname[i])); 120 1.1 nonaka savedevname[i] = '\0'; 121 1.1 nonaka 122 1.1 nonaka #define isnum(c) ((c) >= '0' && (c) <= '9') 123 1.1 nonaka if (i < devlen) { 124 1.1 nonaka if (!isnum(fname[i])) 125 1.1 nonaka return (EUNIT); 126 1.1 nonaka do { 127 1.1 nonaka u *= 10; 128 1.1 nonaka u += fname[i++] - '0'; 129 1.1 nonaka } while (isnum(fname[i])); 130 1.1 nonaka } 131 1.1 nonaka 132 1.3 nonaka #define isvalidpart(c) ((c) >= 'a' && (c) < 'a' + MAXPARTITIONS) 133 1.1 nonaka if (i < devlen) { 134 1.1 nonaka if (!isvalidpart(fname[i])) 135 1.1 nonaka return (EPART); 136 1.1 nonaka p = fname[i++] - 'a'; 137 1.1 nonaka } 138 1.1 nonaka 139 1.1 nonaka if (i != devlen) 140 1.1 nonaka return ENXIO; 141 1.1 nonaka 142 1.1 nonaka *devname = savedevname; 143 1.1 nonaka *unit = u; 144 1.1 nonaka *partition = p; 145 1.1 nonaka fname = col + 1; 146 1.1 nonaka } 147 1.1 nonaka 148 1.1 nonaka if (*fname) 149 1.1 nonaka *file = fname; 150 1.1 nonaka 151 1.1 nonaka return 0; 152 1.1 nonaka } 153 1.1 nonaka 154 1.1 nonaka char * 155 1.1 nonaka sprint_bootsel(const char *filename) 156 1.1 nonaka { 157 1.1 nonaka static char buf[80]; 158 1.1 nonaka char *fsname, *devname; 159 1.1 nonaka uint unit, partition; 160 1.1 nonaka const char *file; 161 1.1 nonaka 162 1.1 nonaka if (parsebootfile(filename, &fsname, &devname, &unit, &partition, 163 1.1 nonaka &file) == 0) { 164 1.1 nonaka snprintf(buf, sizeof(buf), "%s%d%c:%s", devname, unit, 165 1.1 nonaka 'a' + partition, file); 166 1.1 nonaka return buf; 167 1.1 nonaka } 168 1.1 nonaka return "(invalid)"; 169 1.1 nonaka } 170 1.1 nonaka 171 1.1 nonaka static void 172 1.1 nonaka print_banner(void) 173 1.1 nonaka { 174 1.1 nonaka extern const char bootprog_name[]; 175 1.1 nonaka extern const char bootprog_rev[]; 176 1.1 nonaka 177 1.1 nonaka printf("\n"); 178 1.1 nonaka printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev); 179 1.1 nonaka } 180 1.1 nonaka 181 1.1 nonaka void 182 1.1 nonaka boot(dev_t bootdev) 183 1.1 nonaka { 184 1.1 nonaka extern char twiddle_toggle; 185 1.1 nonaka int currname; 186 1.1 nonaka int c; 187 1.1 nonaka 188 1.1 nonaka consinit(CONSDEV_GLASS, -1); 189 1.1 nonaka 190 1.1 nonaka twiddle_toggle = 1; /* no twiddling until we're ready */ 191 1.1 nonaka 192 1.1 nonaka /* set default value: hd0a:netbsd */ 193 1.1 nonaka default_devname = "hd"; 194 1.1 nonaka default_unit = 0; 195 1.1 nonaka default_partition = 0; 196 1.1 nonaka default_filename = names[0][0]; 197 1.1 nonaka 198 1.1 nonaka diskprobe(probed_disks, sizeof(probed_disks)); 199 1.1 nonaka 200 1.5 nonaka snprintf(bootconfpath, sizeof(bootconfpath), "%s%d%c:%s", 201 1.5 nonaka default_devname, default_unit, 'a' + default_partition, 202 1.6 rtr BOOTCFG_FILENAME); 203 1.5 nonaka parsebootconf(bootconfpath); 204 1.1 nonaka 205 1.1 nonaka #ifdef SUPPORT_CONSDEV 206 1.1 nonaka /* 207 1.1 nonaka * If console set in boot.cfg, switch to it. 208 1.1 nonaka * This will print the banner, so we don't need to explicitly do it 209 1.1 nonaka */ 210 1.6 rtr if (bootcfg_info.consdev) 211 1.6 rtr bootcmd_consdev(bootcfg_info.consdev); 212 1.1 nonaka else 213 1.1 nonaka #endif 214 1.1 nonaka print_banner(); 215 1.1 nonaka 216 1.1 nonaka printf("\ndisks: %s\n", probed_disks); 217 1.1 nonaka 218 1.1 nonaka /* Display the menu, if applicable */ 219 1.1 nonaka twiddle_toggle = 0; 220 1.6 rtr if (bootcfg_info.nummenu > 0) { 221 1.1 nonaka /* Does not return */ 222 1.1 nonaka doboottypemenu(); 223 1.1 nonaka } 224 1.1 nonaka 225 1.1 nonaka printf("Press return to boot now, any other key for boot menu\n"); 226 1.1 nonaka currname = 0; 227 1.1 nonaka for (currname = 0; currname < __arraycount(names); currname++) { 228 1.1 nonaka printf("booting %s - starting in ", 229 1.1 nonaka sprint_bootsel(names[currname][0])); 230 1.1 nonaka 231 1.6 rtr c = awaitkey((bootcfg_info.timeout < 0) ? 0 232 1.6 rtr : bootcfg_info.timeout, 1); 233 1.1 nonaka if ((c != '\r') && (c != '\n') && (c != '\0')) { 234 1.1 nonaka printf("type \"?\" or \"help\" for help.\n"); 235 1.1 nonaka bootmenu(); /* does not return */ 236 1.1 nonaka } 237 1.1 nonaka 238 1.1 nonaka /* 239 1.1 nonaka * try pairs of names[] entries, foo and foo.gz 240 1.1 nonaka */ 241 1.1 nonaka /* don't print "booting..." again */ 242 1.1 nonaka bootit(names[currname][0], 0, 0); 243 1.1 nonaka /* since it failed, try compressed bootfile. */ 244 1.1 nonaka bootit(names[currname][1], 0, 1); 245 1.1 nonaka } 246 1.1 nonaka 247 1.1 nonaka bootmenu(); /* does not return */ 248 1.1 nonaka } 249 1.1 nonaka 250 1.1 nonaka void 251 1.1 nonaka bootit(const char *filename, int howto, int tell) 252 1.1 nonaka { 253 1.1 nonaka 254 1.1 nonaka if (tell) { 255 1.1 nonaka printf("booting %s", sprint_bootsel(filename)); 256 1.1 nonaka if (howto) 257 1.1 nonaka printf(" (howto 0x%x)", howto); 258 1.1 nonaka printf("\n"); 259 1.1 nonaka } 260 1.1 nonaka 261 1.1 nonaka if (exec_netbsd(filename, howto) < 0) { 262 1.1 nonaka printf("boot: %s: %s\n", sprint_bootsel(filename), 263 1.1 nonaka strerror(errno)); 264 1.1 nonaka } else { 265 1.1 nonaka printf("boot returned\n"); 266 1.1 nonaka } 267 1.1 nonaka } 268 1.1 nonaka 269 1.1 nonaka static int 270 1.1 nonaka exec_netbsd(const char *file, int howto) 271 1.1 nonaka { 272 1.1 nonaka u_long marks[MARK_MAX]; 273 1.1 nonaka 274 1.1 nonaka BI_ALLOC(BTINFO_MAX); 275 1.1 nonaka 276 1.1 nonaka bi_howto.howto = howto; 277 1.1 nonaka BI_ADD(&bi_howto, BTINFO_HOWTO, sizeof(bi_howto)); 278 1.1 nonaka 279 1.1 nonaka if (loadfile_zboot(file, marks, LOAD_KERNEL) == -1) 280 1.1 nonaka goto out; 281 1.1 nonaka 282 1.1 nonaka /*NOTREACHED*/ 283 1.1 nonaka return 0; 284 1.1 nonaka 285 1.1 nonaka out: 286 1.1 nonaka BI_FREE(); 287 1.1 nonaka bootinfo = 0; 288 1.1 nonaka return -1; 289 1.1 nonaka } 290 1.1 nonaka 291 1.1 nonaka /* 292 1.1 nonaka * bootmenu 293 1.1 nonaka */ 294 1.1 nonaka static char *gettrailer(char *arg); 295 1.1 nonaka static int parseopts(const char *opts, int *howto); 296 1.1 nonaka static int parseboot(char *arg, char **filename, int *howto); 297 1.1 nonaka 298 1.1 nonaka /* ARGSUSED */ 299 1.1 nonaka static void 300 1.1 nonaka bootcmd_help(char *arg) 301 1.1 nonaka { 302 1.1 nonaka 303 1.1 nonaka printf("commands are:\n" 304 1.5 nonaka "boot [xdNx:][filename] [-1acdqsv]\n" 305 1.5 nonaka " (ex. \"boot hd0a:netbsd.old -s\")\n" 306 1.5 nonaka " (ex. \"boot path:/mnt/card/netbsd -1\")\n" 307 1.1 nonaka "ls [path]\n" 308 1.1 nonaka #ifdef SUPPORT_CONSDEV 309 1.1 nonaka "consdev {glass|com [speed]}\n" 310 1.1 nonaka #endif 311 1.1 nonaka "disk\n" 312 1.1 nonaka "help|?\n" 313 1.1 nonaka "quit\n"); 314 1.1 nonaka } 315 1.1 nonaka 316 1.1 nonaka /* ARGSUSED */ 317 1.1 nonaka static void 318 1.1 nonaka bootcmd_quit(char *arg) 319 1.1 nonaka { 320 1.1 nonaka 321 1.1 nonaka printf("Exiting...\n"); 322 1.1 nonaka exit(0); 323 1.1 nonaka /*NOTREACHED*/ 324 1.1 nonaka } 325 1.1 nonaka 326 1.1 nonaka static void 327 1.1 nonaka bootcmd_ls(char *arg) 328 1.1 nonaka { 329 1.1 nonaka const char *save = default_filename; 330 1.1 nonaka 331 1.1 nonaka default_filename = "/"; 332 1.4 tsutsui ls(arg); 333 1.1 nonaka default_filename = save; 334 1.1 nonaka } 335 1.1 nonaka 336 1.1 nonaka static void 337 1.1 nonaka bootcmd_boot(char *arg) 338 1.1 nonaka { 339 1.1 nonaka char *filename; 340 1.1 nonaka int howto; 341 1.1 nonaka 342 1.1 nonaka if (parseboot(arg, &filename, &howto)) { 343 1.1 nonaka bootit(filename, howto, 1); 344 1.1 nonaka } 345 1.1 nonaka } 346 1.1 nonaka 347 1.1 nonaka /* ARGSUSED */ 348 1.1 nonaka static void 349 1.1 nonaka bootcmd_disk(char *arg) 350 1.1 nonaka { 351 1.1 nonaka 352 1.1 nonaka printf("disks: %s\n", probed_disks); 353 1.1 nonaka } 354 1.1 nonaka 355 1.1 nonaka #ifdef SUPPORT_CONSDEV 356 1.1 nonaka static const struct cons_devs { 357 1.1 nonaka const char *name; 358 1.1 nonaka int tag; 359 1.1 nonaka } cons_devs[] = { 360 1.1 nonaka { "glass", CONSDEV_GLASS }, 361 1.1 nonaka { "com", CONSDEV_COM0 }, 362 1.1 nonaka { "com0", CONSDEV_COM0 }, 363 1.1 nonaka { NULL, 0 } 364 1.1 nonaka }; 365 1.1 nonaka 366 1.1 nonaka static void 367 1.1 nonaka bootcmd_consdev(char *arg) 368 1.1 nonaka { 369 1.1 nonaka const struct cons_devs *cdp; 370 1.1 nonaka char *p; 371 1.1 nonaka int speed = 9600; 372 1.1 nonaka 373 1.1 nonaka p = strchr(arg, ' '); 374 1.1 nonaka if (p != NULL) { 375 1.1 nonaka *p++ = '\0'; 376 1.1 nonaka speed = atoi(p); 377 1.1 nonaka } 378 1.1 nonaka for (cdp = cons_devs; cdp->name != NULL; cdp++) { 379 1.1 nonaka if (strcmp(arg, cdp->name) == 0) { 380 1.1 nonaka consinit(cdp->tag, speed); 381 1.1 nonaka print_banner(); 382 1.1 nonaka return; 383 1.1 nonaka } 384 1.1 nonaka } 385 1.1 nonaka printf("invalid console device.\n"); 386 1.1 nonaka } 387 1.1 nonaka #endif 388 1.1 nonaka 389 1.1 nonaka void 390 1.1 nonaka docommand(char *arg) 391 1.1 nonaka { 392 1.1 nonaka char *options; 393 1.1 nonaka int i; 394 1.1 nonaka 395 1.1 nonaka options = gettrailer(arg); 396 1.1 nonaka 397 1.1 nonaka for (i = 0; bootcmds[i].c_name != NULL; i++) { 398 1.1 nonaka if (strcmp(arg, bootcmds[i].c_name) == 0) { 399 1.1 nonaka (*bootcmds[i].c_fn)(options); 400 1.1 nonaka return; 401 1.1 nonaka } 402 1.1 nonaka } 403 1.1 nonaka 404 1.1 nonaka printf("unknown command\n"); 405 1.1 nonaka bootcmd_help(NULL); 406 1.1 nonaka } 407 1.1 nonaka 408 1.1 nonaka void 409 1.1 nonaka bootmenu(void) 410 1.1 nonaka { 411 1.1 nonaka char input[256]; 412 1.1 nonaka char *c; 413 1.1 nonaka 414 1.1 nonaka for (;;) { 415 1.1 nonaka c = input; 416 1.1 nonaka 417 1.1 nonaka input[0] = '\0'; 418 1.1 nonaka printf("> "); 419 1.8 dholland kgets(input, sizeof(input)); 420 1.1 nonaka 421 1.1 nonaka /* 422 1.1 nonaka * Skip leading whitespace. 423 1.1 nonaka */ 424 1.1 nonaka while (*c == ' ') { 425 1.1 nonaka c++; 426 1.1 nonaka } 427 1.1 nonaka if (*c != '\0') { 428 1.1 nonaka docommand(c); 429 1.1 nonaka } 430 1.1 nonaka } 431 1.1 nonaka } 432 1.1 nonaka 433 1.1 nonaka /* 434 1.1 nonaka * chops the head from the arguments and returns the arguments if any, 435 1.1 nonaka * or possibly an empty string. 436 1.1 nonaka */ 437 1.1 nonaka static char * 438 1.1 nonaka gettrailer(char *arg) 439 1.1 nonaka { 440 1.1 nonaka char *options; 441 1.1 nonaka 442 1.1 nonaka if ((options = strchr(arg, ' ')) == NULL) 443 1.1 nonaka return (""); 444 1.1 nonaka else 445 1.1 nonaka *options++ = '\0'; 446 1.1 nonaka 447 1.1 nonaka /* trim leading blanks */ 448 1.7 dholland while (*options == ' ') 449 1.1 nonaka options++; 450 1.1 nonaka 451 1.1 nonaka return options; 452 1.1 nonaka } 453 1.1 nonaka 454 1.1 nonaka static int 455 1.1 nonaka parseopts(const char *opts, int *howto) 456 1.1 nonaka { 457 1.1 nonaka int r, tmpopt = 0; 458 1.1 nonaka 459 1.1 nonaka opts++; /* skip - */ 460 1.1 nonaka while (*opts && *opts != ' ') { 461 1.1 nonaka r = 0; 462 1.1 nonaka BOOT_FLAG(*opts, r); 463 1.1 nonaka if (r == 0) { 464 1.1 nonaka printf("-%c: unknown flag\n", *opts); 465 1.1 nonaka bootcmd_help(NULL); 466 1.1 nonaka return 0; 467 1.1 nonaka } 468 1.1 nonaka tmpopt |= r; 469 1.1 nonaka opts++; 470 1.1 nonaka } 471 1.1 nonaka 472 1.1 nonaka *howto = tmpopt; 473 1.1 nonaka return 1; 474 1.1 nonaka } 475 1.1 nonaka 476 1.1 nonaka static int 477 1.1 nonaka parseboot(char *arg, char **filename, int *howto) 478 1.1 nonaka { 479 1.1 nonaka char *opts = NULL; 480 1.1 nonaka 481 1.1 nonaka *filename = 0; 482 1.1 nonaka *howto = 0; 483 1.1 nonaka 484 1.1 nonaka /* if there were no arguments */ 485 1.1 nonaka if (arg == NULL || *arg == '\0') 486 1.1 nonaka return 1; 487 1.1 nonaka 488 1.1 nonaka /* format is... */ 489 1.1 nonaka /* [[xxNx:]filename] [-adqsv] */ 490 1.1 nonaka 491 1.1 nonaka /* check for just args */ 492 1.1 nonaka if (arg[0] == '-') { 493 1.1 nonaka opts = arg; 494 1.1 nonaka } else { 495 1.1 nonaka /* there's a file name */ 496 1.1 nonaka *filename = arg; 497 1.1 nonaka 498 1.1 nonaka opts = gettrailer(arg); 499 1.1 nonaka if (opts == NULL || *opts == '\0') { 500 1.1 nonaka opts = NULL; 501 1.1 nonaka } else if (*opts != '-') { 502 1.1 nonaka printf("invalid arguments\n"); 503 1.1 nonaka bootcmd_help(NULL); 504 1.1 nonaka return 0; 505 1.1 nonaka } 506 1.1 nonaka } 507 1.1 nonaka 508 1.1 nonaka /* at this point, we have dealt with filenames. */ 509 1.1 nonaka 510 1.1 nonaka /* now, deal with options */ 511 1.1 nonaka if (opts) { 512 1.1 nonaka if (parseopts(opts, howto) == 0) { 513 1.1 nonaka return 0; 514 1.1 nonaka } 515 1.1 nonaka } 516 1.1 nonaka return 1; 517 1.1 nonaka } 518