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