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