]> git.deb.at Git - pkg/abook.git/blob - database.c
Attempt to improve --datafile behavior
[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         return (items == 0) ? 2 : 0;
140 }
141
142 int
143 write_database(FILE *out, struct db_enumerator e)
144 {
145         int j;
146         int i = 0;
147
148         fprintf(out,
149                 "# abook addressbook file\n\n"
150                 "[format]\n"
151                 "program=" PACKAGE "\n"
152                 "version=" VERSION "\n"
153                 "\n\n"
154         );
155
156         db_enumerate_items(e) {
157                 fprintf(out, "[%d]\n", i);
158                 for(j = 0; j < ITEM_FIELDS; j++) {
159                         if( database[e.item][j] != NULL &&
160                                         *database[e.item][j] )
161                                 fprintf(out, "%s=%s\n",
162                                         abook_fields[j].key,
163                                         database[e.item][j]
164                                         );
165                 }
166                 fputc('\n', out);
167                 i++;
168         }
169
170         return 0;
171 }
172
173 int
174 save_database()
175 {
176         FILE *out;
177         struct db_enumerator e = init_db_enumerator(ENUM_ALL);
178
179         if( (out = abook_fopen(datafile, "w")) == NULL )
180                 return -1;
181
182         if( list_is_empty() ) {
183                 fclose(out);
184                 unlink(datafile);
185                 return 1;
186         }
187
188         
189         write_database(out, e);
190         
191         fclose(out);
192         
193         return 0;
194 }
195
196 static void
197 free_item(int item)
198 {
199         free_list_item(database[item]);
200 }
201
202 void
203 free_list_item(list_item item)
204 {
205         int i;
206
207         for(i=0; i<ITEM_FIELDS; i++)
208                 my_free(item[i]);
209 }
210
211 void
212 close_database()
213 {
214         int i;
215         
216         for(i=0; i < items; i++)
217                 free_item(i);
218
219         free(database);
220         free(selected);
221
222         database = NULL;
223         selected = NULL;
224
225         items = 0;
226         first_list_item = curitem = -1;
227         list_capacity = 0;
228 }
229
230 #define _MAX_FIELD_LEN(X)       (X == EMAIL ? MAX_EMAILSTR_LEN:MAX_FIELD_LEN)
231
232 static void
233 validate_item(list_item item)
234 {
235         int i;
236         char *tmp;
237         
238         if(item[EMAIL] == NULL)
239                 item[EMAIL] = strdup("");
240
241         for(i=0; i<ITEM_FIELDS; i++)
242                 if( item[i] && ((int)strlen(item[i]) > _MAX_FIELD_LEN(i) ) ) {
243                         tmp = item[i];
244                         item[i][_MAX_FIELD_LEN(i)-1] = 0;
245                         item[i] = strdup(item[i]);
246                         free(tmp);
247                 }
248 }
249
250
251 static void
252 adjust_list_capacity()
253 {
254         if(list_capacity < 1)
255                 list_capacity = INITIAL_LIST_CAPACITY;
256         else if(items >= list_capacity)
257                 list_capacity *= 2;
258         else if(list_capacity / 2 > items)
259                 list_capacity /= 2;
260         else
261                 return;
262
263         database = (list_item *)abook_realloc(database,
264                         sizeof(list_item) * list_capacity);
265         selected = (char *)abook_realloc(selected, list_capacity);
266 }
267
268 int
269 add_item2database(list_item item)
270 {
271         if( item[NAME] == NULL || ! *item[NAME] ) {
272                 free_list_item(item);
273                 return 1;
274         }
275
276         if( ++items > list_capacity)
277                 adjust_list_capacity();
278
279         validate_item(item);
280
281         selected[LAST_ITEM] = 0;
282         itemcpy(database[LAST_ITEM], item);
283
284         return 0;
285 }
286
287 void
288 remove_selected_items()
289 {
290         int i, j;
291
292         if( list_is_empty() )
293                 return;
294
295         if( ! selected_items() )
296                 selected[ curitem ] = 1;
297         
298         for( j = LAST_ITEM; j >= 0; j-- ) {
299                 if( selected[j] ) {
300                         free_item(j); /* added for .4 data_s_ */
301                         for( i = j; i < LAST_ITEM; i++ ) {
302                                 itemcpy(database[ i ], database[ i + 1 ]);
303                                 selected[ i ] = selected[ i + 1 ];
304                         }
305                         items--;        
306                 }
307         }
308
309         if( curitem > LAST_ITEM && items > 0 )
310                 curitem = LAST_ITEM;
311
312
313         adjust_list_capacity();
314
315         select_none();
316 }
317
318 char *
319 get_surname(char *s)
320 {
321         int i, a;
322         int len = strlen(s);
323         char *name = strdup(s);
324
325         for( a = 0, i = len - 1; i >= 0; i--, a++ ) {
326                 name[a] = s[i];
327                 if(name[a] == ' ')
328                         break;
329         }
330
331         name[ a ] = 0;
332
333         revstr(name);
334
335         return name;
336 }
337
338 static int
339 surnamecmp(const void *i1, const void *i2)
340 {
341         int ret;
342         list_item a,b;
343         char *s1, *s2;
344
345         itemcpy(a, i1);
346         itemcpy(b, i2);
347
348         s1 = get_surname(a[NAME]);
349         s2 = get_surname(b[NAME]);
350
351         if( !(ret = safe_strcoll(s1, s2)) )
352                 ret = safe_strcoll(a[NAME], b[NAME]);
353
354         free(s1);
355         free(s2);
356
357         return ret;
358 }
359
360 static int
361 namecmp(const void *i1, const void *i2)
362 {
363         list_item a, b;
364
365         itemcpy(a, i1);
366         itemcpy(b, i2);
367         
368         return safe_strcoll( a[NAME], b[NAME] );
369 }
370
371 void
372 sort_database()
373 {
374         select_none();
375         
376         qsort((void *)database, items, sizeof(list_item), namecmp);
377
378         refresh_screen();
379 }
380
381 void
382 sort_surname()
383 {
384         select_none();
385
386         qsort((void *)database, items, sizeof(list_item), surnamecmp);
387
388         refresh_screen();
389 }
390
391 int
392 find_item(char *str, int start, int search_fields[])
393 {
394         int i;
395         char *findstr = NULL;
396         char *tmp = NULL;
397         int ret = -1; /* not found */
398         struct db_enumerator e = init_db_enumerator(ENUM_ALL);
399
400         if(list_is_empty() || !is_valid_item(start))
401                 return -2; /* error */
402
403         findstr = strdup(str);
404         findstr = strupper(findstr);
405
406         e.item = start - 1; /* must be "real start" - 1 */
407         db_enumerate_items(e) {
408                 for( i = 0; search_fields[i] >= 0; i++ ) {
409                         tmp = safe_strdup(database[e.item][search_fields[i]]);
410                         if( tmp && strstr(strupper(tmp), findstr) ) {
411                                 ret = e.item;
412                                 goto out;
413                         }
414                         my_free(tmp);
415                 }
416         }
417
418 out:
419         free(findstr);
420         free(tmp);
421         return ret;
422 }
423
424
425 int
426 is_selected(int item)
427 {
428         return selected[item];
429 }
430
431 int
432 is_valid_item(int item)
433 {
434         return item <= LAST_ITEM && item >= 0;
435 }
436
437 int
438 real_db_enumerate_items(struct db_enumerator e)
439 {
440         int item = max(0, e.item + 1);
441         int i;
442         
443         switch(e.mode) {
444 #ifdef DEBUG
445                 case ENUM_ALL:
446                         break;
447 #endif
448                 case ENUM_SELECTED:
449                         for(i = item; i < items; i++) {
450                                 if(is_selected(i)) {
451                                         item = i;
452                                         goto out;
453                                 }
454                         }
455                         return -1;
456 #ifdef DEBUG
457                 default:
458                         fprintf(stderr, "real_db_enumerate_items() "
459                                         "BUG: unknown db_enumerator mode: %d\n",
460                                         e.mode);
461                         break;
462 #endif
463         }
464 out:
465         return (item > LAST_ITEM || item < 0) ? -1 : item;
466 }
467
468 struct db_enumerator
469 init_db_enumerator(int mode)
470 {
471         struct db_enumerator e;
472
473         e.item = -1; /* important - means "start from beginning" */
474         e.mode = mode;
475
476         return e;
477 }