xref: /freebsd/usr.sbin/lpr/common_source/matchjobs.c (revision 1de7b4b805ddbf2429da511c053686ac4591ed89)
1*1de7b4b8SPedro F. Giffuni /*-
2*1de7b4b8SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*1de7b4b8SPedro F. Giffuni  *
4dd8faa9fSGarance A Drosehn  * ------+---------+---------+---------+---------+---------+---------+---------*
5ad1f7851SGarance A Drosehn  * Copyright (c) 2002,2011   - Garance Alistair Drosehn <gad@FreeBSD.org>.
6dd8faa9fSGarance A Drosehn  * All rights reserved.
7dd8faa9fSGarance A Drosehn  *
8dd8faa9fSGarance A Drosehn  * Redistribution and use in source and binary forms, with or without
9dd8faa9fSGarance A Drosehn  * modification, are permitted provided that the following conditions
10dd8faa9fSGarance A Drosehn  * are met:
11dd8faa9fSGarance A Drosehn  *   1. Redistributions of source code must retain the above copyright
12dd8faa9fSGarance A Drosehn  *      notice, this list of conditions and the following disclaimer.
13dd8faa9fSGarance A Drosehn  *   2. Redistributions in binary form must reproduce the above copyright
14dd8faa9fSGarance A Drosehn  *      notice, this list of conditions and the following disclaimer in the
15dd8faa9fSGarance A Drosehn  *      documentation and/or other materials provided with the distribution.
16dd8faa9fSGarance A Drosehn  *
17dd8faa9fSGarance A Drosehn  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18dd8faa9fSGarance A Drosehn  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19dd8faa9fSGarance A Drosehn  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20dd8faa9fSGarance A Drosehn  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21dd8faa9fSGarance A Drosehn  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22dd8faa9fSGarance A Drosehn  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23dd8faa9fSGarance A Drosehn  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24dd8faa9fSGarance A Drosehn  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25dd8faa9fSGarance A Drosehn  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26dd8faa9fSGarance A Drosehn  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27dd8faa9fSGarance A Drosehn  * SUCH DAMAGE.
28dd8faa9fSGarance A Drosehn  *
29dd8faa9fSGarance A Drosehn  * The views and conclusions contained in the software and documentation
30dd8faa9fSGarance A Drosehn  * are those of the authors and should not be interpreted as representing
31dd8faa9fSGarance A Drosehn  * official policies, either expressed or implied, of the FreeBSD Project
32dd8faa9fSGarance A Drosehn  * or FreeBSD, Inc.
33dd8faa9fSGarance A Drosehn  *
34dd8faa9fSGarance A Drosehn  * ------+---------+---------+---------+---------+---------+---------+---------*
35dd8faa9fSGarance A Drosehn  */
36dd8faa9fSGarance A Drosehn 
371f589b47SGarance A Drosehn #include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
381f589b47SGarance A Drosehn __FBSDID("$FreeBSD$");
39dd8faa9fSGarance A Drosehn 
40dd8faa9fSGarance A Drosehn /*
41dd8faa9fSGarance A Drosehn  * movejobs.c - The lpc commands which move jobs around.
42dd8faa9fSGarance A Drosehn  */
43dd8faa9fSGarance A Drosehn 
44dd8faa9fSGarance A Drosehn #include <sys/file.h>
45dd8faa9fSGarance A Drosehn #include <sys/param.h>
46dd8faa9fSGarance A Drosehn #include <sys/queue.h>
47dd8faa9fSGarance A Drosehn #include <sys/time.h>
48dd8faa9fSGarance A Drosehn 
49dd8faa9fSGarance A Drosehn #include <dirent.h>	/* for MAXNAMLEN, for job_cfname in lp.h! */
50dd8faa9fSGarance A Drosehn #include <ctype.h>
51dd8faa9fSGarance A Drosehn #include <errno.h>
52dd8faa9fSGarance A Drosehn #include <fnmatch.h>
53dd8faa9fSGarance A Drosehn #include <stdio.h>
54dd8faa9fSGarance A Drosehn #include <stdlib.h>
55dd8faa9fSGarance A Drosehn #include <string.h>
56dd8faa9fSGarance A Drosehn #include <unistd.h>
57dd8faa9fSGarance A Drosehn #include "ctlinfo.h"
58dd8faa9fSGarance A Drosehn #include "lp.h"
59dd8faa9fSGarance A Drosehn #include "matchjobs.h"
60dd8faa9fSGarance A Drosehn 
61dd8faa9fSGarance A Drosehn #define DEBUG_PARSEJS	0	/* set to 1 when testing */
62dd8faa9fSGarance A Drosehn #define DEBUG_SCANJS	0	/* set to 1 when testing */
63dd8faa9fSGarance A Drosehn 
64dd8faa9fSGarance A Drosehn static int	 match_jobspec(struct jobqueue *_jq, struct jobspec *_jspec);
65dd8faa9fSGarance A Drosehn 
66dd8faa9fSGarance A Drosehn /*
67dd8faa9fSGarance A Drosehn  * isdigit is defined to work on an 'int', in the range 0 to 255, plus EOF.
68dd8faa9fSGarance A Drosehn  * Define a wrapper which can take 'char', either signed or unsigned.
69dd8faa9fSGarance A Drosehn  */
70dd8faa9fSGarance A Drosehn #define isdigitch(Anychar)    isdigit(((int) Anychar) & 255)
71dd8faa9fSGarance A Drosehn 
72dd8faa9fSGarance A Drosehn /*
73dd8faa9fSGarance A Drosehn  * Format a single jobspec into a string fit for printing.
74dd8faa9fSGarance A Drosehn  */
75dd8faa9fSGarance A Drosehn void
76dd8faa9fSGarance A Drosehn format_jobspec(struct jobspec *jspec, int fmt_wanted)
77dd8faa9fSGarance A Drosehn {
78dd8faa9fSGarance A Drosehn 	char rangestr[40], buildstr[200];
79dd8faa9fSGarance A Drosehn 	const char fromuser[] = "from user ";
80dd8faa9fSGarance A Drosehn 	const char fromhost[] = "from host ";
81dd8faa9fSGarance A Drosehn 	size_t strsize;
82dd8faa9fSGarance A Drosehn 
83dd8faa9fSGarance A Drosehn 	/*
84dd8faa9fSGarance A Drosehn 	 * If the struct already has a fmtstring, then release it
85dd8faa9fSGarance A Drosehn 	 * before building a new one.
86dd8faa9fSGarance A Drosehn 	 */
87dd8faa9fSGarance A Drosehn 	if (jspec->fmtoutput != NULL) {
88dd8faa9fSGarance A Drosehn 		free(jspec->fmtoutput);
89dd8faa9fSGarance A Drosehn 		jspec->fmtoutput = NULL;
90dd8faa9fSGarance A Drosehn 	}
91dd8faa9fSGarance A Drosehn 
92dd8faa9fSGarance A Drosehn 	jspec->pluralfmt = 1;		/* assume a "plural result" */
93dd8faa9fSGarance A Drosehn 	rangestr[0] = '\0';
94dd8faa9fSGarance A Drosehn 	if (jspec->startnum >= 0) {
95dd8faa9fSGarance A Drosehn 		if (jspec->startnum != jspec->endrange)
96dd8faa9fSGarance A Drosehn 			snprintf(rangestr, sizeof(rangestr), "%ld-%ld",
97dd8faa9fSGarance A Drosehn 			    jspec->startnum, jspec->endrange);
98dd8faa9fSGarance A Drosehn 		else {
99dd8faa9fSGarance A Drosehn 			jspec->pluralfmt = 0;
100dd8faa9fSGarance A Drosehn 			snprintf(rangestr, sizeof(rangestr), "%ld",
101dd8faa9fSGarance A Drosehn 			    jspec->startnum);
102dd8faa9fSGarance A Drosehn 		}
103dd8faa9fSGarance A Drosehn 	}
104dd8faa9fSGarance A Drosehn 
105dd8faa9fSGarance A Drosehn 	strsize = sizeof(buildstr);
106dd8faa9fSGarance A Drosehn 	buildstr[0] = '\0';
107dd8faa9fSGarance A Drosehn 	switch (fmt_wanted) {
108dd8faa9fSGarance A Drosehn 	case FMTJS_TERSE:
109dd8faa9fSGarance A Drosehn 		/* Build everything but the hostname in a temp string. */
110dd8faa9fSGarance A Drosehn 		if (jspec->wanteduser != NULL)
111dd8faa9fSGarance A Drosehn 			strlcat(buildstr, jspec->wanteduser, strsize);
112dd8faa9fSGarance A Drosehn 		if (rangestr[0] != '\0') {
113dd8faa9fSGarance A Drosehn 			if (buildstr[0] != '\0')
114dd8faa9fSGarance A Drosehn 				strlcat(buildstr, ":", strsize);
115dd8faa9fSGarance A Drosehn 			strlcat(buildstr, rangestr, strsize);
116dd8faa9fSGarance A Drosehn 		}
117dd8faa9fSGarance A Drosehn 		if (jspec->wantedhost != NULL)
118dd8faa9fSGarance A Drosehn 				strlcat(buildstr, "@", strsize);
119dd8faa9fSGarance A Drosehn 
120dd8faa9fSGarance A Drosehn 		/* Get space for the final result, including hostname */
121dd8faa9fSGarance A Drosehn 		strsize = strlen(buildstr) + 1;
122dd8faa9fSGarance A Drosehn 		if (jspec->wantedhost != NULL)
123dd8faa9fSGarance A Drosehn 			strsize += strlen(jspec->wantedhost);
124dd8faa9fSGarance A Drosehn 		jspec->fmtoutput = malloc(strsize);
125dd8faa9fSGarance A Drosehn 
126dd8faa9fSGarance A Drosehn 		/* Put together the final result */
127dd8faa9fSGarance A Drosehn 		strlcpy(jspec->fmtoutput, buildstr, strsize);
128dd8faa9fSGarance A Drosehn 		if (jspec->wantedhost != NULL)
129dd8faa9fSGarance A Drosehn 			strlcat(jspec->fmtoutput, jspec->wantedhost, strsize);
130dd8faa9fSGarance A Drosehn 		break;
131dd8faa9fSGarance A Drosehn 
132dd8faa9fSGarance A Drosehn 	case FMTJS_VERBOSE:
133dd8faa9fSGarance A Drosehn 	default:
134dd8faa9fSGarance A Drosehn 		/* Build everything but the hostname in a temp string. */
135dd8faa9fSGarance A Drosehn 		strlcat(buildstr, rangestr, strsize);
136dd8faa9fSGarance A Drosehn 		if (jspec->wanteduser != NULL) {
137dd8faa9fSGarance A Drosehn 			if (rangestr[0] != '\0')
138dd8faa9fSGarance A Drosehn 				strlcat(buildstr, " ", strsize);
139dd8faa9fSGarance A Drosehn 			strlcat(buildstr, fromuser, strsize);
140dd8faa9fSGarance A Drosehn 			strlcat(buildstr, jspec->wanteduser, strsize);
141dd8faa9fSGarance A Drosehn 		}
142dd8faa9fSGarance A Drosehn 		if (jspec->wantedhost != NULL) {
143dd8faa9fSGarance A Drosehn 			if (jspec->wanteduser == NULL) {
144dd8faa9fSGarance A Drosehn 				if (rangestr[0] != '\0')
145dd8faa9fSGarance A Drosehn 					strlcat(buildstr, " ", strsize);
146dd8faa9fSGarance A Drosehn 				strlcat(buildstr, fromhost, strsize);
147dd8faa9fSGarance A Drosehn 			} else
148dd8faa9fSGarance A Drosehn 				strlcat(buildstr, "@", strsize);
149dd8faa9fSGarance A Drosehn 		}
150dd8faa9fSGarance A Drosehn 
151dd8faa9fSGarance A Drosehn 		/* Get space for the final result, including hostname */
152dd8faa9fSGarance A Drosehn 		strsize = strlen(buildstr) + 1;
153dd8faa9fSGarance A Drosehn 		if (jspec->wantedhost != NULL)
154dd8faa9fSGarance A Drosehn 			strsize += strlen(jspec->wantedhost);
155dd8faa9fSGarance A Drosehn 		jspec->fmtoutput = malloc(strsize);
156dd8faa9fSGarance A Drosehn 
157dd8faa9fSGarance A Drosehn 		/* Put together the final result */
158dd8faa9fSGarance A Drosehn 		strlcpy(jspec->fmtoutput, buildstr, strsize);
159dd8faa9fSGarance A Drosehn 		if (jspec->wantedhost != NULL)
160dd8faa9fSGarance A Drosehn 			strlcat(jspec->fmtoutput, jspec->wantedhost, strsize);
161dd8faa9fSGarance A Drosehn 		break;
162dd8faa9fSGarance A Drosehn 	}
163dd8faa9fSGarance A Drosehn }
164dd8faa9fSGarance A Drosehn 
165dd8faa9fSGarance A Drosehn /*
166dd8faa9fSGarance A Drosehn  * Free all the jobspec-related information.
167dd8faa9fSGarance A Drosehn  */
168dd8faa9fSGarance A Drosehn void
169dd8faa9fSGarance A Drosehn free_jobspec(struct jobspec_hdr *js_hdr)
170dd8faa9fSGarance A Drosehn {
171dd8faa9fSGarance A Drosehn 	struct jobspec *jsinf;
172dd8faa9fSGarance A Drosehn 
173dd8faa9fSGarance A Drosehn 	while (!STAILQ_EMPTY(js_hdr)) {
174dd8faa9fSGarance A Drosehn 		jsinf = STAILQ_FIRST(js_hdr);
175dd8faa9fSGarance A Drosehn 		STAILQ_REMOVE_HEAD(js_hdr, nextjs);
176dd8faa9fSGarance A Drosehn 		if (jsinf->fmtoutput)
177dd8faa9fSGarance A Drosehn 			free(jsinf->fmtoutput);
178dd8faa9fSGarance A Drosehn 		if (jsinf->matcheduser)
179dd8faa9fSGarance A Drosehn 			free(jsinf->matcheduser);
180dd8faa9fSGarance A Drosehn 		free(jsinf);
181dd8faa9fSGarance A Drosehn 	}
182dd8faa9fSGarance A Drosehn }
183dd8faa9fSGarance A Drosehn 
184dd8faa9fSGarance A Drosehn /*
185dd8faa9fSGarance A Drosehn  * This routine takes a string as typed in from the user, and parses it
186dd8faa9fSGarance A Drosehn  * into a job-specification.  A job specification would match one or more
187dd8faa9fSGarance A Drosehn  * jobs in the queue of some single printer (the specification itself does
188dd8faa9fSGarance A Drosehn  * not indicate which queue should be searched).
189dd8faa9fSGarance A Drosehn  *
190dd8faa9fSGarance A Drosehn  * This recognizes a job-number range by itself (all digits, or a range
191dd8faa9fSGarance A Drosehn  * indicated by "digits-digits"), or a userid by itself.  If a `:' is
192dd8faa9fSGarance A Drosehn  * found, it is treated as a separator between a job-number range and
193dd8faa9fSGarance A Drosehn  * a userid, where the job number range is the side which has a digit as
194dd8faa9fSGarance A Drosehn  * the first character.  If an `@' is found, everything to the right of
195dd8faa9fSGarance A Drosehn  * it is treated as the hostname the job originated from.
196dd8faa9fSGarance A Drosehn  *
197dd8faa9fSGarance A Drosehn  * So, the user can specify:
198dd8faa9fSGarance A Drosehn  *	jobrange       userid     userid:jobrange    jobrange:userid
199dd8faa9fSGarance A Drosehn  *	jobrange@hostname   jobrange:userid@hostname
200dd8faa9fSGarance A Drosehn  *	userid@hostname     userid:jobrange@hostname
201dd8faa9fSGarance A Drosehn  *
202dd8faa9fSGarance A Drosehn  * XXX - it would be nice to add "not options" too, such as ^user,
203dd8faa9fSGarance A Drosehn  *	^jobrange, and @^hostname.
204dd8faa9fSGarance A Drosehn  *
205dd8faa9fSGarance A Drosehn  * This routine may modify the original input string if that input is
206dd8faa9fSGarance A Drosehn  * valid.  If the input was *not* valid, then this routine should return
207dd8faa9fSGarance A Drosehn  * with the input string the same as when the routine was called.
208dd8faa9fSGarance A Drosehn  */
209dd8faa9fSGarance A Drosehn int
210dd8faa9fSGarance A Drosehn parse_jobspec(char *jobstr, struct jobspec_hdr *js_hdr)
211dd8faa9fSGarance A Drosehn {
212dd8faa9fSGarance A Drosehn 	struct jobspec *jsinfo;
213dd8faa9fSGarance A Drosehn 	char *atsign, *colon, *lhside, *numstr, *period, *rhside;
214dd8faa9fSGarance A Drosehn 	int jobnum;
215dd8faa9fSGarance A Drosehn 
216dd8faa9fSGarance A Drosehn #if DEBUG_PARSEJS
217dd8faa9fSGarance A Drosehn 	printf("\t [ pjs-input = %s ]\n", jobstr);
218dd8faa9fSGarance A Drosehn #endif
219dd8faa9fSGarance A Drosehn 
220dd8faa9fSGarance A Drosehn 	if ((jobstr == NULL) || (*jobstr == '\0'))
221dd8faa9fSGarance A Drosehn 		return (0);
222dd8faa9fSGarance A Drosehn 
223dd8faa9fSGarance A Drosehn 	jsinfo = malloc(sizeof(struct jobspec));
224dd8faa9fSGarance A Drosehn 	memset(jsinfo, 0, sizeof(struct jobspec));
225dd8faa9fSGarance A Drosehn 	jsinfo->startnum = jsinfo->endrange = -1;
226dd8faa9fSGarance A Drosehn 
227dd8faa9fSGarance A Drosehn 	/* Find the separator characters, and nullify them. */
228dd8faa9fSGarance A Drosehn 	numstr = NULL;
229dd8faa9fSGarance A Drosehn 	atsign = strchr(jobstr, '@');
230dd8faa9fSGarance A Drosehn 	colon = strchr(jobstr, ':');
231dd8faa9fSGarance A Drosehn 	if (atsign != NULL)
232dd8faa9fSGarance A Drosehn 		*atsign = '\0';
233dd8faa9fSGarance A Drosehn 	if (colon != NULL)
234dd8faa9fSGarance A Drosehn 		*colon = '\0';
235dd8faa9fSGarance A Drosehn 
236dd8faa9fSGarance A Drosehn 	/* The at-sign always indicates a hostname. */
237dd8faa9fSGarance A Drosehn 	if (atsign != NULL) {
238dd8faa9fSGarance A Drosehn 		rhside = atsign + 1;
239dd8faa9fSGarance A Drosehn 		if (*rhside != '\0')
240dd8faa9fSGarance A Drosehn 			jsinfo->wantedhost = rhside;
241dd8faa9fSGarance A Drosehn 	}
242dd8faa9fSGarance A Drosehn 
243dd8faa9fSGarance A Drosehn 	/* Finish splitting the input into three parts. */
244dd8faa9fSGarance A Drosehn 	rhside = NULL;
245dd8faa9fSGarance A Drosehn 	if (colon != NULL) {
246dd8faa9fSGarance A Drosehn 		rhside = colon + 1;
247dd8faa9fSGarance A Drosehn 		if (*rhside == '\0')
248dd8faa9fSGarance A Drosehn 			rhside = NULL;
249dd8faa9fSGarance A Drosehn 	}
250dd8faa9fSGarance A Drosehn 	lhside = NULL;
251dd8faa9fSGarance A Drosehn 	if (*jobstr != '\0')
252dd8faa9fSGarance A Drosehn 		lhside = jobstr;
253dd8faa9fSGarance A Drosehn 
254dd8faa9fSGarance A Drosehn 	/*
255dd8faa9fSGarance A Drosehn 	 * If there is a `:' here, then it's either jobrange:userid,
256dd8faa9fSGarance A Drosehn 	 * userid:jobrange, or (if @hostname was not given) perhaps it
257dd8faa9fSGarance A Drosehn 	 * might be hostname:jobnum.  The side which has a digit as the
258dd8faa9fSGarance A Drosehn 	 * first character is assumed to be the jobrange.  It is an
259dd8faa9fSGarance A Drosehn 	 * input error if both sides start with a digit, or if neither
260dd8faa9fSGarance A Drosehn 	 * side starts with a digit.
261dd8faa9fSGarance A Drosehn 	 */
262dd8faa9fSGarance A Drosehn 	if ((lhside != NULL) && (rhside != NULL)) {
263dd8faa9fSGarance A Drosehn 		if (isdigitch(*lhside)) {
264dd8faa9fSGarance A Drosehn 			if (isdigitch(*rhside))
265dd8faa9fSGarance A Drosehn 				goto bad_input;
266dd8faa9fSGarance A Drosehn 			numstr = lhside;
267dd8faa9fSGarance A Drosehn 			jsinfo->wanteduser = rhside;
268dd8faa9fSGarance A Drosehn 		} else if (isdigitch(*rhside)) {
269dd8faa9fSGarance A Drosehn 			numstr = rhside;
270dd8faa9fSGarance A Drosehn 			/*
271dd8faa9fSGarance A Drosehn 			 * The original implementation of 'lpc topq' accepted
272dd8faa9fSGarance A Drosehn 			 * hostname:jobnum.  If the input did not include a
273dd8faa9fSGarance A Drosehn 			 * @hostname, then assume the userid is a hostname if
274dd8faa9fSGarance A Drosehn 			 * it includes a '.'.
275dd8faa9fSGarance A Drosehn 			 */
276dd8faa9fSGarance A Drosehn 			period = strchr(lhside, '.');
277dd8faa9fSGarance A Drosehn 			if ((atsign == NULL) && (period != NULL))
278dd8faa9fSGarance A Drosehn 				jsinfo->wantedhost = lhside;
279dd8faa9fSGarance A Drosehn 			else
280dd8faa9fSGarance A Drosehn 				jsinfo->wanteduser = lhside;
281dd8faa9fSGarance A Drosehn 		} else {
282dd8faa9fSGarance A Drosehn 			/* Neither side is a job number = user error */
283dd8faa9fSGarance A Drosehn 			goto bad_input;
284dd8faa9fSGarance A Drosehn 		}
285dd8faa9fSGarance A Drosehn 	} else if (lhside != NULL) {
286dd8faa9fSGarance A Drosehn 		if (isdigitch(*lhside))
287dd8faa9fSGarance A Drosehn 			numstr = lhside;
288dd8faa9fSGarance A Drosehn 		else
289dd8faa9fSGarance A Drosehn 			jsinfo->wanteduser = lhside;
290dd8faa9fSGarance A Drosehn 	} else if (rhside != NULL) {
291dd8faa9fSGarance A Drosehn 		if (isdigitch(*rhside))
292dd8faa9fSGarance A Drosehn 			numstr = rhside;
293dd8faa9fSGarance A Drosehn 		else
294dd8faa9fSGarance A Drosehn 			jsinfo->wanteduser = rhside;
295dd8faa9fSGarance A Drosehn 	}
296dd8faa9fSGarance A Drosehn 
297dd8faa9fSGarance A Drosehn 	/*
298dd8faa9fSGarance A Drosehn 	 * Break down the numstr.  It should be all digits, or a range
299dd8faa9fSGarance A Drosehn 	 * specified as "\d+-\d+".
300dd8faa9fSGarance A Drosehn 	 */
301dd8faa9fSGarance A Drosehn 	if (numstr != NULL) {
302dd8faa9fSGarance A Drosehn 		errno = 0;
303dd8faa9fSGarance A Drosehn 		jobnum = strtol(numstr, &numstr, 10);
304dd8faa9fSGarance A Drosehn 		if (errno != 0)		/* error in conversion */
305dd8faa9fSGarance A Drosehn 			goto bad_input;
306dd8faa9fSGarance A Drosehn 		if (jobnum < 0)		/* a bogus value for this purpose */
307dd8faa9fSGarance A Drosehn 			goto bad_input;
308dd8faa9fSGarance A Drosehn 		if (jobnum > 99999)	/* too large for job number */
309dd8faa9fSGarance A Drosehn 			goto bad_input;
310dd8faa9fSGarance A Drosehn 		jsinfo->startnum = jsinfo->endrange = jobnum;
311dd8faa9fSGarance A Drosehn 
312dd8faa9fSGarance A Drosehn 		/* Check for a range of numbers */
313dd8faa9fSGarance A Drosehn 		if ((*numstr == '-') && (isdigitch(*(numstr + 1)))) {
314dd8faa9fSGarance A Drosehn 			numstr++;
315dd8faa9fSGarance A Drosehn 			errno = 0;
316dd8faa9fSGarance A Drosehn 			jobnum = strtol(numstr, &numstr, 10);
317dd8faa9fSGarance A Drosehn 			if (errno != 0)		/* error in conversion */
318dd8faa9fSGarance A Drosehn 				goto bad_input;
319dd8faa9fSGarance A Drosehn 			if (jobnum < jsinfo->startnum)
320dd8faa9fSGarance A Drosehn 				goto bad_input;
321dd8faa9fSGarance A Drosehn 			if (jobnum > 99999)	/* too large for job number */
322dd8faa9fSGarance A Drosehn 				goto bad_input;
323dd8faa9fSGarance A Drosehn 			jsinfo->endrange = jobnum;
324dd8faa9fSGarance A Drosehn 		}
325dd8faa9fSGarance A Drosehn 
326dd8faa9fSGarance A Drosehn 		/*
327dd8faa9fSGarance A Drosehn 		 * If there is anything left in the numstr, and if the
328dd8faa9fSGarance A Drosehn 		 * original string did not include a userid or a hostname,
329dd8faa9fSGarance A Drosehn 		 * then this might be the ancient form of '\d+hostname'
3302b239dd1SJens Schweikhardt 		 * (with no separator between jobnum and hostname).  Accept
331dd8faa9fSGarance A Drosehn 		 * that for backwards compatibility, but otherwise any
332dd8faa9fSGarance A Drosehn 		 * remaining characters mean a user-error.  Note that the
333dd8faa9fSGarance A Drosehn 		 * ancient form accepted only a single number, but this
334dd8faa9fSGarance A Drosehn 		 * will also accept a range of numbers.
335dd8faa9fSGarance A Drosehn 		 */
336dd8faa9fSGarance A Drosehn 		if (*numstr != '\0') {
337dd8faa9fSGarance A Drosehn 			if (atsign != NULL)
338dd8faa9fSGarance A Drosehn 				goto bad_input;
339dd8faa9fSGarance A Drosehn 			if (jsinfo->wantedhost != NULL)
340dd8faa9fSGarance A Drosehn 				goto bad_input;
341dd8faa9fSGarance A Drosehn 			if (jsinfo->wanteduser != NULL)
342dd8faa9fSGarance A Drosehn 				goto bad_input;
343dd8faa9fSGarance A Drosehn 			/* Treat as the rest of the string as a hostname */
344dd8faa9fSGarance A Drosehn 			jsinfo->wantedhost = numstr;
345dd8faa9fSGarance A Drosehn 		}
346dd8faa9fSGarance A Drosehn 	}
347dd8faa9fSGarance A Drosehn 
348dd8faa9fSGarance A Drosehn 	if ((jsinfo->startnum < 0) && (jsinfo->wanteduser == NULL) &&
349dd8faa9fSGarance A Drosehn 	    (jsinfo->wantedhost == NULL))
350dd8faa9fSGarance A Drosehn 		goto bad_input;
351dd8faa9fSGarance A Drosehn 
352dd8faa9fSGarance A Drosehn 	/*
353dd8faa9fSGarance A Drosehn 	 * The input was valid, in the sense that it could be parsed
354dd8faa9fSGarance A Drosehn 	 * into the individual parts.  Add this jobspec to the list
355dd8faa9fSGarance A Drosehn 	 * of jobspecs.
356dd8faa9fSGarance A Drosehn 	 */
357dd8faa9fSGarance A Drosehn 	STAILQ_INSERT_TAIL(js_hdr, jsinfo, nextjs);
358dd8faa9fSGarance A Drosehn 
359dd8faa9fSGarance A Drosehn #if DEBUG_PARSEJS
360dd8faa9fSGarance A Drosehn 	printf("\t [   will check for");
361dd8faa9fSGarance A Drosehn 	if (jsinfo->startnum >= 0) {
362dd8faa9fSGarance A Drosehn 		if (jsinfo->startnum == jsinfo->endrange)
363dd8faa9fSGarance A Drosehn 			printf(" jobnum = %ld", jsinfo->startnum);
364dd8faa9fSGarance A Drosehn 		else
365dd8faa9fSGarance A Drosehn 			printf(" jobrange = %ld to %ld", jsinfo->startnum,
366dd8faa9fSGarance A Drosehn 			    jsinfo->endrange);
367dd8faa9fSGarance A Drosehn 	} else {
368dd8faa9fSGarance A Drosehn 		printf(" jobs");
369dd8faa9fSGarance A Drosehn 	}
370dd8faa9fSGarance A Drosehn 	if ((jsinfo->wanteduser != NULL) || (jsinfo->wantedhost != NULL)) {
371dd8faa9fSGarance A Drosehn 		printf(" from");
372dd8faa9fSGarance A Drosehn 		if (jsinfo->wanteduser != NULL)
373dd8faa9fSGarance A Drosehn 			printf(" user = %s", jsinfo->wanteduser);
374dd8faa9fSGarance A Drosehn 		if (jsinfo->wantedhost != NULL)
375dd8faa9fSGarance A Drosehn 			printf(" host = %s", jsinfo->wantedhost);
376dd8faa9fSGarance A Drosehn 	}
377dd8faa9fSGarance A Drosehn 	printf("]\n");
378dd8faa9fSGarance A Drosehn #endif
379dd8faa9fSGarance A Drosehn 
380dd8faa9fSGarance A Drosehn 	return (1);
381dd8faa9fSGarance A Drosehn 
382dd8faa9fSGarance A Drosehn bad_input:
383dd8faa9fSGarance A Drosehn 	/*
384dd8faa9fSGarance A Drosehn 	 * Restore any `@' and `:', in case the calling routine wants to
385dd8faa9fSGarance A Drosehn 	 * write an error message which includes the input string.
386dd8faa9fSGarance A Drosehn 	 */
387dd8faa9fSGarance A Drosehn 	if (atsign != NULL)
388dd8faa9fSGarance A Drosehn 		*atsign = '@';
389dd8faa9fSGarance A Drosehn 	if (colon != NULL)
390dd8faa9fSGarance A Drosehn 		*colon = ':';
391dd8faa9fSGarance A Drosehn 	if (jsinfo != NULL)
392dd8faa9fSGarance A Drosehn 		free(jsinfo);
393dd8faa9fSGarance A Drosehn 	return (0);
394dd8faa9fSGarance A Drosehn }
395dd8faa9fSGarance A Drosehn 
396dd8faa9fSGarance A Drosehn /*
397dd8faa9fSGarance A Drosehn  * Check to see if a given job (specified by a jobqueue entry) matches
398dd8faa9fSGarance A Drosehn  * all of the specifications in a given jobspec.
399dd8faa9fSGarance A Drosehn  *
400dd8faa9fSGarance A Drosehn  * Returns 0 if no match, 1 if the job does match.
401dd8faa9fSGarance A Drosehn  */
402dd8faa9fSGarance A Drosehn static int
403dd8faa9fSGarance A Drosehn match_jobspec(struct jobqueue *jq, struct jobspec *jspec)
404dd8faa9fSGarance A Drosehn {
405dd8faa9fSGarance A Drosehn 	struct cjobinfo *cfinf;
406c547dbe8SGarance A Drosehn 	const char *cf_hoststr;
407dd8faa9fSGarance A Drosehn 	int jnum, match;
408dd8faa9fSGarance A Drosehn 
409dd8faa9fSGarance A Drosehn #if DEBUG_SCANJS
410dd8faa9fSGarance A Drosehn 	printf("\t [ match-js checking %s ]\n", jq->job_cfname);
411dd8faa9fSGarance A Drosehn #endif
412dd8faa9fSGarance A Drosehn 
413dd8faa9fSGarance A Drosehn 	if (jspec == NULL || jq == NULL)
414dd8faa9fSGarance A Drosehn 		return (0);
415dd8faa9fSGarance A Drosehn 
416dd8faa9fSGarance A Drosehn 	/*
417dd8faa9fSGarance A Drosehn 	 * Keep track of which jobs have already been matched by this
418dd8faa9fSGarance A Drosehn 	 * routine, and thus (probably) already processed.
419dd8faa9fSGarance A Drosehn 	 */
420dd8faa9fSGarance A Drosehn 	if (jq->job_matched)
421dd8faa9fSGarance A Drosehn 		return (0);
422dd8faa9fSGarance A Drosehn 
423c547dbe8SGarance A Drosehn 	jnum = calc_jobnum(jq->job_cfname, &cf_hoststr);
424dd8faa9fSGarance A Drosehn 	cfinf = NULL;
425dd8faa9fSGarance A Drosehn 	match = 0;			/* assume the job will not match */
426dd8faa9fSGarance A Drosehn 	jspec->matcheduser = NULL;
427dd8faa9fSGarance A Drosehn 
428dd8faa9fSGarance A Drosehn 	/*
429dd8faa9fSGarance A Drosehn 	 * Check the job-number range.
430dd8faa9fSGarance A Drosehn 	 */
431dd8faa9fSGarance A Drosehn 	if (jspec->startnum >= 0) {
432dd8faa9fSGarance A Drosehn 		if (jnum < jspec->startnum)
433dd8faa9fSGarance A Drosehn 			goto nomatch;
434dd8faa9fSGarance A Drosehn 		if (jnum > jspec->endrange)
435dd8faa9fSGarance A Drosehn 			goto nomatch;
436dd8faa9fSGarance A Drosehn 	}
437dd8faa9fSGarance A Drosehn 
438dd8faa9fSGarance A Drosehn 	/*
439dd8faa9fSGarance A Drosehn 	 * Check the hostname.  Strictly speaking this should be done by
440dd8faa9fSGarance A Drosehn 	 * reading the control file, but it is less expensive to check
441dd8faa9fSGarance A Drosehn 	 * the hostname-part of the control file name.  Also, this value
442dd8faa9fSGarance A Drosehn 	 * can be easily seen in 'lpq -l', while there is no easy way for
443dd8faa9fSGarance A Drosehn 	 * a user/operator to see the hostname in the control file.
444dd8faa9fSGarance A Drosehn 	 */
445dd8faa9fSGarance A Drosehn 	if (jspec->wantedhost != NULL) {
446dd8faa9fSGarance A Drosehn 		if (fnmatch(jspec->wantedhost, cf_hoststr, 0) != 0)
447dd8faa9fSGarance A Drosehn 			goto nomatch;
448dd8faa9fSGarance A Drosehn 	}
449dd8faa9fSGarance A Drosehn 
450dd8faa9fSGarance A Drosehn 	/*
451dd8faa9fSGarance A Drosehn 	 * Check for a match on the user name.  This has to be done
452dd8faa9fSGarance A Drosehn 	 * by reading the control file.
453dd8faa9fSGarance A Drosehn 	 */
454dd8faa9fSGarance A Drosehn 	if (jspec->wanteduser != NULL) {
455dd8faa9fSGarance A Drosehn 		cfinf = ctl_readcf("fakeq", jq->job_cfname);
456dd8faa9fSGarance A Drosehn 		if (cfinf == NULL)
457dd8faa9fSGarance A Drosehn 			goto nomatch;
458ad1f7851SGarance A Drosehn 		if (fnmatch(jspec->wanteduser, cfinf->cji_acctuser, 0) != 0)
459dd8faa9fSGarance A Drosehn 			goto nomatch;
460dd8faa9fSGarance A Drosehn 	}
461dd8faa9fSGarance A Drosehn 
462dd8faa9fSGarance A Drosehn 	/* This job matches all of the specified criteria. */
463dd8faa9fSGarance A Drosehn 	match = 1;
464dd8faa9fSGarance A Drosehn 	jq->job_matched = 1;		/* avoid matching the job twice */
465dd8faa9fSGarance A Drosehn 	jspec->matchcnt++;
466dd8faa9fSGarance A Drosehn 	if (jspec->wanteduser != NULL) {
467dd8faa9fSGarance A Drosehn 		/*
468dd8faa9fSGarance A Drosehn 		 * If the user specified a userid (which may have been a
469dd8faa9fSGarance A Drosehn 		 * pattern), then the caller's "doentry()" routine might
470dd8faa9fSGarance A Drosehn 		 * want to know the userid of this job that matched.
471dd8faa9fSGarance A Drosehn 		 */
472ad1f7851SGarance A Drosehn 		jspec->matcheduser = strdup(cfinf->cji_acctuser);
473dd8faa9fSGarance A Drosehn 	}
474dd8faa9fSGarance A Drosehn #if DEBUG_SCANJS
475dd8faa9fSGarance A Drosehn 	printf("\t [ job matched! ]\n");
476dd8faa9fSGarance A Drosehn #endif
477dd8faa9fSGarance A Drosehn 
478dd8faa9fSGarance A Drosehn nomatch:
479dd8faa9fSGarance A Drosehn 	if (cfinf != NULL)
480dd8faa9fSGarance A Drosehn 		ctl_freeinf(cfinf);
481dd8faa9fSGarance A Drosehn 	return (match);
482dd8faa9fSGarance A Drosehn }
483dd8faa9fSGarance A Drosehn 
484dd8faa9fSGarance A Drosehn /*
485dd8faa9fSGarance A Drosehn  * Scan a queue for all jobs which match a jobspec.  The queue is scanned
486dd8faa9fSGarance A Drosehn  * from top to bottom.
487dd8faa9fSGarance A Drosehn  *
488dd8faa9fSGarance A Drosehn  * The caller can provide a routine which will be executed for each job
489dd8faa9fSGarance A Drosehn  * that does match.  Note that the processing routine might do anything
490dd8faa9fSGarance A Drosehn  * to the matched job -- including the removal of it.
491dd8faa9fSGarance A Drosehn  *
492dd8faa9fSGarance A Drosehn  * This returns the number of jobs which were matched.
493dd8faa9fSGarance A Drosehn  */
494dd8faa9fSGarance A Drosehn int
495dd8faa9fSGarance A Drosehn scanq_jobspec(int qcount, struct jobqueue **squeue, int sopts, struct
496dd8faa9fSGarance A Drosehn     jobspec_hdr *js_hdr, process_jqe doentry, void *doentryinfo)
497dd8faa9fSGarance A Drosehn {
498dd8faa9fSGarance A Drosehn 	struct jobqueue **qent;
499dd8faa9fSGarance A Drosehn 	struct jobspec *jspec;
500dd8faa9fSGarance A Drosehn 	int cnt, matched, total;
501dd8faa9fSGarance A Drosehn 
502dd8faa9fSGarance A Drosehn 	if (qcount < 1)
503dd8faa9fSGarance A Drosehn 		return (0);
504dd8faa9fSGarance A Drosehn 	if (js_hdr == NULL)
505dd8faa9fSGarance A Drosehn 		return (-1);
506dd8faa9fSGarance A Drosehn 
507dd8faa9fSGarance A Drosehn 	/* The caller must specify one of the scanning orders */
508dd8faa9fSGarance A Drosehn 	if ((sopts & (SCQ_JSORDER|SCQ_QORDER)) == 0)
509dd8faa9fSGarance A Drosehn 		return (-1);
510dd8faa9fSGarance A Drosehn 
511dd8faa9fSGarance A Drosehn 	total = 0;
512dd8faa9fSGarance A Drosehn 	if (sopts & SCQ_JSORDER) {
513dd8faa9fSGarance A Drosehn 		/*
514dd8faa9fSGarance A Drosehn 		 * For each job specification, scan through the queue
515dd8faa9fSGarance A Drosehn 		 * looking for every job that matches.
516dd8faa9fSGarance A Drosehn 		 */
517dd8faa9fSGarance A Drosehn 		STAILQ_FOREACH(jspec, js_hdr, nextjs) {
518dd8faa9fSGarance A Drosehn 			for (qent = squeue, cnt = 0; cnt < qcount;
519dd8faa9fSGarance A Drosehn 			    qent++, cnt++) {
520dd8faa9fSGarance A Drosehn 				matched = match_jobspec(*qent, jspec);
521dd8faa9fSGarance A Drosehn 				if (!matched)
522dd8faa9fSGarance A Drosehn 					continue;
523dd8faa9fSGarance A Drosehn 				total++;
524dd8faa9fSGarance A Drosehn 				if (doentry != NULL)
525dd8faa9fSGarance A Drosehn 					doentry(doentryinfo, *qent, jspec);
526dd8faa9fSGarance A Drosehn 				if (jspec->matcheduser != NULL) {
527dd8faa9fSGarance A Drosehn 					free(jspec->matcheduser);
528dd8faa9fSGarance A Drosehn 					jspec->matcheduser = NULL;
529dd8faa9fSGarance A Drosehn 				}
530dd8faa9fSGarance A Drosehn 			}
531dd8faa9fSGarance A Drosehn 			/*
532dd8faa9fSGarance A Drosehn 			 * The entire queue has been scanned for this
533dd8faa9fSGarance A Drosehn 			 * jobspec.  Call the user's routine again with
534dd8faa9fSGarance A Drosehn 			 * a NULL queue-entry, so it can print out any
535dd8faa9fSGarance A Drosehn 			 * kind of per-jobspec summary.
536dd8faa9fSGarance A Drosehn 			 */
537dd8faa9fSGarance A Drosehn 			if (doentry != NULL)
538dd8faa9fSGarance A Drosehn 				doentry(doentryinfo, NULL, jspec);
539dd8faa9fSGarance A Drosehn 		}
540dd8faa9fSGarance A Drosehn 	} else {
541dd8faa9fSGarance A Drosehn 		/*
542dd8faa9fSGarance A Drosehn 		 * For each job in the queue, check all of the job
543dd8faa9fSGarance A Drosehn 		 * specifications to see if any one of them matches
544dd8faa9fSGarance A Drosehn 		 * that job.
545dd8faa9fSGarance A Drosehn 		 */
546dd8faa9fSGarance A Drosehn 		for (qent = squeue, cnt = 0; cnt < qcount;
547dd8faa9fSGarance A Drosehn 		    qent++, cnt++) {
548dd8faa9fSGarance A Drosehn 			STAILQ_FOREACH(jspec, js_hdr, nextjs) {
549dd8faa9fSGarance A Drosehn 				matched = match_jobspec(*qent, jspec);
550dd8faa9fSGarance A Drosehn 				if (!matched)
551dd8faa9fSGarance A Drosehn 					continue;
552dd8faa9fSGarance A Drosehn 				total++;
553dd8faa9fSGarance A Drosehn 				if (doentry != NULL)
554dd8faa9fSGarance A Drosehn 					doentry(doentryinfo, *qent, jspec);
555dd8faa9fSGarance A Drosehn 				if (jspec->matcheduser != NULL) {
556dd8faa9fSGarance A Drosehn 					free(jspec->matcheduser);
557dd8faa9fSGarance A Drosehn 					jspec->matcheduser = NULL;
558dd8faa9fSGarance A Drosehn 				}
559dd8faa9fSGarance A Drosehn 				/*
560dd8faa9fSGarance A Drosehn 				 * Once there is a match, then there is no
561dd8faa9fSGarance A Drosehn 				 * point in checking this same job against
562dd8faa9fSGarance A Drosehn 				 * all the other jobspec's.
563dd8faa9fSGarance A Drosehn 				 */
564dd8faa9fSGarance A Drosehn 				break;
565dd8faa9fSGarance A Drosehn 			}
566dd8faa9fSGarance A Drosehn 		}
567dd8faa9fSGarance A Drosehn 	}
568dd8faa9fSGarance A Drosehn 
569dd8faa9fSGarance A Drosehn 	return (total);
570dd8faa9fSGarance A Drosehn }
571