1/*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License as
4 * published by the Free Software Foundation; either version 2 of
5 * the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
15 * MA 02111-1307 USA
16 */
17/***************************************************************************
18 * LPRng - An Extended Print Spooler System
19 *
20 * Copyright 1988-2003, Patrick Powell, San Diego, CA
21 *     papowell@lprng.com
22 * See LICENSE for conditions of use.
23 *
24 ***************************************************************************/
25
26 static char *const _id =
27"$Id: getqueue.c,v 1.1.1.1 2008/10/15 03:28:26 james26_jang Exp $";
28
29
30/***************************************************************************
31 * Commentary
32 * Patrick Powell Thu Apr 27 21:48:38 PDT 1995
33 *
34 * The spool directory holds files and other information associated
35 * with jobs.  Job files have names of the form cfXNNNhostname.
36 *
37 * The Scan_queue routine will scan a spool directory, looking for new
38 * or missing control files.  If one is found,  it will add it to
39 * the control file list.  It will then sort the list of file names.
40 *
41 * In order to prevent strange things with pointers,  you should not store
42 * pointers to this list,  but use indexes instead.
43 ***************************************************************************/
44
45#include "lp.h"
46#include "child.h"
47#include "errorcodes.h"
48#include "fileopen.h"
49#include "linelist.h"
50#include "getprinter.h"
51#include "gethostinfo.h"
52#include "getqueue.h"
53#include "globmatch.h"
54#include "permission.h"
55#include "lockfile.h"
56#include "merge.h"
57
58#if defined(USER_INCLUDE)
59# include USER_INCLUDE
60#else
61# if defined(ORDER_ROUTINE)
62#   error No 'USER_INCLUDE' file with ORDER_ROUTINE function prototypes specified
63    You need an include file with function prototypes
64# endif
65#endif
66
67/**** ENDINCLUDE ****/
68/*
69 * We make the following assumption:
70 *   a hold file is the thing that LPRng will create.  A control file without
71 *   a hold file is an orphan,  and should be disposed of.  We will ignore these
72 *   and leave it to the checkpc code to clean them up.
73 */
74
75#if defined(JY20031104Scan_queue)
76int Scan_queue( struct line_list *spool_control,
77	struct line_list *sort_order, int *pprintable, int *pheld, int *pmove,
78		int only_queue_process, int *perr, int *pdone,
79		const char *remove_prefix, const char *remove_suffix )
80{
81	DIR *dir;						/* directory */
82	struct dirent *d;				/* directory entry */
83	struct line_list directory_files;
84	char *hf_name;
85	int c, printable, held, move, error, done, p, h, m, e, dn;
86	int remove_prefix_len = safestrlen( remove_prefix );
87	int remove_suffix_len = safestrlen( remove_suffix );
88	struct job job;
89
90	c = printable = held = move = error = done = 0;
91	Init_job( &job );
92	Init_line_list(&directory_files);
93	if( pprintable ) *pprintable = 0;
94	if( pheld ) *pheld = 0;
95	if( pmove ) *pmove = 0;
96	if( perr ) *perr = 0;
97	if( pdone ) *pdone = 0;
98
99	Free_line_list(sort_order);
100
101	if( !(dir = opendir( "." )) ){
102		return(1);
103	}
104
105	hf_name = 0;
106	while(1){
107		while( (d = readdir(dir)) ){
108			hf_name = d->d_name;
109			DEBUG5("Scan_queue: found file '%s'", hf_name );
110			if(
111				(remove_prefix_len && !strncmp( hf_name, remove_prefix, remove_prefix_len ) )
112				|| (remove_suffix_len
113					&& !strcmp( hf_name+strlen(hf_name)-remove_suffix_len, remove_suffix ))
114			){
115				DEBUG1("Scan_queue: removing file '%s'", hf_name );
116				unlink( hf_name );
117				continue;
118			} else if(    (cval(hf_name+0) == 'h')
119				&& (cval(hf_name+1) == 'f')
120				&& isalpha(cval(hf_name+2))
121				&& isdigit(cval(hf_name+3))
122				){
123				break;
124			}
125		}
126		/* found them all */
127		if( d == 0 ){
128			break;
129		}
130
131		Free_job( &job );
132
133		DEBUG2("Scan_queue: processing file '%s'", hf_name );
134
135		/* read the hf file and get the information */
136		Get_file_image_and_split( hf_name, 0, 0,
137			&job.info, Line_ends, 1, Value_sep,1,1,1,0);
138#ifdef ORIGINAL_DEBUG//JY@1020
139		if(DEBUGL5)Dump_line_list("Scan_queue: hf", &job.info );
140#endif
141		if( job.info.count == 0 ){
142			continue;
143		}
144
145		/* get the data file from the control file */
146		Setup_cf_info( &job, 1 );
147		Job_printable(&job,spool_control, &p,&h,&m,&e,&dn);
148		if( p ) ++printable;
149		if( h ) ++held;
150		if( m ) ++move;
151		if( e ) ++error;
152		if( dn ) ++done;
153
154		/* now generate the sort key */
155		DEBUG4("Scan_queue: p %d, m %d, e %d, dn %d, only_queue_process %d",
156			p, m, e, dn, only_queue_process );
157		if( sort_order ){
158			if( !only_queue_process || (p || m || e || dn) ){
159#ifdef ORIGINAL_DEBUG//JY@1020
160				if(DEBUGL4)Dump_job("Scan_queue - before Make_sort_key",&job);
161#endif
162				Make_sort_key( &job );
163				DEBUG5("Scan_queue: sort key '%s'",job.sort_key);
164				Set_str_value(sort_order,job.sort_key,hf_name);
165			}
166		}
167	}
168	closedir(dir);
169
170	Free_job(&job);
171	Free_line_list(&directory_files);
172
173#ifdef ORIGINAL_DEBUG//JY@1020
174	if(DEBUGL5){
175		LOGDEBUG("Scan_queue: final values" );
176		Dump_line_list_sub(SORT_KEY,sort_order);
177	}
178#endif
179	if( pprintable ) *pprintable = printable;
180	if( pheld ) *pheld = held;
181	if( pmove ) *pmove = move;
182	if( perr ) *perr = error;
183	if( pdone ) *pdone = done;
184	DEBUG3("Scan_queue: final printable %d, held %d, move %d, error %d, done %d",
185		printable, held, move, error, done );
186	return(0);
187}
188#endif
189
190/*
191 * char *Get_fd_image( int fd, char *file )
192 *  Get an image of a file from an fd
193 */
194
195char *Get_fd_image( int fd, off_t maxsize )
196{
197	char *s = 0;
198	struct stat statb;
199	char buffer[LARGEBUFFER];
200	int n;
201	off_t len;
202
203	DEBUG3("Get_fd_image: fd %d", fd );
204
205	if( lseek(fd, 0, SEEK_SET) == -1 ){
206		Errorcode = JFAIL;
207		LOGERR_DIE(LOG_INFO)"Get_fd_image: lseek failed" );
208	}
209	if( maxsize && fstat(fd, &statb) == 0
210		&& maxsize< statb.st_size/1024 ){
211		lseek(fd, -maxsize*1024, SEEK_END);
212	}
213
214	len = 0;
215	while( (n = read(fd,buffer,sizeof(buffer))) > 0 ){
216		s = realloc_or_die(s,len+n+1,__FILE__,__LINE__);
217		memcpy(s+len,buffer,n);
218		len += n;
219		s[len] = 0;
220	}
221#ifdef ORIGINAL_DEBUG//JY@1020
222	if(DEBUGL3){
223		SNPRINTF(buffer,32)"%s",s);
224		logDebug("Get_fd_image: len %d '%s'", s?safestrlen(s):0, buffer );
225	}
226#endif
227	return(s);
228}
229
230/*
231 * char *Get_file_image( char *dir, char *file )
232 *  Get an image of a file
233 */
234
235char *Get_file_image( const char *file, off_t maxsize )
236{
237	char *s = 0;
238	struct stat statb;
239	int fd;
240
241	if( file == 0 ) return(0);
242	DEBUG3("Get_file_image: '%s', maxsize %d", file, maxsize );
243	if( (fd = Checkread( file, &statb )) >= 0 ){
244		s = Get_fd_image( fd, maxsize );
245		close(fd);
246	}
247	return(s);
248}
249
250/*
251 * char *Get_fd_image_and_split
252 *  Get an image of a file
253 */
254
255int Get_fd_image_and_split( int fd,
256	int maxsize, int clean,
257	struct line_list *l, const char *sep,
258	int sort, const char *keysep, int uniq, int trim, int nocomments,
259	char **return_image )
260{
261	char *s = 0;
262	if( return_image ) *return_image = 0;
263	s = Get_fd_image( fd, maxsize );
264	if( s == 0 ) return 1;
265	if( clean ) Clean_meta(s);
266	Split( l, s, sep, sort, keysep, uniq, trim, nocomments ,0);
267	if( return_image ){
268		*return_image = s;
269	} else {
270		if( s ) free(s); s = 0;
271	}
272	return(0);
273}
274
275
276/*
277 * char *Get_file_image_and_split
278 *  Get an image of a file
279 */
280
281int Get_file_image_and_split( const char *file,
282	int maxsize, int clean,
283	struct line_list *l, const char *sep,
284	int sort, const char *keysep, int uniq, int trim, int nocomments,
285	char **return_image )
286{
287	char *s = 0;
288	if( return_image ) *return_image = 0;
289	if( !ISNULL(file) ) s = Get_file_image( file, maxsize );
290	if( s == 0 ) return 1;
291	if( clean ) Clean_meta(s);
292	Split( l, s, sep, sort, keysep, uniq, trim, nocomments ,0);
293	if( return_image ){
294		*return_image = s;
295	} else {
296		if( s ) free(s); s = 0;
297	}
298	return(0);
299}
300
301/*
302 * Set up a job data structure with information from the
303 *   file images
304 *  - Check_for_hold -
305 *     checks to see if the job is held by class or by
306 *     command
307 *  Setup_job - gets the job information and updates it
308 *     from the spool queue and control information
309 */
310
311
312void Check_for_hold( struct job *job, struct line_list *spool_control )
313{
314	int held, i;
315	/* get the hold class of the job */
316	held = Get_hold_class(&job->info,spool_control);
317	Set_flag_value(&job->info,HOLD_CLASS,held);
318
319	/* see if we need to hold this job by time */
320	if( !Find_exists_value(&job->info,HOLD_TIME,Value_sep) ){
321		if( Find_flag_value(spool_control,HOLD_TIME,Value_sep) ){
322			i = time((void *)0);
323		} else {
324			i = 0;
325		}
326		Set_flag_value( &job->info, HOLD_TIME, i );
327	}
328	held = Find_flag_value(&job->info,HOLD_TIME,Value_sep);
329	Set_flag_value(&job->info,HELD,held);
330}
331
332/*
333 * Setup_job: called only when you REALLY want to
334 *  read the control file and hold file.
335 */
336
337#if defined(JY20031104Setup_job)
338void Setup_job( struct job *job, struct line_list *spool_control,
339	const char *cf_name, const char *hf_name, int check_for_existence )
340{
341	struct stat statb;
342	char *path, *s;
343	struct line_list *lp;
344	int i, j, size = 0;
345
346	/* add the hold file information directly */
347	DEBUG3("Setup_job: hf_name '%s', cf_name (TRANSFERNAME) '%s'", hf_name, cf_name );
348	if( cf_name ){
349		Set_str_value(&job->info,TRANSFERNAME, cf_name);
350	}
351	cf_name = Find_str_value(&job->info,TRANSFERNAME,Value_sep);
352
353	if( hf_name ){
354		Set_str_value(&job->info,HF_NAME,hf_name);
355	}
356	hf_name = Find_str_value(&job->info,HF_NAME,Value_sep);
357
358	if( cf_name && !Find_str_value(&job->info,NUMBER,Value_sep) ){
359		Check_format( CONTROL_FILE, cf_name, job );
360	}
361
362	if( !Find_str_value(&job->info,JOB_TIME,Value_sep)
363		&& (path = Find_str_value(&job->info,OPENNAME,Value_sep)) ){
364		j = 0;
365		if( stat(path,&statb) ){
366			i = time((void *)0);
367		} else {
368			i = statb.st_mtime;
369#ifdef ST_MTIMESPEC_TV_NSEC
370			j = statb.st_mtimespec.tv_nsec/1000;
371#endif
372#ifdef ST_MTIMENSEC
373			j = statb.st_mtimensec/1000;
374#endif
375		}
376		Set_flag_value(&job->info,JOB_TIME,i);
377		Set_flag_value(&job->info,JOB_TIME_USEC,j);
378	}
379
380	/* set up the control file information */
381	Setup_cf_info( job, check_for_existence );
382
383	/* set the class of the job */
384	if( !Find_str_value(&job->info,CLASS,Value_sep)
385		&& (s = Find_str_value(&job->info,PRIORITY,Value_sep)) ){
386		Set_str_value(&job->info,CLASS,s);
387	}
388	/* set the size of the job */
389	if( !Find_flag_value(&job->info,SIZE,Value_sep) ){
390		size = 0;
391		for( i = 0; i < job->datafiles.count; ++i ){
392			lp = (void *)job->datafiles.list[i];
393			size +=  Find_flag_value(lp,SIZE,Value_sep);
394		}
395		Set_decimal_value(&job->info,SIZE,size);
396	}
397
398	Make_identifier( job );
399	Check_for_hold( job, spool_control );
400
401#ifdef ORIGINAL_DEBUG//JY@1020
402	if(DEBUGL3)Dump_job("Setup_job",job);
403#endif
404}
405#endif
406
407/* Get_hold_class( spool_control, job )
408 *  check to see if the spool class and the job class are compatible
409 *  returns:  non-zero if held, 0 if not held
410 *   i.e.- cmpare(spoolclass,jobclass)
411 */
412
413int Get_hold_class( struct line_list *info, struct line_list *sq )
414{
415	int held, i;
416	char *s, *t;
417	struct line_list l;
418
419	Init_line_list(&l);
420	held = 0;
421	if( (s = Clsses(sq))
422		&& (t = Find_str_value(info,CLASS,Value_sep)) ){
423		held = 1;
424		Free_line_list(&l);
425		Split(&l,s,File_sep,0,0,0,0,0,0);
426		for( i = 0; held && i < l.count; ++i ){
427			held = Globmatch( l.list[i], t );
428		}
429		Free_line_list(&l);
430	}
431	return(held);
432}
433
434/*
435 * Extract the control file and data file information from the
436 * control file image
437 *
438 * Note: we also handle HP extensions here.
439 *
440 *  1. 4-Digit Job Numbers
441 *  ----------------------
442 *
443 *  HP preserves the System V-style 4-digit sequence number, or job number, in
444 *  file names and attributes, while BSD uses 3-digit job numbers.
445 *
446 *
447 *  2. Control and Data File Naming Conventions
448 *  -------------------------------------------
449 *
450 *  Control files are named in the following format:
451 *
452 *     cA<seqn><host>
453 *
454 *     <seqn> is the 4-digit sequence number (aka job number).
455 *     <host> is the originating host name.
456 *
457 *  The data file naming sequence format is:
458 *
459 *      dA<seqn><host>   through   dZ<seqn><host>     followed by...
460 *      da<seqn><host>   through   dz<seqn><host>     followed by...
461 *      eA<seqn><host>   through   eZ<seqn><host>     followed by...
462 *      ea<seqn><host>   through   ez<seqn><host>     ... etc. ...
463 *
464 *
465 *  So the first data file name in a request begins with "dA", the second with
466 *  "dB", the 27th with "da", the 28th with "db", and so forth.
467 *
468 *
469 * 3. HP-Specific BSD Job Attributes (Control File Lines)
470 *  ------------------------------------------------------
471 *
472 *  The following control file lines are extensions of RFC-1179:
473 *
474 *     R<login>
475 *
476 *        Write to the named login's terminal when the job is complete.  This is
477 *        an alternate to the RFC-1179-style e-mail completion notification.
478 *        This notification is selected via the lp "-w" option.
479 *
480 *     A<priority>
481 *
482 *        Specifies the System V-style priority of the request, a single digit
483 *        from 0-7.
484 *
485 *     N B<banner>
486 *
487 *        Note that this line begins with an "N", a space, and then a "B".  The
488 *        argument is the banner page title requested via the lp "-t" option.  If
489 *        that option was not given then the argument is null.
490 *
491 *     N O<options>
492 *
493 *        Note that this line begins with an "N", a space, and then an "O".  The
494 *        argument contains the System V-style "-o" options specified in the lp
495 *        command line.  The option names appear without a leading "-o".  The
496 *        first option name begins in the fourth character of the line; each
497 *        option is separated by a blank.  If no "-o" options were given then the
498 *        argument is null.
499 *
500 */
501
502#if defined(JY20031104Append_Z_value)
503void Append_Z_value( struct job *job, char *s )
504{
505	char *t;
506
507	/* check for empty string */
508	if( !s || !*s ) return;
509	t = Find_str_value(&job->info,"Z",Value_sep);
510	if( t && *t ){
511		t = safestrdup3(t,",",s,__FILE__,__LINE__);
512		Set_str_value(&job->info,"Z",t);
513		if( t ) free(t); t = 0;
514	} else {
515		Set_str_value(&job->info,"Z",s);
516	}
517}
518#endif
519
520#if defined(JY20031104Setup_cf_info)
521int Setup_cf_info( struct job *job, int check_for_existence )
522{
523	char *s;
524	int i, c, n, copies = 0, last_format = 0;
525	struct line_list cf_line_list;
526	struct line_list *datafile = 0;
527	struct stat statb;
528	char buffer[SMALLBUFFER], *t;
529	char *file_found;
530	char *names = 0;
531	int returnstatus = 0;
532	int hpformat;
533
534	Init_line_list(&cf_line_list);
535	names = 0;
536
537	hpformat = Find_flag_value(&job->info,HPFORMAT,Value_sep);
538	if( (s = Find_str_value(&job->info,DATAFILES,Value_sep)) ){
539		Split(&cf_line_list,s,"\001",0,0,0,0,0,0);
540	} else {
541		s = Find_str_value(&job->info,OPENNAME,Value_sep);
542		if( !s ) s = Find_str_value(&job->info,TRANSFERNAME,Value_sep);
543		DEBUG3("Setup_cf_info: control file '%s', hpformat '%d'", s, hpformat );
544		if( Get_file_image_and_split(s,0,0, &cf_line_list, Line_ends,0,0,0,0,0,0)
545			 && check_for_existence ){
546#ifdef ORIGINAL_DEBUG//JY@1020
547			DEBUG3("Setup_cf_info: missing or empty control file '%s'", s );
548			SNPRINTF(buffer,sizeof(buffer))
549				"no control file %s - %s", s, Errormsg(errno) );
550#endif
551			Set_str_value(&job->info,ERROR,buffer);
552			Set_nz_flag_value(&job->info,ERROR_TIME,time(0));
553			returnstatus = 1;
554			goto done;
555		}
556	}
557
558	Free_listof_line_list( &job->datafiles );
559
560	file_found = 0;
561	datafile = malloc_or_die(sizeof(datafile[0]),__FILE__,__LINE__);
562	memset(datafile,0,sizeof(datafile[0]));
563
564	for( i = 0; i < cf_line_list.count; ++i ){
565		s = cf_line_list.list[i];
566		Clean_meta(s);
567		c = cval(s);
568		DEBUG3("Setup_cf_info: doing line '%s'", s );
569		if( islower(c) ){
570			t = s;
571			while( (t = strpbrk(t," \t")) ) *t++ = '_';
572			if( file_found && (safestrcmp(file_found,s+1) || last_format != c) ){
573				Check_max(&job->datafiles,1);
574				job->datafiles.list[job->datafiles.count++] = (void *)datafile;
575				copies = 0;
576				file_found = 0;
577				datafile = malloc_or_die(sizeof(datafile[0]),__FILE__,__LINE__);
578				memset(datafile,0,sizeof(datafile[0]));
579			}
580
581			last_format = c;
582			buffer[0] = c; buffer[1] = 0;
583			Set_str_value(datafile,FORMAT,buffer);
584
585			++copies;
586			Set_flag_value(datafile,COPIES,copies);
587
588			Set_str_value(datafile,TRANSFERNAME,s+1);
589			Set_str_value(datafile,OPENNAME,s+1);
590			file_found = Find_str_value(datafile,TRANSFERNAME,Value_sep);
591			DEBUG4("Setup_cf_info: doing file '%s', format '%c', copies %d",
592				file_found, last_format, copies );
593
594			/* now we check for the status */
595			if( check_for_existence ){
596				if( stat(file_found, &statb) == 0 ){
597					double size;
598					size = statb.st_size;
599					DEBUG4("Setup_cf_info: '%s' - size %0.0f", file_found, size );
600					Set_double_value(datafile,SIZE,size );
601				} else {
602#ifdef ORIGINAL_DEBUG//JY@1020
603					SNPRINTF(buffer,sizeof(buffer))
604						"missing data file %s - %s", file_found, Errormsg(errno) );
605#endif
606					Set_str_value(&job->info,ERROR,buffer);
607					Set_nz_flag_value(&job->info,ERROR_TIME,time(0));
608					returnstatus = 1;
609					goto done;
610				}
611			}
612		} else if( c == 'N' ){
613			if( hpformat && cval(s+1) == ' '){
614				/* this is an HP Format option */
615				/* N B<banner> -> 'T' line */
616				/* N Ooption option option-> prefix to Z */
617				c = cval(s+2);
618				if( c == 'B' ){
619					if( s[3] ) Set_str_value(&job->info,"T",s+3);
620				} else if( c == 'O' ){
621					s = s+3;
622					if( safestrlen(s) ){
623						for( t = s; (t = strpbrk(t," ")); ++t ){
624							*t = ',';
625						}
626						Append_Z_value(job,s);
627					}
628				}
629				continue;
630			}
631			/* if we have a file name AND an 'N' for it, then set up a new file */
632			if( file_found && (t = Find_str_value(datafile,"N",Value_sep))
633				/* && safestrcmp(t,s+1) */ ){
634				Check_max(&job->datafiles,1);
635				job->datafiles.list[job->datafiles.count++] = (void *)datafile;
636				copies = 0;
637				file_found = 0;
638				datafile = malloc_or_die(sizeof(datafile[0]),__FILE__,__LINE__);
639				memset(datafile,0,sizeof(datafile[0]));
640			}
641			Set_str_value(datafile,"N",s+1);
642			if( !names ){
643				names = safestrdup(s+1,__FILE__,__LINE__);
644			} else {
645				names =  safeextend3(names,",",s+1,__FILE__,__LINE__);
646			}
647		} else if( c == 'U' ){
648			Set_str_value(datafile,"U",s+1);
649		} else {
650			if( hpformat && c == 'Z' ){
651				Append_Z_value( job, s+1 );
652			} else if( hpformat && c == 'R' ){
653				Set_str_value(&job->info,"M",s+1);
654			} else if( hpformat && c == 'A' ){
655				n = strtol( s+1,0,10);
656				if( n >= 0 && n <=10){
657					c = n + 'A';
658					buffer[0] = n + 'A';
659					buffer[1] = 0;
660					Set_str_value(&job->info,PRIORITY,buffer);
661				}
662			} else if( isupper(c) ){
663				buffer[0] = c; buffer[1] = 0;
664				DEBUG4("Setup_cf_info: control '%s'='%s'", buffer, s+1 );
665				Set_str_value(&job->info,buffer,s+1);
666			}
667		}
668	}
669	if( file_found ){
670		Check_max(&job->datafiles,1);
671		job->datafiles.list[job->datafiles.count++] = (void *)datafile;
672		datafile = 0;
673	} else {
674		free(datafile);
675		datafile = 0;
676	}
677	Set_str_value(&job->info,FILENAMES,names);
678
679 done:
680
681	if( datafile )	free(datafile); datafile=0;
682	if( names )	free(names); names=0;
683	Free_line_list( &cf_line_list );
684#ifdef ORIGINAL_DEBUG//JY@1020
685	if(DEBUGL4)Dump_job("Setup_cf_info - final",job);
686#endif
687	return(returnstatus);
688}
689#endif
690
691char *Make_hf_image( struct job *job )
692{
693	char *outstr, *s;
694	int i;
695	int len = safestrlen(OPENNAME);
696
697	outstr = 0;
698	for( i = 0; i < job->info.count; ++i ){
699		s = job->info.list[i];
700		if( !ISNULL(s) && !safestrpbrk(s,Line_ends) &&
701			safestrncasecmp(OPENNAME,s,len) ){
702			outstr = safeextend3(outstr,s,"\n",__FILE__,__LINE__);
703		}
704	}
705	return( outstr );
706}
707
708/*
709 * Write a hold file
710 */
711
712int Set_hold_file( struct job *job, struct line_list *perm_check, int fd )
713{
714	char *hf_name, *tempfile, *outstr;
715	int status;
716
717	status = 0;
718	outstr = 0;
719
720#ifdef ORIGINAL_DEBUG//JY@1020
721	if(DEBUGL4)Dump_job("Set_hold_file",job);
722#endif
723	Set_str_value(&job->info,UPDATE_TIME,Time_str(0,0));
724	if( !(hf_name = Find_str_value(&job->info,HF_NAME,Value_sep)) ){
725		Errorcode = JABORT;
726		FATAL(LOG_ERR)"Set_hold_file: LOGIC ERROR- no HF_NAME in job information");
727	}
728	outstr = Make_hf_image( job );
729	if( !fd ){
730		fd = Make_temp_fd( &tempfile );
731		if( Write_fd_str(fd, outstr) < 0 ){
732			LOGERR(LOG_INFO)"Set_hold_file: write to '%s' failed", tempfile );
733			status = 1;
734		}
735		close(fd);
736		if( status == 0 && rename( tempfile, hf_name ) == -1 ){
737			LOGERR(LOG_INFO)"Set_hold_file: rename '%s' to '%s' failed",
738				tempfile, hf_name );
739			status = 1;
740		}
741	} else {
742		if( lseek( fd, 0, SEEK_SET ) == -1 ){
743			LOGERR_DIE(LOG_ERR) "Set_hold_file: lseek failed" );
744		}
745		if( ftruncate( fd, 0 ) ){
746			LOGERR_DIE(LOG_ERR) "Set_hold_file: ftruncate failed" );
747		}
748		if( Write_fd_str(fd, outstr) < 0 ){
749			LOGERR(LOG_INFO)"Set_hold_file: write to '%s' failed", hf_name );
750			status = 1;
751		}
752		/* close(fd); */
753	}
754
755	if( Lpq_status_file_DYN ){
756		unlink(Lpq_status_file_DYN );
757	}
758
759	/* we do this when we have a logger */
760	if( status == 0 && Logger_fd > 0 ){
761		char *t, *u;
762		if( perm_check ){
763			u = Join_line_list( perm_check, "\n" );
764			t = Escape(u,1);
765			outstr = safeextend5(outstr,"\n",LPC,"=",u,__FILE__,__LINE__);
766			if(u) free(u); u = 0;
767			if(t) free(t); t = 0;
768		}
769#ifdef ORIGINAL_DEBUG//JY@1020
770		send_to_logger(-1, -1, job,UPDATE,outstr);
771#endif
772	}
773	if( outstr ) free( outstr ); outstr = 0;
774	return( status );
775}
776
777/*
778 * Get_hold_file( struct job *job, char *hf_name )
779 *
780 *  get hold file contents and initialize job->info hash with them
781 */
782
783void Get_hold_file( struct job *job, char *hf_name )
784{
785	char *s;
786	if( (s = safestrchr(hf_name, '=')) ){
787		hf_name = s+1;
788	}
789	DEBUG1("Get_hold_file: checking on '%s'", hf_name );
790
791	Get_file_image_and_split( hf_name, 0, 0,
792		&job->info, Line_ends, 1, Value_sep,1,1,1,0);
793	if( !Find_str_value(&job->info,HF_NAME,Value_sep) ){
794		Set_str_value(&job->info,HF_NAME,hf_name);
795	}
796}
797
798/*
799 * Get Spool Control Information
800 *  - simply read the file
801 */
802
803void Get_spool_control( const char *file, struct line_list *info )
804{
805	Free_line_list(info);
806	DEBUG2("Get_spool_control:  file '%s'", file );
807	Get_file_image_and_split( file, 0, 0,
808			info,Line_ends,1,Value_sep,1,1,1,0);
809#ifdef ORIGINAL_DEBUG//JY@1020
810	if(DEBUGL4)Dump_line_list("Get_spool_control- info", info );
811#endif
812}
813
814/*
815 * Set Spool Control Information
816 *  - simply write the file
817 */
818
819void Set_spool_control( struct line_list *perm_check, const char *file,
820	struct line_list *info )
821{
822	char *s, *t, *tempfile;
823	struct line_list l;
824	int fd;
825
826	s = t = tempfile = 0;
827	Init_line_list(&l);
828	fd = Make_temp_fd( &tempfile );
829	DEBUG2("Set_spool_control: file '%s', tempfile '%s'",
830		file, tempfile );
831#ifdef ORIGINAL_DEBUG//JY@1020
832	if(DEBUGL4)Dump_line_list("Set_spool_control- info", info );
833#endif
834	s = Join_line_list(info,"\n");
835	if( Write_fd_str(fd, s) < 0 ){
836		Errorcode = JFAIL;
837		LOGERR_DIE(LOG_INFO)"Set_spool_control: cannot write tempfile '%s'",
838			tempfile );
839	}
840	close(fd);
841	if( rename( tempfile, file ) == -1 ){
842		Errorcode = JFAIL;
843		LOGERR_DIE(LOG_INFO)"Set_spool_control: rename of '%s' to '%s' failed",
844			tempfile, file );
845	}
846	/* force and update of the cached status */
847
848	if( Lpq_status_file_DYN ){
849		/* do not check to see if this works */
850		unlink(Lpq_status_file_DYN);
851	}
852
853	if( Logger_fd ){
854		/* log the spool control file changes */
855		t = Escape(s,1);
856		Set_str_value(&l,QUEUE,t);
857		if(s) free(s); s = 0;
858		if(t) free(t); t = 0;
859
860		if( perm_check ){
861			s = Join_line_list( perm_check, "\n" );
862			t = Escape(s,1);
863			Set_str_value(&l,LPC,t);
864			if(s) free(s); s = 0;
865			if(t) free(t); t = 0;
866		}
867		t = Join_line_list( &l, "\n");
868
869#ifdef ORIGINAL_DEBUG//JY@1020
870		send_to_logger(-1,-1,0,QUEUE,t);
871#endif
872	}
873
874	Free_line_list(&l);
875	if(s) free(s); s = 0;
876	if(t) free(t); t = 0;
877}
878
879void intval( const char *key, struct line_list *list, struct job *job )
880{
881	int i = Find_flag_value(list,key,Value_sep);
882	int len = safestrlen(job->sort_key);
883	SNPRINTF(job->sort_key+len,sizeof(job->sort_key)-len)
884    "|%s.0x%08x",key,i&0xffffffff);
885	DEBUG5("intval: '%s'", job->sort_key );
886}
887
888void revintval( const char *key, struct line_list *list, struct job *job )
889{
890	int i = Find_flag_value(list,key,Value_sep);
891	int len = safestrlen(job->sort_key);
892	SNPRINTF(job->sort_key+len,sizeof(job->sort_key)-len)
893	"|%s.0x%08x",key,(~i)&0xffffffff);
894	DEBUG5("revintval: '%s'", job->sort_key );
895}
896
897void strzval( const char *key, struct line_list *list, struct job *job )
898{
899	char *s = Find_str_value(list,key,Value_sep);
900	int len = safestrlen(job->sort_key);
901	SNPRINTF(job->sort_key+len,sizeof(job->sort_key)-len)
902	"|%s.%d",key,s!=0);
903	DEBUG5("strzval: '%s'", job->sort_key );
904}
905
906void strnzval( const char *key, struct line_list *list, struct job *job )
907{
908	char *s = Find_str_value(list,key,Value_sep);
909	int len = safestrlen(job->sort_key);
910	SNPRINTF(job->sort_key+len,sizeof(job->sort_key)-len)
911	"|%s.%d",key,(s==0 || *s == 0));
912	DEBUG5("strnzval: '%s'", job->sort_key );
913}
914
915void strval( const char *key, struct line_list *list, struct job *job,
916	int reverse )
917{
918	char *s = Find_str_value(list,key,Value_sep);
919	int len = safestrlen(job->sort_key);
920	int c = 0;
921
922	if(s) c = cval(s);
923	if( reverse ) c = -c;
924	c = 0xFF & (-c);
925	SNPRINTF(job->sort_key+len,sizeof(job->sort_key)-len)
926	"|%s.%02x",key,c);
927	DEBUG5("strval: '%s'", job->sort_key );
928}
929
930
931/*
932 * Make_sort_key
933 *   Make a sort key from the image information
934 */
935void Make_sort_key( struct job *job )
936{
937	job->sort_key[0] = 0;
938	if( Order_routine_DYN ){
939#if defined(ORDER_ROUTINE)
940		extern char *ORDER_ROUTINE( struct job *job );
941		ORDER_ROUTINE( &job );
942#else
943		Errorcode = JABORT;
944		FATAL(LOG_ERR)"Make_sort_key: order_routine requested and ORDER_ROUTINE undefined");
945#endif
946	} else {
947		/* first key is REMOVE_TIME - remove jobs come last */
948		intval(REMOVE_TIME,&job->info,job);
949#if 0
950		/* first key is DONE_TIME - done jobs come last */
951		intval(DONE_TIME,&job->info,job);
952		intval(INCOMING_TIME,&job->info,job);
953		/* next key is ERROR - error jobs jobs come before removed */
954		intval(ERROR_TIME,&job->info,job);
955#endif
956		/* next key is HOLD - before the error jobs  */
957		intval(HOLD_CLASS,&job->info,job);
958		intval(HOLD_TIME,&job->info,job);
959		/* next key is MOVE - before the held jobs  */
960		strnzval(MOVE,&job->info,job);
961		/* now by priority */
962		if( Ignore_requested_user_priority_DYN == 0 ){
963			strval(PRIORITY,&job->info,job,Reverse_priority_order_DYN);
964		}
965		/* now we do TOPQ */
966		revintval(PRIORITY_TIME,&job->info,job);
967		/* now we do FirstIn, FirstOut */
968		intval(JOB_TIME,&job->info,job);
969		intval(JOB_TIME_USEC,&job->info,job);
970		/* now we do by job number if two at same time (very unlikely) */
971		intval(NUMBER,&job->info,job);
972	}
973}
974#ifdef REMOVE
975
976/*
977 * Set up printer
978 *  1. reset configuration information
979 *  2. check the printer name
980 *  3. get the printcap information
981 *  4. set the configuration variables
982 *  5. If run on the server,  then check for the Lp_device_DYN
983 *     being set.  If it is set, then clear the RemotePrinter_DYN
984 *     and RemoteHost_DYN.
985 */
986
987#if defined(JY20031104Setup_printer)
988int Setup_printer( char *prname, char *error, int errlen, int subserver )
989{
990	char *s;
991	int status = 0;
992	char name[SMALLBUFFER];
993
994	DEBUG3( "Setup_printer: checking printer '%s'", prname );
995
996	/* reset the configuration information, just in case
997	 * this is a subserver or being used to get status
998	 */
999	safestrncpy(name,prname);
1000	if( error ) error[0] = 0;
1001	if( (s = Is_clean_name( name )) ){
1002		SNPRINTF( error, errlen)
1003			"printer '%s' has illegal char at '%s' in name", name, s );
1004		status = 1;
1005		goto error;
1006	}
1007	lowercase(name);
1008	if( !subserver && Status_fd > 0 ){
1009		close( Status_fd );
1010		Status_fd = -1;
1011	}
1012	Set_DYN(&Printer_DYN,name);
1013	Fix_Rm_Rp_info(0,0);
1014
1015	if( Spool_dir_DYN == 0 || *Spool_dir_DYN == 0 ){
1016		SNPRINTF( error, errlen)
1017"spool queue for '%s' does not exist on server %s\n   non-existent printer or you need to run 'checkpc -f'",
1018			name, FQDNHost_FQDN );
1019		status = 2;
1020		goto error;
1021	}
1022
1023	if( chdir( Spool_dir_DYN ) < 0 ){
1024#ifdef ORIGINAL_DEBUG//JY@1020
1025		SNPRINTF( error, errlen)
1026			"printer '%s', chdir to '%s' failed '%s'",
1027				name, Spool_dir_DYN, Errormsg( errno ) );
1028#endif
1029		status = 2;
1030		goto error;
1031	}
1032
1033	/*
1034	 * get the override information from the control/spooling
1035	 * directory
1036	 */
1037
1038	Get_spool_control( Queue_control_file_DYN, &Spool_control );
1039
1040	if( Perm_filters_line_list.count ){
1041		Free_line_list(&Perm_line_list);
1042		Merge_line_list(&Perm_line_list,&RawPerm_line_list,0,0,0);
1043		Filterprintcap( &Perm_line_list, &Perm_filters_line_list,
1044			Printer_DYN );
1045	}
1046
1047#ifdef ORIGINAL_DEBUG//JY@1020
1048	DEBUG1("Setup_printer: printer now '%s', spool dir '%s'",
1049		Printer_DYN, Spool_dir_DYN );
1050	if(DEBUGL3){
1051		Dump_parms("Setup_printer - vars",Pc_var_list);
1052		Dump_line_list("Setup_printer - spool control", &Spool_control );
1053	}
1054#endif
1055
1056 error:
1057	DEBUG3("Setup_printer: status '%d', error '%s'", status, error );
1058	return( status );
1059}
1060#endif
1061
1062/**************************************************************************
1063 * Read_pid( int fd, char *str, int len )
1064 *   - Read the pid from a file
1065 **************************************************************************/
1066int Read_pid( int fd, char *str, int len )
1067{
1068	char line[LINEBUFFER];
1069	int n;
1070
1071	if( lseek( fd, 0, SEEK_SET ) == -1 ){
1072		LOGERR_DIE(LOG_ERR) "Read_pid: lseek failed" );
1073	}
1074
1075	if( str == 0 ){
1076		str = line;
1077		len = sizeof( line );
1078	}
1079	str[0] = 0;
1080	if( (n = read( fd, str, len-1 ) ) < 0 ){
1081		LOGERR_DIE(LOG_ERR) "Read_pid: read failed" );
1082	}
1083	str[n] = 0;
1084	n = atoi( str );
1085	DEBUG3( "Read_pid: %d", n );
1086	return( n );
1087}
1088
1089/**************************************************************************
1090 * Write_pid( int fd )
1091 *   - Write the pid to a file
1092 **************************************************************************/
1093int Write_pid( int fd, int pid, char *str )
1094{
1095	char line[LINEBUFFER];
1096
1097	if( lseek( fd, 0, SEEK_SET ) == -1 ){
1098		LOGERR(LOG_ERR) "Write_pid: lseek failed" );
1099		return -1;
1100	}
1101	if( ftruncate( fd, 0 ) ){
1102		LOGERR(LOG_ERR) "Write_pid: ftruncate failed" );
1103		return -1;
1104	}
1105
1106	if( str == 0 ){
1107		SNPRINTF( line, sizeof(line)) "%d\n", pid );
1108	} else {
1109		SNPRINTF( line, sizeof(line)) "%s\n", str );
1110	}
1111	DEBUG3( "Write_pid: pid %d, str '%s'", pid, str );
1112	if( Write_fd_str( fd, line ) < 0 ){
1113		LOGERR(LOG_ERR) "Write_pid: write failed" );
1114		return -1;
1115	}
1116	return 0;
1117}
1118#endif
1119/***************************************************************************
1120 * int Patselect( struct line_list *tokens, struct line_list *cf );
1121 *    check to see that the token value matches one of the following
1122 *    in the control file:
1123 *  token is INTEGER: then matches the job number
1124 *  token is string: then matches either the user name or host name
1125 *    then try glob matching job ID
1126 *  return:
1127 *   0 if match found
1128 *   nonzero if not match found
1129 ***************************************************************************/
1130
1131int Patselect( struct line_list *token, struct line_list *cf, int starting )
1132{
1133	int match = 1;
1134	int i, n, val;
1135	char *key, *s, *end;
1136
1137#ifdef ORIGINAL_DEBUG//JY@1020
1138	if(DEBUGL3)Dump_line_list("Patselect- tokens", token );
1139	if(DEBUGL3)Dump_line_list("Patselect- info", cf );
1140#endif
1141	for( i = starting; match && i < token->count; ++i ){
1142		key = token->list[i];
1143		DEBUG3("Patselect: key '%s'", key );
1144		/* handle wildcard match */
1145		if( !(match = safestrcasecmp( key, "all" ))){
1146			break;
1147		}
1148		end = key;
1149		val = strtol( key, &end, 10 );
1150		if( *end == 0 ){
1151			n = Find_decimal_value(cf,NUMBER,Value_sep);
1152			/* we check job number */
1153			DEBUG3("Patselect: job number check '%d' to job %d",
1154				val, n );
1155			match = (val != n);
1156		} else {
1157			/* now we check to see if we have a name match */
1158			if( (s = Find_str_value(cf,LOGNAME,Value_sep))
1159				&& !(match = Globmatch(key,s)) ){
1160				break;
1161			}
1162			if( (s = Find_str_value(cf,IDENTIFIER,Value_sep))
1163				&& !(match = Globmatch(key,s)) ){
1164				break;
1165			}
1166		}
1167	}
1168	DEBUG3("Patselect: returning %d", match);
1169	return(match);
1170}
1171
1172/***************************************************************************
1173 * char * Check_format( int type, char *name, struct control_file *job )
1174 * Check to see that the file name has the correct format
1175 * name[0] == 'c' or 'd' (type)
1176 * name[1] = 'f'
1177 * name[2] = A-Za-z
1178 * name[3-5] = NNN
1179 * name[6-end] = only alphanumeric and ., _, or - chars
1180 * RETURNS: 0 if OK, error message (string) if not
1181 *
1182 * Summary of HP's Extensions to RFC-1179
1183 * 1. 4-Digit Job Numbers
1184 *  ----------------------
1185 *  HP preserves the System V-style 4-digit sequence number, or job number, in
1186 *  file names and attributes, while BSD uses 3-digit job numbers.
1187 *  2. Control and Data File Naming Conventions
1188 *  -------------------------------------------
1189 *  Control files are named in the following format:
1190 *     cA<seqn><host>
1191 *     <seqn> is the 4-digit sequence number (aka job number).
1192 *     <host> is the originating host name.
1193 *  The data file naming sequence format is:
1194 *      dA<seqn><host>   through   dZ<seqn><host>     followed by...
1195 *      da<seqn><host>   through   dz<seqn><host>     followed by...
1196 *      eA<seqn><host>   through   eZ<seqn><host>     followed by...
1197 *      ea<seqn><host>   through   ez<seqn><host>     ... etc. ...
1198 *  So the first data file name in a request begins with "dA", the second with
1199 *  "dB", the 27th with "da", the 28th with "db", and so forth.
1200
1201 ***************************************************************************/
1202#if defined(JY20031104Check_format)
1203int Check_format( int type, const char *name, struct job *job )
1204{
1205	int n, c, hpformat;
1206	const char *s;
1207	char *t;
1208	char msg[SMALLBUFFER];
1209
1210	DEBUG4("Check_format: type %d, name '%s'", type, name );
1211	msg[0] = 0;
1212	hpformat = 0;
1213	n = cval(name);
1214	switch( type ){
1215	case DATA_FILE:
1216		if( n != 'd' ){
1217			SNPRINTF(msg, sizeof(msg))
1218				"data file does not start with 'd' - '%s'",
1219				name );
1220			goto error;
1221		}
1222		break;
1223	case CONTROL_FILE:
1224		if( n != 'c' ){
1225			SNPRINTF(msg, sizeof(msg))
1226				"control file does not start with 'c' - '%s'",
1227				name );
1228			goto error;
1229		}
1230		break;
1231	default:
1232		SNPRINTF(msg, sizeof(msg))
1233			"bad file type '%c' - '%s' ", type,
1234			name );
1235		goto error;
1236	}
1237	/* check for second letter */
1238	n = cval(name+1);
1239	if( n == 'A' ){
1240		/* HP format */
1241		hpformat = 1;
1242	} else if( n != 'f' ){
1243		SNPRINTF(msg, sizeof(msg))
1244			"second letter must be f not '%c' - '%s' ", n, name );
1245		goto error;
1246	} else {
1247		n = cval(name+2);
1248		if( !isalpha( n ) ){
1249			SNPRINTF(msg, sizeof(msg))
1250				"third letter must be letter not '%c' - '%s' ", n, name );
1251			goto error;
1252		}
1253    }
1254	if( type == CONTROL_FILE ){
1255		SNPRINTF(msg,sizeof(msg))"%c",n);
1256		Set_str_value(&job->info,PRIORITY,msg);
1257		msg[0] = 0;
1258	}
1259	/*
1260		we now enter the wonderful world of 'conventions'
1261		cfAnnnHostname
1262              ^^^^ starts with letter (number len = 0, 1, 2, 3)
1263		cfAnnnIPV4.Add.ress  (number len = 4, 5, ... )
1264              ^^^^ starts with number or is a 3com type thing
1265		cfAnnnnnnHostName    (len = 6)
1266                 ^^^^ starts with letter
1267		cfAnnnnnnIPV4.Add.ress  (len = 7, ... )
1268                 ^^^^ starts with number
1269	*/
1270
1271	if( hpformat ){
1272		/* we have four digits */
1273		safestrncpy(msg,&name[2]);
1274		t = 0;
1275		n = strtol(msg,&t,10);
1276	} else {
1277		safestrncpy(msg,&name[3]);
1278		for( t = msg; isdigit(cval(t)); ++t );
1279		c = t - msg;
1280		switch( c ){
1281		case 0: case 1: case 2: case 3:
1282			break;
1283		case 4: case 5:
1284			c = 3;
1285			break;
1286		default:
1287			if( cval(msg+6) == '.' ) c = 3;
1288			else c = 6;
1289			break;
1290		}
1291		/* get the number */
1292		t = &msg[c];
1293		c = *t;
1294		*t = 0;
1295		n = strtol(msg,0,10);
1296		*t = c;
1297    }
1298
1299	DEBUG1("Check_format: name '%s', number %d, file '%s'", name, n, t );
1300	if( Find_str_value( &job->info,NUMBER,Value_sep) ){
1301		c = Find_decimal_value( &job->info,NUMBER,Value_sep);
1302		if( c != n ){
1303			SNPRINTF(msg, sizeof(msg))
1304				"job numbers differ '%s', old %d and new %d",
1305					name, c, n );
1306			goto error;
1307		}
1308	} else {
1309		Fix_job_number( job, n );
1310	}
1311	Clean_name(t);
1312	if( (s = Find_str_value( &job->info,FILE_HOSTNAME,Value_sep)) ){
1313		if( safestrcasecmp(s,t) ){
1314			SNPRINTF(msg, sizeof(msg))
1315				"bad hostname '%s' - '%s' ", t, name );
1316			goto error;
1317		}
1318	} else {
1319		Set_str_value(&job->info,FILE_HOSTNAME,t);
1320	}
1321	/* clear out error message */
1322	msg[0] = 0;
1323
1324 error:
1325	if( hpformat ){
1326		Set_flag_value(&job->info,HPFORMAT,hpformat);
1327	}
1328	if( msg[0] ){
1329		DEBUG1("Check_format: %s", msg );
1330		Set_str_value(&job->info,FORMAT_ERROR,msg);
1331	}
1332	return( msg[0] != 0 );
1333}
1334#endif
1335
1336char *Find_start(char *str, const char *key )
1337{
1338	int n = safestrlen(key);
1339	while( (str = strstr(str,key)) && str[n] != '=' );
1340	if( str ) str += (n+1);
1341	return( str );
1342}
1343
1344char *Frwarding(struct line_list *l)
1345{
1346	return( Find_str_value(l,FORWARDING,Value_sep) );
1347}
1348int Pr_disabled(struct line_list *l)
1349{
1350	return( Find_flag_value(l,PRINTING_DISABLED,Value_sep) );
1351}
1352int Sp_disabled(struct line_list *l)
1353{
1354	return( Find_flag_value(l,SPOOLING_DISABLED,Value_sep) );
1355}
1356int Pr_aborted(struct line_list *l)
1357{
1358	return( Find_flag_value(l,PRINTING_ABORTED,Value_sep) );
1359}
1360int Hld_all(struct line_list *l)
1361{
1362	return( Find_flag_value(l,HOLD_ALL,Value_sep) );
1363}
1364char *Clsses(struct line_list *l)
1365{
1366	return( Find_str_value(l,CLASS,Value_sep) );
1367}
1368char *Cntrol_debug(struct line_list *l)
1369{
1370	return( Find_str_value(l,DEBUG,Value_sep) );
1371}
1372char *Srver_order(struct line_list *l)
1373{
1374	return( Find_str_value(l,SERVER_ORDER,Value_sep) );
1375}
1376
1377/*
1378 * Job datastructure management
1379 */
1380
1381void Init_job( struct job *job )
1382{
1383	memset(job,0,sizeof(job[0]) );
1384}
1385
1386void Free_job( struct job *job )
1387{
1388	Free_line_list( &job->info );
1389	Free_listof_line_list( &job->datafiles );
1390	Free_line_list( &job->destination );
1391}
1392
1393void Copy_job( struct job *dest, struct job *src )
1394{
1395	Merge_line_list( &dest->info, &src->info, 0,0,0 );
1396	Merge_listof_line_list( &dest->datafiles, &src->datafiles, 0,0,0 );
1397	Merge_line_list( &dest->destination, &src->destination, 0,0,0 );
1398}
1399
1400/**************************************************************************
1401 * static int Fix_job_number();
1402 * - fixes the job number range and value
1403 **************************************************************************/
1404
1405char *Fix_job_number( struct job *job, int n )
1406{
1407	char buffer[SMALLBUFFER];
1408	int len = 3, max = 1000;
1409
1410	if( n == 0 ){
1411		n = Find_decimal_value( &job->info, NUMBER, Value_sep );
1412	}
1413	if( Long_number_DYN && !Backwards_compatible_DYN ){
1414		len = 6;
1415		max = 1000000;
1416	}
1417	SNPRINTF(buffer,sizeof(buffer))"%0*d",len, n % max );
1418	Set_str_value(&job->info,NUMBER,buffer);
1419	return( Find_str_value(&job->info,NUMBER,Value_sep) );
1420}
1421
1422/************************************************************************
1423 * Make_identifier - add an identifier field to the job
1424 *  the identifier has the format name@host%id
1425 *  It is put in the 'A' field on the name.
1426 *
1427 ************************************************************************/
1428
1429#if defined(JY20031104Make_identifier)
1430char *Make_identifier( struct job *job )
1431{
1432	char *user, *host, *s, *id;
1433	char number[32];
1434	int n;
1435
1436	if( !(s = Find_str_value( &job->info,IDENTIFIER,Value_sep )) ){
1437		if( !(user = Find_str_value( &job->info,"P",Value_sep ))){
1438			user = "nobody";
1439		}
1440		if( !(host= Find_str_value( &job->info,"H",Value_sep ))){
1441			host = "unknown";
1442		}
1443		n = Find_decimal_value( &job->info,NUMBER,Value_sep );
1444		SNPRINTF(number,sizeof(number))"%d",n);
1445		if( (s = safestrchr( host, '.' )) ) *s = 0;
1446		id = safestrdup5(user,"@",host,"+",number,__FILE__,__LINE__);
1447		if( s ) *s = '.';
1448		Set_str_value(&job->info,IDENTIFIER,id);
1449		if( id ) free(id); id = 0;
1450		s = Find_str_value(&job->info,IDENTIFIER,Value_sep);
1451	}
1452	return(s);
1453}
1454#endif
1455
1456#ifdef ORIGINAL_DEBUG//JY@1020
1457void Dump_job( char *title, struct job *job )
1458{
1459	int i;
1460	struct line_list *lp;
1461	if( title ) LOGDEBUG( "*** Job %s *** - 0x%lx", title, Cast_ptr_to_long(job));
1462#ifdef ORIGINAL_DEBUG//JY@1020
1463	Dump_line_list_sub( "info",&job->info);
1464	LOGDEBUG("  datafiles - count %d", job->datafiles.count );
1465#endif
1466	for( i = 0; i < job->datafiles.count; ++i ){
1467		char buffer[SMALLBUFFER];
1468		SNPRINTF(buffer,sizeof(buffer))"  datafile[%d]", i );
1469		lp = (void *)job->datafiles.list[i];
1470#ifdef ORIGINAL_DEBUG//JY@1020
1471		Dump_line_list_sub(buffer,lp);
1472#endif
1473	}
1474#ifdef ORIGINAL_DEBUG//JY@1020
1475	Dump_line_list_sub( "destination",&job->destination);
1476#endif
1477	if( title ) LOGDEBUG( "*** end ***" );
1478}
1479#endif
1480
1481#if defined(JY20031104Job_printable)
1482void Job_printable( struct job *job, struct line_list *spool_control,
1483	int *pprintable, int *pheld, int *pmove, int *perr, int *pdone )
1484{
1485	char *s;
1486	char buffer[SMALLBUFFER];
1487	char destbuffer[SMALLBUFFER];
1488	struct stat statb;
1489	int n, printable = 0, held = 0, move = 0, error = 0, done = 0,destination, destinations;
1490
1491#ifdef ORIGINAL_DEBUG//JY@1020
1492	if(DEBUGL4)Dump_job("Job_printable - job info",job);
1493	if(DEBUGL4)Dump_line_list("Job_printable - spool control",spool_control);
1494#endif
1495
1496	buffer[0] = 0;
1497	if( job->info.count == 0 ){
1498		SNPRINTF(buffer,sizeof(buffer)) "removed" );
1499	} else if( Find_flag_value(&job->info,INCOMING_TIME,Value_sep) ){
1500		SNPRINTF(buffer,sizeof(buffer)) "incoming" );
1501	} else if( (error = Find_flag_value(&job->info,ERROR_TIME,Value_sep)) ){
1502		SNPRINTF(buffer,sizeof(buffer)) "error" );
1503	} else if( Find_flag_value(&job->info,HOLD_TIME,Value_sep) ){
1504		SNPRINTF(buffer,sizeof(buffer)) "hold" );
1505		held = 1;
1506	} else if( (done = Find_flag_value(&job->info,DONE_TIME,Value_sep)) ){
1507		SNPRINTF(buffer,sizeof(buffer)) "done" );
1508	} else if( (n = Find_flag_value(&job->info,SERVER,Value_sep))
1509		&& kill( n, 0 ) == 0 ){
1510		int delta;
1511		time_t now = time((void *)0);
1512		time_t last_change = Find_flag_value(&job->info,START_TIME,Value_sep);
1513		if( !ISNULL(Status_file_DYN) && !stat( Status_file_DYN, &statb )
1514			&& last_change < statb.st_mtime ){
1515			last_change = statb.st_mtime;
1516		}
1517		if( !ISNULL(Log_file_DYN) && !stat( Log_file_DYN, &statb )
1518			&& last_change < statb.st_mtime ){
1519			last_change = statb.st_mtime;
1520		}
1521		delta = now - last_change;
1522		if( Stalled_time_DYN && delta > Stalled_time_DYN ){
1523			SNPRINTF( buffer, sizeof(buffer))
1524				"stalled(%dsec)", delta );
1525		} else {
1526			n = Find_flag_value(&job->info,ATTEMPT,Value_sep);
1527			SNPRINTF(buffer,sizeof(buffer)) "active" );
1528			if( n > 0 ){
1529				SNPRINTF( buffer, sizeof(buffer))
1530					"active(attempt-%d)", n+1 );
1531			}
1532		}
1533		printable = 1;
1534	} else if((s = Find_str_value(&job->info,MOVE,Value_sep)) ){
1535		SNPRINTF(buffer,sizeof(buffer)) "moved->%s", s );
1536		move = 1;
1537	} else if( Get_hold_class(&job->info, spool_control ) ){
1538		SNPRINTF(buffer,sizeof(buffer)) "holdclass" );
1539		held = 1;
1540	} else {
1541		printable = 1;
1542	}
1543	if( (destinations = Find_flag_value(&job->info,DESTINATIONS,Value_sep)) ){
1544		printable = 0;
1545		for( destination = 0; destination < destinations; ++destination ){
1546			Get_destination(job,destination);
1547#ifdef ORIGINAL_DEBUG//JY@1020
1548			if(DEBUGL4)Dump_job("Job_destination_printable - job",job);
1549#endif
1550			destbuffer[0] = 0;
1551			if( Find_flag_value(&job->destination,ERROR_TIME,Value_sep) ){
1552				SNPRINTF(destbuffer,sizeof(destbuffer)) "error" );
1553			} else if( Find_flag_value(&job->destination,HOLD_TIME,Value_sep) ){
1554				SNPRINTF(destbuffer,sizeof(destbuffer)) "hold" );
1555				held += 1;
1556			} else if( Find_flag_value(&job->destination,DONE_TIME,Value_sep) ){
1557				SNPRINTF(destbuffer,sizeof(destbuffer)) "done" );
1558			} else if( (n = Find_flag_value(&job->destination,SERVER,Value_sep))
1559				&& kill( n, 0 ) == 0 ){
1560				int delta;
1561				n = Find_flag_value(&job->destination,START_TIME,Value_sep);
1562				delta = time((void *)0) - n;
1563				if( Stalled_time_DYN && delta > Stalled_time_DYN ){
1564					SNPRINTF( destbuffer, sizeof(destbuffer))
1565						"stalled(%dsec)", delta );
1566				} else {
1567					n = Find_flag_value(&job->destination,ATTEMPT,Value_sep);
1568					SNPRINTF(destbuffer,sizeof(destbuffer)) "active" );
1569					if( n > 0 ){
1570						SNPRINTF( destbuffer, sizeof(destbuffer))
1571							"active(attempt-%d)", n+1 );
1572					}
1573				}
1574				printable += 1;
1575			} else if((s = Find_str_value(&job->destination,MOVE,Value_sep)) ){
1576				SNPRINTF(destbuffer,sizeof(destbuffer)) "moved->%s", s );
1577				move += 1;
1578			} else if( Get_hold_class(&job->destination, spool_control ) ){
1579				SNPRINTF(destbuffer,sizeof(destbuffer)) "holdclass" );
1580				held += 1;
1581			} else {
1582				printable += 1;
1583			}
1584			Set_str_value(&job->destination,PRSTATUS,destbuffer);
1585			Set_flag_value(&job->destination,PRINTABLE,printable);
1586			Set_flag_value(&job->destination,HELD,held);
1587			Update_destination(job);
1588		}
1589	}
1590
1591	Set_str_value(&job->info,PRSTATUS,buffer);
1592	Set_flag_value(&job->info,PRINTABLE,printable);
1593	Set_flag_value(&job->info,HELD,held);
1594	if( pprintable ) *pprintable = printable;
1595	if( pheld ) *pheld = held;
1596	if( pmove ) *pmove = move;
1597	if( perr ) *perr = error;
1598	if( pdone ) *pdone = done;
1599	DEBUG3("Job_printable: printable %d, held %d, move '%d', error '%d', done '%d', status '%s'",
1600		printable, held, move, error, done, buffer );
1601}
1602#endif
1603
1604#ifdef REMOVE
1605int Server_active( char *file )
1606{
1607	struct stat statb;
1608	int serverpid = 0;
1609	int fd = Checkread( file, &statb );
1610	if( fd >= 0 ){
1611		serverpid = Read_pid( fd, 0, 0 );
1612		close(fd);
1613		DEBUG5("Server_active: checking file %s, serverpid %d", file, serverpid );
1614		if( serverpid && kill(serverpid,0) ){
1615			serverpid = 0;
1616		}
1617	}
1618	DEBUG3("Server_active: file %s, serverpid %d", file, serverpid );
1619	return( serverpid );
1620}
1621#endif
1622
1623/*
1624 * Destination Information
1625 *   The destination information is stored in the control file
1626 * as lines of the form:
1627 * NNN=.....   where NNN is the destination number
1628 *                   .... is the escaped destination information
1629 * During normal printing or other activity,  the destination information
1630 * is unpacked into the job->destination line list.
1631 */
1632
1633/*
1634 * Update_destination updates the information with the values in the
1635 *  job->destination line list.
1636 */
1637void Update_destination( struct job *job )
1638{
1639	char *s, *t, buffer[SMALLBUFFER];
1640	int id;
1641	id = Find_flag_value(&job->destination,DESTINATION,Value_sep);
1642	SNPRINTF(buffer,sizeof(buffer))"DEST%d",id);
1643	s = Join_line_list(&job->destination,"\n");
1644	t = Escape(s,1);
1645	Set_str_value(&job->info,buffer,t);
1646	free(s);
1647	free(t);
1648#ifdef ORIGINAL_DEBUG//JY@1020
1649	if(DEBUGL4)Dump_job("Update_destination",job);
1650#endif
1651}
1652
1653/*
1654 * Get_destination puts the requested information into the
1655 *  job->destination structure if it is available.
1656 *  returns: 1 if not found, 0 if found;
1657 */
1658
1659int Get_destination( struct job *job, int n )
1660{
1661	char buffer[SMALLBUFFER];
1662	char *s;
1663	int result = 1;
1664
1665	SNPRINTF(buffer,sizeof(buffer)) "DEST%d", n );
1666
1667	Free_line_list(&job->destination);
1668	if( (s = Find_str_value(&job->info,buffer,Value_sep)) ){
1669		s = safestrdup(s,__FILE__,__LINE__);
1670		Unescape(s);
1671		Split(&job->destination,s,Line_ends,1,Value_sep,1,1,1,0);
1672		if(s) free( s ); s = 0;
1673		result = 0;
1674	}
1675	return( result );
1676}
1677
1678/*
1679 * Get_destination_by_name puts the requested information into the
1680 *  job->destination structure if it is available.
1681 *  returns: 1 if not found, 0 if found;
1682 */
1683
1684int Get_destination_by_name( struct job *job, char *name )
1685{
1686	int result = 1;
1687	char *s;
1688
1689	Free_line_list(&job->destination);
1690	if( name && (s = Find_str_value(&job->info,name,Value_sep)) ){
1691		s = safestrdup(s,__FILE__,__LINE__);
1692		Unescape(s);
1693		Split(&job->destination,s,Line_ends,1,Value_sep,1,1,1,0);
1694		if(s) free( s ); s = 0;
1695		result = 0;
1696	}
1697	return( result );
1698}
1699
1700/*
1701 * Trim_status_file - trim a status file to an acceptible length
1702 */
1703
1704int Trim_status_file( int status_fd, char *file, int max, int min )
1705{
1706	int tempfd, status;
1707	char buffer[LARGEBUFFER];
1708	struct stat statb;
1709	char *tempfile, *s;
1710	int count;
1711
1712	tempfd = status = -1;
1713
1714	DEBUG1("Trim_status_file: file '%s' max %d, min %d", file, max, min);
1715
1716	/* if the file does not exist, do not create it */
1717	if( ISNULL(file) ) return( status_fd );
1718	if( stat( file, &statb ) == 0 ){
1719		DEBUG1("Trim_status_file: '%s' max %d, min %d, size %ld", file, max, min,
1720			(long)(statb.st_size) );
1721		if( max > 0 && statb.st_size/1024 > max ){
1722			status = Checkwrite( file, &statb,O_RDWR,0,0);
1723			tempfd = Make_temp_fd(&tempfile);
1724			if( min > max || min == 0 ){
1725				min = max/4;
1726			}
1727			if( min == 0 ) min = 1;
1728			DEBUG1("Trim_status_file: trimming to %d K", min);
1729			lseek( status, 0, SEEK_SET );
1730			lseek( status, -min*1024, SEEK_END );
1731			while( (count = read( status, buffer, sizeof(buffer) - 1 ) ) > 0 ){
1732				buffer[count] = 0;
1733				if( (s = safestrchr(buffer,'\n')) ){
1734					*s++ = 0;
1735					Write_fd_str( tempfd, s );
1736					break;
1737				}
1738			}
1739			while( (count = read( status, buffer, sizeof(buffer) ) ) > 0 ){
1740				if( write( tempfd, buffer, count) < 0 ){
1741					Errorcode = JABORT;
1742					LOGERR_DIE(LOG_ERR) "Trim_status_file: cannot write tempfile" );
1743				}
1744			}
1745			lseek( tempfd, 0, SEEK_SET );
1746			lseek( status, 0, SEEK_SET );
1747			ftruncate( status, 0 );
1748			while( (count = read( tempfd, buffer, sizeof(buffer) ) ) > 0 ){
1749				if( write( status, buffer, count) < 0 ){
1750					Errorcode = JABORT;
1751					LOGERR_DIE(LOG_ERR) "Trim_status_file: cannot write tempfile" );
1752				}
1753			}
1754			unlink(tempfile);
1755			close(status);
1756		}
1757		close( tempfd );
1758		if( status_fd > 0 ) close( status_fd );
1759		status_fd = Checkwrite( file, &statb,0,0,0);
1760	}
1761	return( status_fd );
1762}
1763
1764/********************************************************************
1765 * int Fix_control( struct job *job, char *order )
1766 *   fix the order of lines in the control file so that they
1767 *   are in the order of the letters in the order string.
1768 * Lines are checked for metacharacters and other trashy stuff
1769 *   that might have crept in by user efforts
1770 *
1771 * job - control file area in memory
1772 * order - order of options
1773 *
1774 *  order string: Letter - relative position in file
1775 *                * matches any character not in string
1776 *                  can have only one wildcard in string
1777 *   Berkeley-            HPJCLIMWT1234
1778 *   PLP-                 HPJCLIMWT1234*
1779 *
1780 * RETURNS: 0 if fixed correctly
1781 *          non-zero if there is something wrong with this file and it should
1782 *          be rejected out of hand
1783 ********************************************************************/
1784
1785/********************************************************************
1786 * BSD and LPRng order
1787 * We use these values to determine the order of jobs in the file
1788 * The order of the characters determines the order of the options
1789 *  in the control file.  A * puts all unspecified options there
1790 ********************************************************************/
1791
1792 static char BSD_order[] = "HPJCLIMWT1234" ;
1793 static char LPRng_order[] = "HPJCLIMWT1234*" ;
1794
1795#if defined(JY20031104Fix_datafile_info)
1796char *Fix_datafile_info( struct job *job, char *number, char *suffix,
1797	char *xlate_format )
1798{
1799	int i, copies, linecount, count, jobcopies, copy, group, offset;
1800	char *s, *Nline, *transfername, *dataline, *jobline;
1801	struct line_list *lp, outfiles;
1802	char prefix[8];
1803	char fmt[2];
1804
1805	Init_line_list(&outfiles);
1806	transfername = dataline = Nline = jobline = 0;
1807#ifdef ORIGINAL_DEBUG//JY@1020
1808	if(DEBUGL4)Dump_job("Fix_datafile_info - starting", job );
1809#endif
1810
1811	/* now we find the number of different data files */
1812
1813	count = 0;
1814	/* we look through the data file list, looking for jobs with the name
1815	 * TRANSFERNAME.  If we find a new one, we create the correct form
1816	 * of the job datafile
1817	 */
1818	for( linecount = 0; linecount < job->datafiles.count; ++linecount ){
1819		lp = (void *)job->datafiles.list[linecount];
1820		transfername = Find_str_value(lp,TRANSFERNAME,Value_sep);
1821		Set_str_value(lp,OTRANSFERNAME,transfername);
1822		if( !(s = Find_casekey_str_value(&outfiles,transfername,Value_sep)) ){
1823			/* we add the entry */
1824			offset = count % 52;
1825			group = count / 52;
1826			++count;
1827			if( (group >= 52) ){
1828				FATAL(LOG_INFO)"Fix_datafile_info: too many data files");
1829			}
1830			SNPRINTF(prefix,sizeof(prefix))"d%c%c",
1831			("fghijklmnopqrstuvwxyzabcde" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" )[group],
1832			("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz")[offset] );
1833			s = safestrdup3(prefix,number,suffix,__FILE__,__LINE__);
1834			if( transfername ) Set_casekey_str_value(&outfiles,transfername,s);
1835			Set_str_value(lp,TRANSFERNAME,s);
1836			if(s) free(s); s = 0;
1837		} else {
1838			Set_str_value(lp,TRANSFERNAME,s);
1839		}
1840	}
1841	Free_line_list(&outfiles);
1842	Set_decimal_value(&job->info,DATAFILE_COUNT,count);
1843
1844#ifdef ORIGINAL_DEBUG//JY@1020
1845	if(DEBUGL4)Dump_job("Fix_datafile_info - after finding duplicates", job );
1846#endif
1847
1848	jobcopies = Find_flag_value(&job->info,COPIES,Value_sep);
1849	if( !jobcopies ) jobcopies = 1;
1850	fmt[0] = 'f'; fmt[1] = 0;
1851	DEBUG4("Fix_datafile_info: jobcopies %d", jobcopies );
1852	for(copy = 0; copy < jobcopies; ++copy ){
1853		for( linecount = 0; linecount < job->datafiles.count; ++linecount ){
1854			jobline = 0;
1855			lp = (void *)job->datafiles.list[linecount];
1856#ifdef ORIGINAL_DEBUG//JY@1020
1857			if(DEBUGL5)Dump_line_list("Fix_datafile_info - info", lp  );
1858#endif
1859			transfername = Find_str_value(lp,TRANSFERNAME,Value_sep);
1860			Nline = Find_str_value(lp,"N",Value_sep);
1861			fmt[0] = 'f';
1862			if( (s = Find_str_value(lp,FORMAT,Value_sep)) ){
1863				fmt[0] = *s;
1864			}
1865			if( xlate_format ){
1866				int l = safestrlen(xlate_format);
1867				for( i = 0; i+1 < l; i+= 2 ){
1868					if( (xlate_format[i] == fmt[0])
1869						|| (xlate_format[i] == '*') ){
1870						fmt[0] = xlate_format[i+1];
1871						break;
1872					}
1873				}
1874			}
1875			copies = Find_flag_value(lp,COPIES,Value_sep);
1876			if( copies == 0 ) copies = 1;
1877			for(i = 0; i < copies; ++i ){
1878				if( Nline && !Nline_after_file_DYN ){
1879					jobline = safeextend4(jobline,"N",Nline,"\n",__FILE__,__LINE__);
1880				}
1881				jobline = safeextend4(jobline,fmt,transfername,"\n",__FILE__,__LINE__);
1882				if( Nline && Nline_after_file_DYN ){
1883					jobline = safeextend4(jobline,"N",Nline,"\n",__FILE__,__LINE__);
1884				}
1885			}
1886			DEBUG4("Fix_datafile_info: file [%d], jobline '%s'",
1887				linecount, jobline);
1888			dataline = safeextend2(dataline,jobline,__FILE__,__LINE__);
1889			if( jobline ) free(jobline); jobline = 0;
1890		}
1891	}
1892	DEBUG4("Fix_datafile_info: adding remove lines" );
1893	for( linecount = 0; linecount < job->datafiles.count; ++linecount ){
1894		jobline = 0;
1895		lp = (void *)job->datafiles.list[linecount];
1896#ifdef ORIGINAL_DEBUG//JY@1020
1897		if(DEBUGL4)Dump_line_list("Fix_datafile_info - info", lp );
1898#endif
1899		transfername = Find_str_value(lp,TRANSFERNAME,Value_sep);
1900		if( !Find_casekey_str_value(&outfiles,transfername,Value_sep) ){
1901			jobline = safeextend4(jobline,"U",transfername,"\n",__FILE__,__LINE__);
1902			Set_casekey_str_value(&outfiles,transfername,"YES");
1903		}
1904		DEBUG4("Fix_datafile_info: file [%d], jobline '%s'",
1905			linecount, jobline);
1906		dataline = safeextend2(dataline,jobline,__FILE__,__LINE__);
1907		if( jobline ) free(jobline); jobline = 0;
1908	}
1909	Free_line_list(&outfiles);
1910	Set_str_value(&job->info,DATAFILES,dataline);
1911	s = Find_str_value(&job->info,DATAFILES,Value_sep);
1912	while( (s = safestrchr(s,'\n')) ) *s++ = '\001';
1913	return(dataline);
1914}
1915#endif
1916
1917int ordercomp(  const void *left, const void *right, const void *orderp)
1918{
1919	const char *lpos, *rpos, *wildcard, *order;
1920	int cmp;
1921
1922	order = (const char *)orderp;
1923
1924	/* blank lines always come last */
1925	if( (wildcard = safestrchr( order, '*' )) ){
1926		wildcard = order + safestrlen(order);
1927	}
1928	lpos = *((const char **)left);
1929	if( lpos == 0 || *lpos == 0 ){
1930		lpos = order+safestrlen(order);
1931	} else if( !(lpos = safestrchr( order, *lpos )) ){
1932		lpos = wildcard;
1933	}
1934	rpos = *((const char **)right);
1935	if( rpos == 0 || *rpos == 0 ){
1936		rpos = order+safestrlen(order);
1937	} else if( !(rpos = safestrchr( order, *rpos )) ){
1938		rpos = wildcard;
1939	}
1940	cmp = lpos - rpos;
1941	DEBUG4("ordercomp '%s' to '%s' -> %d",
1942		*((const char **)left), *((const char **)right), cmp );
1943	return( cmp );
1944}
1945
1946/************************************************************************
1947 * Fix_control:
1948 *  Fix up the control file,  setting the various entries
1949 *  to be compatible with transfer to the remote location
1950 * 1. info will have fromhost, priority, and number information
1951 *   if not, you will need to add it.
1952 *
1953 ************************************************************************/
1954
1955 struct maxlen{
1956	int c, len;
1957 } maxclen[] = {
1958	{ 'A', 131 }, { 'C', 31 }, { 'D', 1024 }, { 'H', 31 }, { 'I', 31 },
1959	{ 'J', 99 }, { 'L', 31 }, { 'N', 131 }, { 'M', 131 }, { 'N', 131 },
1960	{ 'P', 31 }, { 'Q', 131 }, { 'R', 131 }, { 'S', 131 }, { 'T', 79 },
1961	{ 'U', 131 }, { 'W', 31 }, { 'Z', 1024 }, { '1', 131 }, { '2', 131 },
1962	{ '3', 131 }, { '4', 131 },
1963	{0,0}
1964	};
1965
1966#ifdef ORIGINAL_DEBUG//JY@1020
1967void Fix_control( struct job *job, char *filter, char *xlate_format )
1968{
1969	char *s, *file_hostname, *number, *priority,
1970		*order;
1971	char buffer[SMALLBUFFER], pr[2];
1972	int tempfd, tempfc;
1973	int i, n, j, cccc, wildcard, len;
1974	struct line_list controlfile;
1975
1976	Init_line_list(&controlfile);
1977
1978#ifdef ORIGINAL_DEBUG//JY@1020
1979	if(DEBUGL3) Dump_job( "Fix_control: starting", job );
1980#endif
1981
1982	/* we set the control file with the single letter values in the
1983	   hold job file
1984	 */
1985	for( i = 0; i < job->info.count; ++i ){
1986		int c;
1987		s = job->info.list[i];
1988		if( s && (c = s[0]) && isupper(c) && c != 'N' && c != 'U'
1989			&& s[1] == '=' ){
1990			/* remove banner from control file */
1991			if( c == 'L' && Suppress_header_DYN && !Always_banner_DYN ) continue;
1992			s[1] = 0;
1993			Add_line_list( &controlfile, s, Value_sep, 1, 1 );
1994			Set_str_value(&controlfile,s,s+2);
1995			s[1] = '=';
1996		}
1997	}
1998
1999#ifdef ORIGINAL_DEBUG//JY@1020
2000	if(DEBUGL3) Dump_line_list( "Fix_control: control file", &controlfile );
2001#endif
2002
2003	n = j = 0;
2004	n = Find_decimal_value( &job->info,NUMBER,Value_sep);
2005	j = Find_decimal_value( &job->info,SEQUENCE,Value_sep);
2006
2007	number = Fix_job_number(job, n+j);
2008
2009	if( !(priority = Find_str_value( &job->destination,PRIORITY,Value_sep))
2010		&& !(priority = Find_str_value( &job->info,PRIORITY,Value_sep))
2011		&& !(priority = Default_priority_DYN) ){
2012		priority = "A";
2013	}
2014	pr[0] = *priority;
2015	pr[1] = 0;
2016
2017	file_hostname = Find_str_value(&job->info,FILE_HOSTNAME,Value_sep);
2018
2019	if( !file_hostname ){
2020		file_hostname = Find_str_value(&job->info,FROMHOST,Value_sep);
2021		if( file_hostname == 0 || *file_hostname == 0 ){
2022			file_hostname = FQDNHost_FQDN;
2023		}
2024		Set_str_value(&job->info,FILE_HOSTNAME,file_hostname);
2025		file_hostname = Find_str_value(&job->info,FILE_HOSTNAME,Value_sep);
2026	}
2027
2028	if( (Backwards_compatible_DYN || Use_shorthost_DYN)
2029		&& (s = safestrchr( file_hostname, '.' )) ){
2030		*s = 0;
2031	}
2032
2033#ifdef ORIGINAL_DEBUG//JY@1020
2034	if(DEBUGL3) Dump_job( "Fix_control: before fixing", job );
2035#endif
2036
2037	/* fix control file name */
2038
2039	s = safestrdup4("cf",pr,number,file_hostname,__FILE__,__LINE__);
2040	Set_str_value(&job->info,TRANSFERNAME,s);
2041	if(s) free(s); s = 0;
2042
2043	/* fix control file contents */
2044
2045	s = Make_identifier( job );
2046
2047	if( job->destination.count == 0 ){
2048		Set_str_value(&controlfile,IDENTIFIER,s);
2049	} else {
2050		s = Find_str_value(&job->destination,IDENTIFIER,Value_sep);
2051		cccc = Find_flag_value(&job->destination,COPIES,Value_sep);
2052		n = Find_flag_value(&job->destination,COPY_DONE,Value_sep);
2053		if( cccc > 1 ){
2054			SNPRINTF(buffer,sizeof(buffer))"C%d",n+1);
2055			s = safestrdup2(s,buffer,__FILE__,__LINE__);
2056			Set_str_value(&controlfile,IDENTIFIER,s);
2057			if(s) free(s); s = 0;
2058		} else {
2059			Set_str_value(&controlfile,IDENTIFIER,s);
2060		}
2061	}
2062	if( !Find_str_value(&controlfile,DATE,Value_sep) ){
2063		Set_str_value(&controlfile,DATE, Time_str( 0, 0 ) );
2064	}
2065	if( (Use_queuename_DYN || Force_queuename_DYN) &&
2066		!Find_str_value(&controlfile,QUEUENAME,Value_sep) ){
2067		s = Force_queuename_DYN;
2068		if( s == 0 ) s = Queue_name_DYN;
2069		if( s == 0 ) s = Printer_DYN;
2070		Set_str_value(&controlfile,QUEUENAME, s );
2071	}
2072
2073	/* fix up the control file lines overrided by routing */
2074	buffer[1] = 0;
2075	for( i = 0; i < job->destination.count; ++i ){
2076		s = job->destination.list[i];
2077		cccc = cval(s);
2078		buffer[0] = cccc;
2079		if( isupper(cccc) && Find_str_value(&controlfile,buffer,Value_sep) ){
2080			Set_str_value( &controlfile,buffer,s+1);
2081		}
2082	}
2083
2084	order = Control_file_line_order_DYN;
2085    if( !order && Backwards_compatible_DYN ){
2086        order = BSD_order;
2087	} else if( !order ){
2088		order = LPRng_order;
2089	}
2090	wildcard = (safestrchr( order,'*') != 0);
2091
2092	/*
2093	 * remove any line not required and fix up line metacharacters
2094	 */
2095
2096	buffer[1] = 0;
2097	for( i = 0; i < controlfile.count; ){
2098		/* get line and first character on line */
2099		s = controlfile.list[i];
2100		cccc = *s;
2101		buffer[0] = cccc;
2102		/* remove any non-listed options */
2103		if( (!isupper(cccc) && !isdigit(cccc)) || (!safestrchr(order, cccc) && !wildcard) ){
2104			Set_str_value( &controlfile,buffer,0);
2105		} else {
2106			if( Backwards_compatible_DYN ){
2107				for( j = 0; maxclen[j].c && cccc != maxclen[j].c ; ++j );
2108				if( (len = maxclen[j].len) && safestrlen(s+1) > len ){
2109					s[len+1] = 0;
2110				}
2111			}
2112			++i;
2113		}
2114	}
2115
2116	/*
2117	 * we check to see if order is correct - we need to check to
2118	 * see if allowed options in file first.
2119	 */
2120
2121#ifdef ORIGINAL_DEBUG//JY@1020
2122	if(DEBUGL3)Dump_line_list( "Fix_control: before sorting", &controlfile );
2123#endif
2124	n = Mergesort( controlfile.list,
2125		controlfile.count, sizeof( char *), ordercomp, order );
2126	if( n ){
2127		Errorcode = JABORT;
2128		LOGERR_DIE(LOG_ERR) "Fix_control: Mergesort failed" );
2129	}
2130
2131#ifdef ORIGINAL_DEBUG//JY@1020
2132	if(DEBUGL3) Dump_job( "Fix_control: after sorting", job );
2133#endif
2134	for( i = 0; i < controlfile.count; ++i ){
2135		s = controlfile.list[i];
2136		memmove(s+1,s+2,safestrlen(s+2)+1);
2137	}
2138	s = 0;
2139
2140	{
2141		char *datalines;
2142		char *temp = Join_line_list(&controlfile,"\n");
2143		DEBUG3( "Fix_control: control info '%s'", temp );
2144
2145		datalines = Fix_datafile_info( job, number, file_hostname, xlate_format );
2146		DEBUG3( "Fix_control: data info '%s'", datalines );
2147		temp = safeextend2(temp,datalines,__FILE__,__LINE__);
2148		if( datalines ) free(datalines); datalines = 0;
2149		Set_str_value(&job->info,CF_OUT_IMAGE,temp);
2150		if( temp ) free(temp); temp = 0;
2151	}
2152
2153	if( filter ){
2154		DEBUG3("Fix_control: filter '%s'", filter );
2155
2156		tempfd = Make_temp_fd( 0 );
2157		tempfc = Make_temp_fd( 0 );
2158		s = Find_str_value(&job->info,CF_OUT_IMAGE,Value_sep );
2159		if( Write_fd_str( tempfc, s ) < 0 ){
2160			Errorcode = JFAIL;
2161			LOGERR_DIE(LOG_INFO) "Fix_control: write to tempfile failed" );
2162		}
2163		if( lseek( tempfc, 0, SEEK_SET ) == -1 ){
2164			Errorcode = JFAIL;
2165			LOGERR_DIE(LOG_INFO) "Fix_control: lseek failed" );
2166		}
2167		if( (n = Filter_file( tempfc, tempfd, "CONTROL_FILTER",
2168			filter, Filter_options_DYN, job, 0, 1 )) ){
2169			Errorcode = n;
2170			LOGERR_DIE(LOG_ERR) "Fix_control: control filter failed with status '%s'",
2171				Server_status(n) );
2172		}
2173		s = 0;
2174		if( n < 0 ){
2175			Errorcode = JFAIL;
2176			LOGERR_DIE(LOG_INFO) "Fix_control: read from tempfd failed" );
2177		}
2178		s = Get_fd_image( tempfd, 0 );
2179		if( s == 0 || *s == 0 ){
2180			Errorcode = JFAIL;
2181			LOGERR_DIE(LOG_INFO) "Fix_control: zero length control filter output" );
2182		}
2183		DEBUG4("Fix_control: control filter output '%s'", s);
2184		Set_str_value(&job->info,CF_OUT_IMAGE,s);
2185		if(s) free(s); s = 0;
2186		close( tempfc ); tempfc = -1;
2187		close( tempfd ); tempfd = -1;
2188	}
2189}
2190#endif
2191
2192/************************************************************************
2193 * Create_control:
2194 *  Create the control file,  setting the various entries.  This is done
2195 *  on job submission.
2196 *
2197 ************************************************************************/
2198
2199#if defined(JY20031104Create_control)
2200int Create_control( struct job *job, char *error, int errlen,
2201	char *xlate_format )
2202{
2203	char *fromhost, *file_hostname, *number, *priority, *openname;
2204	int status = 0, fd, i;
2205	struct stat statb;
2206
2207#ifdef ORIGINAL_DEBUG//JY@1020
2208	if(DEBUGL3) Dump_job( "Create_control: before fixing", job );
2209#endif
2210
2211	/* deal with authentication */
2212
2213	Make_identifier( job );
2214
2215	if( !(fromhost = Find_str_value(&job->info,FROMHOST,Value_sep)) || Is_clean_name(fromhost) ){
2216		Set_str_value(&job->info,FROMHOST,FQDNRemote_FQDN);
2217		fromhost = Find_str_value(&job->info,FROMHOST,Value_sep);
2218	}
2219	/*
2220	 * accounting name fixup
2221	 * change the accounting name ('R' field in control file)
2222	 * based on hostname
2223	 *  hostname(,hostname)*=($K|value)*(;hostname(,hostname)*=($K|value)*)*
2224	 *  we have a semicolon separated list of entrires
2225	 *  the RemoteHost_IP is compared to these.  If a match is found then the
2226	 *    user name (if any) is used for the accounting name.
2227	 *  The user name list has the format:
2228	 *  ${K}  - value from control file or printcap entry - you must use the
2229	 *          ${K} format.
2230	 *  username  - user name value to substitute.
2231	 */
2232	if( Accounting_namefixup_DYN ){
2233		struct line_list l, listv;
2234		char *accounting_name = 0;
2235		Init_line_list(&l);
2236		Init_line_list(&listv);
2237
2238		DEBUG1("Create_control: Accounting_namefixup '%s'", Accounting_namefixup_DYN );
2239		Split(&listv,Accounting_namefixup_DYN,";",0,0,0,0,0,0);
2240		for( i = 0; i < listv.count; ++i ){
2241			char *s, *t;
2242			int j;
2243			s = listv.list[i];
2244			if( (t = safestrpbrk(s,"=")) ) *t++ = 0;
2245			Free_line_list(&l);
2246			DEBUG1("Create_control: hostlist '%s'", s );
2247			Split(&l,s,",",0,0,0,0,0,0);
2248			if( Match_ipaddr_value(&l,&RemoteHost_IP) ){
2249				Free_line_list(&l);
2250				DEBUG1("Create_control: match, using users '%s'", t );
2251				Split(&l,t,",",0,0,0,0,0,0);
2252#ifdef ORIGINAL_DEBUG//JY@1020
2253				if(DEBUGL1)Dump_line_list("Create_control: before Fix_dollars", &l );
2254#endif
2255				Fix_dollars(&l,job,0,0);
2256#ifdef ORIGINAL_DEBUG//JY@1020
2257				if(DEBUGL1)Dump_line_list("Create_control: after Fix_dollars", &l );
2258#endif
2259				for( j = 0; j < l.count; ++j ){
2260					if( !ISNULL(l.list[j]) ){
2261						accounting_name = l.list[j];
2262						break;
2263					}
2264				}
2265				break;
2266			}
2267		}
2268		DEBUG1("Create_control: accounting_name '%s'", accounting_name );
2269		if( !ISNULL(accounting_name) ){
2270			Set_str_value(&job->info,ACCNTNAME,accounting_name );
2271		}
2272		Free_line_list(&l);
2273		Free_line_list(&listv);
2274	}
2275
2276	if( Force_IPADDR_hostname_DYN ){
2277		char buffer[SMALLBUFFER];
2278		const char *const_s;
2279		int family;
2280		/* We will need to create a dummy record. - no host */
2281		family = RemoteHost_IP.h_addrtype;
2282		const_s = (char *)inet_ntop( family, RemoteHost_IP.h_addr_list.list[0],
2283			buffer, sizeof(buffer) );
2284		DEBUG1("Create_control: remotehost '%s'", const_s );
2285		Set_str_value(&job->info,FROMHOST,const_s);
2286		fromhost = Find_str_value(&job->info,FROMHOST,Value_sep);
2287	}
2288	{
2289		char *s, *t;
2290		if( Force_FQDN_hostname_DYN && !safestrchr(fromhost,'.')
2291			&& (t = safestrchr(FQDNRemote_FQDN,'.')) ){
2292			s = safestrdup2(fromhost, t, __FILE__,__LINE__ );
2293			Set_str_value(&job->info,FROMHOST,s);
2294			if( s ) free(s); s = 0;
2295			fromhost = Find_str_value(&job->info,FROMHOST,Value_sep);
2296		}
2297	}
2298
2299
2300	if( !Find_str_value(&job->info,DATE,Value_sep) ){
2301		char *s = Time_str(0,0);
2302		Set_str_value(&job->info,DATE,s);
2303	}
2304	if( (Use_queuename_DYN || Force_queuename_DYN)
2305		&& !Find_str_value(&job->info,QUEUENAME,Value_sep) ){
2306		char *s = Force_queuename_DYN;
2307		if( s == 0 ) s = Queue_name_DYN;
2308		if( s == 0 ) s = Printer_DYN;
2309		Set_str_value(&job->info,QUEUENAME,s);
2310		Set_DYN(&Queue_name_DYN,s);
2311	}
2312	if( Hld_all(&Spool_control) || Auto_hold_DYN ){
2313		Set_flag_value( &job->info,HOLD_TIME,time((void *)0) );
2314	} else {
2315		Set_flag_value( &job->info,HOLD_TIME,0);
2316	}
2317
2318	number = Find_str_value( &job->info,NUMBER,Value_sep);
2319
2320	priority = Find_str_value( &job->info,PRIORITY,Value_sep);
2321	if( !priority ){
2322		priority = Default_priority_DYN;
2323		if( !priority ) priority = "A";
2324		Set_str_value(&job->info,PRIORITY,priority);
2325		priority = Find_str_value(&job->info,PRIORITY,Value_sep);
2326	}
2327
2328	file_hostname = Find_str_value(&job->info,FROMHOST,Value_sep);
2329	if( ISNULL(file_hostname) ) file_hostname = FQDNRemote_FQDN;
2330	if( ISNULL(file_hostname) ) file_hostname = FQDNHost_FQDN;
2331
2332	if( isdigit(cval(file_hostname)) ){
2333		char * s = safestrdup2("ADDR",file_hostname,__FILE__,__LINE__);
2334		Set_str_value(&job->info,FILE_HOSTNAME,s);
2335		if( s ) free(s); s = 0;
2336	} else {
2337		Set_str_value(&job->info,FILE_HOSTNAME,file_hostname);
2338	}
2339	file_hostname = Find_str_value(&job->info,FILE_HOSTNAME,Value_sep);
2340
2341	/* fix Z options */
2342	Fix_Z_opts( job );
2343	/* fix control file name */
2344
2345	{
2346		char *s = safestrdup4("cf",priority,number,file_hostname,__FILE__,__LINE__);
2347		Set_str_value(&job->info,TRANSFERNAME,s);
2348		if(s) free(s); s = 0;
2349	}
2350
2351	{
2352		/* now we generate the control file image by getting the info
2353		 * from the hold file
2354		 */
2355		char *cf, *datalines;
2356		cf = datalines = 0;
2357		for( i = 0; i < job->info.count; ++i ){
2358			char *t = job->info.list[i];
2359			int c;
2360			if( t && (c = t[0]) && isupper(c) && c != 'N' && c != 'U'
2361				&& t[1] == '=' ){
2362				t[1] = 0;
2363				cf = safeextend4(cf,t,t+2,"\n",__FILE__,__LINE__);
2364				t[1] = '=';
2365			}
2366		}
2367
2368		DEBUG4("Create_control: first part '%s'", cf );
2369		datalines = Fix_datafile_info( job, number, file_hostname, xlate_format );
2370		DEBUG4("Create_control: data info '%s'", datalines );
2371		cf = safeextend2(cf,datalines,__FILE__,__LINE__);
2372		DEBUG4("Create_control: joined '%s'", cf );
2373		if( datalines ) free(datalines); datalines = 0;
2374
2375		openname = Find_str_value(&job->info,OPENNAME,Value_sep);
2376		if( !openname ) openname = Find_str_value(&job->info,TRANSFERNAME,Value_sep);
2377		DEBUG4("Create_control: writing to '%s'", openname );
2378		if( (fd = Checkwrite(openname,&statb,0,1,0)) < 0
2379			|| ftruncate(fd,0) || Write_fd_str(fd,cf) < 0 ){
2380#ifdef ORIGINAL_DEBUG//JY@1020
2381			SNPRINTF(error,errlen)"Write_control: cannot write '%s' - '%s'",
2382				openname, Errormsg(errno) );
2383#endif
2384			status = 1;
2385		}
2386		Max_open(fd);
2387		if( fd > 0 ) close(fd); fd = -1;
2388		if( cf ) free(cf); cf = 0;
2389	}
2390
2391
2392	return( status );
2393}
2394#endif
2395
2396/*
2397 * Buffer management
2398 *  Set up and put values into an output buffer for
2399 *  transmission at a later time
2400 */
2401void Init_buf(char **buf, int *max, int *len)
2402{
2403	DEBUG4("Init_buf: buf 0x%lx, max %d, len %d",
2404		Cast_ptr_to_long(*buf), *max, *len );
2405	if( *max <= 0 ) *max = LARGEBUFFER;
2406	if( *buf == 0 ) *buf = realloc_or_die( *buf, *max+1,__FILE__,__LINE__);
2407	*len = 0;
2408	(*buf)[0] = 0;
2409}
2410
2411void Put_buf_len( const char *s, int cnt, char **buf, int *max, int *len )
2412{
2413	DEBUG4("Put_buf_len: starting- buf 0x%lx, max %d, len %d, adding %d",
2414		Cast_ptr_to_long(*buf), *max, *len, cnt );
2415	if( s == 0 || cnt <= 0 ) return;
2416	if( *max - *len <= cnt ){
2417		*max += ((LARGEBUFFER + cnt )/1024)*1024;
2418		*buf = realloc_or_die( *buf, *max+1,__FILE__,__LINE__);
2419		DEBUG4("Put_buf_len: update- buf 0x%lx, max %d, len %d",
2420		Cast_ptr_to_long(*buf), *max, *len);
2421		if( !*buf ){
2422			Errorcode = JFAIL;
2423			LOGERR_DIE(LOG_INFO)"Put_buf_len: realloc %d failed", *len );
2424		}
2425	}
2426	memcpy( *buf+*len, s, cnt );
2427	*len += cnt;
2428	(*buf)[*len] = 0;
2429}
2430
2431void Put_buf_str( const char *s, char **buf, int *max, int *len )
2432{
2433	if( s && *s ) Put_buf_len( s, safestrlen(s), buf, max, len );
2434}
2435
2436void Free_buf(char **buf, int *max, int *len)
2437{
2438	if( *buf ) free(*buf); *buf = 0;
2439	*len = 0;
2440	*max = 0;
2441}
2442