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