1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
22 /*	  All Rights Reserved  	*/
23 
24 
25 /*
26  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
27  * Use is subject to license terms.
28  */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include "lpsched.h"
33 
34 static int	max_requests_needing_form_mounted ( FSTATUS * );
35 static int	max_requests_needing_pwheel_mounted ( char * );
36 
37 /**
38  ** queue_form() - ADD A REQUEST TO A FORM QUEUE
39  **/
40 
41 void
42 queue_form(RSTATUS *prs, FSTATUS *pfs)
43 {
44 	if ((prs->form = pfs) != NULL) {
45 		prs->form->requests++;
46 		if (prs->printer)
47 			check_form_alert (prs->form, (_FORM *)0);
48 	}
49 	return;
50 }
51 
52 /**
53  ** unqueue_form() - REMOVE A REQUEST FROM A FORM QUEUE
54  **/
55 
56 void
57 unqueue_form(RSTATUS *prs)
58 {
59 	FSTATUS *		pfs	= prs->form;
60 
61 	prs->form = 0;
62 	if (pfs) {
63 		pfs->requests--;
64 		if (prs->printer)
65 			check_form_alert (pfs, (_FORM *)0);
66 	}
67 	return;
68 }
69 
70 /**
71  ** queue_pwheel() - ADD A REQUEST TO A PRINT WHEEL QUEUE
72  **/
73 
74 void
75 queue_pwheel(RSTATUS *prs, char *name)
76 {
77 	if (name) {
78 		prs->pwheel_name = Strdup(name);
79 		/*
80 		 * Don't bother queueing the request for
81 		 * a print wheel if this request is destined for
82 		 * only this printer and the printer doesn't take
83 		 * print wheels.
84 		 */
85 		if (
86 			!one_printer_with_charsets(prs)
87 		     && (prs->pwheel = search_pwstatus(name))
88 		) {
89 			prs->pwheel->requests++;
90 			check_pwheel_alert (prs->pwheel, (PWHEEL *)0);
91 		}
92 	}
93 	return;
94 }
95 
96 /**
97  ** unqueue_pwheel() - REMOVE A REQUEST FROM A PRINT WHEEL QUEUE
98  **/
99 
100 void
101 unqueue_pwheel(RSTATUS *prs)
102 {
103 	PWSTATUS *		ppws	= prs->pwheel;
104 
105 	prs->pwheel = 0;
106 	unload_str (&(prs->pwheel_name));
107 	if (ppws) {
108 		ppws->requests--;
109 		check_pwheel_alert (ppws, (PWHEEL *)0);
110 	}
111 	return;
112 }
113 
114 /**
115  ** check_form_alert() - CHECK CHANGES TO MOUNT FORM ALERT
116  **/
117 
118 void
119 check_form_alert(FSTATUS *pfs, _FORM *pf)
120 {
121 	short			trigger,
122 				fire_off_alert	= 0;
123 
124 	int			requests_waiting;
125 
126 
127 	/*
128 	 * Call this routine whenever a requests has been queued
129 	 * or dequeued for a form, and whenever the form changes.
130 	 * If a pointer to a new _FORM is passed, the FSTATUS
131 	 * structure is updated with the changes. Use a second
132 	 * argument of 0 if no change.
133 	 *
134 	 * WARNING: It is valid to call this routine when adding
135 	 * a NEW form (not just changing it). Thus the members of
136 	 * the structure "pfs->form" may not be set.
137 	 * In this case, though, "pf" MUST be set, and there can
138 	 * be NO alert active.
139 	 */
140 
141 	if (pf) {
142 		if ((trigger = pf->alert.Q) <= 0)
143 			trigger = 1;
144 	} else
145 		trigger = pfs->trigger;
146 
147 	if (Starting)
148 		goto Return;
149 
150 #define	OALERT	pfs->form->alert
151 #define NALERT	pf->alert
152 
153 	requests_waiting = max_requests_needing_form_mounted(pfs);
154 
155 	/*
156 	 * Cancel an active alert if the number of requests queued
157 	 * has dropped below the threshold (or the threshold has been
158 	 * raised), or if the alert command or period has changed.
159 	 * In the latter case we'll reactive the alert later.
160 	 */
161 	if (pfs->alert->active)
162 		if (!requests_waiting || requests_waiting < trigger)
163 			cancel_alert (A_FORM, pfs);
164 
165 		else if (
166 			pf
167 		     && (
168 				!SAME(NALERT.shcmd, OALERT.shcmd)
169 			     || NALERT.W != OALERT.W
170 			     || NALERT.Q != OALERT.Q
171 			)
172 		)
173 			cancel_alert (A_FORM, pfs);
174 
175 	/*
176 	 * If we still have the condition for an alert, we'll fire
177 	 * one off. It is possible the alert is still running, but
178 	 * that's okay. First, we may want to change the alert message;
179 	 * second, the "alert()" routine doesn't execute an alert
180 	 * if it is already running.
181 	 */
182 	if (trigger > 0 && requests_waiting >= trigger)
183 		if ((pf && NALERT.shcmd) || OALERT.shcmd)
184 			fire_off_alert = 1;
185 
186 #undef	OALERT
187 #undef	NALERT
188 
189 Return:	if (pf) {
190 		/*
191 		 * Watch it! We may be adding a new form, so there
192 		 * may be nothing to toss out.
193 		 */
194 		if (pfs->form->name)
195 			free_form (pfs->form);
196 
197 		*(pfs->form) = *pf;
198 		pfs->trigger = trigger;
199 	}
200 
201 	/*
202 	 * Have to do this after updating the changes.
203 	 */
204 	if (fire_off_alert)
205 		alert (A_FORM, pfs);
206 
207 	return;
208 }
209 
210 /**
211  ** check_pwheel_alert() - CHECK CHANGES TO MOUNT PRINTWHEEL ALERT
212  **/
213 
214 void
215 check_pwheel_alert(PWSTATUS *ppws, PWHEEL *ppw)
216 {
217 	short			trigger,
218 				fire_off_alert	= 0;
219 	int			requests_waiting;
220 
221 
222 	/*
223 	 * Call this routine whenever a request has been queued
224 	 * or dequeued for a print-wheel, and whenever the print-wheel
225 	 * changes. If a pointer to a new PWHEEL is passed, the
226 	 * PWSTATUS structure is updated with the changes. Use a
227 	 * second argument of 0 if no change.
228 	 *
229 	 * WARNING: It is valid to call this routine when adding
230 	 * a NEW print wheel (not just changing it). Thus the members
231 	 * of the structure "ppws->pwheel" may not be set.
232 	 * In this case, though, "ppw" MUST be set, and there can
233 	 * be NO alert active.
234 	 */
235 
236 	if (ppw) {
237 		if ((trigger = ppw->alert.Q) <= 0)
238 			trigger = 1;
239 	} else
240 		trigger = ppws->trigger;
241 
242 	if (Starting)
243 		goto Return;
244 
245 #define	OALERT	ppws->pwheel->alert
246 #define NALERT	ppw->alert
247 
248 	requests_waiting = max_requests_needing_pwheel_mounted(ppws->pwheel->name);
249 
250 	/*
251 	 * Cancel an active alert if the number of requests queued
252 	 * has dropped below the threshold (or the threshold has been
253 	 * raised), or if the alert command or period has changed.
254 	 * In the latter case we'll reactive the alert later.
255 	 */
256 	if (ppws->alert->active)
257 		if (!requests_waiting || requests_waiting < trigger)
258 			cancel_alert (A_PWHEEL, ppws);
259 
260 		else if (
261 			ppw
262 		     && (
263 				!SAME(NALERT.shcmd, OALERT.shcmd)
264 			     || NALERT.W != OALERT.W
265 			     || NALERT.Q != OALERT.Q
266 			)
267 		)
268 			cancel_alert (A_PWHEEL, ppws);
269 
270 	/*
271 	 * If we still have the condition for an alert, we'll fire
272 	 * one off. It is possible the alert is still running, but
273 	 * that's okay. First, we may want to change the alert message;
274 	 * second, the "alert()" routine doesn't execute an alert
275 	 * if it is already running.
276 	 */
277 	if (trigger > 0 && requests_waiting >= trigger)
278 		if ((ppw && NALERT.shcmd) || OALERT.shcmd)
279 			fire_off_alert = 1;
280 
281 #undef	OALERT
282 #undef	NALERT
283 
284 Return:	if (ppw) {
285 		/*
286 		 * Watch it! We may be adding a new print wheel, so there
287 		 * may be nothing to toss out.
288 		 */
289 		if (ppws->pwheel->name)
290 			freepwheel (ppws->pwheel);
291 
292 		*(ppws->pwheel) = *ppw;
293 		ppws->trigger = trigger;
294 	}
295 
296 	/*
297 	 * Have to do this after updating the changes.
298 	 */
299 	if (fire_off_alert)
300 		alert (A_PWHEEL, ppws);
301 
302 	return;
303 }
304 
305 static int
306 trayWithForm(PSTATUS *pps, FSTATUS *pfs, int startingTray, int checkAvail)
307 {
308 	int i;
309 	PFSTATUS *ppfs;
310 
311 	ppfs = pps->forms;
312 	if (startingTray < 0)
313 		startingTray = 0;
314 
315 	if (ppfs) {
316 		for (i = startingTray; i < pps->numForms; i++)
317 			if ((!checkAvail || ppfs[i].isAvailable) &&
318 			    (ppfs[i].form == pfs))
319 					return(i);
320 	}
321 	else if (!pfs)
322 		/* no form request matches no form mounted */
323 		return(0);
324 
325 	return(-1);
326 }
327 
328 char *
329 allTraysWithForm(PSTATUS *pps, FSTATUS *pfs)
330 {
331 
332 	int tray = 0;
333 	char *ptr, *p;
334 	char trayList[MAX_INPUT];
335 	int n;
336 
337 	ptr = trayList;
338 	if (pfs && pfs->form && pfs->form->paper)
339 		p = pfs->form->paper;
340 	else
341 		p = "";
342 
343 	n = sizeof (trayList);
344 	snprintf(ptr, n, "LP_TRAY_ARG=%s:", p);
345 
346 	ptr += strlen(ptr);
347 	n -= strlen(ptr);
348 
349 	while ((tray = trayWithForm(pps, pfs, tray, 1)) > 0) {
350 		tray++;
351 		snprintf(ptr, n, "%d,", tray);
352 		ptr += strlen(ptr);
353 		n -= strlen(ptr);
354 	}
355 	if (*(ptr-1) == ',')
356 		*(ptr-1) = 0;
357 
358 	putenv(trayList);
359 	return(NULL);
360 }
361 
362 int
363 isFormUsableOnPrinter(PSTATUS *pps, FSTATUS *pfs)
364 {
365 	return (trayWithForm(pps,pfs,0,1) >= 0 );
366 }
367 int
368 isFormMountedOnPrinter(PSTATUS *pps, FSTATUS *pfs)
369 {
370 	return (trayWithForm(pps,pfs,0,0) >= 0 );
371 }
372 
373 /**
374  ** max_requests_needing_form_mounted()
375  ** max_requests_needing_pwheel_mounted()
376  **/
377 
378 static int
379 max_requests_needing_form_mounted(FSTATUS *pfs)
380 {
381 	PSTATUS *		pps;
382 	RSTATUS *		prs;
383 	int			max	= 0;
384 	int			i;
385 
386 	/*
387 	 * For each printer that doesn't have this form mounted,
388 	 * count the number of requests needing this form and
389 	 * assigned to the printer. Find the maximum across all such
390 	 * printers. Sorry, the code actually has a different loop
391 	 * (it steps through the requests) but the description of what
392 	 * happens below is easier to understand as given. (Looping
393 	 * through the printers would result in #printers x #requests
394 	 * steps, whereas this entails #requests steps.)
395 	 */
396 	for (i = 0; PStatus != NULL && PStatus[i] != NULL; i++)
397 		PStatus[i]->nrequests = 0;
398 
399 	for (prs = Request_List; prs != NULL; prs = prs->next)
400 		if ((prs->form == pfs) && ((pps = prs->printer) != NULL) &&
401 	    	    (!isFormMountedOnPrinter(pps,pfs)) &&
402 		    (++pps->nrequests >= max))
403 			max = pps->nrequests;
404 
405 	if (NewRequest)
406 		if (((pps = NewRequest->printer) != NULL) &&
407 		    (!isFormMountedOnPrinter(pps,pfs)))
408 			if (++pps->nrequests >= max)
409 				max = pps->nrequests;
410 	return (max);
411 }
412 
413 static int
414 max_requests_needing_pwheel_mounted(char *pwheel_name)
415 {
416 	PSTATUS *		pps;
417 	RSTATUS *		prs;
418 	int			max	= 0;
419 	int			i;
420 
421 
422 	/*
423 	 * For each printer that doesn't have this print-wheel mounted,
424 	 * count the number of requests needing this print-wheel and
425 	 * assigned to the printer. Find the maximum across all such
426 	 * printers. Sorry, the code actually has a different loop
427 	 * (it steps through the requests) but the description of what
428 	 * happens below is easier to understand as given. (Looping
429 	 * through the printers would result in #printers x #requests
430 	 * steps, whereas this entails #requests steps.)
431 	 */
432 	for (i = 0; PStatus != NULL && PStatus[i] != NULL; i++)
433 		PStatus[i]->nrequests = 0;
434 
435 	for (prs = Request_List; prs != NULL; prs = prs->next)
436 		if ((prs->pwheel_name != NULL) &&
437 		    (STREQU(prs->pwheel_name, pwheel_name)) &&
438 		    ((pps = prs->printer) != NULL) && pps->printer->daisy &&
439 		    (!SAME(pps->pwheel_name, pwheel_name)))
440 			if (++pps->nrequests >= max)
441 				max = pps->nrequests;
442 
443 	if (NewRequest)
444 		if (
445 			((pps = NewRequest->printer) != NULL)
446 		     && pps->printer->daisy
447 		     && !SAME(pps->pwheel_name, pwheel_name)
448 		)
449 			if (++pps->nrequests >= max)
450 				max = pps->nrequests;
451 	return (max);
452 }
453 
454 /**
455  ** one_printer_with_charsets()
456  **/
457 
458 int
459 one_printer_with_charsets(RSTATUS *prs)
460 {
461 	/*
462 	 * This little function answers the question: Is a request
463 	 * that needs a character set destined for a particular
464 	 * printer that has selectable character sets instead of
465 	 * mountable print wheels?
466 	 */
467 	return (
468 	    STREQU(prs->request->destination, prs->printer->printer->name)
469 	 && !prs->printer->printer->daisy
470 	);
471 }
472