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 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 33 #include <unistd.h> 34 #include <stdlib.h> 35 #include <sys/types.h> 36 #include <ctype.h> 37 #include <string.h> 38 #include <pwd.h> 39 #include <grp.h> 40 #include <signal.h> 41 #include "ttymon.h" 42 #include "tmstruct.h" 43 #include "tmextern.h" 44 45 extern char *strsave(); 46 extern void set_softcar(); 47 extern int vml(); 48 void purge(); 49 static int get_flags(); 50 static int get_ttyflags(); 51 static int same_entry(); 52 static int check_pmtab(); 53 static void insert_pmtab(); 54 static void free_pmtab(); 55 static char *expand(); 56 57 int check_identity(); 58 59 int strcheck(); 60 61 /* 62 * read_pmtab() 63 * - read and parse pmtab 64 * - store table in linked list pointed by global variable "PMtab" 65 * - exit if file does not exist or error detected. 66 */ 67 void 68 read_pmtab() 69 { 70 register struct pmtab *gptr; 71 register char *ptr, *wptr; 72 FILE *fp; 73 int input, state, size, rawc, field, linenum; 74 char oldc; 75 char line[BUFSIZ]; 76 char wbuf[BUFSIZ]; 77 static char *states[] = { 78 "","tag","flags","identity","reserved1","reserved2","reserved3", 79 "device","ttyflags","count","service", "timeout","ttylabel", 80 "modules","prompt","disable msg","terminal type","soft-carrier" 81 }; 82 83 # ifdef DEBUG 84 debug("in read_pmtab"); 85 # endif 86 87 if ((fp = fopen(PMTABFILE,"r")) == NULL) { 88 fatal("open pmtab (%s) failed", PMTABFILE); 89 } 90 91 Nentries = 0; 92 if (check_version(PMTAB_VERS, PMTABFILE) != 0) 93 fatal("check pmtab version failed"); 94 95 for (gptr = PMtab; gptr; gptr = gptr->p_next) { 96 if ((gptr->p_status == SESSION) || 97 (gptr->p_status == LOCKED) || 98 (gptr->p_status == UNACCESS)) { 99 if (gptr->p_fd > 0) { 100 (void)close(gptr->p_fd); 101 gptr->p_fd = 0; 102 } 103 gptr->p_inservice = gptr->p_status; 104 } 105 gptr->p_status = NOTVALID; 106 } 107 108 wptr = wbuf; 109 input = ACTIVE; 110 linenum = 0; 111 do { 112 linenum++; 113 line[0] = '\0'; 114 for (ptr= line,oldc = '\0'; ptr < &line[sizeof(line)-1] && 115 (rawc=getc(fp))!= '\n' && rawc != EOF; ptr++,oldc=(char)rawc){ 116 if ((rawc == '#') && (oldc != '\\')) 117 break; 118 *ptr = (char)rawc; 119 } 120 *ptr = '\0'; 121 122 /* skip rest of the line */ 123 if (rawc != EOF && rawc != '\n') { 124 if (rawc != '#') 125 log("Entry too long.\n"); 126 while ((rawc = getc(fp)) != EOF && rawc != '\n') 127 ; 128 } 129 130 if (rawc == EOF) { 131 if (ptr == line) break; 132 else input = FINISHED; 133 } 134 135 /* if empty line, skip */ 136 for (ptr=line; *ptr != '\0' && isspace(*ptr); ptr++) 137 ; 138 if (*ptr == '\0') continue; 139 140 #ifdef DEBUG 141 debug("**** Next Entry ****\n%s", line); 142 #endif 143 log("Processing pmtab line #%d", linenum); 144 145 /* Now we have the complete line */ 146 147 if ((gptr = ALLOC_PMTAB) == PNULL) 148 fatal("memory allocation failed"); 149 150 /* set hangup flag, this is the default */ 151 gptr->p_ttyflags |= H_FLAG; 152 153 /* 154 * For compatibility reasons, we cannot rely on these 155 * having values assigned from pmtab. 156 */ 157 gptr->p_termtype = ""; 158 gptr->p_softcar = ""; 159 160 for (state=P_TAG,ptr=line;state !=FAILURE && state !=SUCCESS;) { 161 switch(state) { 162 case P_TAG: 163 gptr->p_tag = strsave(getword(ptr,&size,0)); 164 break; 165 case P_FLAGS: 166 (void)strcpy(wptr, getword(ptr,&size,0)); 167 if ((get_flags(wptr, &gptr->p_flags)) != 0) { 168 field = state; 169 state = FAILURE; 170 } 171 break; 172 case P_IDENTITY: 173 gptr->p_identity=strsave(getword(ptr,&size,0)); 174 break; 175 case P_RES1: 176 gptr->p_res1=strsave(getword(ptr,&size,0)); 177 break; 178 case P_RES2: 179 gptr->p_res2=strsave(getword(ptr,&size,0)); 180 break; 181 case P_RES3: 182 gptr->p_res3=strsave(getword(ptr,&size,0)); 183 break; 184 case P_DEVICE: 185 gptr->p_device = strsave(getword(ptr,&size,0)); 186 break; 187 case P_TTYFLAGS: 188 (void)strcpy(wptr, getword(ptr,&size,0)); 189 if ((get_ttyflags(wptr,&gptr->p_ttyflags))!=0) { 190 field = state; 191 state = FAILURE; 192 } 193 break; 194 case P_COUNT: 195 (void)strcpy(wptr, getword(ptr,&size,0)); 196 if (strcheck(wptr, NUM) != 0) { 197 log("wait_read count must be a positive number"); 198 field = state; 199 state = FAILURE; 200 } 201 else 202 gptr->p_count = atoi(wptr); 203 break; 204 case P_SERVER: 205 gptr->p_server = 206 strsave(expand(getword(ptr,&size,1), 207 gptr->p_device)); 208 break; 209 case P_TIMEOUT: 210 (void)strcpy(wptr, getword(ptr,&size,0)); 211 if (strcheck(wptr, NUM) != 0) { 212 log("timeout value must be a positive number"); 213 field = state; 214 state = FAILURE; 215 } 216 else 217 gptr->p_timeout = atoi(wptr); 218 break; 219 case P_TTYLABEL: 220 gptr->p_ttylabel=strsave(getword(ptr,&size,0)); 221 break; 222 case P_MODULES: 223 gptr->p_modules = strsave(getword(ptr,&size,0)); 224 if (vml(gptr->p_modules) != 0) { 225 field = state; 226 state = FAILURE; 227 } 228 break; 229 case P_PROMPT: 230 gptr->p_prompt = strsave(getword(ptr,&size,TRUE)); 231 break; 232 case P_DMSG: 233 gptr->p_dmsg = strsave(getword(ptr,&size,TRUE)); 234 break; 235 236 case P_TERMTYPE: 237 gptr->p_termtype = strsave(getword(ptr,&size,TRUE)); 238 break; 239 240 case P_SOFTCAR: 241 gptr->p_softcar = strsave(getword(ptr,&size,TRUE)); 242 break; 243 244 } /* end switch */ 245 ptr += size; 246 if (state == FAILURE) 247 break; 248 if (*ptr == ':') { 249 ptr++; /* Skip the ':' */ 250 state++ ; 251 } else if (*ptr != '\0') { 252 field = state; 253 state = FAILURE; 254 } 255 if (*ptr == '\0') { 256 /* 257 * Maintain compatibility with older ttymon 258 * pmtab files. If Sun-added fields are 259 * missing, this should not be an error. 260 */ 261 if (state > P_DMSG) { 262 state = SUCCESS; 263 } else { 264 field = state; 265 state = FAILURE; 266 } 267 } 268 } /* end for loop */ 269 270 if (state == SUCCESS) { 271 if (check_pmtab(gptr) == 0) { 272 if (Nentries < Maxfds) 273 insert_pmtab(gptr); 274 else { 275 log("can't add more entries to " 276 "pmtab, Maxfds = %d", Maxfds); 277 free_pmtab(gptr); 278 (void)fclose(fp); 279 return; 280 } 281 } 282 else { 283 log("Parsing failure for entry: \n%s", line); 284 log("-------------------------------------------"); 285 free_pmtab(gptr); 286 } 287 } else { 288 *++ptr = '\0'; 289 log("Parsing failure in the \"%s\" field,\n%s" 290 "<--error detected here", states[field], line); 291 log("-------------------------------------------"); 292 free_pmtab(gptr); 293 } 294 } while (input == ACTIVE); 295 296 (void)fclose(fp); 297 return; 298 } 299 300 /* 301 * get_flags - scan flags field to set U_FLAG and X_FLAG 302 */ 303 static int 304 get_flags(wptr, flags) 305 char *wptr; /* pointer to the input string */ 306 long *flags; /* pointer to the flag to set */ 307 { 308 register char *p; 309 for (p = wptr; *p; p++) { 310 switch (*p) { 311 case 'x': 312 *flags |= X_FLAG; 313 break; 314 case 'u': 315 *flags |= U_FLAG; 316 break; 317 default: 318 log("Invalid flag -- %c", *p); 319 return(-1); 320 } 321 } 322 return(0); 323 } 324 325 /* 326 * get_ttyflags - scan ttyflags field to set corresponding flags 327 */ 328 static int 329 get_ttyflags(wptr, ttyflags) 330 char *wptr; /* pointer to the input string */ 331 long *ttyflags; /* pointer to the flag to be set*/ 332 { 333 register char *p; 334 for (p = wptr; *p; p++) { 335 switch (*p) { 336 case 'c': 337 *ttyflags |= C_FLAG; 338 break; 339 case 'h': /* h means don't hangup */ 340 *ttyflags &= ~H_FLAG; 341 break; 342 case 'b': 343 *ttyflags |= B_FLAG; 344 break; 345 case 'r': 346 *ttyflags |= R_FLAG; 347 break; 348 case 'I': 349 *ttyflags |= I_FLAG; 350 break; 351 default: 352 log("Invalid ttyflag -- %c", *p); 353 return(-1); 354 } 355 } 356 return(0); 357 } 358 359 # ifdef DEBUG 360 /* 361 * pflags - put service flags into intelligible form for output 362 */ 363 364 char * 365 pflags(flags) 366 long flags; /* binary representation of the flags */ 367 { 368 register int i; /* scratch counter */ 369 static char buf[BUFSIZ]; /* formatted flags */ 370 371 if (flags == 0) 372 return("-"); 373 i = 0; 374 if (flags & U_FLAG) { 375 buf[i++] = 'u'; 376 flags &= ~U_FLAG; 377 } 378 if (flags & X_FLAG) { 379 buf[i++] = 'x'; 380 flags &= ~X_FLAG; 381 } 382 if (flags) 383 log("Internal error in pflags"); 384 buf[i] = '\0'; 385 return(buf); 386 } 387 388 /* 389 * pttyflags - put ttyflags into intelligible form for output 390 */ 391 392 char * 393 pttyflags(flags) 394 long flags; /* binary representation of ttyflags */ 395 { 396 register int i; /* scratch counter */ 397 static char buf[BUFSIZ]; /* formatted flags */ 398 399 if (flags == 0) 400 return("h"); 401 i = 0; 402 if (flags & C_FLAG) { 403 buf[i++] = 'c'; 404 flags &= ~C_FLAG; 405 } 406 if (flags & H_FLAG) 407 flags &= ~H_FLAG; 408 else 409 buf[i++] = 'h'; 410 if (flags & B_FLAG) { 411 buf[i++] = 'b'; 412 flags &= ~B_FLAG; 413 } 414 if (flags & R_FLAG) { 415 buf[i++] = 'r'; 416 flags &= ~B_FLAG; 417 } 418 if (flags & I_FLAG) { 419 buf[i++] = 'I'; 420 flags &= ~I_FLAG; 421 } 422 if (flags) 423 log("Internal error in p_ttyflags"); 424 buf[i] = '\0'; 425 return(buf); 426 } 427 428 void 429 dump_pmtab() 430 { 431 struct pmtab *gptr; 432 433 debug("in dump_pmtab"); 434 log("********** dumping pmtab **********"); 435 log(" "); 436 for (gptr=PMtab; gptr; gptr = gptr->p_next) { 437 log("-------------------------------------------"); 438 log("tag:\t\t%s", gptr->p_tag); 439 log("flags:\t\t%s",pflags(gptr->p_flags)); 440 log("identity:\t%s", gptr->p_identity); 441 log("reserved1:\t%s", gptr->p_res1); 442 log("reserved2:\t%s", gptr->p_res2); 443 log("reserved3:\t%s", gptr->p_res3); 444 log("device:\t%s", gptr->p_device); 445 log("ttyflags:\t%s",pttyflags(gptr->p_ttyflags)); 446 log("count:\t\t%d", gptr->p_count); 447 log("server:\t%s", gptr->p_server); 448 log("timeout:\t%d", gptr->p_timeout); 449 log("ttylabel:\t%s", gptr->p_ttylabel); 450 log("modules:\t%s", gptr->p_modules); 451 log("prompt:\t%s", gptr->p_prompt); 452 log("disable msg:\t%s", gptr->p_dmsg); 453 log("terminal type:\t%s", gptr->p_termtype); 454 log("soft-carrier:\t%s", gptr->p_softcar); 455 log("status:\t\t%d", gptr->p_status); 456 log("inservice:\t%d", gptr->p_inservice); 457 log("fd:\t\t%d", gptr->p_fd); 458 log("pid:\t\t%ld", gptr->p_pid); 459 log("uid:\t\t%ld", gptr->p_uid); 460 log("gid:\t\t%ld", gptr->p_gid); 461 log("dir:\t%s", gptr->p_dir); 462 log(" "); 463 } 464 log("********** end dumping pmtab **********"); 465 } 466 # endif 467 468 /* 469 * same_entry(e1,e2) - compare 2 entries of pmtab 470 * if the fields are different, copy e2 to e1 471 * return 1 if same, return 0 if different 472 */ 473 static int 474 same_entry(e1,e2) 475 struct pmtab *e1,*e2; 476 { 477 478 if (strcmp(e1->p_identity, e2->p_identity) != 0) 479 return(0); 480 if (strcmp(e1->p_res1, e2->p_res1) != 0) 481 return(0); 482 if (strcmp(e1->p_res2, e2->p_res2) != 0) 483 return(0); 484 if (strcmp(e1->p_res3, e2->p_res3) != 0) 485 return(0); 486 if (strcmp(e1->p_device, e2->p_device) != 0) 487 return(0); 488 if (strcmp(e1->p_server, e2->p_server) != 0) 489 return(0); 490 if (strcmp(e1->p_ttylabel, e2->p_ttylabel) != 0) 491 return(0); 492 if (strcmp(e1->p_modules, e2->p_modules) != 0) 493 return(0); 494 if (strcmp(e1->p_prompt, e2->p_prompt) != 0) 495 return(0); 496 if (strcmp(e1->p_dmsg, e2->p_dmsg) != 0) 497 return(0); 498 if (strcmp(e1->p_termtype, e2->p_termtype) != 0) 499 return(0); 500 if (strcmp(e1->p_softcar, e2->p_softcar) != 0) 501 return(0); 502 if (e1->p_flags != e2->p_flags) 503 return(0); 504 /* 505 * compare lowest 4 bits only, 506 * because A_FLAG is not part of original ttyflags 507 */ 508 if ((e1->p_ttyflags & ~A_FLAG) != (e2->p_ttyflags & ~A_FLAG)) 509 return(0); 510 if (e1->p_count != e2->p_count) 511 return(0); 512 if (e1->p_timeout != e2->p_timeout) 513 return(0); 514 if (e1->p_uid != e2->p_uid) 515 return(0); 516 if (e1->p_gid != e2->p_gid) 517 return(0); 518 if (strcmp(e1->p_dir, e2->p_dir) != 0) 519 return(0); 520 return(1); 521 } 522 523 524 /* 525 * insert_pmtab - insert a pmtab entry into the linked list 526 */ 527 528 static void 529 insert_pmtab(sp) 530 register struct pmtab *sp; /* ptr to entry to be inserted */ 531 { 532 register struct pmtab *tsp, *savtsp; /* scratch pointers */ 533 int ret; /* strcmp return value */ 534 535 # ifdef DEBUG 536 debug("in insert_pmtab"); 537 # endif 538 savtsp = tsp = PMtab; 539 540 /* 541 * find the correct place to insert this element 542 */ 543 544 while (tsp) { 545 ret = strcmp(sp->p_tag, tsp->p_tag); 546 if (ret > 0) { 547 /* keep on looking */ 548 savtsp = tsp; 549 tsp = tsp->p_next; 550 continue; 551 } 552 else if (ret == 0) { 553 if (tsp->p_status) { 554 /* this is a duplicate entry, ignore it */ 555 log("Ignoring duplicate entry for <%s>", 556 tsp->p_tag); 557 } 558 else { 559 if (same_entry(tsp,sp)) { /* same entry */ 560 tsp->p_status = VALID; 561 } 562 else { /* entry changed */ 563 if ((sp->p_flags & X_FLAG) && 564 ((sp->p_dmsg == NULL) || 565 (*(sp->p_dmsg) == '\0'))) { 566 /* disabled entry */ 567 tsp->p_status = NOTVALID; 568 } 569 else { 570 # ifdef DEBUG 571 debug("replacing <%s>", sp->p_tag); 572 # endif 573 /* replace old entry */ 574 sp->p_next = tsp->p_next; 575 if (tsp == PMtab) { 576 PMtab = sp; 577 } 578 else { 579 savtsp->p_next = sp; 580 } 581 sp->p_status = CHANGED; 582 sp->p_fd = tsp->p_fd; 583 sp->p_pid = tsp->p_pid; 584 sp->p_inservice = 585 tsp->p_inservice; 586 sp = tsp; 587 } 588 } 589 Nentries++; 590 } 591 free_pmtab(sp); 592 return; 593 } 594 else { 595 if ((sp->p_flags & X_FLAG) && 596 ((sp->p_dmsg == NULL) || 597 (*(sp->p_dmsg) == '\0'))) { /* disabled entry */ 598 free_pmtab(sp); 599 return; 600 } 601 /* 602 * Set the state of soft-carrier. 603 * Since this is a one-time only operation, 604 * we do it when this service is added to 605 * the enabled list. 606 */ 607 if (*sp->p_softcar != '\0') 608 set_softcar(sp); 609 610 /* insert it here */ 611 if (tsp == PMtab) { 612 sp->p_next = PMtab; 613 PMtab = sp; 614 } 615 else { 616 sp->p_next = savtsp->p_next; 617 savtsp->p_next = sp; 618 } 619 # ifdef DEBUG 620 debug("adding <%s>", sp->p_tag); 621 # endif 622 Nentries++; 623 /* this entry is "current" */ 624 sp->p_status = VALID; 625 return; 626 } 627 } 628 629 /* 630 * either an empty list or should put element at end of list 631 */ 632 633 if ((sp->p_flags & X_FLAG) && 634 ((sp->p_dmsg == NULL) || 635 (*(sp->p_dmsg) == '\0'))) { /* disabled entry */ 636 free_pmtab(sp); /* do not poll this entry */ 637 return; 638 } 639 /* 640 * Set the state of soft-carrier. 641 * Since this is a one-time only operation, 642 * we do it when this service is added to 643 * the enabled list. 644 */ 645 if (*sp->p_softcar != '\0') 646 set_softcar(sp); 647 sp->p_next = NULL; 648 if (PMtab == NULL) 649 PMtab = sp; 650 else 651 savtsp->p_next = sp; 652 # ifdef DEBUG 653 debug("adding <%s>", sp->p_tag); 654 # endif 655 ++Nentries; 656 /* this entry is "current" */ 657 sp->p_status = VALID; 658 } 659 660 661 /* 662 * purge - purge linked list of "old" entries 663 */ 664 665 666 void 667 purge() 668 { 669 register struct pmtab *sp; /* working pointer */ 670 register struct pmtab *savesp, *tsp; /* scratch pointers */ 671 672 # ifdef DEBUG 673 debug("in purge"); 674 # endif 675 sp = savesp = PMtab; 676 while (sp) { 677 if (sp->p_status) { 678 # ifdef DEBUG 679 debug("p_status not 0"); 680 # endif 681 savesp = sp; 682 sp = sp->p_next; 683 } 684 else { 685 tsp = sp; 686 if (tsp == PMtab) { 687 PMtab = sp->p_next; 688 savesp = PMtab; 689 } 690 else 691 savesp->p_next = sp->p_next; 692 # ifdef DEBUG 693 debug("purging <%s>", sp->p_tag); 694 # endif 695 sp = sp->p_next; 696 free_pmtab(tsp); 697 } 698 } 699 } 700 701 /* 702 * free_pmtab - free one pmtab entry 703 */ 704 static void 705 free_pmtab(p) 706 struct pmtab *p; 707 { 708 #ifdef DEBUG 709 debug("in free_pmtab"); 710 #endif 711 free(p->p_tag); 712 free(p->p_identity); 713 free(p->p_res1); 714 free(p->p_res2); 715 free(p->p_res3); 716 free(p->p_device); 717 free(p->p_server); 718 free(p->p_ttylabel); 719 free(p->p_modules); 720 free(p->p_prompt); 721 free(p->p_dmsg); 722 free(p->p_termtype); 723 free(p->p_softcar); 724 if (p->p_dir) 725 free(p->p_dir); 726 free(p); 727 } 728 729 /* 730 * check_pmtab - check the fields to make sure things are correct 731 * - return 0 if everything is ok 732 * - return -1 if something is wrong 733 */ 734 735 static int 736 check_pmtab(p) 737 struct pmtab *p; 738 { 739 if (p == NULL) { 740 log("pmtab ptr is NULL"); 741 return(-1); 742 } 743 744 /* check service tag */ 745 if ((p->p_tag == NULL) || (*(p->p_tag) == '\0')) { 746 log("port/service tag is missing"); 747 return(-1); 748 } 749 if (strlen(p->p_tag) > (size_t)(MAXID - 1)) { 750 log("port/service tag <%s> is longer than %d", p->p_tag, 751 MAXID-1); 752 return(-1); 753 } 754 if (strcheck(p->p_tag, ALNUM) != 0) { 755 log("port/service tag <%s> is not alphanumeric", p->p_tag); 756 return(-1); 757 } 758 if (check_identity(p) != 0) { 759 return(-1); 760 } 761 762 if (check_device(p->p_device) != 0) 763 return(-1); 764 765 if (check_cmd(p->p_server) != 0) 766 return(-1); 767 return(0); 768 } 769 770 extern struct passwd *getpwnam(); 771 extern void endpwent(); 772 extern struct group *getgrgid(); 773 extern void endgrent(); 774 775 /* 776 * check_identity - check to see if the identity is a valid user 777 * - log name in the passwd file, 778 * - and if its group id is a valid one 779 * - return 0 if everything is ok. Otherwise, return -1 780 */ 781 782 int 783 check_identity(p) 784 struct pmtab *p; 785 { 786 register struct passwd *pwdp; 787 788 if ((p->p_identity == NULL) || (*(p->p_identity) == '\0')) { 789 log("identity field is missing"); 790 return(-1); 791 } 792 if ((pwdp = getpwnam(p->p_identity)) == NULL) { 793 log("missing or bad passwd entry for <%s>", p->p_identity); 794 endpwent(); 795 return(-1); 796 } 797 if (getgrgid(pwdp->pw_gid) == NULL) { 798 log("no group entry for %ld", pwdp->pw_gid); 799 endgrent(); 800 endpwent(); 801 return(-1); 802 } 803 p->p_uid = pwdp->pw_uid; 804 p->p_gid = pwdp->pw_gid; 805 p->p_dir = strsave(pwdp->pw_dir); 806 endgrent(); 807 endpwent(); 808 return(0); 809 } 810 811 /* 812 * expand(cmdp, devp) - expand %d to device name and %% to %, 813 * - any other characters are untouched. 814 * - return the expanded string 815 */ 816 static char * 817 expand(cmdp,devp) 818 char *cmdp; /* ptr to cmd string */ 819 char *devp; /* ptr to device name */ 820 { 821 register char *cp, *dp, *np; 822 static char buf[BUFSIZ]; 823 cp = cmdp; 824 np = buf; 825 dp = devp; 826 while (*cp) { 827 if (*cp != '%') { 828 *np++ = *cp++; 829 continue; 830 } 831 switch (*++cp) { 832 case 'd': 833 while (*dp) { 834 *np++ = *dp++; 835 } 836 cp++; 837 break; 838 case '%': 839 *np++ = *cp++; 840 break; 841 default: 842 *np++ = *cp++; 843 break; 844 } 845 } 846 *np = '\0'; 847 return(buf); 848 } 849 850