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