1/*
2
3Copyright 1986, 1990, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included
12in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall
23not be used in advertising or otherwise to promote the sale, use or
24other dealings in this Software without prior written authorization
25from The Open Group.
26
27*/
28
29#ifdef HAVE_CONFIG_H
30#include <config.h>
31#endif
32#include "Xlibint.h"
33#include "Xintatom.h"
34
35#define HASH(sig) ((sig) & (TABLESIZE-1))
36#define REHASHVAL(sig) ((((sig) % (TABLESIZE-3)) + 2) | 1)
37#define REHASH(idx,rehash) ((idx + rehash) & (TABLESIZE-1))
38
39void
40_XFreeAtomTable(Display *dpy)
41{
42    register Entry *table;
43    register int i;
44    register Entry e;
45
46    if (dpy->atoms) {
47	table = dpy->atoms->table;
48	for (i = TABLESIZE; --i >= 0; ) {
49	    if ((e = *table++) && (e != RESERVED))
50		Xfree(e);
51	}
52	Xfree(dpy->atoms);
53	dpy->atoms = NULL;
54    }
55}
56
57static
58Atom _XInternAtom(
59    Display *dpy,
60    _Xconst char *name,
61    Bool onlyIfExists,
62    unsigned long *psig,
63    int *pidx,
64    int *pn)
65{
66    register AtomTable *atoms;
67    register char *s1, c, *s2;
68    register unsigned long sig;
69    register int idx = 0, i;
70    Entry e;
71    int n, firstidx, rehash = 0;
72    xInternAtomReq *req;
73
74    /* look in the cache first */
75    if (!(atoms = dpy->atoms)) {
76	dpy->atoms = atoms = Xcalloc(1, sizeof(AtomTable));
77	dpy->free_funcs->atoms = _XFreeAtomTable;
78    }
79    sig = 0;
80    for (s1 = (char *)name; (c = *s1++); )
81	sig += c;
82    n = s1 - (char *)name - 1;
83    if (atoms) {
84	firstidx = idx = HASH(sig);
85	while ((e = atoms->table[idx])) {
86	    if (e != RESERVED && e->sig == sig) {
87	    	for (i = n, s1 = (char *)name, s2 = EntryName(e); --i >= 0; ) {
88		    if (*s1++ != *s2++)
89		    	goto nomatch;
90	    	}
91	    	if (!*s2)
92		    return e->atom;
93	    }
94nomatch:    if (idx == firstidx)
95		rehash = REHASHVAL(sig);
96	    idx = REHASH(idx, rehash);
97	    if (idx == firstidx)
98		break;
99	}
100    }
101    *psig = sig;
102    *pidx = idx;
103    if (atoms && !atoms->table[idx])
104	atoms->table[idx] = RESERVED; /* reserve slot */
105    *pn = n;
106    /* not found, go to the server */
107    GetReq(InternAtom, req);
108    req->nbytes = n;
109    req->onlyIfExists = onlyIfExists;
110    req->length += (n+3)>>2;
111    Data(dpy, name, n);
112    return None;
113}
114
115void
116_XUpdateAtomCache(
117    Display *dpy,
118    const char *name,
119    Atom atom,
120    unsigned long sig,
121    int idx,
122    int n)
123{
124    Entry e, oe;
125    register char *s1;
126    register char c;
127    int firstidx, rehash;
128
129    if (!dpy->atoms) {
130	if (idx < 0) {
131	    dpy->atoms = Xcalloc(1, sizeof(AtomTable));
132	    dpy->free_funcs->atoms = _XFreeAtomTable;
133	}
134	if (!dpy->atoms)
135	    return;
136    }
137    if (!sig) {
138	for (s1 = (char *)name; (c = *s1++); )
139	    sig += c;
140	n = s1 - (char *)name - 1;
141	if (idx < 0) {
142	    firstidx = idx = HASH(sig);
143	    if (dpy->atoms->table[idx]) {
144		rehash = REHASHVAL(sig);
145		do
146		    idx = REHASH(idx, rehash);
147		while (idx != firstidx && dpy->atoms->table[idx]);
148	    }
149	}
150    }
151    e = Xmalloc(sizeof(EntryRec) + n + 1);
152    if (e) {
153	e->sig = sig;
154	e->atom = atom;
155	strcpy(EntryName(e), name);
156	if ((oe = dpy->atoms->table[idx]) && (oe != RESERVED))
157	    Xfree(oe);
158	dpy->atoms->table[idx] = e;
159    }
160}
161
162Atom
163XInternAtom (
164    Display *dpy,
165    const char *name,
166    Bool onlyIfExists)
167{
168    Atom atom;
169    unsigned long sig;
170    int idx, n;
171    xInternAtomReply rep;
172
173    if (!name)
174	name = "";
175    LockDisplay(dpy);
176    if ((atom = _XInternAtom(dpy, name, onlyIfExists, &sig, &idx, &n))) {
177	UnlockDisplay(dpy);
178	return atom;
179    }
180    if (dpy->atoms && dpy->atoms->table[idx] == RESERVED)
181	dpy->atoms->table[idx] = NULL; /* unreserve slot */
182    if (_XReply (dpy, (xReply *)&rep, 0, xTrue)) {
183	if ((atom = rep.atom))
184	    _XUpdateAtomCache(dpy, name, atom, sig, idx, n);
185    }
186    UnlockDisplay(dpy);
187    SyncHandle();
188    return (rep.atom);
189}
190
191typedef struct {
192    uint64_t start_seq;
193    uint64_t stop_seq;
194    char **names;
195    Atom *atoms;
196    int count;
197    Status status;
198} _XIntAtomState;
199
200static
201Bool _XIntAtomHandler(
202    register Display *dpy,
203    register xReply *rep,
204    char *buf,
205    int len,
206    XPointer data)
207{
208    register _XIntAtomState *state;
209    register int i, idx = 0;
210    xInternAtomReply replbuf;
211    register xInternAtomReply *repl;
212    uint64_t last_request_read = X_DPY_GET_LAST_REQUEST_READ(dpy);
213
214    state = (_XIntAtomState *)data;
215
216    if (last_request_read < state->start_seq ||
217	last_request_read > state->stop_seq)
218	return False;
219    for (i = 0; i < state->count; i++) {
220	if (state->atoms[i] & 0x80000000) {
221	    idx = ~state->atoms[i];
222	    state->atoms[i] = None;
223	    break;
224	}
225    }
226    if (i >= state->count)
227	return False;
228    if (rep->generic.type == X_Error) {
229	state->status = 0;
230	return False;
231    }
232    repl = (xInternAtomReply *)
233	_XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
234			(SIZEOF(xInternAtomReply) - SIZEOF(xReply)) >> 2,
235			True);
236    if ((state->atoms[i] = repl->atom))
237	_XUpdateAtomCache(dpy, state->names[i], (Atom) repl->atom,
238			  (unsigned long)0, idx, 0);
239    return True;
240}
241
242Status
243XInternAtoms (
244    Display *dpy,
245    char **names,
246    int count,
247    Bool onlyIfExists,
248    Atom *atoms_return)
249{
250    int i, idx, n, tidx;
251    unsigned long sig;
252    _XAsyncHandler async;
253    _XIntAtomState async_state;
254    int missed = -1;
255    xInternAtomReply rep;
256
257    LockDisplay(dpy);
258    async_state.start_seq = X_DPY_GET_REQUEST(dpy) + 1;
259    async_state.atoms = atoms_return;
260    async_state.names = names;
261    async_state.count = count - 1;
262    async_state.status = 1;
263    async.next = dpy->async_handlers;
264    async.handler = _XIntAtomHandler;
265    async.data = (XPointer)&async_state;
266    dpy->async_handlers = &async;
267    for (i = 0; i < count; i++) {
268	if (!(atoms_return[i] = _XInternAtom(dpy, names[i], onlyIfExists,
269					     &sig, &idx, &n))) {
270	    missed = i;
271	    atoms_return[i] = ~((Atom)idx);
272	    async_state.stop_seq = X_DPY_GET_REQUEST(dpy);
273	}
274    }
275    if (missed >= 0) {
276        if (dpy->atoms) {
277	    /* unreserve anything we just reserved */
278	    for (i = 0; i < count; i++) {
279		if (atoms_return[i] & 0x80000000) {
280		    tidx = ~atoms_return[i];
281		    if (dpy->atoms->table[tidx] == RESERVED)
282			dpy->atoms->table[tidx] = NULL;
283		}
284	    }
285        }
286	if (_XReply (dpy, (xReply *)&rep, 0, xTrue)) {
287	    if ((atoms_return[missed] = rep.atom))
288		_XUpdateAtomCache(dpy, names[missed], (Atom) rep.atom,
289				  sig, idx, n);
290	} else {
291	    atoms_return[missed] = None;
292	    async_state.status = 0;
293	}
294    }
295    DeqAsyncHandler(dpy, &async);
296    UnlockDisplay(dpy);
297    if (missed >= 0)
298	SyncHandle();
299    return async_state.status;
300}
301