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