pcnfsd_print.c revision 1.1 1 /* RE_SID: @(%)/usr/dosnfs/shades_SCCS/unix/pcnfsd/v2/src/SCCS/s.pcnfsd_print.c 1.7 92/01/24 19:58:58 SMI */
2 /*
3 **=====================================================================
4 ** Copyright (c) 1986,1987,1988,1989,1990,1991 by Sun Microsystems, Inc.
5 ** @(#)pcnfsd_print.c 1.7 1/24/92
6 **=====================================================================
7 */
8 #include "common.h"
9 /*
10 **=====================================================================
11 ** I N C L U D E F I L E S E C T I O N *
12 ** *
13 ** If your port requires different include files, add a suitable *
14 ** #define in the customization section, and make the inclusion or *
15 ** exclusion of the files conditional on this. *
16 **=====================================================================
17 */
18 #include "pcnfsd.h"
19 #include <malloc.h>
20 #include <stdio.h>
21 #include <pwd.h>
22 #include <sys/file.h>
23 #include <signal.h>
24 #include <sys/stat.h>
25 #include <sys/ioctl.h>
26 #include <netdb.h>
27 #include <errno.h>
28 #include <string.h>
29
30 #ifndef SYSV
31 #include <sys/wait.h>
32 #endif
33
34 #ifdef ISC_2_0
35 #include <sys/fcntl.h>
36 #endif
37
38 #ifdef SHADOW_SUPPORT
39 #include <shadow.h>
40 #endif
41
42 /*
43 **---------------------------------------------------------------------
44 ** Other #define's
45 **---------------------------------------------------------------------
46 */
47 #ifndef MAXPATHLEN
48 #define MAXPATHLEN 1024
49 #endif
50
51 #ifndef SPOOLDIR
52 #define SPOOLDIR "/export/pcnfs"
53 #endif
54
55 #ifndef LPRDIR
56 #define LPRDIR "/usr/bin"
57 #endif
58
59 #ifndef LPCDIR
60 #define LPCDIR "/usr/sbin"
61 #endif
62
63 /*
64 ** The following defintions give the maximum time allowed for
65 ** an external command to run (in seconds)
66 */
67 #define MAXTIME_FOR_PRINT 10
68 #define MAXTIME_FOR_QUEUE 10
69 #define MAXTIME_FOR_CANCEL 10
70 #define MAXTIME_FOR_STATUS 10
71
72 #define QMAX 50
73
74 /*
75 ** The following is derived from ucb/lpd/displayq.c
76 */
77 #define SIZECOL 62
78 #define FILECOL 24
79
80 extern void scramble();
81 extern void run_ps630();
82 extern char *crypt();
83 extern FILE *su_popen();
84 extern int su_pclose();
85 int build_pr_list();
86 char *map_printer_name();
87 char *expand_alias();
88 void *grab();
89 void free_pr_list_item();
90 void free_pr_queue_item();
91 pr_list list_virtual_printers();
92
93 /*
94 **---------------------------------------------------------------------
95 ** Misc. variable definitions
96 **---------------------------------------------------------------------
97 */
98
99 extern int errno;
100 extern int interrupted; /* in pcnfsd_misc.c */
101 struct stat statbuf;
102 char pathname[MAXPATHLEN];
103 char new_pathname[MAXPATHLEN];
104 char sp_name[MAXPATHLEN] = SPOOLDIR;
105 char tempstr[256];
106 char delims[] = " \t\r\n:()";
107
108 pr_list printers = NULL;
109 pr_queue queue = NULL;
110
111 /*
112 **=====================================================================
113 ** C O D E S E C T I O N *
114 **=====================================================================
115 */
116
117 /*
118 * This is the latest word on the security check. The following
119 * routine "suspicious()" returns non-zero if the character string
120 * passed to it contains any shell metacharacters.
121 * Callers will typically code
122 *
123 * if(suspicious(some_parameter)) reject();
124 */
125
126 int suspicious (s)
127 char *s;
128 {
129 if(strpbrk(pathname, ";|&<>`'#!?*()[]^") != NULL)
130 return 1;
131 return 0;
132 }
133
134
135 int
136 valid_pr(pr)
137 char *pr;
138 {
139 char *p;
140 pr_list curr;
141 if(printers == NULL)
142 build_pr_list();
143
144 if(printers == NULL)
145 return(1); /* can't tell - assume it's good */
146
147 p = map_printer_name(pr);
148 if (p == NULL)
149 return(1); /* must be ok is maps to NULL! */
150 curr = printers;
151 while(curr) {
152 if(!strcmp(p, curr->pn))
153 return(1);
154 curr = curr->pr_next;
155 }
156
157 return(0);
158 }
159
160 pirstat pr_init(sys, pr, sp)
161 char *sys;
162 char *pr;
163 char**sp;
164 {
165 int dir_mode = 0777;
166 int rc;
167
168 *sp = &pathname[0];
169 pathname[0] = '\0';
170
171 if(suspicious(sys) || suspicious(pr))
172 return(PI_RES_FAIL);
173
174 /* get pathname of current directory and return to client */
175
176 (void)sprintf(pathname,"%s/%s",sp_name, sys);
177 (void)mkdir(sp_name, dir_mode); /* ignore the return code */
178 (void)chmod(sp_name, dir_mode);
179 rc = mkdir(pathname, dir_mode); /* DON'T ignore this return code */
180 if((rc < 0 && errno != EEXIST) ||
181 (chmod(pathname, dir_mode) != 0) ||
182 (stat(pathname, &statbuf) != 0) ||
183 !(statbuf.st_mode & S_IFDIR)) {
184 (void)sprintf(tempstr,
185 "rpc.pcnfsd: unable to set up spool directory %s\n",
186 pathname);
187 msg_out(tempstr);
188 pathname[0] = '\0'; /* null to tell client bad vibes */
189 return(PI_RES_FAIL);
190 }
191 if (!valid_pr(pr))
192 {
193 pathname[0] = '\0'; /* null to tell client bad vibes */
194 return(PI_RES_NO_SUCH_PRINTER);
195 }
196 return(PI_RES_OK);
197
198 }
199
200
202 psrstat pr_start2(system, pr, user, fname, opts, id)
203 char *system;
204 char *pr;
205 char *user;
206 char *fname;
207 char *opts;
208 char **id;
209 {
210 char snum[20];
211 static char req_id[256];
212 char cmdbuf[256];
213 char resbuf[256];
214 FILE *fd;
215 int i;
216 char *xcmd;
217 char *cp;
218 int failed = 0;
219
220 #ifdef HACK_FOR_ROTATED_TRANSCRIPT
221 char scratch[512];
222 #endif
223
224
225 if(suspicious(system) ||
226 suspicious(pr) ||
227 suspicious(user) ||
228 suspicious(fname))
229 return(PS_RES_FAIL);
230
231 (void)sprintf(pathname,"%s/%s/%s",sp_name,
232 system,
233 fname);
234
235 *id = &req_id[0];
236 req_id[0] = '\0';
237
238 if (stat(pathname, &statbuf))
239 {
240 /*
241 **-----------------------------------------------------------------
242 ** We can't stat the file. Let's try appending '.spl' and
243 ** see if it's already in progress.
244 **-----------------------------------------------------------------
245 */
246
247 (void)strcat(pathname, ".spl");
248 if (stat(pathname, &statbuf))
249 {
250 /*
251 **----------------------------------------------------------------
252 ** It really doesn't exist.
253 **----------------------------------------------------------------
254 */
255
256
257 return(PS_RES_NO_FILE);
258 }
259 /*
260 **-------------------------------------------------------------
261 ** It is already on the way.
262 **-------------------------------------------------------------
263 */
264
265
266 return(PS_RES_ALREADY);
267 }
268
269 if (statbuf.st_size == 0)
270 {
271 /*
272 **-------------------------------------------------------------
273 ** Null file - don't print it, just kill it.
274 **-------------------------------------------------------------
275 */
276 (void)unlink(pathname);
277
278 return(PS_RES_NULL);
279 }
280 /*
281 **-------------------------------------------------------------
282 ** The file is real, has some data, and is not already going out.
283 ** We rename it by appending '.spl' and exec "lpr" to do the
284 ** actual work.
285 **-------------------------------------------------------------
286 */
287 (void)strcpy(new_pathname, pathname);
288 (void)strcat(new_pathname, ".spl");
289
290 /*
291 **-------------------------------------------------------------
292 ** See if the new filename exists so as not to overwrite it.
293 **-------------------------------------------------------------
294 */
295
296
297 if (!stat(new_pathname, &statbuf))
298 {
299 (void)strcpy(new_pathname, pathname); /* rebuild a new name */
300 (void)sprintf(snum, "%d", rand()); /* get some number */
301 (void)strncat(new_pathname, snum, 3);
302 (void)strcat(new_pathname, ".spl"); /* new spool file */
303 }
304 if (rename(pathname, new_pathname))
305 {
306 /*
307 **---------------------------------------------------------------
308 ** Should never happen.
309 **---------------------------------------------------------------
310 */
311 (void)sprintf(tempstr, "rpc.pcnfsd: spool file rename (%s->%s) failed.\n",
312 pathname, new_pathname);
313 msg_out(tempstr);
314 return(PS_RES_FAIL);
315 }
316
317 if (*opts == 'd')
318 {
319 /*
320 **------------------------------------------------------
321 ** This is a Diablo print stream. Apply the ps630
322 ** filter with the appropriate arguments.
323 **------------------------------------------------------
324 */
325 (void)run_ps630(new_pathname, opts);
326 }
327 /*
328 ** Try to match to an aliased printer
329 */
330 xcmd = expand_alias(pr, new_pathname, user, system);
331 /*
332 **----------------------------------------------------------
333 ** Use the copy option so we can remove the orignal spooled
334 ** nfs file from the spool directory.
335 **----------------------------------------------------------
336 */
337 if(!xcmd) {
338 sprintf(cmdbuf, "%s/lpr -P%s %s",
339 LPRDIR, pr, new_pathname);
340 xcmd = cmdbuf;
341 }
342 if ((fd = su_popen(user, xcmd, MAXTIME_FOR_PRINT)) == NULL) {
343 msg_out("rpc.pcnfsd: su_popen failed");
344 return(PS_RES_FAIL);
345 }
346 req_id[0] = '\0'; /* asume failure */
347 while(fgets(resbuf, 255, fd) != NULL) {
348 i = strlen(resbuf);
349 if(i)
350 resbuf[i-1] = '\0'; /* trim NL */
351 if(!strncmp(resbuf, "request id is ", 14))
352 /* New - just the first word is needed */
353 strcpy(req_id, strtok(&resbuf[14], delims));
354 else if (strembedded("disabled", resbuf))
355 failed = 1;
356 }
357 if(su_pclose(fd) == 255)
358 msg_out("rpc.pcnfsd: su_pclose alert");
359 (void)unlink(new_pathname);
360 return((failed | interrupted)? PS_RES_FAIL : PS_RES_OK);
361 }
362
363
365 /*
366 * determine which printers are valid...
367 * on BSD use "lpc status"
368 * on SVR4 use "lpstat -v"
369 */
370 int
371 build_pr_list()
372 {
373 pr_list last = NULL;
374 pr_list curr = NULL;
375 char buff[256];
376 FILE *p;
377 char *cp;
378 int saw_system;
379
380 sprintf(buff, "%s/lpc status", LPCDIR);
381 p = popen(buff, "r");
382 if(p == NULL) {
383 msg_out("rpc.pcnfsd: unable to popen lpc stat");
384 return(0);
385 }
386
387 while(fgets(buff, 255, p) != NULL) {
388 if (isspace(buff[0]))
389 continue;
390
391 if ((cp = strtok(buff, delims)) == NULL)
392 continue;
393
394 curr = (struct pr_list_item *)
395 grab(sizeof (struct pr_list_item));
396
397 /* XXX - Should distinguish remote printers. */
398 curr->pn = strdup(cp);
399 curr->device = strdup(cp);
400 curr->remhost = strdup("");
401 curr->cm = strdup("-");
402 curr->pr_next = NULL;
403
404 if(last == NULL)
405 printers = curr;
406 else
407 last->pr_next = curr;
408 last = curr;
409
410 }
411 (void) fclose(p);
412
413 /*
414 ** Now add on the virtual printers, if any
415 */
416 if(last == NULL)
417 printers = list_virtual_printers();
418 else
419 last->pr_next = list_virtual_printers();
420
421 return(1);
422 }
423
424 void *grab(n)
425 int n;
426 {
427 void *p;
428
429 p = (void *)malloc(n);
430 if(p == NULL) {
431 msg_out("rpc.pcnfsd: malloc failure");
432 exit(1);
433 }
434 return(p);
435 }
436
437 void
438 free_pr_list_item(curr)
439 pr_list curr;
440 {
441 if(curr->pn)
442 free(curr->pn);
443 if(curr->device)
444 free(curr->device);
445 if(curr->remhost)
446 free(curr->remhost);
447 if(curr->cm)
448 free(curr->cm);
449 if(curr->pr_next)
450 free_pr_list_item(curr->pr_next); /* recurse */
451 free(curr);
452 }
453 /*
454 ** Print queue handling.
455 **
456 ** Note that the first thing we do is to discard any
457 ** existing queue.
458 */
459
460 pirstat
461 build_pr_queue(pn, user, just_mine, p_qlen, p_qshown)
462 printername pn;
463 username user;
464 int just_mine;
465 int *p_qlen;
466 int *p_qshown;
467 {
468 pr_queue last = NULL;
469 pr_queue curr = NULL;
470 char buff[256];
471 FILE *p;
472 char *cp;
473 int i;
474 char *rank;
475 char *owner;
476 char *job;
477 char *files;
478 char *totsize;
479
480 if(queue) {
481 free_pr_queue_item(queue);
482 queue = NULL;
483 }
484 *p_qlen = 0;
485 *p_qshown = 0;
486 pn = map_printer_name(pn);
487 if(pn == NULL || suspicious(pn))
488 return(PI_RES_NO_SUCH_PRINTER);
489
490 sprintf(buff, "%s/lpq -P%s", LPRDIR, pn);
491
492 p = su_popen(user, buff, MAXTIME_FOR_QUEUE);
493 if(p == NULL) {
494 msg_out("rpc.pcnfsd: unable to popen() lpq");
495 return(PI_RES_FAIL);
496 }
497
498 while(fgets(buff, 255, p) != NULL) {
499 i = strlen(buff) - 1;
500 buff[i] = '\0'; /* zap trailing NL */
501 if(i < SIZECOL)
502 continue;
503 if(!mystrncasecmp(buff, "rank", 4))
504 continue;
505
506 totsize = &buff[SIZECOL-1];
507 files = &buff[FILECOL-1];
508 cp = totsize;
509 cp--;
510 while(cp > files && isspace(*cp))
511 *cp-- = '\0';
512
513 buff[FILECOL-2] = '\0';
514
515 cp = strtok(buff, delims);
516 if(!cp)
517 continue;
518 rank = cp;
519
520 cp = strtok(NULL, delims);
521 if(!cp)
522 continue;
523 owner = cp;
524
525 cp = strtok(NULL, delims);
526 if(!cp)
527 continue;
528 job = cp;
529
530 *p_qlen += 1;
531
532 if(*p_qshown > QMAX)
533 continue;
534
535 if(just_mine && mystrcasecmp(owner, user))
536 continue;
537
538 *p_qshown += 1;
539
540 curr = (struct pr_queue_item *)
541 grab(sizeof (struct pr_queue_item));
542
543 curr->position = atoi(rank); /* active -> 0 */
544 curr->id = strdup(job);
545 curr->size = strdup(totsize);
546 curr->status = strdup(rank);
547 curr->system = strdup("");
548 curr->user = strdup(owner);
549 curr->file = strdup(files);
550 curr->cm = strdup("-");
551 curr->pr_next = NULL;
552
553 if(last == NULL)
554 queue = curr;
555 else
556 last->pr_next = curr;
557 last = curr;
558
559 }
560 (void) su_pclose(p);
561 return(PI_RES_OK);
562 }
563
564 void
565 free_pr_queue_item(curr)
566 pr_queue curr;
567 {
568 if(curr->id)
569 free(curr->id);
570 if(curr->size)
571 free(curr->size);
572 if(curr->status)
573 free(curr->status);
574 if(curr->system)
575 free(curr->system);
576 if(curr->user)
577 free(curr->user);
578 if(curr->file)
579 free(curr->file);
580 if(curr->cm)
581 free(curr->cm);
582 if(curr->pr_next)
583 free_pr_queue_item(curr->pr_next); /* recurse */
584 free(curr);
585 }
586
587
588
589 pirstat
590 get_pr_status(pn, avail, printing, qlen, needs_operator, status)
591 printername pn;
592 bool_t *avail;
593 bool_t *printing;
594 int *qlen;
595 bool_t *needs_operator;
596 char *status;
597 {
598 char cmd[128];
599 char buff[256];
600 char buff2[256];
601 char pname[64];
602 FILE *p;
603 char *cp;
604 char *cp1;
605 char *cp2;
606 int n;
607 pirstat stat = PI_RES_NO_SUCH_PRINTER;
608
609 /* assume the worst */
610 *avail = FALSE;
611 *printing = FALSE;
612 *needs_operator = FALSE;
613 *qlen = 0;
614 *status = '\0';
615
616 pn = map_printer_name(pn);
617 if(pn == NULL || suspicious(pn))
618 return(PI_RES_NO_SUCH_PRINTER);
619
620 sprintf(pname, "%s:", pn);
621 n = strlen(pname);
622
623 #if 0
624 /*
625 * We used to use lpstat -a, but it breaks because of
626 * printer alias inconsistency
627 */
628 p = popen("/usr/bin/lpstat -a", "r");
629 #else
630 /*
631 * So now we use: lpc status
632 */
633 sprintf(cmd, "%s/lpc status %s", LPCDIR, pn);
634 p = popen(cmd, "r");
635 #endif
636 if(p == NULL) {
637 msg_out("rpc.pcnfsd: unable to popen() lp status");
638 return(PI_RES_FAIL);
639 }
640
641 while(fgets(buff, 255, p) != NULL) {
642 if(strncmp(buff, pname, n))
643 continue;
644 /*
645 ** We have a match. The only failure now is PI_RES_FAIL if
646 ** lpstat output cannot be decoded
647 */
648 stat = PI_RES_FAIL;
649 /*
650 ** The next four lines are usually if the form
651 **
652 ** queuing is [enabled|disabled]
653 ** printing is [enabled|disabled]
654 ** [no entries | N entr[y|ies] in spool area]
655 ** <status message, may include the word "attention">
656 */
657 while(fgets(buff, 255, p) != NULL && isspace(buff[0])) {
658 cp = buff;
659 while(isspace(*cp))
660 cp++;
661 if(*cp == '\0')
662 break;
663 cp1 = cp;
664 cp2 = buff2;
665 while(*cp1 && *cp1 != '\n') {
666 *cp2++ = tolower(*cp1);
667 cp1++;
668 }
669 *cp1 = '\0';
670 *cp2 = '\0';
671 /*
672 ** Now buff2 has a lower-cased copy and cp points at the original;
673 ** both are null terminated without any newline
674 */
675 if(!strncmp(buff2, "queuing", 7)) {
676 *avail = (strstr(buff2, "enabled") != NULL);
677 continue;
678 }
679 if(!strncmp(buff2, "printing", 8)) {
680 *printing = (strstr(buff2, "enabled") != NULL);
681 continue;
682 }
683 if(isdigit(buff2[0]) && (strstr(buff2, "entr") !=NULL)) {
684
685 *qlen = atoi(buff2);
686 continue;
687 }
688 if(strstr(buff2, "attention") != NULL ||
689 strstr(buff2, "error") != NULL)
690 *needs_operator = TRUE;
691 if(*needs_operator || strstr(buff2, "waiting") != NULL)
692 strcpy(status, cp);
693 }
694 stat = PI_RES_OK;
695 break;
696 }
697 (void) pclose(p);
698 return(stat);
699 }
700
701
702 pcrstat pr_cancel(pr, user, id)
703 char *pr;
704 char *user;
705 char *id;
706 {
707 char cmdbuf[256];
708 char resbuf[256];
709 FILE *fd;
710 int i;
711 pcrstat stat = PC_RES_NO_SUCH_JOB;
712
713 pr = map_printer_name(pr);
714 if(pr == NULL || suspicious(pr))
715 return(PC_RES_NO_SUCH_PRINTER);
716 if(suspicious(id))
717 return(PC_RES_NO_SUCH_JOB);
718
719 sprintf(cmdbuf, "%s/lprm -P%s %s", LPRDIR, pr, id);
720 if ((fd = su_popen(user, cmdbuf, MAXTIME_FOR_CANCEL)) == NULL) {
721 msg_out("rpc.pcnfsd: su_popen failed");
722 return(PC_RES_FAIL);
723 }
724 while(fgets(resbuf, 255, fd) != NULL) {
725 i = strlen(resbuf);
726 if(i)
727 resbuf[i-1] = '\0'; /* trim NL */
728 if(strstr(resbuf, "dequeued") != NULL)
729 stat = PC_RES_OK;
730 if(strstr(resbuf, "unknown printer") != NULL)
731 stat = PC_RES_NO_SUCH_PRINTER;
732 if(strstr(resbuf, "Permission denied") != NULL)
733 stat = PC_RES_NOT_OWNER;
734 }
735 if(su_pclose(fd) == 255)
736 msg_out("rpc.pcnfsd: su_pclose alert");
737 return(stat);
738 }
739
740
741 /*
743 ** New subsystem here. We allow the administrator to define
744 ** up to NPRINTERDEFS aliases for printer names. This is done
745 ** using the "/etc/pcnfsd.conf" file, which is read at startup.
746 ** There are three entry points to this subsystem
747 **
748 ** void add_printer_alias(char *printer, char *alias_for, char *command)
749 **
750 ** This is invoked from "config_from_file()" for each
751 ** "printer" line. "printer" is the name of a printer; note that
752 ** it is possible to redefine an existing printer. "alias_for"
753 ** is the name of the underlying printer, used for queue listing
754 ** and other control functions. If it is "-", there is no
755 ** underlying printer, or the administrative functions are
756 ** not applicable to this printer. "command"
757 ** is the command which should be run (via "su_popen()") if a
758 ** job is printed on this printer. The following tokens may be
759 ** embedded in the command, and are substituted as follows:
760 **
761 ** $FILE - path to the file containing the print data
762 ** $USER - login of user
763 ** $HOST - hostname from which job originated
764 **
765 ** Tokens may occur multiple times. If The command includes no
766 ** $FILE token, the string " $FILE" is silently appended.
767 **
768 ** pr_list list_virtual_printers()
769 **
770 ** This is invoked from build_pr_list to generate a list of aliased
771 ** printers, so that the client that asks for a list of valid printers
772 ** will see these ones.
773 **
774 ** char *map_printer_name(char *printer)
775 **
776 ** If "printer" identifies an aliased printer, this function returns
777 ** the "alias_for" name, or NULL if the "alias_for" was given as "-".
778 ** Otherwise it returns its argument.
779 **
780 ** char *expand_alias(char *printer, char *file, char *user, char *host)
781 **
782 ** If "printer" is an aliased printer, this function returns a
783 ** pointer to a static string in which the corresponding command
784 ** has been expanded. Otherwise ot returns NULL.
785 */
786 #define NPRINTERDEFS 16
787 int num_aliases = 0;
788 struct {
789 char *a_printer;
790 char *a_alias_for;
791 char *a_command;
792 } alias [NPRINTERDEFS];
793
794
795
796 void
797 add_printer_alias(printer, alias_for, command)
798 char *printer;
799 char *alias_for;
800 char *command;
801 {
802 if(num_aliases < NPRINTERDEFS) {
803 alias[num_aliases].a_printer = strdup(printer);
804 alias[num_aliases].a_alias_for =
805 (strcmp(alias_for, "-") ? strdup(alias_for) : NULL);
806 if(strstr(command, "$FILE"))
807 alias[num_aliases].a_command = strdup(command);
808 else {
809 alias[num_aliases].a_command = (char *)grab(strlen(command) + 8);
810 strcpy(alias[num_aliases].a_command, command);
811 strcat(alias[num_aliases].a_command, " $FILE");
812 }
813 num_aliases++;
814 }
815 }
816
817 pr_list list_virtual_printers()
818 {
819 pr_list first = NULL;
820 pr_list last = NULL;
821 pr_list curr = NULL;
822 int i;
823
824
825 if(num_aliases == 0)
826 return(NULL);
827
828 for (i = 0; i < num_aliases; i++) {
829 curr = (struct pr_list_item *)
830 grab(sizeof (struct pr_list_item));
831
832 curr->pn = strdup(alias[i].a_printer);
833 if(alias[i].a_alias_for == NULL)
834 curr->device = strdup("");
835 else
836 curr->device = strdup(alias[i].a_alias_for);
837 curr->remhost = strdup("");
838 curr->cm = strdup("(alias)");
839 curr->pr_next = NULL;
840 if(last == NULL)
841 first = curr;
842 else
843 last->pr_next = curr;
844 last = curr;
845
846 }
847 return(first);
848 }
849
850
851 char *
852 map_printer_name(printer)
853 char *printer;
854 {
855 int i;
856 for (i = 0; i < num_aliases; i++){
857 if(!strcmp(printer, alias[i].a_printer))
858 return(alias[i].a_alias_for);
859 }
860 return(printer);
861 }
862
863 static void
864 substitute(string, token, data)
865 char *string;
866 char *token;
867 char *data;
868 {
869 char temp[512];
870 char *c;
871
872 while(c = strstr(string, token)) {
873 *c = '\0';
874 strcpy(temp, string);
875 strcat(temp, data);
876 c += strlen(token);
877 strcat(temp, c);
878 strcpy(string, temp);
879 }
880 }
881
882 char *
883 expand_alias(printer, file, user, host)
884 char *printer;
885 char *file;
886 char *user;
887 char *host;
888 {
889 static char expansion[512];
890 int i;
891 for (i = 0; i < num_aliases; i++){
892 if(!strcmp(printer, alias[i].a_printer)) {
893 strcpy(expansion, alias[i].a_command);
894 substitute(expansion, "$FILE", file);
895 substitute(expansion, "$USER", user);
896 substitute(expansion, "$HOST", host);
897 return(expansion);
898 }
899 }
900 return(NULL);
901 }
902
903