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