xlsatoms.c revision 2f6f3826
1/*
2 *
3Copyright 1989, 1998  The Open Group
4Copyright 2009 Open Text Corporation
5
6Permission to use, copy, modify, distribute, and sell this software and its
7documentation for any purpose is hereby granted without fee, provided that
8the above copyright notice appear in all copies and that both that
9copyright notice and this permission notice appear in supporting
10documentation.
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall not be
23used in advertising or otherwise to promote the sale, use or other dealings
24in this Software without prior written authorization from The Open Group.
25 *
26 * Author:  Jim Fulton, MIT X Consortium
27 * Author:  Peter Harris, Open Text Corporation
28 */
29
30#ifdef HAVE_CONFIG_H
31# include "config.h"
32#endif
33
34#include <limits.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <xcb/xcb.h>
39#include <xcb/xproto.h>
40
41#define ATOMS_PER_BATCH 100 /* This number can be tuned
42				higher for fewer round-trips
43				lower for less bandwidth wasted */
44
45static const char *ProgramName;
46static const char *DisplayString;
47
48static void do_name ( xcb_connection_t *c, const char *format, char *name );
49static int parse_range ( char *range, xcb_atom_t *lowp, xcb_atom_t *highp );
50static void do_range ( xcb_connection_t *c, const char *format, char *range );
51static void list_atoms ( xcb_connection_t *c, const char *format, int mask,
52			 xcb_atom_t low, xcb_atom_t high );
53
54static void
55usage(const char *errmsg)
56{
57    if (errmsg != NULL)
58	fprintf (stderr, "%s: %s\n\n", ProgramName, errmsg);
59
60    fprintf (stderr, "usage:  %s [-options...]\n\n%s\n", ProgramName,
61	     "where options include:\n"
62	     "    -display dpy            X server to which to connect\n"
63	     "    -format string          printf-style format to use\n"
64	     "    -range [num]-[num]      atom values to list\n"
65	     "    -name string            name of single atom to print\n"
66	     "    -version                print program version\n"
67	);
68    exit (1);
69}
70
71int
72main(int argc, char *argv[])
73{
74    char *displayname = NULL;
75    const char *format = "%lu\t%s";
76    int i, doit;
77    int didit = 0;
78    xcb_connection_t *c = NULL;
79
80    ProgramName = argv[0];
81
82    for (doit = 0; doit < 2; doit++) {	/* pre-parse to get display */
83	for (i = 1; i < argc; i++) {
84	    char *arg = argv[i];
85
86	    if (arg[0] == '-') {
87		switch (arg[1]) {
88		  case 'd':			/* -display dpy */
89		    if (++i >= argc) usage ("-display requires an argument");
90		    if (!doit) displayname = argv[i];
91		    continue;
92		  case 'f':			/* -format string */
93		    if (++i >= argc) usage ("-format requires an argument");
94		    if (doit) format = argv[i];
95		    continue;
96		  case 'r':			/* -range num-[num] */
97		    if (++i >= argc) usage ("-range requires an argument");
98		    if (doit) {
99			do_range (c, format, argv[i]);
100			didit = 1;
101		    }
102		    continue;
103		  case 'n':			/* -name string */
104		    if (++i >= argc) usage ("-name requires an argument");
105		    if (doit) {
106			do_name (c, format, argv[i]);
107			didit = 1;
108		    }
109		    continue;
110		  case 'v':
111		    if (strcmp(arg, "-version") == 0) {
112			puts(PACKAGE_STRING);
113			exit(0);
114		    }
115		    /* else FALLTHROUGH to unrecognized arg case below */
116		}
117	    }
118	    fprintf (stderr, "%s: unrecognized argument %s\n\n",
119		     ProgramName, arg);
120	    usage (NULL);
121	}
122	if (!doit) {
123	    DisplayString = displayname;
124	    if (!DisplayString)
125		DisplayString = getenv("DISPLAY");
126	    if (!DisplayString)
127		DisplayString = "";
128	    c = xcb_connect(displayname, NULL);
129	    if (!c || xcb_connection_has_error(c)) {
130		fprintf (stderr, "%s:  unable to open display \"%s\"\n",
131			 ProgramName, DisplayString);
132		exit (1);
133	    }
134	} else
135	    if (!didit)		/* no options, default is list all */
136		list_atoms(c, format, 0, 0, 0);
137    }
138
139    xcb_disconnect(c);
140    exit (0);
141}
142
143static void
144do_name(xcb_connection_t *c, const char *format, char *name)
145{
146    xcb_intern_atom_reply_t *a = xcb_intern_atom_reply(c,
147	xcb_intern_atom_unchecked(c, 1, strlen(name), name), NULL);
148
149    if (a && a->atom != XCB_NONE) {
150	printf (format, (unsigned long) a->atom, name);
151	putchar ('\n');
152    } else {
153	fprintf (stderr, "%s:  no atom named \"%s\" on server \"%s\"\n",
154		 ProgramName, name, DisplayString);
155    }
156
157    if (a)
158	free(a);
159}
160
161
162#define RangeLow (1 << 0)
163#define RangeHigh (1 << 1)
164
165static int
166strtoatom(char *s, xcb_atom_t *atom)
167{
168    long long value;
169    char *end;
170
171    value = strtoll(s, &end, 10);
172    if (s == end || *end != '\0' || value < 0 || value > UINT32_MAX) {
173	return 1;
174    }
175
176    *atom = value;
177    return 0;
178}
179
180static int
181parse_range(char *range, xcb_atom_t *lowp, xcb_atom_t *highp)
182{
183    char *dash;
184    int mask = 0;
185
186    if (!range) {			/* NULL means default */
187	*lowp = 1;
188	return RangeLow;
189    }
190
191    dash = strchr(range, '-');
192    if (!dash) dash = strchr(range, ':');
193    if (dash) {
194	if (dash == range) {		/* -high */
195	    *lowp = 1;
196	} else {			/* low-[high] */
197	    *dash = '\0';
198	    if (strtoatom(range, lowp)) {
199		*dash = '-';
200		goto invalid;
201	    }
202	    *dash = '-';
203	}
204	mask |= RangeLow;
205	dash++;
206	if (*dash) {			/* [low]-high */
207	    if (strtoatom(dash, highp) || *highp < *lowp) {
208		goto invalid;
209	    }
210	    mask |= RangeHigh;
211	}
212    } else {				/* number (low == high) */
213	if (strtoatom(range, lowp)) {
214		goto invalid;
215	}
216	*highp = *lowp;
217	mask |= (RangeLow | RangeHigh);
218    }
219
220    return mask;
221invalid:
222    fprintf(stderr, "%s:  invalid range: %s\n", ProgramName, range);
223    exit(1);
224}
225
226static void
227do_range(xcb_connection_t *c, const char *format, char *range)
228{
229    int mask;
230    xcb_atom_t low, high;
231
232    mask = parse_range (range, &low, &high);
233    list_atoms (c, format, mask, low, high);
234}
235
236static int
237say_batch(xcb_connection_t *c, const char *format, xcb_get_atom_name_cookie_t *cookie, xcb_atom_t low, long count, int stop_error)
238{
239    xcb_generic_error_t *e;
240    char atom_name[1024];
241    long i;
242    int done = 0;
243
244    for (i = 0; i < count; i++)
245	cookie[i] = xcb_get_atom_name(c, i + low);
246
247    for (i = 0; i < count; i++) {
248	xcb_get_atom_name_reply_t *r;
249	r = xcb_get_atom_name_reply(c, cookie[i], &e);
250	if (r) {
251	    if (!done || !stop_error) {
252		/* We could just use %.*s in 'format', but we want to be compatible
253		   with legacy command line usage */
254		snprintf(atom_name, sizeof(atom_name), "%.*s",
255		    r->name_len, xcb_get_atom_name_name(r));
256
257		printf (format, i + low, atom_name);
258		putchar ('\n');
259	    }
260	    free(r);
261	}
262	if (e) {
263	    done = 1;
264	    free(e);
265	}
266    }
267
268    return done && stop_error;
269}
270
271static void
272list_atoms(xcb_connection_t *c, const char *format, int mask, xcb_atom_t low, xcb_atom_t high)
273{
274    xcb_get_atom_name_cookie_t cookie_jar[ATOMS_PER_BATCH];
275    int done = 0;
276    long count;
277
278    if ((mask & RangeLow) == 0)
279	low = 1;
280    if ((mask & RangeHigh) == 0)
281	high = UINT32_MAX;
282
283    while (!done) {
284	count = high - low < ATOMS_PER_BATCH - 1 ? high - low + 1 : ATOMS_PER_BATCH;
285	done = say_batch(c, format, cookie_jar, low, count, (mask & RangeHigh) == 0);
286	if (high - low < UINT32_MAX && low == high - count + 1) {
287	    done = 1;
288	}
289	low += count;
290    }
291}
292