]> git.deb.at Git - pkg/abook.git/blob - database.c
Ids added
[pkg/abook.git] / database.c
1
2 /*
3  * $Id$
4  *
5  * by JH <jheinonen@bigfoot.com>
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_curses.h"
16 #include "abook.h"
17 #include "database.h"
18 #include "list.h"
19 #include "misc.h"
20 #include "options.h"
21 #include "filter.h"
22 #ifdef HAVE_CONFIG_H
23 #       include "config.h"
24 #endif
25
26 static void     free_item(int i);
27
28
29 list_item *database = NULL;
30
31 int items = 0;
32
33 #define INITIAL_LIST_CAPACITY   30
34
35 int list_capacity = 0;
36
37 extern int first_list_item;
38 extern int curitem;
39 extern char *selected;
40
41 extern char *datafile;
42 extern char *rcfile;
43
44 /*
45  * field definitions
46  */
47
48 #include "edit.h"
49
50 /*
51  * notes about adding fields:
52  *      - do not change any fields in TAB_CONTACT
53  *      - do not add fields to contact tab
54  *      - 6 fields per tab is maximum
55  *      - reorganize the field numbers in database.h
56  */
57
58 struct abook_field abook_fields[ITEM_FIELDS] = {
59         {"Name",        "name",         TAB_CONTACT},/* NAME */
60         {"E-mails",     "email",        TAB_CONTACT},/* EMAIL */
61         {"Address",     "address",      TAB_ADDRESS},/* ADDRESS */
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                 }
114                 free(line);
115         }
116
117         free(line);
118         return 0;
119 }
120
121                 
122
123 int
124 load_database(char *filename)
125 {
126         FILE *in;
127
128         if( database != NULL )
129                 close_database();
130
131         if ( (in = abook_fopen(filename, "r")) == NULL )
132                 return -1;
133         
134         parse_database(in);
135
136         if ( items == 0 )
137                 return 2;
138
139         return 0;
140 }
141
142 int
143 write_database(FILE *out)
144 {
145         int i,j;
146
147         fprintf(out, "# abook addressbook file\n\n");
148         fprintf(out, "[format]\n");
149         fprintf(out, "program=" PACKAGE "\n");
150         fprintf(out, "version=" VERSION "\n");
151         fprintf(out, "\n\n");
152
153         for( i = 0; i < items; i++ ) {
154                 fprintf(out, "[%d]\n", i);
155                 for(j=0; j<ITEM_FIELDS; j++) {
156                         if( database[i][j] != NULL && *database[i][j] )
157                                 fprintf(out, "%s=%s\n",
158                                         abook_fields[j].key, database[i][j]);
159                 }
160                 fputc('\n', out);
161         }
162
163         return 0;
164 }
165
166 int
167 save_database()
168 {
169         FILE *out;
170
171         if( (out = abook_fopen(datafile, "w")) == NULL )
172                 return -1;
173
174         if( items < 1 ) {
175                 fclose(out);
176                 unlink(datafile);
177                 return 1;
178         }
179
180         
181         write_database(out);
182         
183         fclose(out);
184         
185         return 0;
186 }
187
188 static void
189 free_item(int item)
190 {
191         free_list_item(database[item]);
192 }
193
194 void
195 free_list_item(list_item item)
196 {
197         int i;
198
199         for(i=0; i<ITEM_FIELDS; i++)
200                 my_free(item[i]);
201 }
202
203 void
204 close_database()
205 {
206         int i;
207         
208         for(i=0; i < items; i++)
209                 free_item(i);
210
211         free(database);
212         free(selected);
213
214         database = NULL;
215         selected = NULL;
216
217         items = 0;
218         first_list_item = curitem = -1;
219         list_capacity = 0;
220 }
221
222 #define _MAX_FIELD_LEN(X)       (X == EMAIL ? MAX_EMAILSTR_LEN:MAX_FIELD_LEN)
223
224 inline static void
225 validate_item(list_item item)
226 {
227         int i;
228         char *tmp;
229         
230         if(item[EMAIL] == NULL)
231                 item[EMAIL] = strdup("");
232
233         for(i=0; i<ITEM_FIELDS; i++)
234                 if( item[i] && (strlen(item[i]) > _MAX_FIELD_LEN(i) ) ) {
235                         tmp = item[i];
236                         item[i][_MAX_FIELD_LEN(i)-1] = 0;
237                         item[i] = strdup(item[i]);
238                         free(tmp);
239                 }
240 }
241
242
243 static void
244 adjust_list_capacity()
245 {
246         if(list_capacity < 1)
247                 list_capacity = INITIAL_LIST_CAPACITY;
248         else if(items >= list_capacity)
249                 list_capacity *= 2;
250         else if(list_capacity / 2 > items)
251                 list_capacity /= 2;
252         else
253                 return;
254
255         database = abook_realloc(database,
256                         sizeof(list_item) * list_capacity);
257         selected = abook_realloc(selected, list_capacity);
258 }
259
260 int
261 add_item2database(list_item item)
262 {
263         if( item[NAME] == NULL || ! *item[NAME] ) {
264                 free_list_item(item);
265                 return 1;
266         }
267
268         if( ++items > list_capacity)
269                 adjust_list_capacity();
270
271         validate_item(item);
272
273         selected[LAST_ITEM] = 0;
274         itemcpy(database[LAST_ITEM], item);
275
276         return 0;
277 }
278
279 void
280 remove_items()
281 {
282         int i, j;
283
284         if( items < 1 || curitem < 0 )
285                 return;
286
287         statusline_addstr("Remove selected item(s) (Y/n)");
288         switch( getch() ) {
289                 case '\r':
290                 case 'y':
291                 case 'Y': break;
292                 default:
293                           clear_statusline();
294                           return;
295         }
296
297         if( ! selected_items() )
298                 selected[ curitem ] = 1;
299         
300         for( j = LAST_ITEM; j >= 0; j-- ) {
301                 if( selected[j] ) {
302                         free_item(j); /* added for .4 data_s_ */
303                         for( i = j; i < LAST_ITEM; i++ ) {
304                                 itemcpy(database[ i ], database[ i + 1 ]);
305                                 selected[ i ] = selected[ i + 1 ];
306                         }
307                         items--;        
308                 }
309         }
310
311         if( curitem > LAST_ITEM && items > 0 )
312                 curitem = LAST_ITEM;
313
314
315         adjust_list_capacity();
316
317         select_none();
318         clear_statusline();     
319         refresh_list();
320 }
321
322 char *
323 get_surname(char *s)
324 {
325         int i, a;
326         int len = strlen(s);
327         char *name = strdup(s);
328
329         for( a = 0, i = len - 1; i >= 0; i--, a++ ) {
330                 name[a] = s[i];
331                 if(name[a] == ' ')
332                         break;
333         }
334
335         name[ a ] = 0;
336
337         revstr(name);
338
339         return name;
340 }
341
342 static int
343 surnamecmp(const void *i1, const void *i2)
344 {
345         int ret;
346         list_item a,b;
347         char *s1, *s2;
348
349         itemcpy(a, i1);
350         itemcpy(b, i2);
351
352         s1 = get_surname(a[NAME]);
353         s2 = get_surname(b[NAME]);
354
355         if( !(ret = safe_strcmp(s1, s2)) )
356                 ret = safe_strcmp(a[NAME], b[NAME]);
357
358         free(s1);
359         free(s2);
360
361         return ret;
362 }
363
364 static int
365 namecmp(const void *i1, const void *i2)
366 {
367         list_item a, b;
368
369         itemcpy(a, i1);
370         itemcpy(b, i2);
371         
372         return safe_strcmp( a[NAME], b[NAME] );
373 }
374
375 void
376 sort_database()
377 {
378         select_none();
379         
380         qsort((void *)database, items, sizeof(list_item), namecmp);
381
382         refresh_screen();
383 }
384
385 void
386 sort_surname()
387 {
388         select_none();
389
390         qsort((void *)database, items, sizeof(list_item), surnamecmp);
391
392         refresh_screen();
393 }
394
395 void
396 clear_database()
397 {
398
399         statusline_addstr("Clear WHOLE database (y/N)");
400         switch( getch() ) {
401                 case 'y':
402                 case 'Y': break;
403                 default:
404                         clear_statusline();
405                         return;
406         }
407
408         close_database();
409
410         refresh_screen();
411 }
412
413 void
414 find(int next)
415 {
416         int i;
417         static char findstr[81];
418         char tmp[81];
419
420 #ifdef DEBUG
421         fprintf(stderr, "find(): findstr = |%s|\n", findstr);
422 #endif
423         
424         if(next) {
425                 if( !*findstr )
426                         return;
427         } else {
428                 clear_statusline();
429                 statusline_addstr("/");
430                 statusline_getnstr(findstr, 67, 0);
431                 strupper(findstr);
432                 clear_statusline();
433         }
434
435         if(items < 1)
436                 return;
437
438         for( i = (curitem < LAST_ITEM) && next ? curitem+1 : curitem;
439                         i < items; i++ ) {
440                 strcpy(tmp, database[i][NAME]);
441                 if( strstr(strupper(tmp), findstr) != NULL ) {
442                         curitem = i;
443                         refresh_list();
444                         break;
445                 }
446         }
447 }
448
449
450 void
451 print_number_of_items()
452 {
453         char *str = mkstr("     " "|%3d/%3d", selected_items(), items);
454
455         mvaddstr(0, COLS-strlen(str), str);
456
457         free(str);
458 }
459
460 void
461 read_database()
462 {
463         if(items > 0) {
464                 statusline_addstr("Your current data will be lost - Press 'y' to continue");
465                 switch( getch() ) {
466                         case 'y':
467                         case 'Y': break;
468                         default: clear_statusline();
469                                  return;
470                 }
471                 clear_statusline();
472         }
473
474         load_database(datafile);
475         refresh_list();
476 }
477
478
479 void
480 print_database()
481 {
482         FILE *handle;
483         char *command = options_get_str("print_command");
484
485         statusline_addstr("Print addressbook? (y/N)");
486         switch( getch() ) {
487                 case 'y':
488                 case 'Y':
489                         break;
490                 default: clear_statusline(); return;
491         }
492         clear_statusline();
493
494         if( ! *command || (handle = popen(command, "w")) == NULL)
495                 return;
496
497         fexport("text", handle);
498         
499         pclose(handle);
500 }
501