"Initial commit to Gerrit"
[profile/ivi/xorg-utils.git] / xprop / xprop.c
1 /*
2
3 Copyright 1990, 1998  The Open Group
4 Copyright (c) 2000  The XFree86 Project, Inc.
5
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
10 documentation.
11   
12 The above copyright notice and this permission notice shall be included
13 in all copies or substantial portions of the Software.
14
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.
22
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
26 from 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
74 typedef 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
84 static thunk *
85 Create_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
97 static void
98 Free_Thunk_List (thunk *list)
99 {
100     free(list);
101 }
102 #endif
103
104 static thunk *
105 Add_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
125 static char *
126 Copy_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
139 static int
140 Read_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
150 static void
151 Read_White_Space (FILE *stream)
152 {
153     int c;
154
155     while ((c = getc(stream)) == ' ' || c == '\n' || c == '\t');
156     ungetc(c, stream);
157 }
158
159 static char _large_buffer[MAXSTR+10];
160
161 static char *
162 Read_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
205 static const char *
206 Skip_Digits (const char *string)
207 {
208     while (isdigit((unsigned char) string[0])) string++;
209     return string;
210 }
211
212 static const char *
213 Scan_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
222 static const char *
223 Scan_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
230 static Atom
231 Parse_Atom (const char *name, int only_if_exists)
232 {
233     /* may return None = 0 */
234     return XInternAtom(dpy, name, only_if_exists);
235 }
236
237 static const char *
238 Skip_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
269 static thunk *_property_formats = NULL;   /* Holds mapping */
270
271 static void
272 Apply_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
280 static void
281 Lookup_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
296 static void
297 Add_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
317 typedef 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
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}
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  */
435 static 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
493 static int XpropMode;
494 #define XpropWindowProperties 0
495 #define XpropFontProperties   1
496
497 static void
498 Setup_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
520 static const char *
521 GetAtomName (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
545 static void
546 Read_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  */
583 static char _formatting_buffer[MAXSTR+100];
584 static char _formatting_buffer2[21];
585
586 static const char *
587 Format_Hex (long wrd)
588 {
589     snprintf(_formatting_buffer2, sizeof(_formatting_buffer2), "0x%lx", wrd);
590     return _formatting_buffer2;
591 }
592
593 static const char *
594 Format_Unsigned (long wrd)
595 {
596     snprintf(_formatting_buffer2, sizeof(_formatting_buffer2), "%lu", wrd);
597     return _formatting_buffer2;
598 }
599
600 static const char *
601 Format_Signed (long wrd)
602 {
603     snprintf(_formatting_buffer2, sizeof(_formatting_buffer2), "%ld", wrd);
604     return _formatting_buffer2;
605 }
606
607 /*ARGSUSED*/
608 static int
609 ignore_errors (Display *dpy, XErrorEvent *ev)
610 {
611     return 0;
612 }
613
614 static const char *
615 Format_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
640 static const char *
641 Format_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
661 static const char *
662 Format_Bool (long value)
663 {
664     if (!value)
665         return "False";
666
667     return "True";
668 }
669
670 static char *_buf_ptr;
671 static int _buf_len;
672
673 static 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
684 static 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
716 static const char *
717 Format_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
733 static const char *
734 Format_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
750 static int
751 is_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
762 static const char *
763 Format_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
857 static const char *
858 Format_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
938 static int
939 is_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
986 static const char *
987 Format_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
1039 static int
1040 Is_A_Format (const char *string)
1041 {
1042     return isdigit((unsigned char) string[0]);
1043 }
1044
1045 static int
1046 Get_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
1059 static char
1060 Get_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
1076 static const char *
1077 Format_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
1108 static const char *
1109 Format_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
1117 static long
1118 Mask_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
1134 static int
1135 Is_A_DFormat (const char *string)
1136 {
1137     return string[0] && string[0] != '-'
1138            && !(isalpha((unsigned char) string[0]) || string[0] == '_');
1139 }
1140
1141 static const char *
1142 Handle_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
1175 static const char *
1176 Handle_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
1197 static int
1198 Mask_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
1210 static const char *
1211 Scan_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
1233 static const char *
1234 Scan_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
1260 static const char *
1261 Handle_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
1277 static void
1278 Display_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
1307 static long
1308 Extract_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
1343 static long
1344 Extract_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
1356 static long
1357 Extract_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
1370 static thunk *
1371 Break_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
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;
1409
1410 /*
1411  *
1412  * Other Stuff (temp.):
1413  *
1414  */
1415
1416 static const char *
1417 Get_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
1435 static const char *
1436 Get_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
1472 static const char *
1473 Get_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
1481 static void
1482 Show_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
1523 static void
1524 Show_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
1544 static thunk *
1545 Handle_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
1586 static void
1587 Remove_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
1599 static void
1600 Set_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
1773 void
1774 usage (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
1802 static void
1803 grammar (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
1826 static void
1827 Parse_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
1855 static int spy = 0;
1856
1857 static int (*old_error_handler)(Display *dpy, XErrorEvent *ev);
1858
1859 static 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
1873 int
1874 main (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 }