1/*
2
3Copyright 1990, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25 * Copyright 1990 Network Computing Devices;
26 * Portions Copyright 1987 by Digital Equipment Corporation
27 *
28 * Permission to use, copy, modify, distribute, and sell this software and
29 * its documentation for any purpose is hereby granted without fee, provided
30 * that the above copyright notice appear in all copies and that both that
31 * copyright notice and this permission notice appear in supporting
32 * documentation, and that the names of Network Computing Devices, or Digital
33 * not be used in advertising or publicity pertaining to distribution
34 * of the software without specific, written prior permission.
35 *
36 * NETWORK COMPUTING DEVICES, AND DIGITAL DISCLAIM ALL WARRANTIES WITH
37 * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
38 * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL NETWORK COMPUTING DEVICES,
39 * OR DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
40 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
41 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
42 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
43 * THIS SOFTWARE.
44 */
45
46#ifdef HAVE_CONFIG_H
47# include "config.h"
48#endif
49
50#include <X11/fonts/FSlib.h>
51#include <stdio.h>
52#include <X11/Xos.h>
53#include <stdlib.h>
54#include <assert.h>
55
56#ifdef HAVE_BSD_STDLIB_H
57#include <bsd/stdlib.h>
58#endif
59
60#ifndef HAVE_REALLOCARRAY
61#define reallocarray(old, num, size) realloc(old, (num) * (size))
62#endif
63
64#ifndef N_START
65#define N_START 1000		/* Maximum # of fonts to start with */
66#endif
67
68static unsigned int max_output_line_width = 79;
69static unsigned int output_line_padding = 3;
70static unsigned int columns = 0;
71
72#define L_SHORT 0
73#define L_MEDIUM 1
74#define L_LONG 2
75#define L_VERYLONG 3
76
77static Bool sort_output = True;
78static int  long_list = L_SHORT;
79static int  nnames = N_START;
80static unsigned int  font_cnt;
81static int  min_max;
82typedef struct {
83    char       *name;
84    FSXFontInfoHeader *info;
85    FSPropInfo *pi;
86    FSPropOffset *po;
87    unsigned char *pd;
88}           FontList;
89static FontList *font_list;
90
91static FSServer *svr;
92
93static char *program_name;
94
95static void usage (const char *msg, int exitval) _X_NORETURN _X_COLD;
96static void get_list ( const char *pattern );
97static int compare ( const void *f1, const void *f2 );
98static void show_fonts ( void );
99static void print_font_header ( void );
100static void show_font_header ( FontList *list );
101static void copy_number ( char **pp1, char **pp2, char **ep1, char **ep2,
102                          int n1, int n2, char suffix );
103static void show_font_props ( FontList *list );
104
105static void _X_NORETURN _X_COLD
106missing_arg (const char *option)
107{
108    char msg[32];
109
110    snprintf(msg, sizeof(msg), "%s requires an argument", option);
111    usage(msg, 1);
112}
113
114static void
115usage(const char *msg, int exitval)
116{
117    if (msg)
118	fprintf(stderr, "%s: %s\n", program_name, msg);
119    fprintf(stderr, "usage:  %s [-options] [-fn pattern]\n", program_name);
120    fprintf(stderr, "%s", "where options include:\n"
121	    "    -l[l[l]]                 give long info about each font\n"
122	    "    -m                       give character min and max bounds\n"
123	    "    -C                       force columns\n"
124	    "    -1                       force single column\n"
125	    "    -u                       keep output unsorted\n"
126	    "    -w width                 maximum width for multiple columns\n"
127	    "    -n columns               number of columns if multi column\n"
128	    "    -server servername       font server to contact\n"
129	    "    -help                    print this message and exit\n"
130	    "    -version                 print command version and exit\n"
131	    "\n");
132    exit(exitval);
133}
134
135int
136main(int argc, char *argv[])
137{
138    int         argcnt = 0;
139    const char *servername = NULL;
140
141    program_name = argv[0];
142
143    for (int i = 1; i < argc; i++) {
144	if (strncmp(argv[i], "-s", 2) == 0) {
145	    if (++i >= argc)
146		missing_arg("-server");
147	    servername = argv[i];
148	}
149	else {
150	    const char *arg = argv[i];
151	    /* accept single or double dash for -help & -version */
152	    if (arg[0] == '-' && arg[1] == '-') {
153		arg++;
154	    }
155
156	    if (strcmp(arg, "-help") == 0) {
157		usage(NULL, 0);
158	    }
159	    else if (strcmp(arg, "-version") == 0) {
160		printf("%s\n", PACKAGE_STRING);
161		exit(0);
162	    }
163	}
164    }
165
166    if ((svr = FSOpenServer(servername)) == NULL) {
167	if (FSServerName(servername) == NULL) {
168	    usage("no font server defined", 1);
169	}
170	fprintf(stderr, "%s:  unable to open server \"%s\"\n",
171		program_name, FSServerName(servername));
172	exit(0);
173    }
174    /* Handle command line arguments, open display */
175    for (argv++, argc--; argc; argv++, argc--) {
176	if (argv[0][0] == '-') {
177	    int i;
178
179	    if (argcnt > 0)
180		usage(NULL, 1);
181	    for (i = 1; argv[0][i]; i++)
182		switch (argv[0][i]) {
183		case 'l':
184		    long_list++;
185		    break;
186		case 'm':
187		    min_max++;
188		    break;
189		case 'C':
190		    columns = 0;
191		    break;
192		case '1':
193		    columns = 1;
194		    break;
195		case 'f':
196		    if (--argc <= 0)
197			missing_arg("-fn");
198		    argcnt++;
199		    argv++;
200		    get_list(argv[0]);
201		    goto next;
202		case 'w':
203		    if (--argc <= 0)
204			missing_arg("-w");
205		    argv++;
206		    max_output_line_width = (unsigned int) atoi(argv[0]);
207		    goto next;
208		case 'n':
209		    if (--argc <= 0)
210			missing_arg("-n");
211		    argv++;
212		    columns = (unsigned int) atoi(argv[0]);
213		    goto next;
214		case 'u':
215		    sort_output = False;
216		    break;
217		case 's':	/* eat -s */
218		    if (--argc <= 0)
219			missing_arg("-server");
220		    argv++;
221		    goto next;
222		default:
223		    fprintf(stderr, "%s: unrecognized option '%s'\n",
224			    program_name, argv[0]);
225		    usage(NULL, 1);
226		}
227	    if (i == 1)
228		usage(NULL, 1);
229	} else {
230	    argcnt++;
231	    get_list(argv[0]);
232	}
233next:	;
234    }
235    if (argcnt == 0)
236	get_list("*");
237    FSCloseServer(svr);
238    show_fonts();
239    exit(0);
240}
241
242static void
243get_list(const char *pattern)
244{
245    int         available = nnames + 1;
246    char      **fonts;
247    FSXFontInfoHeader **info;
248    FSPropInfo **props;
249    FSPropOffset **offsets;
250    unsigned char **pdata;
251
252    /* Get list of fonts matching pattern */
253    for (;;) {
254
255	if (long_list >= L_MEDIUM)
256	    fonts = FSListFontsWithXInfo(svr,
257	       pattern, nnames, &available, &info, &props, &offsets, &pdata);
258	else
259	    fonts = FSListFonts(svr, pattern, nnames, &available);
260	if (fonts == NULL || available < nnames)
261	    break;
262
263	if (long_list >= L_MEDIUM) {
264	    for (int i = 0; i < available; i++) {
265		FSFree((char *) fonts[i]);
266		FSFree((char *) info[i]);
267		FSFree((char *) props[i]);
268		FSFree((char *) offsets[i]);
269		FSFree((char *) pdata[i]);
270	    }
271	    FSFree((char *) fonts);
272	    FSFree((char *) info);
273	    FSFree((char *) props);
274	    FSFree((char *) offsets);
275	    FSFree((char *) pdata);
276	} else {
277	    FSFreeFontNames(fonts);
278	}
279	nnames = available * 2;
280    }
281
282    if (fonts == NULL) {
283	fprintf(stderr, "%s: pattern \"%s\" unmatched\n",
284		program_name, pattern);
285	return;
286    }
287    else {
288	FontList *old_list = font_list;
289
290	font_list = reallocarray(old_list, (font_cnt + (unsigned) available),
291                                 sizeof(FontList));
292
293	if (font_list == NULL) {
294	    free(old_list);
295	    fprintf(stderr, "%s: unable to allocate %zu bytes for font list\n",
296		    program_name,
297		    (font_cnt + (unsigned) available) * sizeof(FontList));
298	    exit(-1);
299	}
300    }
301    for (int i = 0; i < available; i++) {
302	font_list[font_cnt].name = fonts[i];
303
304	if (long_list >= L_MEDIUM) {
305	    font_list[font_cnt].info = info[i];
306	    font_list[font_cnt].pi = props[i];
307	    font_list[font_cnt].po = offsets[i];
308	    font_list[font_cnt].pd = pdata[i];
309	} else
310	    font_list[font_cnt].info = NULL;
311	font_cnt++;
312    }
313}
314
315static int
316compare(const void *f1, const void *f2)
317{
318    const char *p1 = ((const FontList *)f1)->name,
319               *p2 = ((const FontList *)f2)->name;
320
321    while (*p1 && *p2 && *p1 == *p2)
322	p1++, p2++;
323    return (*p1 - *p2);
324}
325
326static void
327show_fonts(void)
328{
329    if (font_cnt == 0)
330	return;
331
332    /* first sort the output */
333    if (sort_output)
334	qsort(font_list, font_cnt, sizeof(FontList), compare);
335
336    if (long_list > L_MEDIUM) {
337	print_font_header();
338	for (unsigned int i = 0; i < font_cnt; i++) {
339	    show_font_header(&font_list[i]);
340	    show_font_props(&font_list[i]);
341	}
342	return;
343    }
344    if (long_list == L_MEDIUM) {
345	print_font_header();
346
347	for (unsigned int i = 0; i < font_cnt; i++) {
348	    show_font_header(&font_list[i]);
349	}
350
351	return;
352    }
353    if ((columns == 0 && isatty(1)) || columns > 1) {
354	unsigned int max_width = 0,
355	             lines_per_column;
356
357	for (unsigned int i = 0; i < font_cnt; i++) {
358	    unsigned int width = (unsigned int) strlen(font_list[i].name);
359	    if (width > max_width)
360		max_width = width;
361	}
362	if (max_width == 0) {
363	    fprintf(stderr, "all %d fontnames listed are zero length",
364		    font_cnt);
365	    exit(-1);
366	}
367	if (columns == 0) {
368	    if ((max_width * 2) + output_line_padding >
369		    max_output_line_width) {
370		columns = 1;
371	    } else {
372		max_width += output_line_padding;
373		columns = ((max_output_line_width +
374			    output_line_padding) / max_width);
375	    }
376	} else {
377	    max_width += output_line_padding;
378	}
379	if (columns <= 1)
380	    goto single_column;
381
382	if (font_cnt < columns)
383	    columns = font_cnt;
384	lines_per_column = (font_cnt + columns - 1) / columns;
385
386	for (unsigned int i = 0; i < lines_per_column; i++) {
387	    for (unsigned int j = 0; j < columns; j++) {
388		unsigned int index = j * lines_per_column + i;
389		if (index >= font_cnt)
390		    break;
391		if (j + 1 == columns)
392		    printf("%s", font_list[index].name);
393		else
394		    printf("%-*s",
395			   max_width,
396			   font_list[index].name);
397	    }
398	    printf("\n");
399	}
400	return;
401    }
402single_column:
403    for (unsigned int i = 0; i < font_cnt; i++)
404	printf("%s\n", font_list[i].name);
405}
406
407static void
408print_font_header(void)
409{
410    printf("DIR  ");
411    printf("MIN  ");
412    printf("MAX ");
413    printf("EXIST ");
414    printf("DFLT ");
415    printf("ASC ");
416    printf("DESC ");
417    printf("NAME");
418    printf("\n");
419}
420
421static void
422show_font_header(FontList *list)
423{
424    const char        *string;
425    FSXFontInfoHeader *pfh;
426
427    pfh = list->info;
428    if (!pfh) {
429	fprintf(stderr,
430		"%s:  no font information for font \"%s\".\n",
431		program_name, list->name ? list->name : "");
432	return;
433    }
434    if (pfh->draw_direction == LeftToRightDrawDirection)
435	string = "-->";
436    else
437	string = "<--";
438    printf("%-4s", string);
439    if (pfh->char_range.min_char.high == 0
440	    && pfh->char_range.max_char.high == 0) {
441	printf(" %3d ", pfh->char_range.min_char.low);
442	printf(" %3d ", pfh->char_range.max_char.low);
443    } else {
444	printf("*%3d ", pfh->char_range.min_char.high);
445	printf("*%3d ", pfh->char_range.max_char.high);
446    }
447    printf("%5s ", (pfh->flags & FontInfoAllCharsExist) ? "all" : "some");
448    printf("%4d ", (pfh->default_char.high << 8) + pfh->default_char.low);
449    printf("%3d ", pfh->font_ascent);
450    printf("%4d ", pfh->font_descent);
451    printf("%s\n", list->name);
452    if (min_max) {
453	char        min[BUFSIZ],
454	            max[BUFSIZ];
455	char       *pmax = max,
456	           *pmin = min;
457	char       *emin = min + sizeof(min),
458	           *emax = max + sizeof(max);
459
460	copy_number(&pmin, &pmax, &emin, &emax,
461		    pfh->min_bounds.left,
462		    pfh->max_bounds.left, ',');
463	copy_number(&pmin, &pmax, &emin, &emax,
464		    pfh->min_bounds.right,
465		    pfh->max_bounds.right, ',');
466	copy_number(&pmin, &pmax, &emin, &emax,
467		    pfh->min_bounds.width,
468		    pfh->max_bounds.width, ',');
469	copy_number(&pmin, &pmax, &emin, &emax,
470		    pfh->min_bounds.ascent,
471		    pfh->max_bounds.ascent, ',');
472	copy_number(&pmin, &pmax, &emin, &emax,
473		    pfh->min_bounds.descent,
474		    pfh->max_bounds.descent, '\0');
475	printf("     min(l,r,w,a,d) = (%s)\n", min);
476	printf("     max(l,r,w,a,d) = (%s)\n", max);
477    }
478}
479
480#ifndef max
481#define	max(a, b)	((a) > (b) ? (a) : (b))
482#endif
483
484/*
485 * Append string representations of n1 to pp1 and n2 to pp2,
486 * followed by the given suffix character,
487 * but not writing into or past ep1 & ep2, respectively.
488 * The string representations will be padded to the same width.
489 */
490static void
491copy_number(char **pp1, char **pp2, char **ep1, char **ep2, int n1, int n2,
492    char suffix)
493{
494    char       *p1 = *pp1;
495    char       *p2 = *pp2;
496    int         w, w1, w2;
497
498    w1 = snprintf(NULL, 0, "%d", n1);
499    w2 = snprintf(NULL, 0, "%d", n2);
500    w = (int) max(w1, w2);
501    assert(w > 0);
502    snprintf(p1, *ep1 - p1, "%*d%c", w, n1, suffix);
503    snprintf(p2, *ep2 - p2, "%*d%c", w, n2, suffix);
504    *pp1 = p1 + strlen(p1);
505    assert(*pp1 < *ep1);
506    *pp2 = p2 + strlen(p2);
507    assert(*pp2 < *ep2);
508}
509
510static void
511show_font_props(FontList *list)
512{
513    const FSPropInfo *pi = list->pi;
514    const FSPropOffset *po = list->po;
515    const unsigned char *pd = list->pd;
516    unsigned int  num_props = pi->num_offsets;
517
518    for (unsigned int i = 0; i < num_props; i++, po++) {
519	fwrite(pd + po->name.position, 1, po->name.length, stdout);
520	putc('\t', stdout);
521	switch (po->type) {
522	case PropTypeString:
523	    fwrite(pd + po->value.position, 1, po->value.length, stdout);
524	    putc('\n', stdout);
525	    break;
526	case PropTypeUnsigned:
527	    printf("%lu\n", (unsigned long) po->value.position);
528	    break;
529	case PropTypeSigned:
530	    printf("%lu\n", (long) po->value.position);
531	    break;
532	default:
533	    fprintf(stderr, "bogus property\n");
534	    break;
535	}
536
537    }
538}
539