/*
 *  linux/kernel/printk.c
 *
 *  (C) 1991  Linus Torvalds
 */

#include <stdarg.h>
#include <stddef.h>
#include <errno.h>

#include <asm/segment.h>
#include <asm/system.h>

#include <linux/sched.h>
#include <linux/kernel.h>

static char buf[1024];

extern int vsprintf(char * buf, const char * fmt, va_list args);
extern void console_print(const char *);

static unsigned long log_page = 0;
static unsigned long log_start = 0;
static unsigned long log_size = 0;
static struct task_struct * log_wait = NULL;

int sys_syslog(int type, char * buf, int len)
{
	unsigned long i;
	char c;

	if (!suser())
		return -EPERM;
	switch (type) {
		case 0:
			i = log_page;
			log_page = 0;
			free_page(i);
			wake_up(&log_wait);
			return 0;
		case 1:
			i = get_free_page();
			if (log_page) {
				free_page(i);
				return 0;
			} else if (log_page = i) {
				log_start = log_size = 0;
				return 0;
			}
			return -ENOMEM;
		case 2:
			if (!buf || len < 0)
				return -EINVAL;
			if (!len)
				return 0;
			verify_area(buf,len);
			while (!log_size) {
				if (!log_page)
					return -EIO;
				if (current->signal & ~current->blocked)
					return -ERESTARTSYS;
				cli();
				if (!log_size)
					interruptible_sleep_on(&log_wait);
				sti();
			}
			i = 0;
			while (log_size && len) {
				c = *((char *) log_page+log_start);
				log_start++;
				log_size--;
				log_start &= 4095;
				put_fs_byte(c,buf);
				buf++;
				i++;
			}
			return i;
	}
	return -EINVAL;
}
			

int printk(const char *fmt, ...)
{
	va_list args;
	int i,j;
	char * p;

	va_start(args, fmt);
	i=vsprintf(buf,fmt,args);
	va_end(args);
	for (j = 0; j < i && log_page ; j++) {
		p = (char *) log_page + (4095 & (log_start+log_size));
		*p = buf[j];
		if (log_size < 4096)
			log_size++;
		else
			log_start++;
	}
	if (log_page)
		wake_up(&log_wait);
	console_print(buf);
	return i;
}
