1 /*
2   fetchlist.c
3 
4   $Id: fetchlist.c,v 1.10 2003/01/10 23:11:43 bears Exp $
5 */
6 
7 #if HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10 
11 #include <stdio.h>
12 #include <errno.h>
13 #include <unistd.h>
14 #include "fetchlist.h"
15 #include "configfile.h"
16 #include "log.h"
17 #include "util.h"
18 #include "portable.h"
19 
20 struct Elem
21 {
22     Str name;
23     FetchMode mode;
24 };
25 
26 static struct Fetchlist
27 {
28     struct Elem *elem;
29     int size;
30     int max;
31     Bool dirty;
32 } fetchlist = { NULL, 0, 0, FALSE };
33 
34 static void
clearList(void)35 clearList( void )
36 {
37     fetchlist.size = 0;
38     fetchlist.dirty = FALSE;
39 }
40 
41 static int
compareElem(const void * elem1,const void * elem2)42 compareElem( const void *elem1, const void *elem2 )
43 {
44     const struct Elem* e1 = (const struct Elem*)elem1;
45     const struct Elem* e2 = (const struct Elem*)elem2;
46     return strcmp( e1->name, e2->name );
47 }
48 
49 static struct Elem *
searchElem(const char * name)50 searchElem( const char *name )
51 {
52     int i;
53 
54     for ( i = 0; i < fetchlist.size; ++i )
55         if ( strcmp( name, fetchlist.elem[ i ].name ) == 0 )
56             return &fetchlist.elem[ i ];
57     return NULL;
58 }
59 
60 static void
appGrp(const char * name,FetchMode mode)61 appGrp( const char *name, FetchMode mode )
62 {
63     struct Elem elem;
64 
65     if ( fetchlist.max < fetchlist.size + 1 )
66     {
67         if ( ! ( fetchlist.elem
68                  = realloc( fetchlist.elem,
69                             ( fetchlist.max + 50 )
70                             * sizeof( fetchlist.elem[ 0 ] ) ) ) )
71             Log_fatal( "Could not realloc fetchlist" );
72         fetchlist.max += 50;
73     }
74     Utl_cpyStr( elem.name, name );
75     elem.mode = mode;
76     fetchlist.elem[ fetchlist.size++ ] = elem;
77 }
78 
79 void
Fetchlist_read(void)80 Fetchlist_read( void )
81 {
82     FILE *f;
83     Str file;
84     char *p;
85     FetchMode mode = OVER;
86     Bool valid;
87     int ret;
88     Str line, grp, modeStr;
89 
90     Log_dbg( LOG_DBG_FETCH, "Reading %s", file );
91     snprintf( file, MAXCHAR, "%s/fetchlist", Cfg_spoolDir() );
92     clearList();
93     if ( ! ( f = fopen( file, "r" ) ) )
94     {
95         Log_inf( "No file %s", file );
96         return;
97     }
98     while ( fgets( line, MAXCHAR, f ) )
99     {
100         p = Utl_stripWhiteSpace( line );
101         if ( *p == '#' || *p == '\0' )
102             continue;
103         ret = sscanf( p, MAXCHAR_FMT " " MAXCHAR_FMT, grp, modeStr );
104         valid = TRUE;
105         if ( ret < 1 || ret > 2 )
106             valid = FALSE;
107         else if ( ret >= 2 )
108         {
109             if ( strcmp( modeStr, "full" ) == 0 )
110                 mode = FULL;
111             else if ( strcmp( modeStr, "thread" ) == 0 )
112                 mode = THREAD;
113             else if ( strcmp( modeStr, "over" ) == 0 )
114                 mode = OVER;
115             else
116                 valid = FALSE;
117         }
118         if ( ! valid )
119         {
120             Log_err( "Invalid entry in %s: %s", file, line );
121             continue;
122         }
123         appGrp( grp, mode );
124     }
125     if ( ferror( f ) )
126         Log_err( "Error reading %s: %s", file, strerror( errno ) );
127     if ( fclose( f ) != 0 )
128         Log_err( "Error closing %s; %s", file, strerror( errno ) );
129 }
130 
131 Bool
Fetchlist_write(void)132 Fetchlist_write( void )
133 {
134     int i;
135     FILE *f;
136     Str file, tmpfname;
137     const char *modeStr = "";
138     Bool res;
139 
140     /* Any changes? */
141     if ( ! fetchlist.dirty )
142         return TRUE;
143 
144     qsort( fetchlist.elem, (size_t)fetchlist.size,
145            sizeof( fetchlist.elem[ 0 ] ), compareElem );
146 
147     snprintf( file, MAXCHAR, "%s/fetchlist", Cfg_spoolDir() );
148     snprintf( tmpfname, MAXCHAR, "%s/.#%d.fetchlist",
149               Cfg_spoolDir(), (int) getpid() );
150 
151     if ( ! ( f = fopen( tmpfname, "w" ) ) )
152     {
153         Log_err( "Could not open %s for writing", file );
154         return FALSE;
155     }
156     res = TRUE;
157     for ( i = 0; i < fetchlist.size; ++i )
158     {
159         switch ( fetchlist.elem[ i ].mode )
160         {
161         case FULL:
162             modeStr = "full"; break;
163         case THREAD:
164             modeStr = "thread"; break;
165         case OVER:
166             modeStr = "over"; break;
167         }
168         fprintf( f, "%s %s\n", fetchlist.elem[ i ].name, modeStr );
169         if ( ferror( f ) )
170         {
171             Log_err( "Error writing %s: %s", tmpfname, strerror( errno ) );
172             clearerr( f );
173             res = FALSE;
174         }
175     }
176     if ( fclose( f ) != 0 )
177     {
178         Log_err( "Error closing %s: %s", tmpfname, strerror( errno ) );
179         res = FALSE;
180     }
181 
182     if ( res )
183     {
184         if ( rename( tmpfname, file ) < 0 )
185         {
186             Log_err( "Rename of %s to %s failed: %s", tmpfname, file,
187                      strerror( errno ) );
188             res = FALSE;
189         }
190     }
191 
192     return res;
193 }
194 
195 int
Fetchlist_size(void)196 Fetchlist_size( void )
197 {
198     return fetchlist.size;
199 }
200 
201 Bool
Fetchlist_contains(const char * name,FetchMode * mode)202 Fetchlist_contains( const char *name, FetchMode *mode )
203 {
204     struct Elem *elem = searchElem( name );
205     if ( elem == NULL )
206 	return FALSE;
207     if ( mode != NULL )
208 	*mode = elem->mode;
209     return TRUE;
210 }
211 
212 Bool
Fetchlist_element(const char ** name,FetchMode * mode,int idx)213 Fetchlist_element( const char **name, FetchMode *mode, int idx )
214 {
215     if ( idx < 0 || idx >= fetchlist.size )
216         return FALSE;
217     *name = fetchlist.elem[ idx ].name;
218     *mode = fetchlist.elem[ idx ].mode;
219     return TRUE;
220 }
221 
222 Bool
Fetchlist_add(const char * name,FetchMode mode)223 Fetchlist_add( const char *name, FetchMode mode )
224 {
225     struct Elem *elem = searchElem( name );
226     fetchlist.dirty = TRUE;
227     if ( elem == NULL )
228     {
229         appGrp( name, mode );
230         return TRUE;
231     }
232     Utl_cpyStr( elem->name, name );
233     elem->mode = mode;
234     return FALSE;
235 }
236 
237 Bool
Fetchlist_remove(const char * name)238 Fetchlist_remove( const char *name )
239 {
240     struct Elem *elem = searchElem( name );
241     if ( elem == NULL )
242         return FALSE;
243     fetchlist.dirty = TRUE;
244     *elem = fetchlist.elem[ fetchlist.size - 1 ];
245     --fetchlist.size;
246     return TRUE;
247 }
248