<commit_before>/*
 * This file is part of Kotaka, a mud library for DGD
 * http://github.com/shentino/kotaka
 *
 * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015  <NAME>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <kotaka/checkarg.h>
#include <kotaka/log.h>
#include <kotaka/paths/intermud.h>
#include <kotaka/paths/string.h>
#include <kotaka/paths/system.h>
#include <kotaka/paths/utility.h>
#include <kotaka/privilege.h>
#include <status.h>

#define SYSTEM_CHANNELS ({ "compile", "debug", "error", "system", "trace" })

mapping intermud;	/*< set of channels to be relayed to intermud */
mapping channels;	/*< channel configuration */
mapping subscribers;	/*< channel subscribers */
mapping buffers;

/* buffers: ([ channel name: header ]) */
/* header: ({ first, last }) */
/* node: ({ prev, content, next }) */

void configure_channels();
void save();
void restore();

static void create()
{
	channels = ([ ]);
	subscribers = ([ ]);
	intermud = ([ ]);

	restore();

	configure_channels();

	save();
}

private void schedule()
{
	mixed **callouts;
	int sz;

	callouts = status(this_object(), O_CALLOUTS);

	for (sz = sizeof(callouts); --sz >= 0; ) {
		if (callouts[sz][CO_FUNCTION] == "flush") {
			return;
		}
	}

	call_out("flush", 0);
}

private void append_node(string channel, string fragment)
{
	mixed *header;
	mixed *node;
	int max;
	int len;

	if (!buffers) {
		buffers = ([ ]);
	}

	header = buffers[channel];

	if (!header) {
		node = ({ nil, "", nil });
		header = ({ node, node });
		buffers[channel] = header;
	} else {
		node = header[1];
	}

	max = status(ST_STRSIZE);

	if (max > 4096) {
		max = 4096;
	}

	while (len = strlen(fragment)) {
		int spare;

		spare = max - strlen(node[1]);

		if (spare >= len) {
			node[1] += fragment;

			return;
		} else {
			mixed *newnode;

			if (spare > 0) {
				node[1] += fragment[0 .. spare - 1];
				fragment = fragment[spare ..];
			}

			newnode = ({ node, "", nil });
			node[2] = newnode;
			header[1] = newnode;
			node = newnode;
		}
	}
}

private void write_node(string channel)
{
	mixed *header;
	mixed *node;

	header = buffers[channel];
	node = header[0];

	{
		mixed *info;

		info = SECRETD->file_info("logs/" + channel + ".log");

		if (info && info[0] >= 1 << 30) {
			SECRETD->rename_file("logs/" + channel + ".dir", "logs/" + channel + ".dir.old");
			SECRETD->make_dir("logs/" + channel + ".dir");
			SECRETD->rename_file("logs/" + channel + ".dir.old", "logs/" + channel + ".dir/old.dir");
			SECRETD->rename_file("logs/" + channel + ".log", "logs/" + channel + ".dir/old.log");
		}
	}

	SECRETD->make_dir(".");
	SECRETD->make_dir("logs");
	SECRETD->write_file("logs/" + channel + ".log", node[1]);

	node[0] = nil;

	if (node[2]) {
		header[0] = node[2];
	} else {
		buffers[channel] = nil;
	}

	if (!map_sizeof(buffers)) {
		buffers = ([ ]);
	}
}

static void flush()
{
	if (buffers && map_sizeof(buffers)) {
		string *channels;
		int sz;

		channels = map_indices(buffers);

		sz = sizeof(channels);

		write_node(channels[random(sz)]);
	}

	if (buffers) {
		schedule();
	}
}

void save()
{
	string buf;

	CONFIGD->make_dir(".");

	buf = STRINGD->hybrid_sprint( ([
		"channels": channels,
		"intermud": intermud
	]) );

	CONFIGD->remove_file("config-tmp");
	CONFIGD->write_file("config-tmp", buf + "\n");
	CONFIGD->remove_file("config");
	CONFIGD->rename_file("config-tmp", "config");
}

void restore()
{
	string buf;<commit_msg>Fix ChannelD flusher, nil out the buffers when its empty<commit_after>/*
 * This file is part of Kotaka, a mud library for DGD
 * http://github.com/shentino/kotaka
 *
 * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015  <NAME>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <kotaka/checkarg.h>
#include <kotaka/log.h>
#include <kotaka/paths/intermud.h>
#include <kotaka/paths/string.h>
#include <kotaka/paths/system.h>
#include <kotaka/paths/utility.h>
#include <kotaka/privilege.h>
#include <status.h>

#define SYSTEM_CHANNELS ({ "compile", "debug", "error", "system", "trace" })

mapping intermud;	/*< set of channels to be relayed to intermud */
mapping channels;	/*< channel configuration */
mapping subscribers;	/*< channel subscribers */
mapping buffers;

/* buffers: ([ channel name: header ]) */
/* header: ({ first, last }) */
/* node: ({ prev, content, next }) */

void configure_channels();
void save();
void restore();

static void create()
{
	channels = ([ ]);
	subscribers = ([ ]);
	intermud = ([ ]);

	restore();

	configure_channels();

	save();
}

private void schedule()
{
	mixed **callouts;
	int sz;

	callouts = status(this_object(), O_CALLOUTS);

	for (sz = sizeof(callouts); --sz >= 0; ) {
		if (callouts[sz][CO_FUNCTION] == "flush") {
			return;
		}
	}

	call_out("flush", 0);
}

private void append_node(string channel, string fragment)
{
	mixed *header;
	mixed *node;
	int max;
	int len;

	if (!buffers) {
		buffers = ([ ]);
	}

	header = buffers[channel];

	if (!header) {
		node = ({ nil, "", nil });
		header = ({ node, node });
		buffers[channel] = header;
	} else {
		node = header[1];
	}

	max = status(ST_STRSIZE);

	if (max > 4096) {
		max = 4096;
	}

	while (len = strlen(fragment)) {
		int spare;

		spare = max - strlen(node[1]);

		if (spare >= len) {
			node[1] += fragment;

			return;
		} else {
			mixed *newnode;

			if (spare > 0) {
				node[1] += fragment[0 .. spare - 1];
				fragment = fragment[spare ..];
			}

			newnode = ({ node, "", nil });
			node[2] = newnode;
			header[1] = newnode;
			node = newnode;
		}
	}
}

private void write_node(string channel)
{
	mixed *header;
	mixed *node;

	header = buffers[channel];
	node = header[0];

	{
		mixed *info;

		info = SECRETD->file_info("logs/" + channel + ".log");

		if (info && info[0] >= 1 << 30) {
			SECRETD->rename_file("logs/" + channel + ".dir", "logs/" + channel + ".dir.old");
			SECRETD->make_dir("logs/" + channel + ".dir");
			SECRETD->rename_file("logs/" + channel + ".dir.old", "logs/" + channel + ".dir/old.dir");
			SECRETD->rename_file("logs/" + channel + ".log", "logs/" + channel + ".dir/old.log");
		}
	}

	SECRETD->make_dir(".");
	SECRETD->make_dir("logs");
	SECRETD->write_file("logs/" + channel + ".log", node[1]);

	node[0] = nil;

	if (node[2]) {
		header[0] = node[2];
	} else {
		buffers[channel] = nil;
	}

	if (!map_sizeof(buffers)) {
		buffers = ([ ]);
	}
}

static void flush()
{
	if (buffers && map_sizeof(buffers)) {
		string *channels;
		int sz;

		channels = map_indices(buffers);

		sz = sizeof(channels);

		write_node(channels[random(sz)]);
	}

	if (!map_sizeof(buffers)) {
		buffers = nil;
	}

	if (buffers) {
		schedule();
	}
}

void save()
{
	string buf;

	CONFIGD->make_dir(".");

	buf = STRINGD->hybrid_sprint( ([
		"channels": channels,
		"intermud": intermud
	]) );

	CONFIGD->remove_file("config-tmp");
	CONFIGD->write_file("config-tmp", buf + "\n");
	CONFIGD->remove_file("config");
	CONFIGD->rename_file("config-tmp", "config");
}

void restore()
{
	string buf;
{'fraction_non_alphanumeric': 0.1744540685616689, 'fraction_numerical': 0.013175552031230938, 'mean_word_length': 3.5645879732739423, 'pattern_counts': {'":': 4, '<': 33, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1662383', 'n_tokens_mistral': 3197, 'n_tokens_neox': 2884, 'n_words': 792}