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
54#ifndef __has_attribute
55# define __has_attribute(x) 0  /* Compatibility with older compilers. */
56#endif
57
58static void
59#if __has_attribute(__cold__)
60__attribute__((__cold__))
61#endif
62#if __has_attribute(noreturn)
63__attribute__((noreturn))
64#endif
65usage(const char *errmsg)
66{
67    if (errmsg != NULL)
68	fprintf (stderr, "%s: %s\n\n", ProgramName, errmsg);
69
70    fprintf (stderr, "usage:  %s [-options...]\n\n%s\n", ProgramName,
71	     "where options include:\n"
72	     "    -display dpy            X server to which to connect\n"
73	     "    -format string          printf-style format to use\n"
74	     "    -range [num]-[num]      atom values to list\n"
75	     "    -name string            name of single atom to print\n"
76	     "    -version                print program version\n"
77	);
78    exit (1);
79}
80
81int
82main(int argc, char *argv[])
83{
84    char *displayname = NULL;
85    const char *format = "%lu\t%s";
86    int didit = 0;
87    xcb_connection_t *c = NULL;
88
89    ProgramName = argv[0];
90
91    for (int doit = 0; doit < 2; doit++) {	/* pre-parse to get display */
92	for (int i = 1; i < argc; i++) {
93	    char *arg = argv[i];
94
95	    if (arg[0] == '-') {
96		switch (arg[1]) {
97		  case 'd':			/* -display dpy */
98		    if (++i >= argc) usage ("-display requires an argument");
99		    if (!doit) displayname = argv[i];
100		    continue;
101		  case 'f':			/* -format string */
102		    if (++i >= argc) usage ("-format requires an argument");
103		    if (doit) format = argv[i];
104		    continue;
105		  case 'r':			/* -range num-[num] */
106		    if (++i >= argc) usage ("-range requires an argument");
107		    if (doit) {
108			do_range (c, format, argv[i]);
109			didit = 1;
110		    }
111		    continue;
112		  case 'n':			/* -name string */
113		    if (++i >= argc) usage ("-name requires an argument");
114		    if (doit) {
115			do_name (c, format, argv[i]);
116			didit = 1;
117		    }
118		    continue;
119		  case 'v':
120		    if (strcmp(arg, "-version") == 0) {
121			puts(PACKAGE_STRING);
122			exit(0);
123		    }
124		    /* else FALLTHROUGH to unrecognized arg case below */
125		}
126	    }
127	    fprintf (stderr, "%s: unrecognized argument %s\n\n",
128		     ProgramName, arg);
129	    usage (NULL);
130	}
131	if (!doit) {
132	    DisplayString = displayname;
133	    if (!DisplayString)
134		DisplayString = getenv("DISPLAY");
135	    if (!DisplayString)
136		DisplayString = "";
137	    c = xcb_connect(displayname, NULL);
138	    if (!c || xcb_connection_has_error(c)) {
139		fprintf (stderr, "%s:  unable to open display \"%s\"\n",
140			 ProgramName, DisplayString);
141		exit (1);
142	    }
143	} else
144	    if (!didit)		/* no options, default is list all */
145		list_atoms(c, format, 0, 0, 0);
146    }
147
148    xcb_disconnect(c);
149    exit (0);
150}
151
152static void
153do_name(xcb_connection_t *c, const char *format, char *name)
154{
155    xcb_intern_atom_reply_t *a = xcb_intern_atom_reply(c,
156	xcb_intern_atom_unchecked(c, 1, strlen(name), name), NULL);
157
158    if (a && a->atom != XCB_NONE) {
159	printf (format, (unsigned long) a->atom, name);
160	putchar ('\n');
161    } else {
162	fprintf (stderr, "%s:  no atom named \"%s\" on server \"%s\"\n",
163		 ProgramName, name, DisplayString);
164    }
165
166    if (a)
167	free(a);
168}
169
170
171#define RangeLow (1 << 0)
172#define RangeHigh (1 << 1)
173
174static int
175strtoatom(char *s, xcb_atom_t *atom)
176{
177    long long value;
178    char *end;
179
180    value = strtoll(s, &end, 10);
181    if (s == end || *end != '\0' || value < 0 || value > UINT32_MAX) {
182	return 1;
183    }
184
185    *atom = (xcb_atom_t) value;
186    return 0;
187}
188
189static int
190parse_range(char *range, xcb_atom_t *lowp, xcb_atom_t *highp)
191{
192    char *dash;
193    int mask = 0;
194
195    if (!range) {			/* NULL means default */
196	*lowp = 1;
197	return RangeLow;
198    }
199
200    dash = strchr(range, '-');
201    if (!dash) dash = strchr(range, ':');
202    if (dash) {
203	if (dash == range) {		/* -high */
204	    *lowp = 1;
205	} else {			/* low-[high] */
206	    *dash = '\0';
207	    if (strtoatom(range, lowp)) {
208		*dash = '-';
209		goto invalid;
210	    }
211	    *dash = '-';
212	}
213	mask |= RangeLow;
214	dash++;
215	if (*dash) {			/* [low]-high */
216	    if (strtoatom(dash, highp) || *highp < *lowp) {
217		goto invalid;
218	    }
219	    mask |= RangeHigh;
220	}
221    } else {				/* number (low == high) */
222	if (strtoatom(range, lowp)) {
223		goto invalid;
224	}
225	*highp = *lowp;
226	mask |= (RangeLow | RangeHigh);
227    }
228
229    return mask;
230invalid:
231    fprintf(stderr, "%s:  invalid range: %s\n", ProgramName, range);
232    exit(1);
233}
234
235static void
236do_range(xcb_connection_t *c, const char *format, char *range)
237{
238    int mask;
239    xcb_atom_t low, high;
240
241    mask = parse_range (range, &low, &high);
242    list_atoms (c, format, mask, low, high);
243}
244
245static int
246say_batch(xcb_connection_t *c, const char *format, xcb_get_atom_name_cookie_t *cookie, xcb_atom_t low, long count, int stop_error)
247{
248    xcb_generic_error_t *e;
249    char atom_name[1024];
250    long i;
251    int done = 0;
252
253    for (i = 0; i < count; i++)
254	cookie[i] = xcb_get_atom_name(c, (xcb_atom_t)i + low);
255
256    for (i = 0; i < count; i++) {
257	xcb_get_atom_name_reply_t *r;
258	r = xcb_get_atom_name_reply(c, cookie[i], &e);
259	if (r) {
260	    if (!done || !stop_error) {
261		/* We could just use %.*s in 'format', but we want to be compatible
262		   with legacy command line usage */
263		snprintf(atom_name, sizeof(atom_name), "%.*s",
264		    r->name_len, xcb_get_atom_name_name(r));
265
266		printf (format, i + low, atom_name);
267		putchar ('\n');
268	    }
269	    free(r);
270	}
271	if (e) {
272	    done = 1;
273	    free(e);
274	}
275    }
276
277    return done && stop_error;
278}
279
280static void
281list_atoms(xcb_connection_t *c, const char *format, int mask, xcb_atom_t low, xcb_atom_t high)
282{
283    xcb_get_atom_name_cookie_t cookie_jar[ATOMS_PER_BATCH];
284    int done = 0;
285
286    if ((mask & RangeLow) == 0)
287	low = 1;
288    if ((mask & RangeHigh) == 0)
289	high = UINT32_MAX;
290
291    while (!done) {
292	long count = (high - low < ATOMS_PER_BATCH - 1) ?
293	    (high - low + 1) : ATOMS_PER_BATCH;
294	done = say_batch(c, format, cookie_jar, low, count, (mask & RangeHigh) == 0);
295	if (high - low < UINT32_MAX && low == high - count + 1) {
296	    done = 1;
297	}
298	low += count;
299    }
300}
301