]> jfr.im git - irc/quakenet/newserv.git/blame - chanstats/chanstats.c
should always be %jd and cast to intmax_t to stop warning on 32bit (gcc 4.2.3) -warni...
[irc/quakenet/newserv.git] / chanstats / chanstats.c
CommitLineData
c86edd1d
Q
1
2#include "chanstats.h"
3#include "../channel/channel.h"
4#include "../lib/sstring.h"
5#include "../lib/splitline.h"
6#include "../irc/irc.h"
7#include "../core/schedule.h"
8#include "../core/error.h"
9#include "../control/control.h"
10#include "../nick/nick.h"
11#include "../lib/irc_string.h"
87698d77 12#include "../lib/version.h"
c86edd1d
Q
13
14#include <stdio.h>
15#include <string.h>
16
70b0a4e5 17MODULE_VERSION("");
87698d77 18
c86edd1d
Q
19int csext;
20int sampleindex;
e36fed08 21time_t chanstats_lastsample;
c86edd1d
Q
22time_t lastday;
23int uponehour;
24int failedinit;
25time_t lastsave;
26
27unsigned int lastdaysamples[HISTORYDAYS];
28unsigned int todaysamples;
29
30void doupdate(void *arg);
31void updatechanstats(chanindex *cip, time_t now);
32void rotatechanstats();
33void savechanstats();
34void loadchanstats();
ab0c8601 35int dochanstatssave(void *source, int argc, char **argv);
c86edd1d
Q
36int dochanstats(void *source, int argc, char **argv);
37int doexpirecheck(void *source, int cargc, char **cargv);
38int douserhistogram(void *source, int cargc, char **cargv);
39int dochanhistogram(void *source, int cargc, char **cargv);
40void dohist_now(int *data, float *bounds, int cats, nick *np);
41void dohist_today(int *data, float *bounds, int cats);
42void dohist_days(int *data, float *bounds, int cats, int days);
43void dohist_namelen(int *data, float *bounds, int cats);
44
45void _init() {
46 time_t now,when;
47
48 csext=registerchanext("chanstats");
49 if (csext<0) {
50 /* PANIC PANIC PANIC PANIC */
51 Error("chanstats",ERR_ERROR,"Couldn't register channel extension");
52 failedinit=1;
53 } else {
54 initchanstatsalloc();
55 lastday=(getnettime()/(24*3600));
56 uponehour=0;
57 sampleindex=-1;
58 memset(lastdaysamples,0,sizeof(lastdaysamples));
59 todaysamples=0;
60 failedinit=0;
61
62 loadchanstats();
63
64 /* Work out when to take the next sample */
65 now=getnettime();
e36fed08 66 if (now < chanstats_lastsample) {
58a4da4a 67 Error("chanstats",ERR_WARNING,"Last sample time in future (%jd > %jd)",(intmax_t)chanstats_lastsample,(intmax_t)now);
c86edd1d 68 when=now;
e36fed08 69 } else if (now<(chanstats_lastsample+SAMPLEINTERVAL)) {
70 lastday=chanstats_lastsample/(24*3600);
71 when=chanstats_lastsample+SAMPLEINTERVAL;
c86edd1d 72 } else {
e36fed08 73 if ((chanstats_lastsample/(24*3600))<lastday) {
c86edd1d
Q
74 rotatechanstats();
75 }
76 when=now;
77 }
78
79 lastsave=now;
80 registercontrolcmd("chanstats",10,1,&dochanstats);
81 registercontrolcmd("channelhistogram",10,13,&dochanhistogram);
82 registercontrolcmd("userhistogram",10,1,&douserhistogram);
83 registercontrolcmd("expirecheck",10,1,&doexpirecheck);
ab0c8601 84 registercontrolhelpcmd("chanstatssave",NO_DEVELOPER,1, &dochanstatssave, "Usage: chanstatssave\nForce a save of chanstats data");
c86edd1d
Q
85 schedulerecurring(when,0,SAMPLEINTERVAL,&doupdate,NULL);
86 }
87}
88
89void _fini() {
90 if (failedinit==0) {
91 savechanstats();
92 deleteschedule(NULL,&doupdate,NULL);
93 deregistercontrolcmd("chanstats",&dochanstats);
94 deregistercontrolcmd("channelhistogram",&dochanhistogram);
95 deregistercontrolcmd("userhistogram",&douserhistogram);
96 deregistercontrolcmd("expirecheck",&doexpirecheck);
ab0c8601 97 deregistercontrolcmd("chanstatssave",&dochanstatssave);
c86edd1d
Q
98 releasechanext(csext);
99 cstsfreeall();
100 }
101}
102
103/*
104 * doupdate:
105 * This is the core function which is scheduled.
106 */
107
108void doupdate(void *arg) {
109 int i;
110 time_t now;
111 chanindex *cip;
112
113 /* Get the current time and move the history pointer along one and count the sample */
114 now=getnettime();
115
116 Error("chanstats",ERR_INFO,"Running chanstats update.");
117
118 if (now/(24*3600) > lastday) {
119 rotatechanstats();
120 lastday=(now/(24*3600));
121 }
122
123 todaysamples++;
124 sampleindex++;
125 if (sampleindex==SAMPLEHISTORY) {
126 sampleindex=0;
127 uponehour=1;
128 }
129
130 /*
131 * Loop over all chans doing update
132 */
133
134 for(i=0;i<CHANNELHASHSIZE;i++) {
135 for (cip=chantable[i];cip;cip=cip->next) {
136 if ((cip->channel!=NULL) || (cip->exts[csext]!=NULL)) {
137 updatechanstats(cip,now);
138 }
139 }
140 }
141
e36fed08 142 chanstats_lastsample=now;
c86edd1d
Q
143 if (now-lastsave > SAVEINTERVAL) {
144 savechanstats();
145 lastsave=now;
146 }
147}
148
149void updatechanstats(chanindex *cip, time_t now) {
150 chanstats *csp;
151 int currentusers;
152
153 csp=cip->exts[csext];
154 if (csp==NULL) {
155 csp=cip->exts[csext]=getchanstats();
156 memset(csp,0,sizeof(chanstats));
157 }
158
159 if (cip->channel!=NULL) {
08d6ada8
CP
160 channel *cp = cip->channel;
161 int i;
162 nick *np;
163
164 currentusers=countuniquehosts(cp);
165
166 for (i=0;i<cp->users->hashsize;i++) {
167 if (cp->users->content[i]==nouser)
168 continue;
169
170 if ((np=getnickbynumeric(cp->users->content[i]))==NULL) {
16739dbe 171 Error("channel",ERR_ERROR,"Found unknown numeric %lu on channel %s",cp->users->content[i],cp->index->name->content);
08d6ada8
CP
172 continue;
173 }
174
175 if (IsXOper(np) || IsService(np))
176 currentusers--;
177 }
178
c86edd1d
Q
179 csp->todaysamples++;
180 } else {
181 currentusers=0;
182 }
183
184 csp->lastsamples[sampleindex]=currentusers;
185 csp->lastsampled=now;
186 csp->todayusers+=currentusers;
187
188 if (currentusers > csp->todaymax) {
189 csp->todaymax=currentusers;
190 }
191}
192
193void rotatechanstats() {
194 int i,j,k;
195 chanindex *cip,*ncip;
196 chanstats *csp;
197
198 for (i=0;i<CHANNELHASHSIZE;i++) {
199 for (cip=chantable[i];cip;cip=ncip) {
200 ncip=cip->next;
201
202 if ((csp=cip->exts[csext])==NULL) {
203 continue;
204 }
205
206 if (csp->todaysamples==0) {
207 /* No samples today, might want to delete */
208 k=0;
209 for(j=0;j<HISTORYDAYS;j++) {
210 k+=csp->lastdays[j];
211 }
212 if (k<50) {
213 freechanstats(csp);
214 cip->exts[csext]=NULL;
215 releasechanindex(cip);
216 continue;
217 }
218 }
219
220 /* Move the samples along one */
221 memmove(&(csp->lastdays[1]),&(csp->lastdays[0]),(HISTORYDAYS-1)*sizeof(short));
222 memmove(&(csp->lastdaysamples[1]),&(csp->lastdaysamples[0]),(HISTORYDAYS-1)*sizeof(char));
223 memmove(&(csp->lastmax[1]),&(csp->lastmax[0]),(HISTORYDAYS-1)*sizeof(unsigned short));
224
225 csp->lastdaysamples[0]=csp->todaysamples;
226 csp->lastdays[0]=(csp->todayusers * 10)/todaysamples;
227 csp->lastmax[0]=csp->todaymax;
228
229 csp->todaysamples=0;
230 csp->todayusers=0;
231 if (cip->channel) {
232 csp->todaymax=cip->channel->users->totalusers;
233 } else {
234 csp->todaymax=0;
235 }
236 }
237 }
238
239 memmove(&lastdaysamples[1],&lastdaysamples[0],(HISTORYDAYS-1)*sizeof(unsigned int));
240 lastdaysamples[0]=todaysamples;
241 todaysamples=0;
242}
243
244void savechanstats() {
245 FILE *fp;
246 int i,j;
247 chanindex *cip;
248 chanstats *chp;
249
ab0c8601 250 if ((fp=fopen("data/chanstats","w"))==NULL) {
c86edd1d
Q
251 return;
252 }
253
254 /* header: samples for today + last HISTORYDAYS days */
255
e36fed08 256 fprintf(fp,"%lu %u",chanstats_lastsample,todaysamples);
c86edd1d
Q
257 for(i=0;i<HISTORYDAYS;i++) {
258 fprintf(fp," %u",lastdaysamples[i]);
259 }
260 fprintf(fp,"\n");
261
e36fed08 262 /* body: channel, chanstats_lastsample, samplestoday, sizetoday, <last sizes, last samples> */
c86edd1d
Q
263 for (i=0;i<CHANNELHASHSIZE;i++) {
264 for (cip=chantable[i];cip;cip=cip->next) {
265 if ((chp=cip->exts[csext])==NULL) {
266 continue;
267 }
268 fprintf(fp,"%s %lu %u %u %u",cip->name->content,chp->lastsampled,chp->todaysamples,chp->todayusers,chp->todaymax);
269
270 for (j=0;j<HISTORYDAYS;j++) {
271 fprintf(fp," %u %u %u",chp->lastdays[j],chp->lastdaysamples[j],chp->lastmax[j]);
272 }
273 fprintf(fp,"\n");
274 }
275 }
276
277 fclose(fp);
278}
279
280void loadchanstats() {
281 FILE *fp;
282 int i;
283 char inbuf[2048];
284 char *args[100];
285 unsigned int chancount=0;
286 chanstats *chp;
287 chanindex *cip;
288
ab0c8601 289 if ((fp=fopen("data/chanstats","r"))==NULL) {
c86edd1d
Q
290 Error("chanstats",ERR_ERROR,"Unable to load channel stats file");
291 return;
292 }
293
294 fgets(inbuf,2048,fp);
295 if (feof(fp)) {
296 Error("chanstats",ERR_ERROR,"Corrupt channel stats file");
297 fclose(fp);
298 return;
299 }
300
301 if ((splitline(inbuf,args,50,0))!=(HISTORYDAYS+2)) {
302 Error("chanstats",ERR_ERROR,"Corrupt channel stats file");
303 fclose(fp);
304 return;
305 }
306
e36fed08 307 chanstats_lastsample=strtol(args[0],NULL,10);
c86edd1d
Q
308 todaysamples=strtol(args[1],NULL,10);
309 for(i=0;i<HISTORYDAYS;i++) {
310 lastdaysamples[i]=strtol(args[i+2],NULL,10);
311 }
312
313 while (!feof(fp)) {
314 fgets(inbuf,2048,fp);
315 if (feof(fp)) {
316 break;
317 }
318
319 if ((splitline(inbuf,args,100,0))!=5+(HISTORYDAYS*3)) {
320 Error("chanstats",ERR_WARNING,"Corrupt channel stats line");
321 continue;
322 }
323
324 cip=findorcreatechanindex(args[0]);
325 if (cip->exts[csext]!=NULL) {
326 Error("chanstats",ERR_ERROR,"Duplicate stats entry for channel %s",args[0]);
327 continue;
328 }
329 cip->exts[csext]=chp=getchanstats();
330
331 chp->lastsampled=strtol(args[1],NULL,10);
332 chp->todaysamples=strtol(args[2],NULL,10);
333 chp->todayusers=strtol(args[3],NULL,10);
334 chp->todaymax=strtol(args[4],NULL,10);
335
336 for(i=0;i<HISTORYDAYS;i++) {
337 chp->lastdays[i]=strtol(args[5+(i*3)],NULL,10);
338 chp->lastdaysamples[i]=strtol(args[6+(i*3)],NULL,10);
339 chp->lastmax[i]=strtol(args[7+(i*3)],NULL,10);
340 }
341
342 chancount++;
343 }
344
345 Error("chanstats",ERR_INFO,"Loaded %u channels",chancount);
346}
347
348int dochanstats(void *source, int cargc, char **cargv) {
349 chanstats *csp;
350 chanindex *cip;
351 nick *sender=(nick *)source;
352 int i,j,k,l;
353 int tot,emp;
354 int themax;
355
356 if (cargc<1) {
357 controlreply(sender,"Usage: chanstats #channel");
358 return CMD_ERROR;
359 }
360
361 cip=findchanindex(cargv[0]);
362
363 if (cip==NULL) {
364 controlreply(sender,"Can't find any record of %s.",cargv[0]);
365 return CMD_ERROR;
366 }
367
368 csp=cip->exts[csext];
369
370 if (csp==NULL) {
371 controlreply(sender,"Can't find any stats for %s.",cip->name->content);
372 return CMD_ERROR;
373 }
374
375 controlreply(sender,"Statistics for channel %s:",cip->name->content);
376
377 if (uponehour==0) {
378 controlreply(sender," [Service up <1hour, can't report last hour stats]");
379 } else {
380 tot=0; emp=0;
381 for(i=0;i<SAMPLEHISTORY;i++) {
382 tot+=csp->lastsamples[i];
383 if (csp->lastsamples[i]==0) {
384 emp++;
385 }
386 }
387 controlreply(sender," Last hour : %6.1f average users, empty %5.1f%% of the time",(float)tot/SAMPLEHISTORY,((float)emp/SAMPLEHISTORY)*100);
388 }
389 controlreply(sender, " Today so far : %6.1f average users, empty %5.1f%% of the time, %4d max",
390 (float)csp->todayusers/todaysamples, ((float)(todaysamples-csp->todaysamples)/todaysamples)*100,
391 csp->todaymax);
392
393 themax=csp->lastmax[0];
394
395 controlreply(sender, " Yesterday : %6.1f average users, empty %5.1f%% of the time, %4d max",
396 (float)csp->lastdays[0]/10, ((float)(lastdaysamples[0]-csp->lastdaysamples[0])/lastdaysamples[0])*100,
397 themax);
398
399 /* 7-day average */
400 j=k=l=0;
401 for (i=0;i<7;i++) {
402 j+=csp->lastdays[i];
403 k+=csp->lastdaysamples[i];
404 l+=lastdaysamples[i];
405 if (csp->lastmax[i]>themax) {
406 themax=csp->lastmax[i];
407 }
408 }
409 controlreply(sender, " 7-day average : %6.1f average users, empty %5.1f%% of the time, %4d max",
410 (float)j/70,(float)((l-k)*100)/l, themax);
411
412 /* 14-day average: continuation of last loop */
413 for (;i<14;i++) {
414 j+=csp->lastdays[i];
415 k+=csp->lastdaysamples[i];
416 l+=lastdaysamples[i];
417 if (csp->lastmax[i]>themax) {
418 themax=csp->lastmax[i];
419 }
420 }
421 controlreply(sender, " 14-day average: %6.1f average users, empty %5.1f%% of the time, %4d max",
422 (float)j/140,(float)((l-k)*100)/l, themax);
423
424 return CMD_OK;
425}
426
427#define EXPIREMIN 4
428
429int doexpirecheck(void *source, int cargc, char **cargv) {
430 nick *sender=(nick *)source;
431 chanindex *cip;
432 chanstats *csp;
433 int i;
434
435 if (cargc<1) {
436 controlreply(sender, "Usage: expirecheck #channel");
437 return CMD_ERROR;
438 }
439
440 if ((cip=findchanindex(cargv[0]))==NULL) {
441 /* Couldn't find the channel at all: it's in no way big enough! */
442
443 controlreply(sender,"delchan %s",cargv[0]);
444 return CMD_OK;
445 }
446
447 if ((csp=(chanstats *)cip->exts[csext])==NULL) {
448 /* No stats: similar */
449
450 controlreply(sender,"delchan %s",cargv[0]);
451 return CMD_OK;
452 }
453
454 /* Did they hit the minimum today? */
455 if (csp->todaymax >= EXPIREMIN) {
456 return CMD_OK;
457 }
458
459 /* Or recently? */
460 for (i=0;i<HISTORYDAYS;i++) {
461 if (csp->lastmax[i] >= EXPIREMIN) {
462 return CMD_OK;
463 }
464 }
465
466 /* If not, delchan time! */
467 controlreply(sender,"delchan %s",cargv[0]);
468 return CMD_OK;
469}
470
471int douserhistogram(void *source, int cargc, char **cargv) {
472 int histdata[21];
473 int serverdata[21];
474 nick *np;
475 nick *sender=(nick *)source;
476 int top=0,tot=0,servertot=0,servertop=0;
477 int i;
478 int theserver=-1;
479 int sig=0,serversig=0;
480
481 if (cargc>0) {
482 if ((theserver=findserver(cargv[0]))<0) {
483 controlreply(sender,"Can't find server %s",cargv[0]);
484 return CMD_ERROR;
485 }
486 }
487
488 for (i=0;i<21;i++) {
489 histdata[i]=0;
490 serverdata[i]=0;
491 }
492
493 for (i=0;i<NICKHASHSIZE;i++) {
494 for (np=nicktable[i];np;np=np->next) {
495 if (np->channels->cursi <= 20) {
496 histdata[np->channels->cursi]++;
497 if (theserver>=0 && homeserver(np->numeric)==theserver) {
498 servertop++;
499 serverdata[np->channels->cursi]++;
500 }
501 top++;
502 }
503 }
504 }
505
506 tot=top;
507 servertot=servertop;
508
509 controlreply(sender,"Size Count %%ge Count+ %s",(theserver>=0?serverlist[theserver].name->content:""));
510
511 for (i=0;i<21;i++) {
512 if (theserver>=0) {
513 controlreply(sender,"%4d %6d %4.1f%% %6d %6d %4.1f%% %6d",i,histdata[i],(100.0*histdata[i])/tot,top,
514 serverdata[i],(100.0*serverdata[i])/servertot, servertop);
515 servertop-=serverdata[i];
516 serversig += (i * serverdata[i]);
517 } else {
518 controlreply(sender,"%4d %6d %4.1f%% %6d",i,histdata[i],(100.0*histdata[i])/tot,top);
519 }
520 sig += (i * histdata[i]);
521 top-=histdata[i];
522 }
523
524 controlreply(sender,"Average channel count (all users): %.2f",(float)sig/tot);
525 if (theserver>=0) {
526 controlreply(sender,"Average channel count (%s): %.2f",serverlist[theserver].name->content,(float)serversig/servertot);
527 }
4ad1cf7a
CP
528
529 return CMD_OK;
c86edd1d
Q
530}
531
532#define HISTITEMS 30
533
534int dochanhistogram(void *source, int cargc, char **cargv) {
535 int histdata[6][HISTITEMS];
536 float bounds[HISTITEMS] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 15.0, 20.0,
537 25.0, 50.0, 75.0, 100.0, 150.0, 200.0, 250.0, 300.0, 400.0, 500.0,
538 1000.0, 1000000.0 };
539 int histsize=23;
540 int hists=0;
541 nick *sender=(nick *)source;
542 nick *targetnick;
543 int i,j,pos;
544 char header[(6*14)+10];
545 char buf[512];
546
547 header[0]='\0';
548
549 if (cargc<1) {
550 controlreply(sender,"Usage: channelhistogram <metrics>");
551 controlreply(sender,"Valid metrics are: now, today, yesterday, days <daycount>, nick <nick>.");
552 controlreply(sender,"Up to 6 metrics may be specified.");
553 return CMD_ERROR;
554 }
555
556 for (i=0;i<cargc;i++) {
557 /* Look for a valid metric */
558 if (!ircd_strcmp(cargv[i],"now")) {
559 dohist_now(histdata[hists++],bounds,histsize,NULL);
560 strcat(header,"Current Chans ");
561 } else if (!ircd_strcmp(cargv[i],"today")) {
562 dohist_today(histdata[hists++],bounds,histsize);
563 strcat(header,"Today Average ");
564 } else if (!ircd_strcmp(cargv[i],"yesterday")) {
565 dohist_days(histdata[hists++],bounds,histsize,1);
566 strcat(header,"Yesterday Avg ");
567 } else if (!ircd_strcmp(cargv[i],"namelen")) {
568 dohist_namelen(histdata[hists++],bounds,histsize);
569 strcat(header,"Name Length ");
570 } else if (!ircd_strcmp(cargv[i],"days")) {
571 if ((i+1)>=cargc) {
572 controlreply(sender,"'days' metric requires a numeric argument.");
573 return CMD_ERROR;
574 }
575 j=strtol(cargv[i+1],NULL,10);
576 if (j<1 || j>14) {
577 controlreply(sender,"Must specify a number of days between 1 and 14.");
578 return CMD_ERROR;
579 }
580 dohist_days(histdata[hists++],bounds,histsize,j);
581 sprintf(buf,"%2d Day Avg ",j);
582 strcat(header,buf);
583 i++;
584 } else if (!ircd_strcmp(cargv[i],"nick")) {
585 if ((i+1)>=cargc) {
586 controlreply(sender,"'nick' metric requires a nickname argument.");
587 return CMD_ERROR;
588 }
589 if ((targetnick=getnickbynick(cargv[i+1]))==NULL) {
590 controlreply(sender,"Couldn't find user %s.",cargv[i+1]);
591 return CMD_ERROR;
592 }
593 dohist_now (histdata[hists++],bounds,histsize,targetnick);
594 sprintf(buf,"w/%-11.11s ",targetnick->nick);
595 strcat(header,buf);
596 i++;
597 } else {
598 controlreply(sender,"Unknown metric %s.",cargv[i]);
599 return CMD_ERROR;
600 }
601 }
602
603 controlreply(sender," %s",header);
604
605 buf[0]='\0';
606 for (i=0;i<hists;i++) {
607 strcat(buf,"InRnge Range+ ");
608 }
609 controlreply(sender," %s",buf);
610
611 for(i=0;i<(histsize-1);i++) {
612 if (i==(histsize-2)) {
613 pos=sprintf(buf,"%6.1f+ ",bounds[i]);
614 } else {
615 pos=sprintf(buf,"%6.1f-%6.1f ",bounds[i],bounds[i+1]);
616 }
617 for(j=0;j<hists;j++) {
618 pos+=sprintf(&(buf[pos]),"%6d %6d ",histdata[j][i]-histdata[j][i+1],histdata[j][i]);
619 }
620 controlreply(sender,"%s",buf);
621 }
622
623 return CMD_OK;
624}
625
626void dohist_now(int *data, float *bounds, int cats, nick *np) {
627 int i,j;
628 chanindex *cip;
629
630 for (i=0;i<cats;i++)
631 data[i]=0;
632
633 for (i=0;i<CHANNELHASHSIZE;i++) {
634 for (cip=chantable[i];cip;cip=cip->next) {
635 if (cip->channel==NULL) {
636 continue;
637 }
638 if (np==NULL || NULL!=getnumerichandlefromchanhash(cip->channel->users, np->numeric))
639 for (j=0;j<cats;j++) {
640 if (cip->channel->users->totalusers>=bounds[j]) {
641 data[j]++;
642 }
643 }
644 }
645 }
646}
647
648void dohist_today(int *data, float *bounds, int cats) {
649 int i,j;
650 chanindex *cip;
651 chanstats *csp;
652 float f;
653
654 for (i=0;i<cats;i++)
655 data[i]=0;
656
657 for (i=0;i<CHANNELHASHSIZE;i++) {
658 for (cip=chantable[i];cip;cip=cip->next) {
659 if ((csp=cip->exts[csext])==NULL) {
660 continue;
661 }
662 f=(float)csp->todayusers/todaysamples;
663 for(j=0;j<cats;j++) {
664 if (f>=bounds[j]) {
665 data[j]++;
666 }
667 }
668 }
669 }
670}
671
672void dohist_days(int *data, float *bounds, int cats, int days) {
673 int i,j,k;
674 chanindex *cip;
675 chanstats *csp;
676 float f;
677
678 for (i=0;i<cats;i++)
679 data[i]=0;
680
681 for (i=0;i<CHANNELHASHSIZE;i++) {
682 for (cip=chantable[i];cip;cip=cip->next) {
683 if ((csp=cip->exts[csext])==NULL) {
684 continue;
685 }
686 k=0;
687 for (j=0;j<days;j++) {
688 k+=csp->lastdays[j];
689 }
690 f=(float)k/(days*10);
691 for(j=0;j<cats;j++) {
692 if (f>=bounds[j]) {
693 data[j]++;
694 }
695 }
696 }
697 }
698}
699
700void dohist_namelen(int *data, float *bounds, int cats) {
701 int i,j;
702 chanindex *cip;
703
704 for (i=0;i<cats;i++)
705 data[i]=0;
706
707 for(i=0;i<CHANNELHASHSIZE;i++) {
708 for (cip=chantable[i];cip;cip=cip->next) {
709 for (j=0;j<cats;j++) {
710 if (cip->name->length>=bounds[j]) {
711 data[j]++;
712 }
713 }
714 }
715 }
716}
ab0c8601
P
717
718int dochanstatssave(void *source, int cargc, char **cargv) {
719 nick *sender=(nick *)source;
720
721 controlreply(sender,"Saving Chanstats..");
722 savechanstats();
723 controlreply(sender,"Chanstats saved");
724 return CMD_OK;
725}