3 Copyright 1990, 1998 The Open Group
4 Copyright (c) 2000 The XFree86 Project, Inc.
6 Permission to use, copy, modify, distribute, and sell this software and its
7 documentation for any purpose is hereby granted without fee, provided that
8 the above copyright notice appear in all copies and that both that
9 copyright notice and this permission notice appear in supporting
12 The above copyright notice and this permission notice shall be included
13 in all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 OTHER DEALINGS IN THE SOFTWARE.
23 Except as contained in this notice, the name of The Open Group shall
24 not be used in advertising or otherwise to promote the sale, use or
25 other dealings in this Software without prior written authorization
34 #include <X11/Xfuncs.h>
35 #include <X11/Xutil.h>
46 #ifdef HAVE_LANGINFO_H
51 #define iswprint(x) isprint(x)
54 #include <X11/Xatom.h>
59 #define MAXELEMENTS 64
62 #define min(a,b) ((a) < (b) ? (a) : (b))
65 /* isprint() in "C" locale */
66 #define c_isprint(c) ((c) >= 0x20 && (c) < 0x7f)
70 * The Thunk Manager - routines to create, add to, and free thunk lists
79 const char *extra_value;
85 Create_Thunk_List (void)
89 tptr = (thunk *) Malloc(sizeof(thunk));
91 tptr->thunk_count = 0;
98 Free_Thunk_List (thunk *list)
105 Add_Thunk (thunk *list, thunk t)
109 i = list->thunk_count;
111 list = (thunk *) realloc(list, (i+1)*sizeof(thunk));
113 Fatal_Error("Out of memory!");
116 list->thunk_count = i;
126 Copy_String (const char *string)
131 length = strlen(string) + 1;
133 new = (char *) Malloc(length);
134 memcpy(new, string, length);
140 Read_Char (FILE *stream)
146 Fatal_Error("Bad format file: Unexpected EOF.");
151 Read_White_Space (FILE *stream)
155 while ((c = getc(stream)) == ' ' || c == '\n' || c == '\t');
159 static char _large_buffer[MAXSTR+10];
162 Read_Quoted (FILE *stream)
167 Read_White_Space(stream);
168 if (Read_Char(stream)!='\'')
169 Fatal_Error("Bad format file format: missing dformat.");
171 ptr = _large_buffer; length = MAXSTR;
174 Fatal_Error("Bad format file format: dformat too long.");
175 c = Read_Char(stream);
178 ptr++[0] = c; length--;
179 if (c == (int) '\\') {
180 c = Read_Char(stream);
184 ptr++[0] = c; length--;
189 return Copy_String(_large_buffer);
194 * Parsing Routines: a group of routines to parse strings into values
196 * Routines: Parse_Atom, Scan_Long, Skip_Past_Right_Paren, Scan_Octal
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!
206 Skip_Digits (const char *string)
208 while (isdigit((unsigned char) string[0])) string++;
213 Scan_Long (const char *string, long *value)
215 if (!isdigit((unsigned char) *string))
216 Fatal_Error("Bad number: %s.", string);
218 *value = atol(string);
219 return Skip_Digits(string);
223 Scan_Octal (const char *string, unsigned long *value)
225 if (sscanf(string, "%lo", value)!=1)
226 Fatal_Error("Bad octal number: %s.", string);
227 return Skip_Digits(string);
231 Parse_Atom (const char *name, int only_if_exists)
233 /* may return None = 0 */
234 return XInternAtom(dpy, name, only_if_exists);
238 Skip_Past_Right_Paren (const char *string)
243 while (c = string++[0], c != ')' || nesting)
246 Fatal_Error("Missing ')'.");
262 * Atom to format, dformat mapping Manager
266 #define D_FORMAT "0x" /* Default format for properties */
267 #define D_DFORMAT " = $0+\n" /* Default display pattern for properties */
269 static thunk *_property_formats = NULL; /* Holds mapping */
272 Apply_Default_Formats (const char **format, const char **dformat)
277 *dformat = D_DFORMAT;
281 Lookup_Formats (Atom atom, const char **format, const char **dformat)
285 if (_property_formats)
286 for (i = _property_formats->thunk_count-1; i >= 0; i--)
287 if (_property_formats[i].value == atom) {
289 *format = _property_formats[i].format;
291 *dformat = _property_formats[i].dformat;
297 Add_Mapping (Atom atom, const char *format, const char *dformat)
301 if (!_property_formats)
302 _property_formats = Create_Thunk_List();
308 _property_formats = Add_Thunk(_property_formats, t);
313 * Setup_Mapping: Routine to setup default atom to format, dformat mapping:
317 typedef struct _propertyRec {
321 const char * dformat;
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"
329 #define RECTANGLE_DFORMAT ":\n"\
330 "\t\tupper left corner: $0, $1\n"\
331 "\t\tsize: $2 by $3\n"
333 #define RGB_COLOR_MAP_DFORMAT ":\n"\
334 "\t\tcolormap id #: $0\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"
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)"\
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)"
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"
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: "\
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"
395 static 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}
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
433 * Font-specific mapping of property names to types:
435 static propertyRec fontPropTable[] = {
437 /* XLFD name properties */
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 },
454 /* other font properties referenced in the XLFD */
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 },
487 /* other font properties */
489 { "FONT", XA_FONT, "32a", 0 },
490 { "FONT_NAME", XA_FONT_NAME, "32a", 0 },
493 static int XpropMode;
494 #define XpropWindowProperties 0
495 #define XpropFontProperties 1
503 if (XpropMode == XpropWindowProperties) {
504 n = sizeof(windowPropTable) / sizeof(propertyRec);
507 n = sizeof (fontPropTable) / sizeof (propertyRec);
510 for ( ; --n >= 0; p++) {
512 p->atom = XInternAtom(dpy, p->name, True);
516 Add_Mapping(p->atom, p->format, p->dformat);
521 GetAtomName (Atom atom)
526 if (XpropMode == XpropWindowProperties) {
527 n = sizeof(windowPropTable) / sizeof(propertyRec);
530 n = sizeof (fontPropTable) / sizeof (propertyRec);
533 for ( ; --n >= 0; p++)
541 * Read_Mapping: routine to read in additional mappings from a stream
542 * already open for reading.
546 Read_Mappings (FILE *stream)
548 char format_buffer[100];
549 char name[1000], *dformat, *format;
553 while ((count = fscanf(stream," %990s %90s ",name,format_buffer)) != EOF) {
555 Fatal_Error("Bad format file format.");
557 atom = Parse_Atom(name, False);
558 format = Copy_String(format_buffer);
560 Read_White_Space(stream);
565 dformat = Read_Quoted(stream);
567 Add_Mapping(atom, format, dformat);
573 * Formatting Routines: a group of routines to translate from various
574 * values to a static read-only string useful for output.
576 * Routines: Format_Hex, Format_Unsigned, Format_Signed, Format_Atom,
577 * Format_Mask_Word, Format_Bool, Format_String, Format_Len_String.
579 * All of the above routines take a long except for Format_String and
583 static char _formatting_buffer[MAXSTR+100];
584 static char _formatting_buffer2[21];
587 Format_Hex (long wrd)
589 snprintf(_formatting_buffer2, sizeof(_formatting_buffer2), "0x%lx", wrd);
590 return _formatting_buffer2;
594 Format_Unsigned (long wrd)
596 snprintf(_formatting_buffer2, sizeof(_formatting_buffer2), "%lu", wrd);
597 return _formatting_buffer2;
601 Format_Signed (long wrd)
603 snprintf(_formatting_buffer2, sizeof(_formatting_buffer2), "%ld", wrd);
604 return _formatting_buffer2;
609 ignore_errors (Display *dpy, XErrorEvent *ev)
615 Format_Atom (Atom atom)
619 XErrorHandler handler;
621 if ((found = GetAtomName(atom)) != NULL)
624 handler = XSetErrorHandler (ignore_errors);
625 name = XGetAtomName(dpy, atom);
626 XSetErrorHandler(handler);
628 snprintf(_formatting_buffer, sizeof(_formatting_buffer),
629 "undefined atom # 0x%lx", atom);
631 int namelen = strlen(name);
632 if (namelen > MAXSTR) namelen = MAXSTR;
633 memcpy(_formatting_buffer, name, namelen);
634 _formatting_buffer[namelen] = '\0';
637 return _formatting_buffer;
641 Format_Mask_Word (long wrd)
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) {
650 strcat(_formatting_buffer, ", ");
653 strcat(_formatting_buffer, Format_Unsigned(bit));
656 strcat(_formatting_buffer, "}");
658 return _formatting_buffer;
662 Format_Bool (long value)
670 static char *_buf_ptr;
685 _format_char (char c, int unicode)
703 if (unicode && (c & 0x80)) {
707 snprintf(_buf_ptr, _buf_len, "%03o", (unsigned char) c);
717 Format_String (const char *string, int unicode)
721 _buf_ptr = _formatting_buffer;
725 while ((c = string++[0]))
726 _format_char(c, unicode);
730 return _formatting_buffer;
734 Format_Len_String (const char *string, int len)
739 data = (char *) Malloc(len+1);
741 memcpy(data, string, len);
744 result = Format_String(data, 0);
751 is_utf8_locale (void)
753 #ifdef HAVE_LANGINFO_H
754 char *charmap = nl_langinfo (CODESET);
756 return charmap && strcmp (charmap, "UTF-8") == 0;
763 Format_Icons (const unsigned long *icon, int len)
765 char *result = NULL, *tail = NULL;
767 const unsigned long *end = icon + len / sizeof (unsigned long);
773 unsigned long width, height;
780 offset = (tail - result);
782 alloced += 80; /* For the header */
783 alloced += (width*4 + 8) * height; /* For the rows (plus padding) */
785 result = Realloc (result, alloced);
786 tail = &result[offset];
788 if (end - icon < width * height)
791 tail += sprintf (tail, "\tIcon (%lu x %lu):\n", width, height);
793 if (width > 144 || height > 144)
795 tail += sprintf (tail, "\t(not shown)");
796 icon += width * height;
800 for (h = 0; h < height; ++h)
802 tail += sprintf (tail, "\t");
804 for (w = 0; w < width; ++w)
806 unsigned char a, r, g, b;
807 unsigned long pixel = *icon++;
808 unsigned long brightness;
810 a = (pixel & 0xff000000) >> 24;
811 r = (pixel & 0x00ff0000) >> 16;
812 g = (pixel & 0x0000ff00) >> 8;
813 b = (pixel & 0x000000ff);
816 (a / 255.0) * (1000 - ((299 * (r / 255.0)) +
817 (587 * (g / 255.0)) +
818 (114 * (b / 255.0))));
820 if (is_utf8_locale())
822 static const char palette[][4] =
825 "\342\226\221", /* 25% */
826 "\342\226\222", /* 50% */
827 "\342\226\223", /* 75% */
828 "\342\226\210", /* 100% */
832 idx = (brightness * ((sizeof (palette)/sizeof(palette[0])) - 1)) / 1000;
834 tail += sprintf (tail, "%s", palette[idx]);
838 static const char palette[] =
839 " .'`,^:\";~-_+<>i!lI?/\\|()1{}[]rcvunxzjftLCJUYXZO0Qoahkbdpqwm*WMB8&%$#@";
842 idx = (brightness * (sizeof(palette) - 2)) / 1000;
844 *tail++ = palette[idx];
848 tail += sprintf (tail, "\n");
851 tail += sprintf (tail, "\n");
858 Format_Len_Text (const char *string, int len, Atom encoding)
860 XTextProperty textprop;
864 /* Try to convert to local encoding. */
865 textprop.encoding = encoding;
867 textprop.value = (unsigned char *) string;
868 textprop.nitems = len;
869 if (XmbTextPropertyToTextList(dpy, &textprop, &list, &count) == Success) {
870 _buf_ptr = _formatting_buffer;
875 len = strlen(string);
878 int n = mbtowc(&wc, string, len);
879 if (n > 0 && iswprint(wc)) {
881 memcpy(_buf_ptr, string, n);
889 snprintf(_buf_ptr, _buf_len, "%03o", (unsigned char) *string);
898 snprintf(_buf_ptr, _buf_len, "\\000");
905 return _formatting_buffer;
907 return Format_Len_String(string, len);
911 * Validate a string as UTF-8 encoded according to RFC 3629
913 * Simply, a unicode code point (up to 21-bits long) is encoded as follows:
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
923 * Validation is done left-to-right, and an error condition, if any, refers to
924 * only the left-most problem in the string.
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
934 #define UTF8_FORBIDDEN_VALUE 1
935 #define UTF8_OVERLONG 2
936 #define UTF8_SHORT_TAIL 3
937 #define UTF8_LONG_TAIL 4
939 is_valid_utf8 (const char *string, int len)
941 unsigned long codepoint;
946 for (i = 0; i < len; i++) {
947 c = (unsigned char) string[i];
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
957 if (rem > 0) return UTF8_SHORT_TAIL;
960 } else if ((c & 0xC0) == 0x80) {
961 if (rem == 0) return UTF8_LONG_TAIL;
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;
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;
973 codepoint = (c & 0x0F) << 12;
974 } else if ((c & 0xF8) == 0xF0) {
975 if (rem > 0) return UTF8_SHORT_TAIL;
977 codepoint = (c & 0x07) << 18;
978 if (codepoint > 0x10FFFF) return UTF8_FORBIDDEN_VALUE;
980 return UTF8_FORBIDDEN_VALUE;
987 Format_Len_Unicode (const char *string, int len)
990 const char *result, *error;
993 int validity = is_valid_utf8(string, len);
995 if (validity != UTF8_VALID) {
997 case UTF8_FORBIDDEN_VALUE:
998 error = "<Invalid UTF-8 string: Forbidden value> "; break;
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;
1007 result = Format_Len_String(string, len);
1008 len2 = strlen(result);
1009 data = (char *) Malloc(len2+1);
1010 memcpy(data, result, len2+1);
1012 memcpy(_formatting_buffer, error, strlen(error)+1);
1013 strcat(_formatting_buffer, data);
1016 return _formatting_buffer;
1019 if (!is_utf8_locale())
1020 return Format_Len_String(string, len);
1022 data = (char *) Malloc(len+1);
1024 memcpy(data, string, len);
1027 result = Format_String(data, 1);
1035 * The Format Manager: a group of routines to manage "formats"
1040 Is_A_Format (const char *string)
1042 return isdigit((unsigned char) string[0]);
1046 Get_Format_Size (const char *format)
1050 Scan_Long(format, &size);
1052 /* Check for legal sizes */
1053 if (size != 0 && size != 8 && size != 16 && size != 32)
1054 Fatal_Error("bad format: %s", format);
1060 Get_Format_Char (const char *format, int i)
1064 /* Remove # at front of format */
1065 format = Scan_Long(format, &size);
1067 Fatal_Error("bad format: %s", format);
1069 /* Last character repeats forever... */
1070 if (i >= (int)strlen(format))
1071 i = strlen(format)-1;
1077 Format_Thunk (thunk t, char format_char)
1082 switch (format_char) {
1084 return Format_Len_String(t.extra_value, (int)t.value);
1086 return Format_Len_Unicode(t.extra_value, (int)t.value);
1088 return Format_Len_Text(t.extra_value, (int)t.value, t.extra_encoding);
1090 return Format_Hex(value);
1092 return Format_Unsigned(value);
1094 return Format_Signed(value);
1096 return Format_Bool(value);
1098 return Format_Mask_Word(value);
1100 return Format_Atom(value);
1102 return Format_Icons((const unsigned long *)t.extra_value, (int)t.value);
1104 Fatal_Error("bad format character: %c", format_char);
1109 Format_Thunk_I (thunk *thunks, const char *format, int i)
1111 if (i >= thunks->thunk_count)
1112 return "<field not available>";
1114 return Format_Thunk(thunks[i], Get_Format_Char(format, i));
1118 Mask_Word (thunk *thunks, const char *format)
1122 for (j = 0; j < (int)strlen(format); j++)
1123 if (Get_Format_Char(format, j) == 'm')
1124 return thunks[j].value;
1130 * The Display Format Manager:
1135 Is_A_DFormat (const char *string)
1137 return string[0] && string[0] != '-'
1138 && !(isalpha((unsigned char) string[0]) || string[0] == '_');
1142 Handle_Backslash (const char *dformat)
1147 if (!(c = *(dformat++)))
1165 dformat = Scan_Octal(dformat, &i);
1176 Handle_Dollar_sign (const char *dformat, thunk *thunks, const char *format)
1180 dformat = Scan_Long(dformat, &i);
1182 if (dformat[0] == '+') {
1185 for (; i < thunks->thunk_count; i++) {
1189 printf("%s", Format_Thunk_I(thunks, format, (int) i));
1192 printf("%s", Format_Thunk_I(thunks, format, (int) i));
1198 Mask_Bit_I (thunk *thunks, const char *format, int i)
1202 value = Mask_Word(thunks, format);
1204 value = value & (1L<<i);
1211 Scan_Term (const char *string, thunk *thunks, const char *format, long *value)
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);
1228 Fatal_Error("Bad term: %s.", string);
1234 Scan_Exp (const char *string, thunk *thunks, const char *format, long *value)
1238 if (string[0] == '(') {
1239 string = Scan_Exp(++string, thunks, format, value);
1241 Fatal_Error("Missing ')'");
1244 if (string[0] == '!') {
1245 string = Scan_Exp(++string, thunks, format, value);
1250 string = Scan_Term(string, thunks, format, value);
1252 if (string[0] == '=') {
1253 string = Scan_Exp(++string, thunks, format, &temp);
1254 *value = *value == temp;
1261 Handle_Question_Mark (const char *dformat, thunk *thunks, const char *format)
1265 dformat = Scan_Exp(dformat, thunks, format, &true);
1267 if (*dformat != '(')
1268 Fatal_Error("Bad conditional: '(' expected: %s.", dformat);
1272 dformat = Skip_Past_Right_Paren(dformat);
1278 Display_Property (thunk *thunks, const char *dformat, const char *format)
1282 while ((c = *(dformat++)))
1287 dformat = Handle_Backslash(dformat);
1290 dformat = Handle_Dollar_sign(dformat, thunks, format);
1293 dformat = Handle_Question_Mark(dformat, thunks, format);
1303 * Routines to convert property data to thunks
1308 Extract_Value (const char **pointer, int *length, int size, int signedp)
1315 value = * (const signed char *) *pointer;
1317 value = * (const unsigned char *) *pointer;
1323 value = * (const short *) *pointer;
1325 value = * (const unsigned short *) *pointer;
1326 *pointer += sizeof(short);
1327 *length -= sizeof(short);
1331 value = * (const long *) *pointer;
1333 value = * (const unsigned long *) *pointer & 0xffffffff;
1334 *pointer += sizeof(long);
1335 *length -= sizeof(long);
1344 Extract_Len_String (const char **pointer, int *length, int size, const char **string)
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);
1357 Extract_Icon (const char **pointer, int *length, int size, const char **icon)
1362 Fatal_Error("can't use format character 'o' with any size except 32.");
1371 Break_Down_Property (const char *pointer, int length, Atom type, const char *format, int size)
1378 thunks = Create_Thunk_List();
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);
1389 else if (format_char == 'o')
1390 t.value = Extract_Icon (&pointer,&length,size,&t.extra_value);
1392 t.value = Extract_Value(&pointer,&length,size,format_char=='i');
1393 thunks = Add_Thunk(thunks, t);
1401 * Variables set by main()
1404 static Window target_win = 0;
1405 static int notype = 0;
1406 static int max_len = MAXSTR;
1407 static XFontStruct *font;
1408 static unsigned long _font_prop;
1412 * Other Stuff (temp.):
1417 Get_Font_Property_Data_And_Type (Atom atom,
1418 long *length, Atom *type, int *size)
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);
1429 return (const char *) &_font_prop;
1436 Get_Window_Property_Data_And_Type (Atom atom,
1437 long *length, Atom *type, int *size)
1441 unsigned long nitems;
1442 unsigned long nbytes;
1443 unsigned long bytes_after;
1444 unsigned char *prop;
1447 status = XGetWindowProperty(dpy, target_win, atom, 0, (max_len+3)/4,
1448 False, AnyPropertyType, &actual_type,
1449 &actual_format, &nitems, &bytes_after,
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!");
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)
1462 else if (actual_format == 0)
1466 *length = min(nitems * nbytes, max_len);
1467 *type = actual_type;
1468 *size = actual_format;
1469 return (const char *)prop;
1473 Get_Property_Data_And_Type (Atom atom, long *length, Atom *type, int *size)
1475 if (target_win == -1)
1476 return Get_Font_Property_Data_And_Type(atom, length, type, size);
1478 return Get_Window_Property_Data_And_Type(atom, length, type, size);
1482 Show_Prop (const char *format, const char *dformat, const char *prop)
1491 atom = Parse_Atom(prop, True);
1493 printf(": no such atom on any window.\n");
1497 data = Get_Property_Data_And_Type(atom, &length, &type, &size);
1499 puts(": not found.");
1503 if (!notype && type != None)
1504 printf("(%s)", Format_Atom(type));
1506 Lookup_Formats(atom, &format, &dformat);
1508 Lookup_Formats(type, &format, &dformat);
1509 Apply_Default_Formats(&format, &dformat);
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",
1518 thunks = Break_Down_Property(data, (int)length, type, format, size);
1520 Display_Property(thunks, dformat, format);
1524 Show_All_Props (void)
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);
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);
1545 Handle_Prop_Requests (int argc, char **argv)
1547 char *format, *dformat, *prop;
1550 /* if no prop referenced, by default list all properties for given window */
1556 thunks = Create_Thunk_List();
1562 /* Get overriding formats, if any */
1563 if (Is_A_Format(argv[0])) {
1564 format = argv++[0]; argc--;
1567 if (Is_A_DFormat(argv[0])) {
1568 dformat = argv++[0]; argc--;
1572 /* Get property name */
1573 prop = argv++[0]; argc--;
1576 t.value = Parse_Atom(prop, True);
1578 t.dformat = dformat;
1580 thunks = Add_Thunk(thunks, t);
1581 Show_Prop(format, dformat, prop);
1587 Remove_Property (Display *dpy, Window w, const char *propname)
1589 Atom id = XInternAtom (dpy, propname, True);
1592 fprintf (stderr, "%s: no such property \"%s\"\n",
1593 program_name, propname);
1596 XDeleteProperty (dpy, w, id);
1600 Set_Property (Display *dpy, Window w, const char *propname, const char *value)
1604 const char *dformat;
1608 unsigned char *data = NULL;
1611 atom = Parse_Atom(propname, False);
1613 format = dformat = NULL;
1614 Lookup_Formats(atom, &format, &dformat);
1616 Fatal_Error("unsupported conversion for %s", propname);
1618 size = Get_Format_Size(format);
1620 format_char = Get_Format_Char(format, 0);
1621 switch (format_char) {
1624 Fatal_Error("can't use format character 's' with any size except 8.");
1626 data = (unsigned char *) value;
1627 nelements = strlen(value);
1630 XTextProperty textprop;
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);
1638 type = textprop.encoding;
1639 data = textprop.value;
1640 nelements = textprop.nitems;
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,",");
1652 intvalue = strtoul(tmp, NULL, 0);
1655 data8[0] = intvalue; data = (unsigned char *) data8; break;
1657 data16[0] = intvalue; data = (unsigned char *) data16; break;
1659 data32[0] = intvalue; data = (unsigned char *) data32; break;
1661 tmp = strtok(NULL,",");
1663 intvalue = strtoul(tmp, NULL,0);
1666 data8[nelements] = intvalue; break;
1668 data16[nelements] = intvalue; break;
1670 data32[nelements] = intvalue; break;
1673 if(nelements == MAXELEMENTS){
1674 fprintf(stderr, "Maximum number of elements reached (%d). List truncated.\n",MAXELEMENTS);
1677 tmp = strtok(NULL,",");
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,",");
1692 intvalue = strtoul(tmp, NULL, 0);
1695 data8[0] = intvalue; data = (unsigned char *) data8; break;
1697 data16[0] = intvalue; data = (unsigned char *) data16; break;
1699 data32[0] = intvalue; data = (unsigned char *) data32; break;
1701 tmp = strtok(NULL,",");
1703 intvalue = strtoul(tmp, NULL,0);
1706 data8[nelements] = intvalue; break;
1708 data16[nelements] = intvalue; break;
1710 data32[nelements] = intvalue; break;
1713 if(nelements == MAXELEMENTS){
1714 fprintf(stderr, "Maximum number of elements reached (%d). List truncated.\n",MAXELEMENTS);
1717 tmp = strtok(NULL,",");
1725 unsigned long boolvalue;
1726 static unsigned char data8;
1727 static unsigned short data16;
1728 static unsigned long data32;
1729 if (!strcmp(value, "True"))
1731 else if (!strcmp(value, "False"))
1734 fprintf(stderr, "cannot convert %s argument to Bool\n", propname);
1740 data8 = boolvalue; data = (unsigned char *) &data8; break;
1742 data16 = boolvalue; data = (unsigned char *) &data16; break;
1744 data32 = boolvalue; data = (unsigned char *) &data32; break;
1751 avalue = Parse_Atom(value, False);
1753 data = (unsigned char *) &avalue;
1760 Fatal_Error("bad format character: %c", format_char);
1763 XChangeProperty(dpy, target_win, atom, type, size, PropModeReplace,
1769 * Routines for parsing command line:
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";
1796 "usage: %s [-options ...] [[format [dformat]] atom] ...\n\n",
1798 fprintf (stderr, "%s\n", help_message);
1805 printf ("Grammar for xprop:\n\n");
1806 printf("\t%s [<disp>] [<select option>] <option>* <mapping>* <spec>*",
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 )>\
1827 Parse_Format_Mapping (int *argc, char ***argv)
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;
1835 NXTOPT; type_name = OPTION;
1837 NXTOPT; format = OPTION;
1838 if (!Is_A_Format(format))
1839 Fatal_Error("Bad format: %s.", format);
1842 if (ARGC>1 && Is_A_DFormat(ARGV[1])) {
1843 ARGV++; ARGC--; dformat = OPTION;
1846 Add_Mapping(Parse_Atom(type_name, False), format, dformat);
1857 static int (*old_error_handler)(Display *dpy, XErrorEvent *ev);
1859 static int spy_error_handler(Display *dpy, XErrorEvent *ev)
1861 if (ev->error_code == BadWindow || ev->error_code == BadMatch) {
1862 /* Window was destroyed */
1867 if (old_error_handler)
1868 return old_error_handler(dpy, ev);
1874 main (int argc, char **argv)
1879 thunk *remove_props = NULL;
1880 thunk *set_props = NULL;
1881 Bool frame_only = False;
1887 /* Set locale for XmbTextProptertyToTextList and iswprint(). */
1888 setlocale(LC_CTYPE, "");
1890 /* Handle display name, opening the display */
1891 Setup_Display_And_Screen(&argc, argv);
1893 /* Handle selecting the window to display properties for */
1894 target_win = Select_Window_Args(&argc, argv);
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;
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);
1911 /* Handle '-' options to setup xprop, select window to work on */
1912 while (argv++, --argc>0 && **argv == '-') {
1913 if (!strcmp(argv[0], "-"))
1915 if (!strcmp(argv[0], "-grammar")) {
1919 if (!strcmp(argv[0], "-notype")) {
1923 if (!strcmp(argv[0], "-spy")) {
1927 if (!strcmp(argv[0], "-len")) {
1928 if (++argv, --argc == 0) usage();
1929 max_len = atoi(argv[0]);
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);
1940 if (!strcmp(argv[0], "-font")) {
1941 if (++argv, --argc == 0) usage();
1942 font = Open_Font(argv[0]);
1946 if (!strcmp(argv[0], "-remove")) {
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);
1954 if (!strcmp(argv[0], "-set")) {
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);
1964 if (!strcmp(argv[0], "-frame")) {
1968 if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "-format")) {
1969 Parse_Format_Mapping(&argc, &argv);
1975 if ((remove_props != NULL || set_props != NULL) && argc > 0)
1978 if (target_win == None)
1979 target_win = Select_Window(dpy, !frame_only);
1981 if (remove_props != NULL) {
1984 if (target_win == -1)
1985 Fatal_Error("-remove works only on windows, not fonts");
1987 count = remove_props->thunk_count;
1988 for (; count > 0; remove_props++, count--)
1989 Remove_Property (dpy, target_win, remove_props->propname);
1992 if (set_props != NULL) {
1995 if (target_win == -1)
1996 Fatal_Error("-set works only on windows, not fonts");
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);
2004 if (remove_props != NULL || set_props != NULL) {
2005 XCloseDisplay (dpy);
2009 props = Handle_Prop_Requests(argc, argv);
2011 if (spy && target_win != -1) {
2013 const char *format, *dformat;
2015 XSelectInput(dpy, target_win, PropertyChangeMask | StructureNotifyMask);
2016 old_error_handler = XSetErrorHandler(spy_error_handler);
2019 XNextEvent(dpy, &event);
2020 if (event.type == DestroyNotify)
2022 if (event.type != PropertyNotify)
2024 format = dformat = NULL;
2027 for (i = 0; i < props->thunk_count; i++)
2028 if (props[i].value == event.xproperty.atom)
2030 if (i >= props->thunk_count)
2032 format = props[i].format;
2033 dformat = props[i].dformat;
2035 Show_Prop(format, dformat, Format_Atom(event.xproperty.atom));