]> git.deb.at Git - pkg/abook.git/blob - database.c
use strcoll instead of strcmp for sorting entries
[pkg/abook.git] / database.c
1
2 /*
3  * $Id$
4  *
5  * by JH <jheinonen@users.sourceforge.net>
6  *
7  * Copyright (C) Jaakko Heinonen
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include "abook.h"
16 #include "database.h"
17 #include "list.h"
18 #include "misc.h"
19 #include "options.h"
20 #include "filter.h"
21 #ifdef HAVE_CONFIG_H
22 #       include "config.h"
23 #endif
24
25 static void     free_item(int i);
26
27
28 list_item *database = NULL;
29
30 int items = 0;
31
32 #define INITIAL_LIST_CAPACITY   30
33
34 int list_capacity = 0;
35
36 extern int first_list_item;
37 extern int curitem;
38 extern char *selected;
39
40 extern char *datafile;
41 extern char *rcfile;
42
43 /*
44  * field definitions
45  */
46
47 #include "edit.h"
48
49 /*
50  * notes about adding fields:
51  *      - do not change any fields in TAB_CONTACT
52  *      - do not add fields to contact tab
53  *      - 6 fields per tab is maximum
54  *      - reorganize the field numbers in database.h
55  */
56
57 struct abook_field abook_fields[ITEM_FIELDS] = {
58         {"Name",        "name",         TAB_CONTACT},/* NAME */
59         {"E-mails",     "email",        TAB_CONTACT},/* EMAIL */
60         {"Address",     "address",      TAB_ADDRESS},/* ADDRESS */
61         {"Address2",    "address2",     TAB_ADDRESS},/* ADDRESS2 */   
62         {"City",        "city",         TAB_ADDRESS},/* CITY */
63         {"State/Province","state",      TAB_ADDRESS},/* STATE */
64         {"ZIP/Postal Code","zip",       TAB_ADDRESS},/* ZIP */
65         {"Country",     "country",      TAB_ADDRESS},/* COUNTRY */
66         {"Home Phone",  "phone",        TAB_PHONE},/* PHONE */
67         {"Work Phone",  "workphone",    TAB_PHONE},/* WORKPHONE */
68         {"Fax",         "fax",          TAB_PHONE},/* FAX */
69         {"Mobile",      "mobile",       TAB_PHONE},/* MOBILEPHONE */
70         {"Nickname/Alias", "nick",      TAB_OTHER},/* NICK */
71         {"URL",         "url",          TAB_OTHER},/* URL */
72         {"Notes",       "notes",        TAB_OTHER},/* NOTES */
73 };
74
75
76 int
77 parse_database(FILE *in)
78 {
79         char *line = NULL;
80         char *tmp;
81         int sec=0, i;
82         list_item item;
83
84         memset(&item, 0, sizeof(item));
85         
86         for(;;) {
87                 line = getaline(in);
88                 if( feof(in) ) {
89                         if( item[NAME] && sec )
90                                 add_item2database(item);
91                         else
92                                 free_list_item(item);
93                         break;
94                 }
95
96                 if( !*line || *line == '\n' || *line == '#' ) {
97                         free(line);
98                         continue;
99                 } else if( *line == '[' ) {
100                         if( item[NAME] && sec )
101                                 add_item2database(item);
102                         else
103                                 free_list_item(item);
104                         memset(&item, 0, sizeof(item));
105                         sec = 1;
106                         if ( !(tmp = strchr(line, ']')))
107                                 sec = 0; /*incorrect section lines are skipped*/
108                 } else if((tmp = strchr(line, '=') ) && sec ) {
109                         *tmp++ = '\0';
110                         for(i=0; i<ITEM_FIELDS; i++)
111                                 if( !strcmp(abook_fields[i].key, line) ) {
112                                         item[i] = strdup(tmp);
113                                         goto next;
114                                 }
115                 }
116 next:
117                 free(line);
118         }
119
120         free(line);
121         return 0;
122 }
123
124                 
125
126 int
127 load_database(char *filename)
128 {
129         FILE *in;
130
131         if( database != NULL )
132                 close_database();
133
134         if ( (in = abook_fopen(filename, "r")) == NULL )
135                 return -1;
136         
137         parse_database(in);
138
139         if ( items == 0 )
140                 return 2;
141
142         return 0;
143 }
144
145 int
146 write_database(FILE *out, struct db_enumerator e)
147 {
148         int j;
149         int i = 0;
150
151         fprintf(out,
152                 "# abook addressbook file\n\n"
153                 "[format]\n"
154                 "program=" PACKAGE "\n"
155                 "version=" VERSION "\n"
156                 "\n\n"
157         );
158
159         db_enumerate_items(e) {
160                 fprintf(out, "[%d]\n", i);
161                 for(j = 0; j < ITEM_FIELDS; j++) {
162                         if( database[e.item][j] != NULL &&
163                                         *database[e.item][j] )
164                                 fprintf(out, "%s=%s\n",
165                                         abook_fields[j].key,
166                                         database[e.item][j]
167                                         );
168                 }
169                 fputc('\n', out);
170                 i++;
171         }
172
173         return 0;
174 }
175
176 int
177 save_database()
178 {
179         FILE *out;
180         struct db_enumerator e = init_db_enumerator(ENUM_ALL);
181
182         if( (out = abook_fopen(datafile, "w")) == NULL )
183                 return -1;
184
185         if( list_is_empty() ) {
186                 fclose(out);
187                 unlink(datafile);
188                 return 1;
189         }
190
191         
192         write_database(out, e);
193         
194         fclose(out);
195         
196         return 0;
197 }
198
199 static void
200 free_item(int item)
201 {
202         free_list_item(database[item]);
203 }
204
205 void
206 free_list_item(list_item item)
207 {
208         int i;
209
210         for(i=0; i<ITEM_FIELDS; i++)
211                 my_free(item[i]);
212 }
213
214 void
215 close_database()
216 {
217         int i;
218         
219         for(i=0; i < items; i++)
220                 free_item(i);
221
222         free(database);
223         free(selected);
224
225         database = NULL;
226         selected = NULL;
227
228         items = 0;
229         first_list_item = curitem = -1;
230         list_capacity = 0;
231 }
232
233 #define _MAX_FIELD_LEN(X)       (X == EMAIL ? MAX_EMAILSTR_LEN:MAX_FIELD_LEN)
234
235 static void
236 validate_item(list_item item)
237 {
238         int i;
239         char *tmp;
240         
241         if(item[EMAIL] == NULL)
242                 item[EMAIL] = strdup("");
243
244         for(i=0; i<ITEM_FIELDS; i++)
245                 if( item[i] && (strlen(item[i]) > _MAX_FIELD_LEN(i) ) ) {
246                         tmp = item[i];
247                         item[i][_MAX_FIELD_LEN(i)-1] = 0;
248                         item[i] = strdup(item[i]);
249                         free(tmp);
250                 }
251 }
252
253
254 static void
255 adjust_list_capacity()
256 {
257         if(list_capacity < 1)
258                 list_capacity = INITIAL_LIST_CAPACITY;
259         else if(items >= list_capacity)
260                 list_capacity *= 2;
261         else if(list_capacity / 2 > items)
262                 list_capacity /= 2;
263         else
264                 return;
265
266         database = abook_realloc(database,
267                         sizeof(list_item) * list_capacity);
268         selected = abook_realloc(selected, list_capacity);
269 }
270
271 int
272 add_item2database(list_item item)
273 {
274         if( item[NAME] == NULL || ! *item[NAME] ) {
275                 free_list_item(item);
276                 return 1;
277         }
278
279         if( ++items > list_capacity)
280                 adjust_list_capacity();
281
282         validate_item(item);
283
284         selected[LAST_ITEM] = 0;
285         itemcpy(database[LAST_ITEM], item);
286
287         return 0;
288 }
289
290 void
291 remove_selected_items()
292 {
293         int i, j;
294
295         if( list_is_empty() )
296                 return;
297
298         if( ! selected_items() )
299                 selected[ curitem ] = 1;
300         
301         for( j = LAST_ITEM; j >= 0; j-- ) {
302                 if( selected[j] ) {
303                         free_item(j); /* added for .4 data_s_ */
304                         for( i = j; i < LAST_ITEM; i++ ) {
305                                 itemcpy(database[ i ], database[ i + 1 ]);
306                                 selected[ i ] = selected[ i + 1 ];
307                         }
308                         items--;        
309                 }
310         }
311
312         if( curitem > LAST_ITEM && items > 0 )
313                 curitem = LAST_ITEM;
314
315
316         adjust_list_capacity();
317
318         select_none();
319 }
320
321 char *
322 get_surname(char *s)
323 {
324         int i, a;
325         int len = strlen(s);
326         char *name = strdup(s);
327
328         for( a = 0, i = len - 1; i >= 0; i--, a++ ) {
329                 name[a] = s[i];
330                 if(name[a] == ' ')
331                         break;
332         }
333
334         name[ a ] = 0;
335
336         revstr(name);
337
338         return name;
339 }
340
341 static int
342 surnamecmp(const void *i1, const void *i2)
343 {
344         int ret;
345         list_item a,b;
346         char *s1, *s2;
347
348         itemcpy(a, i1);
349         itemcpy(b, i2);
350
351         s1 = get_surname(a[NAME]);
352         s2 = get_surname(b[NAME]);
353
354         if( !(ret = safe_strcoll(s1, s2)) )
355                 ret = safe_strcoll(a[NAME], b[NAME]);
356
357         free(s1);
358         free(s2);
359
360         return ret;
361 }
362
363 static int
364 namecmp(const void *i1, const void *i2)
365 {
366         list_item a, b;
367
368         itemcpy(a, i1);
369         itemcpy(b, i2);
370         
371         return safe_strcoll( a[NAME], b[NAME] );
372 }
373
374 void
375 sort_database()
376 {
377         select_none();
378         
379         qsort((void *)database, items, sizeof(list_item), namecmp);
380
381         refresh_screen();
382 }
383
384 void
385 sort_surname()
386 {
387         select_none();
388
389         qsort((void *)database, items, sizeof(list_item), surnamecmp);
390
391         refresh_screen();
392 }
393
394 int
395 find_item(char *str, int start, int search_fields[])
396 {
397         int i;
398         char *findstr = NULL;
399         char *tmp = NULL;
400         int ret = -1; /* not found */
401         struct db_enumerator e = init_db_enumerator(ENUM_ALL);
402
403         if(list_is_empty() || !is_valid_item(start))
404                 return -2; /* error */
405
406         findstr = strdup(str);
407         findstr = strupper(findstr);
408
409         e.item = start - 1; /* must be "real start" - 1 */
410         db_enumerate_items(e) {
411                 for( i = 0; search_fields[i] >= 0; i++ ) {
412                         tmp = safe_strdup(database[e.item][search_fields[i]]);
413                         if( tmp && strstr(strupper(tmp), findstr) ) {
414                                 ret = e.item;
415                                 goto out;
416                         }
417                         my_free(tmp);
418                 }
419         }
420
421 out:
422         free(findstr);
423         free(tmp);
424         return ret;
425 }
426
427
428 int
429 is_selected(int item)
430 {
431         return selected[item];
432 }
433
434 int
435 is_valid_item(int item)
436 {
437         return item <= LAST_ITEM && item >= 0;
438 }
439
440 int
441 real_db_enumerate_items(struct db_enumerator e)
442 {
443         int item = max(0, e.item + 1);
444         int i;
445         
446         switch(e.mode) {
447 #ifdef DEBUG
448                 case ENUM_ALL:
449                         break;
450 #endif
451                 case ENUM_SELECTED:
452                         for(i = item; i < items; i++) {
453                                 if(is_selected(i)) {
454                                         item = i;
455                                         goto out;
456                                 }
457                         }
458                         return -1;
459 #ifdef DEBUG
460                 default:
461                         fprintf(stderr, "real_db_enumerate_items() "
462                                         "BUG: unknown db_enumerator mode: %d\n",
463                                         e.mode);
464                         break;
465 #endif
466         }
467 out:
468         return (item > LAST_ITEM || item < 0) ? -1 : item;
469 }
470
471 struct db_enumerator
472 init_db_enumerator(int mode)
473 {
474         struct db_enumerator new;
475
476         new.item = -1; /* important - means "start from beginning" */
477         new.mode = mode;
478
479         return new;
480 }