1 /* 2 * (C) Copyright 2000-2003 3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 4 * 5 * See file CREDITS for list of people who contributed to this 6 * project. 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License as 10 * published by the Free Software Foundation; either version 2 of 11 * the License, or (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 21 * MA 02111-1307 USA 22 */ 23 24 #include <errno.h> 25 #include <fcntl.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <stddef.h> 29 #include <string.h> 30 #include <sys/types.h> 31 #include <sys/ioctl.h> 32 #include <sys/stat.h> 33 #include <unistd.h> 34 35 #ifdef MTD_OLD 36 # include <linux/mtd/mtd.h> 37 #else 38 # include <mtd/mtd-user.h> 39 #endif 40 41 #include "fw_env.h" 42 43 #define CMD_GETENV "fw_printenv" 44 #define CMD_SETENV "fw_setenv" 45 46 typedef struct envdev_s { 47 char devname[16]; /* Device name */ 48 ulong devoff; /* Device offset */ 49 ulong env_size; /* environment size */ 50 ulong erase_size; /* device erase size */ 51 } envdev_t; 52 53 static envdev_t envdevices[2]; 54 static int curdev; 55 56 #define DEVNAME(i) envdevices[(i)].devname 57 #define DEVOFFSET(i) envdevices[(i)].devoff 58 #define ENVSIZE(i) envdevices[(i)].env_size 59 #define DEVESIZE(i) envdevices[(i)].erase_size 60 61 #define CFG_ENV_SIZE ENVSIZE(curdev) 62 63 #define ENV_SIZE getenvsize() 64 65 typedef struct environment_s { 66 ulong crc; /* CRC32 over data bytes */ 67 unsigned char flags; /* active or obsolete */ 68 char *data; 69 } env_t; 70 71 static env_t environment; 72 73 static int HaveRedundEnv = 0; 74 75 static unsigned char active_flag = 1; 76 static unsigned char obsolete_flag = 0; 77 78 79 #define XMK_STR(x) #x 80 #define MK_STR(x) XMK_STR(x) 81 82 static char default_environment[] = { 83 #if defined(CONFIG_BOOTARGS) 84 "bootargs=" CONFIG_BOOTARGS "\0" 85 #endif 86 #if defined(CONFIG_BOOTCOMMAND) 87 "bootcmd=" CONFIG_BOOTCOMMAND "\0" 88 #endif 89 #if defined(CONFIG_RAMBOOTCOMMAND) 90 "ramboot=" CONFIG_RAMBOOTCOMMAND "\0" 91 #endif 92 #if defined(CONFIG_NFSBOOTCOMMAND) 93 "nfsboot=" CONFIG_NFSBOOTCOMMAND "\0" 94 #endif 95 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) 96 "bootdelay=" MK_STR (CONFIG_BOOTDELAY) "\0" 97 #endif 98 #if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0) 99 "baudrate=" MK_STR (CONFIG_BAUDRATE) "\0" 100 #endif 101 #ifdef CONFIG_LOADS_ECHO 102 "loads_echo=" MK_STR (CONFIG_LOADS_ECHO) "\0" 103 #endif 104 #ifdef CONFIG_ETHADDR 105 "ethaddr=" MK_STR (CONFIG_ETHADDR) "\0" 106 #endif 107 #ifdef CONFIG_ETH1ADDR 108 "eth1addr=" MK_STR (CONFIG_ETH1ADDR) "\0" 109 #endif 110 #ifdef CONFIG_ETH2ADDR 111 "eth2addr=" MK_STR (CONFIG_ETH2ADDR) "\0" 112 #endif 113 #ifdef CONFIG_ETH3ADDR 114 "eth3addr=" MK_STR (CONFIG_ETH3ADDR) "\0" 115 #endif 116 #ifdef CONFIG_ETHPRIME 117 "ethprime=" CONFIG_ETHPRIME "\0" 118 #endif 119 #ifdef CONFIG_IPADDR 120 "ipaddr=" MK_STR (CONFIG_IPADDR) "\0" 121 #endif 122 #ifdef CONFIG_SERVERIP 123 "serverip=" MK_STR (CONFIG_SERVERIP) "\0" 124 #endif 125 #ifdef CFG_AUTOLOAD 126 "autoload=" CFG_AUTOLOAD "\0" 127 #endif 128 #ifdef CONFIG_ROOTPATH 129 "rootpath=" MK_STR (CONFIG_ROOTPATH) "\0" 130 #endif 131 #ifdef CONFIG_GATEWAYIP 132 "gatewayip=" MK_STR (CONFIG_GATEWAYIP) "\0" 133 #endif 134 #ifdef CONFIG_NETMASK 135 "netmask=" MK_STR (CONFIG_NETMASK) "\0" 136 #endif 137 #ifdef CONFIG_HOSTNAME 138 "hostname=" MK_STR (CONFIG_HOSTNAME) "\0" 139 #endif 140 #ifdef CONFIG_BOOTFILE 141 "bootfile=" MK_STR (CONFIG_BOOTFILE) "\0" 142 #endif 143 #ifdef CONFIG_LOADADDR 144 "loadaddr=" MK_STR (CONFIG_LOADADDR) "\0" 145 #endif 146 #ifdef CONFIG_PREBOOT 147 "preboot=" CONFIG_PREBOOT "\0" 148 #endif 149 #ifdef CONFIG_CLOCKS_IN_MHZ 150 "clocks_in_mhz=" "1" "\0" 151 #endif 152 #if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0) 153 "pcidelay=" MK_STR (CONFIG_PCI_BOOTDELAY) "\0" 154 #endif 155 #ifdef CONFIG_EXTRA_ENV_SETTINGS 156 CONFIG_EXTRA_ENV_SETTINGS 157 #endif 158 "\0" /* Termimate env_t data with 2 NULs */ 159 }; 160 161 static int flash_io (int mode); 162 static char *envmatch (char * s1, char * s2); 163 static int env_init (void); 164 static int parse_config (void); 165 166 #if defined(CONFIG_FILE) 167 static int get_config (char *); 168 #endif 169 static inline ulong getenvsize (void) 170 { 171 ulong rc = CFG_ENV_SIZE - sizeof (long); 172 173 if (HaveRedundEnv) 174 rc -= sizeof (char); 175 return rc; 176 } 177 178 /* 179 * Search the environment for a variable. 180 * Return the value, if found, or NULL, if not found. 181 */ 182 char *fw_getenv (char *name) 183 { 184 char *env, *nxt; 185 186 if (env_init ()) 187 return (NULL); 188 189 for (env = environment.data; *env; env = nxt + 1) { 190 char *val; 191 192 for (nxt = env; *nxt; ++nxt) { 193 if (nxt >= &environment.data[ENV_SIZE]) { 194 fprintf (stderr, "## Error: " 195 "environment not terminated\n"); 196 return (NULL); 197 } 198 } 199 val = envmatch (name, env); 200 if (!val) 201 continue; 202 return (val); 203 } 204 return (NULL); 205 } 206 207 /* 208 * Print the current definition of one, or more, or all 209 * environment variables 210 */ 211 void fw_printenv (int argc, char *argv[]) 212 { 213 char *env, *nxt; 214 int i, n_flag; 215 216 if (env_init ()) 217 return; 218 219 if (argc == 1) { /* Print all env variables */ 220 for (env = environment.data; *env; env = nxt + 1) { 221 for (nxt = env; *nxt; ++nxt) { 222 if (nxt >= &environment.data[ENV_SIZE]) { 223 fprintf (stderr, "## Error: " 224 "environment not terminated\n"); 225 return; 226 } 227 } 228 229 printf ("%s\n", env); 230 } 231 return; 232 } 233 234 if (strcmp (argv[1], "-n") == 0) { 235 n_flag = 1; 236 ++argv; 237 --argc; 238 if (argc != 2) { 239 fprintf (stderr, "## Error: " 240 "`-n' option requires exactly one argument\n"); 241 return; 242 } 243 } else { 244 n_flag = 0; 245 } 246 247 for (i = 1; i < argc; ++i) { /* print single env variables */ 248 char *name = argv[i]; 249 char *val = NULL; 250 251 for (env = environment.data; *env; env = nxt + 1) { 252 253 for (nxt = env; *nxt; ++nxt) { 254 if (nxt >= &environment.data[ENV_SIZE]) { 255 fprintf (stderr, "## Error: " 256 "environment not terminated\n"); 257 return; 258 } 259 } 260 val = envmatch (name, env); 261 if (val) { 262 if (!n_flag) { 263 fputs (name, stdout); 264 putc ('=', stdout); 265 } 266 puts (val); 267 break; 268 } 269 } 270 if (!val) 271 fprintf (stderr, "## Error: \"%s\" not defined\n", name); 272 } 273 } 274 275 /* 276 * Deletes or sets environment variables. Returns errno style error codes: 277 * 0 - OK 278 * EINVAL - need at least 1 argument 279 * EROFS - certain variables ("ethaddr", "serial#") cannot be 280 * modified or deleted 281 * 282 */ 283 int fw_setenv (int argc, char *argv[]) 284 { 285 int i, len; 286 char *env, *nxt; 287 char *oldval = NULL; 288 char *name; 289 290 if (argc < 2) { 291 return (EINVAL); 292 } 293 294 if (env_init ()) 295 return (errno); 296 297 name = argv[1]; 298 299 /* 300 * search if variable with this name already exists 301 */ 302 for (nxt = env = environment.data; *env; env = nxt + 1) { 303 for (nxt = env; *nxt; ++nxt) { 304 if (nxt >= &environment.data[ENV_SIZE]) { 305 fprintf (stderr, "## Error: " 306 "environment not terminated\n"); 307 return (EINVAL); 308 } 309 } 310 if ((oldval = envmatch (name, env)) != NULL) 311 break; 312 } 313 314 /* 315 * Delete any existing definition 316 */ 317 if (oldval) { 318 /* 319 * Ethernet Address and serial# can be set only once 320 */ 321 if ((strcmp (name, "ethaddr") == 0) || 322 (strcmp (name, "serial#") == 0)) { 323 fprintf (stderr, "Can't overwrite \"%s\"\n", name); 324 return (EROFS); 325 } 326 327 if (*++nxt == '\0') { 328 *env = '\0'; 329 } else { 330 for (;;) { 331 *env = *nxt++; 332 if ((*env == '\0') && (*nxt == '\0')) 333 break; 334 ++env; 335 } 336 } 337 *++env = '\0'; 338 } 339 340 /* Delete only ? */ 341 if (argc < 3) 342 goto WRITE_FLASH; 343 344 /* 345 * Append new definition at the end 346 */ 347 for (env = environment.data; *env || *(env + 1); ++env); 348 if (env > environment.data) 349 ++env; 350 /* 351 * Overflow when: 352 * "name" + "=" + "val" +"\0\0" > CFG_ENV_SIZE - (env-environment) 353 */ 354 len = strlen (name) + 2; 355 /* add '=' for first arg, ' ' for all others */ 356 for (i = 2; i < argc; ++i) { 357 len += strlen (argv[i]) + 1; 358 } 359 if (len > (&environment.data[ENV_SIZE] - env)) { 360 fprintf (stderr, 361 "Error: environment overflow, \"%s\" deleted\n", 362 name); 363 return (-1); 364 } 365 while ((*env = *name++) != '\0') 366 env++; 367 for (i = 2; i < argc; ++i) { 368 char *val = argv[i]; 369 370 *env = (i == 2) ? '=' : ' '; 371 while ((*++env = *val++) != '\0'); 372 } 373 374 /* end is marked with double '\0' */ 375 *++env = '\0'; 376 377 WRITE_FLASH: 378 379 /* Update CRC */ 380 environment.crc = crc32 (0, (uint8_t*) environment.data, ENV_SIZE); 381 382 /* write environment back to flash */ 383 if (flash_io (O_RDWR)) { 384 fprintf (stderr, "Error: can't write fw_env to flash\n"); 385 return (-1); 386 } 387 388 return (0); 389 } 390 391 static int flash_io (int mode) 392 { 393 int fd, fdr, rc, otherdev, len, resid; 394 erase_info_t erase; 395 char *data = NULL; 396 397 if ((fd = open (DEVNAME (curdev), mode)) < 0) { 398 fprintf (stderr, 399 "Can't open %s: %s\n", 400 DEVNAME (curdev), strerror (errno)); 401 return (-1); 402 } 403 404 len = sizeof (environment.crc); 405 if (HaveRedundEnv) { 406 len += sizeof (environment.flags); 407 } 408 409 if (mode == O_RDWR) { 410 if (HaveRedundEnv) { 411 /* switch to next partition for writing */ 412 otherdev = !curdev; 413 if ((fdr = open (DEVNAME (otherdev), mode)) < 0) { 414 fprintf (stderr, 415 "Can't open %s: %s\n", 416 DEVNAME (otherdev), 417 strerror (errno)); 418 return (-1); 419 } 420 } else { 421 otherdev = curdev; 422 fdr = fd; 423 } 424 printf ("Unlocking flash...\n"); 425 erase.length = DEVESIZE (otherdev); 426 erase.start = DEVOFFSET (otherdev); 427 ioctl (fdr, MEMUNLOCK, &erase); 428 429 if (HaveRedundEnv) { 430 erase.length = DEVESIZE (curdev); 431 erase.start = DEVOFFSET (curdev); 432 ioctl (fd, MEMUNLOCK, &erase); 433 environment.flags = active_flag; 434 } 435 436 printf ("Done\n"); 437 resid = DEVESIZE (otherdev) - CFG_ENV_SIZE; 438 if (resid) { 439 if ((data = malloc (resid)) == NULL) { 440 fprintf (stderr, 441 "Cannot malloc %d bytes: %s\n", 442 resid, 443 strerror (errno)); 444 return (-1); 445 } 446 if (lseek (fdr, DEVOFFSET (otherdev) + CFG_ENV_SIZE, SEEK_SET) 447 == -1) { 448 fprintf (stderr, "seek error on %s: %s\n", 449 DEVNAME (otherdev), 450 strerror (errno)); 451 return (-1); 452 } 453 if ((rc = read (fdr, data, resid)) != resid) { 454 fprintf (stderr, 455 "read error on %s: %s\n", 456 DEVNAME (otherdev), 457 strerror (errno)); 458 return (-1); 459 } 460 } 461 462 printf ("Erasing old environment...\n"); 463 464 erase.length = DEVESIZE (otherdev); 465 erase.start = DEVOFFSET (otherdev); 466 if (ioctl (fdr, MEMERASE, &erase) != 0) { 467 fprintf (stderr, "MTD erase error on %s: %s\n", 468 DEVNAME (otherdev), 469 strerror (errno)); 470 return (-1); 471 } 472 473 printf ("Done\n"); 474 475 printf ("Writing environment to %s...\n", DEVNAME (otherdev)); 476 if (lseek (fdr, DEVOFFSET (otherdev), SEEK_SET) == -1) { 477 fprintf (stderr, 478 "seek error on %s: %s\n", 479 DEVNAME (otherdev), strerror (errno)); 480 return (-1); 481 } 482 if (write (fdr, &environment, len) != len) { 483 fprintf (stderr, 484 "CRC write error on %s: %s\n", 485 DEVNAME (otherdev), strerror (errno)); 486 return (-1); 487 } 488 if (write (fdr, environment.data, ENV_SIZE) != ENV_SIZE) { 489 fprintf (stderr, 490 "Write error on %s: %s\n", 491 DEVNAME (otherdev), strerror (errno)); 492 return (-1); 493 } 494 if (resid) { 495 if (write (fdr, data, resid) != resid) { 496 fprintf (stderr, 497 "write error on %s: %s\n", 498 DEVNAME (curdev), strerror (errno)); 499 return (-1); 500 } 501 free (data); 502 } 503 if (HaveRedundEnv) { 504 /* change flag on current active env partition */ 505 if (lseek (fd, DEVOFFSET (curdev) + sizeof (ulong), SEEK_SET) 506 == -1) { 507 fprintf (stderr, "seek error on %s: %s\n", 508 DEVNAME (curdev), strerror (errno)); 509 return (-1); 510 } 511 if (write (fd, &obsolete_flag, sizeof (obsolete_flag)) != 512 sizeof (obsolete_flag)) { 513 fprintf (stderr, 514 "Write error on %s: %s\n", 515 DEVNAME (curdev), strerror (errno)); 516 return (-1); 517 } 518 } 519 printf ("Done\n"); 520 printf ("Locking ...\n"); 521 erase.length = DEVESIZE (otherdev); 522 erase.start = DEVOFFSET (otherdev); 523 ioctl (fdr, MEMLOCK, &erase); 524 if (HaveRedundEnv) { 525 erase.length = DEVESIZE (curdev); 526 erase.start = DEVOFFSET (curdev); 527 ioctl (fd, MEMLOCK, &erase); 528 if (close (fdr)) { 529 fprintf (stderr, 530 "I/O error on %s: %s\n", 531 DEVNAME (otherdev), 532 strerror (errno)); 533 return (-1); 534 } 535 } 536 printf ("Done\n"); 537 } else { 538 539 if (lseek (fd, DEVOFFSET (curdev), SEEK_SET) == -1) { 540 fprintf (stderr, 541 "seek error on %s: %s\n", 542 DEVNAME (curdev), strerror (errno)); 543 return (-1); 544 } 545 if (read (fd, &environment, len) != len) { 546 fprintf (stderr, 547 "CRC read error on %s: %s\n", 548 DEVNAME (curdev), strerror (errno)); 549 return (-1); 550 } 551 if ((rc = read (fd, environment.data, ENV_SIZE)) != ENV_SIZE) { 552 fprintf (stderr, 553 "Read error on %s: %s\n", 554 DEVNAME (curdev), strerror (errno)); 555 return (-1); 556 } 557 } 558 559 if (close (fd)) { 560 fprintf (stderr, 561 "I/O error on %s: %s\n", 562 DEVNAME (curdev), strerror (errno)); 563 return (-1); 564 } 565 566 /* everything ok */ 567 return (0); 568 } 569 570 /* 571 * s1 is either a simple 'name', or a 'name=value' pair. 572 * s2 is a 'name=value' pair. 573 * If the names match, return the value of s2, else NULL. 574 */ 575 576 static char *envmatch (char * s1, char * s2) 577 { 578 579 while (*s1 == *s2++) 580 if (*s1++ == '=') 581 return (s2); 582 if (*s1 == '\0' && *(s2 - 1) == '=') 583 return (s2); 584 return (NULL); 585 } 586 587 /* 588 * Prevent confusion if running from erased flash memory 589 */ 590 static int env_init (void) 591 { 592 int crc1, crc1_ok; 593 char *addr1; 594 595 int crc2, crc2_ok; 596 char flag1, flag2, *addr2; 597 598 if (parse_config ()) /* should fill envdevices */ 599 return 1; 600 601 if ((addr1 = calloc (1, ENV_SIZE)) == NULL) { 602 fprintf (stderr, 603 "Not enough memory for environment (%ld bytes)\n", 604 ENV_SIZE); 605 return (errno); 606 } 607 608 /* read environment from FLASH to local buffer */ 609 environment.data = addr1; 610 curdev = 0; 611 if (flash_io (O_RDONLY)) { 612 return (errno); 613 } 614 615 crc1_ok = ((crc1 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE)) 616 == environment.crc); 617 if (!HaveRedundEnv) { 618 if (!crc1_ok) { 619 fprintf (stderr, 620 "Warning: Bad CRC, using default environment\n"); 621 memcpy(environment.data, default_environment, sizeof default_environment); 622 } 623 } else { 624 flag1 = environment.flags; 625 626 curdev = 1; 627 if ((addr2 = calloc (1, ENV_SIZE)) == NULL) { 628 fprintf (stderr, 629 "Not enough memory for environment (%ld bytes)\n", 630 ENV_SIZE); 631 return (errno); 632 } 633 environment.data = addr2; 634 635 if (flash_io (O_RDONLY)) { 636 return (errno); 637 } 638 639 crc2_ok = ((crc2 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE)) 640 == environment.crc); 641 flag2 = environment.flags; 642 643 if (crc1_ok && !crc2_ok) { 644 environment.data = addr1; 645 environment.flags = flag1; 646 environment.crc = crc1; 647 curdev = 0; 648 free (addr2); 649 } else if (!crc1_ok && crc2_ok) { 650 environment.data = addr2; 651 environment.flags = flag2; 652 environment.crc = crc2; 653 curdev = 1; 654 free (addr1); 655 } else if (!crc1_ok && !crc2_ok) { 656 fprintf (stderr, 657 "Warning: Bad CRC, using default environment\n"); 658 memcpy(environment.data, default_environment, sizeof default_environment); 659 curdev = 0; 660 free (addr1); 661 } else if (flag1 == active_flag && flag2 == obsolete_flag) { 662 environment.data = addr1; 663 environment.flags = flag1; 664 environment.crc = crc1; 665 curdev = 0; 666 free (addr2); 667 } else if (flag1 == obsolete_flag && flag2 == active_flag) { 668 environment.data = addr2; 669 environment.flags = flag2; 670 environment.crc = crc2; 671 curdev = 1; 672 free (addr1); 673 } else if (flag1 == flag2) { 674 environment.data = addr1; 675 environment.flags = flag1; 676 environment.crc = crc1; 677 curdev = 0; 678 free (addr2); 679 } else if (flag1 == 0xFF) { 680 environment.data = addr1; 681 environment.flags = flag1; 682 environment.crc = crc1; 683 curdev = 0; 684 free (addr2); 685 } else if (flag2 == 0xFF) { 686 environment.data = addr2; 687 environment.flags = flag2; 688 environment.crc = crc2; 689 curdev = 1; 690 free (addr1); 691 } 692 } 693 return (0); 694 } 695 696 697 static int parse_config () 698 { 699 struct stat st; 700 701 #if defined(CONFIG_FILE) 702 /* Fills in DEVNAME(), ENVSIZE(), DEVESIZE(). Or don't. */ 703 if (get_config (CONFIG_FILE)) { 704 fprintf (stderr, 705 "Cannot parse config file: %s\n", strerror (errno)); 706 return 1; 707 } 708 #else 709 strcpy (DEVNAME (0), DEVICE1_NAME); 710 DEVOFFSET (0) = DEVICE1_OFFSET; 711 ENVSIZE (0) = ENV1_SIZE; 712 DEVESIZE (0) = DEVICE1_ESIZE; 713 #ifdef HAVE_REDUND 714 strcpy (DEVNAME (1), DEVICE2_NAME); 715 DEVOFFSET (1) = DEVICE2_OFFSET; 716 ENVSIZE (1) = ENV2_SIZE; 717 DEVESIZE (1) = DEVICE2_ESIZE; 718 HaveRedundEnv = 1; 719 #endif 720 #endif 721 if (stat (DEVNAME (0), &st)) { 722 fprintf (stderr, 723 "Cannot access MTD device %s: %s\n", 724 DEVNAME (0), strerror (errno)); 725 return 1; 726 } 727 728 if (HaveRedundEnv && stat (DEVNAME (1), &st)) { 729 fprintf (stderr, 730 "Cannot access MTD device %s: %s\n", 731 DEVNAME (1), strerror (errno)); 732 return 1; 733 } 734 return 0; 735 } 736 737 #if defined(CONFIG_FILE) 738 static int get_config (char *fname) 739 { 740 FILE *fp; 741 int i = 0; 742 int rc; 743 char dump[128]; 744 745 if ((fp = fopen (fname, "r")) == NULL) { 746 return 1; 747 } 748 749 while ((i < 2) && ((rc = fscanf (fp, "%s %lx %lx %lx", 750 DEVNAME (i), 751 &DEVOFFSET (i), 752 &ENVSIZE (i), 753 &DEVESIZE (i) )) != EOF)) { 754 755 /* Skip incomplete conversions and comment strings */ 756 if ((rc < 3) || (*DEVNAME (i) == '#')) { 757 fgets (dump, sizeof (dump), fp); /* Consume till end */ 758 continue; 759 } 760 761 i++; 762 } 763 fclose (fp); 764 765 HaveRedundEnv = i - 1; 766 if (!i) { /* No valid entries found */ 767 errno = EINVAL; 768 return 1; 769 } else 770 return 0; 771 } 772 #endif 773