1/*
2
3Copyright 1989, 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 */
26
27#ifdef HAVE_CONFIG_H
28# include "config.h"
29#endif
30
31#include <X11/Xlib.h>
32#include <X11/Xutil.h>
33#include <X11/Xos.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <limits.h>
37#include "dsimple.h"
38
39#ifdef HAVE_BSD_STDLIB_H
40#include <bsd/stdlib.h>
41#endif
42
43#ifndef HAVE_REALLOCARRAY
44#define reallocarray(old, num, size) realloc(old, (num) * (size))
45#endif
46
47#define N_START INT_MAX         /* Maximum # of fonts to start with (should
48                                 * always be be > 10000 as modern OSes like
49                                 * Solaris 8 already have more than 9000 XLFD
50                                 * fonts available) */
51
52#define L_SHORT    0
53#define L_MEDIUM   1
54#define L_LONG     2
55#define L_VERYLONG 3
56
57static int  max_output_line_width     = 79;
58static int  output_line_padding       = 3;
59static int  columns                   = 0;
60
61static Bool sort_output               = True;
62static Bool open_instead_of_list      = False;
63static int  long_list                 = L_SHORT;
64static int  nnames                    = N_START;
65static int  font_cnt                  = 0;
66static int  min_max;
67
68typedef struct {
69    const char *name;
70    XFontStruct *info;
71} FontList;
72
73static FontList *font_list = NULL;
74
75/* Local prototypes */
76static void get_list(const char *pattern);
77static int  compare(const void *arg1, const void *arg2);
78static void show_fonts(void);
79static void copy_number(char **pp1, char **pp2, int n1, int n2);
80static int  IgnoreError(Display *disp, XErrorEvent *event);
81static void PrintProperty(XFontProp *prop);
82static void ComputeFontType(XFontStruct *fs);
83static void print_character_metrics(register XFontStruct *info);
84static void do_query_font(Display *dpy, const char *name);
85
86void
87usage(const char *errmsg)
88{
89    if (errmsg != NULL)
90        fprintf(stderr, "%s: %s\n\n", program_name, errmsg);
91
92    fprintf(stderr, "usage:  %s [-options] [-fn pattern]\n%s", program_name,
93            "where options include:\n"
94            "    -l[l[l]]                 give long info about each font\n"
95            "    -m                       give character min and max bounds\n"
96            "    -C                       force columns\n"
97            "    -1                       force single column\n"
98            "    -u                       keep output unsorted\n"
99            "    -o                       use OpenFont/QueryFont instead of ListFonts\n"
100            "    -w width                 maximum width for multiple columns\n"
101            "    -n columns               number of columns if multi column\n"
102            "    -display displayname     X server to contact\n"
103            "    -d displayname           (alias for -display displayname)\n"
104            "    -v                       print program version\n"
105            "\n");
106    Close_Display();
107    exit(EXIT_FAILURE);
108}
109
110int
111main(int argc, char **argv)
112{
113    int argcnt = 0, i;
114
115    INIT_NAME;
116
117    /* Handle command line arguments, open display */
118    Setup_Display_And_Screen(&argc, argv);
119
120    for (argv++, argc--; argc; argv++, argc--) {
121        if (argv[0][0] == '-') {
122            if (argcnt > 0)
123                usage("options may not be specified after font names");
124            for (i = 1; argv[0][i]; i++)
125                switch (argv[0][i]) {
126                case 'l':
127                    long_list++;
128                    break;
129                case 'm':
130                    min_max++;
131                    break;
132                case 'C':
133                    columns = 0;
134                    break;
135                case '1':
136                    columns = 1;
137                    break;
138                case 'f':      /* "-fn" */
139                    if (argv[0][i + 1] != 'n') {
140                        fprintf(stderr, "%s: unrecognized argument %s\n\n",
141                                program_name, argv[0]);
142                        usage(NULL);
143                    }
144                    if (--argc <= 0)
145                        usage("-fn requires an argument");
146                    argcnt++;
147                    argv++;
148                    get_list(argv[0]);
149                    goto next;
150                case 'w':
151                    if (--argc <= 0)
152                        usage("-w requires an argument");
153                    argv++;
154                    max_output_line_width = atoi(argv[0]);
155                    goto next;
156                case 'n':
157                    if (--argc <= 0)
158                        usage("-n requires an argument");
159                    argv++;
160                    columns = atoi(argv[0]);
161                    goto next;
162                case 'o':
163                    open_instead_of_list = True;
164                    break;
165                case 'u':
166                    sort_output = False;
167                    break;
168                case 'v':
169                    puts(PACKAGE_STRING);
170                    exit(0);
171                default:
172                    fprintf(stderr, "%s: unrecognized argument -%c\n\n",
173                            program_name, argv[0][i]);
174                    usage(NULL);
175                    break;
176                }
177            if (i == 1) {
178                fprintf(stderr, "%s: unrecognized argument %s\n\n",
179                        program_name, argv[0]);
180                usage(NULL);
181            }
182        }
183        else {
184            argcnt++;
185            get_list(argv[0]);
186        }
187 next: ;
188    }
189
190    if (argcnt == 0)
191        get_list("*");
192
193    show_fonts();
194
195    Close_Display();
196    return EXIT_SUCCESS;
197}
198
199static void
200get_list(const char *pattern)
201{
202    XFontStruct *info;
203
204    if (open_instead_of_list) {
205        info = XLoadQueryFont(dpy, pattern);
206
207        if (info == NULL) {
208            fprintf(stderr, "%s: pattern \"%s\" unmatched\n",
209                    program_name, pattern);
210            return;
211        }
212
213        font_list = reallocarray(font_list, (font_cnt + 1), sizeof(FontList));
214        if (font_list == NULL)
215            Fatal_Error("Out of memory!");
216        font_list[font_cnt].name = pattern;
217        if (long_list == L_MEDIUM) {
218            font_list[font_cnt].info = info;
219            XUnloadFont(dpy, info->fid);
220        }
221        else {
222            font_list[font_cnt].info = NULL;
223            XFreeFont(dpy, info);
224        }
225        font_cnt++;
226    }
227    else {
228        /* Get list of fonts matching pattern */
229        int available = nnames + 1;
230        char **fonts;
231
232        for (;;) {
233            if (long_list == L_MEDIUM)
234                fonts = XListFontsWithInfo(dpy, pattern, nnames, &available,
235                                           &info);
236            else
237                fonts = XListFonts(dpy, pattern, nnames, &available);
238            if (fonts == NULL) {
239                fprintf(stderr, "%s: pattern \"%s\" unmatched\n",
240                        program_name, pattern);
241                return;
242            }
243            if (available < nnames)
244                break;
245            if (long_list == L_MEDIUM)
246                XFreeFontInfo(fonts, info, available);
247            else
248                XFreeFontNames(fonts);
249            nnames = available * 2;
250        }
251
252        font_list = reallocarray(font_list,
253                                 (font_cnt + available), sizeof(FontList));
254        if (font_list == NULL)
255            Fatal_Error("Out of memory!");
256        for (int i = 0; i < available; i++) {
257            font_list[font_cnt].name = fonts[i];
258            if (long_list == L_MEDIUM)
259                font_list[font_cnt].info = info + i;
260            else
261                font_list[font_cnt].info = NULL;
262
263            font_cnt++;
264        }
265    }
266}
267
268static int
269compare(const void *arg1, const void *arg2)
270{
271    const FontList *f1 = arg1;
272    const FontList *f2 = arg2;
273    const char *p1 = f1->name;
274    const char *p2 = f2->name;
275
276    while (*p1 && *p2 && *p1 == *p2)
277        p1++, p2++;
278    return (*p1 - *p2);
279}
280
281static void
282show_fonts(void)
283{
284    int i;
285
286    if (font_cnt == 0)
287        return;
288
289    /* first sort the output */
290    if (sort_output)
291        qsort(font_list, font_cnt, sizeof(FontList), compare);
292
293    if (long_list > L_MEDIUM) {
294        for (i = 0; i < font_cnt; i++) {
295            do_query_font(dpy, font_list[i].name);
296        }
297        return;
298    }
299
300    if (long_list == L_MEDIUM) {
301        XFontStruct *pfi;
302
303        const char *string;
304
305        printf("DIR  ");
306        printf("MIN  ");
307        printf("MAX ");
308        printf("EXIST ");
309        printf("DFLT ");
310        printf("PROP ");
311        printf("ASC ");
312        printf("DESC ");
313        printf("NAME");
314        printf("\n");
315        for (i = 0; i < font_cnt; i++) {
316            pfi = font_list[i].info;
317            if (!pfi) {
318                fprintf(stderr, "%s:  no font information for font \"%s\".\n",
319                        program_name,
320                        font_list[i].name ? font_list[i].name : "");
321                continue;
322            }
323            switch (pfi->direction) {
324            case FontLeftToRight:
325                string = "-->";
326                break;
327            case FontRightToLeft:
328                string = "<--";
329                break;
330            default:
331                string = "???";
332                break;
333            }
334            printf("%-4s", string);
335            if (pfi->min_byte1 == 0 && pfi->max_byte1 == 0) {
336                printf(" %3d ", pfi->min_char_or_byte2);
337                printf(" %3d ", pfi->max_char_or_byte2);
338            }
339            else {
340                printf("*%3d ", pfi->min_byte1);
341                printf("*%3d ", pfi->max_byte1);
342            }
343            printf("%5s ", pfi->all_chars_exist ? "all" : "some");
344            printf("%4d ", pfi->default_char);
345            printf("%4d ", pfi->n_properties);
346            printf("%3d ", pfi->ascent);
347            printf("%4d ", pfi->descent);
348            printf("%s\n", font_list[i].name);
349            if (min_max) {
350                char min[BUFSIZ], max[BUFSIZ];
351
352                char *pmax = max, *pmin = min;
353
354                strcpy(pmin, "     min(l,r,w,a,d) = (");
355                strcpy(pmax, "     max(l,r,w,a,d) = (");
356                pmin += strlen(pmin);
357                pmax += strlen(pmax);
358
359                copy_number(&pmin, &pmax,
360                            pfi->min_bounds.lbearing,
361                            pfi->max_bounds.lbearing);
362                *pmin++ = *pmax++ = ',';
363                copy_number(&pmin, &pmax,
364                            pfi->min_bounds.rbearing,
365                            pfi->max_bounds.rbearing);
366                *pmin++ = *pmax++ = ',';
367                copy_number(&pmin, &pmax,
368                            pfi->min_bounds.width,
369                            pfi->max_bounds.width);
370                *pmin++ = *pmax++ = ',';
371                copy_number(&pmin, &pmax,
372                            pfi->min_bounds.ascent,
373                            pfi->max_bounds.ascent);
374                *pmin++ = *pmax++ = ',';
375                copy_number(&pmin, &pmax,
376                            pfi->min_bounds.descent,
377                            pfi->max_bounds.descent);
378                *pmin++ = *pmax++ = ')';
379                *pmin = *pmax = '\0';
380                printf("%s\n", min);
381                printf("%s\n", max);
382            }
383        }
384        return;
385    }
386
387    if ((columns == 0 && isatty(1)) || columns > 1) {
388        int width, max_width = 0, lines_per_column, j, index;
389
390        for (i = 0; i < font_cnt; i++) {
391            width = strlen(font_list[i].name);
392            if (width > max_width)
393                max_width = width;
394        }
395        if (max_width == 0)
396            Fatal_Error("all %d fontnames listed are zero length", font_cnt);
397
398        if (columns == 0) {
399            if ((max_width * 2) + output_line_padding >
400                max_output_line_width) {
401                columns = 1;
402            }
403            else {
404                max_width += output_line_padding;
405                columns = ((max_output_line_width +
406                            output_line_padding) / max_width);
407            }
408        }
409        else {
410            max_width += output_line_padding;
411        }
412        if (columns <= 1)
413            goto single_column;
414
415        if (font_cnt < columns)
416            columns = font_cnt;
417        lines_per_column = (font_cnt + columns - 1) / columns;
418
419        for (i = 0; i < lines_per_column; i++) {
420            for (j = 0; j < columns; j++) {
421                index = j * lines_per_column + i;
422                if (index >= font_cnt)
423                    break;
424                if (j + 1 == columns)
425                    printf("%s", font_list[index].name);
426                else
427                    printf("%-*s", max_width, font_list[index].name);
428            }
429            printf("\n");
430        }
431        return;
432    }
433
434 single_column:
435    for (i = 0; i < font_cnt; i++)
436        printf("%s\n", font_list[i].name);
437}
438
439static void
440copy_number(char **pp1, char **pp2, int n1, int n2)
441{
442    char *p1 = *pp1;
443    char *p2 = *pp2;
444    int w;
445
446    sprintf(p1, "%d", n1);
447    sprintf(p2, "%d", n2);
448    w = MAX(strlen(p1), strlen(p2));
449    sprintf(p1, "%*d", w, n1);
450    sprintf(p2, "%*d", w, n2);
451    p1 += strlen(p1);
452    p2 += strlen(p2);
453    *pp1 = p1;
454    *pp2 = p2;
455}
456
457/* ARGSUSED */
458static int
459IgnoreError(Display * disp, XErrorEvent *event)
460{
461    return 0;
462}
463
464static const char *bounds_metrics_title =
465    "width left  right  asc  desc   attr   keysym\n";
466
467#define PrintBounds(_what,_ptr) \
468{   register XCharStruct *p = (_ptr); \
469    printf ("\t%3s\t\t%4d  %4d  %4d  %4d  %4d  0x%04x\n", \
470          (_what), p->width, p->lbearing, \
471          p->rbearing, p->ascent, p->descent, p->attributes); }
472
473static const char *stringValued[] = {   /* values are atoms */
474    /* font name components (see section 3.2 of the XLFD) */
475    "FOUNDRY",
476    "FAMILY_NAME",
477    "WEIGHT_NAME",
478    "SLANT",
479    "SETWIDTH_NAME",
480    "ADD_STYLE_NAME",
481    "SPACING",
482    "CHARSET_REGISTRY",
483    "CHARSET_ENCODING",
484
485    /* other standard X font properties (see section 3.2 of the XLFD) */
486    "FONT",
487    "FACE_NAME",
488    "FULL_NAME",                /* deprecated */
489    "COPYRIGHT",
490    "NOTICE",
491    "FONT_TYPE",
492    "FONT_VERSION",
493    "RASTERIZER_NAME",
494    "RASTERIZER_VERSION",
495
496    /* other registered font properties (see the X.org Registry, sec. 15) */
497    "_ADOBE_POSTSCRIPT_FONTNAME",
498
499    /* unregistered font properties */
500    "CHARSET_COLLECTIONS",
501    "CLASSIFICATION",
502    "DEVICE_FONT_NAME",
503    "FONTNAME_REGISTRY",
504    "MONOSPACED",
505    "QUALITY",
506    "RELATIVE_SET",
507    "STYLE",
508    NULL
509};
510
511static void
512PrintProperty(XFontProp * prop)
513{
514    char *atom, *value;
515    char nosuch[40];
516    int i;
517    XErrorHandler oldhandler = XSetErrorHandler(IgnoreError);
518
519    atom = XGetAtomName(dpy, prop->name);
520    if (!atom) {
521        snprintf(nosuch, sizeof(nosuch), "No such atom = %ld", prop->name);
522        atom = nosuch;
523    }
524    printf("      %s", atom);
525
526    /* Pad out to a column width of 22, but ensure there is always at
527       least one space between property name & value. */
528    for (i = strlen(atom); i < 21; i++)
529        putchar(' ');
530    putchar(' ');
531
532    for (i = 0;; i++) {
533        if (stringValued[i] == NULL) {
534            printf("%ld\n", prop->card32);
535            break;
536        }
537        if (strcmp(stringValued[i], atom) == 0) {
538            value = XGetAtomName(dpy, prop->card32);
539            if (value == NULL)
540                printf("%ld (expected string value)\n", prop->card32);
541            else {
542                printf("%s\n", value);
543                XFree(value);
544            }
545            break;
546        }
547    }
548    if (atom != nosuch)
549        XFree(atom);
550    XSetErrorHandler(oldhandler);
551}
552
553static void
554ComputeFontType(XFontStruct *fs)
555{
556    Bool char_cell = True;
557    const char *reason = NULL;
558    XCharStruct *cs;
559    Atom awatom = XInternAtom(dpy, "AVERAGE_WIDTH", False);
560
561    printf("  font type:\t\t");
562    if (fs->min_bounds.width != fs->max_bounds.width) {
563        printf("Proportional (min and max widths not equal)\n");
564        return;
565    }
566
567    if (awatom) {
568        for (int i = 0; i < fs->n_properties; i++) {
569            if (fs->properties[i].name == awatom &&
570                (fs->max_bounds.width * 10) != fs->properties[i].card32) {
571                char_cell = False;
572                reason = "font width not equal to AVERAGE_WIDTH";
573                break;
574            }
575        }
576    }
577
578    if (fs->per_char) {
579        unsigned int i;
580        for (i = fs->min_char_or_byte2, cs = fs->per_char;
581             i <= fs->max_char_or_byte2; i++, cs++) {
582            if (cs->width == 0)
583                continue;
584            if (cs->width != fs->max_bounds.width) {
585                /* this shouldn't happen since we checked above */
586                printf("Proportional (characters not all the same width)\n");
587                return;
588            }
589            if (char_cell) {
590                if (cs->width < 0) {
591                    if (!(cs->width <= cs->lbearing &&
592                          cs->lbearing <= cs->rbearing &&
593                          cs->rbearing <= 0)) {
594                        char_cell = False;
595                        reason = "ink outside bounding box";
596                    }
597                }
598                else {
599                    if (!(0 <= cs->lbearing &&
600                          cs->lbearing <= cs->rbearing &&
601                          cs->rbearing <= cs->width)) {
602                        char_cell = False;
603                        reason = "ink outside bounding box";
604                    }
605                }
606                if (!(cs->ascent <= fs->ascent &&
607                      cs->descent <= fs->descent)) {
608                    char_cell = False;
609                    reason = "characters not all same ascent or descent";
610                }
611            }
612        }
613    }
614
615    printf("%s", char_cell ? "Character Cell" : "Monospaced");
616    if (reason)
617        printf(" (%s)", reason);
618    printf("\n");
619
620    return;
621}
622
623static void
624print_character_metrics(register XFontStruct *info)
625{
626    register XCharStruct *pc = info->per_char;
627    unsigned int i, j;
628    unsigned n, saven;
629
630    printf("  character metrics:\n");
631    saven = ((info->min_byte1 << 8) | info->min_char_or_byte2);
632    for (j = info->min_byte1; j <= info->max_byte1; j++) {
633        n = saven;
634        for (i = info->min_char_or_byte2; i <= info->max_char_or_byte2; i++) {
635            char *s = XKeysymToString((KeySym) n);
636
637            printf("\t0x%02x%02x (%u)\t%4d  %4d  %4d  %4d  %4d  0x%04x  %s\n",
638                   j, i, n, pc->width, pc->lbearing,
639                   pc->rbearing, pc->ascent, pc->descent, pc->attributes,
640                   s ? s : ".");
641            pc++;
642            n++;
643        }
644        saven += 256;
645    }
646}
647
648static void
649do_query_font(Display *display, const char *name)
650{
651    register int i;
652    register XFontStruct *info = XLoadQueryFont(display, name);
653
654    if (!info) {
655        fprintf(stderr, "%s:  unable to get info about font \"%s\"\n",
656                program_name, name);
657        return;
658    }
659    printf("name:  %s\n", name ? name : "(nil)");
660    printf("  direction:\t\t%s\n", ((info->direction == FontLeftToRight)
661                                    ? "left to right" : "right to left"));
662    printf("  indexing:\t\t%s\n",
663           ((info->min_byte1 == 0 && info->max_byte1 == 0)
664            ? "linear" : "matrix"));
665    printf("  rows:\t\t\t0x%02x thru 0x%02x (%d thru %d)\n",
666           info->min_byte1, info->max_byte1,
667           info->min_byte1, info->max_byte1);
668    printf("  columns:\t\t0x%02x thru 0x%02x (%d thru %d)\n",
669           info->min_char_or_byte2, info->max_char_or_byte2,
670           info->min_char_or_byte2, info->max_char_or_byte2);
671    printf("  all chars exist:\t%s\n",
672           (info->all_chars_exist) ? "yes" : "no");
673    printf("  default char:\t\t0x%04x (%d)\n",
674           info->default_char, info->default_char);
675    printf("  ascent:\t\t%d\n", info->ascent);
676    printf("  descent:\t\t%d\n", info->descent);
677    ComputeFontType(info);
678    printf("  bounds:\t\t%s", bounds_metrics_title);
679    PrintBounds("min", &info->min_bounds);
680    PrintBounds("max", &info->max_bounds);
681    if (info->per_char && long_list >= L_VERYLONG)
682        print_character_metrics(info);
683    printf("  properties:\t\t%d\n", info->n_properties);
684    for (i = 0; i < info->n_properties; i++)
685        PrintProperty(&info->properties[i]);
686    printf("\n");
687
688    XFreeFontInfo(NULL, info, 1);
689}
690