Moved global atoms to the server (based on the work of Sergei
Turchanov <turchanov@otvprim.ru>).

diff --git a/server/Makefile.in b/server/Makefile.in
index e849135..0db09e5 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -6,6 +6,7 @@
 MODULE    = none
 
 C_SRCS = \
+	atom.c \
 	change.c \
 	console.c \
 	context_i386.c \
diff --git a/server/atom.c b/server/atom.c
new file mode 100644
index 0000000..c1f4059
--- /dev/null
+++ b/server/atom.c
@@ -0,0 +1,281 @@
+/*
+ * Server-side atom management
+ *
+ * Copyright (C) 1999, 2000 Alexandre Julliard
+ * Copyright (C) 2000 Turchanov Sergei
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "unicode.h"
+#include "request.h"
+#include "object.h"
+
+#define HASH_SIZE     37
+#define MAX_ATOM_LEN  255
+#define MAX_ATOMS     0x4000
+
+struct atom_entry
+{
+    struct atom_entry *next;   /* hash table list */
+    struct atom_entry *prev;   /* hash table list */
+    int                atom;   /* atom handle */
+    int                count;  /* reference count */
+    int                hash;   /* string hash */
+    WCHAR              str[1]; /* atom string */
+};
+
+struct atom_table
+{
+    struct object       obj;                 /* object header */
+    int                 count;               /* count of atom handles */
+    int                 last;                /* last handle in-use */
+    struct atom_entry **handles;             /* atom handles */
+    struct atom_entry  *entries[HASH_SIZE];  /* hash table entries */
+};
+
+
+static void atom_table_dump( struct object *obj, int verbose );
+static void atom_table_destroy( struct object *obj );
+
+static const struct object_ops atom_table_ops =
+{
+    sizeof(struct atom_table),    /* size */
+    atom_table_dump,              /* dump */
+    no_add_queue,                 /* add_queue */
+    NULL,                         /* remove_queue */
+    NULL,                         /* signaled */
+    NULL,                         /* satified */
+    NULL,                         /* get_poll_events */
+    NULL,                         /* poll_event */
+    no_read_fd,                   /* get_read_fd */
+    no_write_fd,                  /* get_write_fd */
+    no_flush,                     /* flush */
+    no_get_file_info,             /* get_file_info */
+    atom_table_destroy            /* destroy */
+};
+
+static struct atom_table *global_table;
+
+
+/* copy an atom name to a temporary area */
+static const WCHAR *copy_name( const WCHAR *str )
+{
+    static WCHAR buffer[MAX_ATOM_LEN+1];
+    WCHAR *p = buffer;
+
+    while (p < buffer + sizeof(buffer) - 1) if (!(*p++ = *str++)) break;
+    *p = 0;
+    return buffer;
+}
+
+/* create an atom table */
+static struct atom_table *create_table(void)
+{
+    struct atom_table *table;
+
+    if ((table = alloc_object( &atom_table_ops, -1 )))
+    {
+        table->count = 64;
+        table->last  = -1;
+        memset( table->entries, 0, sizeof(table->entries) );
+        if ((table->handles = mem_alloc( sizeof(*table->handles) * table->count )))
+            return table;
+        release_object( table );
+        table = NULL;
+    }
+    return table;
+}
+
+/* retrieve an entry pointer from its atom */
+static struct atom_entry *get_atom_entry( struct atom_table *table, int atom )
+{
+    struct atom_entry *entry = NULL;
+    if (table && (atom >= 0) && (atom <= table->last)) entry = table->handles[atom];
+    if (!entry) set_error( STATUS_INVALID_HANDLE );
+    return entry;
+}
+
+/* add an atom entry in the table and return its handle */
+static int add_atom_entry( struct atom_table *table, struct atom_entry *entry )
+{
+    int i;
+    for (i = 0; i <= table->last; i++)
+        if (!table->handles[i]) goto found;
+    if (i == table->count)
+    {
+        struct atom_entry **new_table = NULL;
+        int new_size = table->count + table->count / 2;
+        if (new_size > MAX_ATOMS) new_size = MAX_ATOMS;
+        if (new_size > table->count)
+            new_table = realloc( table->handles, sizeof(*table->handles) * new_size );
+        if (!new_table)
+        {
+            set_error( STATUS_NO_MEMORY );
+            return -1;
+        }
+        table->count = new_size;
+        table->handles = new_table;
+    }
+    table->last = i;
+ found:
+    table->handles[i] = entry;
+    entry->atom = i;
+    return i;
+}
+
+/* compute the hash code for a string */
+static int atom_hash( const WCHAR *str )
+{
+    int i;
+    WCHAR hash = 0;
+    for (i = 0; str[i]; i++) hash ^= towupper(str[i]) + i;
+    return hash % HASH_SIZE;
+}
+
+/* dump an atom table */
+static void atom_table_dump( struct object *obj, int verbose )
+{
+    int i;
+    struct atom_table *table = (struct atom_table *)obj;
+    assert( obj->ops == &atom_table_ops );
+
+    fprintf( stderr, "Atom table size=%d\n", table->last + 1 );
+    if (!verbose) return;
+    for (i = 0; i <= table->last; i++)
+    {
+        struct atom_entry *entry = table->handles[i];
+        if (!entry) continue;
+        fprintf( stderr, "  %5d: ref=%d hash=%d \"", i, entry->count, entry->hash );
+        dump_strW( entry->str, strlenW(entry->str), stderr, "\"\"");
+        fprintf( stderr, "\"\n" );
+    }
+}
+
+/* destroy the atom table */
+static void atom_table_destroy( struct object *obj )
+{
+    int i;
+    struct atom_table *table = (struct atom_table *)obj;
+    assert( obj->ops == &atom_table_ops );
+    for (i = 0; i <= table->last; i++) free( table->handles[i] );
+    free( table->handles );
+}
+
+/* find an atom entry in its hash list */
+static struct atom_entry *find_atom_entry( struct atom_table *table, const WCHAR *str, int hash )
+{
+    struct atom_entry *entry = table->entries[hash];
+    while (entry)
+    {
+        if (!strcmpiW( entry->str, str )) break;
+        entry = entry->next;
+    }
+    return entry;
+}
+
+/* close the atom table; used on server exit */
+void close_atom_table(void)
+{
+    if (global_table) release_object( global_table );
+}
+
+/* add an atom to the table */
+static int add_atom( struct atom_table *table, const WCHAR *str )
+{
+    struct atom_entry *entry;
+    int hash = atom_hash( str );
+    int atom = -1;
+
+    if (!*str)
+    {
+        set_error( STATUS_OBJECT_NAME_INVALID );
+        return -1;
+    }
+    if ((entry = find_atom_entry( table, str, hash )))  /* exists already */
+    {
+        entry->count++;
+        return entry->atom;
+    }
+
+    if ((entry = mem_alloc( sizeof(*entry) + strlenW(str) * sizeof(WCHAR) )))
+    {
+        if ((atom = add_atom_entry( table, entry )) != -1)
+        {
+            entry->prev  = NULL;
+            if ((entry->next = table->entries[hash])) entry->next->prev = entry;
+            table->entries[hash] = entry;
+            entry->count = 1;
+            entry->hash  = hash;
+            strcpyW( entry->str, str );
+        }
+        else free( entry );
+    }
+    else set_error( STATUS_NO_MEMORY );
+    return atom;
+}
+
+/* delete an atom from the table */
+static void delete_atom( struct atom_table *table, int atom )
+{
+    struct atom_entry *entry = get_atom_entry( table, atom );
+    if (entry && !--entry->count)
+    {
+        if (entry->next) entry->next->prev = entry->prev;
+        if (entry->prev) entry->prev->next = entry->next;
+        else table->entries[entry->hash] = entry->next;
+        table->handles[atom] = NULL;
+        free( entry );
+    }
+}
+
+/* find an atom in the table */
+static int find_atom( struct atom_table *table, const WCHAR *str )
+{
+    struct atom_entry *entry;
+
+    if (table && ((entry = find_atom_entry( table, str, atom_hash(str) )))) return entry->atom;
+    if (!*str) set_error( STATUS_OBJECT_NAME_INVALID );
+    else set_error( STATUS_OBJECT_NAME_NOT_FOUND );
+    return -1;
+}
+
+/* get an atom name and refcount*/
+static int get_atom_name( struct atom_table *table, int atom, WCHAR *str )
+{
+    int count = -1;
+    struct atom_entry *entry = get_atom_entry( table, atom );
+    if (entry)
+    {
+        strcpyW( str, entry->str );
+        count = entry->count;
+    }
+    return count;
+}
+
+/* add a global atom */
+DECL_HANDLER(add_atom)
+{
+    if (!global_table) global_table = create_table();
+    if (global_table) req->atom = add_atom( global_table, copy_name( req->name ) );
+}
+
+/* delete a global atom */
+DECL_HANDLER(delete_atom)
+{
+    delete_atom( global_table, req->atom );
+}
+
+/* find a global atom */
+DECL_HANDLER(find_atom)
+{
+    req->atom = find_atom( global_table, copy_name( req->name ) );
+}
+
+/* get global atom name */
+DECL_HANDLER(get_atom_name)
+{
+    req->count = get_atom_name( global_table, req->atom, req->name );
+}
diff --git a/server/main.c b/server/main.c
index 7ec67d7..50d667b 100644
--- a/server/main.c
+++ b/server/main.c
@@ -95,6 +95,7 @@
 
 #ifdef DEBUG_OBJECTS
     close_registry();
+    close_atom_table();
     dump_objects();  /* dump any remaining objects */
 #endif
     exit(0);
diff --git a/server/object.h b/server/object.h
index 5ab85a1..0a7ad5d 100644
--- a/server/object.h
+++ b/server/object.h
@@ -165,6 +165,10 @@
 
 extern void close_registry(void);
 
+/* atom functions */
+
+extern void close_atom_table(void);
+
 /* global variables (command-line options) */
 
 extern int debug_level;
diff --git a/server/request.h b/server/request.h
index 9957e4e..dc4176a 100644
--- a/server/request.h
+++ b/server/request.h
@@ -163,6 +163,10 @@
 DECL_HANDLER(get_thread_context);
 DECL_HANDLER(set_thread_context);
 DECL_HANDLER(get_selector_entry);
+DECL_HANDLER(add_atom);
+DECL_HANDLER(delete_atom);
+DECL_HANDLER(find_atom);
+DECL_HANDLER(get_atom_name);
 
 #ifdef WANT_REQUEST_HANDLERS
 
@@ -263,6 +267,10 @@
     { (void(*)())req_get_thread_context, sizeof(struct get_thread_context_request) },
     { (void(*)())req_set_thread_context, sizeof(struct set_thread_context_request) },
     { (void(*)())req_get_selector_entry, sizeof(struct get_selector_entry_request) },
+    { (void(*)())req_add_atom, sizeof(struct add_atom_request) },
+    { (void(*)())req_delete_atom, sizeof(struct delete_atom_request) },
+    { (void(*)())req_find_atom, sizeof(struct find_atom_request) },
+    { (void(*)())req_get_atom_name, sizeof(struct get_atom_name_request) },
 };
 #endif  /* WANT_REQUEST_HANDLERS */
 
diff --git a/server/trace.c b/server/trace.c
index 391125d..ab0b439 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1182,6 +1182,45 @@
     fprintf( stderr, " flags=%02x", req->flags );
 }
 
+static void dump_add_atom_request( const struct add_atom_request *req )
+{
+    fprintf( stderr, " name=" );
+    dump_unicode_string( req->name );
+}
+
+static void dump_add_atom_reply( const struct add_atom_request *req )
+{
+    fprintf( stderr, " atom=%d", req->atom );
+}
+
+static void dump_delete_atom_request( const struct delete_atom_request *req )
+{
+    fprintf( stderr, " atom=%d", req->atom );
+}
+
+static void dump_find_atom_request( const struct find_atom_request *req )
+{
+    fprintf( stderr, " name=" );
+    dump_unicode_string( req->name );
+}
+
+static void dump_find_atom_reply( const struct find_atom_request *req )
+{
+    fprintf( stderr, " atom=%d", req->atom );
+}
+
+static void dump_get_atom_name_request( const struct get_atom_name_request *req )
+{
+    fprintf( stderr, " atom=%d", req->atom );
+}
+
+static void dump_get_atom_name_reply( const struct get_atom_name_request *req )
+{
+    fprintf( stderr, " count=%d,", req->count );
+    fprintf( stderr, " name=" );
+    dump_unicode_string( req->name );
+}
+
 static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_new_process_request,
     (dump_func)dump_new_thread_request,
@@ -1276,6 +1315,10 @@
     (dump_func)dump_get_thread_context_request,
     (dump_func)dump_set_thread_context_request,
     (dump_func)dump_get_selector_entry_request,
+    (dump_func)dump_add_atom_request,
+    (dump_func)dump_delete_atom_request,
+    (dump_func)dump_find_atom_request,
+    (dump_func)dump_get_atom_name_request,
 };
 
 static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
@@ -1372,6 +1415,10 @@
     (dump_func)dump_get_thread_context_reply,
     (dump_func)0,
     (dump_func)dump_get_selector_entry_reply,
+    (dump_func)dump_add_atom_reply,
+    (dump_func)0,
+    (dump_func)dump_find_atom_reply,
+    (dump_func)dump_get_atom_name_reply,
 };
 
 static const char * const req_names[REQ_NB_REQUESTS] = {
@@ -1468,6 +1515,10 @@
     "get_thread_context",
     "set_thread_context",
     "get_selector_entry",
+    "add_atom",
+    "delete_atom",
+    "find_atom",
+    "get_atom_name",
 };
 
 /* ### make_requests end ### */
diff --git a/server/unicode.h b/server/unicode.h
index dd7d03b..6479761 100644
--- a/server/unicode.h
+++ b/server/unicode.h
@@ -45,10 +45,10 @@
     return towupper(*str1) - towupper(*str2);
 }
 
-static inline WCHAR *strcpyW( WCHAR *src, const WCHAR *dst ) 
+static inline WCHAR *strcpyW( WCHAR *dst, const WCHAR *src ) 
 {
     const WCHAR *ret = dst;
-    while ((*src++ = *dst++));
+    while ((*dst++ = *src++));
     return (WCHAR *)ret;
 }