Mercurial > noffle
comparison src/noffle.c @ 43:2842f50feb55 noffle
[svn] * client.c, client.h, common.h, config.c, config.h, content.c, content.h,
control.c, control.h, database.c, database.h, dynamicstring.c,
dynamicstring.h, fetch.c, fetch.h, fetchlist.c, fetchlist.h, group.c,
group.h, itemlist.c, itemlist.h, lock.c, lock.h, log.c, log.h, noffle.c,
online.c, online.h, outgoing.c, outgoing.h, over.c, over.h, post.c, post.h,
protocol.c, protocol.h, pseudo.c, pseudo.h, request.c, request.h, server.c,
server.h, util.c, util.h, wildmat.c, wildmat.h: Moved files to the
subdirectory src/
* Makefile.am, acconfig.h, configure.in, docs/Makefile.am, src/Makefile.am,
Makefile.in, aclocal.m4, config.h.in, configure, install-sh, missing,
mkinstalldirs, stamp-h.in, docs/Makefile.in, src/Makefile.in: Added files.
They are used by aclocal, autoheader, autoconf and automake.
* src/config.c, src/config.h: Renamed to configfile.c and configfile.h,
because configure will generate a config.h file itself.
* src/client.c, src/content.c, src/database.c, src/fetch.c, src/fetchlist.c,
src/group.c, src/lock.c, src/noffle.c, src/online.c, src/outgoing.c,
src/over.c, src/pseudo.c, src/request.c, src/server.c, src/util.c:
Changed '#include "config.h"' to '#include "configfile.h"'.
* src/client.c, src/content.c, src/database.c, src/fetch.c, src/fetchlist.c,
src/group.c, src/lock.c, src/online.c, src/outgoing.c, src/post.c,
src/protocol.c, src/request.c, src/server.c: Files now #include <config.h>.
Added missing <stdio.h>. This removes the warnings about snprintf() not
being declared.
* Makefile: Removed. This is now generated by configure.
author | uh1763 |
---|---|
date | Fri, 05 May 2000 22:45:56 +0100 |
parents | |
children | 32ba1198c6fa |
comparison
equal
deleted
inserted
replaced
42:2467ff423c15 | 43:2842f50feb55 |
---|---|
1 /* | |
2 noffle.c | |
3 | |
4 Main program. Implements specified actions, but running as server, which | |
5 is done by Serv_run(), declared in server.h. | |
6 | |
7 Locking policy: lock access to databases while noffle is running, but | |
8 not as server. If noffle runs as server, locking is performed while | |
9 executing NNTP commands, but temporarily released if no new command is | |
10 received for some seconds (to allow multiple clients connect at the same | |
11 time). | |
12 | |
13 $Id: noffle.c 49 2000-05-05 21:45:56Z uh1763 $ | |
14 */ | |
15 | |
16 #include <ctype.h> | |
17 #include <errno.h> | |
18 #include <getopt.h> | |
19 #include <signal.h> | |
20 #include <sys/resource.h> | |
21 #include <syslog.h> | |
22 #include <unistd.h> | |
23 #include "client.h" | |
24 #include "common.h" | |
25 #include "content.h" | |
26 #include "control.h" | |
27 #include "configfile.h" | |
28 #include "database.h" | |
29 #include "fetch.h" | |
30 #include "fetchlist.h" | |
31 #include "group.h" | |
32 #include "itemlist.h" | |
33 #include "log.h" | |
34 #include "online.h" | |
35 #include "outgoing.h" | |
36 #include "over.h" | |
37 #include "pseudo.h" | |
38 #include "util.h" | |
39 #include "server.h" | |
40 #include "request.h" | |
41 #include "lock.h" | |
42 | |
43 struct Noffle | |
44 { | |
45 Bool queryGrps; | |
46 Bool queryDsc; | |
47 Bool queryTimes; | |
48 Bool interactive; | |
49 } noffle = { FALSE, FALSE, FALSE, TRUE }; | |
50 | |
51 static void | |
52 doArt( const char *msgId ) | |
53 { | |
54 const char *id; | |
55 | |
56 if ( strcmp( msgId, "all" ) == 0 ) | |
57 { | |
58 if ( ! Db_first( &id ) ) | |
59 fprintf( stderr, "Database empty.\n" ); | |
60 else | |
61 do | |
62 { | |
63 printf( "From %s %s\n" | |
64 "%s\n" | |
65 "%s\n", | |
66 Db_from( id ), Db_date( id ), | |
67 Db_header( id ), | |
68 Db_body( id ) ); | |
69 } | |
70 while ( Db_next( &id ) ); | |
71 } | |
72 else | |
73 { | |
74 if ( ! Db_contains( msgId ) ) | |
75 fprintf( stderr, "Not in database.\n" ); | |
76 else | |
77 printf( "%s\n%s", Db_header( msgId ), Db_body( msgId ) ); | |
78 } | |
79 } | |
80 | |
81 static void | |
82 doCancel( const char *msgId ) | |
83 { | |
84 switch( Ctrl_cancel( msgId ) ) | |
85 { | |
86 case CANCEL_NO_SUCH_MSG: | |
87 printf( "No such message '%s'.\n", msgId ); | |
88 break; | |
89 | |
90 case CANCEL_OK: | |
91 printf( "Message '%s' cancelled.\n", msgId ); | |
92 break; | |
93 | |
94 case CANCEL_NEEDS_MSG: | |
95 printf( "Message '%s' cancelled in local database only.\n", msgId ); | |
96 break; | |
97 } | |
98 } | |
99 | |
100 /* List articles requested from one particular server */ | |
101 static void | |
102 listRequested1( const char* serv ) | |
103 { | |
104 Str msgid; | |
105 | |
106 if ( ! Req_first( serv, msgid ) ) | |
107 return; | |
108 do | |
109 printf( "%s %s\n", msgid, serv ); | |
110 while ( Req_next( msgid ) ); | |
111 } | |
112 | |
113 /* List requested articles. List for all servers if serv = "all" or serv = | |
114 NULL. */ | |
115 void | |
116 doRequested( const char *arg ) | |
117 { | |
118 Str serv; | |
119 | |
120 if ( ! arg || ! strcmp( arg, "all" ) ) | |
121 { | |
122 Cfg_beginServEnum(); | |
123 while ( Cfg_nextServ( serv ) ) | |
124 listRequested1( serv ); | |
125 } | |
126 else | |
127 listRequested1( arg ); | |
128 } | |
129 | |
130 | |
131 static void | |
132 doDb( void ) | |
133 { | |
134 const char *msgId; | |
135 | |
136 if ( ! Db_first( &msgId ) ) | |
137 fprintf( stderr, "Database empty.\n" ); | |
138 else | |
139 do | |
140 printf( "%s\n", msgId ); | |
141 while ( Db_next( &msgId ) ); | |
142 } | |
143 | |
144 static void | |
145 doFetch( void ) | |
146 { | |
147 Str serv; | |
148 | |
149 Cfg_beginServEnum(); | |
150 while ( Cfg_nextServ( serv ) ) | |
151 if ( Fetch_init( serv ) ) | |
152 { | |
153 Fetch_postArts(); | |
154 | |
155 Fetch_getNewGrps(); | |
156 | |
157 /* Get overviews of new articles and store IDs of new articles | |
158 that are to be fetched becase of FULL or THREAD mode in the | |
159 request database. */ | |
160 Fetch_updateGrps(); | |
161 | |
162 /* get requested articles */ | |
163 Fetch_getReq_(); | |
164 | |
165 Fetch_close(); | |
166 } | |
167 } | |
168 | |
169 static void | |
170 doQuery( void ) | |
171 { | |
172 Str serv; | |
173 | |
174 Cfg_beginServEnum(); | |
175 while ( Cfg_nextServ( serv ) ) | |
176 if ( Fetch_init( serv ) ) | |
177 { | |
178 if ( noffle.queryGrps ) | |
179 Client_getGrps(); | |
180 if ( noffle.queryDsc ) | |
181 Client_getDsc(); | |
182 if ( noffle.queryTimes ) | |
183 Client_getCreationTimes(); | |
184 Fetch_close(); | |
185 } | |
186 } | |
187 | |
188 /* Expire all overviews not in database */ | |
189 static void | |
190 expireContents( void ) | |
191 { | |
192 const Over *ov; | |
193 int i; | |
194 int cntDel, cntLeft; | |
195 Str grp; | |
196 Bool autoUnsubscribe; | |
197 int autoUnsubscribeDays; | |
198 time_t now = time( NULL ), maxAge = 0; | |
199 const char *msgId; | |
200 | |
201 autoUnsubscribe = Cfg_autoUnsubscribe(); | |
202 autoUnsubscribeDays = Cfg_autoUnsubscribeDays(); | |
203 maxAge = Cfg_autoUnsubscribeDays() * 24 * 3600; | |
204 if ( ! Cont_firstGrp( grp ) ) | |
205 return; | |
206 Log_inf( "Expiring overviews not in database" ); | |
207 do | |
208 { | |
209 if ( ! Grp_exists( grp ) ) | |
210 Log_err( "Overview file for unknown group %s exists", grp ); | |
211 else | |
212 { | |
213 cntDel = cntLeft = 0; | |
214 Cont_read( grp ); | |
215 for ( i = Cont_first(); i <= Cont_last(); ++i ) | |
216 if ( ( ov = Cont_get( i ) ) ) | |
217 { | |
218 msgId = Ov_msgId( ov ); | |
219 if ( ! Db_contains( msgId ) ) | |
220 { | |
221 Cont_delete( i ); | |
222 ++cntDel; | |
223 } | |
224 else | |
225 ++cntLeft; | |
226 } | |
227 if ( ! Grp_local( grp ) | |
228 && autoUnsubscribe | |
229 && difftime( now, Grp_lastAccess( grp ) ) > maxAge ) | |
230 { | |
231 Log_ntc( "Auto-unsubscribing from %s after %d " | |
232 "days without access", | |
233 grp, autoUnsubscribeDays ); | |
234 Pseudo_autoUnsubscribed( grp, autoUnsubscribeDays ); | |
235 Fetchlist_read(); | |
236 Fetchlist_remove( grp ); | |
237 Fetchlist_write(); | |
238 } | |
239 Cont_write(); | |
240 Grp_setFirstLast( grp, Cont_first(), Cont_last() ); | |
241 Log_inf( "%ld overviews deleted from group %s, %ld left (%ld-%ld)", | |
242 cntDel, grp, cntLeft, Grp_first( grp ), Grp_last( grp ) ); | |
243 } | |
244 } | |
245 while ( Cont_nextGrp( grp ) ); | |
246 } | |
247 | |
248 static void | |
249 doExpire( void ) | |
250 { | |
251 Db_close(); | |
252 Db_expire(); | |
253 if ( ! Db_open() ) | |
254 return; | |
255 expireContents(); | |
256 } | |
257 | |
258 static void | |
259 doCreateLocalGroup( const char * name ) | |
260 { | |
261 Str grp; | |
262 | |
263 Utl_cpyStr( grp, name ); | |
264 Utl_toLower( grp ); | |
265 name = Utl_stripWhiteSpace( grp ); | |
266 | |
267 if ( Grp_exists( name ) ) | |
268 fprintf( stderr, "'%s' already exists.\n", name ); | |
269 else | |
270 { | |
271 Log_inf( "Creating new local group '%s'", name ); | |
272 Grp_create( name ); | |
273 Grp_setLocal( name ); | |
274 printf( "New local group '%s' created.\n", name ); | |
275 } | |
276 } | |
277 | |
278 static void | |
279 doDeleteLocalGroup( const char * name ) | |
280 { | |
281 Str grp; | |
282 | |
283 Utl_cpyStr( grp, name ); | |
284 Utl_toLower( grp ); | |
285 name = Utl_stripWhiteSpace( grp ); | |
286 | |
287 if ( ! Grp_exists( name ) ) | |
288 fprintf( stderr, "'%s' does not exist.\n", name ); | |
289 else | |
290 { | |
291 int i; | |
292 | |
293 Log_inf( "Deleting group '%s'", name ); | |
294 | |
295 /* | |
296 Delete all articles that are only in the group. Check the | |
297 article Xref for more than one group. | |
298 */ | |
299 Cont_read( name ); | |
300 for ( i = Cont_first(); i <= Cont_last(); i++ ) | |
301 { | |
302 const Over *over; | |
303 Bool toDelete; | |
304 Str msgId; | |
305 | |
306 over = Cont_get( i ); | |
307 toDelete = TRUE; | |
308 if ( over != NULL ) | |
309 { | |
310 ItemList * xref; | |
311 | |
312 Utl_cpyStr( msgId, Ov_msgId( over ) ); | |
313 xref = new_Itl( Db_xref( msgId ), " " ); | |
314 if ( Itl_count( xref ) > 1 ) | |
315 toDelete = FALSE; | |
316 del_Itl( xref ); | |
317 } | |
318 Cont_delete( i ); | |
319 if ( toDelete ) | |
320 Db_delete( msgId ); | |
321 } | |
322 Cont_write(); | |
323 Grp_delete( name ); | |
324 printf( "Group '%s' deleted.\n", name ); | |
325 } | |
326 } | |
327 | |
328 static void | |
329 doList( void ) | |
330 { | |
331 FetchMode mode; | |
332 int i, size; | |
333 const char *name, *modeStr = ""; | |
334 | |
335 Fetchlist_read(); | |
336 size = Fetchlist_size(); | |
337 if ( size == 0 ) | |
338 fprintf( stderr, "Fetch list is empty.\n" ); | |
339 else | |
340 for ( i = 0; i < size; ++i ) | |
341 { | |
342 Fetchlist_element( &name, &mode, i ); | |
343 switch ( mode ) | |
344 { | |
345 case FULL: | |
346 modeStr = "full"; break; | |
347 case THREAD: | |
348 modeStr = "thread"; break; | |
349 case OVER: | |
350 modeStr = "over"; break; | |
351 } | |
352 printf( "%s %s %s\n", name, Grp_serv( name ), modeStr ); | |
353 } | |
354 } | |
355 | |
356 /* A modify command. argc/argv start AFTER '-m'. */ | |
357 static Bool | |
358 doModify( const char *cmd, int argc, char **argv ) | |
359 { | |
360 const char *grp; | |
361 | |
362 if ( argc < 2 ) | |
363 { | |
364 fprintf( stderr, "Insufficient arguments to -m\n" ); | |
365 return FALSE; | |
366 | |
367 } | |
368 else if ( strcmp( cmd, "desc" ) != 0 | |
369 && strcmp( cmd, "post" ) != 0 ) | |
370 { | |
371 fprintf( stderr, "Unknown argument -m %s\n", optarg ); | |
372 return FALSE; | |
373 } | |
374 | |
375 grp = argv[ 0 ]; | |
376 argv++; | |
377 argc--; | |
378 | |
379 if ( strcmp( cmd, "desc" ) == 0 ) | |
380 { | |
381 Str desc; | |
382 | |
383 Utl_cpyStr( desc, *( argv++ ) ); | |
384 while ( --argc > 0 ) | |
385 { | |
386 Utl_catStr( desc, " " ); | |
387 Utl_catStr( desc, *( argv++ ) ); | |
388 } | |
389 | |
390 Grp_setDsc( grp, desc ); | |
391 } | |
392 else | |
393 { | |
394 char c; | |
395 | |
396 if ( ! Grp_local( grp ) ) | |
397 { | |
398 fprintf( stderr, "%s is not a local group\n", grp ); | |
399 return FALSE; | |
400 } | |
401 | |
402 c = **argv; | |
403 if ( c == 'y' || c == 'm' || c == 'n' ) | |
404 Grp_setPostAllow( grp, c ); | |
405 else | |
406 { | |
407 fprintf( stderr, "Access must be 'y', 'n' or 'm'" ); | |
408 return FALSE; | |
409 } | |
410 } | |
411 | |
412 return TRUE; | |
413 } | |
414 | |
415 static void | |
416 doGrps( void ) | |
417 { | |
418 const char *g; | |
419 Str dateLastAccess, dateCreated; | |
420 time_t lastAccess, created; | |
421 | |
422 if ( Grp_firstGrp( &g ) ) | |
423 do | |
424 { | |
425 lastAccess = Grp_lastAccess( g ); | |
426 created = Grp_created( g ); | |
427 ASSERT( lastAccess >= 0 ); | |
428 ASSERT( created >= 0 ); | |
429 strftime( dateLastAccess, MAXCHAR, "%Y-%m-%d %H:%M:%S", | |
430 localtime( &lastAccess ) ); | |
431 strftime( dateCreated, MAXCHAR, "%Y-%m-%d %H:%M:%S", | |
432 localtime( &created ) ); | |
433 printf( "%s\t%s\t%i\t%i\t%i\t%c\t%s\t%s\t%s\n", | |
434 g, Grp_serv( g ), Grp_first( g ), Grp_last( g ), | |
435 Grp_rmtNext( g ), Grp_postAllow( g ), dateCreated, | |
436 dateLastAccess, Grp_dsc( g ) ); | |
437 } | |
438 while ( Grp_nextGrp( &g ) ); | |
439 } | |
440 | |
441 static Bool | |
442 doSubscribe( const char *name, FetchMode mode ) | |
443 { | |
444 if ( ! Grp_exists( name ) ) | |
445 { | |
446 fprintf( stderr, "%s is not available at remote servers.\n", name ); | |
447 return FALSE; | |
448 } | |
449 Fetchlist_read(); | |
450 if ( Fetchlist_add( name, mode ) ) | |
451 printf( "Adding %s to fetch list in %s mode.\n", | |
452 name, mode == FULL ? "full" : mode == THREAD ? | |
453 "thread" : "overview" ); | |
454 else | |
455 printf( "%s is already in fetch list. Mode is now: %s.\n", | |
456 name, mode == FULL ? "full" : mode == THREAD ? | |
457 "thread" : "overview" ); | |
458 if ( ! Fetchlist_write() ) | |
459 fprintf( stderr, "Could not save fetchlist.\n" ); | |
460 return TRUE; | |
461 } | |
462 | |
463 static void | |
464 doUnsubscribe( const char *name ) | |
465 { | |
466 Fetchlist_read(); | |
467 if ( ! Fetchlist_remove( name ) ) | |
468 printf( "%s is not in fetch list.\n", name ); | |
469 else | |
470 printf( "%s removed from fetch list.\n", name ); | |
471 if ( ! Fetchlist_write() ) | |
472 fprintf( stderr, "Could not save fetchlist.\n" ); | |
473 } | |
474 | |
475 static void | |
476 printUsage( void ) | |
477 { | |
478 static const char *msg = | |
479 "Usage: noffle <option>\n" | |
480 "Option is one of the following:\n" | |
481 " -a | --article <msg id>|all Show article(s) in database\n" | |
482 " -c | --cancel <msg id> Remove article from database\n" | |
483 " -C | --create <grp> Create a local group\n" | |
484 " -d | --database Show content of article database\n" | |
485 " -D | --delete <grp> Delete a group\n" | |
486 " -e | --expire Expire articles\n" | |
487 " -f | --fetch Get newsfeed from server/post articles\n" | |
488 " -g | --groups Show all groups available at server\n" | |
489 " -h | --help Show this text\n" | |
490 " -l | --list List groups on fetch list\n" | |
491 " -m | --modify desc <grp> <desc> Modify a group description\n" | |
492 " -m | --modify post <grp> (y|n) Modify posting status of a local group\n" | |
493 " -n | --online Switch to online mode\n" | |
494 " -o | --offline Switch to offline mode\n" | |
495 " -q | --query groups Get group list from server\n" | |
496 " -q | --query desc Get group descriptions from server\n" | |
497 " -q | --query times Get group creation times from server\n" | |
498 " -r | --server Run as server on stdin/stdout\n" | |
499 " -R | --requested List articles marked for download\n" | |
500 " -s | --subscribe-over <grp> Add group to fetch list (overview)\n" | |
501 " -S | --subscribe-full <grp> Add group to fetch list (full)\n" | |
502 " -t | --subscribe-thread <grp> Add group to fetch list (thread)\n" | |
503 " -u | --unsubscribe <grp> Remove group from fetch list\n" | |
504 " -v | --version Print version\n"; | |
505 fprintf( stderr, "%s", msg ); | |
506 } | |
507 | |
508 /* | |
509 Allow core files: Change core limit and change working directory | |
510 to spool directory, where news has write permissions. | |
511 */ | |
512 static void | |
513 enableCorefiles() | |
514 { | |
515 struct rlimit lim; | |
516 | |
517 if ( getrlimit( RLIMIT_CORE, &lim ) != 0 ) | |
518 { | |
519 Log_err( "Cannot get system core limit: %s", strerror( errno ) ); | |
520 return; | |
521 } | |
522 lim.rlim_cur = lim.rlim_max; | |
523 if ( setrlimit( RLIMIT_CORE, &lim ) != 0 ) | |
524 { | |
525 Log_err( "Cannot set system core limit: %s", strerror( errno ) ); | |
526 return; | |
527 } | |
528 Log_dbg( "Core limit set to %i", lim.rlim_max ); | |
529 if ( chdir( Cfg_spoolDir() ) != 0 ) | |
530 { | |
531 Log_err( "Cannot change to directory '%s'", Cfg_spoolDir() ); | |
532 return; | |
533 } | |
534 Log_dbg( "Changed to directory '%s'", Cfg_spoolDir() ); | |
535 } | |
536 | |
537 static Bool | |
538 initNoffle( Bool interactive ) | |
539 { | |
540 Log_init( "noffle", interactive, LOG_NEWS ); | |
541 Cfg_read(); | |
542 Log_dbg( "NOFFLE version %s", Cfg_version() ); | |
543 noffle.interactive = interactive; | |
544 if ( interactive ) | |
545 if ( ! Lock_openDatabases() ) | |
546 return FALSE; | |
547 if ( ! interactive ) | |
548 enableCorefiles(); | |
549 return TRUE; | |
550 } | |
551 | |
552 static void | |
553 closeNoffle( void ) | |
554 { | |
555 if ( noffle.interactive ) | |
556 Lock_closeDatabases(); | |
557 } | |
558 | |
559 static void | |
560 bugReport( int sig ) | |
561 { | |
562 Log_err( "Received SIGSEGV. Please submit a bug report" ); | |
563 signal( SIGSEGV, SIG_DFL ); | |
564 raise( sig ); | |
565 } | |
566 | |
567 static void | |
568 logSignal( int sig ) | |
569 { | |
570 const char *name; | |
571 Bool err = TRUE; | |
572 | |
573 switch ( sig ) | |
574 { | |
575 case SIGABRT: | |
576 name = "SIGABRT"; break; | |
577 case SIGFPE: | |
578 name = "SIGFPE"; break; | |
579 case SIGILL: | |
580 name = "SIGILL"; break; | |
581 case SIGINT: | |
582 name = "SIGINT"; break; | |
583 case SIGTERM: | |
584 name = "SIGTERM"; break; | |
585 case SIGPIPE: | |
586 name = "SIGPIPE"; err = FALSE; break; | |
587 default: | |
588 name = "?"; break; | |
589 } | |
590 if ( err ) | |
591 Log_err( "Received signal %i (%s). Aborting.", sig, name ); | |
592 else | |
593 Log_inf( "Received signal %i (%s). Aborting.", sig, name ); | |
594 signal( sig, SIG_DFL ); | |
595 raise( sig ); | |
596 } | |
597 | |
598 int main ( int argc, char **argv ) | |
599 { | |
600 int c, result; | |
601 struct option longOptions[] = | |
602 { | |
603 { "article", required_argument, NULL, 'a' }, | |
604 { "cancel", required_argument, NULL, 'c' }, | |
605 { "create", required_argument, NULL, 'C' }, | |
606 { "database", no_argument, NULL, 'd' }, | |
607 { "delete", required_argument, NULL, 'D' }, | |
608 { "expire", no_argument, NULL, 'e' }, | |
609 { "fetch", no_argument, NULL, 'f' }, | |
610 { "groups", no_argument, NULL, 'g' }, | |
611 { "help", no_argument, NULL, 'h' }, | |
612 { "list", no_argument, NULL, 'l' }, | |
613 { "modify", required_argument, NULL, 'm' }, | |
614 { "offline", no_argument, NULL, 'o' }, | |
615 { "online", no_argument, NULL, 'n' }, | |
616 { "query", required_argument, NULL, 'q' }, | |
617 { "server", no_argument, NULL, 'r' }, | |
618 { "requested", no_argument, NULL, 'R' }, | |
619 { "subscribe-over", required_argument, NULL, 's' }, | |
620 { "subscribe-full", required_argument, NULL, 'S' }, | |
621 { "subscribe-thread", required_argument, NULL, 't' }, | |
622 { "unsubscribe", required_argument, NULL, 'u' }, | |
623 { "version", no_argument, NULL, 'v' }, | |
624 { NULL, 0, NULL, 0 } | |
625 }; | |
626 | |
627 signal( SIGSEGV, bugReport ); | |
628 signal( SIGABRT, logSignal ); | |
629 signal( SIGFPE, logSignal ); | |
630 signal( SIGILL, logSignal ); | |
631 signal( SIGINT, logSignal ); | |
632 signal( SIGTERM, logSignal ); | |
633 signal( SIGPIPE, logSignal ); | |
634 c = getopt_long( argc, argv, "a:c:C:dD:efghlm:onq:rRs:S:t:u:v", | |
635 longOptions, NULL ); | |
636 if ( ! initNoffle( c != 'r' ) ) | |
637 return EXIT_FAILURE; | |
638 result = EXIT_SUCCESS; | |
639 switch ( c ) | |
640 { | |
641 case 0: | |
642 /* Options that set a flag. */ | |
643 break; | |
644 case 'a': | |
645 if ( ! optarg ) | |
646 { | |
647 fprintf( stderr, "Option -a needs argument.\n" ); | |
648 result = EXIT_FAILURE; | |
649 } | |
650 else | |
651 doArt( optarg ); | |
652 break; | |
653 case 'c': | |
654 if ( ! optarg ) | |
655 { | |
656 fprintf( stderr, "Option -c needs argument.\n" ); | |
657 result = EXIT_FAILURE; | |
658 } | |
659 else | |
660 doCancel( optarg ); | |
661 break; | |
662 case 'C': | |
663 if ( ! optarg ) | |
664 { | |
665 fprintf( stderr, "Option -C needs argument.\n" ); | |
666 result = EXIT_FAILURE; | |
667 } | |
668 else | |
669 doCreateLocalGroup( optarg ); | |
670 break; | |
671 case 'd': | |
672 doDb(); | |
673 break; | |
674 case 'D': | |
675 if ( ! optarg ) | |
676 { | |
677 fprintf( stderr, "Option -D needs argument.\n" ); | |
678 result = EXIT_FAILURE; | |
679 } | |
680 else | |
681 doDeleteLocalGroup( optarg ); | |
682 break; | |
683 case 'e': | |
684 doExpire(); | |
685 break; | |
686 case 'f': | |
687 doFetch(); | |
688 break; | |
689 case 'g': | |
690 doGrps(); | |
691 break; | |
692 case -1: | |
693 case 'h': | |
694 printUsage(); | |
695 break; | |
696 case 'l': | |
697 doList(); | |
698 break; | |
699 case 'm': | |
700 if ( ! optarg ) | |
701 { | |
702 fprintf( stderr, "Option -m needs argument.\n" ); | |
703 result = EXIT_FAILURE; | |
704 } | |
705 else | |
706 if ( ! doModify( optarg, argc - optind, &argv[ optind ] ) ) | |
707 result = EXIT_FAILURE; | |
708 break; | |
709 case 'n': | |
710 if ( Online_true() ) | |
711 fprintf( stderr, "NOFFLE is already online\n" ); | |
712 else | |
713 Online_set( TRUE ); | |
714 break; | |
715 case 'o': | |
716 if ( ! Online_true() ) | |
717 fprintf( stderr, "NOFFLE is already offline\n" ); | |
718 else | |
719 Online_set( FALSE ); | |
720 break; | |
721 case 'q': | |
722 if ( ! optarg ) | |
723 { | |
724 fprintf( stderr, "Option -q needs argument.\n" ); | |
725 result = EXIT_FAILURE; | |
726 } | |
727 else | |
728 { | |
729 if ( strcmp( optarg, "groups" ) == 0 ) | |
730 noffle.queryGrps = TRUE; | |
731 else if ( strcmp( optarg, "desc" ) == 0 ) | |
732 noffle.queryDsc = TRUE; | |
733 else if ( strcmp( optarg, "times" ) == 0 ) | |
734 noffle.queryTimes = TRUE; | |
735 else | |
736 { | |
737 fprintf( stderr, "Unknown argument -q %s\n", optarg ); | |
738 result = EXIT_FAILURE; | |
739 } | |
740 doQuery(); | |
741 } | |
742 break; | |
743 case 'r': | |
744 Log_inf( "Starting as server" ); | |
745 Serv_run(); | |
746 break; | |
747 case 'R': | |
748 doRequested( optarg ); | |
749 break; | |
750 case 's': | |
751 if ( ! optarg ) | |
752 { | |
753 fprintf( stderr, "Option -s needs argument.\n" ); | |
754 result = EXIT_FAILURE; | |
755 } | |
756 else | |
757 result = doSubscribe( optarg, OVER ); | |
758 break; | |
759 case 'S': | |
760 if ( ! optarg ) | |
761 { | |
762 fprintf( stderr, "Option -S needs argument.\n" ); | |
763 result = EXIT_FAILURE; | |
764 } | |
765 else | |
766 doSubscribe( optarg, FULL ); | |
767 break; | |
768 case 't': | |
769 if ( ! optarg ) | |
770 { | |
771 fprintf( stderr, "Option -t needs argument.\n" ); | |
772 result = EXIT_FAILURE; | |
773 } | |
774 else | |
775 result = doSubscribe( optarg, THREAD ); | |
776 break; | |
777 case 'u': | |
778 if ( ! optarg ) | |
779 { | |
780 fprintf( stderr, "Option -u needs argument.\n" ); | |
781 result = EXIT_FAILURE; | |
782 } | |
783 else | |
784 doUnsubscribe( optarg ); | |
785 break; | |
786 case '?': | |
787 /* Error message already printed by getopt_long */ | |
788 result = EXIT_FAILURE; | |
789 break; | |
790 case 'v': | |
791 printf( "NNTP server NOFFLE, version %s.\n", Cfg_version() ); | |
792 break; | |
793 default: | |
794 abort(); /* Never reached */ | |
795 } | |
796 closeNoffle(); | |
797 return result; | |
798 } |