Home | History | Annotate | Line # | Download | only in rpc.pcnfsd
pcnfsd_print.c revision 1.12.2.1
      1  1.12.2.1     yamt /*	$NetBSD: pcnfsd_print.c,v 1.12.2.1 2014/05/22 11:43:09 yamt Exp $	*/
      2       1.2      gwr 
      3       1.1      jtc /* RE_SID: @(%)/usr/dosnfs/shades_SCCS/unix/pcnfsd/v2/src/SCCS/s.pcnfsd_print.c 1.7 92/01/24 19:58:58 SMI */
      4       1.1      jtc /*
      5       1.1      jtc **=====================================================================
      6       1.1      jtc ** Copyright (c) 1986,1987,1988,1989,1990,1991 by Sun Microsystems, Inc.
      7       1.1      jtc **	@(#)pcnfsd_print.c	1.7	1/24/92
      8       1.1      jtc **=====================================================================
      9       1.1      jtc */
     10       1.1      jtc /*
     11       1.1      jtc **=====================================================================
     12       1.1      jtc **             I N C L U D E   F I L E   S E C T I O N                *
     13       1.1      jtc **                                                                    *
     14       1.1      jtc ** If your port requires different include files, add a suitable      *
     15       1.1      jtc ** #define in the customization section, and make the inclusion or    *
     16       1.1      jtc ** exclusion of the files conditional on this.                        *
     17       1.1      jtc **=====================================================================
     18       1.1      jtc */
     19       1.5    lukem 
     20       1.1      jtc #include <sys/file.h>
     21       1.5    lukem #include <sys/ioctl.h>
     22       1.1      jtc #include <sys/stat.h>
     23       1.5    lukem 
     24       1.5    lukem #include <ctype.h>
     25       1.5    lukem #include <errno.h>
     26       1.1      jtc #include <netdb.h>
     27       1.5    lukem #include <pwd.h>
     28       1.5    lukem #include <signal.h>
     29       1.5    lukem #include <stdio.h>
     30       1.5    lukem #include <stdlib.h>
     31       1.1      jtc #include <string.h>
     32       1.5    lukem #include <unistd.h>
     33       1.1      jtc 
     34       1.1      jtc #ifndef SYSV
     35       1.1      jtc #include <sys/wait.h>
     36       1.1      jtc #endif
     37       1.1      jtc 
     38       1.1      jtc #ifdef ISC_2_0
     39       1.1      jtc #include <sys/fcntl.h>
     40       1.1      jtc #endif
     41       1.1      jtc 
     42       1.1      jtc #ifdef SHADOW_SUPPORT
     43       1.1      jtc #include <shadow.h>
     44       1.1      jtc #endif
     45       1.1      jtc 
     46       1.3      gwr #include "paths.h"
     47       1.3      gwr 
     48       1.5    lukem #include "common.h"
     49       1.5    lukem #include "pcnfsd.h"
     50       1.5    lukem #include "extern.h"
     51       1.5    lukem 
     52       1.1      jtc /*
     53       1.1      jtc **---------------------------------------------------------------------
     54       1.5    lukem ** Other #define's
     55       1.1      jtc **---------------------------------------------------------------------
     56       1.1      jtc */
     57       1.1      jtc #ifndef MAXPATHLEN
     58       1.1      jtc #define MAXPATHLEN 1024
     59       1.1      jtc #endif
     60       1.1      jtc 
     61       1.1      jtc /*
     62       1.6      wiz ** The following definitions give the maximum time allowed for
     63       1.1      jtc ** an external command to run (in seconds)
     64       1.1      jtc */
     65       1.1      jtc #define MAXTIME_FOR_PRINT	10
     66       1.1      jtc #define MAXTIME_FOR_QUEUE	10
     67       1.1      jtc #define MAXTIME_FOR_CANCEL	10
     68       1.1      jtc #define MAXTIME_FOR_STATUS	10
     69       1.1      jtc 
     70       1.1      jtc #define QMAX 50
     71       1.1      jtc 
     72       1.1      jtc /*
     73       1.1      jtc ** The following is derived from ucb/lpd/displayq.c
     74       1.1      jtc */
     75       1.1      jtc #define SIZECOL 62
     76       1.1      jtc #define FILECOL 24
     77       1.1      jtc 
     78       1.5    lukem char   *expand_alias __P((char *, char *, char *, char *));
     79       1.5    lukem pr_list	list_virtual_printers __P((void));
     80       1.5    lukem char   *map_printer_name __P((char *));
     81       1.9    lukem void	substitute __P((char *, const char *, const char *));
     82       1.5    lukem int	suspicious __P((char *));
     83       1.5    lukem int	valid_pr __P((char *));
     84       1.1      jtc 
     85       1.1      jtc /*
     86       1.1      jtc **---------------------------------------------------------------------
     87       1.1      jtc **                       Misc. variable definitions
     88       1.1      jtc **---------------------------------------------------------------------
     89       1.1      jtc */
     90       1.1      jtc 
     91       1.5    lukem struct stat statbuf;
     92       1.5    lukem char    pathname[MAXPATHLEN];
     93       1.5    lukem char    new_pathname[MAXPATHLEN];
     94       1.5    lukem char    sp_name[MAXPATHLEN] = SPOOLDIR;
     95       1.5    lukem char    tempstr[256];
     96       1.5    lukem char    delims[] = " \t\r\n:()";
     97       1.1      jtc 
     98       1.5    lukem pr_list printers = NULL;
     99       1.5    lukem pr_queue queue = NULL;
    100       1.1      jtc 
    101       1.1      jtc /*
    102       1.1      jtc **=====================================================================
    103       1.1      jtc **                      C O D E   S E C T I O N                       *
    104       1.1      jtc **=====================================================================
    105       1.1      jtc */
    106       1.1      jtc 
    107       1.1      jtc /*
    108       1.1      jtc  * This is the latest word on the security check. The following
    109       1.1      jtc  * routine "suspicious()" returns non-zero if the character string
    110       1.1      jtc  * passed to it contains any shell metacharacters.
    111       1.1      jtc  * Callers will typically code
    112       1.1      jtc  *
    113       1.1      jtc  *	if(suspicious(some_parameter)) reject();
    114       1.1      jtc  */
    115       1.1      jtc 
    116       1.5    lukem int
    117       1.5    lukem suspicious(s)
    118       1.5    lukem 	char   *s;
    119       1.1      jtc {
    120       1.4      gwr 	if (strpbrk(s, ";|&<>`'#!?*()[]^/${}\n\r\"\\:") != NULL)
    121       1.1      jtc 		return 1;
    122       1.1      jtc 	return 0;
    123       1.1      jtc }
    124       1.1      jtc 
    125       1.1      jtc 
    126       1.1      jtc int
    127       1.1      jtc valid_pr(pr)
    128       1.5    lukem 	char   *pr;
    129       1.1      jtc {
    130       1.5    lukem 	char   *p;
    131       1.5    lukem 	pr_list curr;
    132       1.5    lukem 	if (printers == NULL)
    133       1.1      jtc 		build_pr_list();
    134       1.1      jtc 
    135       1.5    lukem 	if (printers == NULL)
    136       1.5    lukem 		return (1);	/* can't tell - assume it's good */
    137       1.1      jtc 
    138       1.1      jtc 	p = map_printer_name(pr);
    139       1.1      jtc 	if (p == NULL)
    140       1.5    lukem 		return (1);	/* must be ok is maps to NULL! */
    141       1.1      jtc 	curr = printers;
    142       1.5    lukem 	while (curr) {
    143       1.5    lukem 		if (!strcmp(p, curr->pn))
    144       1.5    lukem 			return (1);
    145       1.1      jtc 		curr = curr->pr_next;
    146       1.1      jtc 	}
    147       1.5    lukem 
    148       1.5    lukem 	return (0);
    149       1.1      jtc }
    150       1.4      gwr /*
    151       1.4      gwr  * get pathname of current directory and return to client
    152       1.4      gwr  *
    153       1.4      gwr  * Note: This runs as root on behalf of a client request.
    154       1.4      gwr  * As described in CERT advisory CA-96.08, be careful about
    155       1.4      gwr  * doing a chmod on something that could be a symlink...
    156       1.4      gwr  */
    157       1.4      gwr pirstat
    158       1.4      gwr pr_init(sys, pr, sp)
    159       1.5    lukem 	char   *sys;
    160       1.5    lukem 	char   *pr;
    161       1.5    lukem 	char  **sp;
    162       1.1      jtc {
    163       1.5    lukem 	int     dir_mode = 0777;
    164       1.5    lukem 	int     rc;
    165       1.5    lukem 	mode_t  oldmask;
    166       1.1      jtc 
    167       1.1      jtc 	*sp = &pathname[0];
    168       1.1      jtc 	pathname[0] = '\0';
    169       1.1      jtc 
    170       1.5    lukem 	if (suspicious(sys) || suspicious(pr))
    171       1.5    lukem 		return (PI_RES_FAIL);
    172       1.1      jtc 
    173       1.4      gwr 	/*
    174       1.4      gwr 	 * Make sure the server spool directory exists.
    175       1.4      gwr 	 * Never create it here - the sysadmin does that.
    176       1.4      gwr 	 */
    177       1.4      gwr 	if (stat(sp_name, &statbuf) || !S_ISDIR(statbuf.st_mode))
    178       1.4      gwr 		goto badspool;
    179       1.1      jtc 
    180       1.4      gwr 	/*
    181       1.4      gwr 	 * Create the client spool directory if needed.
    182       1.4      gwr 	 * Just do the mkdir call and ignore EEXIST.
    183       1.4      gwr 	 * Mode of client directory should be 777.
    184       1.4      gwr 	 */
    185       1.7   itojun 	(void) snprintf(pathname, sizeof(pathname), "%s/%s", sp_name, sys);
    186       1.4      gwr 	oldmask = umask(0);
    187       1.1      jtc 	rc = mkdir(pathname, dir_mode);	/* DON'T ignore this return code */
    188       1.4      gwr 	umask(oldmask);
    189       1.4      gwr 	if ((rc < 0) && (errno != EEXIST))
    190       1.4      gwr 		goto badspool;
    191       1.4      gwr 
    192       1.4      gwr 	/* By this point the client spool dir should exist. */
    193       1.4      gwr 	if (stat(pathname, &statbuf) || !S_ISDIR(statbuf.st_mode)) {
    194       1.4      gwr 		/* No spool directory... */
    195       1.5    lukem badspool:
    196       1.7   itojun 		(void) snprintf(tempstr, sizeof(tempstr),
    197       1.4      gwr 		    "rpc.pcnfsd: unable to set up spool directory %s\n",
    198       1.5    lukem 		    pathname);
    199       1.4      gwr 		msg_out(tempstr);
    200       1.5    lukem 		pathname[0] = '\0';	/* null to tell client bad vibes */
    201       1.5    lukem 		return (PI_RES_FAIL);
    202       1.4      gwr 	}
    203       1.4      gwr 	/* OK, we have a spool directory. */
    204       1.5    lukem 	if (!valid_pr(pr)) {
    205       1.5    lukem 		pathname[0] = '\0';	/* null to tell client bad vibes */
    206       1.5    lukem 		return (PI_RES_NO_SUCH_PRINTER);
    207       1.4      gwr 	}
    208       1.5    lukem 	return (PI_RES_OK);
    209       1.1      jtc }
    210       1.5    lukem psrstat
    211       1.9    lukem pr_start2(sys, pr, user, fname, opts, id)
    212       1.9    lukem 	char   *sys;
    213       1.5    lukem 	char   *pr;
    214       1.5    lukem 	char   *user;
    215       1.5    lukem 	char   *fname;
    216       1.5    lukem 	char   *opts;
    217       1.5    lukem 	char  **id;
    218       1.1      jtc {
    219       1.5    lukem 	char    snum[20];
    220       1.5    lukem 	static char req_id[256];
    221       1.5    lukem 	char    cmdbuf[256];
    222       1.5    lukem 	char    resbuf[256];
    223       1.5    lukem 	FILE   *fd;
    224       1.5    lukem 	int     i;
    225       1.5    lukem 	char   *xcmd;
    226       1.5    lukem 	int     failed = 0;
    227       1.1      jtc 
    228       1.1      jtc #ifdef HACK_FOR_ROTATED_TRANSCRIPT
    229       1.5    lukem 	char    scratch[512];
    230       1.1      jtc #endif
    231       1.1      jtc 
    232       1.1      jtc 
    233       1.9    lukem 	if (suspicious(sys) ||
    234       1.5    lukem 	    suspicious(pr) ||
    235       1.5    lukem 	    suspicious(user) ||
    236       1.5    lukem 	    suspicious(fname))
    237       1.5    lukem 		return (PS_RES_FAIL);
    238       1.5    lukem 
    239       1.7   itojun 	(void) snprintf(pathname, sizeof(pathname), "%s/%s/%s", sp_name,
    240       1.9    lukem 	    sys,
    241       1.5    lukem 	    fname);
    242       1.1      jtc 
    243       1.1      jtc 	*id = &req_id[0];
    244       1.1      jtc 	req_id[0] = '\0';
    245       1.1      jtc 
    246       1.5    lukem 	if (stat(pathname, &statbuf)) {
    247       1.5    lukem 		/*
    248       1.5    lukem                 **-----------------------------------------------------------------
    249       1.5    lukem 	        ** We can't stat the file. Let's try appending '.spl' and
    250       1.5    lukem 	        ** see if it's already in progress.
    251       1.5    lukem                 **-----------------------------------------------------------------
    252       1.5    lukem 	        */
    253       1.5    lukem 
    254       1.7   itojun 		(void) strlcat(pathname, ".spl", sizeof(pathname));
    255       1.5    lukem 		if (stat(pathname, &statbuf)) {
    256       1.5    lukem 			/*
    257       1.5    lukem 	                **----------------------------------------------------------------
    258       1.5    lukem 		        ** It really doesn't exist.
    259       1.5    lukem 	                **----------------------------------------------------------------
    260       1.5    lukem 		        */
    261       1.5    lukem 
    262       1.5    lukem 
    263       1.5    lukem 			return (PS_RES_NO_FILE);
    264       1.5    lukem 		}
    265       1.5    lukem 		/*
    266       1.5    lukem                 **-------------------------------------------------------------
    267       1.5    lukem 	        ** It is already on the way.
    268       1.5    lukem                 **-------------------------------------------------------------
    269       1.5    lukem 	        */
    270       1.5    lukem 
    271       1.5    lukem 
    272       1.5    lukem 		return (PS_RES_ALREADY);
    273       1.5    lukem 	}
    274       1.5    lukem 	if (statbuf.st_size == 0) {
    275       1.5    lukem 		/*
    276       1.5    lukem                 **-------------------------------------------------------------
    277       1.5    lukem 	        ** Null file - don't print it, just kill it.
    278       1.5    lukem                 **-------------------------------------------------------------
    279       1.5    lukem 	        */
    280       1.5    lukem 		(void) unlink(pathname);
    281       1.5    lukem 
    282       1.5    lukem 		return (PS_RES_NULL);
    283       1.5    lukem 	}
    284       1.5    lukem 	/*
    285       1.5    lukem         **-------------------------------------------------------------
    286       1.5    lukem         ** The file is real, has some data, and is not already going out.
    287       1.5    lukem         ** We rename it by appending '.spl' and exec "lpr" to do the
    288       1.5    lukem         ** actual work.
    289       1.5    lukem         **-------------------------------------------------------------
    290       1.5    lukem         */
    291       1.7   itojun 	(void) strlcpy(new_pathname, pathname, sizeof(new_pathname));
    292       1.7   itojun 	(void) strlcat(new_pathname, ".spl", sizeof(new_pathname));
    293       1.1      jtc 
    294       1.1      jtc 	/*
    295       1.1      jtc         **-------------------------------------------------------------
    296       1.1      jtc 	** See if the new filename exists so as not to overwrite it.
    297       1.1      jtc         **-------------------------------------------------------------
    298       1.1      jtc 	*/
    299       1.1      jtc 
    300       1.1      jtc 
    301       1.5    lukem 	if (!stat(new_pathname, &statbuf)) {
    302       1.7   itojun 		(void) strlcpy(new_pathname, pathname, sizeof(new_pathname)); /* rebuild a new name */
    303       1.7   itojun 		(void) snprintf(snum, sizeof(snum), "%d", rand()); /* get some number */
    304       1.7   itojun 		(void) strlcat(new_pathname, snum, 4);
    305       1.7   itojun 		(void) strlcat(new_pathname, ".spl", sizeof(new_pathname)); /* new spool file */
    306       1.5    lukem 	}
    307       1.5    lukem 	if (rename(pathname, new_pathname)) {
    308       1.5    lukem 		/*
    309       1.5    lukem                 **---------------------------------------------------------------
    310       1.5    lukem 	        ** Should never happen.
    311       1.5    lukem                 **---------------------------------------------------------------
    312       1.5    lukem                 */
    313       1.7   itojun 		(void) snprintf(tempstr, sizeof(tempstr),
    314       1.7   itojun 		    "rpc.pcnfsd: spool file rename (%s->%s) failed.\n",
    315       1.5    lukem 		    pathname, new_pathname);
    316       1.5    lukem 		msg_out(tempstr);
    317       1.5    lukem 		return (PS_RES_FAIL);
    318       1.5    lukem 	}
    319       1.5    lukem 	if (*opts == 'd') {
    320       1.5    lukem 		/*
    321       1.5    lukem 		 **------------------------------------------------------
    322       1.5    lukem 		 ** This is a Diablo print stream. Apply the ps630
    323       1.5    lukem 		 ** filter with the appropriate arguments.
    324       1.5    lukem 		 **------------------------------------------------------
    325       1.5    lukem 		 */
    326       1.5    lukem #if 0				/* XXX: Temporary fix for CERT advisory
    327       1.5    lukem 				 * CA-96.08 */
    328       1.5    lukem 		(void) run_ps630(new_pathname, opts);
    329       1.4      gwr #else
    330       1.7   itojun 		(void) snprintf(tempstr, sizeof(tempstr),
    331       1.5    lukem 		    "rpc.pcnfsd: ps630 filter disabled for %s\n", pathname);
    332       1.5    lukem 		msg_out(tempstr);
    333       1.5    lukem 		return (PS_RES_FAIL);
    334       1.4      gwr #endif
    335       1.5    lukem 	}
    336       1.5    lukem 	/*
    337       1.5    lukem 	** Try to match to an aliased printer
    338       1.5    lukem 	*/
    339       1.9    lukem 	xcmd = expand_alias(pr, new_pathname, user, sys);
    340       1.5    lukem 	if (!xcmd) {
    341       1.5    lukem #ifdef	SVR4
    342       1.1      jtc 		/*
    343       1.3      gwr 			 * Use the copy option so we can remove the orignal
    344       1.3      gwr 			 * spooled nfs file from the spool directory.
    345       1.3      gwr 			 */
    346       1.7   itojun 		snprintf(cmdbuf, sizeof(cmdbuf), "/usr/bin/lp -c -d%s %s",
    347       1.5    lukem 		    pr, new_pathname);
    348       1.5    lukem #else				/* SVR4 */
    349       1.5    lukem 		/* BSD way: lpr */
    350       1.7   itojun 		snprintf(cmdbuf, sizeof(cmdbuf), "%s/lpr -P%s %s",
    351       1.5    lukem 		    LPRDIR, pr, new_pathname);
    352       1.5    lukem #endif				/* SVR4 */
    353       1.5    lukem 		xcmd = cmdbuf;
    354       1.5    lukem 	}
    355       1.5    lukem 	if ((fd = su_popen(user, xcmd, MAXTIME_FOR_PRINT)) == NULL) {
    356       1.5    lukem 		msg_out("rpc.pcnfsd: su_popen failed");
    357       1.5    lukem 		return (PS_RES_FAIL);
    358       1.5    lukem 	}
    359      1.12  mbalmer 	req_id[0] = '\0';	/* assume failure */
    360       1.5    lukem 	while (fgets(resbuf, 255, fd) != NULL) {
    361       1.5    lukem 		i = strlen(resbuf);
    362       1.5    lukem 		if (i)
    363       1.5    lukem 			resbuf[i - 1] = '\0';	/* trim NL */
    364       1.5    lukem 		if (!strncmp(resbuf, "request id is ", 14))
    365       1.5    lukem 			/* New - just the first word is needed */
    366       1.7   itojun 			strlcpy(req_id, strtok(&resbuf[14], delims),
    367       1.7   itojun 			    sizeof(req_id));
    368       1.5    lukem 		else
    369       1.5    lukem 			if (strembedded("disabled", resbuf))
    370       1.1      jtc 				failed = 1;
    371       1.5    lukem 	}
    372       1.5    lukem 	if (su_pclose(fd) == 255)
    373       1.5    lukem 		msg_out("rpc.pcnfsd: su_pclose alert");
    374       1.5    lukem 	(void) unlink(new_pathname);
    375       1.5    lukem 	return ((failed | interrupted) ? PS_RES_FAIL : PS_RES_OK);
    376       1.1      jtc }
    377       1.1      jtc /*
    378       1.3      gwr  * build_pr_list: determine which printers are valid.
    379       1.3      gwr  * on SVR4 use "lpstat -v"
    380       1.1      jtc  * on BSD use "lpc status"
    381       1.3      gwr  */
    382       1.3      gwr 
    383       1.3      gwr #ifdef	SVR4
    384       1.3      gwr /*
    385       1.3      gwr  * In SVR4 the command to determine which printers are
    386       1.3      gwr  * valid is lpstat -v. The output is something like this:
    387       1.3      gwr  *
    388       1.3      gwr  * device for lp: /dev/lp0
    389       1.3      gwr  * system for pcdslw: hinode
    390       1.3      gwr  * system for bletch: hinode (as printer hisname)
    391       1.3      gwr  *
    392       1.3      gwr  * On SunOS using the SysV compatibility package, the output
    393       1.3      gwr  * is more like:
    394       1.3      gwr  *
    395       1.3      gwr  * device for lp is /dev/lp0
    396       1.3      gwr  * device for pcdslw is the remote printer pcdslw on hinode
    397       1.3      gwr  * device for bletch is the remote printer hisname on hinode
    398       1.3      gwr  *
    399       1.3      gwr  * It is fairly simple to create logic that will handle either
    400       1.3      gwr  * possibility:
    401       1.3      gwr  */
    402       1.3      gwr int
    403       1.3      gwr build_pr_list()
    404       1.3      gwr {
    405       1.3      gwr 	pr_list last = NULL;
    406       1.3      gwr 	pr_list curr = NULL;
    407       1.5    lukem 	char    buff[256];
    408       1.5    lukem 	FILE   *p;
    409       1.5    lukem 	char   *cp;
    410       1.5    lukem 	int     saw_system;
    411       1.3      gwr 
    412       1.3      gwr 	p = popen("lpstat -v", "r");
    413       1.5    lukem 	if (p == NULL) {
    414       1.3      gwr 		msg_out("rpc.pcnfsd: unable to popen() lp status");
    415       1.5    lukem 		return (0);
    416       1.3      gwr 	}
    417       1.5    lukem 	while (fgets(buff, 255, p) != NULL) {
    418       1.3      gwr 		cp = strtok(buff, delims);
    419       1.5    lukem 		if (!cp)
    420       1.3      gwr 			continue;
    421       1.5    lukem 		if (!strcmp(cp, "device"))
    422       1.3      gwr 			saw_system = 0;
    423       1.3      gwr 		else
    424       1.5    lukem 			if (!strcmp(cp, "system"))
    425       1.5    lukem 				saw_system = 1;
    426       1.5    lukem 			else
    427       1.5    lukem 				continue;
    428       1.3      gwr 		cp = strtok(NULL, delims);
    429       1.5    lukem 		if (!cp || strcmp(cp, "for"))
    430       1.3      gwr 			continue;
    431       1.3      gwr 		cp = strtok(NULL, delims);
    432       1.5    lukem 		if (!cp)
    433       1.3      gwr 			continue;
    434       1.3      gwr 		curr = (struct pr_list_item *)
    435       1.5    lukem 		    grab(sizeof(struct pr_list_item));
    436       1.3      gwr 
    437       1.3      gwr 		curr->pn = strdup(cp);
    438       1.3      gwr 		curr->device = NULL;
    439       1.3      gwr 		curr->remhost = NULL;
    440       1.3      gwr 		curr->cm = strdup("-");
    441       1.3      gwr 		curr->pr_next = NULL;
    442       1.3      gwr 
    443       1.3      gwr 		cp = strtok(NULL, delims);
    444       1.3      gwr 
    445       1.5    lukem 		if (cp && !strcmp(cp, "is"))
    446       1.3      gwr 			cp = strtok(NULL, delims);
    447       1.3      gwr 
    448       1.5    lukem 		if (!cp) {
    449       1.3      gwr 			free_pr_list_item(curr);
    450       1.3      gwr 			continue;
    451       1.3      gwr 		}
    452       1.5    lukem 		if (saw_system) {
    453       1.5    lukem 			/* "system" OR "system (as printer pname)" */
    454       1.3      gwr 			curr->remhost = strdup(cp);
    455       1.3      gwr 			cp = strtok(NULL, delims);
    456       1.5    lukem 			if (!cp) {
    457       1.3      gwr 				/* simple format */
    458       1.3      gwr 				curr->device = strdup(curr->pn);
    459       1.3      gwr 			} else {
    460       1.3      gwr 				/* "sys (as printer pname)" */
    461       1.3      gwr 				if (strcmp(cp, "as")) {
    462       1.3      gwr 					free_pr_list_item(curr);
    463       1.3      gwr 					continue;
    464       1.3      gwr 				}
    465       1.3      gwr 				cp = strtok(NULL, delims);
    466       1.3      gwr 				if (!cp || strcmp(cp, "printer")) {
    467       1.3      gwr 					free_pr_list_item(curr);
    468       1.3      gwr 					continue;
    469       1.3      gwr 				}
    470       1.3      gwr 				cp = strtok(NULL, delims);
    471       1.5    lukem 				if (!cp) {
    472       1.3      gwr 					free_pr_list_item(curr);
    473       1.3      gwr 					continue;
    474       1.3      gwr 				}
    475       1.3      gwr 				curr->device = strdup(cp);
    476       1.3      gwr 			}
    477       1.5    lukem 		} else
    478       1.5    lukem 			if (!strcmp(cp, "the")) {
    479       1.5    lukem 				/* start of "the remote printer foo on bar" */
    480       1.5    lukem 				cp = strtok(NULL, delims);
    481       1.5    lukem 				if (!cp || strcmp(cp, "remote")) {
    482       1.5    lukem 					free_pr_list_item(curr);
    483       1.5    lukem 					continue;
    484       1.5    lukem 				}
    485       1.5    lukem 				cp = strtok(NULL, delims);
    486       1.5    lukem 				if (!cp || strcmp(cp, "printer")) {
    487       1.5    lukem 					free_pr_list_item(curr);
    488       1.5    lukem 					continue;
    489       1.5    lukem 				}
    490       1.5    lukem 				cp = strtok(NULL, delims);
    491       1.5    lukem 				if (!cp) {
    492       1.5    lukem 					free_pr_list_item(curr);
    493       1.5    lukem 					continue;
    494       1.5    lukem 				}
    495       1.5    lukem 				curr->device = strdup(cp);
    496       1.5    lukem 				cp = strtok(NULL, delims);
    497       1.5    lukem 				if (!cp || strcmp(cp, "on")) {
    498       1.5    lukem 					free_pr_list_item(curr);
    499       1.5    lukem 					continue;
    500       1.5    lukem 				}
    501       1.5    lukem 				cp = strtok(NULL, delims);
    502       1.5    lukem 				if (!cp) {
    503       1.5    lukem 					free_pr_list_item(curr);
    504       1.5    lukem 					continue;
    505       1.5    lukem 				}
    506       1.5    lukem 				curr->remhost = strdup(cp);
    507       1.5    lukem 			} else {
    508       1.5    lukem 				/* the local name */
    509       1.5    lukem 				curr->device = strdup(cp);
    510       1.5    lukem 				curr->remhost = strdup("");
    511       1.3      gwr 			}
    512       1.3      gwr 
    513       1.5    lukem 		if (last == NULL)
    514       1.3      gwr 			printers = curr;
    515       1.3      gwr 		else
    516       1.3      gwr 			last->pr_next = curr;
    517       1.3      gwr 		last = curr;
    518       1.3      gwr 
    519       1.3      gwr 	}
    520       1.3      gwr 	(void) pclose(p);
    521       1.3      gwr 
    522       1.3      gwr 	/*
    523       1.3      gwr 	 ** Now add on the virtual printers, if any
    524       1.3      gwr 	 */
    525       1.5    lukem 	if (last == NULL)
    526       1.3      gwr 		printers = list_virtual_printers();
    527       1.3      gwr 	else
    528       1.3      gwr 		last->pr_next = list_virtual_printers();
    529       1.3      gwr 
    530       1.5    lukem 	return (1);
    531       1.3      gwr }
    532       1.5    lukem #else				/* SVR4 */
    533       1.3      gwr 
    534       1.3      gwr /*
    535       1.3      gwr  * BSD way: lpc stat
    536       1.1      jtc  */
    537       1.1      jtc int
    538       1.1      jtc build_pr_list()
    539       1.1      jtc {
    540       1.1      jtc 	pr_list last = NULL;
    541       1.1      jtc 	pr_list curr = NULL;
    542       1.5    lukem 	char    buff[256];
    543       1.5    lukem 	FILE   *p;
    544       1.5    lukem 	char   *cp;
    545       1.1      jtc 
    546       1.7   itojun 	snprintf(buff, sizeof(buff), "%s/lpc status", LPCDIR);
    547       1.1      jtc 	p = popen(buff, "r");
    548       1.5    lukem 	if (p == NULL) {
    549       1.1      jtc 		msg_out("rpc.pcnfsd: unable to popen lpc stat");
    550       1.5    lukem 		return (0);
    551       1.1      jtc 	}
    552       1.5    lukem 	while (fgets(buff, 255, p) != NULL) {
    553       1.8      dsl 		if (isspace((unsigned char)buff[0]))
    554       1.1      jtc 			continue;
    555       1.1      jtc 
    556       1.1      jtc 		if ((cp = strtok(buff, delims)) == NULL)
    557       1.1      jtc 			continue;
    558       1.1      jtc 
    559       1.1      jtc 		curr = (struct pr_list_item *)
    560       1.5    lukem 		    grab(sizeof(struct pr_list_item));
    561       1.1      jtc 
    562       1.1      jtc 		/* XXX - Should distinguish remote printers. */
    563       1.1      jtc 		curr->pn = strdup(cp);
    564       1.1      jtc 		curr->device = strdup(cp);
    565       1.1      jtc 		curr->remhost = strdup("");
    566       1.1      jtc 		curr->cm = strdup("-");
    567       1.1      jtc 		curr->pr_next = NULL;
    568       1.1      jtc 
    569       1.5    lukem 		if (last == NULL)
    570       1.1      jtc 			printers = curr;
    571       1.1      jtc 		else
    572       1.1      jtc 			last->pr_next = curr;
    573       1.1      jtc 		last = curr;
    574       1.1      jtc 
    575       1.1      jtc 	}
    576      1.11      wiz 	(void) pclose(p);
    577       1.1      jtc 
    578       1.1      jtc 	/*
    579       1.1      jtc 	 ** Now add on the virtual printers, if any
    580       1.1      jtc 	 */
    581       1.5    lukem 	if (last == NULL)
    582       1.1      jtc 		printers = list_virtual_printers();
    583       1.1      jtc 	else
    584       1.1      jtc 		last->pr_next = list_virtual_printers();
    585       1.1      jtc 
    586       1.5    lukem 	return (1);
    587       1.1      jtc }
    588       1.5    lukem #endif				/* SVR4 */
    589       1.1      jtc 
    590       1.5    lukem void   *
    591       1.5    lukem grab(n)
    592       1.5    lukem 	int     n;
    593       1.1      jtc {
    594       1.5    lukem 	void   *p;
    595       1.1      jtc 
    596       1.5    lukem 	p = (void *) malloc(n);
    597       1.5    lukem 	if (p == NULL) {
    598       1.1      jtc 		msg_out("rpc.pcnfsd: malloc failure");
    599       1.1      jtc 		exit(1);
    600       1.1      jtc 	}
    601       1.5    lukem 	return (p);
    602       1.1      jtc }
    603       1.1      jtc 
    604       1.1      jtc void
    605       1.1      jtc free_pr_list_item(curr)
    606       1.5    lukem 	pr_list curr;
    607       1.1      jtc {
    608       1.5    lukem 	if (curr->pn)
    609       1.1      jtc 		free(curr->pn);
    610       1.5    lukem 	if (curr->device)
    611       1.1      jtc 		free(curr->device);
    612       1.5    lukem 	if (curr->remhost)
    613       1.1      jtc 		free(curr->remhost);
    614       1.5    lukem 	if (curr->cm)
    615       1.1      jtc 		free(curr->cm);
    616       1.5    lukem 	if (curr->pr_next)
    617       1.5    lukem 		free_pr_list_item(curr->pr_next);	/* recurse */
    618       1.1      jtc 	free(curr);
    619       1.1      jtc }
    620       1.1      jtc /*
    621       1.3      gwr  * build_pr_queue:  used to show the print queue.
    622       1.3      gwr  *
    623       1.3      gwr  * Note that the first thing we do is to discard any
    624       1.3      gwr  * existing queue.
    625       1.3      gwr  */
    626       1.3      gwr #ifdef SVR4
    627       1.3      gwr 
    628       1.3      gwr /*
    629       1.3      gwr ** In SVR4 the command to list the print jobs for printer
    630       1.3      gwr ** lp is "lpstat lp" (or, equivalently, "lpstat -p lp").
    631       1.3      gwr ** The output looks like this:
    632       1.5    lukem **
    633       1.3      gwr ** lp-2                    root               939   Jul 10 21:56
    634       1.3      gwr ** lp-5                    geoff               15   Jul 12 23:23
    635       1.3      gwr ** lp-6                    geoff               15   Jul 12 23:23
    636       1.5    lukem **
    637       1.3      gwr ** If the first job is actually printing the first line
    638       1.3      gwr ** is modified, as follows:
    639       1.1      jtc **
    640       1.3      gwr ** lp-2                    root               939   Jul 10 21:56 on lp
    641       1.5    lukem **
    642       1.3      gwr ** I don't yet have any info on what it looks like if the printer
    643       1.3      gwr ** is remote and we're spooling over the net. However for
    644       1.3      gwr ** the purposes of rpc.pcnfsd we can simply say that field 1 is the
    645       1.3      gwr ** job ID, field 2 is the submitter, and field 3 is the size.
    646       1.3      gwr ** We can check for the presence of the string " on " in the
    647       1.3      gwr ** first record to determine if we should count it as rank 0 or rank 1,
    648       1.3      gwr ** but it won't hurt if we get it wrong.
    649       1.3      gwr **/
    650       1.3      gwr 
    651       1.3      gwr pirstat
    652       1.3      gwr build_pr_queue(pn, user, just_mine, p_qlen, p_qshown)
    653       1.5    lukem 	printername pn;
    654       1.5    lukem 	username user;
    655       1.5    lukem 	int     just_mine;
    656       1.5    lukem 	int    *p_qlen;
    657       1.5    lukem 	int    *p_qshown;
    658       1.3      gwr {
    659       1.5    lukem 	pr_queue last = NULL;
    660       1.5    lukem 	pr_queue curr = NULL;
    661       1.5    lukem 	char    buff[256];
    662       1.5    lukem 	FILE   *p;
    663       1.5    lukem 	char   *owner;
    664       1.5    lukem 	char   *job;
    665       1.5    lukem 	char   *totsize;
    666       1.3      gwr 
    667       1.5    lukem 	if (queue) {
    668       1.3      gwr 		free_pr_queue_item(queue);
    669       1.3      gwr 		queue = NULL;
    670       1.3      gwr 	}
    671       1.3      gwr 	*p_qlen = 0;
    672       1.3      gwr 	*p_qshown = 0;
    673       1.3      gwr 
    674       1.3      gwr 	pn = map_printer_name(pn);
    675       1.5    lukem 	if (pn == NULL || !valid_pr(pn) || suspicious(pn))
    676       1.5    lukem 		return (PI_RES_NO_SUCH_PRINTER);
    677       1.3      gwr 
    678       1.7   itojun 	snprintf(buff, sizeof(buff), "/usr/bin/lpstat %s", pn);
    679       1.3      gwr 	p = su_popen(user, buff, MAXTIME_FOR_QUEUE);
    680       1.5    lukem 	if (p == NULL) {
    681       1.3      gwr 		msg_out("rpc.pcnfsd: unable to popen() lpstat queue query");
    682       1.5    lukem 		return (PI_RES_FAIL);
    683       1.3      gwr 	}
    684       1.5    lukem 	while (fgets(buff, 255, p) != NULL) {
    685       1.3      gwr 		job = strtok(buff, delims);
    686       1.5    lukem 		if (!job)
    687       1.3      gwr 			continue;
    688       1.3      gwr 
    689       1.3      gwr 		owner = strtok(NULL, delims);
    690       1.5    lukem 		if (!owner)
    691       1.3      gwr 			continue;
    692       1.3      gwr 
    693       1.3      gwr 		totsize = strtok(NULL, delims);
    694       1.5    lukem 		if (!totsize)
    695       1.3      gwr 			continue;
    696       1.3      gwr 
    697       1.3      gwr 		*p_qlen += 1;
    698       1.3      gwr 
    699       1.5    lukem 		if (*p_qshown > QMAX)
    700       1.3      gwr 			continue;
    701       1.3      gwr 
    702       1.5    lukem 		if (just_mine && strcasecmp(owner, user))
    703       1.3      gwr 			continue;
    704       1.3      gwr 
    705       1.3      gwr 		*p_qshown += 1;
    706       1.3      gwr 
    707       1.3      gwr 		curr = (struct pr_queue_item *)
    708       1.5    lukem 		    grab(sizeof(struct pr_queue_item));
    709       1.3      gwr 
    710       1.3      gwr 		curr->position = *p_qlen;
    711       1.3      gwr 		curr->id = strdup(job);
    712       1.3      gwr 		curr->size = strdup(totsize);
    713       1.3      gwr 		curr->status = strdup("");
    714       1.3      gwr 		curr->system = strdup("");
    715       1.3      gwr 		curr->user = strdup(owner);
    716       1.3      gwr 		curr->file = strdup("");
    717       1.3      gwr 		curr->cm = strdup("-");
    718       1.3      gwr 		curr->pr_next = NULL;
    719       1.3      gwr 
    720       1.5    lukem 		if (last == NULL)
    721       1.3      gwr 			queue = curr;
    722       1.3      gwr 		else
    723       1.3      gwr 			last->pr_next = curr;
    724       1.3      gwr 		last = curr;
    725       1.3      gwr 
    726       1.3      gwr 	}
    727       1.3      gwr 	(void) su_pclose(p);
    728       1.5    lukem 	return (PI_RES_OK);
    729       1.3      gwr }
    730       1.5    lukem #else				/* SVR4 */
    731       1.1      jtc 
    732       1.1      jtc pirstat
    733       1.1      jtc build_pr_queue(pn, user, just_mine, p_qlen, p_qshown)
    734       1.5    lukem 	printername pn;
    735       1.5    lukem 	username user;
    736       1.5    lukem 	int     just_mine;
    737       1.5    lukem 	int    *p_qlen;
    738       1.5    lukem 	int    *p_qshown;
    739       1.1      jtc {
    740       1.5    lukem 	pr_queue last = NULL;
    741       1.5    lukem 	pr_queue curr = NULL;
    742       1.5    lukem 	char    buff[256];
    743       1.5    lukem 	FILE   *p;
    744       1.5    lukem 	char   *cp;
    745       1.5    lukem 	int     i;
    746       1.5    lukem 	char   *rank;
    747       1.5    lukem 	char   *owner;
    748       1.5    lukem 	char   *job;
    749       1.5    lukem 	char   *files;
    750       1.5    lukem 	char   *totsize;
    751       1.1      jtc 
    752       1.5    lukem 	if (queue) {
    753       1.1      jtc 		free_pr_queue_item(queue);
    754       1.1      jtc 		queue = NULL;
    755       1.1      jtc 	}
    756       1.1      jtc 	*p_qlen = 0;
    757       1.1      jtc 	*p_qshown = 0;
    758       1.1      jtc 	pn = map_printer_name(pn);
    759       1.5    lukem 	if (pn == NULL || suspicious(pn))
    760       1.5    lukem 		return (PI_RES_NO_SUCH_PRINTER);
    761       1.1      jtc 
    762       1.7   itojun 	snprintf(buff, sizeof(buff), "%s/lpq -P%s", LPRDIR, pn);
    763       1.1      jtc 
    764       1.1      jtc 	p = su_popen(user, buff, MAXTIME_FOR_QUEUE);
    765       1.5    lukem 	if (p == NULL) {
    766       1.1      jtc 		msg_out("rpc.pcnfsd: unable to popen() lpq");
    767       1.5    lukem 		return (PI_RES_FAIL);
    768       1.1      jtc 	}
    769       1.5    lukem 	while (fgets(buff, 255, p) != NULL) {
    770       1.1      jtc 		i = strlen(buff) - 1;
    771       1.5    lukem 		buff[i] = '\0';	/* zap trailing NL */
    772       1.5    lukem 		if (i < SIZECOL)
    773       1.1      jtc 			continue;
    774       1.5    lukem 		if (!strncasecmp(buff, "rank", 4))
    775       1.1      jtc 			continue;
    776       1.1      jtc 
    777       1.5    lukem 		totsize = &buff[SIZECOL - 1];
    778       1.5    lukem 		files = &buff[FILECOL - 1];
    779       1.1      jtc 		cp = totsize;
    780       1.1      jtc 		cp--;
    781       1.8      dsl 		while (cp > files && isspace((unsigned char)*cp))
    782       1.1      jtc 			*cp-- = '\0';
    783       1.1      jtc 
    784       1.5    lukem 		buff[FILECOL - 2] = '\0';
    785       1.1      jtc 
    786       1.1      jtc 		cp = strtok(buff, delims);
    787       1.5    lukem 		if (!cp)
    788       1.1      jtc 			continue;
    789       1.1      jtc 		rank = cp;
    790       1.1      jtc 
    791       1.1      jtc 		cp = strtok(NULL, delims);
    792       1.5    lukem 		if (!cp)
    793       1.1      jtc 			continue;
    794       1.1      jtc 		owner = cp;
    795       1.1      jtc 
    796       1.1      jtc 		cp = strtok(NULL, delims);
    797       1.5    lukem 		if (!cp)
    798       1.1      jtc 			continue;
    799       1.1      jtc 		job = cp;
    800       1.1      jtc 
    801       1.1      jtc 		*p_qlen += 1;
    802       1.1      jtc 
    803       1.5    lukem 		if (*p_qshown > QMAX)
    804       1.1      jtc 			continue;
    805       1.1      jtc 
    806       1.5    lukem 		if (just_mine && strcasecmp(owner, user))
    807       1.1      jtc 			continue;
    808       1.1      jtc 
    809       1.1      jtc 		*p_qshown += 1;
    810       1.1      jtc 
    811       1.1      jtc 		curr = (struct pr_queue_item *)
    812       1.5    lukem 		    grab(sizeof(struct pr_queue_item));
    813       1.1      jtc 
    814       1.5    lukem 		curr->position = atoi(rank);	/* active -> 0 */
    815       1.1      jtc 		curr->id = strdup(job);
    816       1.1      jtc 		curr->size = strdup(totsize);
    817       1.1      jtc 		curr->status = strdup(rank);
    818       1.1      jtc 		curr->system = strdup("");
    819       1.1      jtc 		curr->user = strdup(owner);
    820       1.1      jtc 		curr->file = strdup(files);
    821       1.1      jtc 		curr->cm = strdup("-");
    822       1.1      jtc 		curr->pr_next = NULL;
    823       1.1      jtc 
    824       1.5    lukem 		if (last == NULL)
    825       1.1      jtc 			queue = curr;
    826       1.1      jtc 		else
    827       1.1      jtc 			last->pr_next = curr;
    828       1.1      jtc 		last = curr;
    829       1.1      jtc 
    830       1.1      jtc 	}
    831       1.1      jtc 	(void) su_pclose(p);
    832       1.5    lukem 	return (PI_RES_OK);
    833       1.1      jtc }
    834       1.5    lukem #endif				/* SVR4 */
    835       1.3      gwr 
    836       1.1      jtc void
    837       1.1      jtc free_pr_queue_item(curr)
    838       1.5    lukem 	pr_queue curr;
    839       1.1      jtc {
    840       1.5    lukem 	if (curr->id)
    841       1.1      jtc 		free(curr->id);
    842       1.5    lukem 	if (curr->size)
    843       1.1      jtc 		free(curr->size);
    844       1.5    lukem 	if (curr->status)
    845       1.1      jtc 		free(curr->status);
    846       1.5    lukem 	if (curr->system)
    847       1.1      jtc 		free(curr->system);
    848       1.5    lukem 	if (curr->user)
    849       1.1      jtc 		free(curr->user);
    850       1.5    lukem 	if (curr->file)
    851       1.1      jtc 		free(curr->file);
    852       1.5    lukem 	if (curr->cm)
    853       1.1      jtc 		free(curr->cm);
    854       1.5    lukem 	if (curr->pr_next)
    855       1.5    lukem 		free_pr_queue_item(curr->pr_next);	/* recurse */
    856       1.1      jtc 	free(curr);
    857       1.1      jtc }
    858       1.3      gwr #ifdef SVR4
    859       1.1      jtc 
    860       1.3      gwr /*
    861       1.3      gwr ** New - SVR4 printer status handling.
    862       1.3      gwr **
    863       1.3      gwr ** The command we'll use for checking the status of printer "lp"
    864       1.3      gwr ** is "lpstat -a lp -p lp". Here are some sample outputs:
    865       1.3      gwr **
    866       1.5    lukem **
    867       1.3      gwr ** lp accepting requests since Wed Jul 10 21:49:25 EDT 1991
    868       1.3      gwr ** printer lp disabled since Thu Feb 21 22:52:36 EST 1991. available.
    869       1.3      gwr ** 	new printer
    870       1.3      gwr ** ---
    871       1.3      gwr ** pcdslw not accepting requests since Fri Jul 12 22:30:00 EDT 1991 -
    872       1.3      gwr ** 	unknown reason
    873       1.3      gwr ** printer pcdslw disabled since Fri Jul 12 22:15:37 EDT 1991. available.
    874       1.3      gwr ** 	new printer
    875       1.3      gwr ** ---
    876       1.3      gwr ** lp accepting requests since Wed Jul 10 21:49:25 EDT 1991
    877       1.3      gwr ** printer lp now printing lp-2. enabled since Sat Jul 13 12:02:17 EDT 1991. available.
    878       1.3      gwr ** ---
    879       1.3      gwr ** lp accepting requests since Wed Jul 10 21:49:25 EDT 1991
    880       1.3      gwr ** printer lp now printing lp-2. enabled since Sat Jul 13 12:02:17 EDT 1991. available.
    881       1.3      gwr ** ---
    882       1.3      gwr ** lp accepting requests since Wed Jul 10 21:49:25 EDT 1991
    883       1.3      gwr ** printer lp disabled since Sat Jul 13 12:05:20 EDT 1991. available.
    884       1.3      gwr ** 	unknown reason
    885       1.3      gwr ** ---
    886       1.3      gwr ** pcdslw not accepting requests since Fri Jul 12 22:30:00 EDT 1991 -
    887       1.3      gwr ** 	unknown reason
    888       1.3      gwr ** printer pcdslw is idle. enabled since Sat Jul 13 12:05:28 EDT 1991. available.
    889       1.3      gwr **
    890       1.3      gwr ** Note that these are actual outputs. The format (which is totally
    891       1.3      gwr ** different from the lpstat in SunOS) seems to break down as
    892       1.3      gwr ** follows:
    893       1.3      gwr ** (1) The first line has the form "printername [not] accepting requests,,,"
    894       1.3      gwr **    This is trivial to decode.
    895       1.3      gwr ** (2) The second line has several forms, all beginning "printer printername":
    896       1.3      gwr ** (2.1) "... disabled"
    897       1.3      gwr ** (2.2) "... is idle"
    898       1.3      gwr ** (2.3) "... now printing jobid"
    899       1.3      gwr ** The "available" comment seems to be meaningless. The next line
    900       1.3      gwr ** is the "reason" code which the operator can supply when issuing
    901       1.3      gwr ** a "disable" or "reject" command.
    902       1.3      gwr ** Note that there is no way to check the number of entries in the
    903       1.3      gwr ** queue except to ask for the queue and count them.
    904       1.3      gwr */
    905       1.1      jtc 
    906       1.1      jtc pirstat
    907  1.12.2.1     yamt get_pr_status(pn, avail, printing, qlen, needs_operator, status, statuslen)
    908       1.5    lukem 	printername pn;
    909       1.5    lukem 	bool_t *avail;
    910       1.5    lukem 	bool_t *printing;
    911       1.5    lukem 	int    *qlen;
    912       1.5    lukem 	bool_t *needs_operator;
    913       1.5    lukem 	char   *status;
    914  1.12.2.1     yamt 	size_t statuslen;
    915       1.1      jtc {
    916       1.5    lukem 	char    buff[256];
    917       1.5    lukem 	char    cmd[64];
    918       1.5    lukem 	FILE   *p;
    919       1.5    lukem 	int     n;
    920       1.5    lukem 	pirstat stat = PI_RES_NO_SUCH_PRINTER;
    921       1.1      jtc 
    922       1.1      jtc 	/* assume the worst */
    923       1.1      jtc 	*avail = FALSE;
    924       1.1      jtc 	*printing = FALSE;
    925       1.1      jtc 	*needs_operator = FALSE;
    926       1.1      jtc 	*qlen = 0;
    927       1.1      jtc 	*status = '\0';
    928       1.1      jtc 
    929       1.1      jtc 	pn = map_printer_name(pn);
    930       1.5    lukem 	if (pn == NULL || !valid_pr(pn) || suspicious(pn))
    931       1.5    lukem 		return (PI_RES_NO_SUCH_PRINTER);
    932       1.3      gwr 	n = strlen(pn);
    933       1.3      gwr 
    934       1.7   itojun 	snprintf(cmd, sizeof(cmd), "/usr/bin/lpstat -a %s -p %s", pn, pn);
    935       1.3      gwr 
    936       1.3      gwr 	p = popen(cmd, "r");
    937       1.5    lukem 	if (p == NULL) {
    938       1.3      gwr 		msg_out("rpc.pcnfsd: unable to popen() lp status");
    939       1.5    lukem 		return (PI_RES_FAIL);
    940       1.3      gwr 	}
    941       1.3      gwr 	stat = PI_RES_OK;
    942       1.3      gwr 
    943       1.5    lukem 	while (fgets(buff, 255, p) != NULL) {
    944       1.5    lukem 		if (!strncmp(buff, pn, n)) {
    945       1.5    lukem 			if (!strstr(buff, "not accepting"))
    946       1.5    lukem 				*avail = TRUE;
    947       1.3      gwr 			continue;
    948       1.3      gwr 		}
    949       1.5    lukem 		if (!strncmp(buff, "printer ", 8)) {
    950       1.5    lukem 			if (!strstr(buff, "disabled"))
    951       1.3      gwr 				*printing = TRUE;
    952       1.5    lukem 			if (strstr(buff, "printing"))
    953  1.12.2.1     yamt 				strlcpy(status, "printing", statuslen);
    954       1.5    lukem 			else
    955       1.5    lukem 				if (strstr(buff, "idle"))
    956  1.12.2.1     yamt 					strlcpy(status, "idle", statuslen);
    957       1.3      gwr 			continue;
    958       1.3      gwr 		}
    959       1.5    lukem 		if (!strncmp(buff, "UX:", 3)) {
    960       1.3      gwr 			stat = PI_RES_NO_SUCH_PRINTER;
    961       1.3      gwr 		}
    962       1.3      gwr 	}
    963       1.3      gwr 	(void) pclose(p);
    964       1.5    lukem 	return (stat);
    965       1.3      gwr }
    966       1.5    lukem #else				/* SVR4 */
    967       1.3      gwr 
    968       1.3      gwr /*
    969       1.3      gwr  * BSD way: lpc status
    970       1.3      gwr  */
    971       1.3      gwr pirstat
    972  1.12.2.1     yamt get_pr_status(pn, avail, printing, qlen, needs_operator, status, statuslen)
    973       1.5    lukem 	printername pn;
    974       1.5    lukem 	bool_t *avail;
    975       1.5    lukem 	bool_t *printing;
    976       1.5    lukem 	int    *qlen;
    977       1.5    lukem 	bool_t *needs_operator;
    978       1.5    lukem 	char   *status;
    979  1.12.2.1     yamt 	size_t statuslen;
    980       1.3      gwr {
    981       1.5    lukem 	char    cmd[128];
    982       1.5    lukem 	char    buff[256];
    983       1.5    lukem 	char    buff2[256];
    984       1.5    lukem 	char    pname[64];
    985       1.5    lukem 	FILE   *p;
    986       1.5    lukem 	char   *cp;
    987       1.5    lukem 	char   *cp1;
    988       1.5    lukem 	char   *cp2;
    989       1.5    lukem 	int     n;
    990       1.9    lukem 	pirstat pstat = PI_RES_NO_SUCH_PRINTER;
    991       1.3      gwr 
    992       1.3      gwr 	/* assume the worst */
    993       1.3      gwr 	*avail = FALSE;
    994       1.3      gwr 	*printing = FALSE;
    995       1.3      gwr 	*needs_operator = FALSE;
    996       1.3      gwr 	*qlen = 0;
    997       1.3      gwr 	*status = '\0';
    998       1.3      gwr 
    999       1.3      gwr 	pn = map_printer_name(pn);
   1000       1.5    lukem 	if (pn == NULL || suspicious(pn))
   1001       1.5    lukem 		return (PI_RES_NO_SUCH_PRINTER);
   1002       1.1      jtc 
   1003       1.7   itojun 	snprintf(pname, sizeof(pname), "%s:", pn);
   1004       1.1      jtc 	n = strlen(pname);
   1005       1.1      jtc 
   1006       1.7   itojun 	snprintf(cmd, sizeof(cmd), "%s/lpc status %s", LPCDIR, pn);
   1007       1.1      jtc 	p = popen(cmd, "r");
   1008       1.5    lukem 	if (p == NULL) {
   1009       1.1      jtc 		msg_out("rpc.pcnfsd: unable to popen() lp status");
   1010       1.5    lukem 		return (PI_RES_FAIL);
   1011       1.1      jtc 	}
   1012       1.5    lukem 	while (fgets(buff, 255, p) != NULL) {
   1013       1.5    lukem 		if (strncmp(buff, pname, n))
   1014       1.1      jtc 			continue;
   1015       1.1      jtc /*
   1016       1.1      jtc ** We have a match. The only failure now is PI_RES_FAIL if
   1017       1.1      jtc ** lpstat output cannot be decoded
   1018       1.1      jtc */
   1019       1.9    lukem 		pstat = PI_RES_FAIL;
   1020       1.1      jtc /*
   1021       1.1      jtc ** The next four lines are usually if the form
   1022       1.1      jtc **
   1023       1.1      jtc **     queuing is [enabled|disabled]
   1024       1.1      jtc **     printing is [enabled|disabled]
   1025       1.1      jtc **     [no entries | N entr[y|ies] in spool area]
   1026       1.1      jtc **     <status message, may include the word "attention">
   1027       1.1      jtc */
   1028       1.8      dsl 		while (fgets(buff, 255, p) != NULL && isspace((unsigned char)buff[0])) {
   1029       1.1      jtc 			cp = buff;
   1030       1.8      dsl 			while (isspace((unsigned char)*cp))
   1031       1.1      jtc 				cp++;
   1032       1.5    lukem 			if (*cp == '\0')
   1033       1.1      jtc 				break;
   1034       1.1      jtc 			cp1 = cp;
   1035       1.1      jtc 			cp2 = buff2;
   1036       1.5    lukem 			while (*cp1 && *cp1 != '\n') {
   1037       1.8      dsl 				*cp2++ = tolower((unsigned char)*cp1);
   1038       1.1      jtc 				cp1++;
   1039       1.1      jtc 			}
   1040       1.1      jtc 			*cp1 = '\0';
   1041       1.1      jtc 			*cp2 = '\0';
   1042       1.1      jtc /*
   1043       1.1      jtc ** Now buff2 has a lower-cased copy and cp points at the original;
   1044       1.1      jtc ** both are null terminated without any newline
   1045       1.5    lukem */
   1046       1.5    lukem 			if (!strncmp(buff2, "queuing", 7)) {
   1047       1.1      jtc 				*avail = (strstr(buff2, "enabled") != NULL);
   1048       1.1      jtc 				continue;
   1049       1.1      jtc 			}
   1050       1.5    lukem 			if (!strncmp(buff2, "printing", 8)) {
   1051       1.1      jtc 				*printing = (strstr(buff2, "enabled") != NULL);
   1052       1.1      jtc 				continue;
   1053       1.1      jtc 			}
   1054       1.8      dsl 			if (isdigit((unsigned char)buff2[0]) && (strstr(buff2, "entr") != NULL)) {
   1055       1.1      jtc 
   1056       1.1      jtc 				*qlen = atoi(buff2);
   1057       1.1      jtc 				continue;
   1058       1.1      jtc 			}
   1059       1.5    lukem 			if (strstr(buff2, "attention") != NULL ||
   1060       1.5    lukem 			    strstr(buff2, "error") != NULL)
   1061       1.1      jtc 				*needs_operator = TRUE;
   1062       1.5    lukem 			if (*needs_operator || strstr(buff2, "waiting") != NULL)
   1063  1.12.2.1     yamt 				strlcpy(status, cp, statuslen);
   1064       1.1      jtc 		}
   1065       1.9    lukem 		pstat = PI_RES_OK;
   1066       1.1      jtc 		break;
   1067       1.1      jtc 	}
   1068       1.1      jtc 	(void) pclose(p);
   1069       1.9    lukem 	return (pstat);
   1070       1.1      jtc }
   1071       1.5    lukem #endif				/* SVR4 */
   1072       1.3      gwr 
   1073       1.3      gwr /*
   1074       1.3      gwr  * pr_cancel: cancel a print job
   1075       1.3      gwr  */
   1076       1.3      gwr #ifdef SVR4
   1077       1.1      jtc 
   1078       1.3      gwr /*
   1079       1.3      gwr ** For SVR4 we have to be prepared for the following kinds of output:
   1080       1.5    lukem **
   1081       1.3      gwr ** # cancel lp-6
   1082       1.3      gwr ** request "lp-6" cancelled
   1083       1.3      gwr ** # cancel lp-33
   1084       1.3      gwr ** UX:cancel: WARNING: Request "lp-33" doesn't exist.
   1085       1.3      gwr ** # cancel foo-88
   1086       1.3      gwr ** UX:cancel: WARNING: Request "foo-88" doesn't exist.
   1087       1.3      gwr ** # cancel foo
   1088       1.3      gwr ** UX:cancel: WARNING: "foo" is not a request id or a printer.
   1089       1.3      gwr **             TO FIX: Cancel requests by id or by
   1090       1.3      gwr **                     name of printer where printing.
   1091       1.3      gwr ** # su geoff
   1092       1.3      gwr ** $ cancel lp-2
   1093       1.3      gwr ** UX:cancel: WARNING: Can't cancel request "lp-2".
   1094       1.3      gwr **             TO FIX: You are not allowed to cancel
   1095       1.3      gwr **                     another's request.
   1096       1.3      gwr **
   1097       1.3      gwr ** There are probably other variations for remote printers.
   1098       1.3      gwr ** Basically, if the reply begins with the string
   1099       1.3      gwr **          "UX:cancel: WARNING: "
   1100       1.3      gwr ** we can strip this off and look for one of the following
   1101       1.3      gwr ** (1) 'R' - should be part of "Request "xxxx" doesn't exist."
   1102       1.3      gwr ** (2) '"' - should be start of ""foo" is not a request id or..."
   1103       1.3      gwr ** (3) 'C' - should be start of "Can't cancel request..."
   1104       1.3      gwr **
   1105       1.3      gwr ** The fly in the ointment: all of this can change if these
   1106       1.3      gwr ** messages are localized..... :-(
   1107       1.3      gwr */
   1108       1.5    lukem pcrstat
   1109       1.5    lukem pr_cancel(pr, user, id)
   1110       1.5    lukem 	char   *pr;
   1111       1.5    lukem 	char   *user;
   1112       1.5    lukem 	char   *id;
   1113       1.1      jtc {
   1114       1.5    lukem 	char    cmdbuf[256];
   1115       1.5    lukem 	char    resbuf[256];
   1116       1.5    lukem 	FILE   *fd;
   1117       1.5    lukem 	pcrstat stat = PC_RES_NO_SUCH_JOB;
   1118       1.1      jtc 
   1119       1.1      jtc 	pr = map_printer_name(pr);
   1120       1.5    lukem 	if (pr == NULL || suspicious(pr))
   1121       1.5    lukem 		return (PC_RES_NO_SUCH_PRINTER);
   1122       1.5    lukem 	if (suspicious(id))
   1123       1.5    lukem 		return (PC_RES_NO_SUCH_JOB);
   1124       1.1      jtc 
   1125       1.7   itojun 	snprintf(cmdbuf, sizeof(cmdbuf), "/usr/bin/cancel %s", id);
   1126       1.3      gwr 	if ((fd = su_popen(user, cmdbuf, MAXTIME_FOR_CANCEL)) == NULL) {
   1127       1.3      gwr 		msg_out("rpc.pcnfsd: su_popen failed");
   1128       1.5    lukem 		return (PC_RES_FAIL);
   1129       1.3      gwr 	}
   1130       1.5    lukem 	if (fgets(resbuf, 255, fd) == NULL)
   1131       1.3      gwr 		stat = PC_RES_FAIL;
   1132       1.5    lukem 	else
   1133       1.5    lukem 		if (!strstr(resbuf, "UX:"))
   1134       1.5    lukem 			stat = PC_RES_OK;
   1135       1.5    lukem 		else
   1136       1.5    lukem 			if (strstr(resbuf, "doesn't exist"))
   1137       1.5    lukem 				stat = PC_RES_NO_SUCH_JOB;
   1138       1.5    lukem 			else
   1139       1.5    lukem 				if (strstr(resbuf, "not a request id"))
   1140       1.5    lukem 					stat = PC_RES_NO_SUCH_JOB;
   1141       1.5    lukem 				else
   1142       1.5    lukem 					if (strstr(resbuf, "Can't cancel request"))
   1143       1.5    lukem 						stat = PC_RES_NOT_OWNER;
   1144       1.5    lukem 					else
   1145       1.5    lukem 						stat = PC_RES_FAIL;
   1146       1.3      gwr 
   1147       1.5    lukem 	if (su_pclose(fd) == 255)
   1148       1.3      gwr 		msg_out("rpc.pcnfsd: su_pclose alert");
   1149       1.5    lukem 	return (stat);
   1150       1.3      gwr }
   1151       1.5    lukem #else				/* SVR4 */
   1152       1.3      gwr 
   1153       1.3      gwr /*
   1154       1.3      gwr  * BSD way: lprm
   1155       1.3      gwr  */
   1156       1.5    lukem pcrstat
   1157       1.5    lukem pr_cancel(pr, user, id)
   1158       1.5    lukem 	char   *pr;
   1159       1.5    lukem 	char   *user;
   1160       1.5    lukem 	char   *id;
   1161       1.3      gwr {
   1162       1.5    lukem 	char    cmdbuf[256];
   1163       1.5    lukem 	char    resbuf[256];
   1164       1.5    lukem 	FILE   *fd;
   1165       1.5    lukem 	int     i;
   1166       1.9    lukem 	pcrstat pstat = PC_RES_NO_SUCH_JOB;
   1167       1.3      gwr 
   1168       1.3      gwr 	pr = map_printer_name(pr);
   1169       1.5    lukem 	if (pr == NULL || suspicious(pr))
   1170       1.5    lukem 		return (PC_RES_NO_SUCH_PRINTER);
   1171       1.5    lukem 	if (suspicious(id))
   1172       1.5    lukem 		return (PC_RES_NO_SUCH_JOB);
   1173       1.5    lukem 
   1174       1.7   itojun 	snprintf(cmdbuf, sizeof(cmdbuf), "%s/lprm -P%s %s", LPRDIR, pr, id);
   1175       1.5    lukem 	if ((fd = su_popen(user, cmdbuf, MAXTIME_FOR_CANCEL)) == NULL) {
   1176       1.5    lukem 		msg_out("rpc.pcnfsd: su_popen failed");
   1177       1.5    lukem 		return (PC_RES_FAIL);
   1178       1.5    lukem 	}
   1179       1.5    lukem 	while (fgets(resbuf, 255, fd) != NULL) {
   1180       1.5    lukem 		i = strlen(resbuf);
   1181       1.5    lukem 		if (i)
   1182       1.5    lukem 			resbuf[i - 1] = '\0';	/* trim NL */
   1183       1.5    lukem 		if (strstr(resbuf, "dequeued") != NULL)
   1184       1.9    lukem 			pstat = PC_RES_OK;
   1185       1.5    lukem 		if (strstr(resbuf, "unknown printer") != NULL)
   1186       1.9    lukem 			pstat = PC_RES_NO_SUCH_PRINTER;
   1187       1.5    lukem 		if (strstr(resbuf, "Permission denied") != NULL)
   1188       1.9    lukem 			pstat = PC_RES_NOT_OWNER;
   1189       1.5    lukem 	}
   1190       1.5    lukem 	if (su_pclose(fd) == 255)
   1191       1.5    lukem 		msg_out("rpc.pcnfsd: su_pclose alert");
   1192       1.9    lukem 	return (pstat);
   1193       1.1      jtc }
   1194       1.5    lukem #endif				/* SVR4 */
   1195       1.5    lukem 
   1196       1.1      jtc /*
   1197       1.1      jtc ** New subsystem here. We allow the administrator to define
   1198       1.1      jtc ** up to NPRINTERDEFS aliases for printer names. This is done
   1199       1.1      jtc ** using the "/etc/pcnfsd.conf" file, which is read at startup.
   1200       1.1      jtc ** There are three entry points to this subsystem
   1201       1.1      jtc **
   1202       1.1      jtc ** void add_printer_alias(char *printer, char *alias_for, char *command)
   1203       1.1      jtc **
   1204       1.1      jtc ** This is invoked from "config_from_file()" for each
   1205       1.1      jtc ** "printer" line. "printer" is the name of a printer; note that
   1206       1.1      jtc ** it is possible to redefine an existing printer. "alias_for"
   1207       1.1      jtc ** is the name of the underlying printer, used for queue listing
   1208       1.1      jtc ** and other control functions. If it is "-", there is no
   1209       1.1      jtc ** underlying printer, or the administrative functions are
   1210       1.1      jtc ** not applicable to this printer. "command"
   1211       1.1      jtc ** is the command which should be run (via "su_popen()") if a
   1212       1.1      jtc ** job is printed on this printer. The following tokens may be
   1213       1.1      jtc ** embedded in the command, and are substituted as follows:
   1214       1.1      jtc **
   1215       1.1      jtc ** $FILE	-	path to the file containing the print data
   1216       1.1      jtc ** $USER	-	login of user
   1217       1.1      jtc ** $HOST	-	hostname from which job originated
   1218       1.1      jtc **
   1219       1.1      jtc ** Tokens may occur multiple times. If The command includes no
   1220       1.1      jtc ** $FILE token, the string " $FILE" is silently appended.
   1221       1.1      jtc **
   1222       1.1      jtc ** pr_list list_virtual_printers()
   1223       1.1      jtc **
   1224       1.1      jtc ** This is invoked from build_pr_list to generate a list of aliased
   1225       1.1      jtc ** printers, so that the client that asks for a list of valid printers
   1226       1.1      jtc ** will see these ones.
   1227       1.1      jtc **
   1228       1.1      jtc ** char *map_printer_name(char *printer)
   1229       1.1      jtc **
   1230       1.1      jtc ** If "printer" identifies an aliased printer, this function returns
   1231       1.1      jtc ** the "alias_for" name, or NULL if the "alias_for" was given as "-".
   1232       1.1      jtc ** Otherwise it returns its argument.
   1233       1.1      jtc **
   1234       1.1      jtc ** char *expand_alias(char *printer, char *file, char *user, char *host)
   1235       1.1      jtc **
   1236       1.1      jtc ** If "printer" is an aliased printer, this function returns a
   1237       1.1      jtc ** pointer to a static string in which the corresponding command
   1238       1.1      jtc ** has been expanded. Otherwise ot returns NULL.
   1239       1.1      jtc */
   1240       1.1      jtc #define NPRINTERDEFS	16
   1241       1.5    lukem int     num_aliases = 0;
   1242       1.1      jtc struct {
   1243       1.5    lukem 	char   *a_printer;
   1244       1.5    lukem 	char   *a_alias_for;
   1245       1.5    lukem 	char   *a_command;
   1246       1.5    lukem }       alias[NPRINTERDEFS];
   1247       1.1      jtc 
   1248       1.1      jtc void
   1249       1.1      jtc add_printer_alias(printer, alias_for, command)
   1250       1.5    lukem 	char   *printer;
   1251       1.5    lukem 	char   *alias_for;
   1252       1.5    lukem 	char   *command;
   1253       1.1      jtc {
   1254       1.7   itojun 	size_t l;
   1255       1.7   itojun 
   1256       1.5    lukem 	if (num_aliases < NPRINTERDEFS) {
   1257       1.1      jtc 		alias[num_aliases].a_printer = strdup(printer);
   1258       1.1      jtc 		alias[num_aliases].a_alias_for =
   1259       1.5    lukem 		    (strcmp(alias_for, "-") ? strdup(alias_for) : NULL);
   1260       1.5    lukem 		if (strstr(command, "$FILE"))
   1261       1.1      jtc 			alias[num_aliases].a_command = strdup(command);
   1262       1.1      jtc 		else {
   1263       1.7   itojun 			l = strlen(command) + 8;
   1264       1.7   itojun 			alias[num_aliases].a_command = (char *) grab(l);
   1265       1.7   itojun 			strlcpy(alias[num_aliases].a_command, command, l);
   1266       1.7   itojun 			strlcat(alias[num_aliases].a_command, " $FILE", l);
   1267       1.1      jtc 		}
   1268       1.1      jtc 		num_aliases++;
   1269       1.1      jtc 	}
   1270       1.1      jtc }
   1271       1.1      jtc 
   1272       1.5    lukem pr_list
   1273       1.5    lukem list_virtual_printers()
   1274       1.1      jtc {
   1275       1.5    lukem 	pr_list first = NULL;
   1276       1.5    lukem 	pr_list last = NULL;
   1277       1.5    lukem 	pr_list curr = NULL;
   1278       1.5    lukem 	int     i;
   1279       1.1      jtc 
   1280       1.1      jtc 
   1281       1.5    lukem 	if (num_aliases == 0)
   1282       1.5    lukem 		return (NULL);
   1283       1.1      jtc 
   1284       1.1      jtc 	for (i = 0; i < num_aliases; i++) {
   1285       1.1      jtc 		curr = (struct pr_list_item *)
   1286       1.5    lukem 		    grab(sizeof(struct pr_list_item));
   1287       1.1      jtc 
   1288       1.1      jtc 		curr->pn = strdup(alias[i].a_printer);
   1289       1.5    lukem 		if (alias[i].a_alias_for == NULL)
   1290       1.1      jtc 			curr->device = strdup("");
   1291       1.1      jtc 		else
   1292       1.1      jtc 			curr->device = strdup(alias[i].a_alias_for);
   1293       1.1      jtc 		curr->remhost = strdup("");
   1294       1.1      jtc 		curr->cm = strdup("(alias)");
   1295       1.1      jtc 		curr->pr_next = NULL;
   1296       1.5    lukem 		if (last == NULL)
   1297       1.1      jtc 			first = curr;
   1298       1.1      jtc 		else
   1299       1.1      jtc 			last->pr_next = curr;
   1300       1.1      jtc 		last = curr;
   1301       1.1      jtc 
   1302       1.1      jtc 	}
   1303       1.5    lukem 	return (first);
   1304       1.1      jtc }
   1305       1.1      jtc 
   1306       1.1      jtc 
   1307       1.5    lukem char   *
   1308       1.1      jtc map_printer_name(printer)
   1309       1.5    lukem 	char   *printer;
   1310       1.1      jtc {
   1311       1.5    lukem 	int     i;
   1312       1.5    lukem 	for (i = 0; i < num_aliases; i++) {
   1313       1.5    lukem 		if (!strcmp(printer, alias[i].a_printer))
   1314       1.5    lukem 			return (alias[i].a_alias_for);
   1315       1.1      jtc 	}
   1316       1.5    lukem 	return (printer);
   1317       1.1      jtc }
   1318       1.1      jtc 
   1319       1.5    lukem void
   1320       1.1      jtc substitute(string, token, data)
   1321       1.5    lukem 	char   *string;
   1322       1.9    lukem 	const char   *token;
   1323       1.9    lukem 	const char   *data;
   1324       1.1      jtc {
   1325       1.5    lukem 	char    temp[512];
   1326       1.5    lukem 	char   *c;
   1327       1.1      jtc 
   1328       1.5    lukem 	while ((c = strstr(string, token)) != NULL) {
   1329       1.1      jtc 		*c = '\0';
   1330       1.7   itojun 		strlcpy(temp, string, sizeof(temp));
   1331       1.7   itojun 		strlcat(temp, data, sizeof(temp));
   1332       1.1      jtc 		c += strlen(token);
   1333       1.7   itojun 		strlcat(temp, c, sizeof(temp));
   1334       1.1      jtc 		strcpy(string, temp);
   1335       1.1      jtc 	}
   1336       1.1      jtc }
   1337       1.1      jtc 
   1338       1.5    lukem char   *
   1339       1.1      jtc expand_alias(printer, file, user, host)
   1340       1.5    lukem 	char   *printer;
   1341       1.5    lukem 	char   *file;
   1342       1.5    lukem 	char   *user;
   1343       1.5    lukem 	char   *host;
   1344       1.1      jtc {
   1345       1.5    lukem 	static char expansion[512];
   1346       1.5    lukem 	int     i;
   1347       1.5    lukem 	for (i = 0; i < num_aliases; i++) {
   1348       1.5    lukem 		if (!strcmp(printer, alias[i].a_printer)) {
   1349       1.7   itojun 			strlcpy(expansion, alias[i].a_command,
   1350       1.7   itojun 			    sizeof(expansion));
   1351       1.1      jtc 			substitute(expansion, "$FILE", file);
   1352       1.1      jtc 			substitute(expansion, "$USER", user);
   1353       1.1      jtc 			substitute(expansion, "$HOST", host);
   1354       1.5    lukem 			return (expansion);
   1355       1.1      jtc 		}
   1356       1.1      jtc 	}
   1357       1.5    lukem 	return (NULL);
   1358       1.1      jtc }
   1359