1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 #pragma ident "%Z%%M% %I% %E% SMI" 23 /* 24 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 /*LINTLIBRARY*/ 29 30 #include <stdio.h> 31 #include <errno.h> 32 #include <string.h> 33 #include <netdb.h> 34 #include <unistd.h> 35 #include <stdlib.h> 36 #include <arpa/inet.h> 37 #include <rpcsvc/ypclnt.h> 38 39 char glob_stdout[BUFSIZ]; 40 char glob_stderr[BUFSIZ]; 41 42 void f_cleanup(FILE *fp, char *tmpf); 43 void fd_cleanup(int fd1, int fd2); 44 static void _freeList(char ***list); 45 46 extern void **list_append(void **list, void *item); 47 48 #ifdef MAIN 49 50 #define THOSTNAME "cherwell" 51 #define TPRINTERNAME "c" 52 53 int 54 main() { 55 char *host = THOSTNAME; 56 const char *user = "cn=Directory Manager"; 57 const char *passwd = "directorymanager"; 58 int result = 0; 59 60 result = _updateldap("add", host, user, passwd, 61 "_pmTestAuthToken", NULL, NULL, "new comment", "false"); 62 if (result != 0) { 63 printf("Add 1 failed, err code = %d\n"); 64 } 65 66 result = _updateldap("delete", host, user, passwd, 67 "_pmTestAuthToken", NULL, NULL, NULL, "false"); 68 if (result != 0) { 69 printf("Delete 1 failed, err code = %d\n"); 70 } 71 72 result = _updateldap("delete", host, user, passwd, TPRINTERNAME, 73 THOSTNAME, "", "new comment", "true"); 74 if (result != 0) { 75 printf("delete failed, err code = %d\n"); 76 } 77 78 result = _updateldap("delete", host, user, passwd, "_default", 79 THOSTNAME, "", "new comment", "true"); 80 if (result != 0) { 81 printf("delete failed, err code = %d\n"); 82 } 83 84 result = _updateldap("add", host, user, passwd, TPRINTERNAME, 85 THOSTNAME, "Solaris", "new comment", "true"); 86 if (result != 0) { 87 printf("add failed, err code = %d\n"); 88 } 89 90 result = _updateldap("modify", host, user, passwd, TPRINTERNAME, 91 THOSTNAME, "", "modified comment", "true"); 92 if (result != 0) { 93 printf("modify failed, err code = %d\n"); 94 } 95 96 result = _updateldap("modify", host, user, passwd, TPRINTERNAME, 97 THOSTNAME, "", NULL, "false"); 98 if (result != 0) { 99 printf("modify failed, err code = %d\n"); 100 } 101 102 103 exit(0); 104 } 105 #endif 106 107 108 int 109 _dorexec( 110 const char *host, 111 const char *user, 112 const char *passwd, 113 const char *cmd, 114 const char *locale) { 115 116 int ret = 0; 117 int fd = 0; 118 int fd2 = 1; 119 120 FILE *fderr; 121 char *ferr; 122 123 (void) memset(glob_stdout, 0, BUFSIZ); 124 (void) memset(glob_stderr, 0, BUFSIZ); 125 126 /* 127 * Re-direct stderr to a file 128 */ 129 ferr = tempnam(NULL, NULL); 130 if (ferr != NULL) { 131 fderr = freopen(ferr, "w+", stderr); 132 } 133 134 fd = rexec((char **)&host, htons(512), user, 135 passwd, cmd, &fd2); 136 137 if (fd > -1) { 138 /* 139 * rexec worked. Clean up stderr file. 140 */ 141 f_cleanup(fderr, ferr); 142 143 ret = read(fd, glob_stdout, BUFSIZ - 1); 144 if (ret < 0) { 145 (void) strncpy(glob_stderr, strerror(errno), 146 (BUFSIZ - 1)); 147 fd_cleanup(fd, fd2); 148 return (errno); 149 } 150 151 ret = read(fd2, glob_stderr, BUFSIZ - 1); 152 if (ret < 0) { 153 (void) strncpy(glob_stderr, strerror(errno), 154 (BUFSIZ - 1)); 155 fd_cleanup(fd, fd2); 156 return (errno); 157 } 158 } else { 159 /* 160 * rexec failed. Read from the stderr file. 161 */ 162 if (fderr != NULL) { 163 char tmpbuf[BUFSIZ]; 164 165 (void) rewind(fderr); 166 strcpy(glob_stderr, ""); 167 while (fgets(tmpbuf, BUFSIZ - 1, 168 fderr) != NULL) { 169 if ((strlen(glob_stderr) + 170 strlen(tmpbuf)) > BUFSIZ - 1) { 171 break; 172 } else { 173 (void) strcat(glob_stderr, tmpbuf); 174 } 175 } 176 } 177 f_cleanup(fderr, ferr); 178 fd_cleanup(fd, fd2); 179 return (1); 180 } 181 fd_cleanup(fd, fd2); 182 return (0); 183 } 184 185 void 186 fd_cleanup(int fd, int fd2) 187 { 188 if (fd > 0) { 189 (void) close(fd); 190 } 191 if (fd2 > 0) { 192 (void) close(fd2); 193 } 194 } 195 196 void 197 f_cleanup(FILE *fp, char *tmpf) 198 { 199 if (fp != NULL) { 200 (void) fclose(fp); 201 } 202 if (tmpf != NULL) { 203 (void) unlink(tmpf); 204 (void) free(tmpf); 205 } 206 } 207 208 struct ns_bsd_addr { 209 char *server; /* server name */ 210 char *printer; /* printer name or NULL */ 211 char *extension; /* RFC-1179 conformance */ 212 char *pname; /* Local printer name */ 213 }; 214 typedef struct ns_bsd_addr ns_bsd_addr_t; 215 216 /* Key/Value pair structure */ 217 struct ns_kvp { 218 char *key; /* key */ 219 void *value; /* value converted */ 220 }; 221 typedef struct ns_kvp ns_kvp_t; 222 223 /* 224 * Information needed to update a name service. 225 * Currently only used for ldap. (see lib/print/ns.h) 226 */ 227 228 /* LDAP bind password security type */ 229 230 typedef enum NS_PASSWD_TYPE { 231 NS_PW_INSECURE = 0, 232 NS_PW_SECURE = 1 233 } NS_PASSWD_TYPE; 234 235 236 struct ns_cred { 237 char *binddn; 238 char *passwd; 239 char *host; 240 int port; /* LDAP port, 0 = default */ 241 NS_PASSWD_TYPE passwdType; /* password security type */ 242 uchar_t *domainDN; /* NS domain DN */ 243 }; 244 typedef struct ns_cred ns_cred_t; 245 246 /* LDAP specific NS Data */ 247 248 typedef struct NS_LDAPDATA { 249 char **attrList; /* list of user defined Key Value Pairs */ 250 } NS_LDAPDATA; 251 252 /* Printer Object structure */ 253 struct ns_printer { 254 char *name; /* primary name of printer */ 255 char **aliases; /* aliases for printer */ 256 char *source; /* name service derived from */ 257 ns_kvp_t **attributes; /* key/value pairs. */ 258 ns_cred_t *cred; /* info to update name service */ 259 void *nsdata; /* name service specific data */ 260 }; 261 typedef struct ns_printer ns_printer_t; 262 263 extern ns_printer_t *ns_printer_get_name(const char *, const char *); 264 extern int ns_printer_put(const ns_printer_t *); 265 extern char *ns_get_value_string(const char *, const ns_printer_t *); 266 extern int ns_set_value(const char *, const void *, ns_printer_t *); 267 extern int ns_set_value_from_string(const char *, const char *, 268 ns_printer_t *); 269 extern ns_bsd_addr_t *bsd_addr_create(const char *, const char *, 270 const char *); 271 extern char *bsd_addr_to_string(const ns_bsd_addr_t *); 272 extern void ns_printer_destroy(ns_printer_t *); 273 274 int 275 _updateoldyp( 276 const char *action, 277 const char *printername, 278 const char *printserver, 279 const char *extensions, 280 const char *comment, 281 const char *isdefault) { 282 283 ns_printer_t *printer; 284 ns_bsd_addr_t *addr; 285 int status = 0; 286 287 char mkcmd[BUFSIZ]; 288 char *domain = NULL; 289 char *host = NULL; 290 291 /* 292 * libprint returns before we know that the printers.conf 293 * map is made. So we'll make it again. 294 */ 295 (void) yp_get_default_domain(&domain); 296 297 if ((yp_master(domain, "printers.conf.byname", &host) != 0) && 298 (yp_master(domain, "passwd.byname", &host) != 0)) { 299 strcpy(mkcmd, "/usr/bin/sleep 1"); 300 } else { 301 sprintf(mkcmd, "/usr/bin/rsh -n %s 'cd /var/yp; " 302 "/usr/ccs/bin/make -f /var/yp/Makefile " 303 "-f /var/yp/Makefile.print printers.conf " 304 "> /dev/null'", host); 305 } 306 307 if (strcmp(action, "delete") == 0) { 308 if ((printer = (ns_printer_t *) 309 ns_printer_get_name(printername, "nis")) == NULL) { 310 return (0); 311 } 312 313 printer->attributes = NULL; 314 status = ns_printer_put(printer); 315 if (status != 0) { 316 (void) free(printer); 317 return (status); 318 } 319 320 if ((printer = (ns_printer_t *) 321 ns_printer_get_name("_default", "nis")) != NULL) { 322 char *dflt = (char *) 323 ns_get_value_string("use", printer); 324 if ((dflt != NULL) && 325 (strcmp(dflt, printername) == 0)) { 326 printer->attributes = NULL; 327 status = ns_printer_put(printer); 328 if (status != 0) { 329 (void) free(printer); 330 return (status); 331 } 332 } 333 } 334 (void) free(printer); 335 (void) system(mkcmd); 336 return (0); 337 338 } else if (strcmp(action, "add") == 0) { 339 printer = (ns_printer_t *)malloc(sizeof (*printer)); 340 memset(printer, 0, sizeof (*printer)); 341 printer->name = (char *)printername; 342 printer->source = "nis"; 343 344 addr = (ns_bsd_addr_t *)malloc(sizeof (*addr)); 345 memset(addr, 0, sizeof (*addr)); 346 addr->printer = (char *)printername; 347 addr->server = (char *)printserver; 348 if ((extensions != NULL) && 349 (strlen(extensions) > 0)) { 350 addr->extension = (char *)extensions; 351 } 352 ns_set_value("bsdaddr", addr, printer); 353 354 if ((comment != NULL) && (strlen(comment) > 0)) { 355 ns_set_value_from_string("description", 356 comment, printer); 357 } 358 status = ns_printer_put(printer); 359 if (status != 0) { 360 (void) free(addr); 361 (void) free(printer); 362 return (status); 363 } 364 365 if (strcmp(isdefault, "true") == 0) { 366 printer->name = "_default"; 367 printer->attributes = NULL; 368 ns_set_value_from_string("use", printername, printer); 369 status = ns_printer_put(printer); 370 if (status != 0) { 371 (void) free(addr); 372 (void) free(printer); 373 return (status); 374 } 375 } 376 (void) free(addr); 377 (void) free(printer); 378 (void) system(mkcmd); 379 return (0); 380 } 381 382 /* 383 * Modify 384 */ 385 if ((printer = (ns_printer_t *) 386 ns_printer_get_name(printername, "nis")) == NULL) { 387 return (1); 388 } 389 if ((comment != NULL) && (strlen(comment) > 0)) { 390 ns_set_value_from_string("description", comment, printer); 391 } else { 392 ns_set_value_from_string("description", 393 NULL, printer); 394 } 395 status = ns_printer_put(printer); 396 if (status != 0) { 397 (void) free(printer); 398 return (status); 399 } 400 401 if ((printer = (ns_printer_t *) 402 ns_printer_get_name("_default", "nis")) != NULL) { 403 char *dflt = (char *)ns_get_value_string("use", printer); 404 if (strcmp(printername, dflt) == 0) { 405 if (strcmp(isdefault, "false") == 0) { 406 /* 407 * We were the default printer but not 408 * any more. 409 */ 410 printer->attributes = NULL; 411 status = ns_printer_put(printer); 412 if (status != 0) { 413 (void) free(printer); 414 return (status); 415 } 416 } 417 } else { 418 if (strcmp(isdefault, "true") == 0) { 419 ns_set_value_from_string("use", 420 printername, printer); 421 status = ns_printer_put(printer); 422 if (status != 0) { 423 (void) free(printer); 424 return (status); 425 } 426 } 427 } 428 } else { 429 printer = (ns_printer_t *)malloc(sizeof (*printer)); 430 memset(printer, 0, sizeof (*printer)); 431 printer->name = "_default"; 432 printer->source = "nis"; 433 ns_set_value_from_string("use", printername, printer); 434 status = ns_printer_put(printer); 435 if (status != 0) { 436 (void) free(printer); 437 return (status); 438 } 439 } 440 (void) system(mkcmd); 441 return (0); 442 } 443 444 int 445 _updateldap( 446 const char *action, 447 const char *host, 448 const char *binddn, 449 const char *passwd, 450 const char *printername, 451 const char *printserver, 452 const char *extensions, 453 const char *comment, 454 const char *isdefault) 455 456 { 457 ns_printer_t *printer; 458 ns_bsd_addr_t *addr; 459 ns_cred_t *cred; 460 461 char *item = NULL; 462 char **attrList = NULL; 463 464 int status; 465 466 if (printserver == NULL) { 467 /* printserver not given so use host */ 468 printserver = host; 469 } 470 471 cred = (ns_cred_t *)malloc(sizeof (*cred)); 472 (void) memset(cred, '\0', sizeof (*cred)); 473 cred->passwd = strdup((char *)passwd); 474 cred->binddn = strdup((char *)binddn); 475 cred->host = strdup((char *)host); 476 477 cred->passwdType = NS_PW_INSECURE; /* use default */ 478 cred->port = 0; /* use default */ 479 cred->domainDN = NULL; /* use default */ 480 481 if (strcmp(action, "delete") == 0) { 482 /* 483 * Delete printer object from LDAP directory DIT 484 */ 485 486 if ((printer = (ns_printer_t *) 487 ns_printer_get_name(printername, "ldap")) == NULL) { 488 return (0); 489 } 490 491 printer->attributes = NULL; 492 printer->nsdata = malloc(sizeof (NS_LDAPDATA)); 493 if (printer->nsdata == NULL) { 494 return (1); 495 } 496 ((NS_LDAPDATA *)(printer->nsdata))->attrList = NULL; 497 printer->cred = cred; 498 printer->source = strdup("ldap"); 499 status = ns_printer_put(printer); 500 free(printer->nsdata); 501 (void) ns_printer_destroy(printer); 502 503 if (status != 0) { 504 return (status); 505 } 506 507 if ((printer = (ns_printer_t *) 508 ns_printer_get_name("_default", "ldap")) != NULL) { 509 char *dflt = (char *) 510 ns_get_value_string("use", printer); 511 if ((dflt != NULL) && 512 (strcmp(dflt, printername) == 0)) { 513 printer->attributes = NULL; 514 printer->nsdata = malloc(sizeof (NS_LDAPDATA)); 515 if (printer->nsdata == NULL) { 516 (void) ns_printer_destroy(printer); 517 return (1); 518 } 519 ((NS_LDAPDATA *)(printer->nsdata))->attrList = NULL; 520 printer->cred = cred; 521 printer->source = strdup("ldap"); 522 status = ns_printer_put(printer); 523 free(printer->nsdata); 524 if (status != 0) { 525 (void) ns_printer_destroy(printer); 526 return (status); 527 } 528 } 529 530 (void) ns_printer_destroy(printer); 531 } 532 return (0); 533 534 } else if (strcmp(action, "add") == 0) { 535 /* 536 * Add new printer object into LDAP directory DIT 537 */ 538 539 printer = (ns_printer_t *)malloc(sizeof (*printer)); 540 if (printer == NULL) { 541 return (1); 542 } 543 (void) memset(printer, 0, sizeof (*printer)); 544 printer->name = strdup((char *)printername); 545 printer->source = strdup("ldap"); 546 547 printer->cred = cred; 548 549 /* set BSD address in attribute list */ 550 551 if (extensions == NULL) { 552 item = (char *)malloc(strlen("bsdaddr") + 553 strlen(printserver) + 554 strlen(printername) + 555 strlen("Solaris") + 6); 556 } else { 557 item = (char *)malloc(strlen("bsdaddr") + 558 strlen(printserver) + 559 strlen(printername) + 560 strlen(extensions) + 6); 561 } 562 if (item == NULL) { 563 (void) ns_printer_destroy(printer); 564 return (1); 565 } 566 567 if (extensions == NULL) { 568 sprintf(item, "%s=%s,%s,%s", "bsdaddr", 569 printserver, printername, "Solaris"); 570 } else { 571 sprintf(item, "%s=%s,%s,%s", "bsdaddr", 572 printserver, printername, extensions); 573 } 574 575 attrList = (char **)list_append((void**)attrList, 576 (void *)item); 577 if ((comment != NULL) && (strlen(comment) > 0)) { 578 item = (char *)malloc(strlen("description") + 579 strlen(comment) + 4); 580 if (item == NULL) { 581 (void) ns_printer_destroy(printer); 582 return (1); 583 } 584 sprintf(item, "%s=%s", "description", comment); 585 attrList = (char **)list_append((void**)attrList, 586 (void *)item); 587 } 588 589 printer->attributes = NULL; 590 printer->nsdata = malloc(sizeof (NS_LDAPDATA) + 2); 591 if (printer->nsdata == NULL) { 592 (void) ns_printer_destroy(printer); 593 return (1); 594 } 595 ((NS_LDAPDATA *)(printer->nsdata))->attrList = attrList; 596 597 status = ns_printer_put(printer); 598 _freeList(&attrList); 599 if (status != 0) { 600 free(printer->nsdata); 601 (void) ns_printer_destroy(printer); 602 return (status); 603 } 604 605 if (strcmp(isdefault, "true") == 0) { 606 (void) free(printer->name); 607 608 printer->name = strdup("_default"); 609 printer->attributes = NULL; 610 611 attrList = NULL; 612 item = (char *)malloc(strlen("use") + 613 strlen(printername) + 4); 614 if (item == NULL) { 615 (void) ns_printer_destroy(printer); 616 return (1); 617 } 618 sprintf(item, "%s=%s", "use", printername); 619 attrList = (char **)list_append((void**)attrList, 620 (void *)item); 621 622 ((NS_LDAPDATA *)(printer->nsdata))->attrList = attrList; 623 624 status = ns_printer_put(printer); 625 _freeList(&attrList); 626 free(printer->nsdata); 627 if (status != 0) { 628 (void) ns_printer_destroy(printer); 629 return (status); 630 } 631 } 632 (void) ns_printer_destroy(printer); 633 return (0); 634 } 635 636 /* 637 * Modify printer object in the LDAP directory DIT 638 */ 639 640 if ((printer = (ns_printer_t *) 641 ns_printer_get_name(printername, "ldap")) == NULL) { 642 return (1); 643 } 644 printer->cred = cred; 645 printer->source = strdup("ldap"); 646 647 if ((comment != NULL) && (strlen(comment) > 0)) { 648 item = (char *)malloc(strlen("description") + 649 strlen(comment) + 4); 650 if (item == NULL) { 651 (void) ns_printer_destroy(printer); 652 return (1); 653 } 654 sprintf(item, "%s=%s", "description", comment); 655 attrList = (char **)list_append((void**)attrList, (void *)item); 656 } else { 657 item = (char *)malloc(strlen("description") + 4); 658 if (item == NULL) { 659 (void) ns_printer_destroy(printer); 660 return (1); 661 } 662 sprintf(item, "%s=", "description"); 663 attrList = (char **)list_append((void**)attrList, (void *)item); 664 } 665 666 printer->attributes = NULL; 667 printer->nsdata = malloc(sizeof (NS_LDAPDATA)); 668 if (printer->nsdata == NULL) { 669 (void) ns_printer_destroy(printer); 670 return (1); 671 } 672 ((NS_LDAPDATA *)(printer->nsdata))->attrList = attrList; 673 674 status = ns_printer_put(printer); 675 _freeList(&attrList); 676 free(printer->nsdata); 677 if (status != 0) { 678 (void) ns_printer_destroy(printer); 679 return (status); 680 } 681 682 /* 683 * Handle the default printer. 684 */ 685 if ((printer = (ns_printer_t *) 686 ns_printer_get_name("_default", "ldap")) != NULL) { 687 char *dflt = (char *)ns_get_value_string("use", printer); 688 689 printer->source = strdup("ldap"); 690 printer->cred = cred; 691 if (strcmp(printername, dflt) == 0) { 692 if (strcmp(isdefault, "false") == 0) { 693 /* 694 * We were the default printer but not 695 * any more. So delete the default entry 696 */ 697 printer->attributes = NULL; 698 printer->nsdata = malloc(sizeof (NS_LDAPDATA)); 699 if (printer->nsdata == NULL) { 700 (void) ns_printer_destroy(printer); 701 return (1); 702 } 703 ((NS_LDAPDATA *)(printer->nsdata))->attrList = NULL; 704 status = ns_printer_put(printer); 705 free(printer->nsdata); 706 if (status != 0) { 707 (void) ns_printer_destroy(printer); 708 return (status); 709 } 710 } 711 } else if (strcmp(isdefault, "true") == 0) { 712 /* 713 * Modify this default entry to use us. 714 */ 715 printer->attributes = NULL; 716 printer->nsdata = malloc(sizeof (NS_LDAPDATA)); 717 if (printer->nsdata == NULL) { 718 (void) ns_printer_destroy(printer); 719 return (1); 720 } 721 attrList = NULL; 722 item = (char *)malloc(strlen("use") + 723 strlen(printername) + 4); 724 if (item == NULL) { 725 (void) ns_printer_destroy(printer); 726 return (1); 727 } 728 sprintf(item, "%s=%s", "use", printername); 729 attrList = (char **)list_append((void**)attrList, 730 (void *)item); 731 732 ((NS_LDAPDATA *)(printer->nsdata))->attrList = attrList; 733 734 status = ns_printer_put(printer); 735 _freeList(&attrList); 736 free(printer->nsdata); 737 738 if (status != 0) { 739 (void) ns_printer_destroy(printer); 740 return (status); 741 } 742 } 743 } else if (strcmp(isdefault, "true") == 0) { 744 /* 745 * No default entry existed and we need one. 746 */ 747 printer = (ns_printer_t *)malloc(sizeof (*printer)); 748 (void) memset(printer, 0, sizeof (*printer)); 749 printer->name = strdup("_default"); 750 printer->source = strdup("ldap"); 751 printer->cred = cred; 752 753 printer->nsdata = malloc(sizeof (NS_LDAPDATA)); 754 if (printer->nsdata == NULL) { 755 (void) ns_printer_destroy(printer); 756 return (1); 757 } 758 759 attrList = NULL; 760 item = (char *)malloc(strlen("use") + strlen(printername) + 4); 761 if (item == NULL) { 762 (void) ns_printer_destroy(printer); 763 return (1); 764 } 765 sprintf(item, "%s=%s", "use", printername); 766 attrList = (char **)list_append((void**)attrList, (void *)item); 767 768 ((NS_LDAPDATA *)(printer->nsdata))->attrList = attrList; 769 770 status = ns_printer_put(printer); 771 _freeList(&attrList); 772 free(printer->nsdata); 773 774 if (status != 0) { 775 (void) ns_printer_destroy(printer); 776 return (status); 777 } 778 } 779 780 (void) ns_printer_destroy(printer); 781 return (0); 782 } 783 784 785 786 787 /* 788 * ***************************************************************************** 789 * 790 * Function: _freeList() 791 * 792 * Description: Free the list created by list_append() where the items in 793 * the list have been strdup'ed. 794 * 795 * Parameters: 796 * Input: char ***list - returned set of kvp values 797 * 798 * Result: void 799 * 800 * ***************************************************************************** 801 */ 802 803 static void 804 _freeList(char ***list) 805 806 { 807 int i = 0; 808 809 /* ------ */ 810 811 if (list != NULL) { 812 if (*list != NULL) { 813 for (i = 0; (*list)[i] != NULL; i++) { 814 free((*list)[i]); 815 } 816 free(*list); 817 } 818 819 *list = NULL; 820 } 821 } /* _freeList */ 822