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