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