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