xprop.c revision c8df0c59
1fc89c0fbSmrg/*
2fc89c0fbSmrg
3fc89c0fbSmrgCopyright 1990, 1998  The Open Group
4fc89c0fbSmrgCopyright (c) 2000  The XFree86 Project, Inc.
5fc89c0fbSmrg
6fc89c0fbSmrgPermission to use, copy, modify, distribute, and sell this software and its
7fc89c0fbSmrgdocumentation for any purpose is hereby granted without fee, provided that
8fc89c0fbSmrgthe above copyright notice appear in all copies and that both that
9fc89c0fbSmrgcopyright notice and this permission notice appear in supporting
10fc89c0fbSmrgdocumentation.
11fc89c0fbSmrg
12fc89c0fbSmrgThe above copyright notice and this permission notice shall be included
13fc89c0fbSmrgin all copies or substantial portions of the Software.
14fc89c0fbSmrg
15fc89c0fbSmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16fc89c0fbSmrgOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17fc89c0fbSmrgMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18fc89c0fbSmrgIN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
19fc89c0fbSmrgOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20fc89c0fbSmrgARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21fc89c0fbSmrgOTHER DEALINGS IN THE SOFTWARE.
22fc89c0fbSmrg
23fc89c0fbSmrgExcept as contained in this notice, the name of The Open Group shall
24fc89c0fbSmrgnot be used in advertising or otherwise to promote the sale, use or
25fc89c0fbSmrgother dealings in this Software without prior written authorization
26fc89c0fbSmrgfrom The Open Group.
27fc89c0fbSmrg
28fc89c0fbSmrg*/
29fc89c0fbSmrg
30fc89c0fbSmrg#include "config.h"
31fc89c0fbSmrg
32fc89c0fbSmrg#include <X11/Xlib.h>
33fc89c0fbSmrg#include <X11/Xos.h>
34fc89c0fbSmrg#include <X11/Xfuncs.h>
35fc89c0fbSmrg#include <X11/Xutil.h>
36f9c28e31Smrg#include <sys/ioctl.h>
37fc89c0fbSmrg#include <stdlib.h>
38fc89c0fbSmrg#include <stdio.h>
39fc89c0fbSmrg#include <ctype.h>
40fc89c0fbSmrg#ifdef HAVE_WCHAR_H
41fc89c0fbSmrg#include <wchar.h>
42fc89c0fbSmrg#endif
43fc89c0fbSmrg#ifdef HAVE_WCTYPE_H
44fc89c0fbSmrg#include <wctype.h>
45fc89c0fbSmrg#endif
46fc89c0fbSmrg#include <locale.h>
4791ec45ceSmrg#ifdef HAVE_LANGINFO_H
4891ec45ceSmrg#include <langinfo.h>
4991ec45ceSmrg#endif
50fc89c0fbSmrg
51fc89c0fbSmrg#ifndef HAVE_WCTYPE_H
52fc89c0fbSmrg#define iswprint(x) isprint(x)
53fc89c0fbSmrg#endif
54fc89c0fbSmrg
55fc89c0fbSmrg#include <X11/Xatom.h>
56fc89c0fbSmrg
57fc89c0fbSmrg#include "dsimple.h"
58fc89c0fbSmrg
59fc89c0fbSmrg#define MAXSTR 500000
60fc89c0fbSmrg#define MAXELEMENTS 64
61fc89c0fbSmrg
62fc89c0fbSmrg#ifndef min
63fc89c0fbSmrg#define min(a,b)  ((a) < (b) ? (a) : (b))
64fc89c0fbSmrg#endif
65fc89c0fbSmrg
66fc89c0fbSmrg/* isprint() in "C" locale */
67fc89c0fbSmrg#define c_isprint(c) ((c) >= 0x20 && (c) < 0x7f)
68fc89c0fbSmrg
6949bee38cSmrgstatic unsigned int term_width = 144 + 8;
70f9c28e31Smrg
71fc89c0fbSmrg/*
72fc89c0fbSmrg *
73fc89c0fbSmrg * The Thunk Manager - routines to create, add to, and free thunk lists
74fc89c0fbSmrg *
75fc89c0fbSmrg */
76fc89c0fbSmrg
77fc89c0fbSmrgtypedef struct {
78fc89c0fbSmrg  int thunk_count;
79fc89c0fbSmrg  const char *propname;
80fc89c0fbSmrg  long value;
81fc89c0fbSmrg  Atom extra_encoding;
82fc89c0fbSmrg  const char *extra_value;
83fc89c0fbSmrg  const char *format;
84fc89c0fbSmrg  const char *dformat;
85fc89c0fbSmrg} thunk;
86fc89c0fbSmrg
87fc89c0fbSmrgstatic thunk *
88fc89c0fbSmrgCreate_Thunk_List (void)
89fc89c0fbSmrg{
90fc89c0fbSmrg    thunk *tptr;
91fc89c0fbSmrg
92f4a95042Smrg    tptr = malloc(sizeof(thunk));
93f4a95042Smrg    if (!tptr)
94f4a95042Smrg	Fatal_Error("Out of memory!");
95fc89c0fbSmrg
96fc89c0fbSmrg    tptr->thunk_count = 0;
97fc89c0fbSmrg
98fc89c0fbSmrg    return tptr;
99fc89c0fbSmrg}
100fc89c0fbSmrg
101fc89c0fbSmrgstatic thunk *
102fc89c0fbSmrgAdd_Thunk (thunk *list, thunk t)
103fc89c0fbSmrg{
104fc89c0fbSmrg    int i;
105fc89c0fbSmrg
106fc89c0fbSmrg    i = list->thunk_count;
107fc89c0fbSmrg
108f4a95042Smrg    list = realloc(list, (i+1)*sizeof(thunk));
109fc89c0fbSmrg    if (!list)
110fc89c0fbSmrg	Fatal_Error("Out of memory!");
111fc89c0fbSmrg
112fc89c0fbSmrg    list[i++] = t;
113fc89c0fbSmrg    list->thunk_count = i;
114fc89c0fbSmrg
115fc89c0fbSmrg    return list;
116fc89c0fbSmrg}
117fc89c0fbSmrg
118fc89c0fbSmrg/*
119fc89c0fbSmrg * Misc. routines
120fc89c0fbSmrg */
121fc89c0fbSmrg
122fc89c0fbSmrgstatic int
123fc89c0fbSmrgRead_Char (FILE *stream)
124fc89c0fbSmrg{
125fc89c0fbSmrg    int c;
126fc89c0fbSmrg
127fc89c0fbSmrg    c = getc(stream);
128fc89c0fbSmrg    if (c == EOF)
129fc89c0fbSmrg	Fatal_Error("Bad format file: Unexpected EOF.");
130fc89c0fbSmrg    return c;
131fc89c0fbSmrg}
132fc89c0fbSmrg
133fc89c0fbSmrgstatic void
134fc89c0fbSmrgRead_White_Space (FILE *stream)
135fc89c0fbSmrg{
136fc89c0fbSmrg    int c;
137fc89c0fbSmrg
138fc89c0fbSmrg    while ((c = getc(stream)) == ' ' || c == '\n' || c == '\t');
139fc89c0fbSmrg    ungetc(c, stream);
140fc89c0fbSmrg}
141fc89c0fbSmrg
142fc89c0fbSmrgstatic char _large_buffer[MAXSTR+10];
143fc89c0fbSmrg
144fc89c0fbSmrgstatic char *
145fc89c0fbSmrgRead_Quoted (FILE *stream)
146fc89c0fbSmrg{
147fc89c0fbSmrg    char *ptr;
14849bee38cSmrg    int length;
149fc89c0fbSmrg
150fc89c0fbSmrg    Read_White_Space(stream);
151fc89c0fbSmrg    if (Read_Char(stream)!='\'')
152fc89c0fbSmrg	Fatal_Error("Bad format file format: missing dformat.");
153fc89c0fbSmrg
154fc89c0fbSmrg    ptr = _large_buffer; length = MAXSTR;
155fc89c0fbSmrg    for (;;) {
15649bee38cSmrg	int c;
15749bee38cSmrg
158fc89c0fbSmrg	if (length < 0)
159fc89c0fbSmrg	    Fatal_Error("Bad format file format: dformat too long.");
160fc89c0fbSmrg	c = Read_Char(stream);
161fc89c0fbSmrg	if (c == (int) '\'')
162fc89c0fbSmrg	    break;
163fc89c0fbSmrg	ptr++[0] = c; length--;
164fc89c0fbSmrg	if (c == (int) '\\') {
165fc89c0fbSmrg	    c = Read_Char(stream);
166fc89c0fbSmrg	    if (c == '\n') {
167fc89c0fbSmrg		ptr--; length++;
168b5cf802fSmrg	    } else {
169fc89c0fbSmrg		ptr++[0] = c; length--;
170b5cf802fSmrg	    }
171fc89c0fbSmrg	}
172fc89c0fbSmrg    }
173fc89c0fbSmrg    ptr++[0] = '\0';
174fc89c0fbSmrg
175f4a95042Smrg    ptr = strdup(_large_buffer);
176f4a95042Smrg    if (!ptr)
177f4a95042Smrg	Fatal_Error("Out of memory!");
178f4a95042Smrg    return ptr;
179fc89c0fbSmrg}
180fc89c0fbSmrg
181fc89c0fbSmrg/*
182fc89c0fbSmrg *
183fc89c0fbSmrg * Parsing Routines: a group of routines to parse strings into values
184fc89c0fbSmrg *
185fc89c0fbSmrg * Routines: Parse_Atom, Scan_Long, Skip_Past_Right_Paren, Scan_Octal
186fc89c0fbSmrg *
187fc89c0fbSmrg * Routines of the form Parse_XXX take a string which is parsed to a value.
188fc89c0fbSmrg * Routines of the form Scan_XXX take a string, parse the beginning to a value,
189fc89c0fbSmrg * and return the rest of the string.  The value is returned via. the last
190fc89c0fbSmrg * parameter.  All numeric values are longs!
191fc89c0fbSmrg *
192fc89c0fbSmrg */
193fc89c0fbSmrg
194fc89c0fbSmrgstatic const char *
195fc89c0fbSmrgSkip_Digits (const char *string)
196fc89c0fbSmrg{
197fc89c0fbSmrg    while (isdigit((unsigned char) string[0])) string++;
198fc89c0fbSmrg    return string;
199fc89c0fbSmrg}
200fc89c0fbSmrg
201fc89c0fbSmrgstatic const char *
202fc89c0fbSmrgScan_Long (const char *string, long *value)
203fc89c0fbSmrg{
204fc89c0fbSmrg    if (!isdigit((unsigned char) *string))
205fc89c0fbSmrg	Fatal_Error("Bad number: %s.", string);
206fc89c0fbSmrg
207fc89c0fbSmrg    *value = atol(string);
208fc89c0fbSmrg    return Skip_Digits(string);
209fc89c0fbSmrg}
210fc89c0fbSmrg
211fc89c0fbSmrgstatic const char *
212fc89c0fbSmrgScan_Octal (const char *string, unsigned long *value)
213fc89c0fbSmrg{
214fc89c0fbSmrg    if (sscanf(string, "%lo", value)!=1)
215fc89c0fbSmrg	Fatal_Error("Bad octal number: %s.", string);
216fc89c0fbSmrg    return Skip_Digits(string);
217fc89c0fbSmrg}
218fc89c0fbSmrg
219fc89c0fbSmrgstatic Atom
220fc89c0fbSmrgParse_Atom (const char *name, int only_if_exists)
221fc89c0fbSmrg{
222fc89c0fbSmrg    /* may return None = 0 */
223fc89c0fbSmrg    return XInternAtom(dpy, name, only_if_exists);
224fc89c0fbSmrg}
225fc89c0fbSmrg
226fc89c0fbSmrgstatic const char *
227fc89c0fbSmrgSkip_Past_Right_Paren (const char *string)
228fc89c0fbSmrg{
229fc89c0fbSmrg    char c;
230fc89c0fbSmrg    int nesting = 0;
231fc89c0fbSmrg
232fc89c0fbSmrg    while (c = string++[0], c != ')' || nesting)
233fc89c0fbSmrg	switch (c) {
234fc89c0fbSmrg	  case '\0':
235fc89c0fbSmrg	    Fatal_Error("Missing ')'.");
236fc89c0fbSmrg	  case '(':
237fc89c0fbSmrg	    nesting++;
238fc89c0fbSmrg	    break;
239fc89c0fbSmrg	  case ')':
240fc89c0fbSmrg	    nesting--;
241fc89c0fbSmrg	    break;
242fc89c0fbSmrg	  case '\\':
243fc89c0fbSmrg	    string++;
244fc89c0fbSmrg	    break;
245fc89c0fbSmrg	}
246fc89c0fbSmrg    return string;
247fc89c0fbSmrg}
248fc89c0fbSmrg
249fc89c0fbSmrg/*
250fc89c0fbSmrg *
251fc89c0fbSmrg * Atom to format, dformat mapping Manager
252fc89c0fbSmrg *
253fc89c0fbSmrg */
254fc89c0fbSmrg
255fc89c0fbSmrg#define D_FORMAT "0x"              /* Default format for properties */
256fc89c0fbSmrg#define D_DFORMAT " = $0+\n"       /* Default display pattern for properties */
257fc89c0fbSmrg
258fc89c0fbSmrgstatic thunk *_property_formats = NULL;   /* Holds mapping */
259fc89c0fbSmrg
260fc89c0fbSmrgstatic void
261fc89c0fbSmrgApply_Default_Formats (const char **format, const char **dformat)
262fc89c0fbSmrg{
263fc89c0fbSmrg    if (!*format)
264fc89c0fbSmrg	*format = D_FORMAT;
265fc89c0fbSmrg    if (!*dformat)
266fc89c0fbSmrg	*dformat = D_DFORMAT;
267fc89c0fbSmrg}
268fc89c0fbSmrg
269fc89c0fbSmrgstatic void
270fc89c0fbSmrgLookup_Formats (Atom atom, const char **format, const char **dformat)
271fc89c0fbSmrg{
272fc89c0fbSmrg    if (_property_formats)
27349bee38cSmrg	for (int i = _property_formats->thunk_count-1; i >= 0; i--) {
27449bee38cSmrg	    if (_property_formats[i].value == (long) atom) {
275fc89c0fbSmrg		if (!*format)
276fc89c0fbSmrg		    *format = _property_formats[i].format;
277fc89c0fbSmrg		if (!*dformat)
278fc89c0fbSmrg		    *dformat = _property_formats[i].dformat;
279fc89c0fbSmrg		break;
280fc89c0fbSmrg	    }
28149bee38cSmrg	}
282fc89c0fbSmrg}
283fc89c0fbSmrg
284fc89c0fbSmrgstatic void
285fc89c0fbSmrgAdd_Mapping (Atom atom, const char *format, const char *dformat)
286fc89c0fbSmrg{
287f4a95042Smrg    thunk t = {0};
288fc89c0fbSmrg
289fc89c0fbSmrg    if (!_property_formats)
290fc89c0fbSmrg	_property_formats = Create_Thunk_List();
291fc89c0fbSmrg
292fc89c0fbSmrg    t.value = atom;
293fc89c0fbSmrg    t.format = format;
294fc89c0fbSmrg    t.dformat = dformat;
295fc89c0fbSmrg
296fc89c0fbSmrg    _property_formats = Add_Thunk(_property_formats, t);
297fc89c0fbSmrg}
298fc89c0fbSmrg
299fc89c0fbSmrg/*
300fc89c0fbSmrg *
301fc89c0fbSmrg * Setup_Mapping: Routine to setup default atom to format, dformat mapping:
302fc89c0fbSmrg *
303fc89c0fbSmrg */
304fc89c0fbSmrg
305fc89c0fbSmrgtypedef struct _propertyRec {
306fc89c0fbSmrg    const char *	name;
307fc89c0fbSmrg    Atom		atom;
308fc89c0fbSmrg    const char *	format;
309fc89c0fbSmrg    const char *	dformat;
310fc89c0fbSmrg} propertyRec;
311fc89c0fbSmrg
312fc89c0fbSmrg#define ARC_DFORMAT	":\n"\
313fc89c0fbSmrg"\t\tarc at $0, $1\n"\
314fc89c0fbSmrg"\t\tsize: $2 by $3\n"\
315fc89c0fbSmrg"\t\tfrom angle $4 to angle $5\n"
316fc89c0fbSmrg
317fc89c0fbSmrg#define RECTANGLE_DFORMAT	":\n"\
318fc89c0fbSmrg"\t\tupper left corner: $0, $1\n"\
319fc89c0fbSmrg"\t\tsize: $2 by $3\n"
320fc89c0fbSmrg
321fc89c0fbSmrg#define RGB_COLOR_MAP_DFORMAT	":\n"\
322fc89c0fbSmrg"\t\tcolormap id #: $0\n"\
323fc89c0fbSmrg"\t\tred-max: $1\n"\
324fc89c0fbSmrg"\t\tred-mult: $2\n"\
325fc89c0fbSmrg"\t\tgreen-max: $3\n"\
326fc89c0fbSmrg"\t\tgreen-mult: $4\n"\
327fc89c0fbSmrg"\t\tblue-max: $5\n"\
328fc89c0fbSmrg"\t\tblue-mult: $6\n"\
329fc89c0fbSmrg"\t\tbase-pixel: $7\n"\
330fc89c0fbSmrg"\t\tvisual id #: $8\n"\
331fc89c0fbSmrg"\t\tkill id #: $9\n"
332fc89c0fbSmrg
333fc89c0fbSmrg#define WM_HINTS_DFORMAT	":\n"\
334fc89c0fbSmrg"?m0(\t\tClient accepts input or input focus: $1\n)"\
335fc89c0fbSmrg"?m1(\t\tInitial state is "\
336fc89c0fbSmrg"?$2=0(Don't Care State)"\
337fc89c0fbSmrg"?$2=1(Normal State)"\
338fc89c0fbSmrg"?$2=2(Zoomed State)"\
339fc89c0fbSmrg"?$2=3(Iconic State)"\
340fc89c0fbSmrg"?$2=4(Inactive State)"\
341fc89c0fbSmrg".\n)"\
342fc89c0fbSmrg"?m2(\t\tbitmap id # to use for icon: $3\n)"\
343fc89c0fbSmrg"?m5(\t\tbitmap id # of mask for icon: $7\n)"\
344fc89c0fbSmrg"?m3(\t\twindow id # to use for icon: $4\n)"\
345fc89c0fbSmrg"?m4(\t\tstarting position for icon: $5, $6\n)"\
346fc89c0fbSmrg"?m6(\t\twindow id # of group leader: $8\n)"\
34791ec45ceSmrg"?m8(\t\tThe urgency hint bit is set\n)"
348fc89c0fbSmrg
349fc89c0fbSmrg#define WM_ICON_SIZE_DFORMAT	":\n"\
350fc89c0fbSmrg"\t\tminimum icon size: $0 by $1\n"\
351fc89c0fbSmrg"\t\tmaximum icon size: $2 by $3\n"\
352fc89c0fbSmrg"\t\tincremental size change: $4 by $5\n"
353fc89c0fbSmrg
354fc89c0fbSmrg#define WM_SIZE_HINTS_DFORMAT ":\n"\
355fc89c0fbSmrg"?m0(\t\tuser specified location: $1, $2\n)"\
356fc89c0fbSmrg"?m2(\t\tprogram specified location: $1, $2\n)"\
357fc89c0fbSmrg"?m1(\t\tuser specified size: $3 by $4\n)"\
358fc89c0fbSmrg"?m3(\t\tprogram specified size: $3 by $4\n)"\
359fc89c0fbSmrg"?m4(\t\tprogram specified minimum size: $5 by $6\n)"\
360fc89c0fbSmrg"?m5(\t\tprogram specified maximum size: $7 by $8\n)"\
361fc89c0fbSmrg"?m6(\t\tprogram specified resize increment: $9 by $10\n)"\
362fc89c0fbSmrg"?m7(\t\tprogram specified minimum aspect ratio: $11/$12\n"\
363fc89c0fbSmrg"\t\tprogram specified maximum aspect ratio: $13/$14\n)"\
364fc89c0fbSmrg"?m8(\t\tprogram specified base size: $15 by $16\n)"\
365fc89c0fbSmrg"?m9(\t\twindow gravity: "\
366fc89c0fbSmrg"?$17=0(Forget)"\
367fc89c0fbSmrg"?$17=1(NorthWest)"\
368fc89c0fbSmrg"?$17=2(North)"\
369fc89c0fbSmrg"?$17=3(NorthEast)"\
370fc89c0fbSmrg"?$17=4(West)"\
371fc89c0fbSmrg"?$17=5(Center)"\
372fc89c0fbSmrg"?$17=6(East)"\
373fc89c0fbSmrg"?$17=7(SouthWest)"\
374fc89c0fbSmrg"?$17=8(South)"\
375fc89c0fbSmrg"?$17=9(SouthEast)"\
376fc89c0fbSmrg"?$17=10(Static)"\
377fc89c0fbSmrg"\n)"
378fc89c0fbSmrg
379fc89c0fbSmrg#define WM_STATE_DFORMAT	 ":\n"\
380fc89c0fbSmrg"\t\twindow state: ?$0=0(Withdrawn)?$0=1(Normal)?$0=3(Iconic)\n"\
381fc89c0fbSmrg"\t\ticon window: $1\n"
382fc89c0fbSmrg
383fc89c0fbSmrgstatic propertyRec windowPropTable[] = {
384fc89c0fbSmrg    {"ARC",		XA_ARC,		"16iiccii",   ARC_DFORMAT },
385fc89c0fbSmrg    {"ATOM",		XA_ATOM,	 "32a",	      0 },
386fc89c0fbSmrg    {"BITMAP",		XA_BITMAP,	 "32x",	      ": bitmap id # $0\n" },
387fc89c0fbSmrg    {"CARDINAL",	XA_CARDINAL,	 "0c",	      0 },
388fc89c0fbSmrg    {"COLORMAP",	XA_COLORMAP,	 "32x",	      ": colormap id # $0\n" },
389fc89c0fbSmrg    {"CURSOR",		XA_CURSOR,	 "32x",	      ": cursor id # $0\n" },
390fc89c0fbSmrg    {"DRAWABLE",	XA_DRAWABLE,	 "32x",	      ": drawable id # $0\n" },
391fc89c0fbSmrg    {"FONT",		XA_FONT,	 "32x",	      ": font id # $0\n" },
392fc89c0fbSmrg    {"INTEGER",		XA_INTEGER,	 "0i",	      0 },
393fc89c0fbSmrg    {"PIXMAP",		XA_PIXMAP,	 "32x",	      ": pixmap id # $0\n" },
394fc89c0fbSmrg    {"POINT",		XA_POINT,	 "16ii",      " = $0, $1\n" },
395fc89c0fbSmrg    {"RECTANGLE",	XA_RECTANGLE,	 "16iicc",    RECTANGLE_DFORMAT },
396fc89c0fbSmrg    {"RGB_COLOR_MAP",	XA_RGB_COLOR_MAP,"32xcccccccxx",RGB_COLOR_MAP_DFORMAT},
397fc89c0fbSmrg    {"STRING",		XA_STRING,	 "8s",	      0 },
398a0996ce0Smrg    {"UTF8_STRING",		0,	 "8u",	      0 },
399fc89c0fbSmrg    {"WINDOW",		XA_WINDOW,	 "32x",	      ": window id # $0+\n" },
400fc89c0fbSmrg    {"VISUALID",	XA_VISUALID,	 "32x",	      ": visual id # $0\n" },
401fc89c0fbSmrg    {"WM_COLORMAP_WINDOWS",	0,	 "32x",       ": window id # $0+\n"},
402fc89c0fbSmrg    {"WM_COMMAND",	XA_WM_COMMAND,	 "8s",	      " = { $0+ }\n" },
403fc89c0fbSmrg    {"WM_HINTS",	XA_WM_HINTS,	 "32mbcxxiixx",	WM_HINTS_DFORMAT },
404fc89c0fbSmrg    {"WM_ICON_NAME",	XA_WM_ICON_NAME, "8t",	      0 },
405fc89c0fbSmrg    {"WM_ICON_SIZE",	XA_WM_ICON_SIZE, "32cccccc",  WM_ICON_SIZE_DFORMAT},
406fc89c0fbSmrg    {"WM_NAME",		XA_WM_NAME,	 "8t",	      0 },
407fc89c0fbSmrg    {"WM_PROTOCOLS",		0,	 "32a",	      ": protocols  $0+\n"},
408fc89c0fbSmrg    {"WM_SIZE_HINTS",	XA_WM_SIZE_HINTS,"32mii",     WM_SIZE_HINTS_DFORMAT },
40991ec45ceSmrg    {"_NET_WM_ICON",            0,       "32o",        0 },
410fc89c0fbSmrg    {"WM_STATE",		0,	 "32cx",      WM_STATE_DFORMAT}
411fc89c0fbSmrg};
412fc89c0fbSmrg#undef ARC_DFORMAT
413fc89c0fbSmrg#undef RECTANGLE_DFORMAT
414fc89c0fbSmrg#undef RGB_COLOR_MAP_DFORMAT
415fc89c0fbSmrg#undef WM_ICON_SIZE_DFORMAT
416fc89c0fbSmrg#undef WM_HINTS_DFORMAT
417fc89c0fbSmrg#undef WM_SIZE_HINTS_DFORMAT
418fc89c0fbSmrg#undef WM_STATE_DFORMAT
419fc89c0fbSmrg
420fc89c0fbSmrg/*
421fc89c0fbSmrg * Font-specific mapping of property names to types:
422fc89c0fbSmrg */
423fc89c0fbSmrgstatic propertyRec fontPropTable[] = {
424fc89c0fbSmrg
425fc89c0fbSmrg    /* XLFD name properties */
426fc89c0fbSmrg
427fc89c0fbSmrg    { "FOUNDRY",		0, 	 		"32a",	0 },
428fc89c0fbSmrg    { "FAMILY_NAME",		XA_FAMILY_NAME,		"32a",	0 },
429fc89c0fbSmrg    { "WEIGHT_NAME",		0,			"32a",	0 },
430fc89c0fbSmrg    { "SLANT",			0,			"32a",	0 },
431fc89c0fbSmrg    { "SETWIDTH_NAME",		0,			"32a",	0 },
432fc89c0fbSmrg    { "ADD_STYLE_NAME",		0,			"32a",	0 },
433fc89c0fbSmrg    { "PIXEL_SIZE",		0,			"32c",	0 },
434fc89c0fbSmrg    { "POINT_SIZE",		XA_POINT_SIZE,		"32c",	0 },
435fc89c0fbSmrg    { "RESOLUTION_X",		0,			"32c",	0 },
436fc89c0fbSmrg    { "RESOLUTION_Y",		0,			"32c",	0 },
437fc89c0fbSmrg    { "SPACING",		0,			"32a",	0 },
438fc89c0fbSmrg    { "AVERAGE_WIDTH",		0,			"32c",	0 },
439fc89c0fbSmrg    { "CHARSET_REGISTRY",	0,			"32a",	0 },
440fc89c0fbSmrg    { "CHARSET_ENCODING",	0,			"32a",	0 },
441fc89c0fbSmrg
442fc89c0fbSmrg    /* other font properties referenced in the XLFD */
443fc89c0fbSmrg
444fc89c0fbSmrg    { "QUAD_WIDTH",		XA_QUAD_WIDTH,		"32i",	0 },
445fc89c0fbSmrg    { "RESOLUTION",		XA_RESOLUTION,		"32c",	0 },
446fc89c0fbSmrg    { "MIN_SPACE",		XA_MIN_SPACE,		"32c",	0 },
447fc89c0fbSmrg    { "NORM_SPACE",		XA_NORM_SPACE,		"32c",	0 },
448fc89c0fbSmrg    { "MAX_SPACE",		XA_MAX_SPACE,		"32c",	0 },
449fc89c0fbSmrg    { "END_SPACE",		XA_END_SPACE,		"32c",	0 },
450fc89c0fbSmrg    { "SUPERSCRIPT_X",		XA_SUPERSCRIPT_X,	"32i",	0 },
451fc89c0fbSmrg    { "SUPERSCRIPT_Y",		XA_SUPERSCRIPT_Y,	"32i",	0 },
452fc89c0fbSmrg    { "SUBSCRIPT_X",		XA_SUBSCRIPT_X,		"32i",	0 },
453fc89c0fbSmrg    { "SUBSCRIPT_Y",		XA_SUBSCRIPT_Y,		"32i",	0 },
454fc89c0fbSmrg    { "UNDERLINE_POSITION",	XA_UNDERLINE_POSITION,	"32i",	0 },
455fc89c0fbSmrg    { "UNDERLINE_THICKNESS",	XA_UNDERLINE_THICKNESS,	"32i",	0 },
456fc89c0fbSmrg    { "STRIKEOUT_ASCENT",	XA_STRIKEOUT_ASCENT,	"32i",	0 },
457fc89c0fbSmrg    { "STRIKEOUT_DESCENT",	XA_STRIKEOUT_DESCENT,	"32i",	0 },
458fc89c0fbSmrg    { "ITALIC_ANGLE",		XA_ITALIC_ANGLE,	"32i",	0 },
459fc89c0fbSmrg    { "X_HEIGHT",		XA_X_HEIGHT,		"32i",	0 },
460fc89c0fbSmrg    { "WEIGHT",			XA_WEIGHT,		"32i",	0 },
461fc89c0fbSmrg    { "FACE_NAME",		0,			"32a",	0 },
462fc89c0fbSmrg    { "COPYRIGHT",		XA_COPYRIGHT,		"32a",	0 },
463fc89c0fbSmrg    { "AVG_CAPITAL_WIDTH",	0,			"32i",	0 },
464fc89c0fbSmrg    { "AVG_LOWERCASE_WIDTH",	0,			"32i",	0 },
465fc89c0fbSmrg    { "RELATIVE_SETWIDTH",	0,			"32c",	0 },
466fc89c0fbSmrg    { "RELATIVE_WEIGHT",	0,			"32c",	0 },
467fc89c0fbSmrg    { "CAP_HEIGHT",		XA_CAP_HEIGHT,		"32c",	0 },
468fc89c0fbSmrg    { "SUPERSCRIPT_SIZE",	0,			"32c",	0 },
469fc89c0fbSmrg    { "FIGURE_WIDTH",		0,			"32i",	0 },
470fc89c0fbSmrg    { "SUBSCRIPT_SIZE",		0,			"32c",	0 },
471fc89c0fbSmrg    { "SMALL_CAP_SIZE",		0,			"32i",	0 },
472fc89c0fbSmrg    { "NOTICE",			XA_NOTICE,		"32a",	0 },
473fc89c0fbSmrg    { "DESTINATION",		0,			"32c",	0 },
474fc89c0fbSmrg
475fc89c0fbSmrg    /* other font properties */
476fc89c0fbSmrg
477fc89c0fbSmrg    { "FONT",			XA_FONT,		"32a",	0 },
478fc89c0fbSmrg    { "FONT_NAME",		XA_FONT_NAME,		"32a",	0 },
479fc89c0fbSmrg};
480fc89c0fbSmrg
481fc89c0fbSmrgstatic int XpropMode;
482fc89c0fbSmrg#define XpropWindowProperties 0
483fc89c0fbSmrg#define XpropFontProperties   1
484fc89c0fbSmrg
485fc89c0fbSmrgstatic void
486fc89c0fbSmrgSetup_Mapping (void)
487fc89c0fbSmrg{
488fc89c0fbSmrg    int n;
489fc89c0fbSmrg    propertyRec *p;
490fc89c0fbSmrg
491fc89c0fbSmrg    if (XpropMode == XpropWindowProperties) {
492fc89c0fbSmrg	n = sizeof(windowPropTable) / sizeof(propertyRec);
493fc89c0fbSmrg	p = windowPropTable;
494fc89c0fbSmrg    } else {
495fc89c0fbSmrg	n = sizeof (fontPropTable) / sizeof (propertyRec);
496fc89c0fbSmrg	p = fontPropTable;
497fc89c0fbSmrg    }
498fc89c0fbSmrg    for ( ; --n >= 0; p++) {
499fc89c0fbSmrg	if (! p->atom) {
500fc89c0fbSmrg	    p->atom = XInternAtom(dpy, p->name, True);
501fc89c0fbSmrg	    if (p->atom == None)
502fc89c0fbSmrg		continue;
503fc89c0fbSmrg	}
504fc89c0fbSmrg	Add_Mapping(p->atom, p->format, p->dformat);
505fc89c0fbSmrg    }
506fc89c0fbSmrg}
507fc89c0fbSmrg
508fc89c0fbSmrgstatic const char *
509fc89c0fbSmrgGetAtomName (Atom atom)
510fc89c0fbSmrg{
511fc89c0fbSmrg    int n;
512fc89c0fbSmrg    propertyRec *p;
513fc89c0fbSmrg
514fc89c0fbSmrg    if (XpropMode == XpropWindowProperties) {
515fc89c0fbSmrg	n = sizeof(windowPropTable) / sizeof(propertyRec);
516fc89c0fbSmrg	p = windowPropTable;
517fc89c0fbSmrg    } else {
518fc89c0fbSmrg	n = sizeof (fontPropTable) / sizeof (propertyRec);
519fc89c0fbSmrg	p = fontPropTable;
520fc89c0fbSmrg    }
521fc89c0fbSmrg    for ( ; --n >= 0; p++)
522fc89c0fbSmrg	if (p->atom == atom)
523fc89c0fbSmrg	    return p->name;
524fc89c0fbSmrg
525fc89c0fbSmrg    return NULL;
526fc89c0fbSmrg}
527fc89c0fbSmrg
528fc89c0fbSmrg/*
529fc89c0fbSmrg * Read_Mapping: routine to read in additional mappings from a stream
530fc89c0fbSmrg *               already open for reading.
531fc89c0fbSmrg */
532fc89c0fbSmrg
533fc89c0fbSmrgstatic void
534fc89c0fbSmrgRead_Mappings (FILE *stream)
535fc89c0fbSmrg{
536fc89c0fbSmrg    char format_buffer[100];
537576bbdfcSmrg    char name[1000];
53849bee38cSmrg    int count;
539fc89c0fbSmrg
540fc89c0fbSmrg    while ((count = fscanf(stream," %990s %90s ",name,format_buffer)) != EOF) {
54149bee38cSmrg	const char *dformat, *format;
54249bee38cSmrg	int c;
54349bee38cSmrg	Atom atom;
54449bee38cSmrg
545fc89c0fbSmrg	if (count != 2)
546fc89c0fbSmrg	    Fatal_Error("Bad format file format.");
547fc89c0fbSmrg
548fc89c0fbSmrg	atom = Parse_Atom(name, False);
549f4a95042Smrg	format = strdup(format_buffer);
550f4a95042Smrg	if (!format)
551f4a95042Smrg	    Fatal_Error("Out of memory!");
552fc89c0fbSmrg
553fc89c0fbSmrg	Read_White_Space(stream);
554fc89c0fbSmrg	dformat = D_DFORMAT;
555fc89c0fbSmrg	c = getc(stream);
556fc89c0fbSmrg	ungetc(c, stream);
557fc89c0fbSmrg	if (c == (int) '\'')
558fc89c0fbSmrg	    dformat = Read_Quoted(stream);
559fc89c0fbSmrg
560fc89c0fbSmrg	Add_Mapping(atom, format, dformat);
561fc89c0fbSmrg    }
562fc89c0fbSmrg}
563fc89c0fbSmrg
564fc89c0fbSmrg/*
565fc89c0fbSmrg *
566fc89c0fbSmrg * Formatting Routines: a group of routines to translate from various
567fc89c0fbSmrg *                      values to a static read-only string useful for output.
568fc89c0fbSmrg *
569fc89c0fbSmrg * Routines: Format_Hex, Format_Unsigned, Format_Signed, Format_Atom,
570fc89c0fbSmrg *           Format_Mask_Word, Format_Bool, Format_String, Format_Len_String.
571fc89c0fbSmrg *
572fc89c0fbSmrg * All of the above routines take a long except for Format_String and
573fc89c0fbSmrg * Format_Len_String.
574fc89c0fbSmrg *
575fc89c0fbSmrg */
576fc89c0fbSmrgstatic char _formatting_buffer[MAXSTR+100];
577fc89c0fbSmrgstatic char _formatting_buffer2[21];
578fc89c0fbSmrg
579fc89c0fbSmrgstatic const char *
580fc89c0fbSmrgFormat_Hex (long wrd)
581fc89c0fbSmrg{
582fc89c0fbSmrg    snprintf(_formatting_buffer2, sizeof(_formatting_buffer2), "0x%lx", wrd);
583fc89c0fbSmrg    return _formatting_buffer2;
584fc89c0fbSmrg}
585fc89c0fbSmrg
586fc89c0fbSmrgstatic const char *
587fc89c0fbSmrgFormat_Unsigned (long wrd)
588fc89c0fbSmrg{
589fc89c0fbSmrg    snprintf(_formatting_buffer2, sizeof(_formatting_buffer2), "%lu", wrd);
590fc89c0fbSmrg    return _formatting_buffer2;
591fc89c0fbSmrg}
592fc89c0fbSmrg
593fc89c0fbSmrgstatic const char *
594fc89c0fbSmrgFormat_Signed (long wrd)
595fc89c0fbSmrg{
596fc89c0fbSmrg    snprintf(_formatting_buffer2, sizeof(_formatting_buffer2), "%ld", wrd);
597fc89c0fbSmrg    return _formatting_buffer2;
598fc89c0fbSmrg}
599fc89c0fbSmrg
600fc89c0fbSmrg/*ARGSUSED*/
601fc89c0fbSmrgstatic int
60249bee38cSmrgignore_errors (Display *display, XErrorEvent *ev)
603fc89c0fbSmrg{
604fc89c0fbSmrg    return 0;
605fc89c0fbSmrg}
606fc89c0fbSmrg
607fc89c0fbSmrgstatic const char *
608fc89c0fbSmrgFormat_Atom (Atom atom)
609fc89c0fbSmrg{
610fc89c0fbSmrg    const char *found;
611fc89c0fbSmrg    char *name;
612fc89c0fbSmrg    XErrorHandler handler;
613fc89c0fbSmrg
614fc89c0fbSmrg    if ((found = GetAtomName(atom)) != NULL)
615fc89c0fbSmrg	return found;
616fc89c0fbSmrg
617fc89c0fbSmrg    handler = XSetErrorHandler (ignore_errors);
618fc89c0fbSmrg    name = XGetAtomName(dpy, atom);
619fc89c0fbSmrg    XSetErrorHandler(handler);
620fc89c0fbSmrg    if (! name)
621fc89c0fbSmrg	snprintf(_formatting_buffer, sizeof(_formatting_buffer),
622fc89c0fbSmrg		 "undefined atom # 0x%lx", atom);
623fc89c0fbSmrg    else {
62449bee38cSmrg	size_t namelen = strlen(name);
625fc89c0fbSmrg	if (namelen > MAXSTR) namelen = MAXSTR;
626fc89c0fbSmrg	memcpy(_formatting_buffer, name, namelen);
627fc89c0fbSmrg	_formatting_buffer[namelen] = '\0';
628fc89c0fbSmrg	XFree(name);
629fc89c0fbSmrg    }
630fc89c0fbSmrg    return _formatting_buffer;
631fc89c0fbSmrg}
632fc89c0fbSmrg
633fc89c0fbSmrgstatic const char *
634fc89c0fbSmrgFormat_Mask_Word (long wrd)
635fc89c0fbSmrg{
63649bee38cSmrg    unsigned long bit_mask, bit;
637fc89c0fbSmrg    int seen = 0;
638fc89c0fbSmrg
639fc89c0fbSmrg    strcpy(_formatting_buffer, "{MASK: ");
640fc89c0fbSmrg    for (bit=0, bit_mask=1; bit <= sizeof(long)*8; bit++, bit_mask<<=1) {
641fc89c0fbSmrg	if (bit_mask & wrd) {
642fc89c0fbSmrg	    if (seen) {
643fc89c0fbSmrg		strcat(_formatting_buffer, ", ");
644fc89c0fbSmrg	    }
645fc89c0fbSmrg	    seen = 1;
646fc89c0fbSmrg	    strcat(_formatting_buffer, Format_Unsigned(bit));
647fc89c0fbSmrg	}
648fc89c0fbSmrg    }
649fc89c0fbSmrg    strcat(_formatting_buffer, "}");
650fc89c0fbSmrg
651fc89c0fbSmrg    return _formatting_buffer;
652fc89c0fbSmrg}
653fc89c0fbSmrg
654fc89c0fbSmrgstatic const char *
655fc89c0fbSmrgFormat_Bool (long value)
656fc89c0fbSmrg{
657fc89c0fbSmrg    if (!value)
658fc89c0fbSmrg	return "False";
659fc89c0fbSmrg
660fc89c0fbSmrg    return "True";
661fc89c0fbSmrg}
662fc89c0fbSmrg
663fc89c0fbSmrgstatic char *_buf_ptr;
664fc89c0fbSmrgstatic int _buf_len;
665fc89c0fbSmrg
666fc89c0fbSmrgstatic void
667fc89c0fbSmrg_put_char (char c)
668fc89c0fbSmrg{
669fc89c0fbSmrg    if (_buf_len <= 0) {
670fc89c0fbSmrg	_buf_ptr[0] = '\0';
671fc89c0fbSmrg	return;
672fc89c0fbSmrg    }
673fc89c0fbSmrg    _buf_ptr++[0] = c;
674fc89c0fbSmrg    _buf_len--;
675fc89c0fbSmrg}
676fc89c0fbSmrg
677fc89c0fbSmrgstatic void
678a0996ce0Smrg_format_char (char c, int unicode)
679fc89c0fbSmrg{
680fc89c0fbSmrg    switch (c) {
681fc89c0fbSmrg      case '\\':
682fc89c0fbSmrg      case '\"':
683fc89c0fbSmrg	_put_char('\\');
684fc89c0fbSmrg	_put_char(c);
685fc89c0fbSmrg	break;
686fc89c0fbSmrg      case '\n':
687fc89c0fbSmrg	_put_char('\\');
688fc89c0fbSmrg	_put_char('n');
689fc89c0fbSmrg	break;
690fc89c0fbSmrg      case '\t':
691fc89c0fbSmrg	_put_char('\\');
692fc89c0fbSmrg	_put_char('t');
693fc89c0fbSmrg	break;
694fc89c0fbSmrg      default:
695fc89c0fbSmrg	if (!c_isprint(c)) {
696a0996ce0Smrg	    if (unicode && (c & 0x80)) {
697a0996ce0Smrg		_put_char(c);
698a0996ce0Smrg	    } else {
699a0996ce0Smrg		_put_char('\\');
700a0996ce0Smrg		snprintf(_buf_ptr, _buf_len, "%03o", (unsigned char) c);
701a0996ce0Smrg		_buf_ptr += 3;
702a0996ce0Smrg		_buf_len -= 3;
703a0996ce0Smrg	    }
704fc89c0fbSmrg	} else
705fc89c0fbSmrg	  _put_char(c);
706fc89c0fbSmrg    }
707fc89c0fbSmrg}
708fc89c0fbSmrg
709fc89c0fbSmrgstatic const char *
710a0996ce0SmrgFormat_String (const char *string, int unicode)
711fc89c0fbSmrg{
712fc89c0fbSmrg    char c;
713fc89c0fbSmrg
714fc89c0fbSmrg    _buf_ptr = _formatting_buffer;
715fc89c0fbSmrg    _buf_len = MAXSTR;
716fc89c0fbSmrg    _put_char('\"');
717fc89c0fbSmrg
718fc89c0fbSmrg    while ((c = string++[0]))
719a0996ce0Smrg	_format_char(c, unicode);
720fc89c0fbSmrg
721fc89c0fbSmrg    *_buf_ptr++ = '"';
722fc89c0fbSmrg    *_buf_ptr++ = '\0';
723fc89c0fbSmrg    return _formatting_buffer;
724fc89c0fbSmrg}
725fc89c0fbSmrg
726fc89c0fbSmrgstatic const char *
727576bbdfcSmrgFormat_Len_String (const char *string, int len, int unicode)
728fc89c0fbSmrg{
729fc89c0fbSmrg    char *data;
730fc89c0fbSmrg    const char *result;
731fc89c0fbSmrg
732f4a95042Smrg    data = malloc(len+1);
733f4a95042Smrg    if (!data)
734f4a95042Smrg	Fatal_Error("Out of memory!");
735fc89c0fbSmrg
736fc89c0fbSmrg    memcpy(data, string, len);
737fc89c0fbSmrg    data[len] = '\0';
738fc89c0fbSmrg
739576bbdfcSmrg    result = Format_String(data, unicode);
740fc89c0fbSmrg    free(data);
741fc89c0fbSmrg
742fc89c0fbSmrg    return result;
743fc89c0fbSmrg}
744fc89c0fbSmrg
74591ec45ceSmrgstatic int
74691ec45ceSmrgis_utf8_locale (void)
74791ec45ceSmrg{
74891ec45ceSmrg#ifdef HAVE_LANGINFO_H
74991ec45ceSmrg    char *charmap = nl_langinfo (CODESET);
75091ec45ceSmrg
75191ec45ceSmrg    return charmap && strcmp (charmap, "UTF-8") == 0;
75291ec45ceSmrg#else
75391ec45ceSmrg    return 0;
75491ec45ceSmrg#endif
75591ec45ceSmrg}
75691ec45ceSmrg
757f9c28e31Smrgstatic int
758f9c28e31Smrgis_truecolor_term (void)
759f9c28e31Smrg{
760f9c28e31Smrg    char *colorterm = getenv( "COLORTERM" );
761f9c28e31Smrg
762f9c28e31Smrg    if (colorterm && !strcmp(colorterm,"truecolor"))
763f9c28e31Smrg	return 1;
764f9c28e31Smrg
765f9c28e31Smrg    return 0;
766f9c28e31Smrg}
767f9c28e31Smrg
76891ec45ceSmrgstatic const char *
76991ec45ceSmrgFormat_Icons (const unsigned long *icon, int len)
77091ec45ceSmrg{
77181676fe2Smrg    static char *result = NULL;
77281676fe2Smrg    char *tail = NULL;
77391ec45ceSmrg    int alloced;
77491ec45ceSmrg    const unsigned long *end = icon + len / sizeof (unsigned long);
77591ec45ceSmrg
77681676fe2Smrg    free(result);
77781676fe2Smrg    result = NULL;
77881676fe2Smrg
77991ec45ceSmrg    alloced = 0;
78091ec45ceSmrg
78191ec45ceSmrg    while (icon < end)
78291ec45ceSmrg    {
783c8df0c59Smrg	unsigned long width, height, display_width, display_height;
784f9c28e31Smrg	unsigned int icon_pixel_bytes;
785f9c28e31Smrg	unsigned int icon_line_bytes;
78691ec45ceSmrg	int offset;
78791ec45ceSmrg
788c8df0c59Smrg	width = display_width = *icon++;
789c8df0c59Smrg	height = display_height = *icon++;
790c8df0c59Smrg	if (is_truecolor_term())
791c8df0c59Smrg	    display_height /= 2; /* Two vertical icon pixels per character. */
792c8df0c59Smrg	else
793c8df0c59Smrg	    display_width *= 2;  /* Two horizontal characters per icon pixel. */
794f9c28e31Smrg
795f9c28e31Smrg	icon_pixel_bytes = 1;
796f9c28e31Smrg	if (is_truecolor_term())
797c8df0c59Smrg	    icon_pixel_bytes = 39; /* 21 control characters, and up to 18 chars of RGB. */
798f9c28e31Smrg	else if (is_utf8_locale())
799f9c28e31Smrg	    icon_pixel_bytes = 3; /* Up to 3 bytes per character in that mode. */
800f9c28e31Smrg
801f9c28e31Smrg	/* Initial tab, pixels, and newline. */
802f9c28e31Smrg	icon_line_bytes = 8 + display_width * icon_pixel_bytes + 1;
80391ec45ceSmrg
80491ec45ceSmrg	offset = (tail - result);
80591ec45ceSmrg
806f9c28e31Smrg	alloced += 80;				/* For the header, final newline, color reset */
807c8df0c59Smrg	alloced += icon_line_bytes * display_height;	/* For the rows */
80891ec45ceSmrg
809f4a95042Smrg	result = realloc (result, alloced);
810f4a95042Smrg	if (!result)
811f4a95042Smrg	    Fatal_Error("Out of memory!");
81291ec45ceSmrg	tail = &result[offset];
81391ec45ceSmrg
81491ec45ceSmrg	if (end - icon < width * height)
81591ec45ceSmrg	    break;
81691ec45ceSmrg
81791ec45ceSmrg	tail += sprintf (tail, "\tIcon (%lu x %lu):\n", width, height);
81891ec45ceSmrg
819f9c28e31Smrg	if ((display_width + 8) > term_width || height > 144)
82091ec45ceSmrg	{
821f9c28e31Smrg	    tail += sprintf (tail, "\t(not shown)\n");
82291ec45ceSmrg	    icon += width * height;
82391ec45ceSmrg	    continue;
82491ec45ceSmrg	}
82591ec45ceSmrg
826c8df0c59Smrg	for (unsigned int h = 0; h < display_height; ++h)
82791ec45ceSmrg	{
82891ec45ceSmrg	    tail += sprintf (tail, "\t");
82991ec45ceSmrg
83049bee38cSmrg	    for (unsigned int w = 0; w < width; ++w)
83191ec45ceSmrg	    {
83291ec45ceSmrg		unsigned char a, r, g, b;
83391ec45ceSmrg		unsigned long pixel = *icon++;
83491ec45ceSmrg		unsigned long brightness;
83591ec45ceSmrg
83691ec45ceSmrg		a = (pixel & 0xff000000) >> 24;
83791ec45ceSmrg		r = (pixel & 0x00ff0000) >> 16;
83891ec45ceSmrg		g = (pixel & 0x0000ff00) >> 8;
83991ec45ceSmrg		b = (pixel & 0x000000ff);
84091ec45ceSmrg
84191ec45ceSmrg		brightness =
84291ec45ceSmrg		    (a / 255.0) * (1000 - ((299 * (r / 255.0)) +
84391ec45ceSmrg					   (587 * (g / 255.0)) +
84491ec45ceSmrg					   (114 * (b / 255.0))));
84591ec45ceSmrg
846f9c28e31Smrg		if (is_truecolor_term())
847f9c28e31Smrg		{
848f9c28e31Smrg		    float opacity = a / 255.0;
849c8df0c59Smrg		    unsigned long pixel2 = *(icon + width - 1);
850f9c28e31Smrg
851f9c28e31Smrg		    r = r * opacity;
852f9c28e31Smrg		    g = g * opacity;
853f9c28e31Smrg		    b = b * opacity;
854c8df0c59Smrg		    tail += sprintf (tail, "\033[48;2;%d;%d;%d;", r, g, b);
855c8df0c59Smrg
856c8df0c59Smrg		    opacity = ((pixel2 & 0xff000000) >> 24) / 255.0;
857c8df0c59Smrg		    r = ((pixel2 & 0x00ff0000) >> 16) * opacity;
858c8df0c59Smrg		    g = ((pixel2 & 0x0000ff00) >>  8) * opacity;
859c8df0c59Smrg		    b = ((pixel2 & 0x000000ff) >>  0) * opacity;
860c8df0c59Smrg		    tail += sprintf (tail, "38;2;%d;%d;%dm\342\226\204", r, g, b);
861f9c28e31Smrg
862f9c28e31Smrg		}
863f9c28e31Smrg		else if (is_utf8_locale())
86491ec45ceSmrg		{
86591ec45ceSmrg		    static const char palette[][4] =
86691ec45ceSmrg		    {
86791ec45ceSmrg			" ",
86891ec45ceSmrg			"\342\226\221",		/* 25% */
86991ec45ceSmrg			"\342\226\222",		/* 50% */
87091ec45ceSmrg			"\342\226\223",		/* 75% */
87191ec45ceSmrg			"\342\226\210",		/* 100% */
87291ec45ceSmrg		    };
87391ec45ceSmrg		    int idx;
87491ec45ceSmrg
87591ec45ceSmrg		    idx = (brightness * ((sizeof (palette)/sizeof(palette[0])) - 1)) / 1000;
87691ec45ceSmrg
877f9c28e31Smrg		    tail += sprintf (tail, "%s%s", palette[idx], palette[idx]);
87891ec45ceSmrg		}
87991ec45ceSmrg		else
88091ec45ceSmrg		{
88191ec45ceSmrg		    static const char palette[] =
88291ec45ceSmrg			" .'`,^:\";~-_+<>i!lI?/\\|()1{}[]rcvunxzjftLCJUYXZO0Qoahkbdpqwm*WMB8&%$#@";
88391ec45ceSmrg		    int idx;
88491ec45ceSmrg
88591ec45ceSmrg		    idx = (brightness * (sizeof(palette) - 2)) / 1000;
88691ec45ceSmrg
88791ec45ceSmrg		    *tail++ = palette[idx];
888f9c28e31Smrg		    *tail++ = palette[idx];
88991ec45ceSmrg		}
89091ec45ceSmrg	    }
89191ec45ceSmrg
892c8df0c59Smrg	    if (is_truecolor_term()) {
893c8df0c59Smrg	        /* Reset colors before newline to avoid bleed. */
894c8df0c59Smrg	        tail += sprintf (tail, "\033[0m");
895c8df0c59Smrg	        icon += width; /* Advance past handled row. */
896c8df0c59Smrg	    }
89791ec45ceSmrg	    tail += sprintf (tail, "\n");
89891ec45ceSmrg	}
89991ec45ceSmrg
90091ec45ceSmrg	tail += sprintf (tail, "\n");
90191ec45ceSmrg    }
90291ec45ceSmrg
90391ec45ceSmrg    return result;
90491ec45ceSmrg}
90591ec45ceSmrg
906fc89c0fbSmrgstatic const char *
907fc89c0fbSmrgFormat_Len_Text (const char *string, int len, Atom encoding)
908fc89c0fbSmrg{
909fc89c0fbSmrg    XTextProperty textprop;
91049bee38cSmrg    char **start_list;
911fc89c0fbSmrg    int count;
912fc89c0fbSmrg
913fc89c0fbSmrg    /* Try to convert to local encoding. */
914fc89c0fbSmrg    textprop.encoding = encoding;
915fc89c0fbSmrg    textprop.format = 8;
916fc89c0fbSmrg    textprop.value = (unsigned char *) string;
917fc89c0fbSmrg    textprop.nitems = len;
918f9c28e31Smrg    if (XmbTextPropertyToTextList(dpy, &textprop, &start_list, &count) == Success) {
91949bee38cSmrg	char **list = start_list;
920fc89c0fbSmrg	_buf_ptr = _formatting_buffer;
921fc89c0fbSmrg	_buf_len = MAXSTR;
922fc89c0fbSmrg	*_buf_ptr++ = '"';
923fc89c0fbSmrg	while (count > 0) {
924fc89c0fbSmrg	    string = *list++;
925fc89c0fbSmrg	    len = strlen(string);
926fc89c0fbSmrg	    while (len > 0) {
927fc89c0fbSmrg		wchar_t wc;
928fc89c0fbSmrg		int n = mbtowc(&wc, string, len);
929fc89c0fbSmrg		if (n > 0 && iswprint(wc)) {
930fc89c0fbSmrg		    if (_buf_len >= n) {
931fc89c0fbSmrg			memcpy(_buf_ptr, string, n);
932fc89c0fbSmrg			_buf_ptr += n;
933fc89c0fbSmrg			_buf_len -= n;
934fc89c0fbSmrg		    }
935fc89c0fbSmrg		    string += n;
936fc89c0fbSmrg		    len -= n;
937fc89c0fbSmrg		} else {
938fc89c0fbSmrg		    _put_char('\\');
939fc89c0fbSmrg		    snprintf(_buf_ptr, _buf_len, "%03o", (unsigned char) *string);
940fc89c0fbSmrg		    _buf_ptr += 3;
941fc89c0fbSmrg		    _buf_len -= 3;
942fc89c0fbSmrg		    string++;
943fc89c0fbSmrg		    len--;
944fc89c0fbSmrg		}
945fc89c0fbSmrg	    }
946fc89c0fbSmrg	    count--;
947fc89c0fbSmrg	    if (count > 0) {
948fc89c0fbSmrg		snprintf(_buf_ptr, _buf_len, "\\000");
949fc89c0fbSmrg	        _buf_ptr += 4;
950fc89c0fbSmrg		_buf_len -= 4;
951fc89c0fbSmrg	    }
952fc89c0fbSmrg	}
953f9c28e31Smrg	XFreeStringList(start_list);
954fc89c0fbSmrg	*_buf_ptr++ = '"';
955fc89c0fbSmrg	*_buf_ptr++ = '\0';
956fc89c0fbSmrg	return _formatting_buffer;
957fc89c0fbSmrg    } else
958576bbdfcSmrg	return Format_Len_String(string, len, 0);
959fc89c0fbSmrg}
960fc89c0fbSmrg
961a0996ce0Smrg/*
962a0996ce0Smrg * Validate a string as UTF-8 encoded according to RFC 3629
963a0996ce0Smrg *
964a0996ce0Smrg * Simply, a unicode code point (up to 21-bits long) is encoded as follows:
965a0996ce0Smrg *
966a0996ce0Smrg *    Char. number range  |        UTF-8 octet sequence
967a0996ce0Smrg *       (hexadecimal)    |              (binary)
968a0996ce0Smrg *    --------------------+---------------------------------------------
969a0996ce0Smrg *    0000 0000-0000 007F | 0xxxxxxx
970a0996ce0Smrg *    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
971a0996ce0Smrg *    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
972a0996ce0Smrg *    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
973a0996ce0Smrg *
974a0996ce0Smrg * Validation is done left-to-right, and an error condition, if any, refers to
975a0996ce0Smrg * only the left-most problem in the string.
976a0996ce0Smrg *
977a0996ce0Smrg * Return values:
978a0996ce0Smrg *   UTF8_VALID: Valid UTF-8 encoded string
979a0996ce0Smrg *   UTF8_OVERLONG: Using more bytes than needed for a code point
980a0996ce0Smrg *   UTF8_SHORT_TAIL: Not enough bytes in a multi-byte sequence
981a0996ce0Smrg *   UTF8_LONG_TAIL: Too many bytes in a multi-byte sequence
982a0996ce0Smrg *   UTF8_FORBIDDEN_VALUE: Forbidden prefix or code point outside 0x10FFFF
983a0996ce0Smrg */
984a0996ce0Smrg#define UTF8_VALID 0
985a0996ce0Smrg#define UTF8_FORBIDDEN_VALUE 1
986a0996ce0Smrg#define UTF8_OVERLONG 2
987a0996ce0Smrg#define UTF8_SHORT_TAIL 3
988a0996ce0Smrg#define UTF8_LONG_TAIL 4
989a0996ce0Smrgstatic int
990a0996ce0Smrgis_valid_utf8 (const char *string, int len)
991a0996ce0Smrg{
992f4a95042Smrg    unsigned long codepoint = 0;
99349bee38cSmrg    int rem = 0;
994a0996ce0Smrg
99549bee38cSmrg    for (int i = 0; i < len; i++) {
99649bee38cSmrg	 unsigned char c = (unsigned char) string[i];
997a0996ce0Smrg
998a0996ce0Smrg	/* Order of type check:
999a0996ce0Smrg	 *   - Single byte code point
1000a0996ce0Smrg	 *   - Non-starting byte of multi-byte sequence
1001a0996ce0Smrg	 *   - Start of 2-byte sequence
1002a0996ce0Smrg	 *   - Start of 3-byte sequence
1003a0996ce0Smrg	 *   - Start of 4-byte sequence
1004a0996ce0Smrg	 */
1005a0996ce0Smrg	if (!(c & 0x80)) {
1006a0996ce0Smrg	    if (rem > 0) return UTF8_SHORT_TAIL;
1007a0996ce0Smrg	    rem = 0;
1008a0996ce0Smrg	    codepoint = c;
1009a0996ce0Smrg	} else if ((c & 0xC0) == 0x80) {
1010a0996ce0Smrg	    if (rem == 0) return UTF8_LONG_TAIL;
1011a0996ce0Smrg	    rem--;
1012a0996ce0Smrg	    codepoint |= (c & 0x3F) << (rem * 6);
1013a0996ce0Smrg	    if (codepoint == 0) return UTF8_OVERLONG;
1014a0996ce0Smrg	} else if ((c & 0xE0) == 0xC0) {
1015a0996ce0Smrg	    if (rem > 0) return UTF8_SHORT_TAIL;
1016a0996ce0Smrg	    rem = 1;
1017a0996ce0Smrg	    codepoint = (c & 0x1F) << 6;
1018a0996ce0Smrg	    if (codepoint == 0) return UTF8_OVERLONG;
1019a0996ce0Smrg	} else if ((c & 0xF0) == 0xE0) {
1020a0996ce0Smrg	    if (rem > 0) return UTF8_SHORT_TAIL;
1021a0996ce0Smrg	    rem = 2;
1022a0996ce0Smrg	    codepoint = (c & 0x0F) << 12;
1023a0996ce0Smrg	} else if ((c & 0xF8) == 0xF0) {
1024a0996ce0Smrg	    if (rem > 0) return UTF8_SHORT_TAIL;
1025a0996ce0Smrg	    rem = 3;
1026a0996ce0Smrg	    codepoint = (c & 0x07) << 18;
1027a0996ce0Smrg	    if (codepoint > 0x10FFFF) return UTF8_FORBIDDEN_VALUE;
1028a0996ce0Smrg	} else
1029a0996ce0Smrg	    return UTF8_FORBIDDEN_VALUE;
1030a0996ce0Smrg    }
1031a0996ce0Smrg
1032a0996ce0Smrg    return UTF8_VALID;
1033a0996ce0Smrg}
1034a0996ce0Smrg
1035a0996ce0Smrgstatic const char *
1036a0996ce0SmrgFormat_Len_Unicode (const char *string, int len)
1037a0996ce0Smrg{
1038a0996ce0Smrg    int validity = is_valid_utf8(string, len);
1039a0996ce0Smrg
1040a0996ce0Smrg    if (validity != UTF8_VALID) {
104149bee38cSmrg	char *data;
104249bee38cSmrg	const char *result, *error;
104349bee38cSmrg
1044a0996ce0Smrg	switch (validity) {
1045a0996ce0Smrg	  case UTF8_FORBIDDEN_VALUE:
1046a0996ce0Smrg	    error = "<Invalid UTF-8 string: Forbidden value> "; break;
1047a0996ce0Smrg	  case UTF8_OVERLONG:
1048a0996ce0Smrg	    error = "<Invalid UTF-8 string: Overlong encoding> "; break;
1049a0996ce0Smrg	  case UTF8_SHORT_TAIL:
1050a0996ce0Smrg	    error = "<Invalid UTF-8 string: Tail too short> "; break;
1051a0996ce0Smrg	  case UTF8_LONG_TAIL:
1052a0996ce0Smrg	    error = "<Invalid UTF-8 string: Tail too long> "; break;
1053f4a95042Smrg	  default:
1054f4a95042Smrg	    error = "<Invalid UTF-8 string: Unknown error>"; break;
1055a0996ce0Smrg	}
1056a0996ce0Smrg
1057576bbdfcSmrg	result = Format_Len_String(string, len, 0);
1058576bbdfcSmrg	/* result is stored in _formatting_buffer, so make a temporary
1059576bbdfcSmrg	   copy before we overwrite _formatting_buffer with error */
1060576bbdfcSmrg	data = strdup(result);
1061f4a95042Smrg	if (!data)
1062f4a95042Smrg	    Fatal_Error("Out of memory!");
1063a0996ce0Smrg
1064a0996ce0Smrg	memcpy(_formatting_buffer, error, strlen(error)+1);
1065a0996ce0Smrg	strcat(_formatting_buffer, data);
1066a0996ce0Smrg	free(data);
1067a0996ce0Smrg
1068a0996ce0Smrg	return _formatting_buffer;
1069a0996ce0Smrg    }
1070a0996ce0Smrg
1071576bbdfcSmrg    return Format_Len_String(string, len, is_utf8_locale());
1072a0996ce0Smrg}
1073a0996ce0Smrg
1074fc89c0fbSmrg/*
1075fc89c0fbSmrg *
1076fc89c0fbSmrg * The Format Manager: a group of routines to manage "formats"
1077fc89c0fbSmrg *
1078fc89c0fbSmrg */
1079fc89c0fbSmrg
1080fc89c0fbSmrgstatic int
1081fc89c0fbSmrgIs_A_Format (const char *string)
1082fc89c0fbSmrg{
1083fc89c0fbSmrg    return isdigit((unsigned char) string[0]);
1084fc89c0fbSmrg}
1085fc89c0fbSmrg
1086fc89c0fbSmrgstatic int
1087fc89c0fbSmrgGet_Format_Size (const char *format)
1088fc89c0fbSmrg{
1089fc89c0fbSmrg    long size;
1090fc89c0fbSmrg
1091fc89c0fbSmrg    Scan_Long(format, &size);
1092fc89c0fbSmrg
1093fc89c0fbSmrg    /* Check for legal sizes */
1094fc89c0fbSmrg    if (size != 0 && size != 8 && size != 16 && size != 32)
1095fc89c0fbSmrg	Fatal_Error("bad format: %s", format);
1096fc89c0fbSmrg
1097fc89c0fbSmrg    return (int) size;
1098fc89c0fbSmrg}
1099fc89c0fbSmrg
1100fc89c0fbSmrgstatic char
1101fc89c0fbSmrgGet_Format_Char (const char *format, int i)
1102fc89c0fbSmrg{
1103fc89c0fbSmrg    long size;
1104fc89c0fbSmrg
1105fc89c0fbSmrg    /* Remove # at front of format */
1106fc89c0fbSmrg    format = Scan_Long(format, &size);
1107fc89c0fbSmrg    if (!*format)
1108fc89c0fbSmrg	Fatal_Error("bad format: %s", format);
1109fc89c0fbSmrg
1110fc89c0fbSmrg    /* Last character repeats forever... */
1111fc89c0fbSmrg    if (i >= (int)strlen(format))
1112fc89c0fbSmrg	i = strlen(format)-1;
1113fc89c0fbSmrg
1114fc89c0fbSmrg    return format[i];
1115fc89c0fbSmrg}
1116fc89c0fbSmrg
1117fc89c0fbSmrgstatic const char *
1118fc89c0fbSmrgFormat_Thunk (thunk t, char format_char)
1119fc89c0fbSmrg{
1120fc89c0fbSmrg    long value;
1121fc89c0fbSmrg    value = t.value;
1122fc89c0fbSmrg
1123fc89c0fbSmrg    switch (format_char) {
1124fc89c0fbSmrg      case 's':
1125576bbdfcSmrg	return Format_Len_String(t.extra_value, (int)t.value, 0);
1126a0996ce0Smrg      case 'u':
1127a0996ce0Smrg	return Format_Len_Unicode(t.extra_value, (int)t.value);
1128fc89c0fbSmrg      case 't':
1129fc89c0fbSmrg	return Format_Len_Text(t.extra_value, (int)t.value, t.extra_encoding);
1130fc89c0fbSmrg      case 'x':
1131fc89c0fbSmrg	return Format_Hex(value);
1132fc89c0fbSmrg      case 'c':
1133fc89c0fbSmrg	return Format_Unsigned(value);
1134fc89c0fbSmrg      case 'i':
1135fc89c0fbSmrg	return Format_Signed(value);
1136fc89c0fbSmrg      case 'b':
1137fc89c0fbSmrg	return Format_Bool(value);
1138fc89c0fbSmrg      case 'm':
1139fc89c0fbSmrg	return Format_Mask_Word(value);
1140fc89c0fbSmrg      case 'a':
1141fc89c0fbSmrg	return Format_Atom(value);
114291ec45ceSmrg      case 'o':
114391ec45ceSmrg	  return Format_Icons((const unsigned long *)t.extra_value, (int)t.value);
1144fc89c0fbSmrg      default:
1145fc89c0fbSmrg	Fatal_Error("bad format character: %c", format_char);
1146fc89c0fbSmrg    }
1147fc89c0fbSmrg}
1148fc89c0fbSmrg
1149fc89c0fbSmrgstatic const char *
1150fc89c0fbSmrgFormat_Thunk_I (thunk *thunks, const char *format, int i)
1151fc89c0fbSmrg{
1152fc89c0fbSmrg    if (i >= thunks->thunk_count)
1153fc89c0fbSmrg	return "<field not available>";
1154fc89c0fbSmrg
1155fc89c0fbSmrg    return Format_Thunk(thunks[i], Get_Format_Char(format, i));
1156fc89c0fbSmrg}
1157fc89c0fbSmrg
1158fc89c0fbSmrgstatic long
1159fc89c0fbSmrgMask_Word (thunk *thunks, const char *format)
1160fc89c0fbSmrg{
116149bee38cSmrg    for (int j = 0; j  < (int)strlen(format); j++)
1162fc89c0fbSmrg	if (Get_Format_Char(format, j) == 'm')
1163fc89c0fbSmrg	    return thunks[j].value;
1164fc89c0fbSmrg    return 0;
1165fc89c0fbSmrg}
1166fc89c0fbSmrg
1167fc89c0fbSmrg/*
1168fc89c0fbSmrg *
1169fc89c0fbSmrg * The Display Format Manager:
1170fc89c0fbSmrg *
1171fc89c0fbSmrg */
1172fc89c0fbSmrg
1173fc89c0fbSmrgstatic int
1174fc89c0fbSmrgIs_A_DFormat (const char *string)
1175fc89c0fbSmrg{
1176fc89c0fbSmrg    return string[0] && string[0] != '-'
1177fc89c0fbSmrg	   && !(isalpha((unsigned char) string[0]) || string[0] == '_');
1178fc89c0fbSmrg}
1179fc89c0fbSmrg
1180fc89c0fbSmrgstatic const char *
1181fc89c0fbSmrgHandle_Backslash (const char *dformat)
1182fc89c0fbSmrg{
1183fc89c0fbSmrg    char c;
1184fc89c0fbSmrg    unsigned long i;
1185fc89c0fbSmrg
1186fc89c0fbSmrg    if (!(c = *(dformat++)))
1187fc89c0fbSmrg	return dformat;
1188fc89c0fbSmrg
1189fc89c0fbSmrg    switch (c) {
1190fc89c0fbSmrg      case 'n':
1191fc89c0fbSmrg	putchar('\n');
1192fc89c0fbSmrg	break;
1193fc89c0fbSmrg      case 't':
1194fc89c0fbSmrg	putchar('\t');
1195fc89c0fbSmrg	break;
1196fc89c0fbSmrg      case '0':
1197fc89c0fbSmrg      case '1':
1198fc89c0fbSmrg      case '2':
1199fc89c0fbSmrg      case '3':
1200fc89c0fbSmrg      case '4':
1201fc89c0fbSmrg      case '5':
1202fc89c0fbSmrg      case '6':
1203fc89c0fbSmrg      case '7':
1204fc89c0fbSmrg	dformat = Scan_Octal(dformat, &i);
1205fc89c0fbSmrg	putchar((int) i);
1206fc89c0fbSmrg	break;
1207fc89c0fbSmrg      default:
1208fc89c0fbSmrg	putchar(c);
1209fc89c0fbSmrg	break;
1210fc89c0fbSmrg    }
1211fc89c0fbSmrg    return dformat;
1212fc89c0fbSmrg}
1213fc89c0fbSmrg
1214fc89c0fbSmrgstatic const char *
1215fc89c0fbSmrgHandle_Dollar_sign (const char *dformat, thunk *thunks, const char *format)
1216fc89c0fbSmrg{
1217fc89c0fbSmrg    long i;
1218fc89c0fbSmrg
1219fc89c0fbSmrg    dformat = Scan_Long(dformat, &i);
1220fc89c0fbSmrg
1221fc89c0fbSmrg    if (dformat[0] == '+') {
1222fc89c0fbSmrg	int seen = 0;
1223fc89c0fbSmrg	dformat++;
1224fc89c0fbSmrg	for (; i < thunks->thunk_count; i++) {
1225fc89c0fbSmrg	    if (seen)
1226fc89c0fbSmrg		printf(", ");
1227fc89c0fbSmrg	    seen = 1;
1228fc89c0fbSmrg	    printf("%s", Format_Thunk_I(thunks, format, (int) i));
1229fc89c0fbSmrg	}
1230fc89c0fbSmrg    } else
1231fc89c0fbSmrg	printf("%s", Format_Thunk_I(thunks, format, (int) i));
1232fc89c0fbSmrg
1233fc89c0fbSmrg    return dformat;
1234fc89c0fbSmrg}
1235fc89c0fbSmrg
1236fc89c0fbSmrgstatic int
1237fc89c0fbSmrgMask_Bit_I (thunk *thunks, const char *format, int i)
1238fc89c0fbSmrg{
1239fc89c0fbSmrg    long value;
1240fc89c0fbSmrg
1241fc89c0fbSmrg    value = Mask_Word(thunks, format);
1242fc89c0fbSmrg
1243fc89c0fbSmrg    value = value & (1L<<i);
1244fc89c0fbSmrg    if (value)
1245fc89c0fbSmrg	value = 1;
1246fc89c0fbSmrg    return value;
1247fc89c0fbSmrg}
1248fc89c0fbSmrg
1249fc89c0fbSmrgstatic const char *
1250fc89c0fbSmrgScan_Term (const char *string, thunk *thunks, const char *format, long *value)
1251fc89c0fbSmrg{
1252fc89c0fbSmrg    long i;
1253fc89c0fbSmrg
1254fc89c0fbSmrg    *value = 0;
1255fc89c0fbSmrg
1256fc89c0fbSmrg    if (isdigit((unsigned char) *string))
1257fc89c0fbSmrg	string = Scan_Long(string, value);
1258fc89c0fbSmrg    else if (*string == '$') {
1259fc89c0fbSmrg	string = Scan_Long(++string, &i);
1260fc89c0fbSmrg	if (i >= thunks->thunk_count)
1261fc89c0fbSmrg	    i = thunks->thunk_count;
1262fc89c0fbSmrg	*value = thunks[i].value;
1263fc89c0fbSmrg    } else if (*string == 'm') {
1264fc89c0fbSmrg	string = Scan_Long(++string, &i);
1265fc89c0fbSmrg	*value = Mask_Bit_I(thunks, format, (int) i);
1266fc89c0fbSmrg    } else
1267fc89c0fbSmrg	Fatal_Error("Bad term: %s.", string);
1268fc89c0fbSmrg
1269fc89c0fbSmrg    return string;
1270fc89c0fbSmrg}
1271fc89c0fbSmrg
1272fc89c0fbSmrgstatic const char *
1273fc89c0fbSmrgScan_Exp (const char *string, thunk *thunks, const char *format, long *value)
1274fc89c0fbSmrg{
1275fc89c0fbSmrg    long temp;
1276fc89c0fbSmrg
1277fc89c0fbSmrg    if (string[0] == '(') {
1278fc89c0fbSmrg	string = Scan_Exp(++string, thunks, format, value);
1279fc89c0fbSmrg	if (string[0]!=')')
1280fc89c0fbSmrg	    Fatal_Error("Missing ')'");
1281fc89c0fbSmrg	return ++string;
1282fc89c0fbSmrg    }
1283fc89c0fbSmrg    if (string[0] == '!') {
1284fc89c0fbSmrg	string = Scan_Exp(++string, thunks, format, value);
1285fc89c0fbSmrg	*value = !*value;
1286fc89c0fbSmrg	return string;
1287fc89c0fbSmrg    }
1288fc89c0fbSmrg
1289fc89c0fbSmrg    string = Scan_Term(string, thunks, format, value);
1290fc89c0fbSmrg
1291fc89c0fbSmrg    if (string[0] == '=') {
1292fc89c0fbSmrg	string = Scan_Exp(++string, thunks, format, &temp);
1293fc89c0fbSmrg	*value = *value == temp;
1294fc89c0fbSmrg    }
1295fc89c0fbSmrg
1296fc89c0fbSmrg    return string;
1297fc89c0fbSmrg}
1298fc89c0fbSmrg
1299fc89c0fbSmrgstatic const char *
1300fc89c0fbSmrgHandle_Question_Mark (const char *dformat, thunk *thunks, const char *format)
1301fc89c0fbSmrg{
1302c8df0c59Smrg    long is_true;
1303fc89c0fbSmrg
1304c8df0c59Smrg    dformat = Scan_Exp(dformat, thunks, format, &is_true);
1305fc89c0fbSmrg
1306fc89c0fbSmrg    if (*dformat != '(')
1307fc89c0fbSmrg	Fatal_Error("Bad conditional: '(' expected: %s.", dformat);
1308fc89c0fbSmrg    ++dformat;
1309fc89c0fbSmrg
1310c8df0c59Smrg    if (!is_true)
1311fc89c0fbSmrg	dformat = Skip_Past_Right_Paren(dformat);
1312fc89c0fbSmrg
1313fc89c0fbSmrg    return dformat;
1314fc89c0fbSmrg}
1315fc89c0fbSmrg
1316fc89c0fbSmrgstatic void
1317fc89c0fbSmrgDisplay_Property (thunk *thunks, const char *dformat, const char *format)
1318fc89c0fbSmrg{
1319fc89c0fbSmrg    char c;
1320fc89c0fbSmrg
1321fc89c0fbSmrg    while ((c = *(dformat++)))
1322fc89c0fbSmrg	switch (c) {
1323fc89c0fbSmrg	  case ')':
1324fc89c0fbSmrg	    continue;
1325fc89c0fbSmrg	  case '\\':
1326fc89c0fbSmrg	    dformat = Handle_Backslash(dformat);
1327fc89c0fbSmrg	    continue;
1328fc89c0fbSmrg	  case '$':
1329fc89c0fbSmrg	    dformat = Handle_Dollar_sign(dformat, thunks, format);
1330fc89c0fbSmrg	    continue;
1331fc89c0fbSmrg	  case '?':
1332fc89c0fbSmrg	    dformat = Handle_Question_Mark(dformat, thunks, format);
1333fc89c0fbSmrg	    continue;
1334fc89c0fbSmrg	  default:
1335fc89c0fbSmrg	    putchar(c);
1336fc89c0fbSmrg	    continue;
1337fc89c0fbSmrg	}
1338fc89c0fbSmrg}
1339fc89c0fbSmrg
1340fc89c0fbSmrg/*
1341fc89c0fbSmrg *
1342fc89c0fbSmrg * Routines to convert property data to thunks
1343fc89c0fbSmrg *
1344fc89c0fbSmrg */
1345fc89c0fbSmrg
1346fc89c0fbSmrgstatic long
1347fc89c0fbSmrgExtract_Value (const char **pointer, int *length, int size, int signedp)
1348fc89c0fbSmrg{
1349fc89c0fbSmrg    long value;
1350fc89c0fbSmrg
1351fc89c0fbSmrg    switch (size) {
1352fc89c0fbSmrg      case 8:
1353fc89c0fbSmrg	if (signedp)
1354fc89c0fbSmrg	    value = * (const signed char *) *pointer;
1355fc89c0fbSmrg	else
1356fc89c0fbSmrg	    value = * (const unsigned char *) *pointer;
1357fc89c0fbSmrg	*pointer += 1;
1358fc89c0fbSmrg	*length -= 1;
1359fc89c0fbSmrg	break;
1360fc89c0fbSmrg      case 16:
1361fc89c0fbSmrg	if (signedp)
1362fc89c0fbSmrg	    value = * (const short *) *pointer;
1363fc89c0fbSmrg	else
1364fc89c0fbSmrg	    value = * (const unsigned short *) *pointer;
1365fc89c0fbSmrg	*pointer += sizeof(short);
1366fc89c0fbSmrg	*length -= sizeof(short);
1367fc89c0fbSmrg	break;
1368fc89c0fbSmrg      case 32:
1369fc89c0fbSmrg	if (signedp)
1370fc89c0fbSmrg	    value = * (const long *) *pointer;
1371fc89c0fbSmrg	else
1372fc89c0fbSmrg	    value = * (const unsigned long *) *pointer & 0xffffffff;
1373fc89c0fbSmrg	*pointer += sizeof(long);
1374fc89c0fbSmrg	*length -= sizeof(long);
1375fc89c0fbSmrg	break;
1376fc89c0fbSmrg      default:
1377fc89c0fbSmrg	abort();
1378fc89c0fbSmrg    }
1379fc89c0fbSmrg    return value;
1380fc89c0fbSmrg}
1381fc89c0fbSmrg
1382fc89c0fbSmrgstatic long
1383fc89c0fbSmrgExtract_Len_String (const char **pointer, int *length, int size, const char **string)
1384fc89c0fbSmrg{
1385fc89c0fbSmrg    int len;
1386fc89c0fbSmrg
1387fc89c0fbSmrg    if (size != 8)
1388fc89c0fbSmrg	Fatal_Error("can't use format character 's' with any size except 8.");
1389fc89c0fbSmrg    len = 0; *string = *pointer;
1390fc89c0fbSmrg    while ((len++, --*length, *((*pointer)++)) && *length>0);
1391fc89c0fbSmrg
1392fc89c0fbSmrg    return len;
1393fc89c0fbSmrg}
1394fc89c0fbSmrg
139591ec45ceSmrgstatic long
139691ec45ceSmrgExtract_Icon (const char **pointer, int *length, int size, const char **icon)
139791ec45ceSmrg{
139891ec45ceSmrg    int len = 0;
139991ec45ceSmrg
140091ec45ceSmrg    if (size != 32)
140191ec45ceSmrg	Fatal_Error("can't use format character 'o' with any size except 32.");
140291ec45ceSmrg
140391ec45ceSmrg    len = *length;
140491ec45ceSmrg    *icon = *pointer;
140591ec45ceSmrg    *length = 0;
140691ec45ceSmrg    return len;
140791ec45ceSmrg}
140891ec45ceSmrg
1409fc89c0fbSmrgstatic thunk *
1410fc89c0fbSmrgBreak_Down_Property (const char *pointer, int length, Atom type, const char *format, int size)
1411fc89c0fbSmrg{
1412fc89c0fbSmrg    thunk *thunks;
1413f4a95042Smrg    thunk t = {0};
1414fc89c0fbSmrg    int i;
1415fc89c0fbSmrg
1416fc89c0fbSmrg    thunks = Create_Thunk_List();
1417fc89c0fbSmrg    i = 0;
1418fc89c0fbSmrg
1419fc89c0fbSmrg    while (length >= size/8) {
142049bee38cSmrg	char format_char = Get_Format_Char(format, i);
142149bee38cSmrg
1422a0996ce0Smrg	if (format_char == 's' || format_char == 'u')
1423fc89c0fbSmrg	    t.value = Extract_Len_String(&pointer,&length,size,&t.extra_value);
1424fc89c0fbSmrg	else if (format_char == 't') {
1425fc89c0fbSmrg	    t.extra_encoding = type;
1426fc89c0fbSmrg	    t.value = Extract_Len_String(&pointer,&length,size,&t.extra_value);
142791ec45ceSmrg	}
142891ec45ceSmrg	else if (format_char == 'o')
142991ec45ceSmrg	    t.value = Extract_Icon (&pointer,&length,size,&t.extra_value);
143091ec45ceSmrg	else
1431fc89c0fbSmrg	    t.value = Extract_Value(&pointer,&length,size,format_char=='i');
1432fc89c0fbSmrg	thunks = Add_Thunk(thunks, t);
1433fc89c0fbSmrg	i++;
1434fc89c0fbSmrg    }
1435fc89c0fbSmrg
1436fc89c0fbSmrg    return thunks;
1437fc89c0fbSmrg}
1438fc89c0fbSmrg
1439fc89c0fbSmrg/*
1440fc89c0fbSmrg * Variables set by main()
1441fc89c0fbSmrg */
1442fc89c0fbSmrg
1443fc89c0fbSmrgstatic Window target_win = 0;
1444fc89c0fbSmrgstatic int notype = 0;
1445fc89c0fbSmrgstatic int max_len = MAXSTR;
1446fc89c0fbSmrgstatic XFontStruct *font;
1447fc89c0fbSmrgstatic unsigned long _font_prop;
1448fc89c0fbSmrg
1449fc89c0fbSmrg/*
1450fc89c0fbSmrg *
1451fc89c0fbSmrg * Other Stuff (temp.):
1452fc89c0fbSmrg *
1453fc89c0fbSmrg */
1454fc89c0fbSmrg
1455fc89c0fbSmrgstatic const char *
1456fc89c0fbSmrgGet_Font_Property_Data_And_Type (Atom atom,
1457fc89c0fbSmrg                                 long *length, Atom *type, int *size)
1458fc89c0fbSmrg{
1459fc89c0fbSmrg    *type = None;
1460fc89c0fbSmrg
146149bee38cSmrg    for (int i = 0; i < font->n_properties; i++)
1462fc89c0fbSmrg	if (atom == font->properties[i].name) {
1463fc89c0fbSmrg	    _font_prop = font->properties[i].card32;
1464fc89c0fbSmrg	    *length = sizeof(long);
1465fc89c0fbSmrg	    *size = 32;
1466fc89c0fbSmrg	    return (const char *) &_font_prop;
1467fc89c0fbSmrg	}
1468fc89c0fbSmrg    *size = 0;
1469fc89c0fbSmrg    return NULL;
1470fc89c0fbSmrg}
1471fc89c0fbSmrg
1472fc89c0fbSmrgstatic const char *
1473fc89c0fbSmrgGet_Window_Property_Data_And_Type (Atom atom,
1474fc89c0fbSmrg                                   long *length, Atom *type, int *size)
1475fc89c0fbSmrg{
1476fc89c0fbSmrg    Atom actual_type;
1477fc89c0fbSmrg    int actual_format;
1478fc89c0fbSmrg    unsigned long nitems;
1479fc89c0fbSmrg    unsigned long nbytes;
1480fc89c0fbSmrg    unsigned long bytes_after;
148181676fe2Smrg    static unsigned char *prop = NULL;
1482fc89c0fbSmrg    int status;
148381676fe2Smrg
148481676fe2Smrg    if (prop)
148581676fe2Smrg    {
148681676fe2Smrg	XFree(prop);
148781676fe2Smrg	prop = NULL;
148881676fe2Smrg    }
148981676fe2Smrg
1490fc89c0fbSmrg    status = XGetWindowProperty(dpy, target_win, atom, 0, (max_len+3)/4,
1491fc89c0fbSmrg				False, AnyPropertyType, &actual_type,
1492fc89c0fbSmrg				&actual_format, &nitems, &bytes_after,
1493fc89c0fbSmrg				&prop);
1494fc89c0fbSmrg    if (status == BadWindow)
1495fc89c0fbSmrg	Fatal_Error("window id # 0x%lx does not exists!", target_win);
1496fc89c0fbSmrg    if (status != Success)
1497fc89c0fbSmrg	Fatal_Error("XGetWindowProperty failed!");
1498fc89c0fbSmrg
1499fc89c0fbSmrg    if (actual_format == 32)
1500fc89c0fbSmrg	nbytes = sizeof(long);
1501fc89c0fbSmrg    else if (actual_format == 16)
1502fc89c0fbSmrg	nbytes = sizeof(short);
1503fc89c0fbSmrg    else if (actual_format == 8)
1504fc89c0fbSmrg	nbytes = 1;
1505fc89c0fbSmrg    else if (actual_format == 0)
1506fc89c0fbSmrg        nbytes = 0;
1507fc89c0fbSmrg    else
1508fc89c0fbSmrg	abort();
1509fc89c0fbSmrg    *length = min(nitems * nbytes, max_len);
1510fc89c0fbSmrg    *type = actual_type;
1511fc89c0fbSmrg    *size = actual_format;
1512fc89c0fbSmrg    return (const char *)prop;
1513fc89c0fbSmrg}
1514fc89c0fbSmrg
1515fc89c0fbSmrgstatic const char *
1516fc89c0fbSmrgGet_Property_Data_And_Type (Atom atom, long *length, Atom *type, int *size)
1517fc89c0fbSmrg{
151849bee38cSmrg    if (target_win == (Window) -1)
1519fc89c0fbSmrg	return Get_Font_Property_Data_And_Type(atom, length, type, size);
1520fc89c0fbSmrg    else
1521fc89c0fbSmrg	return Get_Window_Property_Data_And_Type(atom, length, type, size);
1522fc89c0fbSmrg}
1523fc89c0fbSmrg
1524fc89c0fbSmrgstatic void
1525fc89c0fbSmrgShow_Prop (const char *format, const char *dformat, const char *prop)
1526fc89c0fbSmrg{
1527fc89c0fbSmrg    const char *data;
1528fc89c0fbSmrg    long length;
1529fc89c0fbSmrg    Atom atom, type;
1530fc89c0fbSmrg    thunk *thunks;
1531fc89c0fbSmrg    int size, fsize;
1532fc89c0fbSmrg
1533fc89c0fbSmrg    printf("%s", prop);
1534fc89c0fbSmrg    atom = Parse_Atom(prop, True);
1535fc89c0fbSmrg    if (atom == None) {
1536fc89c0fbSmrg	printf(":  no such atom on any window.\n");
1537fc89c0fbSmrg	return;
1538fc89c0fbSmrg    }
1539fc89c0fbSmrg
1540fc89c0fbSmrg    data = Get_Property_Data_And_Type(atom, &length, &type, &size);
1541fc89c0fbSmrg    if (!size) {
1542fc89c0fbSmrg	puts(":  not found.");
1543fc89c0fbSmrg	return;
1544fc89c0fbSmrg    }
1545fc89c0fbSmrg
1546fc89c0fbSmrg    if (!notype && type != None)
1547fc89c0fbSmrg	printf("(%s)", Format_Atom(type));
1548fc89c0fbSmrg
1549fc89c0fbSmrg    Lookup_Formats(atom, &format, &dformat);
1550fc89c0fbSmrg    if (type != None)
1551fc89c0fbSmrg	Lookup_Formats(type, &format, &dformat);
1552fc89c0fbSmrg    Apply_Default_Formats(&format, &dformat);
1553fc89c0fbSmrg
1554fc89c0fbSmrg    fsize = Get_Format_Size(format);
1555fc89c0fbSmrg    if (fsize != size && fsize != 0) {
1556fc89c0fbSmrg	printf(": Type mismatch: assumed size %d bits, actual size %d bits.\n",
1557fc89c0fbSmrg	       fsize, size);
1558fc89c0fbSmrg	return;
1559fc89c0fbSmrg    }
1560fc89c0fbSmrg
1561fc89c0fbSmrg    thunks = Break_Down_Property(data, (int)length, type, format, size);
1562fc89c0fbSmrg    Display_Property(thunks, dformat, format);
156381676fe2Smrg    free(thunks);
1564fc89c0fbSmrg}
1565fc89c0fbSmrg
1566fc89c0fbSmrgstatic void
1567fc89c0fbSmrgShow_All_Props (void)
1568fc89c0fbSmrg{
156949bee38cSmrg    if (target_win != (Window) -1) {
157049bee38cSmrg	int count;
157149bee38cSmrg	Atom *atoms = XListProperties(dpy, target_win, &count);
157249bee38cSmrg	for (int i = 0; i < count; i++) {
157349bee38cSmrg	    const char *name = Format_Atom(atoms[i]);
1574fc89c0fbSmrg	    Show_Prop(NULL, NULL, name);
1575fc89c0fbSmrg	}
157681676fe2Smrg	XFree(atoms);
157749bee38cSmrg    } else {
157849bee38cSmrg	for (int i = 0; i < font->n_properties; i++) {
157949bee38cSmrg	    Atom atom = font->properties[i].name;
158049bee38cSmrg	    const char *name = Format_Atom(atom);
1581fc89c0fbSmrg	    Show_Prop(NULL, NULL, name);
1582fc89c0fbSmrg	}
158349bee38cSmrg    }
1584fc89c0fbSmrg}
1585fc89c0fbSmrg
1586fc89c0fbSmrgstatic thunk *
1587fc89c0fbSmrgHandle_Prop_Requests (int argc, char **argv)
1588fc89c0fbSmrg{
1589fc89c0fbSmrg    char *format, *dformat, *prop;
1590f4a95042Smrg    thunk *thunks, t = {0};
1591fc89c0fbSmrg
1592fc89c0fbSmrg    /* if no prop referenced, by default list all properties for given window */
1593fc89c0fbSmrg    if (!argc) {
1594fc89c0fbSmrg	Show_All_Props();
1595fc89c0fbSmrg	return NULL;
1596fc89c0fbSmrg    }
1597fc89c0fbSmrg
1598fc89c0fbSmrg    thunks = Create_Thunk_List();
1599fc89c0fbSmrg
1600fc89c0fbSmrg    while (argc > 0) {
1601fc89c0fbSmrg	format = NULL;
1602fc89c0fbSmrg	dformat = NULL;
1603fc89c0fbSmrg
1604fc89c0fbSmrg	/* Get overriding formats, if any */
1605fc89c0fbSmrg	if (Is_A_Format(argv[0])) {
1606fc89c0fbSmrg	    format = argv++[0]; argc--;
160781676fe2Smrg	    if (!argc) usage("format specified without atom");
1608fc89c0fbSmrg	}
1609fc89c0fbSmrg	if (Is_A_DFormat(argv[0])) {
1610fc89c0fbSmrg	    dformat = argv++[0]; argc--;
161181676fe2Smrg	    if (!argc) usage("dformat specified without atom");
1612fc89c0fbSmrg	}
1613fc89c0fbSmrg
1614fc89c0fbSmrg	/* Get property name */
1615fc89c0fbSmrg	prop = argv++[0]; argc--;
1616fc89c0fbSmrg
1617fc89c0fbSmrg	t.propname = prop;
1618fc89c0fbSmrg	t.value = Parse_Atom(prop, True);
1619fc89c0fbSmrg	t.format = format;
1620fc89c0fbSmrg	t.dformat = dformat;
1621fc89c0fbSmrg	if (t.value)
1622fc89c0fbSmrg	    thunks = Add_Thunk(thunks, t);
1623fc89c0fbSmrg	Show_Prop(format, dformat, prop);
1624fc89c0fbSmrg    }
1625fc89c0fbSmrg    return thunks;
1626fc89c0fbSmrg}
1627fc89c0fbSmrg
1628fc89c0fbSmrgstatic void
162949bee38cSmrgRemove_Property (Display *display, Window w, const char *propname)
1630fc89c0fbSmrg{
163149bee38cSmrg    Atom id = XInternAtom (display, propname, True);
1632fc89c0fbSmrg
1633fc89c0fbSmrg    if (id == None) {
1634fc89c0fbSmrg	fprintf (stderr, "%s:  no such property \"%s\"\n",
1635fc89c0fbSmrg		 program_name, propname);
1636fc89c0fbSmrg	return;
1637fc89c0fbSmrg    }
163849bee38cSmrg    XDeleteProperty (display, w, id);
1639fc89c0fbSmrg}
1640fc89c0fbSmrg
1641fc89c0fbSmrgstatic void
164249bee38cSmrgSet_Property (Display *display, Window w, const char *propname, const char *value)
1643fc89c0fbSmrg{
1644fc89c0fbSmrg    Atom atom;
1645fc89c0fbSmrg    const char *format;
1646fc89c0fbSmrg    const char *dformat;
1647fc89c0fbSmrg    int size;
1648fc89c0fbSmrg    char format_char;
1649fc89c0fbSmrg    Atom type = 0;
1650576bbdfcSmrg    const unsigned char *data = NULL;
1651fc89c0fbSmrg    int nelements = 0;
1652fc89c0fbSmrg
1653fc89c0fbSmrg    atom = Parse_Atom(propname, False);
1654fc89c0fbSmrg
1655fc89c0fbSmrg    format = dformat = NULL;
1656fc89c0fbSmrg    Lookup_Formats(atom, &format, &dformat);
1657fc89c0fbSmrg    if (format == NULL)
1658fc89c0fbSmrg	Fatal_Error("unsupported conversion for %s", propname);
1659fc89c0fbSmrg
1660fc89c0fbSmrg    size = Get_Format_Size(format);
1661fc89c0fbSmrg
1662fc89c0fbSmrg    format_char = Get_Format_Char(format, 0);
1663fc89c0fbSmrg    switch (format_char) {
1664fc89c0fbSmrg      case 's':
1665fc89c0fbSmrg	if (size != 8)
1666fc89c0fbSmrg	    Fatal_Error("can't use format character 's' with any size except 8.");
1667fc89c0fbSmrg	type = XA_STRING;
1668576bbdfcSmrg	data = (const unsigned char *) value;
1669576bbdfcSmrg	nelements = strlen(value);
1670576bbdfcSmrg	break;
1671576bbdfcSmrg      case 'u':
1672576bbdfcSmrg	if (size != 8)
1673576bbdfcSmrg	    Fatal_Error("can't use format character 'u' with any size except 8.");
167449bee38cSmrg	type = XInternAtom(display, "UTF8_STRING", False);
1675576bbdfcSmrg	data = (const unsigned char *) value;
1676fc89c0fbSmrg	nelements = strlen(value);
1677fc89c0fbSmrg	break;
1678fc89c0fbSmrg      case 't': {
1679fc89c0fbSmrg	XTextProperty textprop;
1680fc89c0fbSmrg	if (size != 8)
1681fc89c0fbSmrg	    Fatal_Error("can't use format character 't' with any size except 8.");
168249bee38cSmrg	if (XmbTextListToTextProperty(display, (char **) &value, 1,
1683fc89c0fbSmrg				      XStdICCTextStyle, &textprop) != Success) {
1684fc89c0fbSmrg	    fprintf(stderr, "cannot convert %s argument to STRING or COMPOUND_TEXT.\n", propname);
1685fc89c0fbSmrg	    return;
1686fc89c0fbSmrg	}
1687fc89c0fbSmrg	type = textprop.encoding;
1688fc89c0fbSmrg	data = textprop.value;
1689fc89c0fbSmrg	nelements = textprop.nitems;
1690fc89c0fbSmrg	break;
1691fc89c0fbSmrg      }
1692fc89c0fbSmrg      case 'x':
1693fc89c0fbSmrg      case 'c': {
1694fc89c0fbSmrg	static unsigned char data8[MAXELEMENTS];
1695fc89c0fbSmrg	static unsigned short data16[MAXELEMENTS];
1696fc89c0fbSmrg	static unsigned long data32[MAXELEMENTS];
1697fc89c0fbSmrg	unsigned long intvalue;
1698fc89c0fbSmrg	char * value2 = strdup(value);
1699fc89c0fbSmrg	char * tmp = strtok(value2,",");
1700fc89c0fbSmrg	nelements = 1;
1701fc89c0fbSmrg	intvalue = strtoul(tmp, NULL, 0);
1702fc89c0fbSmrg	switch(size) {
1703fc89c0fbSmrg	    case 8:
1704576bbdfcSmrg	        data8[0] = intvalue; data = (const unsigned char *) data8; break;
1705fc89c0fbSmrg	    case 16:
1706576bbdfcSmrg	        data16[0] = intvalue; data = (const unsigned char *) data16; break;
1707fc89c0fbSmrg	    case 32:
1708576bbdfcSmrg	        data32[0] = intvalue; data = (const unsigned char *) data32; break;
1709fc89c0fbSmrg	}
1710fc89c0fbSmrg	tmp = strtok(NULL,",");
1711fc89c0fbSmrg	while(tmp != NULL){
1712fc89c0fbSmrg	    intvalue = strtoul(tmp, NULL,0);
1713fc89c0fbSmrg	    switch(size) {
1714fc89c0fbSmrg		case 8:
1715fc89c0fbSmrg	    	    data8[nelements] = intvalue; break;
1716fc89c0fbSmrg		case 16:
1717fc89c0fbSmrg	    	    data16[nelements] = intvalue; break;
1718fc89c0fbSmrg		case 32:
1719fc89c0fbSmrg	    	    data32[nelements] = intvalue; break;
1720fc89c0fbSmrg	    }
1721fc89c0fbSmrg	    nelements++;
1722fc89c0fbSmrg	    if(nelements == MAXELEMENTS){
1723fc89c0fbSmrg		fprintf(stderr, "Maximum number of elements reached (%d). List truncated.\n",MAXELEMENTS);
1724fc89c0fbSmrg		break;
1725fc89c0fbSmrg	    }
1726fc89c0fbSmrg	    tmp = strtok(NULL,",");
1727fc89c0fbSmrg	}
1728fc89c0fbSmrg
1729fc89c0fbSmrg	type = XA_CARDINAL;
1730fc89c0fbSmrg	free(value2);
1731fc89c0fbSmrg	break;
1732fc89c0fbSmrg      }
1733fc89c0fbSmrg      case 'i': {
1734fc89c0fbSmrg	static unsigned char data8[MAXELEMENTS];
1735fc89c0fbSmrg	static unsigned short data16[MAXELEMENTS];
1736fc89c0fbSmrg	static unsigned long data32[MAXELEMENTS];
1737fc89c0fbSmrg	unsigned long intvalue;
1738fc89c0fbSmrg	char * value2 = strdup(value);
1739fc89c0fbSmrg	char * tmp = strtok(value2,",");
1740fc89c0fbSmrg	nelements = 1;
1741fc89c0fbSmrg	intvalue = strtoul(tmp, NULL, 0);
1742fc89c0fbSmrg	switch(size) {
1743fc89c0fbSmrg	    case 8:
1744576bbdfcSmrg	        data8[0] = intvalue; data = (const unsigned char *) data8; break;
1745fc89c0fbSmrg	    case 16:
1746576bbdfcSmrg	        data16[0] = intvalue; data = (const unsigned char *) data16; break;
1747fc89c0fbSmrg	    case 32:
1748576bbdfcSmrg	        data32[0] = intvalue; data = (const unsigned char *) data32; break;
1749fc89c0fbSmrg	}
1750fc89c0fbSmrg	tmp = strtok(NULL,",");
1751fc89c0fbSmrg	while(tmp != NULL){
1752fc89c0fbSmrg	    intvalue = strtoul(tmp, NULL,0);
1753fc89c0fbSmrg	    switch(size) {
1754fc89c0fbSmrg		case 8:
1755fc89c0fbSmrg	    	    data8[nelements] = intvalue; break;
1756fc89c0fbSmrg		case 16:
1757fc89c0fbSmrg	    	    data16[nelements] = intvalue; break;
1758fc89c0fbSmrg		case 32:
1759fc89c0fbSmrg	    	    data32[nelements] = intvalue; break;
1760fc89c0fbSmrg	    }
1761fc89c0fbSmrg	    nelements++;
1762fc89c0fbSmrg	    if(nelements == MAXELEMENTS){
1763fc89c0fbSmrg		fprintf(stderr, "Maximum number of elements reached (%d). List truncated.\n",MAXELEMENTS);
1764fc89c0fbSmrg		break;
1765fc89c0fbSmrg	    }
1766fc89c0fbSmrg	    tmp = strtok(NULL,",");
1767fc89c0fbSmrg	}
1768fc89c0fbSmrg
1769fc89c0fbSmrg	type = XA_INTEGER;
1770fc89c0fbSmrg	free(value2);
1771fc89c0fbSmrg	break;
1772fc89c0fbSmrg      }
1773fc89c0fbSmrg      case 'b': {
1774fc89c0fbSmrg	unsigned long boolvalue;
1775fc89c0fbSmrg	static unsigned char data8;
1776fc89c0fbSmrg	static unsigned short data16;
1777fc89c0fbSmrg	static unsigned long data32;
1778fc89c0fbSmrg	if (!strcmp(value, "True"))
1779fc89c0fbSmrg	    boolvalue = 1;
1780fc89c0fbSmrg	else if (!strcmp(value, "False"))
1781fc89c0fbSmrg	    boolvalue = 0;
1782fc89c0fbSmrg	else {
1783fc89c0fbSmrg	    fprintf(stderr, "cannot convert %s argument to Bool\n", propname);
1784fc89c0fbSmrg	    return;
1785fc89c0fbSmrg	}
1786fc89c0fbSmrg	type = XA_INTEGER;
1787fc89c0fbSmrg	switch (size) {
1788fc89c0fbSmrg	  case 8:
1789576bbdfcSmrg	    data8 = boolvalue; data = (const unsigned char *) &data8; break;
1790fc89c0fbSmrg	  case 16:
1791576bbdfcSmrg	    data16 = boolvalue; data = (const unsigned char *) &data16; break;
1792fc89c0fbSmrg	  case 32: default:
1793576bbdfcSmrg	    data32 = boolvalue; data = (const unsigned char *) &data32; break;
1794fc89c0fbSmrg	}
1795fc89c0fbSmrg	nelements = 1;
1796fc89c0fbSmrg	break;
1797fc89c0fbSmrg      }
1798fc89c0fbSmrg      case 'a': {
1799fc89c0fbSmrg	static Atom avalue;
1800fc89c0fbSmrg	avalue = Parse_Atom(value, False);
1801fc89c0fbSmrg	type = XA_ATOM;
1802576bbdfcSmrg	data = (const unsigned char *) &avalue;
1803fc89c0fbSmrg	nelements = 1;
1804fc89c0fbSmrg	break;
1805fc89c0fbSmrg      }
1806fc89c0fbSmrg      case 'm':
1807fc89c0fbSmrg	/* NYI */
1808fc89c0fbSmrg      default:
1809fc89c0fbSmrg	Fatal_Error("bad format character: %c", format_char);
1810fc89c0fbSmrg    }
1811fc89c0fbSmrg
181249bee38cSmrg    XChangeProperty(display, target_win, atom, type, size, PropModeReplace,
1813fc89c0fbSmrg		    data, nelements);
1814fc89c0fbSmrg}
1815fc89c0fbSmrg
1816fc89c0fbSmrg/*
1817fc89c0fbSmrg *
1818fc89c0fbSmrg * Routines for parsing command line:
1819fc89c0fbSmrg *
1820fc89c0fbSmrg */
1821fc89c0fbSmrg
182249bee38cSmrgstatic void
1823f9c28e31Smrgprint_help (void)
1824fc89c0fbSmrg{
182581676fe2Smrg    static const char *help_message =
1826fc89c0fbSmrg"where options include:\n"
1827f9c28e31Smrg"    -help                          print out a summary of command line options\n"
1828fc89c0fbSmrg"    -grammar                       print out full grammar for command line\n"
1829fc89c0fbSmrg"    -display host:dpy              the X server to contact\n"
1830fc89c0fbSmrg"    -id id                         resource id of window to examine\n"
1831fc89c0fbSmrg"    -name name                     name of window to examine\n"
1832fc89c0fbSmrg"    -font name                     name of font to examine\n"
1833fc89c0fbSmrg"    -remove propname               remove a property\n"
1834fc89c0fbSmrg"    -set propname value            set a property to a given value\n"
1835fc89c0fbSmrg"    -root                          examine the root window\n"
1836fc89c0fbSmrg"    -len n                         display at most n bytes of any property\n"
1837fc89c0fbSmrg"    -notype                        do not display the type field\n"
1838fc89c0fbSmrg"    -fs filename                   where to look for formats for properties\n"
1839fc89c0fbSmrg"    -frame                         don't ignore window manager frames\n"
1840fc89c0fbSmrg"    -f propname format [dformat]   formats to use for property of given name\n"
184181676fe2Smrg"    -spy                           examine window properties forever\n"
184281676fe2Smrg"    -version                       print program version\n";
1843fc89c0fbSmrg
1844fc89c0fbSmrg
1845fc89c0fbSmrg    fflush (stdout);
184681676fe2Smrg
1847fc89c0fbSmrg    fprintf (stderr,
1848fc89c0fbSmrg	     "usage:  %s [-options ...] [[format [dformat]] atom] ...\n\n",
1849fc89c0fbSmrg	     program_name);
1850fc89c0fbSmrg    fprintf (stderr, "%s\n", help_message);
1851f9c28e31Smrg}
1852f9c28e31Smrg
185349bee38cSmrgstatic inline void _X_NORETURN _X_COLD
185449bee38cSmrghelp (void)
185549bee38cSmrg{
1856f9c28e31Smrg	print_help();
1857f9c28e31Smrg	exit(0);
1858f9c28e31Smrg}
1859f9c28e31Smrg
186049bee38cSmrgvoid _X_NORETURN _X_COLD
1861f9c28e31Smrgusage (const char *errmsg)
1862f9c28e31Smrg{
1863f9c28e31Smrg    if (errmsg != NULL)
1864f9c28e31Smrg	fprintf (stderr, "%s: %s\n\n", program_name, errmsg);
1865f9c28e31Smrg
1866f9c28e31Smrg    print_help();
1867fc89c0fbSmrg    exit (1);
1868fc89c0fbSmrg}
1869fc89c0fbSmrg
187049bee38cSmrgstatic void _X_NORETURN _X_COLD
1871fc89c0fbSmrggrammar (void)
1872fc89c0fbSmrg{
1873fc89c0fbSmrg    printf ("Grammar for xprop:\n\n");
1874fc89c0fbSmrg    printf("\t%s [<disp>] [<select option>] <option>* <mapping>* <spec>*",
1875fc89c0fbSmrg	   program_name);
1876fc89c0fbSmrg    printf("\n\n\tdisp ::= -display host:dpy\
1877fc89c0fbSmrg\n\tselect option ::= -root | -id <id> | -font <font> | -name <name>\
1878fc89c0fbSmrg\n\toption ::= -len <n> | -notype | -spy | {-formats|-fs} <format file>\
1879fc89c0fbSmrg\n\tmapping ::= {-f|-format} <atom> <format> [<dformat>]\
1880fc89c0fbSmrg\n\t            | -remove <propname>\
1881fc89c0fbSmrg\n\t            | -set <propname> <value>\
1882fc89c0fbSmrg\n\tspec ::= [<format> [<dformat>]] <atom>\
1883fc89c0fbSmrg\n\tformat ::= {0|8|16|32}{a|b|c|i|m|s|t|x}*\
1884fc89c0fbSmrg\n\tdformat ::= <unit><unit>*             (can't start with a letter or '-')\
1885fc89c0fbSmrg\n\tunit ::= ?<exp>(<unit>*) | $<n> | <display char>\
1886fc89c0fbSmrg\n\texp ::= <term> | <term>=<exp> | !<exp>\
1887fc89c0fbSmrg\n\tterm ::= <n> | $<n> | m<n>\
1888fc89c0fbSmrg\n\tdisplay char ::= <normal char> | \\<non digit char> | \\<octal number>\
1889fc89c0fbSmrg\n\tnormal char ::= <any char except a digit, $, ?, \\, or )>\
1890fc89c0fbSmrg\n\n");
1891fc89c0fbSmrg    exit(0);
1892fc89c0fbSmrg}
1893fc89c0fbSmrg
1894fc89c0fbSmrgstatic void
1895fc89c0fbSmrgParse_Format_Mapping (int *argc, char ***argv)
1896fc89c0fbSmrg{
1897fc89c0fbSmrg#define ARGC (*argc)
1898fc89c0fbSmrg#define ARGV (*argv)
1899fc89c0fbSmrg#define OPTION ARGV[0]
190049bee38cSmrg#define NXTOPT if (++ARGV, --ARGC==0) usage("insufficient arguments for -format")
1901fc89c0fbSmrg    char *type_name, *format, *dformat;
1902fc89c0fbSmrg
1903fc89c0fbSmrg    NXTOPT; type_name = OPTION;
1904fc89c0fbSmrg
1905fc89c0fbSmrg    NXTOPT; format = OPTION;
1906fc89c0fbSmrg    if (!Is_A_Format(format))
1907fc89c0fbSmrg	Fatal_Error("Bad format: %s.", format);
1908fc89c0fbSmrg
1909fc89c0fbSmrg    dformat = NULL;
1910fc89c0fbSmrg    if (ARGC>1 && Is_A_DFormat(ARGV[1])) {
1911fc89c0fbSmrg	ARGV++; ARGC--; dformat = OPTION;
1912fc89c0fbSmrg    }
1913fc89c0fbSmrg
1914fc89c0fbSmrg    Add_Mapping(Parse_Atom(type_name, False), format, dformat);
1915fc89c0fbSmrg}
1916fc89c0fbSmrg
1917fc89c0fbSmrg/*
1918fc89c0fbSmrg *
1919fc89c0fbSmrg * The Main Program:
1920fc89c0fbSmrg *
1921fc89c0fbSmrg */
1922fc89c0fbSmrg
1923fc89c0fbSmrgstatic int spy = 0;
1924fc89c0fbSmrg
192549bee38cSmrgstatic int (*old_error_handler)(Display *display, XErrorEvent *ev);
192691ec45ceSmrg
192749bee38cSmrgstatic int spy_error_handler(Display *display, XErrorEvent *ev)
192891ec45ceSmrg{
192991ec45ceSmrg    if (ev->error_code == BadWindow || ev->error_code == BadMatch) {
193091ec45ceSmrg	/* Window was destroyed */
193191ec45ceSmrg	puts("");
193291ec45ceSmrg	exit(0);
193391ec45ceSmrg    }
193491ec45ceSmrg
193591ec45ceSmrg    if (old_error_handler)
193649bee38cSmrg	return old_error_handler(display, ev);
193791ec45ceSmrg
193891ec45ceSmrg    return 0;
193991ec45ceSmrg}
194091ec45ceSmrg
1941fc89c0fbSmrgint
1942fc89c0fbSmrgmain (int argc, char **argv)
1943fc89c0fbSmrg{
1944fc89c0fbSmrg    FILE *stream;
1945fc89c0fbSmrg    char *name;
1946fc89c0fbSmrg    thunk *props;
1947fc89c0fbSmrg    thunk *remove_props = NULL;
1948fc89c0fbSmrg    thunk *set_props = NULL;
1949fc89c0fbSmrg    Bool frame_only = False;
1950fc89c0fbSmrg    int n;
1951fc89c0fbSmrg    char **nargv;
1952fc89c0fbSmrg
1953f9c28e31Smrg#ifdef TIOCGWINSZ
1954f9c28e31Smrg    struct winsize ws;
1955f9c28e31Smrg    ws.ws_col = 0;
1956f9c28e31Smrg    if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col != 0)
1957f9c28e31Smrg	term_width = ws.ws_col;
1958f9c28e31Smrg#endif
1959f9c28e31Smrg
1960fc89c0fbSmrg    INIT_NAME;
1961fc89c0fbSmrg
1962fc89c0fbSmrg    /* Set locale for XmbTextProptertyToTextList and iswprint(). */
1963fc89c0fbSmrg    setlocale(LC_CTYPE, "");
1964fc89c0fbSmrg
1965fc89c0fbSmrg    /* Handle display name, opening the display */
1966fc89c0fbSmrg    Setup_Display_And_Screen(&argc, argv);
1967fc89c0fbSmrg
1968fc89c0fbSmrg    /* Handle selecting the window to display properties for */
1969fc89c0fbSmrg    target_win = Select_Window_Args(&argc, argv);
1970fc89c0fbSmrg
1971fc89c0fbSmrg    /* Set up default atom to format, dformat mapping */
1972fc89c0fbSmrg    XpropMode = XpropWindowProperties;
1973fc89c0fbSmrg    for (n = argc, nargv = argv; n; nargv++, n--)
1974fc89c0fbSmrg	if (! strcmp(nargv[0], "-font")) {
1975fc89c0fbSmrg	    XpropMode = XpropFontProperties;
1976fc89c0fbSmrg	    break;
1977fc89c0fbSmrg	}
1978fc89c0fbSmrg    Setup_Mapping();
1979fc89c0fbSmrg    if ((name = getenv("XPROPFORMATS"))) {
1980fc89c0fbSmrg	if (!(stream = fopen(name, "r")))
1981fc89c0fbSmrg	    Fatal_Error("unable to open file %s for reading.", name);
1982fc89c0fbSmrg	Read_Mappings(stream);
1983fc89c0fbSmrg	fclose(stream);
1984fc89c0fbSmrg    }
1985fc89c0fbSmrg
1986fc89c0fbSmrg    /* Handle '-' options to setup xprop, select window to work on */
1987fc89c0fbSmrg    while (argv++, --argc>0 && **argv == '-') {
1988fc89c0fbSmrg	if (!strcmp(argv[0], "-"))
1989fc89c0fbSmrg	    continue;
1990f9c28e31Smrg	if (!strcmp(argv[0], "-help")) {
1991f9c28e31Smrg	    help ();
1992f9c28e31Smrg	    /* NOTREACHED */
1993f9c28e31Smrg	}
1994fc89c0fbSmrg	if (!strcmp(argv[0], "-grammar")) {
1995fc89c0fbSmrg	    grammar ();
1996fc89c0fbSmrg	    /* NOTREACHED */
1997fc89c0fbSmrg	}
1998fc89c0fbSmrg	if (!strcmp(argv[0], "-notype")) {
1999fc89c0fbSmrg	    notype = 1;
2000fc89c0fbSmrg	    continue;
2001fc89c0fbSmrg	}
2002fc89c0fbSmrg	if (!strcmp(argv[0], "-spy")) {
2003fc89c0fbSmrg	    spy = 1;
2004fc89c0fbSmrg	    continue;
2005fc89c0fbSmrg	}
2006fc89c0fbSmrg	if (!strcmp(argv[0], "-len")) {
200781676fe2Smrg	    if (++argv, --argc == 0) usage("-len requires an argument");
2008fc89c0fbSmrg	    max_len = atoi(argv[0]);
2009fc89c0fbSmrg	    continue;
2010fc89c0fbSmrg	}
2011fc89c0fbSmrg	if (!strcmp(argv[0], "-formats") || !strcmp(argv[0], "-fs")) {
201281676fe2Smrg	    if (++argv, --argc == 0) usage("-fs requires an argument");
2013fc89c0fbSmrg	    if (!(stream = fopen(argv[0], "r")))
2014fc89c0fbSmrg		Fatal_Error("unable to open file %s for reading.", argv[0]);
2015fc89c0fbSmrg	    Read_Mappings(stream);
2016fc89c0fbSmrg	    fclose(stream);
2017fc89c0fbSmrg	    continue;
2018fc89c0fbSmrg	}
2019fc89c0fbSmrg	if (!strcmp(argv[0], "-font")) {
202081676fe2Smrg	    if (++argv, --argc == 0) usage("-font requires an argument");
2021fc89c0fbSmrg	    font = Open_Font(argv[0]);
202249bee38cSmrg	    target_win = (Window) -1;
2023fc89c0fbSmrg	    continue;
2024fc89c0fbSmrg	}
2025fc89c0fbSmrg	if (!strcmp(argv[0], "-remove")) {
2026f4a95042Smrg	    thunk t = {0};
202781676fe2Smrg	    if (++argv, --argc == 0) usage("-remove requires an argument");
2028fc89c0fbSmrg	    t.propname = argv[0];
2029fc89c0fbSmrg	    if (remove_props == NULL) remove_props = Create_Thunk_List();
2030fc89c0fbSmrg	    remove_props = Add_Thunk(remove_props, t);
2031fc89c0fbSmrg	    continue;
2032fc89c0fbSmrg	}
2033fc89c0fbSmrg	if (!strcmp(argv[0], "-set")) {
2034f4a95042Smrg	    thunk t = {0};
203549bee38cSmrg	    if (argc < 3) usage("insufficient arguments for -set");
2036fc89c0fbSmrg	    t.propname = argv[1];
2037fc89c0fbSmrg	    t.extra_value = argv[2];
2038fc89c0fbSmrg	    argv += 3; argc -= 3;
2039fc89c0fbSmrg	    if (set_props == NULL) set_props = Create_Thunk_List();
2040fc89c0fbSmrg	    set_props = Add_Thunk(set_props, t);
2041fc89c0fbSmrg	    continue;
2042fc89c0fbSmrg	}
2043fc89c0fbSmrg	if (!strcmp(argv[0], "-frame")) {
2044fc89c0fbSmrg	    frame_only = True;
2045fc89c0fbSmrg	    continue;
2046fc89c0fbSmrg	}
2047fc89c0fbSmrg	if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "-format")) {
2048fc89c0fbSmrg	    Parse_Format_Mapping(&argc, &argv);
2049fc89c0fbSmrg	    continue;
2050fc89c0fbSmrg	}
205181676fe2Smrg	if (!strcmp(argv[0], "-version")) {
205281676fe2Smrg	    puts(PACKAGE_STRING);
205381676fe2Smrg	    exit(0);
205481676fe2Smrg	}
205581676fe2Smrg	fprintf (stderr, "%s: unrecognized argument %s\n\n",
205681676fe2Smrg		 program_name, argv[0]);
205781676fe2Smrg	usage(NULL);
2058fc89c0fbSmrg    }
2059fc89c0fbSmrg
206081676fe2Smrg    if ((remove_props != NULL || set_props != NULL) && argc > 0) {
206181676fe2Smrg	fprintf (stderr, "%s: unrecognized argument %s\n\n",
206281676fe2Smrg		 program_name, argv[0]);
206381676fe2Smrg	usage(NULL);
206481676fe2Smrg    }
2065fc89c0fbSmrg
206691ec45ceSmrg    if (target_win == None)
206791ec45ceSmrg	target_win = Select_Window(dpy, !frame_only);
2068fc89c0fbSmrg
2069fc89c0fbSmrg    if (remove_props != NULL) {
2070fc89c0fbSmrg	int count;
2071fc89c0fbSmrg
207249bee38cSmrg	if (target_win == (Window) -1)
2073fc89c0fbSmrg	    Fatal_Error("-remove works only on windows, not fonts");
2074fc89c0fbSmrg
2075fc89c0fbSmrg	count = remove_props->thunk_count;
2076fc89c0fbSmrg	for (; count > 0; remove_props++, count--)
2077fc89c0fbSmrg	    Remove_Property (dpy, target_win, remove_props->propname);
2078fc89c0fbSmrg    }
2079fc89c0fbSmrg
2080fc89c0fbSmrg    if (set_props != NULL) {
2081fc89c0fbSmrg	int count;
2082fc89c0fbSmrg
208349bee38cSmrg	if (target_win == (Window) -1)
2084fc89c0fbSmrg	    Fatal_Error("-set works only on windows, not fonts");
2085fc89c0fbSmrg
2086fc89c0fbSmrg	count = set_props->thunk_count;
2087fc89c0fbSmrg	for (; count > 0; set_props++, count--)
2088fc89c0fbSmrg	    Set_Property (dpy, target_win, set_props->propname,
2089fc89c0fbSmrg			  set_props->extra_value);
2090fc89c0fbSmrg    }
2091fc89c0fbSmrg
2092fc89c0fbSmrg    if (remove_props != NULL || set_props != NULL) {
2093fc89c0fbSmrg	XCloseDisplay (dpy);
2094fc89c0fbSmrg	exit (0);
2095fc89c0fbSmrg    }
2096fc89c0fbSmrg
2097fc89c0fbSmrg    props = Handle_Prop_Requests(argc, argv);
2098fc89c0fbSmrg
209949bee38cSmrg    if (spy && target_win != (Window) -1) {
2100fc89c0fbSmrg	XEvent event;
2101fc89c0fbSmrg	const char *format, *dformat;
2102fc89c0fbSmrg
210391ec45ceSmrg	XSelectInput(dpy, target_win, PropertyChangeMask | StructureNotifyMask);
210491ec45ceSmrg 	old_error_handler = XSetErrorHandler(spy_error_handler);
2105fc89c0fbSmrg	for (;;) {
210691ec45ceSmrg	    fflush(stdout);
2107fc89c0fbSmrg	    XNextEvent(dpy, &event);
210891ec45ceSmrg 	    if (event.type == DestroyNotify)
210991ec45ceSmrg 		break;
211091ec45ceSmrg 	    if (event.type != PropertyNotify)
211191ec45ceSmrg 		continue;
2112fc89c0fbSmrg	    format = dformat = NULL;
2113fc89c0fbSmrg	    if (props) {
2114fc89c0fbSmrg		int i;
2115fc89c0fbSmrg		for (i = 0; i < props->thunk_count; i++)
211649bee38cSmrg		    if (props[i].value == (long) event.xproperty.atom)
2117fc89c0fbSmrg			break;
2118fc89c0fbSmrg		if (i >= props->thunk_count)
2119fc89c0fbSmrg		    continue;
2120fc89c0fbSmrg		format = props[i].format;
2121fc89c0fbSmrg		dformat = props[i].dformat;
2122fc89c0fbSmrg	    }
2123fc89c0fbSmrg	    Show_Prop(format, dformat, Format_Atom(event.xproperty.atom));
2124fc89c0fbSmrg	}
2125fc89c0fbSmrg    }
2126fc89c0fbSmrg    exit (0);
2127fc89c0fbSmrg}
2128