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