1 /* $Id$
2 Written 1999 by Tobias Ernst and released do the Public Domain.
3 This file is part of NLTOOLS, the nodelist processor of the Husky fidonet
4 software project.
5
6 Main program of the NLTOOLS.
7 */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <assert.h>
12 #include <ctype.h>
13 #include <time.h>
14 #include <errno.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17
18 #include <huskylib/compiler.h>
19
20 #ifdef HAS_IO_H
21 # include <io.h>
22 #endif
23
24 #ifdef HAS_UNISTD_H
25 # include <unistd.h>
26 #endif
27
28 #include <huskylib/dirlayer.h>
29
30 #include <fidoconf/fidoconf.h>
31 #include <huskylib/xstr.h>
32 #include <fidoconf/common.h>
33 #include <huskylib/log.h>
34
35 #ifdef USE_HPTZIP
36 # include <hptzip/hptzip.h>
37 #endif
38
39 #include "nlstring.h"
40 #include "nlfind.h"
41 #include "nldate.h"
42 #include "julian.h"
43 #include "ulc.h"
44 #include "version.h"
45
46 /* store the nldiff command name */
47 static char *differ = NULL;
48
49 /*
50 # if 0
51 */
52 /* This is not needed now because I imported fexist.c from smapi, and the
53 routines in fexist.c are a little more intelligent than this one! */
54 /*
55 static int nl_fexist(char *filename)
56 {
57 FILE *f = fopen(filename, "rb");
58 if (f == NULL)
59 return 0;
60 fclose(f);
61 return 1;
62 }
63
64 #else
65 #define nl_fexist fexist
66 #endif
67 */
68
mk_uncompressdir(char * nldir)69 static char *mk_uncompressdir( char *nldir )
70 {
71 char *upath = NULL;
72 size_t l;
73 FILE *f;
74 l = strlen( nldir ) + 12;
75 /* construct a temporary directory name below nodelist dir */
76 xstrscat( &upath, nldir, "nlupdate.tmp", NULL );
77 adaptcase( upath );
78
79 /* try to create it */
80 mymkdir( upath );
81
82 w_log( LL_DIR, "Use temp dir: '%s'", upath );
83
84 /* check if we can write to the directory */
85 nfree( upath );
86 xscatprintf( &upath, "%s%s%c%s", nldir, "nlupdate.tmp", PATH_DELIM, "nlupdate.flg" );
87 f = fopen( upath, "w" );
88 if( f == NULL )
89 {
90 fprintf( stderr, "%s\n", upath );
91 upath[l] = '\0';
92 w_log( LL_ERROR, "Cannot create/access temporary directory (%s): %s", upath,
93 strerror( errno ) );
94 free( upath );
95 return NULL;
96 }
97 fclose( f );
98 remove( upath );
99 upath[l + 1] = '\0';
100
101 return upath;
102 }
103
destroy_uncompressdir(char * upath)104 int destroy_uncompressdir( char *upath )
105 {
106 size_t l = strlen( upath );
107 char *dfile;
108 husky_DIR *hdir;
109
110 hdir = husky_opendir( upath );
111 if( hdir == NULL )
112 {
113 w_log( LL_ERROR, "Cannot read temporary directory (%s): %s", upath, strerror( errno ) );
114 free( upath );
115 return 0;
116 }
117
118 while( ( dfile = husky_readdir( hdir ) ) != NULL )
119 {
120 if( ( strcmp( dfile, "." ) ) && ( strcmp( dfile, ".." ) ) )
121 {
122 strcpy( upath + l, dfile );
123 /* there is room for this because of the
124 addition of FILENAME_MAX in mk_uncompressdir */
125
126 if( remove( upath ) )
127 {
128 w_log( LL_ERROR, "Cannot delete file '%s': %s", upath, strerror( errno ) );
129 }
130 else
131 w_log( LL_DEL, "File '%s' removed", upath );
132 }
133 }
134 husky_closedir( hdir );
135
136 upath[l - 1] = '\0';
137
138 if( rmdir( upath ) )
139 {
140 w_log( LL_ERROR, "Cannot remove temporary directory '%s': %s", upath, strerror( errno ) );
141 free( upath );
142 return 0;
143 }
144
145 free( upath );
146 return 1;
147 }
148
uncompress(s_fidoconfig * config,char * directory,char * filename,char * tempdir)149 static int uncompress( s_fidoconfig * config, char *directory, char *filename, char *tempdir )
150 {
151 char *tmpfilename = NULL;
152 char cmd[256];
153 FILE *f;
154 int j, found, exitcode;
155 UINT i;
156
157 xstrscat( &tmpfilename, directory, filename, NULL );
158
159 /* open the packet so we can see what archiver has been used */
160 f = fopen( tmpfilename, "rb" );
161 if( f == NULL )
162 {
163 w_log( LL_ERROR, "Cannot access %s: %s", tmpfilename, strerror( errno ) );
164 return 0;
165 }
166
167 /* find what unpacker to use */
168 for( i = 0, found = 0; ( i < config->unpackCount ) && !found; i++ )
169 {
170 fseek( f, config->unpack[i].offset, config->unpack[i].offset >= 0 ? SEEK_SET : SEEK_END );
171 if( ferror( f ) )
172 {
173 w_log( LL_ERROR, "Seek error on %s: %s", tmpfilename, strerror( errno ) );
174 break;
175 }
176 for( found = 1, j = 0; j < config->unpack[i].codeSize; j++ )
177 {
178 if( ( getc( f ) & config->unpack[i].mask[j] ) != config->unpack[i].matchCode[j] )
179 found = 0;
180 }
181 if( found )
182 break;
183 }
184
185 fclose( f );
186
187 if( !found )
188 {
189 w_log( LL_ERROR, "Did not find an appropriate unpacker for %s", tmpfilename );
190 nfree( tmpfilename );
191 return 0;
192 }
193
194 /* create the unpack command */
195 /* unpack_command(cmd,config->unpack[i].call, directory, filename, tempdir); */
196 fillCmdStatement( cmd, config->unpack[i].call, tmpfilename, "", tempdir );
197
198 /* execute the unpacker */
199 w_log( LL_EXEC, "Executing '%s'", cmd );
200
201 if( fc_stristr( config->unpack[i].call, ZIPINTERNAL ) )
202 {
203 exitcode = 1;
204 #ifdef USE_HPTZIP
205 exitcode = UnPackWithZlib( tmpfilename, NULL, tempdir );
206 #endif
207 }
208 else
209 {
210 exitcode = cmdcall( cmd );
211 }
212
213 if( exitcode != 0 )
214 {
215 w_log( LL_EXEC, "Unpacker returned exit code '%d'", exitcode );
216 nfree( tmpfilename );
217 return 0;
218 }
219 adaptcase_refresh_dir( tempdir );
220
221 nfree( tmpfilename );
222 return 1;
223 }
224
get_uncompressed_filename(s_fidoconfig * config,char * directory,char * filename,char * tempdir,int expday,int * reason)225 static char *get_uncompressed_filename( s_fidoconfig * config,
226 char *directory, char *filename,
227 char *tempdir, int expday, int *reason )
228 {
229 size_t l = strlen( filename );
230 char *rv;
231
232 w_log( LL_FUNC, "get_uncompressed_filename()" );
233
234 if( reason )
235 {
236 *reason = 0; /* means: all sorts of errors */
237 }
238
239 assert( l >= 4 ); /* a match should never happen w/o ".???" at the end */
240
241 if( isdigit( filename[l - 3] ) && expday == atoi( filename + l - 3 ) )
242 {
243 /* the file is not compressed and the day number in the file
244 name matches the expected day number */
245
246 w_log( LL_DEBUG, "File '%s' is not compressed", filename );
247
248 if( ( rv = malloc( strlen( directory ) + l + 1 ) ) == NULL )
249 {
250 w_log( LL_ERROR, "Out of memory" );
251 w_log( LL_FUNC, "get_uncompressed_filename() failed" );
252 return NULL;
253 }
254 strcpy( rv, directory );
255 strcat( rv, filename );
256 w_log( LL_FUNC, "get_uncompressed_filename() OK" );
257 return rv;
258 }
259 else if( expday % 100 == atoi( filename + l - 2 ) )
260 {
261 /* the file is compressed and its name looks like it could match */
262
263 w_log( LL_DEBUG, "File '%s' is compressed", filename );
264
265 if( !uncompress( config, directory, filename, tempdir ) )
266 {
267 w_log( LL_DEBUG, "Uncompress failed" );
268 return NULL;
269 }
270
271 if( ( rv = malloc( strlen( tempdir ) + l + 1 ) ) == NULL )
272 {
273 w_log( LL_ERROR, "Out of memory" );
274 w_log( LL_FUNC, "get_uncompressed_filename() failed" );
275 return NULL;
276 }
277 strcpy( rv, tempdir );
278 strcat( rv, filename );
279 sprintf( rv + strlen( tempdir ) + l - 3, "%03d", expday % 1000 );
280
281 adaptcase( rv );
282 w_log( LL_DEBUG, "Expected uncompressed filename after adaptcase(): %s", rv );
283 if( access( rv, F_OK | R_OK ) )
284 {
285 w_log( LL_WARN, "Uncompressed file '%s' does not exist", rv );
286 free( rv );
287 w_log( LL_FUNC, "get_uncompressed_filename() failed" );
288 return NULL;
289 }
290 w_log( LL_FUNC, "get_uncompressed_filename() OK" );
291 return rv;
292 }
293 else
294 {
295 /* no match */
296
297 w_log( LL_DEBUG, "File suffix %d does not match exp day %d", atoi( filename + l - 2 ), expday );
298
299 if( reason )
300 {
301 *reason = 1; /* means: file extension simply doesn't match */
302 }
303
304 w_log( LL_FUNC, "get_uncompressed_filename() failed" );
305 return NULL;
306 }
307 }
308
basedir(const char * stemname)309 static char *basedir( const char *stemname )
310 {
311 char *rv;
312 size_t l;
313 int j;
314
315 l = strlen( stemname );
316
317 if( ( rv = malloc( l + 1 ) ) == NULL )
318 {
319 return NULL;
320 }
321 strcpy( rv, stemname );
322
323 for( j = l - 1; j >= 0 && rv[j] != '/' && rv[j] != '\\' && rv[j] != ':'; j-- )
324 {
325 rv[j] = '\0';
326 }
327
328 return rv;
329 }
330
call_differ(char * nodelist,char * nodediff)331 static int call_differ( char *nodelist, char *nodediff )
332 {
333 char *cmd = NULL;
334 int rv;
335
336 xscatprintf( &cmd, "%s -n %s %s", differ, nodelist, nodediff );
337 w_log( LL_EXEC, "Executing diff processor (%s)", cmd );
338 rv = system( cmd );
339 nfree( cmd );
340 if( rv != 0 )
341 {
342 w_log( LL_ERROR, "Diff processor returned exit code '%d'", rv );
343 return 0;
344 }
345 return 1;
346 }
347
348
349
350 /* Purpose of try_full_update:
351
352 fullbase + match might contain a full update for rawnl, with julian
353 being the julian day number that we expect to find in fullbase + match.
354 try to unpack the update and see if it really contains this day number, and
355 if so, use it.
356
357 Return values: 0: no hit, no error
358 1: fatal error
359 2: hit, but non-fatal error
360 3: hit
361
362 */
363
try_full_update(s_fidoconfig * config,char * rawnl,char * fullbase,nlist * fulllist,int j,char * tmpdir,long julian,int nl)364 static int try_full_update( s_fidoconfig * config, char *rawnl, char *fullbase,
365 nlist * fulllist, int j, char *tmpdir, long julian, int nl )
366 {
367 char *ufn;
368 char *newfn;
369 int ndnr;
370 int rv = 0;
371 int reason;
372
373 w_log( LL_FUNC, "try_full_update()" );
374
375 if( fulllist->julians[j] == -1 )
376 {
377 w_log( LL_FUNC, "try_full_update() failed (fulllist->julians[j]==-1)" );
378 return 0; /* this full update has an unknown date number */
379 }
380
381 decode_julian_date( julian, NULL, NULL, NULL, &ndnr );
382
383 ufn = get_uncompressed_filename( config, fullbase, fulllist->matches[j], tmpdir, ndnr, &reason );
384
385 w_log( LL_FILENAME, "Uncomressed filename: %s", ufn );
386
387 if( !reason )
388 fulllist->julians[j] = -1;
389
390 if( ufn != NULL )
391 {
392 if( julian == ( fulllist->julians[j] = parse_nodelist_date( ufn ) ) )
393 {
394 w_log( LL_INFO, "Found full update: %s", ufn );
395
396 newfn = malloc( strlen( config->nodelistDir ) +
397 strlen( config->nodelists[nl].nodelistName ) + 5 );
398
399 if( newfn == NULL )
400 {
401 w_log( LL_ERROR, "Out of memory" );
402 w_log( LL_FUNC, "try_full_update() failed" );
403 return 1;
404 }
405 else
406 {
407 strcpy( newfn, config->nodelistDir );
408 strcat( newfn, config->nodelists[nl].nodelistName );
409 sprintf( newfn + strlen( newfn ), ".%03d", ndnr );
410
411 w_log( LL_FILE, "Copy '%s' to '%s'", ufn, newfn );
412
413 if( copy_file( ufn, newfn, 1 ) )
414 {
415 w_log( LL_ERROR, "Error copying '%s' to '%s'", ufn, newfn );
416 w_log( LL_FUNC, "try_full_update() failed" );
417 return 1;
418 }
419 else
420 {
421 rv = 3;
422 if( rawnl != NULL )
423 {
424 w_log( LL_FILE, "Replacing '%s' with '%s'", rawnl, newfn );
425
426 if( remove( rawnl ) )
427 {
428 w_log( LL_ERROR, "Cannot remove '%s'", rawnl );
429 rv = 2; /* error, but still proceed */
430 }
431 }
432 else
433 {
434 w_log( LL_CREAT, "Creating '%s'", newfn );
435 }
436
437 }
438 free( newfn );
439 }
440 }
441 else
442 {
443 w_log( LL_ALERT, "%s does not contain nodelist for day %03d", ufn, ndnr );
444 }
445
446 free( ufn );
447 }
448 w_log( LL_FUNC, "try_full_update() OK" );
449 return rv;
450 }
451
452
453
454 /* Do_update finds an update for a already existing unpacked nodelist. it first
455 parses the day number of the existing nodelist instance, then increases it
456 by steps of 7, and looks for matching diff or full updates. If there is no
457 existing instance of the nodelst, you must use create_instance instead.
458
459 today is passed as argument, because we only want to call julian_today()
460 once. Imagine nlupdate being started at 23:59:59 .... */
461
do_update(s_fidoconfig * config,int nl,char * rawnl,long today,char * tmpdir)462 static int do_update( s_fidoconfig * config, int nl, char *rawnl, long today, char *tmpdir )
463 {
464 long julian, i;
465 int ndnr;
466 int checkdiff = 1;
467 int j = 0;
468 int rv = 1;
469 char *diffbase = NULL, *fullbase = NULL;
470 nlist *difflist = NULL, *fulllist = NULL;
471 int hit;
472 char *ufn = NULL, *cfn = NULL;
473
474 w_log( LL_FUNC, "do_update()" );
475
476 if( ( julian = parse_nodelist_date( rawnl ) ) == -1 )
477 return 0;
478
479 if( today - julian < 7 )
480 {
481 w_log( LL_INFO, "%s younger than 7 days, no update necessary", rawnl );
482 return 1;
483 }
484
485 /* build the list of candidate nodediff files */
486 if( config->nodelists[nl].diffUpdateStem != NULL )
487 {
488 if( ( diffbase = basedir( config->nodelists[nl].diffUpdateStem ) ) != NULL )
489 {
490 difflist = find_nodelistfiles( diffbase,
491 config->nodelists[nl].diffUpdateStem + strlen( diffbase ), 1 );
492 }
493 else
494 difflist = NULL;
495 }
496
497 /* build the list of candidate full update files */
498 if( config->nodelists[nl].fullUpdateStem )
499 {
500 if( ( fullbase = basedir( config->nodelists[nl].fullUpdateStem ) ) != NULL )
501 {
502 fulllist = find_nodelistfiles( fullbase,
503 config->nodelists[nl].fullUpdateStem + strlen( fullbase ), 1 );
504 }
505 else
506 fullbase = NULL;
507 }
508
509 if( !fulllist && !difflist )
510 {
511 if( config->nodelists[nl].fullUpdateStem )
512 {
513 if( !access( config->nodelists[nl].fullUpdateStem, F_OK ) )
514 {
515 w_log( LL_INFO, "Full update '%s' not an FTS5000 compatible",
516 config->nodelists[nl].fullUpdateStem );
517 xstrscat( &ufn, config->nodelistDir, config->nodelists[nl].nodelistName, NULL );
518
519 w_log( LL_FILE, "Copy '%s' to '%s'", config->nodelists[nl].fullUpdateStem, ufn );
520
521 if( copy_file( config->nodelists[nl].fullUpdateStem, ufn, 1 ) )
522 {
523 w_log( LL_ERROR, "Error copying '%s' to '%s'", config->nodelists[nl].fullUpdateStem,
524 ufn );
525 w_log( LL_FUNC, "do_update() failed" );
526 nfree( ufn );
527 return 0;
528 }
529 else
530 {
531 w_log( LL_FUNC, "do_update() OK" );
532 nfree( ufn );
533 return 1;
534 }
535 }
536 else
537 {
538 w_log( LL_INFO, "Full update '%s' not found", config->nodelists[nl].fullUpdateStem );
539 w_log( LL_FUNC, "do_update() failed" );
540 return 0;
541 }
542 }
543 }
544
545 /* search for diffs or full updates */
546
547 for( i = julian + 7; i <= today && rawnl; i += 7 )
548 {
549 decode_julian_date( i, NULL, NULL, NULL, &ndnr );
550 hit = 0;
551
552 if( checkdiff && difflist != NULL ) /* search for diffs */
553 {
554 for( j = 0; j < difflist->n && !hit; j++ )
555 {
556
557 w_log( LL_DEBUG, "Checking %s%s", diffbase, difflist->matches[j] );
558
559 ufn = get_uncompressed_filename( config, diffbase,
560 difflist->matches[j], tmpdir, ndnr, NULL );
561
562 w_log( LL_DEBUG, "ufn is: %s", ufn );
563
564 if( ufn != NULL )
565 {
566
567 /* nodediffs contain the header of the nodelist to which
568 they apply as the first line, so we need to compare the
569 parsed date with julian, not with i */
570
571 if( julian == parse_nodelist_date( ufn ) )
572 {
573 w_log( LL_INFO, "Found diff update: %s", ufn );
574
575 if( call_differ( rawnl, ufn ) )
576 {
577 hit = 1;
578 difflist->applied[j] = 1;
579 }
580 }
581 else
582 {
583 w_log( LL_INFO, "%s is not applicable to %s", rawnl, ufn );
584 }
585 free( ufn );
586 }
587 }
588 }
589
590 if( !hit )
591 {
592 if( ( fulllist == NULL ) && config->nodelists[nl].fullUpdateStem )
593 {
594 /* non-standard full update nodelist file */
595
596 #if 0
597 if( add_match( fulllist, config->nodelists[nl].fullUpdateStem ) )
598 {
599 switch ( try_full_update( config, rawnl, fullbase, fulllist, j, tmpdir, i, nl ) )
600 {
601 case 0: /* no hit, no error */
602 break;
603 case 1:
604 i = today; /* fatal error; stop searching! */
605 rv = 0;
606 break;
607 case 2:
608 rv = 0; /* hit, minor error, but don't exit loop! */
609 hit = 1;
610 break;
611 case 3:
612 hit = 1; /* hit, no errors; */
613 break;
614 }
615 }
616 #endif
617 }
618 else if( fulllist != NULL )
619 { /* search for fulls */
620 for( j = 0; j < fulllist->n && !hit; j++ )
621 {
622
623 w_log( LL_DEBUG, "Checking %s%s", fullbase, fulllist->matches[j] );
624
625 switch ( try_full_update( config, rawnl, fullbase, fulllist, j, tmpdir, i, nl ) )
626 {
627 case 0: /* no hit, no error */
628 break;
629 case 1:
630 i = today; /* fatal error; stop searching! */
631 rv = 0;
632 break;
633 case 2:
634 rv = 0; /* hit, minor error, but don't exit loop! */
635 hit = 1;
636 break;
637 case 3:
638 hit = 1; /* hit, no errors; */
639 break;
640 }
641 }
642 }
643 }
644
645 if( hit ) /* analyse the new nodelist */
646 {
647 if( rawnl != NULL )
648 free( rawnl );
649 rawnl = findNodelist( config, nl );
650 if( rawnl == NULL )
651 {
652 w_log( LL_ERROR,
653 "Instance of nodelist %s vanished during update!?",
654 config->nodelists[nl].nodelistName );
655 }
656 else if( ( i = parse_nodelist_date( rawnl ) ) == -1 )
657 {
658 free( rawnl );
659 rawnl = NULL;
660 }
661 else if( i <= julian )
662 {
663 w_log( LL_ERROR, "Nodelist %s still as old as before!?", rawnl );
664 rv = 0;
665 i = today; /* exit loop :-( */
666 }
667 else
668 {
669 julian = i;
670 }
671 checkdiff = 1;
672 }
673 else
674 {
675 checkdiff = 0;
676 }
677 }
678
679 if( config->nodelists[nl].delAppliedDiff )
680 for( j = 0; j < difflist->n; j++ )
681 if( difflist->applied[j] )
682 {
683 xstrscat( &cfn, diffbase, difflist->matches[j], NULL );
684 if( remove( cfn ) )
685 {
686 w_log( LL_ERROR, "Cannot delete file '%s': %s", cfn, strerror( errno ) );
687 }
688 else
689 w_log( LL_DEL, "File '%s' removed", cfn );
690 nfree( cfn );
691 }
692
693 if( fullbase != NULL )
694 free( fullbase );
695 if( diffbase != NULL )
696 free( diffbase );
697 free_nlist( fulllist );
698 fulllist = NULL;
699
700 /* check if nodelist is extraordinarily old, just for information */
701 if( today - julian > 14 )
702 {
703 w_log( LL_ALERT, "%s is more than two weeks old, but still no way to update it", rawnl );
704 }
705
706 free( rawnl );
707 return rv;
708 }
709
710 /* Create_instance is used instead of do_instance if there is not yet any
711 unpacked instance of the nodelist in the nodelist directory. In this case,
712 of course, diff updates are useless, and we only search for full
713 updates. Create_instance will start at the current day number, and then
714 decrease it in steps of 1 until it finds a matching full update.
715
716 today is passed as argument, because we only want to call julian_today()
717 once. Imagine nlupdate being started at 23:59:59 .... */
718
create_instance(s_fidoconfig * config,int nl,long today,char * tmpdir)719 static int create_instance( s_fidoconfig * config, int nl, long today, char *tmpdir )
720 {
721 long i;
722 int j;
723 int rv = 0;
724 char *fullbase = NULL;
725 nlist *fulllist = NULL;
726 int hit;
727
728 /* build the list of candidate full update files */
729
730 w_log( LL_FUNC, "create_instance()" );
731
732 if( config->nodelists[nl].fullUpdateStem )
733 {
734 if( ( fullbase = basedir( config->nodelists[nl].fullUpdateStem ) ) != NULL )
735 {
736 fulllist = find_nodelistfiles( fullbase,
737 config->nodelists[nl].fullUpdateStem + strlen( fullbase ), 1 );
738 }
739 else
740 fullbase = NULL;
741 }
742 else
743 {
744 w_log( LL_ERR, "fullupdate not present" );
745 w_log( LL_FUNC, "create_instance() failed" );
746 return 0;
747 }
748
749 if( fulllist == NULL )
750 { /* non-standard full update nodelist file */
751 if( !access( config->nodelists[nl].fullUpdateStem, F_OK ) )
752 {
753 w_log( LL_INFO, "Full update '%s' not an FTS5000 compatible",
754 config->nodelists[nl].fullUpdateStem );
755 nfree( fullbase );
756 xstrscat( &fullbase, config->nodelistDir, config->nodelists[nl].nodelistName, NULL );
757
758 w_log( LL_FILE, "Copy '%s' to '%s'", config->nodelists[nl].fullUpdateStem, fullbase );
759
760 if( copy_file( config->nodelists[nl].fullUpdateStem, fullbase, 1 ) )
761 {
762 w_log( LL_ERROR, "Error copying '%s' to '%s'", config->nodelists[nl].fullUpdateStem,
763 fullbase );
764 w_log( LL_FUNC, "create_instance() failed" );
765 nfree( fullbase );
766 return 0;
767 }
768
769 nfree( fullbase );
770 w_log( LL_FUNC, "create_instance() OK" );
771 return 1;
772 }
773 else
774 {
775 w_log( LL_INFO, "Full update '%s' not found", config->nodelists[nl].fullUpdateStem );
776 w_log( LL_FUNC, "create_instance() failed" );
777 return 0;
778 }
779 }
780
781 /* search for diffs or full updates */
782 hit = 0;
783 for( i = today; i > today - 10 * 366 && !rv; i-- )
784 {
785 if( ( fulllist != NULL ) && ( !hit ) )
786 { /* search for fulls */
787 for( j = 0; j < fulllist->n && !hit; j++ )
788 {
789 switch ( try_full_update( config, NULL, fullbase, fulllist, j, tmpdir, i, nl ) )
790 {
791 case 0: /* no hit, no error */
792 break;
793 case 1:
794 i = today - 10 * 366; /* fatal error; stop searching! */
795 rv = 0;
796 break;
797 case 2:
798 case 3:
799 rv = 1; /* hit */
800 break;
801 }
802 }
803 }
804 }
805
806 if( fullbase != NULL )
807 free( fullbase );
808
809 w_log( LL_FUNC, "create_instance() rc=%d", rv );
810 return rv;
811 }
812
813
process(s_fidoconfig * config)814 static int process( s_fidoconfig * config )
815 {
816 UINT i;
817 int rv = 0;
818 char *nodelist;
819 char *tmpdir;
820 long today = julian_today( );
821
822 if( config->nodelistDir == NULL )
823 {
824 w_log( LL_CRIT, "No nodelist directory configured in fidoconfig" );
825 return 8;
826 }
827
828 if( config->nodelistCount < 1 )
829 {
830 w_log( LL_CRIT, "No nodelist configured in fidoconfig" );
831 return 8;
832 }
833
834 if( ( tmpdir = mk_uncompressdir( config->nodelistDir ) ) != NULL )
835 {
836 for( i = 0; i < config->nodelistCount; i++ )
837 {
838 nodelist = findNodelist( config, i );
839
840 if( nodelist == NULL )
841 {
842 w_log( LL_ALERT, "No instance of nodelist %s found.", config->nodelists[i].nodelistName );
843
844 /* New: If there is no instance, we can still try
845 to find a full update! */
846
847 if( config->nodelists[i].fullUpdateStem != NULL )
848 {
849 w_log( LL_INFO, "Check fullupdate: %s", config->nodelists[i].fullUpdateStem );
850 if( !create_instance( config, i, today, tmpdir ) )
851 {
852 w_log( LL_ALERT,
853 "no full update for nodelist %s found.", config->nodelists[i].nodelistName );
854 if( rv < 4 )
855 rv = 4;
856 }
857 else
858 {
859 nodelist = findNodelist( config, i );
860 if( nodelist == NULL )
861 {
862 w_log( LL_ERROR, "Unpacked full update, but "
863 "still no instance of nodelist %s !?", config->nodelists[i].nodelistName );
864 if( rv < 8 )
865 rv = 8;
866 }
867 }
868 }
869 else
870 {
871 w_log( LL_ALERT, "Don't know how to create instance"
872 " of nodelist %s (no full update configured)", config->nodelists[i].nodelistName );
873 if( rv < 4 )
874 rv = 4;
875 }
876 }
877
878 if( nodelist != NULL )
879 {
880 if( config->nodelists[i].diffUpdateStem == NULL &&
881 config->nodelists[i].fullUpdateStem == NULL )
882 {
883 w_log( LL_ALERT, "Don't know how to update "
884 "nodelist %s", config->nodelists[i].nodelistName );
885 if( rv < 4 )
886 rv = 4;
887 free( nodelist );
888 }
889 else
890 {
891 w_log( LL_INFO, "Trying to update %s", nodelist );
892
893 if( !do_update( config, i, nodelist, today, tmpdir ) )
894 {
895 if( rv < 8 )
896 rv = 8;
897 }
898 }
899 }
900 }
901
902 destroy_uncompressdir( tmpdir );
903 }
904
905 w_log( LL_FUNC, "%s::process() done", __FILE__ );
906 return rv;
907 }
908
usage()909 void usage()
910 {
911 printf(
912 "USAGE:\n"
913 "\tnlupdate [-h] [-v] [-q] [-c config]\n"
914 "where:\n"
915 "\t-h\t - print this help and exit;\n"
916 "\t-v\t - print version information and exit;\n"
917 "\t-q\t - quiet mode (supress normal output, print errors only).\n"
918 "\t-c config - read configuration from alternate fidoconfig file.\n"
919 );
920 }
921
main(int argc,char ** argv)922 int main( int argc, char **argv )
923 {
924 s_fidoconfig *config;
925 int rv;
926 int l = 0, i, flag_quiet = 0;
927 char *versionStr, *configfile = NULL;
928
929 versionStr = GenVersionStr( "nlupdate", VER_MAJOR, VER_MINOR, VER_PATCH, VER_BRANCH, cvs_date );
930
931 for( i = 1; i < argc; i++ )
932 {
933 int j, plen;
934 if( argv[i][0] == '-' )
935 {
936 int plen = sstrlen( argv[i] );
937 for( j = 1; j < plen; j++ )
938 switch ( argv[i][j] )
939 {
940 case 'h':
941 usage( );
942 return 0;
943 case 'v':
944 printversion( );
945 return 0;
946 case 'c':
947 if( plen > ++j )
948 { configfile = argv[i] + j;
949 j=plen;
950 }else if( argc > ++i )
951 configfile = argv[i];
952 else
953 {
954 w_log( LL_ERR, "Fatal: parameter after \"-c\" is required\n" );
955 return 1;
956 }
957 break;
958 case 'q':
959 flag_quiet = 1;
960 }
961 }
962 }
963
964 if( !flag_quiet )
965 printversion( );
966
967 config = readConfig( configfile );
968
969 /* construct the name of the nldiff command */
970 if( argc )
971 {
972 l = strlen( argv[0] );
973 if( l )
974 {
975 for( l--; l >= 0 && argv[0][l] != '/' && argv[0][l] != '\\' && argv[0][l] != ':'; l-- );
976
977 l++;
978 }
979 }
980 differ = malloc( l + 7 );
981 if( l )
982 memcpy( differ, argv[0], l );
983 strcpy( differ + l, "nldiff" );
984
985 /* run the main program */
986 if( config != NULL )
987 {
988 {
989 char errloglevels[3] = { LL_ERR, LL_CRIT, '\0' };
990 initLog( config->logFileDir, config->logEchoToScreen, config->loglevels,
991 flag_quiet? errloglevels : config->screenloglevels );
992 }
993 openLog( LOGNAME, versionStr );
994 w_log( LL_START, "Start" );
995
996 rv = process( config );
997
998 w_log( LL_STOP, "End" );
999 closeLog( );
1000 disposeConfig( config );
1001 free( differ );
1002 return rv;
1003
1004 }
1005 else
1006 {
1007 w_log( LL_ERR, "Fatal: Cannot open fidoconfig.\n" );
1008 free( differ );
1009 return 8;
1010 }
1011 }
1012