xprop.c revision f9c28e31
1/* 2 3Copyright 1990, 1998 The Open Group 4Copyright (c) 2000 The XFree86 Project, Inc. 5 6Permission to use, copy, modify, distribute, and sell this software and its 7documentation for any purpose is hereby granted without fee, provided that 8the above copyright notice appear in all copies and that both that 9copyright notice and this permission notice appear in supporting 10documentation. 11 12The above copyright notice and this permission notice shall be included 13in all copies or substantial portions of the Software. 14 15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR 19OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21OTHER DEALINGS IN THE SOFTWARE. 22 23Except as contained in this notice, the name of The Open Group shall 24not be used in advertising or otherwise to promote the sale, use or 25other dealings in this Software without prior written authorization 26from The Open Group. 27 28*/ 29 30#include "config.h" 31 32#include <X11/Xlib.h> 33#include <X11/Xos.h> 34#include <X11/Xfuncs.h> 35#include <X11/Xutil.h> 36#include <sys/ioctl.h> 37#include <stdlib.h> 38#include <stdio.h> 39#include <ctype.h> 40#ifdef HAVE_WCHAR_H 41#include <wchar.h> 42#endif 43#ifdef HAVE_WCTYPE_H 44#include <wctype.h> 45#endif 46#include <locale.h> 47#ifdef HAVE_LANGINFO_H 48#include <langinfo.h> 49#endif 50 51#ifndef HAVE_WCTYPE_H 52#define iswprint(x) isprint(x) 53#endif 54 55#include <X11/Xatom.h> 56 57#include "dsimple.h" 58 59#define MAXSTR 500000 60#define MAXELEMENTS 64 61 62#ifndef min 63#define min(a,b) ((a) < (b) ? (a) : (b)) 64#endif 65 66/* isprint() in "C" locale */ 67#define c_isprint(c) ((c) >= 0x20 && (c) < 0x7f) 68 69static int term_width = 144 + 8; 70 71/* 72 * 73 * The Thunk Manager - routines to create, add to, and free thunk lists 74 * 75 */ 76 77typedef struct { 78 int thunk_count; 79 const char *propname; 80 long value; 81 Atom extra_encoding; 82 const char *extra_value; 83 const char *format; 84 const char *dformat; 85} thunk; 86 87static thunk * 88Create_Thunk_List (void) 89{ 90 thunk *tptr; 91 92 tptr = malloc(sizeof(thunk)); 93 if (!tptr) 94 Fatal_Error("Out of memory!"); 95 96 tptr->thunk_count = 0; 97 98 return tptr; 99} 100 101static thunk * 102Add_Thunk (thunk *list, thunk t) 103{ 104 int i; 105 106 i = list->thunk_count; 107 108 list = realloc(list, (i+1)*sizeof(thunk)); 109 if (!list) 110 Fatal_Error("Out of memory!"); 111 112 list[i++] = t; 113 list->thunk_count = i; 114 115 return list; 116} 117 118/* 119 * Misc. routines 120 */ 121 122static int 123Read_Char (FILE *stream) 124{ 125 int c; 126 127 c = getc(stream); 128 if (c == EOF) 129 Fatal_Error("Bad format file: Unexpected EOF."); 130 return c; 131} 132 133static void 134Read_White_Space (FILE *stream) 135{ 136 int c; 137 138 while ((c = getc(stream)) == ' ' || c == '\n' || c == '\t'); 139 ungetc(c, stream); 140} 141 142static char _large_buffer[MAXSTR+10]; 143 144static char * 145Read_Quoted (FILE *stream) 146{ 147 char *ptr; 148 int c, length; 149 150 Read_White_Space(stream); 151 if (Read_Char(stream)!='\'') 152 Fatal_Error("Bad format file format: missing dformat."); 153 154 ptr = _large_buffer; length = MAXSTR; 155 for (;;) { 156 if (length < 0) 157 Fatal_Error("Bad format file format: dformat too long."); 158 c = Read_Char(stream); 159 if (c == (int) '\'') 160 break; 161 ptr++[0] = c; length--; 162 if (c == (int) '\\') { 163 c = Read_Char(stream); 164 if (c == '\n') { 165 ptr--; length++; 166 } else { 167 ptr++[0] = c; length--; 168 } 169 } 170 } 171 ptr++[0] = '\0'; 172 173 ptr = strdup(_large_buffer); 174 if (!ptr) 175 Fatal_Error("Out of memory!"); 176 return ptr; 177} 178 179/* 180 * 181 * Parsing Routines: a group of routines to parse strings into values 182 * 183 * Routines: Parse_Atom, Scan_Long, Skip_Past_Right_Paren, Scan_Octal 184 * 185 * Routines of the form Parse_XXX take a string which is parsed to a value. 186 * Routines of the form Scan_XXX take a string, parse the beginning to a value, 187 * and return the rest of the string. The value is returned via. the last 188 * parameter. All numeric values are longs! 189 * 190 */ 191 192static const char * 193Skip_Digits (const char *string) 194{ 195 while (isdigit((unsigned char) string[0])) string++; 196 return string; 197} 198 199static const char * 200Scan_Long (const char *string, long *value) 201{ 202 if (!isdigit((unsigned char) *string)) 203 Fatal_Error("Bad number: %s.", string); 204 205 *value = atol(string); 206 return Skip_Digits(string); 207} 208 209static const char * 210Scan_Octal (const char *string, unsigned long *value) 211{ 212 if (sscanf(string, "%lo", value)!=1) 213 Fatal_Error("Bad octal number: %s.", string); 214 return Skip_Digits(string); 215} 216 217static Atom 218Parse_Atom (const char *name, int only_if_exists) 219{ 220 /* may return None = 0 */ 221 return XInternAtom(dpy, name, only_if_exists); 222} 223 224static const char * 225Skip_Past_Right_Paren (const char *string) 226{ 227 char c; 228 int nesting = 0; 229 230 while (c = string++[0], c != ')' || nesting) 231 switch (c) { 232 case '\0': 233 Fatal_Error("Missing ')'."); 234 case '(': 235 nesting++; 236 break; 237 case ')': 238 nesting--; 239 break; 240 case '\\': 241 string++; 242 break; 243 } 244 return string; 245} 246 247/* 248 * 249 * Atom to format, dformat mapping Manager 250 * 251 */ 252 253#define D_FORMAT "0x" /* Default format for properties */ 254#define D_DFORMAT " = $0+\n" /* Default display pattern for properties */ 255 256static thunk *_property_formats = NULL; /* Holds mapping */ 257 258static void 259Apply_Default_Formats (const char **format, const char **dformat) 260{ 261 if (!*format) 262 *format = D_FORMAT; 263 if (!*dformat) 264 *dformat = D_DFORMAT; 265} 266 267static void 268Lookup_Formats (Atom atom, const char **format, const char **dformat) 269{ 270 int i; 271 272 if (_property_formats) 273 for (i = _property_formats->thunk_count-1; i >= 0; i--) 274 if (_property_formats[i].value == atom) { 275 if (!*format) 276 *format = _property_formats[i].format; 277 if (!*dformat) 278 *dformat = _property_formats[i].dformat; 279 break; 280 } 281} 282 283static void 284Add_Mapping (Atom atom, const char *format, const char *dformat) 285{ 286 thunk t = {0}; 287 288 if (!_property_formats) 289 _property_formats = Create_Thunk_List(); 290 291 t.value = atom; 292 t.format = format; 293 t.dformat = dformat; 294 295 _property_formats = Add_Thunk(_property_formats, t); 296} 297 298/* 299 * 300 * Setup_Mapping: Routine to setup default atom to format, dformat mapping: 301 * 302 */ 303 304typedef struct _propertyRec { 305 const char * name; 306 Atom atom; 307 const char * format; 308 const char * dformat; 309} propertyRec; 310 311#define ARC_DFORMAT ":\n"\ 312"\t\tarc at $0, $1\n"\ 313"\t\tsize: $2 by $3\n"\ 314"\t\tfrom angle $4 to angle $5\n" 315 316#define RECTANGLE_DFORMAT ":\n"\ 317"\t\tupper left corner: $0, $1\n"\ 318"\t\tsize: $2 by $3\n" 319 320#define RGB_COLOR_MAP_DFORMAT ":\n"\ 321"\t\tcolormap id #: $0\n"\ 322"\t\tred-max: $1\n"\ 323"\t\tred-mult: $2\n"\ 324"\t\tgreen-max: $3\n"\ 325"\t\tgreen-mult: $4\n"\ 326"\t\tblue-max: $5\n"\ 327"\t\tblue-mult: $6\n"\ 328"\t\tbase-pixel: $7\n"\ 329"\t\tvisual id #: $8\n"\ 330"\t\tkill id #: $9\n" 331 332#define WM_HINTS_DFORMAT ":\n"\ 333"?m0(\t\tClient accepts input or input focus: $1\n)"\ 334"?m1(\t\tInitial state is "\ 335"?$2=0(Don't Care State)"\ 336"?$2=1(Normal State)"\ 337"?$2=2(Zoomed State)"\ 338"?$2=3(Iconic State)"\ 339"?$2=4(Inactive State)"\ 340".\n)"\ 341"?m2(\t\tbitmap id # to use for icon: $3\n)"\ 342"?m5(\t\tbitmap id # of mask for icon: $7\n)"\ 343"?m3(\t\twindow id # to use for icon: $4\n)"\ 344"?m4(\t\tstarting position for icon: $5, $6\n)"\ 345"?m6(\t\twindow id # of group leader: $8\n)"\ 346"?m8(\t\tThe urgency hint bit is set\n)" 347 348#define WM_ICON_SIZE_DFORMAT ":\n"\ 349"\t\tminimum icon size: $0 by $1\n"\ 350"\t\tmaximum icon size: $2 by $3\n"\ 351"\t\tincremental size change: $4 by $5\n" 352 353#define WM_SIZE_HINTS_DFORMAT ":\n"\ 354"?m0(\t\tuser specified location: $1, $2\n)"\ 355"?m2(\t\tprogram specified location: $1, $2\n)"\ 356"?m1(\t\tuser specified size: $3 by $4\n)"\ 357"?m3(\t\tprogram specified size: $3 by $4\n)"\ 358"?m4(\t\tprogram specified minimum size: $5 by $6\n)"\ 359"?m5(\t\tprogram specified maximum size: $7 by $8\n)"\ 360"?m6(\t\tprogram specified resize increment: $9 by $10\n)"\ 361"?m7(\t\tprogram specified minimum aspect ratio: $11/$12\n"\ 362"\t\tprogram specified maximum aspect ratio: $13/$14\n)"\ 363"?m8(\t\tprogram specified base size: $15 by $16\n)"\ 364"?m9(\t\twindow gravity: "\ 365"?$17=0(Forget)"\ 366"?$17=1(NorthWest)"\ 367"?$17=2(North)"\ 368"?$17=3(NorthEast)"\ 369"?$17=4(West)"\ 370"?$17=5(Center)"\ 371"?$17=6(East)"\ 372"?$17=7(SouthWest)"\ 373"?$17=8(South)"\ 374"?$17=9(SouthEast)"\ 375"?$17=10(Static)"\ 376"\n)" 377 378#define WM_STATE_DFORMAT ":\n"\ 379"\t\twindow state: ?$0=0(Withdrawn)?$0=1(Normal)?$0=3(Iconic)\n"\ 380"\t\ticon window: $1\n" 381 382static propertyRec windowPropTable[] = { 383 {"ARC", XA_ARC, "16iiccii", ARC_DFORMAT }, 384 {"ATOM", XA_ATOM, "32a", 0 }, 385 {"BITMAP", XA_BITMAP, "32x", ": bitmap id # $0\n" }, 386 {"CARDINAL", XA_CARDINAL, "0c", 0 }, 387 {"COLORMAP", XA_COLORMAP, "32x", ": colormap id # $0\n" }, 388 {"CURSOR", XA_CURSOR, "32x", ": cursor id # $0\n" }, 389 {"DRAWABLE", XA_DRAWABLE, "32x", ": drawable id # $0\n" }, 390 {"FONT", XA_FONT, "32x", ": font id # $0\n" }, 391 {"INTEGER", XA_INTEGER, "0i", 0 }, 392 {"PIXMAP", XA_PIXMAP, "32x", ": pixmap id # $0\n" }, 393 {"POINT", XA_POINT, "16ii", " = $0, $1\n" }, 394 {"RECTANGLE", XA_RECTANGLE, "16iicc", RECTANGLE_DFORMAT }, 395 {"RGB_COLOR_MAP", XA_RGB_COLOR_MAP,"32xcccccccxx",RGB_COLOR_MAP_DFORMAT}, 396 {"STRING", XA_STRING, "8s", 0 }, 397 {"UTF8_STRING", 0, "8u", 0 }, 398 {"WINDOW", XA_WINDOW, "32x", ": window id # $0+\n" }, 399 {"VISUALID", XA_VISUALID, "32x", ": visual id # $0\n" }, 400 {"WM_COLORMAP_WINDOWS", 0, "32x", ": window id # $0+\n"}, 401 {"WM_COMMAND", XA_WM_COMMAND, "8s", " = { $0+ }\n" }, 402 {"WM_HINTS", XA_WM_HINTS, "32mbcxxiixx", WM_HINTS_DFORMAT }, 403 {"WM_ICON_NAME", XA_WM_ICON_NAME, "8t", 0 }, 404 {"WM_ICON_SIZE", XA_WM_ICON_SIZE, "32cccccc", WM_ICON_SIZE_DFORMAT}, 405 {"WM_NAME", XA_WM_NAME, "8t", 0 }, 406 {"WM_PROTOCOLS", 0, "32a", ": protocols $0+\n"}, 407 {"WM_SIZE_HINTS", XA_WM_SIZE_HINTS,"32mii", WM_SIZE_HINTS_DFORMAT }, 408 {"_NET_WM_ICON", 0, "32o", 0 }, 409 {"WM_STATE", 0, "32cx", WM_STATE_DFORMAT} 410}; 411#undef ARC_DFORMAT 412#undef RECTANGLE_DFORMAT 413#undef RGB_COLOR_MAP_DFORMAT 414#undef WM_ICON_SIZE_DFORMAT 415#undef WM_HINTS_DFORMAT 416#undef WM_SIZE_HINTS_DFORMAT 417#undef WM_STATE_DFORMAT 418 419/* 420 * Font-specific mapping of property names to types: 421 */ 422static propertyRec fontPropTable[] = { 423 424 /* XLFD name properties */ 425 426 { "FOUNDRY", 0, "32a", 0 }, 427 { "FAMILY_NAME", XA_FAMILY_NAME, "32a", 0 }, 428 { "WEIGHT_NAME", 0, "32a", 0 }, 429 { "SLANT", 0, "32a", 0 }, 430 { "SETWIDTH_NAME", 0, "32a", 0 }, 431 { "ADD_STYLE_NAME", 0, "32a", 0 }, 432 { "PIXEL_SIZE", 0, "32c", 0 }, 433 { "POINT_SIZE", XA_POINT_SIZE, "32c", 0 }, 434 { "RESOLUTION_X", 0, "32c", 0 }, 435 { "RESOLUTION_Y", 0, "32c", 0 }, 436 { "SPACING", 0, "32a", 0 }, 437 { "AVERAGE_WIDTH", 0, "32c", 0 }, 438 { "CHARSET_REGISTRY", 0, "32a", 0 }, 439 { "CHARSET_ENCODING", 0, "32a", 0 }, 440 441 /* other font properties referenced in the XLFD */ 442 443 { "QUAD_WIDTH", XA_QUAD_WIDTH, "32i", 0 }, 444 { "RESOLUTION", XA_RESOLUTION, "32c", 0 }, 445 { "MIN_SPACE", XA_MIN_SPACE, "32c", 0 }, 446 { "NORM_SPACE", XA_NORM_SPACE, "32c", 0 }, 447 { "MAX_SPACE", XA_MAX_SPACE, "32c", 0 }, 448 { "END_SPACE", XA_END_SPACE, "32c", 0 }, 449 { "SUPERSCRIPT_X", XA_SUPERSCRIPT_X, "32i", 0 }, 450 { "SUPERSCRIPT_Y", XA_SUPERSCRIPT_Y, "32i", 0 }, 451 { "SUBSCRIPT_X", XA_SUBSCRIPT_X, "32i", 0 }, 452 { "SUBSCRIPT_Y", XA_SUBSCRIPT_Y, "32i", 0 }, 453 { "UNDERLINE_POSITION", XA_UNDERLINE_POSITION, "32i", 0 }, 454 { "UNDERLINE_THICKNESS", XA_UNDERLINE_THICKNESS, "32i", 0 }, 455 { "STRIKEOUT_ASCENT", XA_STRIKEOUT_ASCENT, "32i", 0 }, 456 { "STRIKEOUT_DESCENT", XA_STRIKEOUT_DESCENT, "32i", 0 }, 457 { "ITALIC_ANGLE", XA_ITALIC_ANGLE, "32i", 0 }, 458 { "X_HEIGHT", XA_X_HEIGHT, "32i", 0 }, 459 { "WEIGHT", XA_WEIGHT, "32i", 0 }, 460 { "FACE_NAME", 0, "32a", 0 }, 461 { "COPYRIGHT", XA_COPYRIGHT, "32a", 0 }, 462 { "AVG_CAPITAL_WIDTH", 0, "32i", 0 }, 463 { "AVG_LOWERCASE_WIDTH", 0, "32i", 0 }, 464 { "RELATIVE_SETWIDTH", 0, "32c", 0 }, 465 { "RELATIVE_WEIGHT", 0, "32c", 0 }, 466 { "CAP_HEIGHT", XA_CAP_HEIGHT, "32c", 0 }, 467 { "SUPERSCRIPT_SIZE", 0, "32c", 0 }, 468 { "FIGURE_WIDTH", 0, "32i", 0 }, 469 { "SUBSCRIPT_SIZE", 0, "32c", 0 }, 470 { "SMALL_CAP_SIZE", 0, "32i", 0 }, 471 { "NOTICE", XA_NOTICE, "32a", 0 }, 472 { "DESTINATION", 0, "32c", 0 }, 473 474 /* other font properties */ 475 476 { "FONT", XA_FONT, "32a", 0 }, 477 { "FONT_NAME", XA_FONT_NAME, "32a", 0 }, 478}; 479 480static int XpropMode; 481#define XpropWindowProperties 0 482#define XpropFontProperties 1 483 484static void 485Setup_Mapping (void) 486{ 487 int n; 488 propertyRec *p; 489 490 if (XpropMode == XpropWindowProperties) { 491 n = sizeof(windowPropTable) / sizeof(propertyRec); 492 p = windowPropTable; 493 } else { 494 n = sizeof (fontPropTable) / sizeof (propertyRec); 495 p = fontPropTable; 496 } 497 for ( ; --n >= 0; p++) { 498 if (! p->atom) { 499 p->atom = XInternAtom(dpy, p->name, True); 500 if (p->atom == None) 501 continue; 502 } 503 Add_Mapping(p->atom, p->format, p->dformat); 504 } 505} 506 507static const char * 508GetAtomName (Atom atom) 509{ 510 int n; 511 propertyRec *p; 512 513 if (XpropMode == XpropWindowProperties) { 514 n = sizeof(windowPropTable) / sizeof(propertyRec); 515 p = windowPropTable; 516 } else { 517 n = sizeof (fontPropTable) / sizeof (propertyRec); 518 p = fontPropTable; 519 } 520 for ( ; --n >= 0; p++) 521 if (p->atom == atom) 522 return p->name; 523 524 return NULL; 525} 526 527/* 528 * Read_Mapping: routine to read in additional mappings from a stream 529 * already open for reading. 530 */ 531 532static void 533Read_Mappings (FILE *stream) 534{ 535 char format_buffer[100]; 536 char name[1000]; 537 const char *dformat, *format; 538 int count, c; 539 Atom atom; 540 541 while ((count = fscanf(stream," %990s %90s ",name,format_buffer)) != EOF) { 542 if (count != 2) 543 Fatal_Error("Bad format file format."); 544 545 atom = Parse_Atom(name, False); 546 format = strdup(format_buffer); 547 if (!format) 548 Fatal_Error("Out of memory!"); 549 550 Read_White_Space(stream); 551 dformat = D_DFORMAT; 552 c = getc(stream); 553 ungetc(c, stream); 554 if (c == (int) '\'') 555 dformat = Read_Quoted(stream); 556 557 Add_Mapping(atom, format, dformat); 558 } 559} 560 561/* 562 * 563 * Formatting Routines: a group of routines to translate from various 564 * values to a static read-only string useful for output. 565 * 566 * Routines: Format_Hex, Format_Unsigned, Format_Signed, Format_Atom, 567 * Format_Mask_Word, Format_Bool, Format_String, Format_Len_String. 568 * 569 * All of the above routines take a long except for Format_String and 570 * Format_Len_String. 571 * 572 */ 573static char _formatting_buffer[MAXSTR+100]; 574static char _formatting_buffer2[21]; 575 576static const char * 577Format_Hex (long wrd) 578{ 579 snprintf(_formatting_buffer2, sizeof(_formatting_buffer2), "0x%lx", wrd); 580 return _formatting_buffer2; 581} 582 583static const char * 584Format_Unsigned (long wrd) 585{ 586 snprintf(_formatting_buffer2, sizeof(_formatting_buffer2), "%lu", wrd); 587 return _formatting_buffer2; 588} 589 590static const char * 591Format_Signed (long wrd) 592{ 593 snprintf(_formatting_buffer2, sizeof(_formatting_buffer2), "%ld", wrd); 594 return _formatting_buffer2; 595} 596 597/*ARGSUSED*/ 598static int 599ignore_errors (Display *dpy, XErrorEvent *ev) 600{ 601 return 0; 602} 603 604static const char * 605Format_Atom (Atom atom) 606{ 607 const char *found; 608 char *name; 609 XErrorHandler handler; 610 611 if ((found = GetAtomName(atom)) != NULL) 612 return found; 613 614 handler = XSetErrorHandler (ignore_errors); 615 name = XGetAtomName(dpy, atom); 616 XSetErrorHandler(handler); 617 if (! name) 618 snprintf(_formatting_buffer, sizeof(_formatting_buffer), 619 "undefined atom # 0x%lx", atom); 620 else { 621 int namelen = strlen(name); 622 if (namelen > MAXSTR) namelen = MAXSTR; 623 memcpy(_formatting_buffer, name, namelen); 624 _formatting_buffer[namelen] = '\0'; 625 XFree(name); 626 } 627 return _formatting_buffer; 628} 629 630static const char * 631Format_Mask_Word (long wrd) 632{ 633 long bit_mask, bit; 634 int seen = 0; 635 636 strcpy(_formatting_buffer, "{MASK: "); 637 for (bit=0, bit_mask=1; bit <= sizeof(long)*8; bit++, bit_mask<<=1) { 638 if (bit_mask & wrd) { 639 if (seen) { 640 strcat(_formatting_buffer, ", "); 641 } 642 seen = 1; 643 strcat(_formatting_buffer, Format_Unsigned(bit)); 644 } 645 } 646 strcat(_formatting_buffer, "}"); 647 648 return _formatting_buffer; 649} 650 651static const char * 652Format_Bool (long value) 653{ 654 if (!value) 655 return "False"; 656 657 return "True"; 658} 659 660static char *_buf_ptr; 661static int _buf_len; 662 663static void 664_put_char (char c) 665{ 666 if (_buf_len <= 0) { 667 _buf_ptr[0] = '\0'; 668 return; 669 } 670 _buf_ptr++[0] = c; 671 _buf_len--; 672} 673 674static void 675_format_char (char c, int unicode) 676{ 677 switch (c) { 678 case '\\': 679 case '\"': 680 _put_char('\\'); 681 _put_char(c); 682 break; 683 case '\n': 684 _put_char('\\'); 685 _put_char('n'); 686 break; 687 case '\t': 688 _put_char('\\'); 689 _put_char('t'); 690 break; 691 default: 692 if (!c_isprint(c)) { 693 if (unicode && (c & 0x80)) { 694 _put_char(c); 695 } else { 696 _put_char('\\'); 697 snprintf(_buf_ptr, _buf_len, "%03o", (unsigned char) c); 698 _buf_ptr += 3; 699 _buf_len -= 3; 700 } 701 } else 702 _put_char(c); 703 } 704} 705 706static const char * 707Format_String (const char *string, int unicode) 708{ 709 char c; 710 711 _buf_ptr = _formatting_buffer; 712 _buf_len = MAXSTR; 713 _put_char('\"'); 714 715 while ((c = string++[0])) 716 _format_char(c, unicode); 717 718 *_buf_ptr++ = '"'; 719 *_buf_ptr++ = '\0'; 720 return _formatting_buffer; 721} 722 723static const char * 724Format_Len_String (const char *string, int len, int unicode) 725{ 726 char *data; 727 const char *result; 728 729 data = malloc(len+1); 730 if (!data) 731 Fatal_Error("Out of memory!"); 732 733 memcpy(data, string, len); 734 data[len] = '\0'; 735 736 result = Format_String(data, unicode); 737 free(data); 738 739 return result; 740} 741 742static int 743is_utf8_locale (void) 744{ 745#ifdef HAVE_LANGINFO_H 746 char *charmap = nl_langinfo (CODESET); 747 748 return charmap && strcmp (charmap, "UTF-8") == 0; 749#else 750 return 0; 751#endif 752} 753 754static int 755is_truecolor_term (void) 756{ 757 char *colorterm = getenv( "COLORTERM" ); 758 759 if (colorterm && !strcmp(colorterm,"truecolor")) 760 return 1; 761 762 return 0; 763} 764 765static const char * 766Format_Icons (const unsigned long *icon, int len) 767{ 768 static char *result = NULL; 769 char *tail = NULL; 770 int alloced; 771 const unsigned long *end = icon + len / sizeof (unsigned long); 772 773 free(result); 774 result = NULL; 775 776 alloced = 0; 777 778 while (icon < end) 779 { 780 unsigned long width, height, display_width; 781 unsigned int icon_pixel_bytes; 782 unsigned int icon_line_bytes; 783 int w, h; 784 int offset; 785 786 width = *icon++; 787 height = *icon++; 788 display_width = width * 2; /* Two characters per icon pixel. */ 789 790 icon_pixel_bytes = 1; 791 if (is_truecolor_term()) 792 icon_pixel_bytes = 25; /* 16 control characters, and up to 9 chars of RGB. */ 793 else if (is_utf8_locale()) 794 icon_pixel_bytes = 3; /* Up to 3 bytes per character in that mode. */ 795 796 /* Initial tab, pixels, and newline. */ 797 icon_line_bytes = 8 + display_width * icon_pixel_bytes + 1; 798 799 offset = (tail - result); 800 801 alloced += 80; /* For the header, final newline, color reset */ 802 alloced += icon_line_bytes * height; /* For the rows */ 803 804 result = realloc (result, alloced); 805 if (!result) 806 Fatal_Error("Out of memory!"); 807 tail = &result[offset]; 808 809 if (end - icon < width * height) 810 break; 811 812 tail += sprintf (tail, "\tIcon (%lu x %lu):\n", width, height); 813 814 if ((display_width + 8) > term_width || height > 144) 815 { 816 tail += sprintf (tail, "\t(not shown)\n"); 817 icon += width * height; 818 continue; 819 } 820 821 for (h = 0; h < height; ++h) 822 { 823 tail += sprintf (tail, "\t"); 824 825 for (w = 0; w < width; ++w) 826 { 827 unsigned char a, r, g, b; 828 unsigned long pixel = *icon++; 829 unsigned long brightness; 830 831 a = (pixel & 0xff000000) >> 24; 832 r = (pixel & 0x00ff0000) >> 16; 833 g = (pixel & 0x0000ff00) >> 8; 834 b = (pixel & 0x000000ff); 835 836 brightness = 837 (a / 255.0) * (1000 - ((299 * (r / 255.0)) + 838 (587 * (g / 255.0)) + 839 (114 * (b / 255.0)))); 840 841 if (is_truecolor_term()) 842 { 843 float opacity = a / 255.0; 844 845 r = r * opacity; 846 g = g * opacity; 847 b = b * opacity; 848 849 tail += sprintf (tail, "\033[38;2;%d;%d;%dm\342\226\210\342\226\210", r, g, b ); 850 } 851 else if (is_utf8_locale()) 852 { 853 static const char palette[][4] = 854 { 855 " ", 856 "\342\226\221", /* 25% */ 857 "\342\226\222", /* 50% */ 858 "\342\226\223", /* 75% */ 859 "\342\226\210", /* 100% */ 860 }; 861 int idx; 862 863 idx = (brightness * ((sizeof (palette)/sizeof(palette[0])) - 1)) / 1000; 864 865 tail += sprintf (tail, "%s%s", palette[idx], palette[idx]); 866 } 867 else 868 { 869 static const char palette[] = 870 " .'`,^:\";~-_+<>i!lI?/\\|()1{}[]rcvunxzjftLCJUYXZO0Qoahkbdpqwm*WMB8&%$#@"; 871 int idx; 872 873 idx = (brightness * (sizeof(palette) - 2)) / 1000; 874 875 *tail++ = palette[idx]; 876 *tail++ = palette[idx]; 877 } 878 } 879 880 tail += sprintf (tail, "\n"); 881 } 882 883 /* Reset colors. */ 884 if (is_truecolor_term()) 885 tail += sprintf (tail, "\033[0m"); 886 887 tail += sprintf (tail, "\n"); 888 } 889 890 return result; 891} 892 893static const char * 894Format_Len_Text (const char *string, int len, Atom encoding) 895{ 896 XTextProperty textprop; 897 char **list, **start_list; 898 int count; 899 900 /* Try to convert to local encoding. */ 901 textprop.encoding = encoding; 902 textprop.format = 8; 903 textprop.value = (unsigned char *) string; 904 textprop.nitems = len; 905 if (XmbTextPropertyToTextList(dpy, &textprop, &start_list, &count) == Success) { 906 list = start_list; 907 _buf_ptr = _formatting_buffer; 908 _buf_len = MAXSTR; 909 *_buf_ptr++ = '"'; 910 while (count > 0) { 911 string = *list++; 912 len = strlen(string); 913 while (len > 0) { 914 wchar_t wc; 915 int n = mbtowc(&wc, string, len); 916 if (n > 0 && iswprint(wc)) { 917 if (_buf_len >= n) { 918 memcpy(_buf_ptr, string, n); 919 _buf_ptr += n; 920 _buf_len -= n; 921 } 922 string += n; 923 len -= n; 924 } else { 925 _put_char('\\'); 926 snprintf(_buf_ptr, _buf_len, "%03o", (unsigned char) *string); 927 _buf_ptr += 3; 928 _buf_len -= 3; 929 string++; 930 len--; 931 } 932 } 933 count--; 934 if (count > 0) { 935 snprintf(_buf_ptr, _buf_len, "\\000"); 936 _buf_ptr += 4; 937 _buf_len -= 4; 938 } 939 } 940 XFreeStringList(start_list); 941 *_buf_ptr++ = '"'; 942 *_buf_ptr++ = '\0'; 943 return _formatting_buffer; 944 } else 945 return Format_Len_String(string, len, 0); 946} 947 948/* 949 * Validate a string as UTF-8 encoded according to RFC 3629 950 * 951 * Simply, a unicode code point (up to 21-bits long) is encoded as follows: 952 * 953 * Char. number range | UTF-8 octet sequence 954 * (hexadecimal) | (binary) 955 * --------------------+--------------------------------------------- 956 * 0000 0000-0000 007F | 0xxxxxxx 957 * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 958 * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 959 * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 960 * 961 * Validation is done left-to-right, and an error condition, if any, refers to 962 * only the left-most problem in the string. 963 * 964 * Return values: 965 * UTF8_VALID: Valid UTF-8 encoded string 966 * UTF8_OVERLONG: Using more bytes than needed for a code point 967 * UTF8_SHORT_TAIL: Not enough bytes in a multi-byte sequence 968 * UTF8_LONG_TAIL: Too many bytes in a multi-byte sequence 969 * UTF8_FORBIDDEN_VALUE: Forbidden prefix or code point outside 0x10FFFF 970 */ 971#define UTF8_VALID 0 972#define UTF8_FORBIDDEN_VALUE 1 973#define UTF8_OVERLONG 2 974#define UTF8_SHORT_TAIL 3 975#define UTF8_LONG_TAIL 4 976static int 977is_valid_utf8 (const char *string, int len) 978{ 979 unsigned long codepoint = 0; 980 int rem, i; 981 unsigned char c; 982 983 rem = 0; 984 for (i = 0; i < len; i++) { 985 c = (unsigned char) string[i]; 986 987 /* Order of type check: 988 * - Single byte code point 989 * - Non-starting byte of multi-byte sequence 990 * - Start of 2-byte sequence 991 * - Start of 3-byte sequence 992 * - Start of 4-byte sequence 993 */ 994 if (!(c & 0x80)) { 995 if (rem > 0) return UTF8_SHORT_TAIL; 996 rem = 0; 997 codepoint = c; 998 } else if ((c & 0xC0) == 0x80) { 999 if (rem == 0) return UTF8_LONG_TAIL; 1000 rem--; 1001 codepoint |= (c & 0x3F) << (rem * 6); 1002 if (codepoint == 0) return UTF8_OVERLONG; 1003 } else if ((c & 0xE0) == 0xC0) { 1004 if (rem > 0) return UTF8_SHORT_TAIL; 1005 rem = 1; 1006 codepoint = (c & 0x1F) << 6; 1007 if (codepoint == 0) return UTF8_OVERLONG; 1008 } else if ((c & 0xF0) == 0xE0) { 1009 if (rem > 0) return UTF8_SHORT_TAIL; 1010 rem = 2; 1011 codepoint = (c & 0x0F) << 12; 1012 } else if ((c & 0xF8) == 0xF0) { 1013 if (rem > 0) return UTF8_SHORT_TAIL; 1014 rem = 3; 1015 codepoint = (c & 0x07) << 18; 1016 if (codepoint > 0x10FFFF) return UTF8_FORBIDDEN_VALUE; 1017 } else 1018 return UTF8_FORBIDDEN_VALUE; 1019 } 1020 1021 return UTF8_VALID; 1022} 1023 1024static const char * 1025Format_Len_Unicode (const char *string, int len) 1026{ 1027 char *data; 1028 const char *result, *error; 1029 1030 int validity = is_valid_utf8(string, len); 1031 1032 if (validity != UTF8_VALID) { 1033 switch (validity) { 1034 case UTF8_FORBIDDEN_VALUE: 1035 error = "<Invalid UTF-8 string: Forbidden value> "; break; 1036 case UTF8_OVERLONG: 1037 error = "<Invalid UTF-8 string: Overlong encoding> "; break; 1038 case UTF8_SHORT_TAIL: 1039 error = "<Invalid UTF-8 string: Tail too short> "; break; 1040 case UTF8_LONG_TAIL: 1041 error = "<Invalid UTF-8 string: Tail too long> "; break; 1042 default: 1043 error = "<Invalid UTF-8 string: Unknown error>"; break; 1044 } 1045 1046 result = Format_Len_String(string, len, 0); 1047 /* result is stored in _formatting_buffer, so make a temporary 1048 copy before we overwrite _formatting_buffer with error */ 1049 data = strdup(result); 1050 if (!data) 1051 Fatal_Error("Out of memory!"); 1052 1053 memcpy(_formatting_buffer, error, strlen(error)+1); 1054 strcat(_formatting_buffer, data); 1055 free(data); 1056 1057 return _formatting_buffer; 1058 } 1059 1060 return Format_Len_String(string, len, is_utf8_locale()); 1061} 1062 1063/* 1064 * 1065 * The Format Manager: a group of routines to manage "formats" 1066 * 1067 */ 1068 1069static int 1070Is_A_Format (const char *string) 1071{ 1072 return isdigit((unsigned char) string[0]); 1073} 1074 1075static int 1076Get_Format_Size (const char *format) 1077{ 1078 long size; 1079 1080 Scan_Long(format, &size); 1081 1082 /* Check for legal sizes */ 1083 if (size != 0 && size != 8 && size != 16 && size != 32) 1084 Fatal_Error("bad format: %s", format); 1085 1086 return (int) size; 1087} 1088 1089static char 1090Get_Format_Char (const char *format, int i) 1091{ 1092 long size; 1093 1094 /* Remove # at front of format */ 1095 format = Scan_Long(format, &size); 1096 if (!*format) 1097 Fatal_Error("bad format: %s", format); 1098 1099 /* Last character repeats forever... */ 1100 if (i >= (int)strlen(format)) 1101 i = strlen(format)-1; 1102 1103 return format[i]; 1104} 1105 1106static const char * 1107Format_Thunk (thunk t, char format_char) 1108{ 1109 long value; 1110 value = t.value; 1111 1112 switch (format_char) { 1113 case 's': 1114 return Format_Len_String(t.extra_value, (int)t.value, 0); 1115 case 'u': 1116 return Format_Len_Unicode(t.extra_value, (int)t.value); 1117 case 't': 1118 return Format_Len_Text(t.extra_value, (int)t.value, t.extra_encoding); 1119 case 'x': 1120 return Format_Hex(value); 1121 case 'c': 1122 return Format_Unsigned(value); 1123 case 'i': 1124 return Format_Signed(value); 1125 case 'b': 1126 return Format_Bool(value); 1127 case 'm': 1128 return Format_Mask_Word(value); 1129 case 'a': 1130 return Format_Atom(value); 1131 case 'o': 1132 return Format_Icons((const unsigned long *)t.extra_value, (int)t.value); 1133 default: 1134 Fatal_Error("bad format character: %c", format_char); 1135 } 1136} 1137 1138static const char * 1139Format_Thunk_I (thunk *thunks, const char *format, int i) 1140{ 1141 if (i >= thunks->thunk_count) 1142 return "<field not available>"; 1143 1144 return Format_Thunk(thunks[i], Get_Format_Char(format, i)); 1145} 1146 1147static long 1148Mask_Word (thunk *thunks, const char *format) 1149{ 1150 int j; 1151 1152 for (j = 0; j < (int)strlen(format); j++) 1153 if (Get_Format_Char(format, j) == 'm') 1154 return thunks[j].value; 1155 return 0; 1156} 1157 1158/* 1159 * 1160 * The Display Format Manager: 1161 * 1162 */ 1163 1164static int 1165Is_A_DFormat (const char *string) 1166{ 1167 return string[0] && string[0] != '-' 1168 && !(isalpha((unsigned char) string[0]) || string[0] == '_'); 1169} 1170 1171static const char * 1172Handle_Backslash (const char *dformat) 1173{ 1174 char c; 1175 unsigned long i; 1176 1177 if (!(c = *(dformat++))) 1178 return dformat; 1179 1180 switch (c) { 1181 case 'n': 1182 putchar('\n'); 1183 break; 1184 case 't': 1185 putchar('\t'); 1186 break; 1187 case '0': 1188 case '1': 1189 case '2': 1190 case '3': 1191 case '4': 1192 case '5': 1193 case '6': 1194 case '7': 1195 dformat = Scan_Octal(dformat, &i); 1196 putchar((int) i); 1197 break; 1198 default: 1199 putchar(c); 1200 break; 1201 } 1202 return dformat; 1203} 1204 1205static const char * 1206Handle_Dollar_sign (const char *dformat, thunk *thunks, const char *format) 1207{ 1208 long i; 1209 1210 dformat = Scan_Long(dformat, &i); 1211 1212 if (dformat[0] == '+') { 1213 int seen = 0; 1214 dformat++; 1215 for (; i < thunks->thunk_count; i++) { 1216 if (seen) 1217 printf(", "); 1218 seen = 1; 1219 printf("%s", Format_Thunk_I(thunks, format, (int) i)); 1220 } 1221 } else 1222 printf("%s", Format_Thunk_I(thunks, format, (int) i)); 1223 1224 return dformat; 1225} 1226 1227static int 1228Mask_Bit_I (thunk *thunks, const char *format, int i) 1229{ 1230 long value; 1231 1232 value = Mask_Word(thunks, format); 1233 1234 value = value & (1L<<i); 1235 if (value) 1236 value = 1; 1237 return value; 1238} 1239 1240static const char * 1241Scan_Term (const char *string, thunk *thunks, const char *format, long *value) 1242{ 1243 long i; 1244 1245 *value = 0; 1246 1247 if (isdigit((unsigned char) *string)) 1248 string = Scan_Long(string, value); 1249 else if (*string == '$') { 1250 string = Scan_Long(++string, &i); 1251 if (i >= thunks->thunk_count) 1252 i = thunks->thunk_count; 1253 *value = thunks[i].value; 1254 } else if (*string == 'm') { 1255 string = Scan_Long(++string, &i); 1256 *value = Mask_Bit_I(thunks, format, (int) i); 1257 } else 1258 Fatal_Error("Bad term: %s.", string); 1259 1260 return string; 1261} 1262 1263static const char * 1264Scan_Exp (const char *string, thunk *thunks, const char *format, long *value) 1265{ 1266 long temp; 1267 1268 if (string[0] == '(') { 1269 string = Scan_Exp(++string, thunks, format, value); 1270 if (string[0]!=')') 1271 Fatal_Error("Missing ')'"); 1272 return ++string; 1273 } 1274 if (string[0] == '!') { 1275 string = Scan_Exp(++string, thunks, format, value); 1276 *value = !*value; 1277 return string; 1278 } 1279 1280 string = Scan_Term(string, thunks, format, value); 1281 1282 if (string[0] == '=') { 1283 string = Scan_Exp(++string, thunks, format, &temp); 1284 *value = *value == temp; 1285 } 1286 1287 return string; 1288} 1289 1290static const char * 1291Handle_Question_Mark (const char *dformat, thunk *thunks, const char *format) 1292{ 1293 long true; 1294 1295 dformat = Scan_Exp(dformat, thunks, format, &true); 1296 1297 if (*dformat != '(') 1298 Fatal_Error("Bad conditional: '(' expected: %s.", dformat); 1299 ++dformat; 1300 1301 if (!true) 1302 dformat = Skip_Past_Right_Paren(dformat); 1303 1304 return dformat; 1305} 1306 1307static void 1308Display_Property (thunk *thunks, const char *dformat, const char *format) 1309{ 1310 char c; 1311 1312 while ((c = *(dformat++))) 1313 switch (c) { 1314 case ')': 1315 continue; 1316 case '\\': 1317 dformat = Handle_Backslash(dformat); 1318 continue; 1319 case '$': 1320 dformat = Handle_Dollar_sign(dformat, thunks, format); 1321 continue; 1322 case '?': 1323 dformat = Handle_Question_Mark(dformat, thunks, format); 1324 continue; 1325 default: 1326 putchar(c); 1327 continue; 1328 } 1329} 1330 1331/* 1332 * 1333 * Routines to convert property data to thunks 1334 * 1335 */ 1336 1337static long 1338Extract_Value (const char **pointer, int *length, int size, int signedp) 1339{ 1340 long value; 1341 1342 switch (size) { 1343 case 8: 1344 if (signedp) 1345 value = * (const signed char *) *pointer; 1346 else 1347 value = * (const unsigned char *) *pointer; 1348 *pointer += 1; 1349 *length -= 1; 1350 break; 1351 case 16: 1352 if (signedp) 1353 value = * (const short *) *pointer; 1354 else 1355 value = * (const unsigned short *) *pointer; 1356 *pointer += sizeof(short); 1357 *length -= sizeof(short); 1358 break; 1359 case 32: 1360 if (signedp) 1361 value = * (const long *) *pointer; 1362 else 1363 value = * (const unsigned long *) *pointer & 0xffffffff; 1364 *pointer += sizeof(long); 1365 *length -= sizeof(long); 1366 break; 1367 default: 1368 abort(); 1369 } 1370 return value; 1371} 1372 1373static long 1374Extract_Len_String (const char **pointer, int *length, int size, const char **string) 1375{ 1376 int len; 1377 1378 if (size != 8) 1379 Fatal_Error("can't use format character 's' with any size except 8."); 1380 len = 0; *string = *pointer; 1381 while ((len++, --*length, *((*pointer)++)) && *length>0); 1382 1383 return len; 1384} 1385 1386static long 1387Extract_Icon (const char **pointer, int *length, int size, const char **icon) 1388{ 1389 int len = 0; 1390 1391 if (size != 32) 1392 Fatal_Error("can't use format character 'o' with any size except 32."); 1393 1394 len = *length; 1395 *icon = *pointer; 1396 *length = 0; 1397 return len; 1398} 1399 1400static thunk * 1401Break_Down_Property (const char *pointer, int length, Atom type, const char *format, int size) 1402{ 1403 thunk *thunks; 1404 thunk t = {0}; 1405 int i; 1406 char format_char; 1407 1408 thunks = Create_Thunk_List(); 1409 i = 0; 1410 1411 while (length >= size/8) { 1412 format_char = Get_Format_Char(format, i); 1413 if (format_char == 's' || format_char == 'u') 1414 t.value = Extract_Len_String(&pointer,&length,size,&t.extra_value); 1415 else if (format_char == 't') { 1416 t.extra_encoding = type; 1417 t.value = Extract_Len_String(&pointer,&length,size,&t.extra_value); 1418 } 1419 else if (format_char == 'o') 1420 t.value = Extract_Icon (&pointer,&length,size,&t.extra_value); 1421 else 1422 t.value = Extract_Value(&pointer,&length,size,format_char=='i'); 1423 thunks = Add_Thunk(thunks, t); 1424 i++; 1425 } 1426 1427 return thunks; 1428} 1429 1430/* 1431 * Variables set by main() 1432 */ 1433 1434static Window target_win = 0; 1435static int notype = 0; 1436static int max_len = MAXSTR; 1437static XFontStruct *font; 1438static unsigned long _font_prop; 1439 1440/* 1441 * 1442 * Other Stuff (temp.): 1443 * 1444 */ 1445 1446static const char * 1447Get_Font_Property_Data_And_Type (Atom atom, 1448 long *length, Atom *type, int *size) 1449{ 1450 int i; 1451 1452 *type = None; 1453 1454 for (i = 0; i < font->n_properties; i++) 1455 if (atom == font->properties[i].name) { 1456 _font_prop = font->properties[i].card32; 1457 *length = sizeof(long); 1458 *size = 32; 1459 return (const char *) &_font_prop; 1460 } 1461 *size = 0; 1462 return NULL; 1463} 1464 1465static const char * 1466Get_Window_Property_Data_And_Type (Atom atom, 1467 long *length, Atom *type, int *size) 1468{ 1469 Atom actual_type; 1470 int actual_format; 1471 unsigned long nitems; 1472 unsigned long nbytes; 1473 unsigned long bytes_after; 1474 static unsigned char *prop = NULL; 1475 int status; 1476 1477 if (prop) 1478 { 1479 XFree(prop); 1480 prop = NULL; 1481 } 1482 1483 status = XGetWindowProperty(dpy, target_win, atom, 0, (max_len+3)/4, 1484 False, AnyPropertyType, &actual_type, 1485 &actual_format, &nitems, &bytes_after, 1486 &prop); 1487 if (status == BadWindow) 1488 Fatal_Error("window id # 0x%lx does not exists!", target_win); 1489 if (status != Success) 1490 Fatal_Error("XGetWindowProperty failed!"); 1491 1492 if (actual_format == 32) 1493 nbytes = sizeof(long); 1494 else if (actual_format == 16) 1495 nbytes = sizeof(short); 1496 else if (actual_format == 8) 1497 nbytes = 1; 1498 else if (actual_format == 0) 1499 nbytes = 0; 1500 else 1501 abort(); 1502 *length = min(nitems * nbytes, max_len); 1503 *type = actual_type; 1504 *size = actual_format; 1505 return (const char *)prop; 1506} 1507 1508static const char * 1509Get_Property_Data_And_Type (Atom atom, long *length, Atom *type, int *size) 1510{ 1511 if (target_win == -1) 1512 return Get_Font_Property_Data_And_Type(atom, length, type, size); 1513 else 1514 return Get_Window_Property_Data_And_Type(atom, length, type, size); 1515} 1516 1517static void 1518Show_Prop (const char *format, const char *dformat, const char *prop) 1519{ 1520 const char *data; 1521 long length; 1522 Atom atom, type; 1523 thunk *thunks; 1524 int size, fsize; 1525 1526 printf("%s", prop); 1527 atom = Parse_Atom(prop, True); 1528 if (atom == None) { 1529 printf(": no such atom on any window.\n"); 1530 return; 1531 } 1532 1533 data = Get_Property_Data_And_Type(atom, &length, &type, &size); 1534 if (!size) { 1535 puts(": not found."); 1536 return; 1537 } 1538 1539 if (!notype && type != None) 1540 printf("(%s)", Format_Atom(type)); 1541 1542 Lookup_Formats(atom, &format, &dformat); 1543 if (type != None) 1544 Lookup_Formats(type, &format, &dformat); 1545 Apply_Default_Formats(&format, &dformat); 1546 1547 fsize = Get_Format_Size(format); 1548 if (fsize != size && fsize != 0) { 1549 printf(": Type mismatch: assumed size %d bits, actual size %d bits.\n", 1550 fsize, size); 1551 return; 1552 } 1553 1554 thunks = Break_Down_Property(data, (int)length, type, format, size); 1555 Display_Property(thunks, dformat, format); 1556 free(thunks); 1557} 1558 1559static void 1560Show_All_Props (void) 1561{ 1562 Atom *atoms, atom; 1563 const char *name; 1564 int count, i; 1565 1566 if (target_win != -1) { 1567 atoms = XListProperties(dpy, target_win, &count); 1568 for (i = 0; i < count; i++) { 1569 name = Format_Atom(atoms[i]); 1570 Show_Prop(NULL, NULL, name); 1571 } 1572 XFree(atoms); 1573 } else 1574 for (i = 0; i < font->n_properties; i++) { 1575 atom = font->properties[i].name; 1576 name = Format_Atom(atom); 1577 Show_Prop(NULL, NULL, name); 1578 } 1579} 1580 1581static thunk * 1582Handle_Prop_Requests (int argc, char **argv) 1583{ 1584 char *format, *dformat, *prop; 1585 thunk *thunks, t = {0}; 1586 1587 /* if no prop referenced, by default list all properties for given window */ 1588 if (!argc) { 1589 Show_All_Props(); 1590 return NULL; 1591 } 1592 1593 thunks = Create_Thunk_List(); 1594 1595 while (argc > 0) { 1596 format = NULL; 1597 dformat = NULL; 1598 1599 /* Get overriding formats, if any */ 1600 if (Is_A_Format(argv[0])) { 1601 format = argv++[0]; argc--; 1602 if (!argc) usage("format specified without atom"); 1603 } 1604 if (Is_A_DFormat(argv[0])) { 1605 dformat = argv++[0]; argc--; 1606 if (!argc) usage("dformat specified without atom"); 1607 } 1608 1609 /* Get property name */ 1610 prop = argv++[0]; argc--; 1611 1612 t.propname = prop; 1613 t.value = Parse_Atom(prop, True); 1614 t.format = format; 1615 t.dformat = dformat; 1616 if (t.value) 1617 thunks = Add_Thunk(thunks, t); 1618 Show_Prop(format, dformat, prop); 1619 } 1620 return thunks; 1621} 1622 1623static void 1624Remove_Property (Display *dpy, Window w, const char *propname) 1625{ 1626 Atom id = XInternAtom (dpy, propname, True); 1627 1628 if (id == None) { 1629 fprintf (stderr, "%s: no such property \"%s\"\n", 1630 program_name, propname); 1631 return; 1632 } 1633 XDeleteProperty (dpy, w, id); 1634} 1635 1636static void 1637Set_Property (Display *dpy, Window w, const char *propname, const char *value) 1638{ 1639 Atom atom; 1640 const char *format; 1641 const char *dformat; 1642 int size; 1643 char format_char; 1644 Atom type = 0; 1645 const unsigned char *data = NULL; 1646 int nelements = 0; 1647 1648 atom = Parse_Atom(propname, False); 1649 1650 format = dformat = NULL; 1651 Lookup_Formats(atom, &format, &dformat); 1652 if (format == NULL) 1653 Fatal_Error("unsupported conversion for %s", propname); 1654 1655 size = Get_Format_Size(format); 1656 1657 format_char = Get_Format_Char(format, 0); 1658 switch (format_char) { 1659 case 's': 1660 if (size != 8) 1661 Fatal_Error("can't use format character 's' with any size except 8."); 1662 type = XA_STRING; 1663 data = (const unsigned char *) value; 1664 nelements = strlen(value); 1665 break; 1666 case 'u': 1667 if (size != 8) 1668 Fatal_Error("can't use format character 'u' with any size except 8."); 1669 type = XInternAtom(dpy, "UTF8_STRING", False); 1670 data = (const unsigned char *) value; 1671 nelements = strlen(value); 1672 break; 1673 case 't': { 1674 XTextProperty textprop; 1675 if (size != 8) 1676 Fatal_Error("can't use format character 't' with any size except 8."); 1677 if (XmbTextListToTextProperty(dpy, (char **) &value, 1, 1678 XStdICCTextStyle, &textprop) != Success) { 1679 fprintf(stderr, "cannot convert %s argument to STRING or COMPOUND_TEXT.\n", propname); 1680 return; 1681 } 1682 type = textprop.encoding; 1683 data = textprop.value; 1684 nelements = textprop.nitems; 1685 break; 1686 } 1687 case 'x': 1688 case 'c': { 1689 static unsigned char data8[MAXELEMENTS]; 1690 static unsigned short data16[MAXELEMENTS]; 1691 static unsigned long data32[MAXELEMENTS]; 1692 unsigned long intvalue; 1693 char * value2 = strdup(value); 1694 char * tmp = strtok(value2,","); 1695 nelements = 1; 1696 intvalue = strtoul(tmp, NULL, 0); 1697 switch(size) { 1698 case 8: 1699 data8[0] = intvalue; data = (const unsigned char *) data8; break; 1700 case 16: 1701 data16[0] = intvalue; data = (const unsigned char *) data16; break; 1702 case 32: 1703 data32[0] = intvalue; data = (const unsigned char *) data32; break; 1704 } 1705 tmp = strtok(NULL,","); 1706 while(tmp != NULL){ 1707 intvalue = strtoul(tmp, NULL,0); 1708 switch(size) { 1709 case 8: 1710 data8[nelements] = intvalue; break; 1711 case 16: 1712 data16[nelements] = intvalue; break; 1713 case 32: 1714 data32[nelements] = intvalue; break; 1715 } 1716 nelements++; 1717 if(nelements == MAXELEMENTS){ 1718 fprintf(stderr, "Maximum number of elements reached (%d). List truncated.\n",MAXELEMENTS); 1719 break; 1720 } 1721 tmp = strtok(NULL,","); 1722 } 1723 1724 type = XA_CARDINAL; 1725 free(value2); 1726 break; 1727 } 1728 case 'i': { 1729 static unsigned char data8[MAXELEMENTS]; 1730 static unsigned short data16[MAXELEMENTS]; 1731 static unsigned long data32[MAXELEMENTS]; 1732 unsigned long intvalue; 1733 char * value2 = strdup(value); 1734 char * tmp = strtok(value2,","); 1735 nelements = 1; 1736 intvalue = strtoul(tmp, NULL, 0); 1737 switch(size) { 1738 case 8: 1739 data8[0] = intvalue; data = (const unsigned char *) data8; break; 1740 case 16: 1741 data16[0] = intvalue; data = (const unsigned char *) data16; break; 1742 case 32: 1743 data32[0] = intvalue; data = (const unsigned char *) data32; break; 1744 } 1745 tmp = strtok(NULL,","); 1746 while(tmp != NULL){ 1747 intvalue = strtoul(tmp, NULL,0); 1748 switch(size) { 1749 case 8: 1750 data8[nelements] = intvalue; break; 1751 case 16: 1752 data16[nelements] = intvalue; break; 1753 case 32: 1754 data32[nelements] = intvalue; break; 1755 } 1756 nelements++; 1757 if(nelements == MAXELEMENTS){ 1758 fprintf(stderr, "Maximum number of elements reached (%d). List truncated.\n",MAXELEMENTS); 1759 break; 1760 } 1761 tmp = strtok(NULL,","); 1762 } 1763 1764 type = XA_INTEGER; 1765 free(value2); 1766 break; 1767 } 1768 case 'b': { 1769 unsigned long boolvalue; 1770 static unsigned char data8; 1771 static unsigned short data16; 1772 static unsigned long data32; 1773 if (!strcmp(value, "True")) 1774 boolvalue = 1; 1775 else if (!strcmp(value, "False")) 1776 boolvalue = 0; 1777 else { 1778 fprintf(stderr, "cannot convert %s argument to Bool\n", propname); 1779 return; 1780 } 1781 type = XA_INTEGER; 1782 switch (size) { 1783 case 8: 1784 data8 = boolvalue; data = (const unsigned char *) &data8; break; 1785 case 16: 1786 data16 = boolvalue; data = (const unsigned char *) &data16; break; 1787 case 32: default: 1788 data32 = boolvalue; data = (const unsigned char *) &data32; break; 1789 } 1790 nelements = 1; 1791 break; 1792 } 1793 case 'a': { 1794 static Atom avalue; 1795 avalue = Parse_Atom(value, False); 1796 type = XA_ATOM; 1797 data = (const unsigned char *) &avalue; 1798 nelements = 1; 1799 break; 1800 } 1801 case 'm': 1802 /* NYI */ 1803 default: 1804 Fatal_Error("bad format character: %c", format_char); 1805 } 1806 1807 XChangeProperty(dpy, target_win, atom, type, size, PropModeReplace, 1808 data, nelements); 1809} 1810 1811/* 1812 * 1813 * Routines for parsing command line: 1814 * 1815 */ 1816 1817void 1818print_help (void) 1819{ 1820 static const char *help_message = 1821"where options include:\n" 1822" -help print out a summary of command line options\n" 1823" -grammar print out full grammar for command line\n" 1824" -display host:dpy the X server to contact\n" 1825" -id id resource id of window to examine\n" 1826" -name name name of window to examine\n" 1827" -font name name of font to examine\n" 1828" -remove propname remove a property\n" 1829" -set propname value set a property to a given value\n" 1830" -root examine the root window\n" 1831" -len n display at most n bytes of any property\n" 1832" -notype do not display the type field\n" 1833" -fs filename where to look for formats for properties\n" 1834" -frame don't ignore window manager frames\n" 1835" -f propname format [dformat] formats to use for property of given name\n" 1836" -spy examine window properties forever\n" 1837" -version print program version\n"; 1838 1839 1840 fflush (stdout); 1841 1842 fprintf (stderr, 1843 "usage: %s [-options ...] [[format [dformat]] atom] ...\n\n", 1844 program_name); 1845 fprintf (stderr, "%s\n", help_message); 1846} 1847 1848void help (void) { 1849 print_help(); 1850 exit(0); 1851} 1852 1853void 1854usage (const char *errmsg) 1855{ 1856 if (errmsg != NULL) 1857 fprintf (stderr, "%s: %s\n\n", program_name, errmsg); 1858 1859 print_help(); 1860 exit (1); 1861} 1862 1863static void 1864grammar (void) 1865{ 1866 printf ("Grammar for xprop:\n\n"); 1867 printf("\t%s [<disp>] [<select option>] <option>* <mapping>* <spec>*", 1868 program_name); 1869 printf("\n\n\tdisp ::= -display host:dpy\ 1870\n\tselect option ::= -root | -id <id> | -font <font> | -name <name>\ 1871\n\toption ::= -len <n> | -notype | -spy | {-formats|-fs} <format file>\ 1872\n\tmapping ::= {-f|-format} <atom> <format> [<dformat>]\ 1873\n\t | -remove <propname>\ 1874\n\t | -set <propname> <value>\ 1875\n\tspec ::= [<format> [<dformat>]] <atom>\ 1876\n\tformat ::= {0|8|16|32}{a|b|c|i|m|s|t|x}*\ 1877\n\tdformat ::= <unit><unit>* (can't start with a letter or '-')\ 1878\n\tunit ::= ?<exp>(<unit>*) | $<n> | <display char>\ 1879\n\texp ::= <term> | <term>=<exp> | !<exp>\ 1880\n\tterm ::= <n> | $<n> | m<n>\ 1881\n\tdisplay char ::= <normal char> | \\<non digit char> | \\<octal number>\ 1882\n\tnormal char ::= <any char except a digit, $, ?, \\, or )>\ 1883\n\n"); 1884 exit(0); 1885} 1886 1887static void 1888Parse_Format_Mapping (int *argc, char ***argv) 1889{ 1890#define ARGC (*argc) 1891#define ARGV (*argv) 1892#define OPTION ARGV[0] 1893#define NXTOPT if (++ARGV, --ARGC==0) usage("insufficent arguments for -format") 1894 char *type_name, *format, *dformat; 1895 1896 NXTOPT; type_name = OPTION; 1897 1898 NXTOPT; format = OPTION; 1899 if (!Is_A_Format(format)) 1900 Fatal_Error("Bad format: %s.", format); 1901 1902 dformat = NULL; 1903 if (ARGC>1 && Is_A_DFormat(ARGV[1])) { 1904 ARGV++; ARGC--; dformat = OPTION; 1905 } 1906 1907 Add_Mapping(Parse_Atom(type_name, False), format, dformat); 1908} 1909 1910/* 1911 * 1912 * The Main Program: 1913 * 1914 */ 1915 1916static int spy = 0; 1917 1918static int (*old_error_handler)(Display *dpy, XErrorEvent *ev); 1919 1920static int spy_error_handler(Display *dpy, XErrorEvent *ev) 1921{ 1922 if (ev->error_code == BadWindow || ev->error_code == BadMatch) { 1923 /* Window was destroyed */ 1924 puts(""); 1925 exit(0); 1926 } 1927 1928 if (old_error_handler) 1929 return old_error_handler(dpy, ev); 1930 1931 return 0; 1932} 1933 1934int 1935main (int argc, char **argv) 1936{ 1937 FILE *stream; 1938 char *name; 1939 thunk *props; 1940 thunk *remove_props = NULL; 1941 thunk *set_props = NULL; 1942 Bool frame_only = False; 1943 int n; 1944 char **nargv; 1945 1946#ifdef TIOCGWINSZ 1947 struct winsize ws; 1948 ws.ws_col = 0; 1949 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col != 0) 1950 term_width = ws.ws_col; 1951#endif 1952 1953 INIT_NAME; 1954 1955 /* Set locale for XmbTextProptertyToTextList and iswprint(). */ 1956 setlocale(LC_CTYPE, ""); 1957 1958 /* Handle display name, opening the display */ 1959 Setup_Display_And_Screen(&argc, argv); 1960 1961 /* Handle selecting the window to display properties for */ 1962 target_win = Select_Window_Args(&argc, argv); 1963 1964 /* Set up default atom to format, dformat mapping */ 1965 XpropMode = XpropWindowProperties; 1966 for (n = argc, nargv = argv; n; nargv++, n--) 1967 if (! strcmp(nargv[0], "-font")) { 1968 XpropMode = XpropFontProperties; 1969 break; 1970 } 1971 Setup_Mapping(); 1972 if ((name = getenv("XPROPFORMATS"))) { 1973 if (!(stream = fopen(name, "r"))) 1974 Fatal_Error("unable to open file %s for reading.", name); 1975 Read_Mappings(stream); 1976 fclose(stream); 1977 } 1978 1979 /* Handle '-' options to setup xprop, select window to work on */ 1980 while (argv++, --argc>0 && **argv == '-') { 1981 if (!strcmp(argv[0], "-")) 1982 continue; 1983 if (!strcmp(argv[0], "-help")) { 1984 help (); 1985 /* NOTREACHED */ 1986 } 1987 if (!strcmp(argv[0], "-grammar")) { 1988 grammar (); 1989 /* NOTREACHED */ 1990 } 1991 if (!strcmp(argv[0], "-notype")) { 1992 notype = 1; 1993 continue; 1994 } 1995 if (!strcmp(argv[0], "-spy")) { 1996 spy = 1; 1997 continue; 1998 } 1999 if (!strcmp(argv[0], "-len")) { 2000 if (++argv, --argc == 0) usage("-len requires an argument"); 2001 max_len = atoi(argv[0]); 2002 continue; 2003 } 2004 if (!strcmp(argv[0], "-formats") || !strcmp(argv[0], "-fs")) { 2005 if (++argv, --argc == 0) usage("-fs requires an argument"); 2006 if (!(stream = fopen(argv[0], "r"))) 2007 Fatal_Error("unable to open file %s for reading.", argv[0]); 2008 Read_Mappings(stream); 2009 fclose(stream); 2010 continue; 2011 } 2012 if (!strcmp(argv[0], "-font")) { 2013 if (++argv, --argc == 0) usage("-font requires an argument"); 2014 font = Open_Font(argv[0]); 2015 target_win = -1; 2016 continue; 2017 } 2018 if (!strcmp(argv[0], "-remove")) { 2019 thunk t = {0}; 2020 if (++argv, --argc == 0) usage("-remove requires an argument"); 2021 t.propname = argv[0]; 2022 if (remove_props == NULL) remove_props = Create_Thunk_List(); 2023 remove_props = Add_Thunk(remove_props, t); 2024 continue; 2025 } 2026 if (!strcmp(argv[0], "-set")) { 2027 thunk t = {0}; 2028 if (argc < 3) usage("insufficent arguments for -set"); 2029 t.propname = argv[1]; 2030 t.extra_value = argv[2]; 2031 argv += 3; argc -= 3; 2032 if (set_props == NULL) set_props = Create_Thunk_List(); 2033 set_props = Add_Thunk(set_props, t); 2034 continue; 2035 } 2036 if (!strcmp(argv[0], "-frame")) { 2037 frame_only = True; 2038 continue; 2039 } 2040 if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "-format")) { 2041 Parse_Format_Mapping(&argc, &argv); 2042 continue; 2043 } 2044 if (!strcmp(argv[0], "-version")) { 2045 puts(PACKAGE_STRING); 2046 exit(0); 2047 } 2048 fprintf (stderr, "%s: unrecognized argument %s\n\n", 2049 program_name, argv[0]); 2050 usage(NULL); 2051 } 2052 2053 if ((remove_props != NULL || set_props != NULL) && argc > 0) { 2054 fprintf (stderr, "%s: unrecognized argument %s\n\n", 2055 program_name, argv[0]); 2056 usage(NULL); 2057 } 2058 2059 if (target_win == None) 2060 target_win = Select_Window(dpy, !frame_only); 2061 2062 if (remove_props != NULL) { 2063 int count; 2064 2065 if (target_win == -1) 2066 Fatal_Error("-remove works only on windows, not fonts"); 2067 2068 count = remove_props->thunk_count; 2069 for (; count > 0; remove_props++, count--) 2070 Remove_Property (dpy, target_win, remove_props->propname); 2071 } 2072 2073 if (set_props != NULL) { 2074 int count; 2075 2076 if (target_win == -1) 2077 Fatal_Error("-set works only on windows, not fonts"); 2078 2079 count = set_props->thunk_count; 2080 for (; count > 0; set_props++, count--) 2081 Set_Property (dpy, target_win, set_props->propname, 2082 set_props->extra_value); 2083 } 2084 2085 if (remove_props != NULL || set_props != NULL) { 2086 XCloseDisplay (dpy); 2087 exit (0); 2088 } 2089 2090 props = Handle_Prop_Requests(argc, argv); 2091 2092 if (spy && target_win != -1) { 2093 XEvent event; 2094 const char *format, *dformat; 2095 2096 XSelectInput(dpy, target_win, PropertyChangeMask | StructureNotifyMask); 2097 old_error_handler = XSetErrorHandler(spy_error_handler); 2098 for (;;) { 2099 fflush(stdout); 2100 XNextEvent(dpy, &event); 2101 if (event.type == DestroyNotify) 2102 break; 2103 if (event.type != PropertyNotify) 2104 continue; 2105 format = dformat = NULL; 2106 if (props) { 2107 int i; 2108 for (i = 0; i < props->thunk_count; i++) 2109 if (props[i].value == event.xproperty.atom) 2110 break; 2111 if (i >= props->thunk_count) 2112 continue; 2113 format = props[i].format; 2114 dformat = props[i].dformat; 2115 } 2116 Show_Prop(format, dformat, Format_Atom(event.xproperty.atom)); 2117 } 2118 } 2119 exit (0); 2120} 2121