- New implementation of SendMessage, ReceiveMessage, ReplyMessage functions
[wine] / memory / atom.c
1 /*
2  * Atom table functions
3  *
4  * Copyright 1993, 1994, 1995 Alexandre Julliard
5  */
6
7 /*
8  * Warning: The code assumes that LocalAlloc() returns a block aligned
9  * on a 4-bytes boundary (because of the shifting done in
10  * HANDLETOATOM).  If this is not the case, the allocation code will
11  * have to be changed.
12  */
13
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <ctype.h>
18
19 #include "winuser.h"
20 #include "instance.h"
21 #include "ldt.h"
22 #include "stackframe.h"
23 #include "user.h"
24 #include "debug.h"
25
26 #ifdef CONFIG_IPC
27 #include "dde_atom.h"
28 #endif
29
30 #define DEFAULT_ATOMTABLE_SIZE    37
31 #define MIN_STR_ATOM              0xc000
32 #define MAX_ATOM_LEN              255
33
34 #define ATOMTOHANDLE(atom)        ((HANDLE16)(atom) << 2)
35 #define HANDLETOATOM(handle)      ((ATOM)(0xc000 | ((handle) >> 2)))
36
37 #define HAS_ATOM_TABLE(sel)  \
38           ((INSTANCEDATA*)PTR_SEG_OFF_TO_LIN(sel,0))->atomtable != 0)
39
40 #define GET_ATOM_TABLE(sel)  ((ATOMTABLE*)PTR_SEG_OFF_TO_LIN(sel, \
41           ((INSTANCEDATA*)PTR_SEG_OFF_TO_LIN(sel,0))->atomtable))
42
43 typedef struct
44 {
45     HANDLE16    next;
46     WORD        refCount;
47     BYTE        length;
48     BYTE        str[1];
49 } ATOMENTRY;
50
51 typedef struct
52 {
53     WORD        size;
54     HANDLE16    entries[1];
55 } ATOMTABLE;
56                 
57 static WORD ATOM_GlobalTable = 0;
58
59 /***********************************************************************
60  *           ATOM_InitTable
61  *
62  * NOTES
63  *      Should this validate the value of entries to be 0 < x < 0x3fff?
64  *
65  * RETURNS
66  *      Handle: Success
67  *      0: Failure
68  */
69 static HANDLE16 ATOM_InitTable(
70                 WORD selector, /* [in] Segment */
71                 WORD entries   /* [in] Size of atom table */
72 ) {
73     int i;
74     HANDLE16 handle;
75     ATOMTABLE *table;
76
77       /* We consider the first table to be initialized as the global table. 
78        * This works, as USER (both built-in and native) is the first one to 
79        * register ... 
80        */
81
82     if (!ATOM_GlobalTable) ATOM_GlobalTable = selector; 
83
84
85       /* Allocate the table */
86
87     handle = LOCAL_Alloc( selector, LMEM_FIXED,
88                           sizeof(ATOMTABLE) + (entries-1) * sizeof(HANDLE16) );
89     if (!handle) return 0;
90     table = (ATOMTABLE *)PTR_SEG_OFF_TO_LIN( selector, handle );
91     table->size = entries;
92     for (i = 0; i < entries; i++) table->entries[i] = 0;
93
94       /* Store a pointer to the table in the instance data */
95
96     ((INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( selector, 0 ))->atomtable = handle;
97     return handle;
98 }
99
100
101 /***********************************************************************
102  *           ATOM_Init
103  *
104  * Global table initialisation.
105  */
106 BOOL32 ATOM_Init( WORD globalTableSel )
107 {
108     return ATOM_InitTable( globalTableSel, DEFAULT_ATOMTABLE_SIZE ) != 0;
109 }
110
111
112 /***********************************************************************
113  *           ATOM_GetTable
114  *
115  * Return a pointer to the atom table of a given segment, creating
116  * it if necessary.
117  *
118  * RETURNS
119  *      Pointer to table: Success
120  *      NULL: Failure
121  */
122 static ATOMTABLE *ATOM_GetTable(
123                   WORD selector, /* [in] Segment */
124                   BOOL32 create  /* [in] Create */ )
125 {
126     INSTANCEDATA *ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( selector, 0 );
127     if (ptr->atomtable)
128     {
129         ATOMTABLE *table = (ATOMTABLE *)((char *)ptr + ptr->atomtable);
130         if (table->size) return table;
131     }
132     if (!create) return NULL;
133     if (!ATOM_InitTable( selector, DEFAULT_ATOMTABLE_SIZE )) return NULL;
134     /* Reload ptr in case it moved in linear memory */
135     ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( selector, 0 );
136     return (ATOMTABLE *)((char *)ptr + ptr->atomtable);
137 }
138
139
140 /***********************************************************************
141  *           ATOM_MakePtr
142  *
143  * Make an ATOMENTRY pointer from a handle (obtained from GetAtomHandle()).
144  */
145 static ATOMENTRY *ATOM_MakePtr(
146                   WORD selector,  /* [in] Segment */
147                   HANDLE16 handle /* [in] Handle */
148 ) {
149     return (ATOMENTRY *)PTR_SEG_OFF_TO_LIN( selector, handle );
150 }
151
152
153 /***********************************************************************
154  *           ATOM_Hash
155  * RETURNS
156  *      The hash value for the input string
157  */
158 static WORD ATOM_Hash(
159             WORD entries, /* [in] Total number of entries */
160             LPCSTR str,   /* [in] Pointer to string to hash */
161             WORD len      /* [in] Length of string */
162 ) {
163     WORD i, hash = 0;
164
165     TRACE(atom,"%x, %s, %x\n", entries, str, len);
166     
167     for (i = 0; i < len; i++) hash ^= toupper(str[i]) + i;
168     return hash % entries;
169 }
170
171 static BOOL32 ATOM_IsIntAtom(LPCSTR atomstr,WORD *atomid) {
172         LPSTR   xend;
173
174         if (!HIWORD(atomstr)) {
175                 *atomid = LOWORD(atomstr);
176                 return TRUE;
177         }
178         if (atomstr[0]!='#')
179                 return FALSE;
180         *atomid=strtol(atomstr+1,&xend,10);
181         if (*xend) {
182                 FIXME(atom,"found atom named '%s'\n",atomstr);
183                 return FALSE;
184         }
185         return TRUE;
186 }
187
188
189 /***********************************************************************
190  *           ATOM_AddAtom
191  *
192  * Windows DWORD aligns the atom entry size.
193  * The remaining unused string space created by the alignment
194  * gets padded with '\0's in a certain way to ensure
195  * that at least one trailing '\0' remains.
196  *
197  * RETURNS
198  *      Atom: Success
199  *      0: Failure
200  */
201 static ATOM ATOM_AddAtom(
202             WORD selector, /* [in] Segment */
203             LPCSTR str     /* [in] Pointer to the string to add */
204 ) {
205     WORD hash;
206     HANDLE16 entry;
207     ATOMENTRY * entryPtr;
208     ATOMTABLE * table;
209     int len, ae_len;
210     WORD        iatom;
211
212     TRACE(atom,"0x%x, %s\n", selector, str);
213     
214     if (ATOM_IsIntAtom(str,&iatom))
215         return iatom;
216     if ((len = strlen( str )) > MAX_ATOM_LEN) len = MAX_ATOM_LEN;
217     if (!(table = ATOM_GetTable( selector, TRUE ))) return 0;
218     hash = ATOM_Hash( table->size, str, len );
219     entry = table->entries[hash];
220     while (entry)
221     {
222         entryPtr = ATOM_MakePtr( selector, entry );
223         if ((entryPtr->length == len) && 
224             (!lstrncmpi32A( entryPtr->str, str, len )))
225         {
226             entryPtr->refCount++;
227         TRACE(atom,"-- existing 0x%x\n", entry);
228             return HANDLETOATOM( entry );
229         }
230         entry = entryPtr->next;
231     }
232
233     ae_len = (sizeof(ATOMENTRY)+len+3) & ~3;
234     entry = LOCAL_Alloc( selector, LMEM_FIXED, ae_len);
235     if (!entry) return 0;
236     /* Reload the table ptr in case it moved in linear memory */
237     table = ATOM_GetTable( selector, FALSE );
238     entryPtr = ATOM_MakePtr( selector, entry );
239     entryPtr->next = table->entries[hash];
240     entryPtr->refCount = 1;
241     entryPtr->length = len;
242     strncpy( entryPtr->str, str, ae_len - sizeof(ATOMENTRY) + 1); /* always use strncpy ('\0's padding) */
243     table->entries[hash] = entry;
244     TRACE(atom,"-- new 0x%x\n", entry);
245     return HANDLETOATOM( entry );
246 }
247
248
249 /***********************************************************************
250  *           ATOM_DeleteAtom
251  * RETURNS
252  *      0: Success
253  *      Atom: Failure
254  */
255 static ATOM ATOM_DeleteAtom(
256             WORD selector, /* [in] Segment */
257             ATOM atom      /* [in] Atom to delete */
258 ) {
259     ATOMENTRY * entryPtr;
260     ATOMTABLE * table;
261     HANDLE16 entry, *prevEntry;
262     WORD hash;
263
264     TRACE(atom,"0x%x, 0x%x\n", selector, atom);
265     
266     if (atom < MIN_STR_ATOM) return 0;  /* Integer atom */
267
268     if (!(table = ATOM_GetTable( selector, FALSE ))) return 0;
269     entry = ATOMTOHANDLE( atom );
270     entryPtr = ATOM_MakePtr( selector, entry );
271
272       /* Find previous atom */
273     hash = ATOM_Hash( table->size, entryPtr->str, entryPtr->length );
274     prevEntry = &table->entries[hash];
275     while (*prevEntry && *prevEntry != entry)
276     {
277         ATOMENTRY * prevEntryPtr = ATOM_MakePtr( selector, *prevEntry );
278         prevEntry = &prevEntryPtr->next;
279     }    
280     if (!*prevEntry) return atom;
281
282       /* Delete atom */
283     if (--entryPtr->refCount == 0)
284     {
285         *prevEntry = entryPtr->next;
286         LOCAL_Free( selector, entry );
287     }    
288     return 0;
289 }
290
291
292 /***********************************************************************
293  *           ATOM_FindAtom
294  * RETURNS
295  *      Atom: Success
296  *      0: Failure
297  */
298 static ATOM ATOM_FindAtom(
299             WORD selector, /* [in] Segment */
300             LPCSTR str     /* [in] Pointer to string to find */
301 ) {
302     ATOMTABLE * table;
303     WORD hash,iatom;
304     HANDLE16 entry;
305     int len;
306
307     TRACE(atom,"%x, %s\n", selector, str);
308     if (ATOM_IsIntAtom(str,&iatom))
309         return iatom;
310     if ((len = strlen( str )) > 255) len = 255;
311     if (!(table = ATOM_GetTable( selector, FALSE ))) return 0;
312     hash = ATOM_Hash( table->size, str, len );
313     entry = table->entries[hash];
314     while (entry)
315     {
316         ATOMENTRY * entryPtr = ATOM_MakePtr( selector, entry );
317         if ((entryPtr->length == len) && 
318             (!lstrncmpi32A( entryPtr->str, str, len )))
319     {    TRACE(atom,"-- found %x\n", entry);
320             return HANDLETOATOM( entry );
321     }
322         entry = entryPtr->next;
323     }
324     TRACE(atom,"-- not found\n");
325     return 0;
326 }
327
328
329 /***********************************************************************
330  *           ATOM_GetAtomName
331  * RETURNS
332  *      Length of string copied to buffer: Success
333  *      0: Failure
334  */
335 static UINT32 ATOM_GetAtomName(
336               WORD selector, /* [in]  Segment */
337               ATOM atom,     /* [in]  Atom identifier */
338               LPSTR buffer,  /* [out] Pointer to buffer for atom string */
339               INT32 count    /* [in]  Size of buffer */
340 ) {
341     ATOMTABLE * table;
342     ATOMENTRY * entryPtr;
343     HANDLE16 entry;
344     char * strPtr;
345     UINT32 len;
346     char text[8];
347     
348     TRACE(atom,"%x, %x\n", selector, atom);
349     
350     if (!count) return 0;
351     if (atom < MIN_STR_ATOM)
352     {
353         sprintf( text, "#%d", atom );
354         len = strlen(text);
355         strPtr = text;
356     }
357     else
358     {
359        if (!(table = ATOM_GetTable( selector, FALSE ))) return 0;
360         entry = ATOMTOHANDLE( atom );
361         entryPtr = ATOM_MakePtr( selector, entry );
362         len = entryPtr->length;
363         strPtr = entryPtr->str;
364     }
365     if (len >= count) len = count-1;
366     memcpy( buffer, strPtr, len );
367     buffer[len] = '\0';
368     return len;
369 }
370
371
372 /***********************************************************************
373  *           InitAtomTable16   (KERNEL.68)
374  */
375 WORD WINAPI InitAtomTable16( WORD entries )
376 {
377     if (!entries) entries = DEFAULT_ATOMTABLE_SIZE;  /* sanity check */
378     return ATOM_InitTable( CURRENT_DS, entries );
379 }
380
381
382 /***********************************************************************
383  *           GetAtomHandle   (KERNEL.73)
384  */
385 HANDLE16 WINAPI GetAtomHandle( ATOM atom )
386 {
387     if (atom < MIN_STR_ATOM) return 0;
388     return ATOMTOHANDLE( atom );
389 }
390
391
392 /***********************************************************************
393  *           AddAtom16   (KERNEL.70)
394  */
395 ATOM WINAPI AddAtom16( SEGPTR str )
396 {
397     ATOM atom;
398     HANDLE16 ds = CURRENT_DS;
399
400     if (!HIWORD(str)) return (ATOM)LOWORD(str);  /* Integer atom */
401     if (SELECTOR_TO_ENTRY(LOWORD(str)) == SELECTOR_TO_ENTRY(ds))
402     {
403         /* If the string is in the same data segment as the atom table, make */
404         /* a copy of the string to be sure it doesn't move in linear memory. */
405         char buffer[MAX_ATOM_LEN+1];
406         lstrcpyn32A( buffer, (char *)PTR_SEG_TO_LIN(str), sizeof(buffer) );
407         atom = ATOM_AddAtom( ds, buffer );
408     }
409     else atom = ATOM_AddAtom( ds, (LPCSTR)PTR_SEG_TO_LIN(str) );
410     return atom;
411 }
412
413
414 /***********************************************************************
415  *           AddAtom32A   (KERNEL32.0)
416  * Adds a string to the atom table and returns the atom identifying the
417  * string.
418  *
419  * RETURNS
420  *      Atom: Success
421  *      0: Failure
422  */
423 ATOM WINAPI AddAtom32A(
424             LPCSTR str /* [in] Pointer to string to add */
425 ) {
426     return GlobalAddAtom32A( str );  /* FIXME */
427 }
428
429
430 /***********************************************************************
431  *           AddAtom32W   (KERNEL32.1)
432  * See AddAtom32A
433  */
434 ATOM WINAPI AddAtom32W( LPCWSTR str )
435 {
436     return GlobalAddAtom32W( str );  /* FIXME */
437 }
438
439
440 /***********************************************************************
441  *           DeleteAtom16   (KERNEL.71)
442  */
443 ATOM WINAPI DeleteAtom16( ATOM atom )
444 {
445     return ATOM_DeleteAtom( CURRENT_DS, atom );
446 }
447
448
449 /***********************************************************************
450  *           DeleteAtom32   (KERNEL32.69)
451  * Decrements the reference count of a string atom.  If count becomes
452  * zero, the string associated with the atom is removed from the table.
453  *
454  * RETURNS
455  *      0: Success
456  *      Atom: Failure
457  */
458 ATOM WINAPI DeleteAtom32(
459             ATOM atom /* [in] Atom to delete */
460 ) {
461     return GlobalDeleteAtom( atom );  /* FIXME */
462 }
463
464
465 /***********************************************************************
466  *           FindAtom16   (KERNEL.69)
467  */
468 ATOM WINAPI FindAtom16( SEGPTR str )
469 {
470     if (!HIWORD(str)) return (ATOM)LOWORD(str);  /* Integer atom */
471     return ATOM_FindAtom( CURRENT_DS, (LPCSTR)PTR_SEG_TO_LIN(str) );
472 }
473
474
475 /***********************************************************************
476  *           FindAtom32A   (KERNEL32.117)
477  * Searches the local atom table for the string and returns the atom
478  * associated with that string.
479  *
480  * RETURNS
481  *      Atom: Success
482  *      0: Failure
483  */
484 ATOM WINAPI FindAtom32A(
485             LPCSTR str /* [in] Pointer to string to find */
486 ) {
487     return GlobalFindAtom32A( str );  /* FIXME */
488 }
489
490
491 /***********************************************************************
492  *           FindAtom32W   (KERNEL32.118)
493  * See FindAtom32A
494  */
495 ATOM WINAPI FindAtom32W( LPCWSTR str )
496 {
497     return GlobalFindAtom32W( str );  /* FIXME */
498 }
499
500
501 /***********************************************************************
502  *           GetAtomName16   (KERNEL.72)
503  */
504 UINT16 WINAPI GetAtomName16( ATOM atom, LPSTR buffer, INT16 count )
505 {
506     return (UINT16)ATOM_GetAtomName( CURRENT_DS, atom, buffer, count );
507 }
508
509
510 /***********************************************************************
511  *           GetAtomName32A   (KERNEL32.149)
512  * Retrieves a copy of the string associated with the atom.
513  *
514  * RETURNS
515  *      Length of string: Success
516  *      0: Failure
517  */
518 UINT32 WINAPI GetAtomName32A(
519               ATOM atom,    /* [in]  Atom */
520               LPSTR buffer, /* [out] Pointer to string for atom string */
521               INT32 count   /* [in]  Size of buffer */
522 ) {
523     return GlobalGetAtomName32A( atom, buffer, count );  /* FIXME */
524 }
525
526
527 /***********************************************************************
528  *           GetAtomName32W   (KERNEL32.150)
529  * See GetAtomName32A
530  */
531 UINT32 WINAPI GetAtomName32W( ATOM atom, LPWSTR buffer, INT32 count )
532 {
533     return GlobalGetAtomName32W( atom, buffer, count );  /* FIXME */
534 }
535
536
537 /***********************************************************************
538  *           GlobalAddAtom16   (USER.268)
539  */
540 ATOM WINAPI GlobalAddAtom16( SEGPTR str )
541 {
542     if (!HIWORD(str)) return (ATOM)LOWORD(str);  /* Integer atom */
543 #ifdef CONFIG_IPC
544     return DDE_GlobalAddAtom( str );
545 #else
546     return ATOM_AddAtom( ATOM_GlobalTable, (LPCSTR)PTR_SEG_TO_LIN(str) );
547 #endif
548 }
549
550
551 /***********************************************************************
552  *           GlobalAddAtom32A   (KERNEL32.313)
553  * Adds a character string to the global atom table and returns a unique
554  * value identifying the string.
555  *
556  * RETURNS
557  *      Atom: Success
558  *      0: Failure
559  */
560 ATOM WINAPI GlobalAddAtom32A(
561             LPCSTR str /* [in] Pointer to string to add */
562 ) {
563     if (!HIWORD(str)) return (ATOM)LOWORD(str);  /* Integer atom */
564     return ATOM_AddAtom( ATOM_GlobalTable, str );
565 }
566
567
568 /***********************************************************************
569  *           GlobalAddAtom32W   (KERNEL32.314)
570  * See GlobalAddAtom32A
571  */
572 ATOM WINAPI GlobalAddAtom32W( LPCWSTR str )
573 {
574     char buffer[MAX_ATOM_LEN+1];
575     if (!HIWORD(str)) return (ATOM)LOWORD(str);  /* Integer atom */
576     lstrcpynWtoA( buffer, str, sizeof(buffer) );
577     return ATOM_AddAtom( ATOM_GlobalTable, buffer );
578 }
579
580
581 /***********************************************************************
582  *           GlobalDeleteAtom   (USER.269) (KERNEL32.317)
583  * Decrements the reference count of a string atom.  If the count is
584  * zero, the string associated with the atom is removed from the table.
585  *
586  * RETURNS
587  *      0: Success
588  *      Atom: Failure
589  */
590 ATOM WINAPI GlobalDeleteAtom(
591             ATOM atom /* [in] Atom to delete */
592 ) {
593 #ifdef CONFIG_IPC
594     return DDE_GlobalDeleteAtom( atom );
595 #else
596     return ATOM_DeleteAtom( ATOM_GlobalTable, atom );
597 #endif
598 }
599
600
601 /***********************************************************************
602  *           GlobalFindAtom16   (USER.270)
603  */
604 ATOM WINAPI GlobalFindAtom16( SEGPTR str )
605 {
606     if (!HIWORD(str)) return (ATOM)LOWORD(str);  /* Integer atom */
607 #ifdef CONFIG_IPC
608     return DDE_GlobalFindAtom( str );
609 #else
610     return ATOM_FindAtom( ATOM_GlobalTable, (LPCSTR)PTR_SEG_TO_LIN(str) );
611 #endif
612 }
613
614
615 /***********************************************************************
616  *           GlobalFindAtom32A   (KERNEL32.318)
617  * Searches the atom table for the string and returns the atom
618  * associated with it.
619  *
620  * RETURNS
621  *      Atom: Success
622  *      0: Failure
623  */
624 ATOM WINAPI GlobalFindAtom32A(
625             LPCSTR str /* [in] Pointer to string to search for */
626 ) {
627     if (!HIWORD(str)) return (ATOM)LOWORD(str);  /* Integer atom */
628     return ATOM_FindAtom( ATOM_GlobalTable, str );
629 }
630
631
632 /***********************************************************************
633  *           GlobalFindAtom32W   (KERNEL32.319)
634  * See GlobalFindAtom32A
635  */
636 ATOM WINAPI GlobalFindAtom32W( LPCWSTR str )
637 {
638     char buffer[MAX_ATOM_LEN+1];
639     if (!HIWORD(str)) return (ATOM)LOWORD(str);  /* Integer atom */
640     lstrcpynWtoA( buffer, str, sizeof(buffer) );
641     return ATOM_FindAtom( ATOM_GlobalTable, buffer );
642 }
643
644
645 /***********************************************************************
646  *           GlobalGetAtomName16   (USER.271)
647  */
648 UINT16 WINAPI GlobalGetAtomName16( ATOM atom, LPSTR buffer, INT16 count )
649 {
650 #ifdef CONFIG_IPC
651     return DDE_GlobalGetAtomName( atom, buffer, count );
652 #else
653     return (UINT16)ATOM_GetAtomName( ATOM_GlobalTable, atom, buffer, count );
654 #endif
655 }
656
657
658 /***********************************************************************
659  *           GlobalGetAtomName32A   (KERNEL32.323)
660  * Retrieves a copy of the string associated with an atom.
661  *
662  * RETURNS
663  *      Length of string in characters: Success
664  *      0: Failure
665  */
666 UINT32 WINAPI GlobalGetAtomName32A(
667               ATOM atom,    /* [in]  Atom identifier */
668               LPSTR buffer, /* [out] Pointer to buffer for atom string */
669               INT32 count   /* [in]  Size of buffer */
670 ) {
671     return ATOM_GetAtomName( ATOM_GlobalTable, atom, buffer, count );
672 }
673
674
675 /***********************************************************************
676  *           GlobalGetAtomName32W   (KERNEL32.324)
677  * See GlobalGetAtomName32A
678  */
679 UINT32 WINAPI GlobalGetAtomName32W( ATOM atom, LPWSTR buffer, INT32 count )
680 {
681     char tmp[MAX_ATOM_LEN+1];
682     ATOM_GetAtomName( ATOM_GlobalTable, atom, tmp, sizeof(tmp) );
683     lstrcpynAtoW( buffer, tmp, count );
684     return lstrlen32W( buffer );
685 }