1 /*                             -*- Mode: C++-C -*-
2  *
3  *		 Copyright 1994 Christopher B. Liebman
4  *
5  *     Permission to use, copy, modify, distribute, and sell this software
6  *     and its documentation for any purpose is hereby granted without fee,
7  *     provided that the above copyright notice appear in all copies and that
8  *     both that copyright notice and this permission notice appear in
9  *     supporting documentation, and that the name Christopher B. Liebman not
10  *     be used in advertising or publicity pertaining to distribution of this
11  *     software without specific, written prior permission.
12  *
13  *    THIS SOFTWARE IS PROVIDED `AS-IS'.  CHRISTOPHER B. LIEBMAN, DISCLAIMS
14  *    ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT
15  *    LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
16  *    PARTICULAR PURPOSE, OR NONINFRINGEMENT.  IN NO EVENT SHALL CHRISTOPHER
17  *    B. LIEBMAN, BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING SPECIAL,
18  *    INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA, OR
19  *    PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF
20  *    WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF
21  *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Author          : Chris Liebman
24  * Created On      : Tue Jan 11 14:11:30 1994
25  * Last Modified By: Chris Liebman
26  * Last Modified On: Sun Feb 13 17:03:59 1994
27  * Update Count    : 139
28  * Status          : Released
29  *
30  * HISTORY
31  * 13-Feb-1994		Chris Liebman
32  *    Last Modified: Sat Feb 12 22:51:24 1994 #138 (Chris Liebman)
33  *    Removed the labelBindings. (the general bindings can now specify the
34  *    labels.
35  *
36  * 31-Jan-1994		Chris Liebman
37  *    Last Modified: Sat Jan 29 23:44:35 1994 #133 (Chris Liebman)
38  *    *Major* changes to support new searching configuration.
39  *
40  * 24-Jan-1994		Chris Liebman
41  *    Last Modified: Sun Jan 23 18:55:48 1994 #64 (Chris Liebman)
42  *    Added path support and added X-Face header support.
43  *
44  * 14-Jan-1994		Chris Liebman
45  *    Last Modified: Tue Jan 11 14:19:02 1994 #1 (Chris Liebman)
46  *    added #if to use XtDatabase() for versions of Xt less than 5
47  *
48  * PURPOSE
49  * 	Handle images in an image independent way.
50 */
51 
52 #ifndef lint
53 static char *RCSid = "$Id: face_image.c,v 1.12 1994/02/23 13:17:02 liebman Exp $";
54 #endif
55 
56 #include "faces.h"
57 #include "face_image.h"
58 #include "face_search.h"
59 #include <sys/stat.h>
60 
61 /*
62  *  This is the master list of supported image types.
63 */
64 
65 static FaceImageType*	AllImageTypes = NULL;
66 
67 /*
68  *   Look up an image type by name.
69 */
70 
71 FaceImageType*
FaceImageTypeByName(name)72 FaceImageTypeByName(name)
73 char* name;
74 {
75     FaceImageType* type;
76 
77     for (type = AllImageTypes; type != NULL; type = type->next)
78     {
79 	if (strcmp(name, type->name) == 0)
80 	{
81 	    return type;
82 	}
83     }
84 
85     return NULL;
86 }
87 
88 /*
89  * Register a new image type.
90 */
91 
92 void
FaceImageTypeRegister(type)93 FaceImageTypeRegister(type)
94 FaceImageType* type;
95 {
96     type->next = AllImageTypes;
97     AllImageTypes = type;
98 }
99 
100 /*
101  * Parse a string of ':' seperated image type names into an array of
102  * image type pointers.
103 */
104 
105 FaceImageType**
FaceImageTypeListParse(str)106 FaceImageTypeListParse(str)
107 char*		str;
108 {
109     FaceImageType*	type;
110     char*		name;
111     int			type_count = 0;
112     int			array_size = 0;
113     FaceImageType**	array = NULL;
114 
115     if (str == NULL)
116     {
117 	return NULL;
118     }
119 
120     array_size = 10;
121     array = (FaceImageType**)XtMalloc(array_size * sizeof(FaceImageType*));
122 
123     while((name = ParseToken(&str, ":")) != NULL)
124     {
125 	type = FaceImageTypeByName(name);
126 
127 	if (type == NULL)
128 	{
129 	    fprintf(stderr, "FaceImageTypeListParse: bad type name: <%s>\n", name);
130 	    continue;
131 	}
132 
133 	if (type_count >= array_size)
134 	{
135 	    array_size += 10;
136 	    array = (FaceImageType**)XtRealloc((char*)array,
137 				       array_size * sizeof(FaceImageType*));
138 	}
139 
140 	array[type_count++] = type;
141     }
142 
143     array[type_count] = NULL;
144 
145     return array;
146 }
147 
148 /*
149  *   Here we store a complete list of images.
150 */
151 
152 static FaceImage	*TheImages = NULL;
153 
154 /*
155  *    Create face image data for face.
156 */
157 
158 FaceImage*
FaceImageCreate(file,type,data)159 FaceImageCreate(file, type, data)
160 char*		file;
161 FaceImageType*	type;
162 void*		data;
163 {
164     FaceImage*	fi;
165 
166     fi = (FaceImage *)XtCalloc(1, sizeof(FaceImage));
167     fi->file  = XtNewString(file);
168     fi->refs  = 1;
169     fi->type  = type;
170     fi->data  = data;
171 
172     /*
173      *  Put the new image on the list.
174     */
175 
176     fi->next = TheImages;
177     fi->prev = NULL;
178 
179     if (fi->next != NULL)
180     {
181 	fi->next->prev = fi;
182     }
183 
184     TheImages = fi;
185 
186     return fi;
187 }
188 
189 typedef struct face_image_load_info
190 {
191     MailItem		*item;
192     FaceImageType*	type;
193 } FaceImageLoadInfo;
194 
195 static int
FaceImageLoadWork(file,path,info)196 FaceImageLoadWork(file, path, info)
197 char*			file;
198 char*			path;
199 FaceImageLoadInfo*	info;
200 {
201     struct stat		buf;
202     void*		image_data = NULL;
203     FaceImage*		fi;
204     int			length;
205     static char*	filename = NULL;
206     static int		filename_length = 0;
207 
208     char                my_label[512];
209     /*
210      *    First see if we already have this image.
211     */
212 
213 
214     for (fi = TheImages; fi != NULL; fi = fi->next)
215     {
216 	if (strcmp(fi->file, file) == 0)
217 	{
218 	  if (info && info->item) {
219 	    sprintf(my_label, "%s@%s", info->item->user, info->item->host);
220 	    if (strcmp(fi->label, my_label))
221 	      break;
222 	  }
223 	    /*
224 	     * Yep!
225 	    */
226 
227 	    fi->refs += 1;
228 
229 	    info->item->image = fi;
230 	    return 1;
231 	}
232     }
233 
234     /*
235      * Only read if file exists and is not a directory.
236     */
237 
238     if ((stat(file, &buf) != -1) && !(buf.st_mode & S_IFDIR))
239     {
240 	/*
241 	 * Attempt to read the file.
242 	*/
243 
244 	image_data = info->type->read(file, info->type->data);
245     }
246 
247 
248     /*
249      *    Try the common extension if we failed.
250     */
251 
252     if (image_data == NULL)
253     {
254 	/*
255 	 *   Now try common extension.
256 	*/
257 
258 	length = strlen(file) + strlen(info->type->extension) + 1;
259 
260 	if (filename_length < length)
261 	{
262 	    filename_length = length;
263 
264 	    if (filename)
265 	    {
266 		XtFree(filename);
267 	    }
268 
269 	    filename = XtMalloc(filename_length);
270 	}
271 
272 	sprintf(filename, "%s%s", file, info->type->extension);
273 
274 	/*
275 	 * Only read if file exists and is not a directory.
276 	*/
277 
278 	if ((stat(filename, &buf) != -1) && !(buf.st_mode & S_IFDIR))
279 	{
280 	    /*
281 	     * Attempt to read the file.
282 	    */
283 
284 	    image_data = info->type->read(filename, info->type->data);
285 	}
286     }
287 
288     /*
289      *    If we still do not have it then fail.
290     */
291 
292     if (image_data == NULL)
293     {
294 	return 0;
295     }
296 
297     /*
298      *   Ok, create a face image struct.
299     */
300     fi = FaceImageCreate(file, info->type, image_data);
301 
302     info->item->image = fi;
303 
304     return 1;
305 }
306 
307 int
FaceImageLoad(file,item,data)308 FaceImageLoad(file, item, data)
309 char*		file;
310 MailItem*	item;
311 FaceSearchData*	data;
312 {
313     int			type;
314     FaceImageType**	types = TheFacesResources.image_types;
315     int			found = 0;
316     FaceImageLoadInfo	info;
317     char**		paths = TheFacesResources.image_paths;
318 
319     if (data != NULL)
320     {
321 	/*
322 	 * Use the image types in teh search data.
323 	*/
324 
325 	if (data->itypes != NULL)
326 	{
327 	    types = data->itypes;
328 	}
329 
330 	/*
331 	 * Fixup paths.
332 	*/
333 
334 	if (data->paths != NULL)
335 	{
336 	    paths = data->paths;
337 	}
338     }
339 
340     /*
341      *   No image types?
342     */
343 
344     if (types == NULL)
345     {
346 	fprintf(stderr, "FaceImageLoad: no image types!\n");
347 	return 0;
348     }
349 
350     info.item = item;
351 
352     for(type = 0; types[type] != NULL; ++type)
353     {
354 	info.type = types[type];
355 
356 	/*
357 	 *   enumerate thru paths if needed.
358 	*/
359 
360 	if ((*file != '/') &&
361 	    strncmp(file, "./", 2) &&
362 	    strncmp(file, "../", 3))
363 	{
364 	    found = PathEnumerate(file, paths, FaceImageLoadWork, &info);
365 	}
366 	else
367 	{
368 	    found = FaceImageLoadWork(file, ".", &info);
369 	}
370 
371 	if (found)
372 	{
373 	    break;
374 	}
375     }
376 
377 #ifdef FACEDB_DEBUG
378     if (found)
379       fprintf(stderr, "FaceImageLoad: Reporting %s as found\n", file);
380 #endif
381 
382     return found;
383 }
384 
385 /*
386  *    Free an image.
387 */
388 
389 void
FaceImageFree(fi)390 FaceImageFree(fi)
391 FaceImage	*fi;
392 {
393     if (!fi)
394     {
395 	return;
396     }
397 
398     /*
399      *   First remove one reference.  If there are still more refs just
400      * return.
401     */
402 
403     fi->refs -= 1;
404     if (fi->refs != 0)
405     {
406 	return;
407     }
408 
409     /*
410      * The previous image is now previous to the next image.
411     */
412 
413     if (fi->next != NULL)
414     {
415 	fi->next->prev = fi->prev;
416     }
417 
418     /*
419      * The next face is now next from the previous face.
420     */
421 
422     if (fi->prev != NULL)
423     {
424 	fi->prev->next = fi->next;
425     }
426 
427     /*
428      * If this was the first image then the next image is
429      * first.
430     */
431 
432     if (fi == TheImages)
433     {
434 	TheImages = fi->next;
435     }
436 
437     /*
438      *    Ok, free the name.
439     */
440 
441     XtFree(fi->file);
442 
443     /*
444      *    Free any label.
445     */
446 
447     XtFree(fi->label);
448 
449     /*
450      *    Free the image data.
451     */
452 
453     if (fi->type != NULL && fi->type->free != NULL)
454     {
455 	fi->type->free(fi->data, fi->type->data);
456     }
457 
458     /*
459      *    Free the struct.
460     */
461 
462     XtFree((void *)fi);
463 }
464 
465 /*
466  *    Retrieve the image pixmap.
467 */
468 
469 Pixmap
FaceImagePixmap(fi)470 FaceImagePixmap(fi)
471 FaceImage	*fi;
472 {
473     Pixmap	pixmap = None;
474 
475     if (fi != NULL && fi->type != NULL && fi->type->pixmap != NULL)
476     {
477 	pixmap = fi->type->pixmap(fi->data, fi->type->data);
478     }
479 
480     return(pixmap);
481 }
482 
483 /*
484  *    Retrieve the image shape mask.
485 */
486 
487 Pixmap
FaceImageShape(fi)488 FaceImageShape(fi)
489 FaceImage	*fi;
490 {
491     Pixmap	shape = None;
492 
493     if (fi != NULL && fi->type != NULL && fi->type->shape != NULL)
494     {
495 	shape = fi->type->shape(fi->data, fi->type->data);
496     }
497 
498     return(shape);
499 }
500 
501 
502 /*
503  * Add a label to an image, if there is no image then create one.
504 */
505 
506 void
FaceImageLabelCreate(item)507 FaceImageLabelCreate(item)
508 MailItem*	item;
509 {
510     FaceImage*	fi;
511     char	*label;
512 
513     label = XtMalloc(strlen(item->user) + strlen(item->host) + 2);
514     sprintf(label, "%s@%s", item->user, item->host);
515 
516     /*
517      *   Ok, create a face image struct.
518     */
519 
520     if (item->image == NULL)
521     {
522 	/*
523 	 *    First see if we already have this image/label.
524 	*/
525 
526 	for (fi = TheImages; fi != NULL; fi = fi->next)
527 	{
528 	    if (strcmp(fi->file, label) == 0)
529 	    {
530 		/*
531 		 * Yep!
532 		*/
533 
534 		XtFree(label);
535 		fi->refs += 1;
536 
537 		item->image = fi;
538 		return;
539 	    }
540 	}
541 
542 	item->image = FaceImageCreate(label, NULL, NULL);
543     }
544 
545     /*
546      * Add the label.
547     */
548 
549     item->image->label = label;
550 }
551 
552 String
StringConcat(s1,s2)553 StringConcat(s1, s2)
554 String	s1;
555 String	s2;
556 {
557     String	s;
558 
559     s = XtMalloc(strlen(s1) + strlen(s2) + 1);
560     sprintf(s, "%s%s", s1, s2);
561 
562     return(s);
563 }
564 
565 void
FaceImageRef(fi)566 FaceImageRef(fi)
567 FaceImage* fi;
568 {
569     if (fi)
570     {
571 	fi->refs += 1;
572     }
573 }
574 
575 void
FaceImageCount(fi)576 FaceImageCount(fi)
577 FaceImage* fi;
578 {
579     if (fi)
580     {
581 	fi->list_count += 1;
582     }
583 }
584 
585 void
FaceImageDecount(fi)586 FaceImageDecount(fi)
587 FaceImage* fi;
588 {
589     if (fi)
590     {
591 	fi->list_count -= 1;
592     }
593 }
594 
595 String
FaceImageLabelGet(fi)596 FaceImageLabelGet(fi)
597 FaceImage* fi;
598 {
599     if (!fi)
600     {
601 	return "NoLabel";
602     }
603 
604     return fi->label;
605 }
606 
607 /*
608  *  Find an image for the given mail item.
609 */
610 
611 void
FaceImageFind(item)612 FaceImageFind(item)
613 MailItem* item;
614 {
615     FaceSearch(item, TheFacesResources.image_search);
616     FaceImageLabelCreate(item);
617 }
618