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