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