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