Release 950727
[wine] / ipc / dde_atom.c
1 /***************************************************************************
2  * Copyright 1995, Technion, Israel Institute of Technology
3  * Electrical Eng, Software Lab.
4  * Author:    Michael Veksler.
5  ***************************************************************************
6  * File:      dde_atom.c
7  * Purpose :  atom functionality for DDE
8  */
9
10 #include <ctype.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include "dde_atom.h"
15 #include "shm_main_blk.h"
16 #include "shm_fragment.h"
17 #include "stddebug.h"
18 #include "debug.h"
19
20 typedef struct
21 {
22         WORD        count;
23         BYTE        str[1];
24 } AtomData, *AtomData_ptr;
25
26 #define EMPTY   0                  /* empty hash entry */
27 #define DELETED -1                 /* deleted hash entry */
28 #define MIN_STR_ATOM 0xfc00
29
30 /* OFS2AtomData_ptr: extract AtomData_ptr from ofs */
31 #define OFS2AtomData_ptr(ofs) ((AtomData*)((int)&main_block->block+(ofs)))
32
33 /* OFS2AtomStr: find the string of the atom */
34 #define OFS2AtomStr(ofs)      (OFS2AtomData_ptr(atom_ofs)->str)
35
36 /* offset of an atom according to index */
37 #define ATOM_OFS(idx) (main_block->atoms[idx])
38
39 /* rot_left: rotate (with wrap-around) */
40 static __inline__ int rot_left(unsigned var,int count)
41 {
42   return (var<<count) | (var>> (sizeof(var)-count));
43 }
44 /* find the entry in the atom table for this string */
45 static int FindHash(LPCSTR str)    /* ignore str case */
46 {
47   int i,j;
48   unsigned hash1,hash2;
49   int deleted=-1;                  /* hash for deleted entry */
50   int atom_ofs;
51
52   /* get basic hash parameters */
53   for (i= hash1= hash2= 0; str[i] ; i++) {
54      hash1= rot_left(hash1,5) ^ toupper(str[i]);
55      hash2= rot_left(hash2,4) ^ toupper(str[i]);
56   }
57
58   hash1%= DDE_ATOMS;
59   atom_ofs=ATOM_OFS(hash1);
60   switch (atom_ofs) {
61     case EMPTY:                    /* empty atom entry */
62       return hash1;                
63     case DELETED:                  /* deleted atom entry */
64       deleted=hash1;
65       break;
66     default :                      /* non empty atom entry */
67       if (  strcasecmp( OFS2AtomStr(atom_ofs) , str) == 0)
68          return hash1;             /* found string in atom table */
69   }
70   hash2%= DDE_ATOMS-1 ;            /* hash2=0..(DDE_ATOMS-2) */
71   hash2++;                         /* hash2=1..(DDE_ATOMS-1) */
72
73   /* make jumps in the hash table by hash2 steps */
74   for (i=hash1+hash2 ; ; i+=hash2) {
75      /* i wraps around into j */
76      j=i-DDE_ATOMS;
77      if (j >= 0)
78         i=j;                       /* i wraps around */
79      
80      if (i==hash1) 
81         /* here if covered all hash locations, and got back to beginning */
82         return deleted;            /* return first empty entry - if any */
83      atom_ofs=ATOM_OFS(i);
84      switch (atom_ofs) {
85        case EMPTY:                 /* empty atom entry */
86          return i;                 
87        case DELETED:               /* deleted atom entry */
88          if (deleted < 0)
89             /* consider only the first deleted entry */
90             deleted= i;
91          break;
92        default :                   /* nonempty atom entry */
93          if (  strcasecmp( OFS2AtomStr(atom_ofs) , str) == 0)
94             return i;      /* found string in atom table */
95      }
96   }
97 }
98
99 void ATOM_GlobalInit(void)
100 {
101   int i;
102   
103   for (i=0 ; i < DDE_ATOMS ; i++)
104      ATOM_OFS(i)=EMPTY;
105 }
106
107 /***********************************************************************
108  *           GlobalAddAtom   (USER.268)
109  */
110
111 /* important! don't forget to unlock semaphores before return */
112 ATOM GlobalAddAtom( LPCSTR str )
113 {
114   int atom_idx;
115   int atom_ofs;
116   AtomData_ptr ptr;
117   ATOM atom;
118
119   dprintf_atom(stddeb,"GlobalAddAtom(%p)\n", str);
120   if ((unsigned) str < MIN_STR_ATOM)       /* MS-windows convention */
121      return (ATOM) (unsigned) str;
122   if (str[0] == '#') {             /* wine convention */
123      atom= (ATOM) atoi(&str[1]);
124      return (atom<MIN_STR_ATOM) ? atom : 0;
125   }
126   dprintf_atom(stddeb,"GlobalAddAtom(\"%s\")\n",str);
127
128   DDE_IPC_init();               /* will initialize only if needed */
129   
130   shm_write_wait(main_block->sem);
131
132   atom_idx=FindHash(str);
133   atom=(ATOM)0;
134
135   /* use "return" only at the end so semaphore handling is done only once */
136   if (atom_idx>=0) {
137      /* unless table full and item not found */
138      switch (atom_ofs= ATOM_OFS(atom_idx)) {
139        case DELETED:
140        case EMPTY:                 /* need to allocate new atom */
141          atom_ofs= shm_FragmentAlloc(&main_block->block,
142                                      strlen(str)+sizeof(AtomData));
143          if (atom_ofs==NIL)
144             break;                 /* no more memory (atom==0) */
145          ATOM_OFS(atom_idx)=atom_ofs;
146          ptr=OFS2AtomData_ptr(atom_ofs);
147          strcpy(ptr->str,str);
148          ptr->count=1;
149          atom=(ATOM)(atom_idx+MIN_STR_ATOM);
150          break;
151        default :                   /* has to update existing atom */
152          OFS2AtomData_ptr(atom_ofs)->count++;
153          atom=(ATOM)(atom_idx+MIN_STR_ATOM);
154      } /* end of switch */
155   } /* end of if */
156   shm_write_signal(main_block->sem);
157   return atom;
158 }
159
160 /***********************************************************************
161  *           GlobalDeleteAtom   (USER.269)
162  */
163
164 ATOM GlobalDeleteAtom( ATOM atom )
165 {
166   int atom_idx;
167   int atom_ofs;
168   AtomData_ptr atom_ptr;
169   ATOM retval=(ATOM) 0;
170   
171   dprintf_atom(stddeb,"GlobalDeleteAtom(\"%d\")\n",(int)atom);
172   atom_idx=(int)atom - MIN_STR_ATOM;
173   
174   if (atom_idx < 0 )
175      return 0;
176
177   DDE_IPC_init();          /* will initialize only if needed */
178
179   shm_write_wait(main_block->sem);
180   /* return used only once from here on -- for semaphore simplicity */
181   switch (atom_ofs=ATOM_OFS(atom_idx)) {
182     case DELETED:
183     case EMPTY:
184       fprintf(stderr,"trying to free unallocated atom %d\n", atom);
185       retval=atom;
186       break;
187     default :
188       atom_ptr=OFS2AtomData_ptr(atom_ofs);
189       if ( --atom_ptr->count == 0) {
190          shm_FragmentFree(&main_block->block,atom_ofs);
191          ATOM_OFS(atom_idx)=DELETED;
192       }
193   }
194   
195   shm_write_signal(main_block->sem);
196   return retval;
197 }
198
199 /***********************************************************************
200  *           GlobalFindAtom   (USER.270)
201  */
202 ATOM GlobalFindAtom( LPCSTR str )
203 {
204   int atom_idx;
205   int atom_ofs;
206
207   dprintf_atom(stddeb,"GlobalFindAtom(%p)\n", str );
208   if ((unsigned) str < MIN_STR_ATOM) /* MS-windows convention */
209      return (ATOM) (unsigned) str;
210   if (str[0] == '#') {             /* wine convention */
211      ATOM atom= (ATOM) atoi(&str[1]);
212      return (atom<MIN_STR_ATOM) ? atom : 0;
213   }
214   dprintf_atom(stddeb,"GlobalFindAtom(\"%s\")\n",str);
215
216   DDE_IPC_init();               /* will initialize only if needed */
217
218   shm_read_wait(main_block->sem);
219   atom_idx=FindHash(str);
220   if (atom_idx>=0)                 
221      atom_ofs=ATOM_OFS(atom_idx);  /* is it free ? */
222   else
223      atom_ofs=EMPTY;
224   shm_read_signal(main_block->sem);
225
226   if (atom_ofs==EMPTY || atom_ofs==DELETED)
227      return 0;
228   else
229      return (ATOM)(atom_idx+MIN_STR_ATOM);
230 }
231
232 /***********************************************************************
233  *           GlobalGetAtomName   (USER.271)
234  */
235 WORD GlobalGetAtomName( ATOM atom, LPSTR buffer, short count )
236 {
237   int atom_idx, atom_ofs;
238   int size;
239   /* temporary buffer to hold maximum "#65535\0" */
240   char str_num[7];
241   
242   if (count<2)                     /* no sense to go on */
243      return 0;
244   atom_idx=(int)atom - MIN_STR_ATOM;
245   
246   if (atom_idx < 0) {              /* word atom */
247      /* use wine convention... */
248      sprintf(str_num,"#%d%n",(int)atom,&size);
249      if (size+1>count) {           /* overflow ? */
250         /* truncate the string */
251         size=count-1;
252         str_num[size]='\0';
253      }
254      strcpy(buffer,str_num);
255      return size;
256   }
257
258   DDE_IPC_init();               /* will initialize only if needed */
259
260   /* string atom */
261   shm_read_wait(main_block->sem);
262   atom_ofs=ATOM_OFS(atom_idx);
263   if (atom_ofs==EMPTY || atom_ofs==DELETED) {
264      fprintf(stderr,"GlobalGetAtomName: illegal atom=%d\n",(int)atom);
265      size=0;
266   } else {                         /* non empty entry */
267      /* string length will be at most count-1, find actual size */
268      sprintf(buffer,"%.*s%n",count-1, OFS2AtomStr(atom_ofs), &size);
269   }
270   shm_read_signal(main_block->sem);
271   return size;
272 }
273