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