6a36e92a19fda95f6d01ed8e7250e31d8dc0e9a1
[yaz-moved-to-github.git] / src / ccl_stop_words.c
1 /*
2  * Copyright (c) 1995, the EUROPAGATE consortium (see below).
3  *
4  * The EUROPAGATE consortium members are:
5  *
6  *    University College Dublin
7  *    Danmarks Teknologiske Videnscenter
8  *    An Chomhairle Leabharlanna
9  *    Consejo Superior de Investigaciones Cientificas
10  *
11  * Permission to use, copy, modify, distribute, and sell this software and
12  * its documentation, in whole or in part, for any purpose, is hereby granted,
13  * provided that:
14  *
15  * 1. This copyright and permission notice appear in all copies of the
16  * software and its documentation. Notices of copyright or attribution
17  * which appear at the beginning of any file must remain unchanged.
18  *
19  * 2. The names of EUROPAGATE or the project partners may not be used to
20  * endorse or promote products derived from this software without specific
21  * prior written permission.
22  *
23  * 3. Users of this software (implementors and gateway operators) agree to
24  * inform the EUROPAGATE consortium of their use of the software. This
25  * information will be used to evaluate the EUROPAGATE project and the
26  * software, and to plan further developments. The consortium may use
27  * the information in later publications.
28  * 
29  * 4. Users of this software agree to make their best efforts, when
30  * documenting their use of the software, to acknowledge the EUROPAGATE
31  * consortium, and the role played by the software in their work.
32  *
33  * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND,
34  * EXPRESS, IMPLIED, OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
35  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
36  * IN NO EVENT SHALL THE EUROPAGATE CONSORTIUM OR ITS MEMBERS BE LIABLE
37  * FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF
38  * ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
39  * OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND
40  * ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
41  * USE OR PERFORMANCE OF THIS SOFTWARE.
42  *
43  */
44
45 /** 
46  * \file ccl_stop_words.c
47  * \brief Removes stop words from terms in RPN tree
48  */
49
50 #include <stdio.h>
51 #include <string.h>
52 #include <ctype.h>
53 #include <yaz/ccl.h>
54 #include <yaz/nmem.h>
55
56 struct ccl_stop_info {
57     char *qualname;
58     char *term;
59     struct ccl_stop_info *next;
60 };
61
62 struct ccl_stop_words {
63     char *blank_chars;
64     NMEM nmem; /* memory for removed items */
65     struct ccl_stop_info *removed_items;
66 };
67     
68 static void append_removed_item(ccl_stop_words_t csw,
69                                 const char *qname,
70                                 const char *t, size_t len)
71 {
72     struct ccl_stop_info *csi = nmem_malloc(csw->nmem, sizeof(*csi));
73     struct ccl_stop_info **csip = &csw->removed_items;
74     if (qname)
75         csi->qualname = nmem_strdup(csw->nmem, qname);
76     else
77         csi->qualname = 0;
78
79     csi->term = nmem_malloc(csw->nmem, len+1);
80     memcpy(csi->term, t, len);
81     csi->term[len] = '\0';
82     csi->next = 0;
83
84     while (*csip)
85         csip = &(*csip)->next;
86     
87     *csip = csi;
88 }
89
90 ccl_stop_words_t ccl_stop_words_create(void)
91 {
92     NMEM nmem = nmem_create();
93     ccl_stop_words_t csw = xmalloc(sizeof(*csw));
94     csw->nmem = nmem;
95     csw->removed_items = 0;
96     csw->blank_chars = xstrdup(" \r\n\t");
97     return csw;
98 }
99
100 void ccl_stop_words_destroy(ccl_stop_words_t csw)
101 {
102     if (csw)
103     {
104         nmem_destroy(csw->nmem);
105         xfree(csw->blank_chars);
106         xfree(csw);
107     }
108 }
109
110 struct ccl_rpn_node *ccl_remove_stop_r(ccl_stop_words_t csw,
111                                        CCL_bibset bibset,
112                                        struct ccl_rpn_node *p)
113 {
114     struct ccl_rpn_node *left, *right;
115     switch (p->kind)
116     {
117     case CCL_RPN_AND:
118     case CCL_RPN_OR:
119     case CCL_RPN_NOT:
120     case CCL_RPN_PROX:
121         left = ccl_remove_stop_r(csw, bibset, p->u.p[0]);
122         right = ccl_remove_stop_r(csw, bibset, p->u.p[1]);
123         if (!left || !right)
124         {
125             /* we must delete our binary node and return child (if any) */
126             p->u.p[0] = 0;
127             p->u.p[1] = 0;
128             ccl_rpn_delete(p);
129             if (left)
130                 return left;
131             else
132                 return right;
133         }
134         break;
135     case CCL_RPN_SET:
136         break;
137     case CCL_RPN_TERM:
138         if (p->u.t.term)
139         {
140             int found = 1;
141             while (found)
142             {
143                 char *cp = p->u.t.term;
144                 found = 0;
145                 while (1)
146                 {
147                     while (*cp && strchr(csw->blank_chars, *cp))
148                         cp++;
149                     if (!*cp)
150                         break;
151                     else
152                     {
153                         char *cp0 = cp;
154                         while (*cp && !strchr(csw->blank_chars, *cp))
155                             cp++;
156                         if (cp != cp0)
157                         {
158                             size_t len = cp - cp0;
159                             if (ccl_search_stop(bibset, p->u.t.qual,
160                                                 cp0, len))
161                             {
162                                 append_removed_item(csw, p->u.t.qual,
163                                                     cp0, len);
164                                 while (*cp && strchr(csw->blank_chars, *cp))
165                                     cp++;
166                                 memmove(cp0, cp, strlen(cp)+1);
167                                 found = 1;
168                                 break;
169                             }
170                         }
171                     }
172                 }
173             }
174         }
175         /* chop right blanks .. and see if term it gets empty */
176         if (p->u.t.term && csw->removed_items)
177         {
178             char *cp = p->u.t.term + strlen(p->u.t.term);
179             while (1)
180             {
181                 if (cp == p->u.t.term)
182                 {
183                     /* term is empty / blank */
184                     ccl_rpn_delete(p);
185                     return 0;
186                 }
187                 if (!strchr(csw->blank_chars, cp[-1]))
188                     break;
189                 /* chop right */
190                 cp[-1] = 0;
191                 --cp;
192             }
193         }
194         break;
195     }
196     return p;
197 }
198
199 int ccl_stop_words_tree(ccl_stop_words_t csw,
200                         CCL_bibset bibset, struct ccl_rpn_node **t)
201 {
202     struct ccl_rpn_node *r;
203     
204     /* remove list items */
205     nmem_reset(csw->nmem);
206     csw->removed_items = 0;
207     
208     r = ccl_remove_stop_r(csw, bibset, *t);
209     *t = r;
210     if (csw->removed_items)
211         return 1;
212     return 0;
213 }
214
215 int ccl_stop_words_info(ccl_stop_words_t csw, int idx,
216                         const char **qualname, const char **term)
217 {
218     struct ccl_stop_info *csi = csw->removed_items;
219     int i = 0;
220     while (csi && i < idx)
221     {
222         csi = csi->next;
223         i++;
224     }
225     if (csi)
226     {
227         *qualname = csi->qualname;
228         *term = csi->term;
229         return 1;
230     }
231     return 0;
232 }
233
234 /*
235  * Local variables:
236  * c-basic-offset: 4
237  * indent-tabs-mode: nil
238  * End:
239  * vim: shiftwidth=4 tabstop=8 expandtab
240  */
241