1/***************************************************************************
2 * LPRng - An Extended Print Spooler System
3 *
4 * Copyright 1988-2003, Patrick Powell, San Diego, CA
5 *     papowell@lprng.com
6 * See LICENSE for conditions of use.
7 *
8 ***************************************************************************/
9
10 static char *const _id =
11"$Id: linelist.c,v 1.1.1.1 2008/10/15 03:28:26 james26_jang Exp $";
12
13#include "lp.h"
14#include "errorcodes.h"
15#include "globmatch.h"
16#include "gethostinfo.h"
17#include "child.h"
18#include "fileopen.h"
19#include "getqueue.h"
20#include "getprinter.h"
21#include "lpd_logger.h"
22#include "lpd_dispatch.h"
23#include "lpd_jobs.h"
24#include "linelist.h"
25
26/**** ENDINCLUDE ****/
27
28/* lowercase and uppercase (destructive) a string */
29void lowercase( char *s )
30{
31	int c;
32	if( s ){
33		for( ; (c = cval(s)); ++s ){
34			if( isupper(c) ) *s = tolower(c);
35		}
36	}
37}
38void uppercase( char *s )
39{
40	int c;
41	if( s ){
42		for( ; (c = cval(s)); ++s ){
43			if( islower(c) ) *s = toupper(c);
44		}
45	}
46}
47
48/*
49 * Trunc str - remove trailing white space (destructive)
50 */
51
52char *trunc_str( char *s)
53{
54	char *t;
55	if(s && *s){
56		for( t=s+safestrlen(s); t > s && isspace(cval(t-1)); --t );
57		*t = 0;
58	}
59	return( s );
60}
61
62int Lastchar( char *s )
63{
64	int c = 0;
65	if( s && *s ){
66		s += safestrlen(s)-1;
67		c = cval(s);
68	}
69	return(c);
70}
71
72/*
73 * Memory Allocation Routines
74 * - same as malloc, realloc, but with error messages
75 */
76#if defined(DMALLOC)
77#undef malloc
78#define malloc(size) \
79  _malloc_leap(file, line, size)
80#undef calloc
81#define calloc(count, size) \
82  _calloc_leap(file, line, count, size)
83#undef realloc
84#define realloc(ptr, size) \
85  _realloc_leap(file, line, ptr, size)
86#endif
87void *malloc_or_die( size_t size, const char *file, int line )
88{
89    void *p;
90    p = malloc(size);
91    if( p == 0 ){
92        LOGERR_DIE(LOG_INFO) "malloc of %d failed, file '%s', line %d",
93			size, file, line );
94    }
95	DEBUG6("malloc_or_die: size %d, addr 0x%lx, file '%s', line %d",
96		size,  Cast_ptr_to_long(p), file, line );
97    return( p );
98}
99
100void *realloc_or_die( void *p, size_t size, const char *file, int line )
101{
102	void *orig = p;
103	if( p == 0 ){
104		p = malloc(size);
105	} else {
106		p = realloc(p, size);
107	}
108    if( p == 0 ){
109        LOGERR(LOG_INFO) "realloc of 0x%lx, new size %d failed, file '%s', line %d",
110			Cast_ptr_to_long(orig), size, file, line );
111		abort();
112    }
113	DEBUG6("realloc_or_die: size %d, orig 0x%lx, addr 0x%lx, file '%s', line %d",
114		size, Cast_ptr_to_long(orig), Cast_ptr_to_long(p), file, line );
115    return( p );
116}
117
118/*
119 * duplicate a string safely, generate an error message
120 */
121
122char *safestrdup (const char *p, const char *file, int line)
123{
124    char *new = 0;
125
126	if( p == 0) p = "";
127	new = malloc_or_die( safestrlen (p) + 1, file, line );
128	strcpy( new, p );
129	return( new );
130}
131
132/*
133 * char *safestrdup2( char *s1, char *s2, char *file, int line )
134 *  duplicate two concatenated strings
135 *  returns: malloced string area
136 */
137
138char *safestrdup2( const char *s1, const char *s2, const char *file, int line )
139{
140	int n = 1 + (s1?safestrlen(s1):0) + (s2?safestrlen(s2):0);
141	char *s = malloc_or_die( n, file, line );
142	s[0] = 0;
143	if( s1 ) strcat(s,s1);
144	if( s2 ) strcat(s,s2);
145	return( s );
146}
147
148/*
149 * char *safeextend2( char *s1, char *s2, char *file, int line )
150 *  extends a malloc'd string
151 *  returns: malloced string area
152 */
153
154char *safeextend2( char *s1, const char *s2, const char *file, int line )
155{
156	char *s;
157	int n = 1 + (s1?safestrlen(s1):0) + (s2?safestrlen(s2):0);
158	s = realloc_or_die( s1, n, file, line );
159	if( s1 == 0 ) *s = 0;
160	if( s2 ) strcat(s,s2);
161	return(s);
162}
163
164/*
165 * char *safestrdup3( char *s1, char *s2, char *s3, char *file, int line )
166 *  duplicate three concatenated strings
167 *  returns: malloced string area
168 */
169
170char *safestrdup3( const char *s1, const char *s2, const char *s3,
171	const char *file, int line )
172{
173	int n = 1 + (s1?safestrlen(s1):0) + (s2?safestrlen(s2):0) + (s3?safestrlen(s3):0);
174	char *s = malloc_or_die( n, file, line );
175	s[0] = 0;
176	if( s1 ) strcat(s,s1);
177	if( s2 ) strcat(s,s2);
178	if( s3 ) strcat(s,s3);
179	return( s );
180}
181
182
183/*
184 * char *safeextend3( char *s1, char *s2, char *s3 char *file, int line )
185 *  extends a malloc'd string
186 *  returns: malloced string area
187 */
188
189char *safeextend3( char *s1, const char *s2, const char *s3,
190	const char *file, int line )
191{
192	char *s;
193	int n = 1 + (s1?safestrlen(s1):0) + (s2?safestrlen(s2):0) + (s3?safestrlen(s3):0);
194	s = realloc_or_die( s1, n, file, line );
195	if( s1 == 0 ) *s = 0;
196	if( s2 ) strcat(s,s2);
197	if( s3 ) strcat(s,s3);
198	return(s);
199}
200
201
202
203/*
204 * char *safeextend4( char *s1, char *s2, char *s3, char *s4,
205 *	char *file, int line )
206 *  extends a malloc'd string
207 *  returns: malloced string area
208 */
209
210char *safeextend4( char *s1, const char *s2, const char *s3, const char *s4,
211	const char *file, int line )
212{
213	char *s;
214	int n = 1 + (s1?safestrlen(s1):0) + (s2?safestrlen(s2):0)
215		+ (s3?safestrlen(s3):0) + (s4?safestrlen(s4):0);
216	s = realloc_or_die( s1, n, file, line );
217	if( s1 == 0 ) *s = 0;
218	if( s2 ) strcat(s,s2);
219	if( s3 ) strcat(s,s3);
220	if( s4 ) strcat(s,s4);
221	return(s);
222}
223
224/*
225 * char *safestrdup4
226 *  duplicate four concatenated strings
227 *  returns: malloced string area
228 */
229
230char *safestrdup4( const char *s1, const char *s2,
231	const char *s3, const char *s4,
232	const char *file, int line )
233{
234	int n = 1 + (s1?safestrlen(s1):0) + (s2?safestrlen(s2):0)
235		+ (s3?safestrlen(s3):0) + (s4?safestrlen(s4):0);
236	char *s = malloc_or_die( n, file, line );
237	s[0] = 0;
238	if( s1 ) strcat(s,s1);
239	if( s2 ) strcat(s,s2);
240	if( s3 ) strcat(s,s3);
241	if( s4 ) strcat(s,s4);
242	return( s );
243}
244
245
246
247/*
248 * char *safeextend5( char *s1, char *s2, char *s3, char *s4, char *s5
249 *	char *file, int line )
250 *  extends a malloc'd string
251 *  returns: malloced string area
252 */
253
254char *safeextend5( char *s1, const char *s2, const char *s3, const char *s4, const char *s5,
255	const char *file, int line )
256{
257	char *s;
258	int n = 1 + (s1?safestrlen(s1):0) + (s2?safestrlen(s2):0)
259		+ (s3?safestrlen(s3):0) + (s4?safestrlen(s4):0) + (s5?safestrlen(s5):0);
260	s = realloc_or_die( s1, n, file, line );
261	if( s1 == 0 ) *s = 0;
262	if( s2 ) strcat(s,s2);
263	if( s3 ) strcat(s,s3);
264	if( s4 ) strcat(s,s4);
265	if( s5 ) strcat(s,s5);
266	return(s);
267}
268
269
270/*
271 * char *safestrdup5
272 *  duplicate five concatenated strings
273 *  returns: malloced string area
274 */
275
276char *safestrdup5( const char *s1, const char *s2,
277	const char *s3, const char *s4, const char *s5,
278	const char *file, int line )
279{
280	int n = 1 + (s1?safestrlen(s1):0) + (s2?safestrlen(s2):0)
281		+ (s3?safestrlen(s3):0) + (s4?safestrlen(s4):0) + (s5?safestrlen(s5):0);
282	char *s = malloc_or_die( n, file, line );
283	s[0] = 0;
284	if( s1 ) strcat(s,s1);
285	if( s2 ) strcat(s,s2);
286	if( s3 ) strcat(s,s3);
287	if( s4 ) strcat(s,s4);
288	if( s5 ) strcat(s,s5);
289	return( s );
290}
291
292/*
293  Line Splitting and List Management
294
295  Model:  we have a list of malloced and duplicated lines
296          we never remove the lines unless we free them.
297          we never put them in unless we malloc them
298 */
299
300/*
301 * void Init_line_list( struct line_list *l )
302 *  - inititialize a list by zeroing it
303 */
304
305void Init_line_list( struct line_list *l )
306{
307	memset(l, 0, sizeof(l[0]));
308}
309
310/*
311 * void Free_line_list( struct line_list *l )
312 *  - clear a list by freeing the allocated array
313 */
314
315void Free_line_list( struct line_list *l )
316{
317	int i;
318	if( l == 0 ) return;
319	if( l->list ){
320		for( i = 0; i < l->count; ++i ){
321			if( l->list[i] ) free( l->list[i]); l->list[i] = 0;
322		}
323		free(l->list);
324	}
325	memset(l,0,sizeof(l[0]));
326}
327
328void Free_listof_line_list( struct line_list *l )
329{
330	int i;
331	struct line_list *lp;
332	if( l == 0 ) return;
333	for( i = 0; i < l->count; ++i ){
334		lp = (void *)l->list[i];
335		Free_line_list(lp);
336		memset( lp, 0, sizeof(lp[0]) );
337	}
338	Free_line_list(l);
339}
340
341/*
342 * void Check_max( struct line_list *l, int incr )
343 *
344 */
345
346void Check_max( struct line_list *l, int incr )
347{
348	if( l->count+incr >= l->max ){
349		l->max += 100+incr;
350		if( !(l->list = realloc_or_die( l->list, l->max*sizeof(char *),
351			__FILE__,__LINE__)) ){
352			Errorcode = JFAIL;
353			LOGERR(LOG_INFO) "Check_max: realloc %d failed",
354				l->max*sizeof(char*) );
355		}
356	}
357}
358
359
360/*
361 *char *Add_line_list( struct line_list *l, char *str,
362 *  char *sep, int sort, int uniq )
363 *  - add a copy of str to the line list
364 *  sep      - key separator, used for sorting
365 *  sort = 1 - sort the values
366 *  uniq = 1 - only one value
367 *  returns:  added string
368 */
369
370char *Add_line_list( struct line_list *l, char *str,
371		const char *sep, int sort, int uniq )
372{
373	char *s = 0;
374	int c = 0, cmp, mid;
375	if(DEBUGL5){
376		char b[48];
377		int n;
378		SNPRINTF( b,sizeof(b)-8)"%s",str );
379		if( (n = safestrlen(b)) > (int)sizeof(b)-10 ) strcpy( b+n,"..." );
380		LOGDEBUG("Add_line_list: '%s', sep '%s', sort %d, uniq %d",
381			b, sep, sort, uniq );
382	}
383
384	Check_max(l, 2);
385	str = safestrdup( str,__FILE__,__LINE__);
386	if( sort == 0 ){
387		l->list[l->count++] = str;
388	} else {
389		s = 0;
390		if( sep && (s = safestrpbrk( str, sep )) ){ c = *s; *s = 0; }
391		/* find everything <= the mid point */
392		/* cmp = key <> list[mid] */
393		cmp = Find_last_key( l, str, sep, &mid );
394		if( s ) *s = c;
395		/* str < list[mid+1] */
396		if( cmp == 0 && uniq ){
397			/* we replace */
398			free( l->list[mid] );
399			l->list[mid] = str;
400		} else if( cmp >= 0 ){
401			/* we need to insert after mid */
402			++l->count;
403			memmove( l->list+mid+2, l->list+mid+1,
404				sizeof( char * ) * (l->count - mid - 1));
405			l->list[mid+1] = str;
406		} else if( cmp < 0 ) {
407			/* we need to insert before mid */
408			++l->count;
409			memmove( l->list+mid+1, l->list+mid,
410				sizeof( char * ) * (l->count - mid));
411			l->list[mid] = str;
412		}
413	}
414#ifdef ORIGINAL_DEBUG//JY@1020
415	if(DEBUGL5)Dump_line_list("Add_line_list: result", l);
416#endif
417	return( str );
418}
419
420/*
421 *void Add_casekey_line_list( struct line_list *l, char *str,
422 *  char *sep, int sort, int uniq )
423 *  - add a copy of str to the line list, using case sensitive keys
424 *  sep      - key separator, used for sorting
425 *  sort = 1 - sort the values
426 *  uniq = 1 - only one value
427 */
428
429void Add_casekey_line_list( struct line_list *l, char *str,
430		const char *sep, int sort, int uniq )
431{
432	char *s = 0;
433	int c = 0, cmp, mid;
434	if(DEBUGL5){
435		char b[40];
436		int n;
437		SNPRINTF( b,sizeof(b)-8)"%s",str );
438		if( (n = safestrlen(b)) > (int)sizeof(b)-10 ) strcpy( b+n,"..." );
439		LOGDEBUG("Add_casekey_line_list: '%s', sep '%s', sort %d, uniq %d",
440			b, sep, sort, uniq );
441	}
442
443	Check_max(l, 2);
444	str = safestrdup( str,__FILE__,__LINE__);
445	if( sort == 0 ){
446		l->list[l->count++] = str;
447	} else {
448		s = 0;
449		if( sep && (s = safestrpbrk( str, sep )) ){ c = *s; *s = 0; }
450		/* find everything <= the mid point */
451		/* cmp = key <> list[mid] */
452		cmp = Find_last_casekey( l, str, sep, &mid );
453		if( s ) *s = c;
454		/* str < list[mid+1] */
455		if( cmp == 0 && uniq ){
456			/* we replace */
457			free( l->list[mid] );
458			l->list[mid] = str;
459		} else if( cmp >= 0 ){
460			/* we need to insert after mid */
461			++l->count;
462			memmove( l->list+mid+2, l->list+mid+1,
463				sizeof( char * ) * (l->count - mid - 1));
464			l->list[mid+1] = str;
465		} else if( cmp < 0 ) {
466			/* we need to insert before mid */
467			++l->count;
468			memmove( l->list+mid+1, l->list+mid,
469				sizeof( char * ) * (l->count - mid));
470			l->list[mid] = str;
471		}
472	}
473	/* if(DEBUGL4)Dump_line_list("Add_casekey_line_list: result", l); */
474}
475
476/*
477 * Prefix_line_list( struct line_list *l, char *str )
478 *  put the str at the front of the line list
479 */
480void Prefix_line_list( struct line_list *l, char *str )
481{
482	Check_max(l, 2);
483	str = safestrdup( str,__FILE__,__LINE__);
484
485	memmove( &l->list[1], &l->list[0], l->count * sizeof(l->list[0]) );
486	l->list[0] = str;
487	++l->count;
488}
489
490void Merge_line_list( struct line_list *dest, struct line_list *src,
491	char *sep, int sort, int uniq )
492{
493	int i;
494	for( i = 0; i < src->count; ++i ){
495		Add_line_list( dest, src->list[i], sep, sort, uniq );
496	}
497}
498
499void Merge_listof_line_list( struct line_list *dest, struct line_list *src,
500	char *sep, int sort, int uniq )
501{
502	struct line_list *sp, *dp;
503	int i;
504	for( i = 0; i < src->count; ++i ){
505		if( (sp = (void *)src->list[i]) ){
506			Check_max( dest, 1 );
507			dp = malloc_or_die(sizeof(dp[0]),__FILE__,__LINE__);
508			memset(dp,0,sizeof(dp[0]));
509			Merge_line_list( dp, sp, sep, sort, uniq);
510			dest->list[dest->count++] = (void *)dp;
511		}
512	}
513}
514
515
516void Move_line_list( struct line_list *dest, struct line_list *src )
517{
518	int i;
519
520	Free_line_list(dest);
521	Check_max(dest,src->count);
522	for( i = 0; i < src->count; ++i ){
523		dest->list[i] = src->list[i];
524		src->list[i] = 0;
525	}
526	src->count = 0;
527	dest->count = i;
528}
529
530/*
531 * Split( struct line_list *l, char *str, int sort, char *keysep,
532 *		int uniq, int trim, int nocomments, char *escape )
533 *  Split the str up into strings, as delimted by sep.
534 *   put duplicates of the original into the line_list l.
535 *  If sort != 0, then sort them using keysep to separate sort key from value
536 *  if uniq != then replace rather than add entries
537 *  if trim != 0 then remove leading and trailing whitespace and
538 *    if trim is a printable character any characters at start == trim
539 *  if nocomments != 0, then remove comments as well
540 *  if escape != 0, then allow the characters in the string to be escaped
541 *     i.e. - escape = ":" then \: would be replace by :
542 *
543 */
544void Split( struct line_list *l, char *str, const char *sep,
545	int sort, const char *keysep, int uniq, int trim, int nocomments, char *escape )
546{
547	char *end = 0, *t, *buffer = 0;
548	int len, blen = 0;
549	if(DEBUGL5){
550		char b[40];
551		int n;
552		SNPRINTF( b,sizeof(b)-8)"%s",str );
553		if( (n = safestrlen(b)) > (int)sizeof(b)-10 ) strcpy( b+n,"..." );
554		LOGDEBUG("Split: str 0x%lx '%s', sep '%s', escape '%s', sort %d, keysep '%s', uniq %d, trim %d",
555			Cast_ptr_to_long(str), b, sep, escape, sort, keysep, uniq, trim );
556	}
557	for( ; str && *str; str = end ){
558		end = 0;
559		t = str;
560		if( !ISNULL(sep) ) while( (t = safestrpbrk( t, sep )) ){
561			if( escape && t != str && cval(t-1) == '\\'
562				&& strchr( escape, cval(t) ) ){
563				++t;
564				DEBUG5("Split: escape '%s'", t );
565				continue;
566			}
567			end = t+1;
568			break;
569		}
570		if( !end ){
571			t = str + safestrlen(str);
572		}
573		DEBUG5("Split: str 0x%lx, ('%c%c...') end 0x%lx, t 0x%lx",
574			Cast_ptr_to_long(str), str[0], str[1],
575			Cast_ptr_to_long(end), Cast_ptr_to_long(t));
576		if( trim ){
577			while( isspace(cval(str)) ) ++str;
578			/* you can also remove leading characters */
579			if( cval(str) == trim && isprint(trim) ) ++str;
580			for( ; t > str && isspace(cval(t-1)); --t );
581		}
582		len = t - str;
583		DEBUG5("Split: after trim len %d, str 0x%lx, end 0x%lx, t 0x%lx",
584			len, Cast_ptr_to_long(str),
585			Cast_ptr_to_long(end), Cast_ptr_to_long(t));
586		if( len <= 0 || (nocomments && *str == '#') ) continue;
587		if( blen <= len ){
588			blen = 2*len;
589			buffer = realloc_or_die(buffer,blen+1,__FILE__,__LINE__);
590		}
591		memmove(buffer,str,len);
592		buffer[len] = 0;
593		Add_line_list( l, buffer, keysep, sort, uniq );
594	}
595	if( buffer ) free(buffer);
596}
597
598char *Join_line_list( struct line_list *l, char *sep )
599{
600	char *s, *t, *str = 0;
601	int len = 0, i, n = 0;
602
603	if( sep ) n = safestrlen(sep);
604	for( i = 0; i < l->count; ++i ){
605		s = l->list[i];
606		if( s && *s ) len += safestrlen(s) + n;
607	}
608	if( len ){
609		str = malloc_or_die(len+1,__FILE__,__LINE__);
610		t = str;
611		for( i = 0; i < l->count; ++i ){
612			s = l->list[i];
613			if( s && *s ){
614				strcpy( t, s );
615				t += safestrlen(t);
616				if( n ){
617					strcpy(t,sep);
618					t += n;
619				}
620			}
621		}
622		*t = 0;
623	}
624	return( str );
625}
626
627char *Join_line_list_with_sep( struct line_list *l, char *sep )
628{
629	char *s = Join_line_list( l, sep );
630	int len = 0;
631
632	if( sep ) len = safestrlen(sep);
633	if( s ){
634		*(s+safestrlen(s)-len) = 0;;
635	}
636	return( s );
637}
638
639/*
640 * join the line list with a separator, putting quotes around
641 *  the entries starting at position 1.
642 */
643char *Join_line_list_with_quotes( struct line_list *l, char *sep )
644{
645	char *s, *t, *str = 0;
646	int len = 0, i, n = 0;
647
648	if( sep ) n = safestrlen(sep);
649	for( i = 0; i < l->count; ++i ){
650		s = l->list[i];
651		if( s && *s ) len += safestrlen(s) + n + 2;
652	}
653	if( len ){
654		str = malloc_or_die(len+1,__FILE__,__LINE__);
655		t = str;
656		for( i = 0; i < l->count; ++i ){
657			s = l->list[i];
658			if( s && *s ){
659				if( i ) *t++ = '\'';
660				strcpy( t, s );
661				t += safestrlen(t);
662				if( i ) *t++ = '\'';
663				if( n ){
664					strcpy(t,sep);
665					t += n;
666				}
667			}
668		}
669		*t = 0;
670	}
671	return( str );
672}
673
674#ifdef ORIGINAL_DEBUG//JY@1020
675void Dump_line_list( const char *title, struct line_list *l )
676{
677	int i;
678	LOGDEBUG("Dump_line_list: %s - 0x%lx, count %d, max %d, list 0x%lx",
679		title, Cast_ptr_to_long(l), l?l->count:0, l?l->max:0, l?Cast_ptr_to_long(l->list):(long)0 );
680	if(l)for( i = 0; i < l->count; ++i ){
681		LOGDEBUG( "  [%2d] 0x%lx ='%s'", i, Cast_ptr_to_long(l->list[i]), l->list[i] );
682	}
683}
684#endif
685
686#ifdef ORIGINAL_DEBUG//JY@1020
687void Dump_line_list_sub( const char *title, struct line_list *l )
688{
689	int i;
690	LOGDEBUG(" %s - 0x%lx, count %d, max %d, list 0x%lx",
691		title, Cast_ptr_to_long(l), l?l->count:0, l?l->max:0, l?Cast_ptr_to_long(l->list):(long)0 );
692	if(l)for( i = 0; i < l->count; ++i ){
693		LOGDEBUG( "  [%2d] 0x%lx ='%s'", i, Cast_ptr_to_long(l->list[i]), l->list[i] );
694	}
695}
696#endif
697
698/*
699 * Find_str_in_flat
700 *   find the string value starting with key and ending with sep
701 */
702char *Find_str_in_flat( char *str, const char *key, const char *sep )
703{
704	char *s, *end;
705	int n, c = 0;
706
707	if( str == 0 || key == 0 || sep == 0 ) return( 0 );
708	n = safestrlen(key);
709	for( s = str; (s = strstr(s,key)); ){
710		s += n;
711		if( *s == '=' ){
712			++s;
713			if( (end = safestrpbrk( s, sep )) ) { c = *end; *end = c; }
714			s = safestrdup(s,__FILE__,__LINE__);
715			if( end ) *end = c;
716			return( s );
717		}
718	}
719	return( 0 );
720}
721
722/*
723 * int Find_first_key( struct line_list *l, char *key, char *sep, int *mid )
724 * int Find_last_key( struct line_list *l, char *key, char *sep, int *mid )
725 *  Search the list for the last corresponding key value
726 *  The list has lines of the form:
727 *    key [separator] value
728 *  returns:
729 *    *at = index of last tested value
730 *    return value: 0 if found;
731 *                  <0 if list[*at] < key
732 *                  >0 if list[*at] > key
733 */
734
735int Find_last_key( struct line_list *l, const char *key, const char *sep, int *m )
736{
737	int c=0, cmp=-1, cmpl = 0, bot, top, mid;
738	char *s, *t;
739	mid = bot = 0; top = l->count-1;
740	DEBUG5("Find_last_key: count %d, key '%s'", l->count, key );
741	while( cmp && bot <= top ){
742		mid = (top+bot)/2;
743		s = l->list[mid];
744		t = 0;
745		if( sep && (t = safestrpbrk(s, sep )) ) { c = *t; *t = 0; }
746		cmp = safestrcasecmp(key,s);
747		if( t ) *t = c;
748		if( cmp > 0 ){
749			bot = mid+1;
750		} else if( cmp < 0 ){
751			top = mid -1;
752		} else while( mid+1 < l->count ){
753			s = l->list[mid+1];
754			DEBUG5("Find_last_key: existing entry, mid %d, '%s'",
755				mid, l->list[mid] );
756			t = 0;
757			if( sep && (t = safestrpbrk(s, sep )) ) { c = *t; *t = 0; }
758			cmpl = safestrcasecmp(s,key);
759			if( t ) *t = c;
760			if( cmpl ) break;
761			++mid;
762		}
763		DEBUG5("Find_last_key: cmp %d, top %d, mid %d, bot %d",
764			cmp, top, mid, bot);
765	}
766	if( m ) *m = mid;
767	DEBUG5("Find_last_key: key '%s', cmp %d, mid %d", key, cmp, mid );
768	return( cmp );
769}
770
771
772/*
773 * int Find_first_casekey( struct line_list *l, char *key, char *sep, int *mid )
774 * int Find_last_casekey( struct line_list *l, char *key, char *sep, int *mid )
775 *  Search the list for the last corresponding key value using case sensitive keys
776 *  The list has lines of the form:
777 *    key [separator] value
778 *  returns:
779 *    *at = index of last tested value
780 *    return value: 0 if found;
781 *                  <0 if list[*at] < key
782 *                  >0 if list[*at] > key
783 */
784
785int Find_last_casekey( struct line_list *l, const char *key, const char *sep, int *m )
786{
787	int c=0, cmp=-1, cmpl = 0, bot, top, mid;
788	char *s, *t;
789	mid = bot = 0; top = l->count-1;
790	DEBUG5("Find_last_casekey: count %d, key '%s'", l->count, key );
791	while( cmp && bot <= top ){
792		mid = (top+bot)/2;
793		s = l->list[mid];
794		t = 0;
795		if( sep && (t = safestrpbrk(s, sep )) ) { c = *t; *t = 0; }
796		cmp = safestrcmp(key,s);
797		if( t ) *t = c;
798		if( cmp > 0 ){
799			bot = mid+1;
800		} else if( cmp < 0 ){
801			top = mid -1;
802		} else while( mid+1 < l->count ){
803			s = l->list[mid+1];
804			DEBUG5("Find_last_key: existing entry, mid %d, '%s'",
805				mid, l->list[mid] );
806			t = 0;
807			if( sep && (t = safestrpbrk(s, sep )) ) { c = *t; *t = 0; }
808			cmpl = safestrcmp(s,key);
809			if( t ) *t = c;
810			if( cmpl ) break;
811			++mid;
812		}
813		DEBUG5("Find_last_casekey: cmp %d, top %d, mid %d, bot %d",
814			cmp, top, mid, bot);
815	}
816	if( m ) *m = mid;
817	DEBUG5("Find_last_casekey: key '%s', cmp %d, mid %d", key, cmp, mid );
818	return( cmp );
819}
820
821int Find_first_key( struct line_list *l, const char *key, const char *sep, int *m )
822{
823	int c=0, cmp=-1, cmpl = 0, bot, top, mid;
824	char *s, *t;
825	mid = bot = 0; top = l->count-1;
826	DEBUG5("Find_first_key: count %d, key '%s', sep '%s'",
827		l->count, key, sep );
828	while( cmp && bot <= top ){
829		mid = (top+bot)/2;
830		s = l->list[mid];
831		t = 0;
832		if( sep && (t = safestrpbrk(s, sep )) ) { c = *t; *t = 0; }
833		cmp = safestrcasecmp(key,s);
834		if( t ) *t = c;
835		if( cmp > 0 ){
836			bot = mid+1;
837		} else if( cmp < 0 ){
838			top = mid -1;
839		} else while( mid > 0 ){
840			s = l->list[mid-1];
841			t = 0;
842			if( sep && (t = safestrpbrk(s, sep )) ) { c = *t; *t = 0; }
843			cmpl = safestrcasecmp(s,key);
844			if( t ) *t = c;
845			if( cmpl ) break;
846			--mid;
847		}
848		DEBUG5("Find_first_key: cmp %d, top %d, mid %d, bot %d",
849			cmp, top, mid, bot);
850	}
851	if( m ) *m = mid;
852	DEBUG5("Find_first_key: cmp %d, mid %d, key '%s', count %d",
853		cmp, mid, key, l->count );
854	return( cmp );
855}
856
857int Find_first_casekey( struct line_list *l, const char *key, const char *sep, int *m )
858{
859	int c=0, cmp=-1, cmpl = 0, bot, top, mid;
860	char *s, *t;
861	mid = bot = 0; top = l->count-1;
862	DEBUG5("Find_first_casekey: count %d, key '%s', sep '%s'",
863		l->count, key, sep );
864	while( cmp && bot <= top ){
865		mid = (top+bot)/2;
866		s = l->list[mid];
867		t = 0;
868		if( sep && (t = safestrpbrk(s, sep )) ) { c = *t; *t = 0; }
869		cmp = safestrcmp(key,s);
870		if( t ) *t = c;
871		if( cmp > 0 ){
872			bot = mid+1;
873		} else if( cmp < 0 ){
874			top = mid -1;
875		} else while( mid > 0 ){
876			s = l->list[mid-1];
877			t = 0;
878			if( sep && (t = safestrpbrk(s, sep )) ) { c = *t; *t = 0; }
879			cmpl = safestrcmp(s,key);
880			if( t ) *t = c;
881			if( cmpl ) break;
882			--mid;
883		}
884		DEBUG5("Find_first_casekey: cmp %d, top %d, mid %d, bot %d",
885			cmp, top, mid, bot);
886	}
887	if( m ) *m = mid;
888	DEBUG5("Find_first_casekey: cmp %d, mid %d, key '%s', count %d",
889		cmp, mid, key, l->count );
890	return( cmp );
891}
892
893/*
894 * char *Find_value( struct line_list *l, char *key, char *sep )
895 *  Search the list for a corresponding key value
896 *          value
897 *   key    "1"
898 *   key@   "0"
899 *   key#v  v
900 *   key=v  v
901 *   key v  v
902 *  If key does not exist, we return "0"
903 */
904
905const char *Find_value( struct line_list *l, const char *key, const char *sep )
906{
907	const char *s = "0";
908	int mid, cmp = -1;
909
910	DEBUG5("Find_value: key '%s', sep '%s'", key, sep );
911	if( l ) cmp = Find_first_key( l, key, sep, &mid );
912	DEBUG5("Find_value: key '%s', cmp %d, mid %d", key, cmp, mid );
913	if( cmp==0 ){
914		if( sep ){
915			s = Fix_val( safestrpbrk(l->list[mid], sep ) );
916		} else {
917			s = l->list[mid];
918		}
919	}
920	DEBUG4( "Find_value: key '%s', value '%s'", key, s );
921	return(s);
922}
923
924/*
925 * char *Find_first_letter( struct line_list *l, char letter, int *mid )
926 *   return the first entry starting with the letter
927 */
928
929char *Find_first_letter( struct line_list *l, const char letter, int *mid )
930{
931	char *s = 0;
932	int i;
933	if(l)for( i = 0; i < l->count; ++i ){
934		if( (s = l->list[i])[0] == letter ){
935			if( mid ) *mid = i;
936			DEBUG4( "Find_first_letter: letter '%c', at [%d]=value '%s'", letter, i, s );
937			return(s+1);
938		}
939	}
940	return(0);
941}
942
943/*
944 * char *Find_exists_value( struct line_list *l, char *key, char *sep )
945 *  Search the list for a corresponding key value
946 *          value
947 *   key    "1"
948 *   key@   "0"
949 *   key#v  v
950 *   key=v  v
951 *   If value exists we return 0 (null)
952 */
953
954const char *Find_exists_value( struct line_list *l, const char *key, const char *sep )
955{
956	const char *s = 0;
957	int mid, cmp = -1;
958
959	if( l ) cmp = Find_first_key( l, key, sep, &mid );
960	if( cmp==0 ){
961		if( sep ){
962			s = Fix_val( safestrpbrk(l->list[mid], sep ) );
963		} else {
964			s = l->list[mid];
965		}
966	}
967	DEBUG4( "Find_exists_value: key '%s', cmp %d, value '%s'", key, cmp, s );
968	return(s);
969}
970
971
972/*
973 * char *Find_str_value( struct line_list *l, char *key, char *sep )
974 *  Search the list for a corresponding key value
975 *          value
976 *   key    0
977 *   key@   0
978 *   key#v  0
979 *   key=v  v
980 */
981
982char *Find_str_value( struct line_list *l, const char *key, const char *sep )
983{
984	char *s = 0;
985	int mid, cmp = -1;
986
987	if( l ) cmp = Find_first_key( l, key, sep, &mid );
988	if( cmp==0 ){
989		/*
990		 *  value: NULL, "", "@", "=xx", "#xx".
991		 *  returns: "0", "1","0",  "xx",  "xx"
992		 */
993		if( sep ){
994			s = safestrpbrk(l->list[mid], sep );
995			if( s && *s == '=' ){
996				++s;
997			} else {
998				s = 0;
999			}
1000		} else {
1001			s = l->list[mid];
1002		}
1003	}
1004	DEBUG4( "Find_str_value: key '%s', value '%s'", key, s );
1005	return(s);
1006}
1007
1008
1009/*
1010 * char *Find_casekey_str_value( struct line_list *l, char *key, char *sep )
1011 *  Search the list for a corresponding key value using case sensitive keys
1012 *          value
1013 *   key    0
1014 *   key@   0
1015 *   key#v  0
1016 *   key=v  v
1017 */
1018
1019char *Find_casekey_str_value( struct line_list *l, const char *key, const char *sep )
1020{
1021	char *s = 0;
1022	int mid, cmp = -1;
1023
1024	if( l ) cmp = Find_first_casekey( l, key, sep, &mid );
1025	if( cmp==0 ){
1026		/*
1027		 *  value: NULL, "", "@", "=xx", "#xx".
1028		 *  returns: "0", "1","0",  "xx",  "xx"
1029		 */
1030		if( sep ){
1031			s = safestrpbrk(l->list[mid], sep );
1032			if( s && *s == '=' ){
1033				++s;
1034			} else {
1035				s = 0;
1036			}
1037		} else {
1038			s = l->list[mid];
1039		}
1040	}
1041	DEBUG4( "Find_casekey_str_value: key '%s', value '%s'", key, s );
1042	return(s);
1043}
1044
1045
1046/*
1047 * Set_str_value( struct line_list *l, char *key, char *value )
1048 *   set a string value in an ordered, sorted list
1049 */
1050void Set_str_value( struct line_list *l, const char *key, const char *value )
1051{
1052	char *s = 0;
1053	int mid;
1054	if( key == 0 ) return;
1055	if(DEBUGL6){
1056		char buffer[16];
1057		SNPRINTF(buffer,sizeof(buffer)-5)"%s",value);
1058		buffer[12] = 0;
1059		if( value && safestrlen(value) > 12 ) strcat(buffer,"...");
1060		LOGDEBUG("Set_str_value: '%s'= 0x%lx '%s'", key,
1061			Cast_ptr_to_long(value), buffer);
1062	}
1063	if( value && *value ){
1064		s = safestrdup3(key,"=",value,__FILE__,__LINE__);
1065		Add_line_list(l,s,Value_sep,1,1);
1066		if(s) free(s); s = 0;
1067	} else if( !Find_first_key(l, key, Value_sep, &mid ) ){
1068		Remove_line_list(l,mid);
1069	}
1070}
1071
1072/*
1073 * Set_expanded_str_value( struct line_list *l, char *key, char *value )
1074 *   set a string value in an ordered, sorted list
1075 */
1076void Set_expanded_str_value( struct line_list *l, const char *key, const char *orig )
1077{
1078	char *s = 0;
1079	char *value = 0;
1080	int mid;
1081	if( key == 0 ) return;
1082	value = Fix_str( (char *)orig );
1083	if(DEBUGL6){
1084		char buffer[16];
1085		SNPRINTF(buffer,sizeof(buffer)-5)"%s",value);
1086		buffer[12] = 0;
1087		if( value && safestrlen(value) > 12 ) strcat(buffer,"...");
1088		LOGDEBUG("Set_str_value: '%s'= 0x%lx '%s'", key,
1089			Cast_ptr_to_long(value), buffer);
1090	}
1091	if( value && *value ){
1092		s = safestrdup3(key,"=",value,__FILE__,__LINE__);
1093		Add_line_list(l,s,Value_sep,1,1);
1094		if(s) free(s); s = 0;
1095	} else if( !Find_first_key(l, key, Value_sep, &mid ) ){
1096		Remove_line_list(l,mid);
1097	}
1098	if( value ) free(value); value = 0;
1099}
1100
1101/*
1102 * Set_casekey_str_value( struct line_list *l, char *key, char *value )
1103 *   set an string value in an ordered, sorted list, with case sensitive keys
1104 */
1105void Set_casekey_str_value( struct line_list *l, const char *key, const char *value )
1106{
1107	char *s = 0;
1108	int mid;
1109	if( key == 0 ) return;
1110	if(DEBUGL6){
1111		char buffer[16];
1112		SNPRINTF(buffer,sizeof(buffer)-5)"%s",value);
1113		buffer[12] = 0;
1114		if( value && safestrlen(value) > 12 ) strcat(buffer,"...");
1115		LOGDEBUG("Set_str_value: '%s'= 0x%lx '%s'", key,
1116			Cast_ptr_to_long(value), buffer);
1117	}
1118	if( value && *value ){
1119		s = safestrdup3(key,"=",value,__FILE__,__LINE__);
1120		Add_casekey_line_list(l,s,Value_sep,1,1);
1121		if(s) free(s); s = 0;
1122	} else if( !Find_first_casekey(l, key, Value_sep, &mid ) ){
1123		Remove_line_list(l,mid);
1124	}
1125}
1126
1127
1128/*
1129 * Set_flag_value( struct line_list *l, char *key, int value )
1130 *   set a flag value in an ordered, sorted list
1131 */
1132void Set_flag_value( struct line_list *l, const char *key, long value )
1133{
1134	char buffer[SMALLBUFFER];
1135	if( key == 0 ) return;
1136	SNPRINTF(buffer,sizeof(buffer))"%s=0x%lx",key,value);
1137	Add_line_list(l,buffer,Value_sep,1,1);
1138}
1139
1140
1141/*
1142 * Set_nz_flag_value( struct line_list *l, char *key, int value )
1143 *   set a nonzero flag value in an ordered, sorted list
1144 */
1145void Set_nz_flag_value( struct line_list *l, const char *key, long value )
1146{
1147	if( !Find_flag_value( l, key, Value_sep ) ){
1148		Set_flag_value( l, key, value );
1149	}
1150}
1151
1152
1153/*
1154 * Set_double_value( struct line_list *l, char *key, int value )
1155 *   set a double value in an ordered, sorted list
1156 */
1157void Set_double_value( struct line_list *l, const char *key, double value )
1158{
1159	char buffer[SMALLBUFFER];
1160	if( key == 0 ) return;
1161	SNPRINTF(buffer,sizeof(buffer))"%s=%0.0f",key,value);
1162	Add_line_list(l,buffer,Value_sep,1,1);
1163}
1164
1165
1166/*
1167 * Set_decimal_value( struct line_list *l, char *key, int value )
1168 *   set a decimal value in an ordered, sorted list
1169 */
1170void Set_decimal_value( struct line_list *l, const char *key, long value )
1171{
1172	char buffer[SMALLBUFFER];
1173	if( key == 0 ) return;
1174	SNPRINTF(buffer,sizeof(buffer))"%s=%ld",key,value);
1175	Add_line_list(l,buffer,Value_sep,1,1);
1176}
1177/*
1178 * Remove_line_list( struct line_list *l, int mid )
1179 *   Remove the indicated entry and move the other
1180 *   entries up.
1181 */
1182void Remove_line_list( struct line_list *l, int mid )
1183{
1184	char *s;
1185	if( mid >= 0 && mid < l->count ){
1186		if( (s = l->list[mid]) ){
1187			free(s);
1188			l->list[mid] = 0;
1189		}
1190		memmove(&l->list[mid],&l->list[mid+1],(l->count-mid-1)*sizeof(char *));
1191		--l->count;
1192	}
1193}
1194
1195
1196/*
1197 * Remove_duplicates_line_list( struct line_list *l )
1198 *   Remove duplicate entries in the list
1199 */
1200void Remove_duplicates_line_list( struct line_list *l )
1201{
1202	char *s, *t;
1203	int i, j;
1204	for( i = 0; i < l->count; ){
1205		if( (s = l->list[i]) ){
1206			for( j = i+1; j < l->count; ){
1207				if( !(t = l->list[j]) || !safestrcmp(s,t) ){
1208					Remove_line_list( l, j );
1209				} else {
1210					++j;
1211				}
1212			}
1213			++i;
1214		} else {
1215			Remove_line_list( l, i );
1216		}
1217	}
1218}
1219
1220
1221/*
1222 * char *Find_flag_value( struct line_list *l, char *key, char *sep )
1223 *  Search the list for a corresponding key value
1224 *          value
1225 *   key    1
1226 *   key@   0
1227 *   key#v  v  if v is integer, 0 otherwise
1228 *   key=v  v  if v is integer, 0 otherwise
1229 */
1230
1231int Find_flag_value( struct line_list *l, const char *key, const char *sep )
1232{
1233	const char *s;
1234	char *e;
1235	int n = 0;
1236
1237	if( l && (s = Find_value( l, key, sep )) ){
1238		e = 0;
1239		n = strtol(s,&e,0);
1240		if( !e || *e ) n = 0;
1241	}
1242	DEBUG4( "Find_flag_value: key '%s', value '%d'", key, n );
1243	return(n);
1244}
1245
1246
1247/*
1248 * char *Find_decimal_value( struct line_list *l, char *key, char *sep )
1249 *  Search the list for a corresponding key value
1250 *          value
1251 *   key    1
1252 *   key@   0
1253 *   key#v  v  if v is decimal, 0 otherwise
1254 *   key=v  v  if v is decimal, 0 otherwise
1255 */
1256
1257int Find_decimal_value( struct line_list *l, const char *key, const char *sep )
1258{
1259	const char *s = 0;
1260	char *e;
1261	int n = 0;
1262
1263	if( l && (s = Find_value( l, key, sep )) ){
1264		e = 0;
1265		n = strtol(s,&e,10);
1266		if( !e || *e ){
1267			e = 0;
1268			n = strtol(s,&e,0);
1269			if( !e || *e ) n = 0;
1270		}
1271	}
1272	DEBUG4( "Find_decimal_value: key '%s', value '%d'", key, n );
1273	return(n);
1274}
1275
1276
1277/*
1278 * double Find_double_value( struct line_list *l, char *key, char *sep )
1279 *  Search the list for a corresponding key value
1280 *          value
1281 *   key    1
1282 *   key@   0
1283 *   key#v  v  if v is decimal, 0 otherwise
1284 *   key=v  v  if v is decimal, 0 otherwise
1285 */
1286
1287double Find_double_value( struct line_list *l, const char *key, const char *sep )
1288{
1289	const char *s = 0;
1290	char *e;
1291	double n = 0;
1292
1293	if( l && (s = Find_value( l, key, sep )) ){
1294		e = 0;
1295		n = strtod(s,&e);
1296	}
1297	DEBUG4( "Find_double_value: key '%s', value '%0.0f'", key, n );
1298	return(n);
1299}
1300
1301/*
1302 * char *Fix_val( char *s )
1303 *  passed: NULL, "", "@", "=xx", "#xx".
1304 *  returns: "0", "1","0",  "xx",  "xx"
1305 */
1306
1307
1308const char *Fix_val( const char *s )
1309{
1310	int c = 0;
1311	if( s ){
1312		c = cval(s);
1313		++s;
1314		while( isspace(cval(s)) ) ++s;
1315	}
1316	if( c == 0 ){
1317		s = "1";
1318	} else if( c == '@' ){
1319		s = "0";
1320	}
1321	return( s );
1322}
1323
1324/*
1325 * Find_tags( struct line_list dest,
1326 *  struct line_list *list, char *tag )
1327 *
1328 * Scan the list (ordered, of course) for the
1329 * set of entries starting with 'tag' and extract them
1330 * to list
1331 */
1332
1333void Find_tags( struct line_list *dest, struct line_list *l, char *key )
1334{
1335	int cmp=-1, cmpl = 0, bot, top, mid, len;
1336	char *s;
1337
1338	if( key == 0 || *key == 0 ) return;
1339	mid = bot = 0; top = l->count-1;
1340	len = safestrlen(key);
1341	DEBUG5("Find_tags: count %d, key '%s'", l->count, key );
1342	while( cmp && bot <= top ){
1343		mid = (top+bot)/2;
1344		s = l->list[mid];
1345		cmp = safestrncasecmp(key,s,len);
1346		if( cmp > 0 ){
1347			bot = mid+1;
1348		} else if( cmp < 0 ){
1349			top = mid -1;
1350		} else while( mid > 0 ){
1351			DEBUG5("Find_tags: existing entry, mid %d, '%s'", mid, l->list[mid] );
1352			s = l->list[mid-1];
1353			cmpl = safestrncasecmp(s,key,len);
1354			if( cmpl ) break;
1355			--mid;
1356		}
1357		DEBUG5("Find_tags: cmp %d, top %d, mid %d, bot %d",
1358			cmp, top, mid, bot);
1359	}
1360	if( cmp == 0 ){
1361		s = l->list[mid];
1362		do{
1363			DEBUG5("Find_tags: adding '%s'", s+len );
1364			Add_line_list(dest,s+len,Value_sep,1,1);
1365			++mid;
1366		} while( mid < l->count
1367			&& (s = l->list[mid])
1368			&& !(cmp = safestrncasecmp(key,s,len)));
1369	}
1370}
1371
1372/*
1373 * Find_defaulttags( struct line_list dest,
1374 *  struct keywords *var_list, char *tag )
1375 *
1376 * Scan the variable list for default values
1377 * starting with 'tag' and extract them
1378 * to list
1379 */
1380
1381void Find_default_tags( struct line_list *dest,
1382	struct keywords *var_list, char *tag )
1383{
1384	int len = safestrlen(tag);
1385	char *key, *value;
1386
1387	if( var_list ) while( var_list->keyword ){
1388		if( !strncmp((key = var_list->keyword), tag, len)
1389			&& (value = var_list->default_value) ){
1390			if( *value == '=' ) ++value;
1391			DEBUG5("Find_default_tags: adding '%s'='%s'", key, value);
1392			Set_str_value(dest, key+len, value );
1393		}
1394		++var_list;
1395	}
1396}
1397
1398
1399
1400/*
1401 * Read_file_list( struct line_list *model, char *str
1402 *	char *sep, int sort, char *keysep, int uniq, int trim, int marker,
1403 *  int doinclude, int nocomment, int depth, int maxdepth )
1404 *  read the model information from these files
1405 *  if marker != then add a NULL line after each file
1406 */
1407
1408void Read_file_list( int required, struct line_list *model, char *str,
1409	const char *linesep, int sort, const char *keysep, int uniq, int trim,
1410	int marker, int doinclude, int nocomment, int depth, int maxdepth )
1411{
1412	struct line_list l;
1413	int i, start, end, c=0, n, found;
1414	char *s, *t;
1415	struct stat statb;
1416
1417	Init_line_list(&l);
1418	DEBUG3("Read_file_list: '%s', doinclude %d, depth %d, maxdepth %d",
1419		str, doinclude, depth, maxdepth );
1420	if( depth > maxdepth ){
1421		Errorcode = JABORT;
1422		LOGERR_DIE(LOG_ERR)
1423			"Read_file_list: recursion depth %d exceeds maxdepth %d for file '%s'",
1424			depth, maxdepth, str );
1425	}
1426	Split( &l, str, File_sep, 0, 0, 0, 1, 0 ,0);
1427	start = model->count;
1428	for( i = 0; i < l.count; ++i ){
1429		if( stat( l.list[i], &statb ) == -1 ){
1430			if( required || depth ){
1431				Errorcode = JABORT;
1432				LOGERR_DIE(LOG_ERR)
1433					"Read_file_list: cannot stat required or included file '%s'",
1434					l.list[i] );
1435			}
1436			continue;
1437		}
1438		Read_file_and_split( model, l.list[i], linesep, sort, keysep,
1439			uniq, trim, nocomment );
1440		if( doinclude ){
1441			/* scan through the list, looking for include lines */
1442			for( end = model->count; start < end; ){
1443				t = 0;
1444				s = model->list[start];
1445				found = 0;
1446				t = 0;
1447				if( s && (t = safestrpbrk( s, Whitespace )) ){
1448					c = *t; *t = 0;
1449					found = !safestrcasecmp( s, "include" );
1450					*t = c;
1451				}
1452				if( found ){
1453					DEBUG4("Read_file_list: include '%s'", t+1 );
1454					Read_file_list( 1, model, t+1, linesep, sort, keysep, uniq, trim,
1455						marker, doinclude, nocomment, depth+1, maxdepth );
1456					/* at this point the include lines are at
1457					 *  end to model->count-1
1458					 * we need to move the lines from start to end-1
1459					 * to model->count, and then move end to start
1460					 */
1461					n = end - start;
1462					Check_max( model, n );
1463					/* copy to end */
1464#ifdef ORIGINAL_DEBUG//JY@1020
1465					if(DEBUGL5)Dump_line_list("Read_file_list: include before",
1466						model );
1467#endif
1468					memmove( &model->list[model->count],
1469						&model->list[start], n*sizeof(char *) );
1470					memmove( &model->list[start],
1471						&model->list[end],(model->count-start)*sizeof(char *));
1472#ifdef ORIGINAL_DEBUG//JY@1020
1473					if(DEBUGL4)Dump_line_list("Read_file_list: include after",
1474						model );
1475#endif
1476					end = model->count;
1477					start = end - n;
1478					DEBUG4("Read_file_list: start now '%s'",model->list[start]);
1479					/* we get rid of include line */
1480					s = model->list[start];
1481					free(s);
1482					model->list[start] = 0;
1483					memmove( &model->list[start], &model->list[start+1],
1484						n*sizeof(char *) );
1485					--model->count;
1486					end = model->count;
1487				} else {
1488					++start;
1489				}
1490			}
1491		}
1492		if( marker ){
1493			/* put null at end of list */
1494			Check_max( model, 1 );
1495			model->list[model->count++] = 0;
1496		}
1497	}
1498	Free_line_list(&l);
1499#ifdef ORIGINAL_DEBUG//JY@1020
1500	if(DEBUGL5)Dump_line_list("Read_file_list: result", model);
1501#endif
1502}
1503
1504void Read_fd_and_split( struct line_list *list, int fd,
1505	const char *linesep, int sort, const char *keysep, int uniq,
1506	int trim, int nocomment )
1507{
1508	int size = 0, count, len;
1509	char *sv;
1510	char buffer[LARGEBUFFER];
1511
1512	sv = 0;
1513	while( (count = read(fd, buffer, sizeof(buffer)-1)) > 0 ){
1514		buffer[count] = 0;
1515		len = size+count+1;
1516		if( (sv = realloc_or_die( sv, len,__FILE__,__LINE__)) == 0 ){
1517			Errorcode = JFAIL;
1518			LOGERR_DIE(LOG_INFO) "Read_fd_and_split: realloc %d failed", len );
1519		}
1520		memmove( sv+size, buffer, count );
1521		size += count;
1522		sv[size] = 0;
1523	}
1524	close( fd );
1525	DEBUG4("Read_fd_and_split: size %d", size );
1526	Split( list, sv, linesep, sort, keysep, uniq, trim, nocomment ,0);
1527	if( sv ) free( sv );
1528}
1529
1530void Read_file_and_split( struct line_list *list, char *file,
1531	const char *linesep, int sort, const char *keysep, int uniq,
1532	int trim, int nocomment )
1533{
1534	int fd;
1535	struct stat statb;
1536
1537	DEBUG3("Read_file_and_split: '%s', trim %d, nocomment %d",
1538		file, trim, nocomment );
1539	if( (fd = Checkread( file, &statb )) < 0 ){
1540#ifdef ORIGINAL_DEBUG//JY@1020
1541		LOGERR_DIE(LOG_INFO)
1542		"Read_file_and_split: cannot open '%s' - '%s'",
1543			file, Errormsg(errno) );
1544#endif
1545	}
1546	Read_fd_and_split( list, fd, linesep, sort, keysep, uniq,
1547		trim, nocomment );
1548}
1549
1550#ifdef REMOVE
1551/*
1552 * Printcap information
1553 */
1554
1555
1556/*
1557 * Build_pc_names( struct line_list *names, struct line_list *order, char *s )
1558 *  names = list of aliases and names
1559 *  order = order that names were found
1560 *
1561 *   get the primary name
1562 *   if it is not in the names lists, add to order list
1563 *   put the names and aliases in the names list
1564 */
1565int  Build_pc_names( struct line_list *names, struct line_list *order,
1566	char *str, struct host_information *hostname  )
1567{
1568	char *s, *t;
1569	int c = 0, i, ok = 0, len, start_oh, end_oh;
1570	struct line_list l, opts, oh;
1571
1572	Init_line_list(&l);
1573	Init_line_list(&opts);
1574	Init_line_list(&oh);
1575
1576	DEBUG4("Build_pc_names: '%s'", str);
1577	if( (s = safestrpbrk(str, ":")) ){
1578		c = *s; *s = 0;
1579		Split(&opts,s+1,":",1,Value_sep,0,1,0,":");
1580#if 0
1581		for( i = 0; i < opts.count; ++i ){
1582			t = opts.list[i];
1583			while( t && (t = strstr(t,"\\:")) ){
1584				memmove(t, t+1, safestrlen(t+1)+1 );
1585				++t;
1586			}
1587		}
1588#endif
1589	}
1590	Split(&l,str,"|",0,0,0,1,0,0);
1591	if( s ) *s = c;
1592#ifdef ORIGINAL_DEBUG//JY@1020
1593	if(DEBUGL4)Dump_line_list("Build_pc_names- names", &l);
1594	if(DEBUGL4)Dump_line_list("Build_pc_names- options", &opts);
1595#endif
1596	if( l.count == 0 ){
1597		if(Warnings){
1598			WARNMSG(
1599			"no name for printcap entry '%s'", str );
1600		} else {
1601			LOGMSG(LOG_INFO)
1602			"no name for printcap entry '%s'", str );
1603		}
1604	} else {
1605		ok = 1;
1606		if( Find_flag_value( &opts,SERVER,Value_sep) && !Is_server ){
1607			DEBUG4("Build_pc_names: not server" );
1608			ok = 0;
1609		} else if( Find_flag_value( &opts,CLIENT,Value_sep) && Is_server ){
1610			DEBUG4("Build_pc_names: not client" );
1611			ok = 0;
1612		} else if( !Find_first_key(&opts,"oh",Value_sep,&start_oh)
1613			&& !Find_last_key(&opts,"oh",Value_sep,&end_oh) ){
1614			ok = 0;
1615			DEBUG4("Build_pc_names: start_oh %d, end_oh %d",
1616				start_oh, end_oh );
1617			for( i = start_oh; !ok && i <= end_oh; ++i ){
1618				DEBUG4("Build_pc_names: [%d] '%s'", i, opts.list[i] );
1619				if( (t = safestrchr( opts.list[i], '=' )) ){
1620					Split(&oh,t+1,File_sep,0,0,0,1,0,0);
1621					ok = !Match_ipaddr_value(&oh, hostname);
1622					DEBUG4("Build_pc_names: check host '%s', ok %d",
1623						t+1, ok );
1624					Free_line_list(&oh);
1625				}
1626			}
1627		}
1628		if( ok && ((s = safestrpbrk( l.list[0], Value_sep))
1629			|| (s = safestrpbrk( l.list[0], "@")) ) ){
1630			ok = 0;
1631			if(Warnings){
1632				WARNMSG(
1633				"bad printcap name '%s', has '%c' character",
1634				l.list[0], *s );
1635			} else {
1636				LOGMSG(LOG_INFO)
1637				"bad printcap name '%s', has '%c' character",
1638				l.list[0], *s );
1639			}
1640		} else if( ok ){
1641#ifdef ORIGINAL_DEBUG//JY@1020
1642			if(DEBUGL4)Dump_line_list("Build_pc_names: adding ", &l);
1643			if(DEBUGL4)Dump_line_list("Build_pc_names- before names", names );
1644			if(DEBUGL4)Dump_line_list("Build_pc_names- before order", order );
1645#endif
1646			if( !Find_exists_value( names, l.list[0], Value_sep ) ){
1647				Add_line_list(order,l.list[0],0,0,0);
1648			}
1649			for( i = 0; i < l.count; ++i ){
1650				if( safestrpbrk( l.list[i], Value_sep ) ){
1651					continue;
1652				}
1653				Set_str_value(names,l.list[i],l.list[0]);
1654			}
1655			len = safestrlen(str);
1656			s = str;
1657			DEBUG4("Build_pc_names: before '%s'", str );
1658			*s = 0;
1659			for( i = 0; i < l.count; ++i ){
1660				if( *str ) *s++ = '|';
1661				strcpy(s,l.list[i]);
1662				s += safestrlen(s);
1663			}
1664			for( i = 0; i < opts.count; ++i ){
1665				*s++ = ':';
1666				strcpy(s,opts.list[i]);
1667				s += safestrlen(s);
1668			}
1669			if( safestrlen(str) > len ){
1670				Errorcode = JABORT;
1671				FATAL(LOG_ERR) "Build_pc_names: LINE GREW! fatal error");
1672			}
1673			DEBUG4("Build_pc_names: after '%s'", str );
1674		}
1675	}
1676
1677	Free_line_list(&l);
1678	Free_line_list(&opts);
1679	DEBUG4("Build_pc_names: returning ok '%d'", ok );
1680	return ok;
1681}
1682
1683/*
1684 * Build_printcap_info
1685 *  OUTPUT
1686 *  names = list of names in the form
1687 *           alias=primary
1688 *  order = list of names in order
1689 *  list  = list of all of the printcap entries
1690 *  INPUT
1691 *  input = orginal list information in split line format
1692 *
1693 *  run through the raw information, extrating primary name and aliases
1694 *  create entries in the names and order lists
1695 */
1696void Build_printcap_info(
1697	struct line_list *names, struct line_list *order,
1698	struct line_list *list, struct line_list *raw,
1699	struct host_information *hostname )
1700{
1701	int i, c;
1702	char *t, *keyid = 0;
1703	int appendline = 0;
1704
1705	DEBUG1("Build_printcap_info: list->count %d, raw->count %d",
1706		list->count, raw->count );
1707	for( i = 0; i < raw->count; ++i ){
1708		t = raw->list[i];
1709		DEBUG4("Build_printcap_info: doing '%s'", t );
1710		if( t ) while( isspace( cval(t) ) ) ++t;
1711		/* ignore blank lines and comments */
1712		if( t == 0 || (c = *t) == 0 || c == '#') continue;
1713		/* append lines starting with :, | */
1714		if( keyid
1715			&& (safestrchr(Printcap_sep,c) || appendline) ){
1716			DEBUG4("Build_printcap_info: old keyid '%s', adding '%s'",
1717				keyid, t );
1718			keyid = safeextend3(keyid, " ", t,__FILE__,__LINE__ );
1719			if( (appendline = (Lastchar(keyid) == '\\')) ){
1720				keyid[safestrlen(keyid)-1] = 0;
1721			}
1722		} else {
1723			DEBUG4("Build_printcap_info: old keyid '%s', new '%s'",
1724				keyid, t );
1725			if( keyid ){
1726				if( Build_pc_names( names, order, keyid, hostname ) ){
1727					Add_line_list( list, keyid, Printcap_sep, 1, 0 );
1728				}
1729				free(keyid); keyid = 0;
1730			}
1731			keyid = safestrdup(t,__FILE__,__LINE__);
1732			if( (appendline = (Lastchar(keyid) == '\\')) ){
1733				keyid[safestrlen(keyid)-1] = 0;
1734			}
1735		}
1736	}
1737	if( keyid ){
1738		if( Build_pc_names( names, order, keyid, hostname ) ){
1739			Add_line_list( list, keyid, Printcap_sep, 1, 0 );
1740		}
1741		free(keyid); keyid = 0;
1742	}
1743#ifdef ORIGINAL_DEBUG//JY@1020
1744	if(DEBUGL4) Dump_line_list( "Build_printcap_info- end", list );
1745#endif
1746	return;
1747}
1748
1749/*
1750 * char *Select_pc_info(
1751 *   - returns the main name of the print queue
1752 * struct line_list *aliases  = list of names and aliases
1753 * struct line_list *info     = printcap infor
1754 * struct line_list *names    = entry names in the input list
1755 *                              alias=mainname
1756 * struct line_list *input    = printcap entries, starting with mainname
1757 *
1758 *  Select the printcap information and put it in the info list.
1759 *  Return the main name;
1760 */
1761
1762char *Select_pc_info( const char *id,
1763	struct line_list *info,
1764	struct line_list *aliases,
1765	struct line_list *names,
1766	struct line_list *order,
1767	struct line_list *input,
1768	int depth, int wildcard )
1769{
1770	int start, end, i, c;
1771	char *s, *t, *found = 0, *allglob = 0;
1772	struct line_list l;
1773
1774	Init_line_list(&l);
1775	DEBUG1("Select_pc_info: looking for '%s', depth %d", id, depth );
1776	if( depth > 5 ){
1777		Errorcode = JABORT;
1778		FATAL(LOG_ERR)"Select_pc_info: printcap tc recursion depth %d", depth );
1779	}
1780#ifdef ORIGINAL_DEBUG//JY@1020
1781	if(DEBUGL4)Dump_line_list("Select_pc_info- names", names );
1782	if(DEBUGL4)Dump_line_list("Select_pc_info- order", order );
1783	if(DEBUGL4)Dump_line_list("Select_pc_info- input", input );
1784#endif
1785	start = 0; end = 0;
1786	found = Find_str_value( names, id, Value_sep );
1787	if( !found && PC_filters_line_list.count ){
1788		Filterprintcap( &l, &PC_filters_line_list, id);
1789		Build_printcap_info( names, order, input, &l, &Host_IP );
1790		Free_line_list( &l );
1791#ifdef ORIGINAL_DEBUG//JY@1020
1792		if(DEBUGL4)Dump_line_list("Select_pc_info- after filter aliases", aliases );
1793		if(DEBUGL4)Dump_line_list("Select_pc_info- after filter info", info );
1794		if(DEBUGL4)Dump_line_list("Select_pc_info- after filter names", names );
1795		if(DEBUGL4)Dump_line_list("Select_pc_info- after filter input", input );
1796#endif
1797		found = Find_str_value( names, id, Value_sep );
1798	}
1799	/* do partial glob match  */
1800	c = 0;
1801	for( i = 0; !found && i < names->count; ++i ){
1802		s = names->list[i];
1803		if( (t = safestrpbrk(s, Value_sep)) ){
1804			c = *t; *t = 0;
1805			DEBUG1("Select_pc_info: wildcard trying '%s'", s );
1806			if( !safestrcmp(s, id ) ){
1807				found = t+1;
1808			}
1809			*t = c;
1810		}
1811	}
1812	if( !found && wildcard ){
1813		c = 0;
1814		for( i = 0; !found && i < names->count; ++i ){
1815			s = names->list[i];
1816			if( (t = safestrpbrk(s, Value_sep)) ){
1817				c = *t; *t = 0;
1818				DEBUG1("Select_pc_info: wildcard trying '%s'", s );
1819				if( !strcmp(s,"*") ){
1820					if( ISNULL(allglob) ){
1821						allglob = t+1;
1822					}
1823				} else if( !Globmatch( s, id ) ){
1824					found = t+1;
1825				}
1826				*t = c;
1827			}
1828		}
1829	}
1830	if( !found ){
1831		found = allglob;
1832	}
1833	if( found ){
1834		Find_pc_info( found, info, aliases, names, order, input, depth, 0 );
1835	}
1836	DEBUG1("Select_pc_info: returning '%s'", found );
1837#ifdef ORIGINAL_DEBUG//JY@1020
1838	if(DEBUGL4)Dump_line_list("Select_pc_info- returning aliases", aliases );
1839	if(DEBUGL4)Dump_line_list("Select_pc_info- returning info", info );
1840#endif
1841	return( found );
1842}
1843
1844void Find_pc_info( char *name,
1845	struct line_list *info,
1846	struct line_list *aliases,
1847	struct line_list *names,
1848	struct line_list *order,
1849	struct line_list *input,
1850	int depth, int wildcard )
1851{
1852	int start, end, i, j, c, start_tc, end_tc;
1853	char *s, *t, *u;
1854	struct line_list l, pc, tc;
1855
1856	Init_line_list(&l); Init_line_list(&pc); Init_line_list(&tc);
1857
1858	DEBUG1("Find_pc_info: found name '%s'", name );
1859	if( Find_first_key(input,name,Printcap_sep,&start)
1860		|| Find_last_key(input,name,Printcap_sep,&end) ){
1861		Errorcode = JABORT;
1862		FATAL(LOG_ERR)
1863			"Find_pc_info: name '%s' in names and not in input list",
1864			name );
1865	}
1866	DEBUG4("Find_pc_info: name '%s', start %d, end %d",
1867		name, start, end );
1868	for(; start <= end; ++start ){
1869		u = input->list[start];
1870		DEBUG4("Find_pc_info: line [%d]='%s'", start, u );
1871		if( u && *u ){
1872			Add_line_list( &pc, u, 0, 0, 0 );
1873		}
1874	}
1875#ifdef ORIGINAL_DEBUG//JY@1020
1876	if(DEBUGL4)Dump_line_list("Find_pc_info- entry lines", &l );
1877#endif
1878	for( start = 0; start < pc.count; ++ start ){
1879		u = pc.list[start];
1880		c = 0;
1881		if( (t = safestrpbrk(u,":")) ){
1882			Split(&l, t+1, ":", 1, Value_sep, 0, 1, 0,":");
1883		}
1884		if( aliases ){
1885			if( t ){
1886				c = *t; *t = 0;
1887				Split(aliases, u, Printcap_sep, 0, 0, 0, 0, 0,0);
1888				Remove_duplicates_line_list(aliases);
1889				*t = c;
1890			} else {
1891				Split(aliases, u, Printcap_sep, 0, 0, 0, 0, 0,0);
1892				Remove_duplicates_line_list(aliases);
1893			}
1894		}
1895		/* get the tc entries */
1896#ifdef ORIGINAL_DEBUG//JY@1020
1897		if(DEBUGL4)Dump_line_list("Find_pc_info- pc entry", &l );
1898#endif
1899		if( !Find_first_key(&l,"tc",Value_sep,&start_tc)
1900			&& !Find_last_key(&l,"tc",Value_sep,&end_tc) ){
1901			for( ; start_tc <= end_tc; ++start_tc ){
1902				if( (s = l.list[start_tc]) ){
1903					lowercase(s);
1904					DEBUG4("Find_pc_info: tc '%s'", s );
1905					if( (t = safestrchr( s, '=' )) ){
1906						Split(&tc,t+1,File_sep,0,0,0,1,0,0);
1907					}
1908					free( l.list[start_tc] );
1909					l.list[start_tc] = 0;
1910				}
1911			}
1912		}
1913#ifdef ORIGINAL_DEBUG//JY@1020
1914		if(DEBUGL4)Dump_line_list("Find_pc_info- tc", &tc );
1915#endif
1916		for( j = 0; j < tc.count; ++j ){
1917			s = tc.list[j];
1918			DEBUG4("Find_pc_info: tc entry '%s'", s );
1919			if( !Select_pc_info( s, info, 0, names, order, input, depth+1, wildcard ) ){
1920				FATAL(LOG_ERR)
1921				"Find_pc_info: tc entry '%s' not found", s);
1922			}
1923		}
1924		Free_line_list(&tc);
1925#ifdef ORIGINAL_DEBUG//JY@1020
1926		if(DEBUGL4)Dump_line_list("Find_pc_info - adding", &l );
1927#endif
1928		for( i = 0; i < l.count; ++i ){
1929			if( (t = l.list[i]) ){
1930				Add_line_list( info, t, Value_sep, 1, 1 );
1931			}
1932		}
1933		Free_line_list(&l);
1934	}
1935	Free_line_list(&pc);
1936}
1937#endif
1938
1939/*
1940 * variable lists and initialization
1941 */
1942/***************************************************************************
1943 * Clear_var_list( struct pc_var_list *vars );
1944 *   Set the printcap variable value to 0 or null;
1945 ***************************************************************************/
1946
1947void Clear_var_list( struct keywords *v, int setv )
1948{
1949	char *s;
1950	void *p;
1951	struct keywords *vars;
1952    for( vars = v; vars->keyword; ++vars ){
1953		if( !(p = vars->variable) ) continue;
1954        switch( vars->type ){
1955            case STRING_K:
1956				s = ((char **)p)[0];
1957				if(s)free(s);
1958				((char **)p)[0] = 0;
1959				break;
1960            case INTEGER_K:
1961            case FLAG_K: ((int *)p)[0] = 0; break;
1962            default: break;
1963        }
1964		if( setv && vars->default_value ){
1965			Config_value_conversion( vars, vars->default_value );
1966		}
1967    }
1968#ifdef ORIGINAL_DEBUG//JY@1020
1969	if(DEBUGL5)Dump_parms("Clear_var_list: after",v );
1970#endif
1971}
1972
1973/***************************************************************************
1974 * Set_var_list( struct keywords *vars, struct line_list *values );
1975 *  for each name in  keywords
1976 *    find entry in values
1977 ***************************************************************************/
1978
1979void Set_var_list( struct keywords *keys, struct line_list *values )
1980{
1981	struct keywords *vars;
1982	const char *s;
1983
1984	for( vars = keys; vars->keyword; ++vars ){
1985		if( (s = Find_exists_value( values, vars->keyword, Value_sep )) ){
1986			Config_value_conversion( vars, s );
1987		}
1988	}
1989}
1990
1991
1992/***************************************************************************
1993 * int Check_str_keyword( char *name, int *value )
1994 * - check a string for a simple keyword name
1995 ***************************************************************************/
1996
1997#define FIXV(S,V) { S, N_(S), INTEGER_K, (void *)0, V, 0,0 }
1998 static struct keywords simple_words[] = {
1999 FIXV( "all", 1 ), FIXV( "yes", 1 ), FIXV( "allow", 1 ), FIXV( "true", 1 ),
2000 FIXV( "no", 0 ), FIXV( "deny", 0 ), FIXV( "false", 0 ),
2001 FIXV( "none", 0 ),
2002{0,0,0,0,0,0,0}
2003 };
2004
2005int Check_str_keyword( const char *name, int *value )
2006{
2007	struct keywords *keys;
2008	for( keys = simple_words; keys->keyword; ++keys ){
2009		if( !safestrcasecmp( name, keys->keyword ) ){
2010			*value = keys->maxval;
2011			return( 1 );
2012		}
2013	}
2014	return( 0 );
2015}
2016
2017/***************************************************************************
2018 * void Config_value_conversion( struct keyword *key, char *value )
2019 *  set the value of the variable as required
2020 ***************************************************************************/
2021#if defined(JYWENG20031104Config_value_conversion)
2022void Config_value_conversion( struct keywords *key, const char *s )
2023{
2024	int i = 0, c = 0, value = 0;
2025	char *end;		/* end of conversion */
2026	void *p;
2027
2028	DEBUG5("Config_value_conversion: '%s'='%s'", key->keyword, s );
2029	if( !(p = key->variable) ) return;
2030	while(s && isspace(cval(s)) ) ++s;
2031	/*
2032	 * we have null str "", "@", "#val", or "=val"
2033	 * FLAG              1   0     val!=0     val!=0
2034     * INT               1   0     val        val
2035	 */
2036	switch( key->type ){
2037	case FLAG_K:
2038	case INTEGER_K:
2039		DEBUG5("Config_value_conversion: int '%s'", s );
2040		i = 1;
2041		if( s && (c=cval(s)) ){
2042			if( c == '@' ){
2043				i = 0;
2044			} else {
2045				/* get rid of leading junk */
2046				while( safestrchr(Value_sep,c) ){
2047					++s;
2048					c = cval(s);
2049				}
2050				if( Check_str_keyword( s, &value ) ){
2051					i = value;
2052				} else {
2053					end = 0;
2054					i = strtol( s, &end, 0 );
2055					if( end == 0 ){
2056						i = 1;
2057					}
2058				}
2059			}
2060		}
2061		((int *)p)[0] = i;
2062		DEBUG5("Config_value_conversion: setting '%d'", i );
2063		break;
2064	case STRING_K:
2065		end = ((char **)p)[0];
2066		DEBUG5("Config_value_conversion:  current value '%s'", end );
2067		if( end ) free( end );
2068		((char **)p)[0] = 0;
2069		while(s && (c=cval(s)) && safestrchr(Value_sep,c) ) ++s;
2070		end = 0;
2071		if( s && *s ){
2072			end = safestrdup(s,__FILE__,__LINE__);
2073			trunc_str(end);
2074		}
2075		((char **)p)[0] = end;
2076		DEBUG5("Config_value_conversion: setting '%s'", end );
2077		break;
2078	default:
2079		break;
2080	}
2081}
2082#endif
2083
2084 static struct keywords Keyletter[] = {
2085	{ "P", 0, STRING_K, &Printer_DYN, 0,0,0 },
2086	{ "Q", 0, STRING_K, &Queue_name_DYN, 0,0,0 },
2087	{ "h", 0, STRING_K, &ShortHost_FQDN, 0,0,0 },
2088	{ "H", 0, STRING_K, &FQDNHost_FQDN, 0,0,0 },
2089	{ "a", 0, STRING_K, &Architecture_DYN, 0,0,0 },
2090	{ "R", 0, STRING_K, &RemotePrinter_DYN, 0,0,0 },
2091	{ "M", 0, STRING_K, &RemoteHost_DYN, 0,0,0 },
2092	{ "D", 0, STRING_K, &Current_date_DYN, 0,0,0 },
2093	{ 0,0,0,0,0,0,0 }
2094};
2095
2096void Expand_percent( char **var )
2097{
2098	struct keywords *key;
2099	char *str, *s, *t, *u, **v = var;
2100	int c, len;
2101
2102	if( v == 0 || (str = *v) == 0 || !safestrpbrk(str,"%") ){
2103		return;
2104	}
2105	DEBUG4("Expand_percent: starting '%s'", str );
2106	if( Current_date_DYN == 0 ){
2107		Set_DYN(&Current_date_DYN, Time_str(0,0) );
2108		if( (s = safestrrchr(Current_date_DYN,'-')) ){
2109			*s = 0;
2110		}
2111	}
2112	s = str;
2113	while( (s = safestrpbrk(s,"%")) ){
2114		t = 0;
2115		if( (c = cval(s+1)) && isalpha( c ) ){
2116			for( key = Keyletter; t == 0 && key->keyword; ++key ){
2117				if( (c == key->keyword[0]) ){
2118					t = *(char **)key->variable;
2119					break;
2120				}
2121			}
2122		}
2123		if( t ){
2124			*s = 0;
2125			s += 2;
2126			len = safestrlen(str) + safestrlen(t);
2127			u = str;
2128			str = safestrdup3(str,t,s,__FILE__,__LINE__);
2129			if(u) free(u); u = 0;
2130			s = str+len;
2131		} else {
2132			++s;
2133		}
2134	}
2135	*v = str;
2136	DEBUG4("Expand_percent: ending '%s'", str );
2137}
2138
2139/***************************************************************************
2140 * Expand_vars:
2141 *  expand the values of a selected list of strings
2142 *  These should be from _DYN
2143 ***************************************************************************/
2144void Expand_vars( void )
2145{
2146	void *p;
2147	struct keywords *var;
2148
2149#ifdef REMOVE
2150	/* check to see if you need to expand */
2151	for( var = Pc_var_list; var->keyword; ++var ){
2152		if( var->type == STRING_K && (p = var->variable) ){
2153			Expand_percent(p);
2154		}
2155	}
2156#endif
2157}
2158
2159
2160#ifdef ORIGINAL_DEBUG//JY@1020
2161/***************************************************************************
2162 * Expand_hash_values:
2163 *  expand the values of a hash
2164 ***************************************************************************/
2165void Expand_hash_values( struct line_list *hash )
2166{
2167	char *u, *s;
2168	int i;
2169
2170	/* check to see if you need to expand */
2171	for( i = 0; i < hash->count; ++i ){
2172		s = hash->list[i];
2173		if( safestrchr( s, '%' ) ){
2174			u = safestrdup(s,__FILE__,__LINE__);
2175			Expand_percent( &u );
2176			if( s ) free(s); s = 0;
2177			hash->list[i] = u;
2178		}
2179	}
2180}
2181#endif
2182
2183/*
2184 * Set a _DYN variable
2185 */
2186
2187char *Set_DYN( char **v, const char *s )
2188{
2189	char *t = *v;
2190	*v = 0;
2191	if( s && *s ) *v = safestrdup(s,__FILE__,__LINE__);
2192	if( t ) free(t);
2193	return( *v );
2194}
2195
2196/*
2197 * Clear the total configuration information
2198 *  - we simply remove all dynmically allocated values
2199 */
2200void Clear_config( void )
2201{
2202	struct line_list **l;
2203
2204#ifdef REMOVE
2205	DEBUGF(DDB1)("Clear_config: freeing everything");
2206	Remove_tempfiles();
2207	Clear_tempfile_list();
2208    Clear_var_list( Pc_var_list, 1 );
2209    Clear_var_list( DYN_var_list, 1 );
2210	for( l = Allocs; *l; ++l ) Free_line_list(*l);
2211#endif
2212}
2213
2214char *Find_default_var_value( void *v )
2215{
2216#ifdef REMOVE
2217	struct keywords *k;
2218	char *s;
2219	for( k = Pc_var_list; (s = k->keyword); ++k ){
2220		if( k->type == STRING_K && k->variable == v ){
2221			s = k->default_value;
2222			if( s && cval(s) == '=' ) ++s;
2223			DEBUG1("Find_default_var_value: found 0x%lx key '%s' '%s'",
2224				(long)v, k->keyword, s );
2225			return( s );
2226		}
2227	}
2228#endif
2229	return(0);
2230}
2231
2232/***************************************************************************
2233 * void Get_config( char *names )
2234 *  gets the configuration information from a list of files
2235 ***************************************************************************/
2236
2237void Get_config( int required, char *path )
2238{
2239#ifdef REMOVE
2240	DEBUG1("Get_config: required '%d', '%s'", required, path );
2241	/* void Read_file_list( int required, struct line_list *model, char *str,
2242	 *  const char *linesep, int sort, const char *keysep, int uniq, int trim,
2243	 *  int marker, int doinclude, int nocomment, int depth, int maxdepth )
2244	 */
2245	Read_file_list( /*required*/required,
2246		/*model*/ &Config_line_list,/*str*/ path,
2247		/*linesep*/Line_ends, /*sort*/1, /*keysep*/Value_sep,/*uniq*/1,
2248		/*trim*/':',/*marker*/0,/*doinclude*/1,/*nocomment*/1,
2249		/*depth*/0,/*maxdepth*/4 );
2250
2251	Set_var_list( Pc_var_list, &Config_line_list);
2252	Get_local_host();
2253	Expand_vars();
2254#endif
2255}
2256
2257/***************************************************************************
2258 * void Reset_config( char *names )
2259 *  Resets the configuration and printcap information
2260 ***************************************************************************/
2261
2262void Reset_config( void )
2263{
2264#ifdef REMOVE
2265	DEBUG1("Reset_config: starting");
2266	Clear_var_list( Pc_var_list, 1 );
2267	Free_line_list( &PC_entry_line_list );
2268	Free_line_list( &PC_alias_line_list );
2269	Set_var_list( Pc_var_list, &Config_line_list);
2270	Expand_vars();
2271#endif
2272}
2273
2274void close_on_exec( int fd )
2275{
2276    for( ;fd <= Max_fd+10; fd++ ){
2277        (void) close( fd);
2278    }
2279}
2280
2281void Setup_env_for_process( struct line_list *env, struct job *job )
2282{
2283	struct line_list env_names;
2284	struct passwd *pw;
2285	char *s, *t, *u, *name;
2286	int i;
2287
2288#ifdef JYDEBUG//JYWeng
2289aaaaaa=fopen("/tmp/qqqqq", "a");
2290fprintf(aaaaaa, "Setup_env_for_process: check point 1\n");
2291fclose(aaaaaa);
2292#endif
2293	Init_line_list(&env_names);
2294#ifdef JYDEBUG//JYWeng
2295aaaaaa=fopen("/tmp/qqqqq", "a");
2296fprintf(aaaaaa, "Setup_env_for_process: check point 1.1\n");
2297fclose(aaaaaa);
2298#endif
2299
2300#if 1
2301{
2302char a1[8], a2[8], a3[8], a4[8], a5[16];
2303int b1=0, b2=0;
2304strcpy(a1, "root");
2305strcpy(a2, "x");
2306strcpy(a3, "root");
2307strcpy(a4, "/root");
2308strcpy(a5, "/bin/bash");
2309pw->pw_name=a1;
2310pw->pw_passwd=a2;
2311pw->pw_gecos=a3;
2312pw->pw_dir=a4;
2313pw->pw_shell=a5;
2314pw->pw_uid=b1;
2315pw->pw_gid=b2;
2316}
2317#else
2318	if( (pw = getpwuid( getuid())) == 0 ){
2319		LOGERR_DIE(LOG_INFO) "setup_envp: getpwuid(%d) failed", getuid());
2320	}
2321#endif
2322
2323#ifdef JYDEBUG//JYWeng
2324aaaaaa=fopen("/tmp/qqqqq", "a");
2325fprintf(aaaaaa, "Setup_env_for_process: check point 2\n");
2326fclose(aaaaaa);
2327#endif
2328	Set_str_value(env,"PRINTER",Printer_DYN);
2329	Set_str_value(env,"USER",pw->pw_name);
2330	Set_str_value(env,"LOGNAME",pw->pw_name);
2331	Set_str_value(env,"HOME",pw->pw_dir);
2332	Set_str_value(env,"LOGDIR",pw->pw_dir);
2333	Set_str_value(env,"PATH",Filter_path_DYN);
2334	Set_str_value(env,"LD_LIBRARY_PATH",Filter_ld_path_DYN);
2335	Set_str_value(env,"SHELL",Shell_DYN);
2336	Set_str_value(env,"IFS"," \t");
2337
2338	s = getenv( "TZ" );  Set_str_value(env,"TZ",s);
2339	Set_str_value(env,"SPOOL_DIR", Spool_dir_DYN );
2340	if( PC_entry_line_list.count ){
2341		t = Join_line_list_with_sep(&PC_alias_line_list,"|");
2342		s = Join_line_list_with_sep(&PC_entry_line_list,"\n :");
2343		u = safestrdup4(t,(s?"\n :":0),s,"\n",__FILE__,__LINE__);
2344		Expand_percent( &u );
2345		Set_str_value(env, "PRINTCAP_ENTRY",u);
2346		if(s) free(s); s = 0;
2347		if(t) free(t); t = 0;
2348		if(u) free(u); u = 0;
2349	}
2350#ifdef JYDEBUG//JYWeng
2351aaaaaa=fopen("/tmp/qqqqq", "a");
2352fprintf(aaaaaa, "Setup_env_for_process: check point 3\n");
2353fclose(aaaaaa);
2354#endif
2355	if( job ){
2356		if( !(s = Find_str_value(&job->info,CF_OUT_IMAGE,Value_sep)) ){
2357			s = Find_str_value(&job->info,OPENNAME,Value_sep);
2358			if( !s ) s = Find_str_value(&job->info,TRANSFERNAME,Value_sep);
2359			s = Get_file_image( s, 0 );
2360			Set_str_value(&job->info, CF_OUT_IMAGE, s );
2361			if( s ) free(s); s = 0;
2362			s = Find_str_value(&job->info,CF_OUT_IMAGE,Value_sep);
2363		}
2364		Set_str_value(env, "CONTROL", s );
2365	}
2366
2367#ifdef JYDEBUG//JYWeng
2368aaaaaa=fopen("/tmp/qqqqq", "a");
2369fprintf(aaaaaa, "Setup_env_for_process: check point 4\n");
2370fclose(aaaaaa);
2371#endif
2372	if( Pass_env_DYN ){
2373		Free_line_list(&env_names);
2374		Split(&env_names,Pass_env_DYN,File_sep,1,Value_sep,1,1,0,0);
2375		for( i = 0; i < env_names.count; ++i ){
2376			name = env_names.list[i];
2377			if( (s = getenv( name )) ){
2378				Set_str_value( env, name, s);
2379			}
2380		}
2381	}
2382#ifdef JYDEBUG//JYWeng
2383aaaaaa=fopen("/tmp/qqqqq", "a");
2384fprintf(aaaaaa, "Setup_env_for_process: check point 5\n");
2385fclose(aaaaaa);
2386#endif
2387	Free_line_list(&env_names);
2388	Check_max(env,1);
2389	env->list[env->count] = 0;
2390#ifdef ORIGINAL_DEBUG//JY@1020
2391	if(DEBUGL1)Dump_line_list("Setup_env_for_process", env );
2392#endif
2393}
2394
2395/***************************************************************************
2396 * void Getprintcap_pathlist( char *path )
2397 * Read printcap information from a (semi)colon or comma separated set of files
2398 *   or filter specifications
2399 *   1. break path up into set of path names
2400 *   2. read the printcap information into memory
2401 *   3. parse the printcap informormation
2402 ***************************************************************************/
2403
2404void Getprintcap_pathlist( int required,
2405	struct line_list *raw, struct line_list *filters,
2406	char *path )
2407{
2408	struct line_list l;
2409	int i, c;
2410
2411	Init_line_list(&l);
2412	DEBUG2("Getprintcap_pathlist: processing '%s'", path );
2413	Split(&l,path,Strict_file_sep,0,0,0,1,0,0);
2414	for( i = 0; i < l.count; ++i ){
2415		path = l.list[i];
2416		c = path[0];
2417		switch( c ){
2418		case '|':
2419			DEBUG2("Getprintcap_pathlist: filter '%s'", path );
2420			if( filters ) Add_line_list( filters, path, 0, 0, 0 );
2421			break;
2422		case '/':
2423			DEBUG2("Getprintcap_pathlist: file '%s'", path );
2424			/*
2425			void Read_file_list( int required, struct line_list *model, char *str,
2426				const char *linesep, int sort, const char *keysep, int uniq, int trim,
2427				int marker, int doinclude, int nocomment, int depth, int maxdepth )
2428			*/
2429			Read_file_list(/*required*/required,/*model*/raw,/*str*/path,
2430				/*linesep*/Line_ends,/*sort*/0,/*keysep*/0,/*uniq*/0,/*trim*/1,
2431				/*marker*/0,/*doinclude*/1,/*nocomment*/1,/*depth*/0,/*maxdepth*/4);
2432			break;
2433		default:
2434			FATAL(LOG_ERR)
2435				"Getprintcap_pathlist: entry not filter or absolute pathname '%s'",
2436				path );
2437		}
2438	}
2439	Free_line_list(&l);
2440
2441#ifdef ORIGINAL_DEBUG//JY@1020
2442	if(DEBUGL4){
2443		Dump_line_list( "Getprintcap_pathlist - filters", filters  );
2444		Dump_line_list( "Getprintcap_pathlist - info", raw  );
2445	}
2446#endif
2447}
2448
2449/***************************************************************************
2450 * int Filterprintcap( struct line_list *raw, *filters, char *str )
2451 *  - for each entry in the filters list do the following:
2452 *    - make the filter, sending it the 'name' for access
2453 *    - read from the filter until EOF, adding it to the raw list
2454 *    - kill off the filter process
2455 ***************************************************************************/
2456
2457#if defined(JYWENG20031104Filterprintcap)
2458void Filterprintcap( struct line_list *raw, struct line_list *filters,
2459	const char *str )
2460{
2461	int count, n, intempfd, outtempfd;
2462	char *filter;
2463
2464	if( filters->count > 0 ){
2465		intempfd = Make_temp_fd( 0 );
2466		outtempfd = Make_temp_fd( 0 );
2467		if( Write_fd_str( intempfd, str) < 0
2468			|| Write_fd_str( intempfd,"\n") < 0 ){
2469			Errorcode = JABORT;
2470			LOGERR_DIE(LOG_ERR) "Filterprintcap: Write_fd_str failed");
2471		}
2472		for( count = 0; count < filters->count; ++count ){
2473			filter = filters->list[count];
2474			DEBUG2("Filterprintcap: filter '%s'", filter );
2475			if( lseek(intempfd,0,SEEK_SET) == -1 ){
2476				Errorcode = JABORT;
2477				LOGERR_DIE(LOG_ERR) "Filterprintcap: lseek intempfd failed");
2478			}
2479			n = Filter_file(intempfd, outtempfd, "PC_FILTER",
2480				filter, Filter_options_DYN, 0,
2481				0, 0 );
2482			if( n ){
2483				Errorcode = JABORT;
2484				LOGERR_DIE(LOG_ERR) "Filterprintcap: filter '%s' failed", filter);
2485			}
2486		}
2487		if( lseek(outtempfd,0,SEEK_SET) == -1 ){
2488			Errorcode = JABORT;
2489			LOGERR_DIE(LOG_ERR) "Filterprintcap: lseek outtempfd failed");
2490		}
2491		Read_fd_and_split( raw,outtempfd,Line_ends,0,0,0,1,1);
2492		/* do not worry if these fail */
2493		close( intempfd); intempfd = -1;
2494		close( outtempfd); outtempfd = -1;
2495	}
2496}
2497#endif
2498
2499#ifdef ORIGINAL_DEBUG//JY@1020
2500/***************************************************************************
2501 * int In_group( char* *group, char *user );
2502 *  returns 1 on failure, 0 on success
2503 *  scan group for user name
2504 * Note: we first check for the group.  If there is none, we check for
2505 *  wildcard (*) in group name, and then scan only if we need to
2506 ***************************************************************************/
2507
2508int In_group( char *group, char *user )
2509{
2510	struct group *grent;
2511	struct passwd *pwent;
2512	char **members;
2513	int result = 1;
2514
2515	DEBUGF(DDB3)("In_group: checking '%s' for membership in group '%s'", user, group);
2516	if( group == 0 || user == 0 ){
2517		return( result );
2518	}
2519	/* first try getgrnam, see if it is a group */
2520	pwent = getpwnam(user);
2521	if( (grent = getgrnam( group )) ){
2522		DEBUGF(DDB3)("In_group: group id: %d\n", grent->gr_gid);
2523		if( pwent && ((int)pwent->pw_gid == (int)grent->gr_gid) ){
2524			DEBUGF(DDB3)("In_group: user default group id: %d\n", pwent->pw_gid);
2525			result = 0;
2526		} else for( members = grent->gr_mem; result && *members; ++members ){
2527			DEBUGF(DDB3)("In_group: member '%s'", *members);
2528			result = (safestrcmp( user, *members ) != 0);
2529		}
2530	}
2531	if( result && safestrchr( group, '*') ){
2532		/* wildcard in group name, scan through all groups */
2533		setgrent();
2534		while( result && (grent = getgrent()) ){
2535			DEBUGF(DDB3)("In_group: group name '%s'", grent->gr_name);
2536			/* now do match against group */
2537			if( Globmatch( group, grent->gr_name ) == 0 ){
2538				if( pwent && ((int)pwent->pw_gid == (int)grent->gr_gid) ){
2539					DEBUGF(DDB3)("In_group: user default group id: %d\n",
2540					pwent->pw_gid);
2541					result = 0;
2542				} else {
2543					DEBUGF(DDB3)("In_group: found '%s'", grent->gr_name);
2544					for( members = grent->gr_mem; result && *members; ++members ){
2545						DEBUGF(DDB3)("In_group: member '%s'", *members);
2546						result = (safestrcmp( user, *members ) != 0);
2547					}
2548				}
2549			}
2550		}
2551		endgrent();
2552	}
2553	if( result && group[0] == '@' ) {	/* look up user in netgroup */
2554#ifdef HAVE_INNETGR
2555		if( !innetgr( group+1, NULL, user, NULL ) ) {
2556			DEBUGF(DDB3)( "In_group: user %s NOT in netgroup %s", user, group+1 );
2557		} else {
2558			DEBUGF(DDB3)( "In_group: user %s in netgroup %s", user, group+1 );
2559			result = 0;
2560		}
2561#else
2562		DEBUGF(DDB3)( "In_group: no innetgr() call, netgroups not permitted" );
2563#endif
2564	}
2565	DEBUGF(DDB3)("In_group: result: %d", result );
2566	return( result );
2567}
2568
2569int Check_for_rg_group( char *user )
2570{
2571	int i, match = 0;
2572	struct line_list l;
2573	char *s;
2574
2575	Init_line_list(&l);
2576
2577	s = RestrictToGroupMembers_DYN;
2578	DEBUG3("Check_for_rg_group: name '%s', restricted_group '%s'",
2579		user, s );
2580	if( s ){
2581		match = 1;
2582		Split(&l,s,List_sep,0,0,0,0,0,0);
2583		for( i = 0; match && i < l.count; ++i ){
2584			s = l.list[i];
2585			match = In_group( s, user );
2586		}
2587	}
2588	Free_line_list(&l);
2589	DEBUG3("Check_for_rg_group: result: %d", match );
2590	return( match );
2591}
2592#endif
2593
2594
2595/***************************************************************************
2596 * Make_temp_fd( char *name, int namelen )
2597 * 1. we can call this repeatedly,  and it will make
2598 *    different temporary files.
2599 * 2. we NEVER modify the temporary file name - up to the first '.'
2600 *    is the base - we keep adding suffixes as needed.
2601 * 3. Remove_files uses the tempfile information to find and delete temp
2602 *    files so be careful.
2603 ***************************************************************************/
2604
2605
2606char *Init_tempfile( void )
2607{
2608	char *dir = 0, *s;
2609	struct stat statb;
2610
2611	if( Is_server ){
2612		if( dir == 0 )  dir = Spool_dir_DYN;
2613		if( dir == 0 )  dir = Server_tmp_dir_DYN;
2614	} else {
2615		dir = getenv( "LPR_TMP" );
2616		if( dir == 0 ) dir = Default_tmp_dir_DYN;
2617	}
2618	/* remove trailing / */
2619	if( (s = safestrrchr(dir,'/')) && s[1] == 0 ) *s = 0;
2620	if( dir == 0 || stat( dir, &statb ) != 0
2621		|| !S_ISDIR(statb.st_mode) ){
2622		FATAL(LOG_ERR) "Init_tempfile: bad tempdir '%s'", dir );
2623	}
2624	DEBUG3("Init_tempfile: temp file '%s'", dir );
2625	return( dir );
2626}
2627
2628int Make_temp_fd_in_dir( char **temppath, char *dir )
2629{
2630	int tempfd;
2631	struct stat statb;
2632	char pathname[MAXPATHLEN];
2633
2634	SNPRINTF(pathname,sizeof(pathname))"%s/temp%02dXXXXXX",dir,Tempfiles.count );
2635	tempfd = mkstemp( pathname );
2636	if( tempfd == -1 ){
2637		Errorcode = JFAIL;
2638		FATAL(LOG_INFO)"Make_temp_fd_in_dir: cannot create tempfile '%s'", pathname );
2639	}
2640	Add_line_list(&Tempfiles,pathname,0,0,0);
2641	if( temppath ){
2642		*temppath = Tempfiles.list[Tempfiles.count-1];
2643	}
2644	if( fchmod(tempfd,(Is_server?Spool_file_perms_DYN:0) | 0600 ) == -1 ){
2645		Errorcode = JFAIL;
2646		LOGERR_DIE(LOG_INFO)"Make_temp_fd_in_dir: chmod '%s' to 0%o failed ",
2647			pathname, Spool_file_perms_DYN );
2648	}
2649	if( stat(pathname,&statb) == -1 ){
2650		Errorcode = JFAIL;
2651		LOGERR_DIE(LOG_INFO)"Make_temp_fd_in_dir: stat '%s' failed ", pathname );
2652	}
2653	DEBUG1("Make_temp_fd_in_dir: fd %d, name '%s'", tempfd, pathname );
2654	return( tempfd );
2655}
2656
2657int Make_temp_fd( char **temppath )
2658{
2659	return( Make_temp_fd_in_dir( temppath, Init_tempfile()) );
2660}
2661
2662/***************************************************************************
2663 * Clear_tempfile_list()
2664 *  - clear the list of tempfiles created for this job
2665 *  - this is done by a child process
2666 ***************************************************************************/
2667void Clear_tempfile_list(void)
2668{
2669	Free_line_list(&Tempfiles);
2670}
2671
2672/***************************************************************************
2673 * Unlink_tempfiles()
2674 *  - remove the tempfiles created for this job
2675 ***************************************************************************/
2676
2677void Unlink_tempfiles(void)
2678{
2679	int i;
2680	for( i = 0; i < Tempfiles.count; ++i ){
2681		DEBUG4("Unlink_tempfiles: unlinking '%s'", Tempfiles.list[i] );
2682		unlink(Tempfiles.list[i]);
2683	}
2684	Free_line_list(&Tempfiles);
2685}
2686
2687
2688/***************************************************************************
2689 * Remove_tempfiles()
2690 *  - remove the tempfiles created for this job
2691 ***************************************************************************/
2692
2693void Remove_tempfiles(void)
2694{
2695	Unlink_tempfiles();
2696}
2697
2698/***************************************************************************
2699 * Split_cmd_line
2700 *   if we have xx "yy zz" we split this as
2701 *  xx
2702 *  yy zz
2703 ***************************************************************************/
2704
2705void Split_cmd_line( struct line_list *l, char *line )
2706{
2707	char *s = line, *t;
2708	int c;
2709
2710	DEBUG1("Split_cmd_line: line '%s'", line );
2711	while( s && cval(s) ){
2712		while( strchr(Whitespace,cval(s)) ) ++s;
2713		/* ok, we have skipped the whitespace */
2714		if( (c = cval(s)) ){
2715			if( c == '"' || c == '\'' ){
2716				/* we now have hit a quoted string */
2717				++s;
2718				t = strchr(s,c);
2719			} else if( !(t = strpbrk(s, Whitespace)) ){
2720				t = s+safestrlen(s);
2721			}
2722			if( t ){
2723				c = cval(t);
2724				*t = 0;
2725				Add_line_list(l, s, 0, 0, 0);
2726				*t = c;
2727				if( c ) ++t;
2728			}
2729			s = t;
2730		}
2731	}
2732#ifdef ORIGINAL_DEBUG//JY@1020
2733	if(DEBUGL1){ Dump_line_list("Split_cmd_line", l ); }
2734#endif
2735}
2736
2737/***************************************************************************
2738 * Make_passthrough
2739 *
2740 * int Make_passthrough   - returns PID of process
2741 *  char *line            - command line
2742 *  char *flags,          - additional flags
2743 *  struct line_list *passfd, - file descriptors
2744 *  struct job *job       - job with for option expansion
2745 *  struct line_list *env_init  - environment
2746 ***************************************************************************/
2747
2748int Make_passthrough( char *line, char *flags, struct line_list *passfd,
2749	struct job *job, struct line_list *env_init )
2750{
2751	int c, i, pid = -1, noopts, root, newfd, fd;
2752	struct line_list cmd;
2753	struct line_list env;
2754	char error[SMALLBUFFER];
2755	char *s;
2756
2757#ifdef JYDEBUG//JYWeng
2758aaaaaa=fopen("/tmp/qqqqq", "a");
2759fprintf(aaaaaa, "Make_passthrough: check point 1\n");
2760fclose(aaaaaa);
2761#endif
2762
2763	DEBUG1("Make_passthrough: cmd '%s', flags '%s'", line, flags );
2764	if( job ){
2765		s = Find_str_value( &job->info,QUEUENAME, Value_sep );
2766		if( !ISNULL(s) ){
2767			Set_DYN(&Queue_name_DYN,s );
2768		}
2769	}
2770	Init_line_list(&env);
2771	if( env_init ){
2772		Merge_line_list(&env,env_init,Value_sep,1,1);
2773	}
2774	Init_line_list(&cmd);
2775
2776#ifdef JYDEBUG//JYWeng
2777aaaaaa=fopen("/tmp/qqqqq", "a");
2778fprintf(aaaaaa, "Make_passthrough: check point 2\n");
2779fclose(aaaaaa);
2780#endif
2781	while( isspace(cval(line)) ) ++line;
2782	if( cval(line) == '|' ) ++line;
2783	noopts = root = 0;
2784	while( cval(line) ){
2785		while( isspace(cval(line)) ) ++line;
2786		if( !safestrncmp(line,"$-", 2)
2787			||	!safestrncmp(line,"-$", 2) ){
2788			noopts = 1;
2789			line += 2;
2790		} else if( !safestrncasecmp(line,"root", 4) ){
2791			/* only set to root if it is the LPD server */
2792			root = Is_server;
2793			line += 4;
2794		} else {
2795			break;
2796		}
2797	}
2798
2799#ifdef JYDEBUG//JYWeng
2800aaaaaa=fopen("/tmp/qqqqq", "a");
2801fprintf(aaaaaa, "Make_passthrough: check point 3\n");
2802fclose(aaaaaa);
2803#endif
2804	c = cval(line);
2805#if defined(JYWENG20031104Fix_dollars)
2806	if( strpbrk(line, "<>|;") || c == '(' ){
2807		Add_line_list( &cmd, Shell_DYN, 0, 0, 0 );
2808		Add_line_list( &cmd, "-c", 0, 0, 0 );
2809		Add_line_list( &cmd, line, 0, 0, 0 );
2810		if( c != '(' ){
2811			s = cmd.list[cmd.count-1];
2812			s = safestrdup3("( ",s," )",__FILE__,__LINE__);
2813			if( cmd.list[cmd.count-1] ) free( cmd.list[cmd.count-1] );
2814			cmd.list[cmd.count-1] = s;
2815		}
2816		Fix_dollars(&cmd, job, 1, flags);
2817	} else {
2818		Split_cmd_line(&cmd, line);
2819		if( !noopts ){
2820			Split(&cmd, flags, Whitespace, 0,0, 0, 0, 0,0);
2821		}
2822		Fix_dollars(&cmd, job, 0, flags);
2823	}
2824#endif
2825
2826	Check_max(&cmd,1);
2827	cmd.list[cmd.count] = 0;
2828
2829	Setup_env_for_process(&env, job);
2830
2831	if(DEBUGL1){
2832#ifdef ORIGINAL_DEBUG//JY@1020
2833		Dump_line_list("Make_passthrough - cmd",&cmd );
2834		LOGDEBUG("Make_passthrough: fd count %d, root %d", passfd->count, root );
2835#endif
2836		for( i = 0 ; i < passfd->count; ++i ){
2837			fd = Cast_ptr_to_int(passfd->list[i]);
2838			LOGDEBUG("  [%d]=%d",i,fd);
2839		}
2840#ifdef ORIGINAL_DEBUG//JY@1020
2841		Dump_line_list("Make_passthrough - env",&env );
2842#endif
2843	}
2844#ifdef JYDEBUG//JYWeng
2845aaaaaa=fopen("/tmp/qqqqq", "a");
2846fprintf(aaaaaa, "Make_passthrough: check point 4.5\n");
2847fclose(aaaaaa);
2848#endif
2849
2850	c = cmd.list[0][0];
2851#ifdef JYDEBUG//JYWeng
2852aaaaaa=fopen("/tmp/qqqqq", "a");
2853fprintf(aaaaaa, "Make_passthrough: check point 5\n");
2854fclose(aaaaaa);
2855#endif
2856	if( c != '/' ){
2857#ifdef JYDEBUG//JYWeng
2858aaaaaa=fopen("/tmp/qqqqq", "a");
2859fprintf(aaaaaa, "Make_passthrough: check point 5.1\n");
2860fclose(aaaaaa);
2861#endif
2862		FATAL(LOG_ERR)"Make_passthrough: bad filter - not absolute path name'%s'",
2863			cmd.list[0] );
2864	}
2865#ifdef JYDEBUG//JYWeng
2866aaaaaa=fopen("/tmp/qqqqq", "a");
2867fprintf(aaaaaa, "Make_passthrough: check point 5.2\n");
2868fclose(aaaaaa);
2869#endif
2870	if( (pid = dofork(0)) == -1 ){
2871#ifdef JYDEBUG//JYWeng
2872aaaaaa=fopen("/tmp/qqqqq", "a");
2873fprintf(aaaaaa, "Make_passthrough: check point 5.3\n");
2874fclose(aaaaaa);
2875#endif
2876		LOGERR_DIE(LOG_ERR)"Make_passthrough: fork failed");
2877	} else if( pid == 0 ){
2878#ifdef JYDEBUG//JYWeng
2879aaaaaa=fopen("/tmp/qqqqq", "a");
2880fprintf(aaaaaa, "Make_passthrough: check point 5.4\n");
2881fclose(aaaaaa);
2882#endif
2883		for( i = 0; i < passfd->count; ++i ){
2884#ifdef JYDEBUG//JYWeng
2885aaaaaa=fopen("/tmp/qqqqq", "a");
2886fprintf(aaaaaa, "Make_passthrough: check point 5.5\n");
2887fclose(aaaaaa);
2888#endif
2889			fd = Cast_ptr_to_int(passfd->list[i]);
2890			if( fd < i  ){
2891#ifdef JYDEBUG//JYWeng
2892aaaaaa=fopen("/tmp/qqqqq", "a");
2893fprintf(aaaaaa, "Make_passthrough: check point 5.6\n");
2894fclose(aaaaaa);
2895#endif
2896				/* we have fd 3 -> 4, but 3 gets wiped out */
2897				do{
2898#ifdef JYDEBUG//JYWeng
2899aaaaaa=fopen("/tmp/qqqqq", "a");
2900fprintf(aaaaaa, "Make_passthrough: check point 5.7\n");
2901fclose(aaaaaa);
2902#endif
2903					newfd = dup(fd);
2904					Max_open(newfd);
2905					if( newfd < 0 ){
2906#ifdef JYDEBUG//JYWeng
2907aaaaaa=fopen("/tmp/qqqqq", "a");
2908fprintf(aaaaaa, "Make_passthrough: check point 5.8\n");
2909fclose(aaaaaa);
2910#endif
2911						Errorcode = JABORT;
2912						LOGERR_DIE(LOG_INFO)"Make_passthrough: dup failed");
2913					}
2914					DEBUG4("Make_passthrough: fd [%d] = %d, dup2 -> %d",
2915						i, fd, newfd );
2916					passfd->list[i] = Cast_int_to_voidstar(newfd);
2917				} while( newfd < i );
2918#ifdef JYDEBUG//JYWeng
2919aaaaaa=fopen("/tmp/qqqqq", "a");
2920fprintf(aaaaaa, "Make_passthrough: check point 5.9\n");
2921fclose(aaaaaa);
2922#endif
2923			}
2924		}
2925#ifdef JYDEBUG//JYWeng
2926aaaaaa=fopen("/tmp/qqqqq", "a");
2927fprintf(aaaaaa, "Make_passthrough: check point 6\n");
2928fclose(aaaaaa);
2929#endif
2930		if(DEBUGL4){
2931			LOGDEBUG("Make_passthrough: after fixing fd, count %d", passfd->count );
2932			for( i = 0 ; i < passfd->count; ++i ){
2933				fd = Cast_ptr_to_int(passfd->list[i]);
2934				LOGDEBUG("  [%d]=%d",i,fd);
2935			}
2936		}
2937		/* set up full perms for filter */
2938		if( Is_server ){
2939			if( root ){
2940				if( UID_root ) To_euid_root();
2941			} else {
2942				Full_daemon_perms();
2943			}
2944		} else {
2945			Full_user_perms();
2946		}
2947#ifdef JYDEBUG//JYWeng
2948aaaaaa=fopen("/tmp/qqqqq", "a");
2949fprintf(aaaaaa, "Make_passthrough: check point 7\n");
2950fclose(aaaaaa);
2951#endif
2952		for( i = 0; i < passfd->count; ++i ){
2953			fd = Cast_ptr_to_int(passfd->list[i]);
2954			if( dup2(fd,i) == -1 ){
2955				SNPRINTF(error,sizeof(error))
2956					"Make_passthrough: pid %d, dup2(%d,%d) failed", getpid(), fd, i );
2957				Write_fd_str(2,error);
2958				exit(JFAIL);
2959			}
2960		}
2961		close_on_exec(passfd->count);
2962		execve(cmd.list[0],cmd.list,env.list);
2963#ifdef ORIGINAL_DEBUG//JY@1020
2964		SNPRINTF(error,sizeof(error))
2965			"Make_passthrough: pid %d, execve '%s' failed - '%s'\n", getpid(),
2966			cmd.list[0], Errormsg(errno) );
2967#endif
2968		Write_fd_str(2,error);
2969		exit(JABORT);
2970	}
2971	passfd->count = 0;
2972	Free_line_list(passfd);
2973	Free_line_list(&env);
2974	Free_line_list(&cmd);
2975	return( pid );
2976}
2977
2978/*
2979 * Filter_file:  we filter a file through this program
2980 *  input_fd = input file descriptor.  if -1, then we make it /dev/null
2981 *  tempfile = name of tempfile for output
2982 *  error_header = header used for error messages from filter
2983 *  pgm      = program
2984 *  filter_options = options for filter
2985 *  job      = job we are doing the work for
2986 *  env      = environment options we want to pass
2987 * RETURN:
2988 *   exit status of the filter,  adjusted to be in the JXXX status
2989 *   if it exits with error status, we get JSIGNAL
2990 */
2991
2992int Filter_file( int input_fd, int output_fd, char *error_header,
2993	char *pgm, char * filter_options, struct job *job,
2994	struct line_list *env, int verbose )
2995{
2996	int innull_fd, outnull_fd, pid, len, n;
2997	char *s;
2998	int of_error[2];
2999    plp_status_t status;
3000	struct line_list files;
3001	char buffer[SMALLBUFFER];
3002
3003	Init_line_list( &files );
3004
3005	of_error[0] = of_error[1] = -1;
3006
3007	innull_fd = input_fd;
3008	if( innull_fd < 0 && (innull_fd = open("/dev/null", O_RDWR )) < 0 ){
3009		Errorcode = JFAIL;
3010		LOGERR_DIE(LOG_INFO)"Filter_file: open /dev/null failed");
3011	}
3012	Max_open(innull_fd);
3013
3014	outnull_fd = output_fd;
3015	if( outnull_fd < 0 && (outnull_fd = open("/dev/null", O_RDWR )) < 0 ){
3016		Errorcode = JFAIL;
3017		LOGERR_DIE(LOG_INFO)"Filter_file: open /dev/null failed");
3018	}
3019	Max_open(outnull_fd);
3020
3021	if( pipe( of_error ) == -1 ){
3022		Errorcode = JFAIL;
3023		LOGERR_DIE(LOG_INFO)"Filter_file: pipe() failed");
3024	}
3025	Max_open(of_error[0]); Max_open(of_error[1]);
3026	DEBUG3("Filter_file: fd of_error[%d,%d]", of_error[0], of_error[1] );
3027
3028	Check_max(&files, 10 );
3029	files.list[files.count++] = Cast_int_to_voidstar(innull_fd);	/* stdin */
3030	files.list[files.count++] = Cast_int_to_voidstar(outnull_fd);	/* stdout */
3031	files.list[files.count++] = Cast_int_to_voidstar(of_error[1]);	/* stderr */
3032	if( (pid = Make_passthrough( pgm, filter_options, &files, job, env )) < 0 ){
3033		Errorcode = JFAIL;
3034		LOGERR_DIE(LOG_INFO)"Filter_file: could not create process '%s'", pgm);
3035	}
3036	files.count = 0;
3037	Free_line_list(&files);
3038
3039	if( input_fd < 0 ) close(innull_fd); innull_fd = -1;
3040	if( output_fd < 0 ) close(outnull_fd); outnull_fd = -1;
3041	if( (close(of_error[1]) == -1 ) ){
3042		Errorcode = JFAIL;
3043		LOGERR_DIE(LOG_INFO)"Filter_file: X8 close(%d) failed",
3044			of_error[1]);
3045	}
3046	of_error[1] = -1;
3047	buffer[0] = 0;
3048	len = 0;
3049	while( len < (int)sizeof(buffer)-1
3050		&& (n = read(of_error[0],buffer+len,sizeof(buffer)-len-1)) >0 ){
3051		buffer[n+len] = 0;
3052		while( (s = safestrchr(buffer,'\n')) ){
3053			*s++ = 0;
3054#ifdef ORIGINAL_DEBUG//JY@1020
3055			SETSTATUS(job)"%s: %s", error_header, buffer );
3056#endif
3057			memmove(buffer,s,safestrlen(s)+1);
3058		}
3059		len = safestrlen(buffer);
3060	}
3061	if( buffer[0] ){
3062#ifdef ORIGINAL_DEBUG//JY@1020
3063		SETSTATUS(job)"%s: %s", error_header, buffer );
3064#endif
3065	}
3066	if( (close(of_error[0]) == -1 ) ){
3067		Errorcode = JFAIL;
3068		LOGERR_DIE(LOG_INFO)"Filter_file: X8 close(%d) failed",
3069			of_error[0]);
3070	}
3071	of_error[0] = -1;
3072	while( (n = plp_waitpid(pid,&status,0)) != pid ){
3073		int err = errno;
3074#ifdef ORIGINAL_DEBUG//JY@1020
3075		DEBUG1("Filter_file: waitpid(%d) returned %d, err '%s'",
3076			pid, n, Errormsg(err) );
3077#endif
3078		if( err == EINTR ) continue;
3079		Errorcode = JABORT;
3080		LOGERR_DIE(LOG_ERR) "Filter_file: waitpid(%d) failed", pid);
3081	}
3082	DEBUG1("Filter_file: pid %d, exit status '%s'", pid, Decode_status(&status) );
3083	n = 0;
3084	if( WIFSIGNALED(status) ){
3085		Errorcode = JFAIL;
3086		LOGERR_DIE(LOG_INFO)"Filter_file: pgm '%s' died with signal %d, '%s'",
3087			pgm, n, Sigstr(n));
3088	}
3089	n = WEXITSTATUS(status);
3090	if( n > 0 && n < 32 ) n+=(JFAIL-1);
3091#ifdef ORIGINAL_DEBUG//JY@1020
3092	DEBUG1("Filter_file: final status '%s'", Server_status(n) );
3093	if( verbose ){
3094		SETSTATUS(job)"Filter_file: pgm '%s' exited with status '%s'", pgm, Server_status(n));
3095	}
3096#endif
3097	return( n );
3098}
3099
3100#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3101#define LOWER "abcdefghijklmnopqrstuvwxyz"
3102#define DIGIT "01234567890"
3103#define SAFE "-_."
3104#define LESS_SAFE SAFE "@/:()=,+-%"
3105
3106char *Is_clean_name( char *s )
3107{
3108	int c;
3109	if( s ){
3110		for( ; (c = cval(s)); ++s ){
3111			if( !(isalnum(c) || safestrchr( SAFE, c )) ) return( s );
3112		}
3113	}
3114	return( 0 );
3115}
3116
3117void Clean_name( char *s )
3118{
3119	int c;
3120	if( s ){
3121		for( ; (c = cval(s)); ++s ){
3122			if( !(isalnum(c) || safestrchr( SAFE, c )) ) *s = '_';
3123		}
3124	}
3125}
3126
3127/*
3128 * Find a possible bad character in a line
3129 */
3130
3131int Is_meta( int c )
3132{
3133	return( !( isspace(c) || isalnum( c )
3134		|| (Safe_chars_DYN && safestrchr(Safe_chars_DYN,c))
3135		|| safestrchr( LESS_SAFE, c ) ) );
3136}
3137
3138char *Find_meta( char *s )
3139{
3140	int c = 0;
3141	if( s ){
3142		for( ; (c = cval(s)); ++s ){
3143			if( Is_meta( c ) ) return( s );
3144		}
3145		s = 0;
3146	}
3147	return( s );
3148}
3149
3150void Clean_meta( char *t )
3151{
3152	char *s = t;
3153	if( t ){
3154		while( (s = safestrchr(s,'\\')) ) *s = '/';
3155		s = t;
3156		for( s = t; (s = Find_meta( s )); ++s ){
3157			*s = '_';
3158		}
3159	}
3160}
3161
3162#ifdef ORIGINAL_DEBUG//JY@1020
3163/**********************************************************************
3164 * Dump_parms( char *title, struct keywords *k )
3165 * - dump the list of keywords and variable values given by the
3166 *   entries in the array.
3167 **********************************************************************/
3168
3169void Dump_parms( char *title, struct keywords *k )
3170{
3171	char *s;
3172	void *p;
3173	int v;
3174
3175	if( title ) LOGDEBUG( "*** Current Values '%s' ***", title );
3176	for( ; k &&  k->keyword; ++k ){
3177		if( !(p = k->variable) ) continue;
3178		switch(k->type){
3179		case FLAG_K:
3180			v =	*(int *)(p);
3181			LOGDEBUG( "  %s FLAG %d", k->keyword, v);
3182			break;
3183		case INTEGER_K:
3184			v =	*(int *)(p);
3185			LOGDEBUG( "  %s# %d (0x%x, 0%o)", k->keyword,v,v,v);
3186			break;
3187		case STRING_K:
3188			s = *(char **)(p);
3189			if( s ){
3190				LOGDEBUG( "  %s= '%s'", k->keyword, s );
3191			} else {
3192				LOGDEBUG( "  %s= <NULL>", k->keyword );
3193			}
3194			break;
3195		default:
3196			LOGDEBUG( "  %s: UNKNOWN TYPE", k->keyword );
3197		}
3198	}
3199	if( title ) LOGDEBUG( "*** <END> ***");
3200}
3201#endif
3202
3203#ifdef ORIGINAL_DEBUG//JY@1020
3204/**********************************************************************
3205 * Dump_parms( char *title, struct keywords *k )
3206 * - dump the list of keywords and variable values given by the
3207 *   entries in the array.
3208 **********************************************************************/
3209
3210void Dump_default_parms( int fd, char *title, struct keywords *k )
3211{
3212	char *def, *key;
3213	char buffer[2*SMALLBUFFER];
3214	int n;
3215
3216	if( title ){
3217		SNPRINTF(buffer,sizeof(buffer))"%s\n", title );
3218		Write_fd_str(fd, buffer);
3219	}
3220	for( ; k &&  k->keyword; ++k ){
3221		n = 0;
3222		key = k->keyword;
3223		def = k->default_value;
3224		switch(k->type){
3225		case FLAG_K:
3226			if( def ){
3227				if( cval(def) == '=' ) ++def;
3228				n = strtol(def,0,0);
3229			}
3230			SNPRINTF(buffer,sizeof(buffer))" :%s%s\n", key, n?"":"@");
3231			break;
3232		case INTEGER_K:
3233			if( def ){
3234				if( cval(def) == '=' ) ++def;
3235				n = strtol(def,0,0);
3236			}
3237			SNPRINTF(buffer,sizeof(buffer))" :%s=%d\n", key, n);
3238			break;
3239		case STRING_K:
3240			if( def ){
3241				if( cval(def) == '=' ) ++def;
3242			} else {
3243				def = "";
3244			}
3245			SNPRINTF(buffer,sizeof(buffer))" :%s=%s\n", key, def);
3246			break;
3247		default:
3248			SNPRINTF(buffer,sizeof(buffer))"# %s UNKNOWN\n", key);
3249		}
3250		Write_fd_str(fd, buffer);
3251	}
3252	Write_fd_str(fd, "\n");
3253}
3254#endif
3255
3256/***************************************************************************
3257 *char *Fix_Z_opts( struct job *job )
3258 *
3259 * fix the -Z option value
3260 *  Remove_Z_DYN - remove these from the Z string
3261 *  Prefix_Z_DYN - put these at the start
3262 *  Append_Z_DYN - put these at the end
3263 *  Prefix_option_to_option - prefix options to start of option
3264 *     OS Z -> O and S to Z
3265 *     Z  S -> Z to S
3266 ***************************************************************************/
3267
3268void Remove_sequential_separators( char *start )
3269{
3270	char *end;
3271	if( start == 0 || *start == 0 ) return;
3272	while( strchr( File_sep, *start) ){
3273		memmove(start,start+1,safestrlen(start+1)+1);
3274	}
3275	for( end = start + safestrlen(start)-1;
3276		*start && (end = strpbrk( end, File_sep )); ){
3277		*end-- = 0;
3278	}
3279	for( ; *start && (end = strpbrk(start+1,File_sep)); start = end ){
3280		if( start+1 == end ){
3281			memmove(start,start+1,safestrlen(start+1)+1);
3282			end = start;
3283		}
3284	}
3285}
3286
3287#if defined(JYWENG20031104Fix_dollars)
3288void Fix_Z_opts( struct job *job )
3289{
3290	char *str, *s, *pattern, *start, *end;
3291	char buffer[16];
3292	struct line_list l;
3293	int i, c, n;
3294
3295	Init_line_list(&l);
3296	str = Find_str_value( &job->info,"Z", Value_sep );
3297	DEBUG4("Fix_Z_opts: initially '%s', remove '%s', append '%s', prefix '%s'",
3298		str, Remove_Z_DYN, Append_Z_DYN, Prefix_Z_DYN );
3299	DEBUG4("Fix_Z_opts: prefix_options '%s'", Prefix_option_to_option_DYN );
3300	if( Prefix_option_to_option_DYN ){
3301		s = Prefix_option_to_option_DYN;
3302		while( s && *s ){
3303			if( !isalpha(cval(s)) ){
3304				memmove(s,s+1,safestrlen(s+1)+1);
3305			} else {
3306				++s;
3307			}
3308		}
3309		s = Prefix_option_to_option_DYN;
3310		/* now we have the fixed value */
3311		DEBUG4("Fix_Z_opts: prefix_options fixed '%s'", s);
3312		n = safestrlen(s);
3313		if( n < 2 ){
3314			FATAL(LOG_ERR) "Fix_Z_opts: not enough letters '%s'", s );
3315		}
3316		/* find the starting values */
3317		str = 0;
3318		buffer[1] = 0;
3319		for( i = 0; i < n-1; ++i ){
3320			buffer[0] = s[i];
3321			if( (start = Find_str_value(&job->info,buffer,Value_sep)) ){
3322				str= safeextend2(str,start, __FILE__,__LINE__);
3323				Set_str_value(&job->info,buffer,0);
3324			}
3325		}
3326		/* do we need to prefix it? */
3327		if( str ){
3328			buffer[0] = s[i];
3329			start = Find_str_value(&job->info,buffer,Value_sep);
3330				/* put at start */
3331			start= safestrdup3(str,(start?",":""),start,
3332				__FILE__,__LINE__);
3333			Set_str_value(&job->info, buffer, start );
3334			if( start ) free(start); start = 0;
3335		}
3336		if( str ) free(str); str = 0;
3337	}
3338	str = Find_str_value( &job->info,"Z", Value_sep );
3339	DEBUG4("Fix_Z_opts: after Prefix_option_to_option '%s'", str );
3340	if( Remove_Z_DYN && str ){
3341		/* remove the various options - split on commas */
3342		Split(&l, Remove_Z_DYN, ",", 0, 0, 0, 0, 0,0);
3343		for( i = 0; i < l.count; ++i ){
3344			pattern = l.list[i];
3345			DEBUG4("Fix_Z_opts: REMOVE pattern '%s'", pattern );
3346			for( start = str; start && *start; start = end ){
3347				c = 0;
3348				if( !(end = strpbrk(start,",")) ){
3349					end = start+safestrlen(start);
3350				}
3351				c = *end;
3352				*end = 0;
3353				/* now we have the option */
3354				DEBUG4("Fix_Z_opts: str '%s'", start );
3355				if( !Globmatch( pattern, start) ){
3356					/* move the values up in the string, end -> ',' */
3357					if( c ){
3358						memmove( start,end+1, safestrlen(end+1)+1);
3359					} else {
3360						*start = 0;
3361					}
3362					end = start;
3363				} else {
3364					*end = c;
3365					if( c ) ++end;
3366				}
3367			}
3368		}
3369		Free_line_list(&l);
3370	}
3371	DEBUG4("Fix_Z_opts: after remove '%s'", str );
3372	if( Append_Z_DYN && *Append_Z_DYN ){
3373		s = safestrdup3(str,",",Append_Z_DYN,__FILE__,__LINE__);
3374		Set_str_value(&job->info,"Z",s);
3375		str = Find_str_value(&job->info,"Z",Value_sep);
3376		if(s) free(s); s = 0;
3377	}
3378	DEBUG4("Fix_Z_opts: after append '%s'", str );
3379	if( Prefix_Z_DYN && *Prefix_Z_DYN ){
3380		s = safestrdup3(Prefix_Z_DYN,",",str,__FILE__,__LINE__);
3381		Set_str_value(&job->info,"Z",s);
3382		str = Find_str_value(&job->info,"Z",Value_sep);
3383		if(s) free(s); s = 0;
3384	}
3385	DEBUG4("Fix_Z_opts: after Prefix_Z '%s'", str );
3386	for( s = safestrchr(str,','); s; s = strchr(s,',') ){
3387		if( cval(s+1) == ',' ){
3388			memmove(s,s+1,safestrlen(s+1)+1);
3389		} else {
3390			++s;
3391		}
3392	}
3393	if( str ){
3394		if( cval(str) == ',' ){
3395			memmove(str,str+1,safestrlen(str+1)+1);
3396		}
3397		if( (n = safestrlen(str)) && cval(str+n-1) == ',' ) str[n-1] = 0;
3398	}
3399	DEBUG4("Fix_Z_opts: final Z '%s'", str );
3400	Free_line_list(&l);
3401}
3402#endif
3403
3404#if defined(JYWENG20031104Fix_dollars)
3405/***************************************************************************
3406 * void Fix_dollars( struct line_list *l, struct job *job,
3407 *   int nosplit, char *flags )
3408 * Note: see the code for the keys!
3409 * replace
3410 *  \x with x except for \r,\n,\t, -> space
3411 *  \nnn with nnn
3412 *  $*   with flag string, and then evaluate options
3413 *  $X   with -X<value>
3414 *  $0X  with -X <value>
3415 *  $-X  with  <value>
3416 *  $0-X with  <value> (same as $-X)
3417 *  ${s}   with value of control file parameter s (must be upper case)
3418 *  ${ss}  with value of printcap option ss
3419 *  $'{ss} with quoted value of printcap option ss
3420 *
3421 *  nosplit - do not split the option value over two entries
3422 *  flags -   flags to use for $*
3423 ***************************************************************************/
3424
3425void Fix_dollars( struct line_list *l, struct job *job, int nosplit, char *flags )
3426{
3427	int i, j, count, space, notag, kind, n, c, position, quote;
3428	const char *str;
3429	char *strv, *s, *t, *rest;
3430	char buffer[SMALLBUFFER], tag[32];
3431
3432#ifdef ORIGINAL_DEBUG//JY@1020
3433	if(DEBUGL4)Dump_line_list("Fix_dollars- before", l );
3434#endif
3435	for( count = 0; count < l->count; ++count ){
3436		position = 0;
3437		for( strv = l->list[count]; (s = safestrpbrk(strv+position,"$\\")); ){
3438			DEBUG4("Fix_dollars: expanding [%d]='%s'", count, strv );
3439			position = s - strv;
3440			c = cval(s);
3441			*s++ = 0;
3442			if( c == '\\' ){
3443				c = *s++;
3444				/* check for end of string */
3445				if( c == 0 ) break;
3446				if( c == 'r' || c == 'n' || c == 't' ){
3447					c = ' ';
3448				} else if( isdigit( c ) ){
3449					tag[0] = c;
3450					if( (tag[1] = *s) ) ++s;
3451					if( (tag[2] = *s) ) ++s;
3452					tag[3] = 0;
3453					c = strtol( tag, 0, 8 );
3454				}
3455				if( !isprint(c) || isspace(c) ) c = ' ';
3456				strv[position] = c;
3457				++position;
3458				memmove(strv+position,s,safestrlen(s)+1);
3459				continue;
3460			}
3461			/* now we handle the $ */
3462			str = 0;
3463			rest = 0;
3464			n = space = notag = quote = 0;
3465			kind = STRING_K;
3466			while( (c = cval(s)) && safestrchr( " 0-'", c) ){
3467				switch( c ){
3468				case '0': case ' ': space = 1; break;
3469				case '-':           notag = 1; break;
3470				case '\'':          quote = 1; break;
3471				default: break;
3472				}
3473				++s;
3474			}
3475			rest = s+1;
3476			if( c == '*' ){
3477				if( flags && *flags ){
3478					rest = safestrdup(rest,__FILE__,__LINE__);
3479					position = safestrlen(strv);
3480					l->list[count] = strv
3481						 = safeextend3(strv,flags,rest,__FILE__,__LINE__);
3482					if( rest ) free(rest); rest = 0;
3483				}
3484				continue;
3485			} else if( c == '{' ){
3486				++s;
3487				if( !(rest = safestrchr(rest,'}')) ){
3488					break;
3489				}
3490				*rest++ = 0;
3491				if( !cval(s+1) && isupper(cval(s)) ){
3492					str = job?Find_str_value( &job->info,s,Value_sep):0;
3493				} else {
3494					str = Find_value( &PC_entry_line_list, s, Value_sep );
3495				}
3496				notag = 1;
3497				space = 0;
3498			} else {
3499				quote = 0;
3500				switch( c ){
3501				case 'a':
3502					str = Accounting_file_DYN;
3503					if( str && cval(str) == '|' ) str = 0;
3504					break;
3505				case 'b': str = job?Find_str_value(&job->info,SIZE,Value_sep):0; break;
3506				case 'c':
3507					notag = 1; space=0;
3508					t = job?Find_str_value(&job->info,FORMAT,Value_sep):0;
3509					if( t && *t == 'l'){
3510						str="-c";
3511					}
3512					break;
3513				case 'd': str = Spool_dir_DYN; break;
3514				case 'e':
3515					str = job?Find_str_value(&job->info,
3516						DF_NAME,Value_sep):0;
3517					break;
3518				case 'f':
3519					str = job?Find_str_value(&job->info,"N",Value_sep):0;
3520					break;
3521				case 'h':
3522					str = job?Find_str_value(&job->info,FROMHOST,Value_sep):0;
3523					break;
3524				case 'i':
3525					str = job?Find_str_value(&job->info,"I",Value_sep):0;
3526					break;
3527				case 'j':
3528					str = job?Find_str_value(&job->info,NUMBER,Value_sep):0;
3529					break;
3530				case 'k':
3531					str = job?Find_str_value(&job->info,TRANSFERNAME,Value_sep):0;
3532					break;
3533				case 'l':
3534					kind = INTEGER_K; n = Page_length_DYN; break;
3535				case 'n':
3536					str = job?Find_str_value(&job->info,LOGNAME,Value_sep):0;
3537					break;
3538				case 'p': str = RemotePrinter_DYN; break;
3539				case 'r': str = RemoteHost_DYN; break;
3540				case 's': str = Status_file_DYN; break;
3541				case 't':
3542					str = Time_str( 0, time( (void *)0 ) ); break;
3543				case 'w': kind = INTEGER_K; n = Page_width_DYN; break;
3544				case 'x': kind = INTEGER_K; n = Page_x_DYN; break;
3545				case 'y': kind = INTEGER_K; n = Page_y_DYN; break;
3546				case 'F':
3547					str = job?Find_str_value(&job->info,FORMAT,Value_sep):0;
3548					break;
3549				case 'P': str = Printer_DYN; break;
3550				case 'S': str = Comment_tag_DYN; break;
3551				/* case '_': str = esc_Auth_client_id_DYN; break; */
3552				default:
3553					if( isupper(c) ){
3554						buffer[1] = 0; buffer[0] = c;
3555						str = job?Find_str_value( &job->info,buffer,Value_sep):0;
3556					}
3557					break;
3558				}
3559			}
3560			buffer[0] = 0;
3561			tag[0] = 0;
3562			switch( kind ){
3563			case INTEGER_K:
3564				SNPRINTF(buffer,sizeof(buffer))"%d", n );
3565				str = buffer;
3566				break;
3567			}
3568			DEBUG4(
3569				"Fix_dollars: strv '%s', found '%s', rest '%s', notag %d, space %d",
3570				strv, str, rest, notag, space );
3571			tag[0] = 0;
3572			if( str && !cval(str) ) str = 0;
3573			if( quote && !str ) str = "";
3574			if( str ){
3575				rest = safestrdup(rest,__FILE__,__LINE__);
3576				if( notag ){
3577					space = 0;
3578				} else {
3579					i = 0;
3580					if( (quote || nosplit) && !space ) tag[i++] = '\'';
3581					tag[i++] = '-'; tag[i++] = c; tag[i++] = 0;
3582					l->list[count] = strv = safeextend2( strv, tag, __FILE__,__LINE__ );
3583					if( !(quote || nosplit) ) tag[0] = 0;
3584					tag[1] = 0;
3585				}
3586				if( space ){
3587					DEBUG4("Fix_dollars: space [%d]='%s'", count, l->list[count] );
3588					if( quote || nosplit ){
3589						position = safestrlen(strv) + safestrlen(str) + 2;
3590						l->list[count] =
3591							strv = safeextend5( strv," '",str,"'",rest,__FILE__,__LINE__);
3592					} else {
3593						Check_max(l,2);
3594						for( i = l->count; i >= count; --i ){
3595							l->list[i+1] = l->list[i];
3596						}
3597						++l->count;
3598						++count;
3599						l->list[count] = strv = safestrdup2(str,rest,__FILE__,__LINE__);
3600						position = safestrlen(str);
3601					}
3602				} else {
3603					position = safestrlen(strv) + safestrlen(str)+safestrlen(tag);
3604					l->list[count] = strv
3605						 = safeextend4(strv,str,tag,rest,__FILE__,__LINE__);
3606				}
3607				if( rest ) free(rest); rest = 0;
3608			} else {
3609				memmove(strv+position,rest,safestrlen(rest)+1);
3610			}
3611			DEBUG4("Fix_dollars: [%d]='%s'", count, strv );
3612		}
3613	}
3614	for( i = j = 0; i < l->count; ++i ){
3615		if( (s = l->list[i]) && *s == 0 ){
3616			free(s); s = 0;
3617		}
3618		l->list[j] = s;
3619		if( s ) ++j;
3620	}
3621	l->count = j;
3622#ifdef ORIGINAL_DEBUG//JY@1020
3623	if(DEBUGL4)Dump_line_list("Fix_dollars- after", l );
3624#endif
3625}
3626#endif
3627
3628/*
3629 * char *Make_pathname( char *dir, char *file )
3630 *  - makes a full pathname from the dir and file part
3631 */
3632
3633char *Make_pathname( const char *dir,  const char *file )
3634{
3635	char *s, *path;
3636	if( file == 0 ){
3637		path = 0;
3638	} else if( file[0] == '/' ){
3639		path = safestrdup(file,__FILE__,__LINE__);
3640	} else if( dir ){
3641		path = safestrdup3(dir,"/",file,__FILE__,__LINE__);
3642	} else {
3643		path = safestrdup2("./",file,__FILE__,__LINE__);
3644	}
3645	if( (s = path) ) while((s = strstr(s,"//"))) memmove(s,s+1,safestrlen(s)+1 );
3646	return(path);
3647}
3648
3649/***************************************************************************
3650 * Get_keywords and keyval
3651 * - decode the control word and return a key
3652 ***************************************************************************/
3653
3654int Get_keyval( char *s, struct keywords *controlwords )
3655{
3656	int i;
3657	char *t;
3658	for( i = 0; controlwords[i].keyword; ++i ){
3659		if(
3660			safestrcasecmp( s, controlwords[i].keyword ) == 0
3661			|| ( (t = controlwords[i].translation) && safestrcasecmp( s, _(t) ) == 0)
3662			){
3663			return( controlwords[i].type );
3664		}
3665	}
3666	return( 0 );
3667}
3668
3669char *Get_keystr( int c, struct keywords *controlwords )
3670{
3671	int i;
3672	for( i = 0; controlwords[i].keyword; ++i ){
3673		if( controlwords[i].type == c ){
3674			return( controlwords[i].keyword );
3675		}
3676	}
3677	return( 0 );
3678}
3679
3680char *Escape( char *str, int level )
3681{
3682	char *s = 0;
3683	int i, c, j, k, incr = 3*level;
3684	int len = 0;
3685
3686	if( str == 0 || *str == 0 ) return(0);
3687	if( level <= 0 ) level = 1;
3688
3689	len = safestrlen(str);
3690	for( j = 0; (c = cval(str+j)); ++j ){
3691		if( c != ' ' && !isalnum( c ) ){
3692			len += incr;
3693		}
3694	}
3695	DEBUG5("Escape: level %d, allocated length %d, length %d, for '%s'",
3696		level, len, safestrlen(str), str );
3697	s = malloc_or_die(len+1,__FILE__,__LINE__);
3698	i = 0;
3699	for( i = j = 0; (c = cval(str+j)); ++j ){
3700		if( c == ' ' ){
3701			s[i++] = '?';
3702		} else if( !isalnum( c ) ){
3703			SNPRINTF(s+i,4)"%%%02x",c);
3704			/* we encode the % as %25 and move the other stuff over */
3705			for( k = 1; k < level; ++k ){
3706				/* we move the stuff after the % two positions */
3707				/* s+i is the %, s+i+1 is the first digit */
3708				memmove(s+i+3, s+i+1, safestrlen(s+i+1)+1);
3709				memmove(s+i+1, "25", 2 );
3710			}
3711			i += safestrlen(s+i);
3712		} else {
3713			s[i++] = c;
3714		}
3715	}
3716	s[i] = 0;
3717	DEBUG5("Escape: final length %d '%s'", i,  s );
3718	return(s);
3719}
3720
3721/*
3722 * we replace a colon by \072 in a dynmaically allocated string
3723 */
3724
3725void Escape_colons( struct line_list *list )
3726{
3727	int linenumber, len, c;
3728	char *str, *s, *t, *newstr;
3729
3730	for( linenumber = 0; list && linenumber < list->count; ++linenumber ){
3731		str = list->list[linenumber];
3732
3733		if( str == 0 || strchr(str,':') == 0 ) continue;
3734
3735		len = safestrlen(str);
3736		for( s = str; (s = strchr(s,':')); ++s ){
3737			len += 4;
3738		}
3739		DEBUG4("Escape_colons: new length %d for '%s'",
3740			len, str );
3741		newstr = t = malloc_or_die(len+1,__FILE__,__LINE__);
3742		for( s = str; (c = cval(s)); ++s ){
3743			if( c != ':' ){
3744				*t++ = c;
3745			} else {
3746				strcpy(t,"\\072");
3747				t += 4;
3748			}
3749		}
3750		*t = 0;
3751		free(str);
3752		list->list[linenumber] = newstr;
3753		DEBUG4("Escape_colons: '%s'", newstr );
3754	}
3755}
3756
3757void Unescape( char *str )
3758{
3759	int i, c;
3760	char *s = str;
3761	char buffer[4];
3762	if( str == 0 ) return;
3763	for( i = 0; (c = cval(str)); ++str ){
3764		if( c == '?' ){
3765			c = ' ';
3766		} else if( c == '%'
3767			&& (buffer[0] = cval(str+1))
3768			&& (buffer[1] = cval(str+2))
3769			){
3770			buffer[2] = 0;
3771			c = strtol(buffer,0,16);
3772			str += 2;
3773		}
3774		s[i++] = c;
3775	}
3776	s[i] = 0;
3777	DEBUG5("Unescape '%s'", s );
3778}
3779
3780char *Find_str_in_str( char *str, const char *key, const char *sep )
3781{
3782	char *s = 0, *end;
3783	int len = safestrlen(key), c;
3784
3785	if(str) for( s = str; (s = strstr(s,key)); ++s ){
3786		c = cval(s+len);
3787		if( !(safestrchr(Value_sep, c) || safestrchr(sep, c)) ) continue;
3788		if( s > str && !safestrchr(sep,cval(s-1)) ) continue;
3789		s += len;
3790		if( (end = safestrpbrk(s,sep)) ){ c = *end; *end = 0; }
3791		/* skip over Value_sep character
3792		 * x@;  -> x@\000  - get null str
3793		 * x;   -> x\000  - get null str
3794		 * x=v;  -> x=v  - get v
3795		 */
3796		if( *s ) ++s;
3797		if( *s ){
3798			s = safestrdup(s,__FILE__,__LINE__);
3799		} else {
3800			s = 0;
3801		}
3802		if( end ) *end = c;
3803		break;
3804	}
3805	return(s);
3806}
3807
3808/*
3809 * int Find_key_in_list( struct line_list *l, char *key, char *sep, int *mid )
3810 *  Search and unsorted list for a key value, starting at *mid.
3811 *
3812 *  The list has lines of the form:
3813 *    key [separator] value
3814 *  returns:
3815 *    *at = index of last tested value
3816 *    return value: 0 if found;
3817 *                  <0 if list[*at] < key
3818 *                  >0 if list[*at] > key
3819 */
3820
3821int Find_key_in_list( struct line_list *l, const char *key, const char *sep, int *m )
3822{
3823	int mid = 0, cmp = -1, c = 0;
3824	char *s, *t;
3825	if( m ) mid = *m;
3826	DEBUG5("Find_key_in_list: start %d, count %d, key '%s'", mid, l->count, key );
3827	while( mid < l->count ){
3828		s = l->list[mid];
3829		t = 0;
3830		if( sep && (t = safestrpbrk(s, sep )) ) { c = *t; *t = 0; }
3831		cmp = safestrcasecmp(key,s);
3832		if( t ) *t = c;
3833		DEBUG5("Find_key_in_list: cmp %d, mid %d", cmp, mid);
3834		if( cmp == 0 ){
3835			if( m ) *m = mid;
3836			break;
3837		}
3838		++mid;
3839	}
3840	DEBUG5("Find_key_in_list: key '%s', cmp %d, mid %d", key, cmp, mid );
3841	return( cmp );
3842}
3843
3844/***************************************************************************
3845 * int Fix_str( char * str )
3846 * - make a copy of the original string
3847 * - substitute all the escape characters
3848 * \f, \n, \r, \t, and \nnn
3849 ***************************************************************************/
3850
3851char *Fix_str( char *str )
3852{
3853	char *s, *end, *dupstr, buffer[4];
3854	int c, len;
3855	DEBUG3("Fix_str: '%s'", str );
3856	if( str == 0 ) return(str);
3857	dupstr = s = safestrdup(str,__FILE__,__LINE__);
3858	DEBUG3("Fix_str: dup '%s', 0x%lx", dupstr, Cast_ptr_to_long(dupstr) );
3859	for( ; (s = safestrchr(s,'\\')); ){
3860		end = s+1;
3861		c = cval(end);
3862		/* check for \nnn */
3863		if( isdigit( c ) ){
3864			for( len = 0; len < 3; ++len ){
3865				if( !isdigit(cval(end)) ){
3866					break;
3867				}
3868				buffer[len] = *end++;
3869			}
3870			c = strtol(buffer,0,8);
3871		} else {
3872			switch( c ){
3873				case 'f': c = '\f'; break;
3874				case 'r': c = '\r'; break;
3875				case 'n': c = '\n'; break;
3876				case 't': c = '\t'; break;
3877			}
3878			++end;
3879		}
3880		s[0] = c;
3881		if( c == 0 ) break;
3882		memcpy(s+1,end,safestrlen(end)+1);
3883		++s;
3884	}
3885	if( *dupstr == 0 ){ free(dupstr); dupstr = 0; }
3886	DEBUG3( "Fix_str: final str '%s' -> '%s'", str, dupstr );
3887	return( dupstr );
3888}
3889
3890#if defined(JYWENG20031104Shutdown_or_close)
3891/***************************************************************************
3892 * int Shutdown_or_close( int fd )
3893 * - if the file descriptor is a socket, then do a shutdown (write), return fd;
3894 *   or if the
3895 * - otherwise close it and return -1;
3896 ***************************************************************************/
3897
3898int Shutdown_or_close( int fd )
3899{
3900	struct stat statb;
3901
3902	if( fd < 0 || fstat( fd, &statb ) == -1 ){
3903		fd = -1;
3904	} else if( Backwards_compatible_DYN || !Half_close_DYN
3905		|| !(S_ISSOCK(statb.st_mode)) || shutdown( fd, 1 ) == -1 ){
3906		close(fd);
3907		fd = -1;
3908	}
3909	return( fd );
3910}
3911#endif
3912
3913#ifdef REMOVE
3914/*
3915 *  Support for non-copy on write fork as for NT
3916 *   1. Preparation for the fork is done by calling 'Setup_lpd_call'
3917 *      This establishes a default setup for the new process by setting
3918 *      up a list of parameters and file descriptors to be passed.
3919 *   2. The user then adds fork/process specific options
3920 *   3. The fork is done by calling Make_lpd_call which actually
3921 *      does the fork() operation.  If the lpd_path option is set,
3922 *      then a -X command line flag is added and an execv() of the program
3923 *      is done.
3924 *   4.A - fork()
3925 *        Make_lpd_call (child) will call Do_work(),  which dispatches
3926 *         a call to the required function.
3927 *   4.B - execv()
3928 *        The execv'd process checks the command line parameters for -X
3929 *         flag and when it finds it calls Do_work() with the same parameters
3930 *         as would be done for the fork() version.
3931 */
3932
3933void Setup_lpd_call( struct line_list *passfd, struct line_list *args )
3934{
3935	Free_line_list( args );
3936	Check_max(passfd, 10 );
3937	passfd->count = 0;
3938	passfd->list[passfd->count++] = Cast_int_to_voidstar(0);
3939	passfd->list[passfd->count++] = Cast_int_to_voidstar(1);
3940	passfd->list[passfd->count++] = Cast_int_to_voidstar(2);
3941	if( Mail_fd > 0 ){
3942		Set_decimal_value(args,MAIL_FD,passfd->count);
3943		passfd->list[passfd->count++] = Cast_int_to_voidstar(Mail_fd);
3944	}
3945	if( Status_fd > 0 ){
3946		Set_decimal_value(args,STATUS_FD,passfd->count);
3947		passfd->list[passfd->count++] = Cast_int_to_voidstar(Status_fd);
3948	}
3949	if( Logger_fd > 0 ){
3950		Set_decimal_value(args,LOGGER,passfd->count);
3951		passfd->list[passfd->count++] = Cast_int_to_voidstar(Logger_fd);
3952	}
3953	if( Lpd_request > 0 ){
3954		Set_decimal_value(args,LPD_REQUEST,passfd->count);
3955		passfd->list[passfd->count++] = Cast_int_to_voidstar(Lpd_request);
3956	}
3957	Set_flag_value(args,DEBUG,Debug);
3958	Set_flag_value(args,DEBUGFV,DbgFlag);
3959#ifdef DMALLOC
3960	{
3961		extern int _dmalloc_outfile_fd;
3962		if( _dmalloc_outfile_fd > 0 ){
3963			Set_decimal_value(args,DMALLOC_OUTFILE,passfd->count);
3964			passfd->list[passfd->count++] = Cast_int_to_voidstar(_dmalloc_outfile_fd);
3965		}
3966	}
3967#endif
3968}
3969
3970/*
3971 * Make_lpd_call - does the actual forking operation
3972 *  - sets up file descriptor for child, can close_on_exec()
3973 *  - does fork() or execve() as appropriate
3974 *
3975 *  returns: pid of child or -1 if fork failed.
3976 */
3977
3978int Make_lpd_call( char *name, struct line_list *passfd, struct line_list *args )
3979{
3980	int pid, fd, i, n, newfd;
3981	struct line_list env;
3982
3983
3984	Init_line_list(&env);
3985	pid = dofork(1);
3986	if( pid ){
3987		return(pid);
3988	}
3989	Name = "LPD_CALL";
3990#ifdef ORIGINAL_DEBUG//JY@1020
3991	if(DEBUGL2){
3992		LOGDEBUG("Make_lpd_call: lpd path '%s'", Lpd_path_DYN );
3993		LOGDEBUG("Make_lpd_call: passfd count %d", passfd->count );
3994		for( i = 0; i < passfd->count; ++i ){
3995			LOGDEBUG(" [%d] %d", i, Cast_ptr_to_int(passfd->list[i]));
3996		}
3997		Dump_line_list("Make_lpd_call - args", args );
3998	}
3999#endif
4000	for( i = 0; i < passfd->count; ++i ){
4001		fd = Cast_ptr_to_int(passfd->list[i]);
4002		if( fd < i  ){
4003			/* we have fd 3 -> 4, but 3 gets wiped out */
4004			do{
4005				newfd = dup(fd);
4006				Max_open(newfd);
4007				if( newfd < 0 ){
4008					Errorcode = JABORT;
4009					LOGERR_DIE(LOG_INFO)"Make_lpd_call: dup failed");
4010				}
4011				DEBUG4("Make_lpd_call: fd [%d] = %d, dup2 -> %d",
4012					i, fd, newfd );
4013				passfd->list[i] = Cast_int_to_voidstar(newfd);
4014			} while( newfd < i );
4015		}
4016	}
4017#ifdef ORIGINAL_DEBUG//JY@1020
4018	if(DEBUGL2){
4019		LOGDEBUG("Make_lpd_call: after fixing fd count %d", passfd->count);
4020		for( i = 0 ; i < passfd->count; ++i ){
4021			fd = Cast_ptr_to_int(passfd->list[i]);
4022			LOGDEBUG("  [%d]=%d",i,fd);
4023		}
4024	}
4025#endif
4026	for( i = 0; i < passfd->count; ++i ){
4027		fd = Cast_ptr_to_int(passfd->list[i]);
4028		DEBUG2("Make_lpd_call: fd %d -> %d",fd, i );
4029		if( dup2( fd, i ) == -1 ){
4030			Errorcode = JABORT;
4031			LOGERR_DIE(LOG_INFO)"Make_lpd_call: dup2(%d,%d) failed",
4032				fd, i );
4033		}
4034	}
4035	/* close other ones to simulate close_on_exec() */
4036	n = Max_fd+10;
4037	for( i = passfd->count ; i < n; ++i ){
4038		close(i);
4039	}
4040	passfd->count = 0;
4041	Free_line_list( passfd );
4042#ifdef JYDEBUG//JYWeng
4043aaaaaa=fopen("/tmp/qqqqq", "a");
4044fprintf(aaaaaa, "linelist:Make_lpd_call: b4 Do_work\n");
4045fclose(aaaaaa);
4046#endif
4047	Do_work( name, args );
4048	return(0);
4049}
4050
4051void Do_work( char *name, struct line_list *args )
4052{
4053	void  (*proc)() = 0;
4054	Logger_fd = Find_flag_value(args, LOGGER,Value_sep);
4055	Status_fd = Find_flag_value(args, STATUS_FD,Value_sep);
4056	Mail_fd = Find_flag_value(args, MAIL_FD,Value_sep);
4057	Lpd_request = Find_flag_value(args, LPD_REQUEST,Value_sep);
4058	/* undo the non-blocking IO */
4059	if( Lpd_request > 0 ) Set_block_io( Lpd_request );
4060	Debug= Find_flag_value( args, DEBUG, Value_sep);
4061	DbgFlag= Find_flag_value( args, DEBUGFV, Value_sep);
4062#ifdef DMALLOC
4063	{
4064		extern int _dmalloc_outfile_fd;
4065		_dmalloc_outfile_fd = Find_flag_value(args, DMALLOC_OUTFILE,Value_sep);
4066	}
4067#endif
4068#ifdef JYDEBUG//JYWeng
4069aaaaaa=fopen("/tmp/qqqqq", "a");
4070fprintf(aaaaaa, "linelist: Do_work: starting...name=%s\n", name);
4071fclose(aaaaaa);
4072#endif
4073#ifdef ORIGINAL_DEBUG//JY@1020
4074	if( !safestrcasecmp(name,"logger") ) proc = Logger;
4075	else if( !safestrcasecmp(name,"all") ) proc = Service_all;
4076#else
4077	if( !safestrcasecmp(name,"all") ) proc = Service_all;
4078#endif
4079	else if( !safestrcasecmp(name,"server") ) proc = Service_connection;
4080	else if( !safestrcasecmp(name,"queue") ) proc = Service_queue;
4081	else if( !safestrcasecmp(name,"printer") ) proc = Service_worker;
4082	DEBUG3("Do_work: '%s', proc 0x%lx ", name, Cast_ptr_to_long(proc) );
4083	(proc)(args);
4084	cleanup(0);
4085}
4086
4087/*
4088 * Start_worker - general purpose dispatch function
4089 *   - adds an input FD
4090 */
4091
4092int Start_worker( char *name, struct line_list *parms, int fd )
4093{
4094	struct line_list passfd, args;
4095	int pid;
4096
4097	Init_line_list(&passfd);
4098	Init_line_list(&args);
4099#ifdef ORIGINAL_DEBUG//JY@1020
4100	if(DEBUGL1){
4101		DEBUG1("Start_worker: fd %d", fd );
4102		Dump_line_list("Start_worker - parms", parms );
4103	}
4104#endif
4105	Setup_lpd_call( &passfd, &args );
4106	Merge_line_list( &args, parms, Value_sep,1,1);
4107	Free_line_list( parms );
4108	if( fd ){
4109		Check_max(&passfd,2);
4110		Set_decimal_value(&args,INPUT,passfd.count);
4111		passfd.list[passfd.count++] = Cast_int_to_voidstar(fd);
4112	}
4113
4114	pid = Make_lpd_call( name, &passfd, &args );
4115	Free_line_list( &args );
4116	passfd.count = 0;
4117	Free_line_list( &passfd );
4118	DEBUG1("Start_worker: pid %d", pid );
4119	return(pid);
4120}
4121#endif
4122