On 18.07.2013 09:25:23, Vojtech Horky wrote:
> Just a sidenote: if you are more interested in the "microkernel
> programming", there are also tickets that are for kernel - such as 
> #91 or #466 - but they do not target the more complex parts 
> (e.g. memory or thread management).

I've tried something with #91. But in the end I'm not sure if I done it 
correctly. Maybe there are too much additions to existing code.
For example, I added a character buffer to outdev and wrote an 
outdev_push_character() routine for it, I don't know if this was a good 
idea. I added a new command 'pager' to console. Modifed stdout_write() 
to use the new buffer if the buffering is on and there is an existing 
console command that requested the use of the buffer, with this patch 
there is one if statement more in stdout_write(), it still passes the 
kernel printf string test.

There is problematic code in the patch, like for example:

stdout->char_buffering_on = true;
ch = indev_pop_character(stdin);
stdout->char_buffering_on = false;

I used this to wait for keypress and buffer other messages. Those other 
messages will get printed in the end, but I don't really know if it's 
safe to write code like that and how much important it is for messages 
to arive on time on the output device. Maybe the pager should be turned 
off during critical debugging, or maybe I should have just left things
unbuffered during the wait for keypress.

Anyway, as far as I tested, the pager is working. To use it enter 
'pager' in kconsole, or 'pager -p' for prompt mode. Output of the next 
command will get buffered and paged. In ordinary mode ENTER is to 
scroll one line down and SPACE is to view the next page. In prompt 
mode, UP and DOWN are for moving inside the buffer and Q is to quit the
pager prompt mode.
=== modified file 'kernel/Makefile'
--- kernel/Makefile	2013-06-11 14:12:45 +0000
+++ kernel/Makefile	2013-07-25 09:16:42 +0000
@@ -196,6 +196,7 @@
 	generic/src/console/chardev.c \
 	generic/src/console/console.c \
 	generic/src/console/prompt.c \
+	generic/src/console/pager.c \
 	generic/src/cpu/cpu.c \
 	generic/src/ddi/ddi.c \
 	generic/src/ddi/irq.c \

=== modified file 'kernel/generic/include/console/chardev.h'
--- kernel/generic/include/console/chardev.h	2012-04-02 15:52:07 +0000
+++ kernel/generic/include/console/chardev.h	2013-07-26 09:51:32 +0000
@@ -41,6 +41,7 @@
 #include <synch/spinlock.h>
 
 #define INDEV_BUFLEN  512
+#define OUTDEV_BUFLEN 10024
 
 struct indev;
 
@@ -88,6 +89,14 @@
 	/** Fields suitable for multiplexing. */
 	link_t link;
 	list_t list;
+
+	wchar_t buffer[OUTDEV_BUFLEN];
+	size_t counter;
+
+	bool char_buffering_on;
+
+	/** Command that requested the use of buffer. */
+	const char *cmd_buffer_data_source;
 	
 	/** Implementation of outdev operations. */
 	outdev_operations_t *op;
@@ -101,6 +110,7 @@
 
 extern void outdev_initialize(const char *, outdev_t *,
     outdev_operations_t *);
+extern int outdev_push_character(outdev_t *, wchar_t);
 
 extern bool check_poll(indev_t *);
 

=== modified file 'kernel/generic/include/console/console.h'
--- kernel/generic/include/console/console.h	2012-11-23 20:25:27 +0000
+++ kernel/generic/include/console/console.h	2013-07-27 11:00:21 +0000
@@ -52,6 +52,14 @@
 		} \
 	} while (0)
 
+#define PAGER_PAGE_SIZE 30
+#define PAGER_LINE_BUFLEN 1024
+
+extern bool pager_prompt_mode;
+
+extern void pager(outdev_t*, bool prompt_mode);
+wchar_t pager_wait_keypress(outdev_t *outdev, indev_t *indev, const char *prompt);
+
 extern indev_t *stdin;
 extern outdev_t *stdout;
 

=== modified file 'kernel/generic/include/console/prompt.h'
--- kernel/generic/include/console/prompt.h	2012-07-10 12:38:05 +0000
+++ kernel/generic/include/console/prompt.h	2013-07-26 08:20:03 +0000
@@ -41,6 +41,11 @@
 
 extern bool console_prompt_display_all_hints(indev_t *, size_t);
 extern bool console_prompt_more_hints(indev_t *, size_t *);
+void console_prompt_mode(outdev_t *dev);
+int prompt_puts(const wchar_t *string);
+void delete_prompt_message(int length);
+int printpos(int pos, int buffer_length);
+unsigned int update_lineno(outdev_t *dev);
 
 #endif
 

=== modified file 'kernel/generic/src/console/chardev.c'
--- kernel/generic/src/console/chardev.c	2012-04-02 15:52:07 +0000
+++ kernel/generic/src/console/chardev.c	2013-07-26 14:24:34 +0000
@@ -131,12 +131,44 @@
     outdev_operations_t *op)
 {
 	outdev->name = name;
+	outdev->char_buffering_on = false;
+	outdev->cmd_buffer_data_source = NULL;
 	spinlock_initialize(&outdev->lock, "chardev.outdev.lock");
 	link_initialize(&outdev->link);
 	list_initialize(&outdev->list);
+	outdev->counter = 0;
 	outdev->op = op;
 }
 
+/** Push character into character buffer.
+ *
+ * @param outdev Output character device.
+ *
+ * @param ch Character pushed.
+ *
+ * @return -1 on buffer full, 0 on success
+ *
+ */
+int outdev_push_character(outdev_t *outdev, wchar_t ch)
+{
+	ASSERT(outdev);
+	
+	spinlock_lock(&outdev->lock);
+	if (outdev->counter == OUTDEV_BUFLEN - 1) { 
+		/* Buffer full */
+		spinlock_unlock(&outdev->lock);
+		outdev->counter = 0;
+		return -1;
+	}
+	
+	outdev->buffer[outdev->counter] = ch;
+	outdev->counter++;
+
+	spinlock_unlock(&outdev->lock);
+
+	return 0;
+}
+
 bool check_poll(indev_t *indev)
 {
 	if (indev == NULL)

=== modified file 'kernel/generic/src/console/cmd.c'
--- kernel/generic/src/console/cmd.c	2012-11-23 15:52:03 +0000
+++ kernel/generic/src/console/cmd.c	2013-07-26 14:08:32 +0000
@@ -572,6 +572,20 @@
 	.argv = NULL
 };
 
+static int cmd_pager(cmd_arg_t *argv);
+static cmd_arg_t pager_argv = {
+	.type = ARG_TYPE_STRING_OPTIONAL,
+	.buffer = flag_buf,
+	.len = sizeof(flag_buf)
+};
+static cmd_info_t pager_info = {
+	.name = "pager",
+	.description = "Enable paging (use -p for prompt mode).",
+	.func = cmd_pager,
+	.argc = 1,
+	.argv = &pager_argv
+};
+
 static cmd_info_t *basic_commands[] = {
 	&call0_info,
 	&mcall0_info,
@@ -612,6 +626,7 @@
 	&pio_write_8_info,
 	&pio_write_16_info,
 	&pio_write_32_info,
+	&pager_info,
 	NULL
 };
 
@@ -1578,6 +1593,46 @@
 	return 1;
 }
 
+bool pager_prompt_mode = false;
+
+/** Command enabling/disabling paging
+ *
+ * @param argv Ignored
+ *
+ * @return Always 1
+ */
+int cmd_pager(cmd_arg_t *argv)
+{
+	if (str_cmp(flag_buf, "-p") == 0) {
+		if (!stdout->char_buffering_on) {
+			stdout->char_buffering_on = true;
+			pager_prompt_mode = true;
+			printf("Prompt paging enabled for the next command.\n");
+		}
+		else {
+			stdout->char_buffering_on = false;
+			pager_prompt_mode = false;
+			printf("Paging disabled.\n");
+		}
+		return 1;
+	}
+	else if (str_cmp(flag_buf, "") == 0) {
+		if (!stdout->char_buffering_on) {
+			stdout->char_buffering_on = true;
+			printf("Paging enabled for the next command.\n");
+		}
+		else {
+			stdout->char_buffering_on = false;
+			printf("Paging disabled.\n");
+		}
+		return 1;
+	}
+	else
+		printf("Unknown argument \"%s\".\n", flag_buf);
+	
+	return 1;
+}
+
 #endif
 
 /** @}

=== modified file 'kernel/generic/src/console/console.c'
--- kernel/generic/src/console/console.c	2012-12-02 16:42:21 +0000
+++ kernel/generic/src/console/console.c	2013-07-27 09:37:38 +0000
@@ -124,10 +124,19 @@
 
 static void stdout_write(outdev_t *dev, wchar_t ch)
 {
-	list_foreach(dev->list, cur) {
-		outdev_t *sink = list_get_instance(cur, outdev_t, link);
-		if ((sink) && (sink->op->write))
-			sink->op->write(sink, ch);
+	/* Check to see if buffering is on and that there is a command that requested it. */
+	if (dev->char_buffering_on && dev->cmd_buffer_data_source && dev->counter < OUTDEV_BUFLEN) {
+		if (outdev_push_character(dev, ch) == -1) {
+			dev->char_buffering_on = false;
+			printf("[%s] character buffer full", dev->name);
+		}
+	}
+	else {
+		list_foreach(dev->list, cur) {
+			outdev_t *sink = list_get_instance(cur, outdev_t, link);
+			if ((sink) && (sink->op->write))
+				sink->op->write(sink, ch);
+		}
 	}
 }
 

=== modified file 'kernel/generic/src/console/kconsole.c'
--- kernel/generic/src/console/kconsole.c	2012-11-12 01:04:45 +0000
+++ kernel/generic/src/console/kconsole.c	2013-07-26 14:07:19 +0000
@@ -756,8 +756,18 @@
 		cmd_info_t *cmd_info = parse_cmdline(cmdline, STR_BOUNDS(MAX_CMDLINE));
 		if (!cmd_info)
 			continue;
-		
+
+		if (stdout->char_buffering_on && (str_cmp(cmd_info->name, "pager") != 0))
+			/* Request stdout's buffering. */
+			stdout->cmd_buffer_data_source = str_dup(cmd_info->name);
+
 		(void) cmd_info->func(cmd_info->argv);
+
+		if (stdout->char_buffering_on && stdout->cmd_buffer_data_source)
+				pager(stdout, pager_prompt_mode);
+
+		stdout->cmd_buffer_data_source = NULL;
+
 	}
 	free(cmdline);
 }

=== added file 'kernel/generic/src/console/pager.c'
--- kernel/generic/src/console/pager.c	1970-01-01 00:00:00 +0000
+++ kernel/generic/src/console/pager.c	2013-07-26 20:52:37 +0000
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2013 Marin Ramesa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup genericconsole
+ * @{
+ */
+
+/**
+ * @file  pager.c
+ * @brief Console pager.
+ *
+ */
+
+#include <console/console.h>
+#include <console/chardev.h>
+#include <console/prompt.h>
+#include <putchar.h>
+#include <str.h>
+
+/** Pager main routine.
+ *
+ * @param dev - Output
+ * @param prompt_mode - Is the pager in prompt mode?
+ *
+ * @return void
+ */
+void pager(outdev_t *dev, bool prompt_mode)
+{
+	size_t i;
+	unsigned int lineno = 1, pageno = 0, ret;
+	const char *help = "ENTER scroll one line. SPACE next page.";
+	wchar_t ch;
+
+	if (!prompt_mode) {
+
+		dev->char_buffering_on = false;
+
+		for (i = 0; i < dev->counter; i++) {
+			putchar(dev->buffer[i]);
+			if (dev->buffer[i] == '\n')
+				lineno++;
+			if (lineno % PAGER_PAGE_SIZE == 0) {
+				i++;
+				ret = printf("(line %u, page %u) ", lineno, ++pageno);
+				ch = pager_wait_keypress(dev, stdin, help);
+				delete_prompt_message(str_length(help)+ret);
+				/* ENTER */
+				while (ch == '\n') {
+					if (lineno % PAGER_PAGE_SIZE == 0)
+						pageno++; 
+					while (dev->buffer[i] != '\n') {
+						putchar(dev->buffer[i++]);
+						if (i == dev->counter-1) {
+							putchar(dev->buffer[i]); 
+							goto end;
+						}
+					}
+					putchar(dev->buffer[i++]);
+					lineno++;
+					ret = printf("(line %u, page %u) ", lineno, pageno);
+					ch = pager_wait_keypress(dev, stdin, help);
+					delete_prompt_message(str_length(help)+ret);
+				}
+				/* SPACE */
+				if (ch == ' ') {
+					putchar(dev->buffer[i]);
+					lineno++;	
+					continue;
+				}
+			}
+		}
+
+		end:
+		dev->counter = 0;
+
+		return;
+	}
+	else {
+
+		dev->char_buffering_on = false;
+		
+		console_prompt_mode(dev);
+
+		dev->counter = 0;
+
+		pager_prompt_mode = false;		
+
+		return;
+	}
+}
+
+/** Wait for keypress on indev while printing prompt, enable outdev's buffer.
+ *
+ * @param odev - Output
+ * @param idev - Input
+ * @param prompt - Prompt to print.
+ *
+ * @return character in indev
+ */
+wchar_t pager_wait_keypress(outdev_t *odev, indev_t *idev, const char *prompt)
+{
+	wchar_t ch;
+
+	puts(prompt);
+	odev->char_buffering_on = true;
+	ch = indev_pop_character(idev);
+	odev->char_buffering_on = false;
+
+	return ch;
+}
+
+/** @}
+ */

=== modified file 'kernel/generic/src/console/prompt.c'
--- kernel/generic/src/console/prompt.c	2012-07-17 14:55:21 +0000
+++ kernel/generic/src/console/prompt.c	2013-07-27 11:06:14 +0000
@@ -37,6 +37,9 @@
  */
 
 #include <console/prompt.h>
+#include <putchar.h>
+#include <str.h>
+#include <console/console.h>
 
 /** Display the <i>display all possibilities</i> prompt and wait for answer.
  *
@@ -116,5 +119,204 @@
 	return *display_hints > 0;
 }
 
+/** Move through the outdev_t's buffer in prompt mode.
+ *
+ * @param dev - pointer to the structure that holds the output buffer
+ *
+ * @return void
+ */
+void console_prompt_mode(outdev_t *dev)
+{
+	int k = 0, ret;
+	unsigned int lineno = 1;
+	unsigned int i, charno = 0, tmp;
+	wchar_t ch;
+	wchar_t line[PAGER_LINE_BUFLEN];
+
+	/* save buffer counter for later */
+	tmp = dev->counter;
+
+	/* print the whole buffer */
+	for (i = 0; i < dev->counter; i++) {
+		putchar(dev->buffer[i]);
+		if (dev->buffer[i] == '\n')
+			lineno++;
+	}
+
+	printf("DOWN and UP to move. Q to quit.\n");
+
+	ret = printpos(k, lineno);
+
+	/* print the first line */
+	do {
+		if (charno < dev->counter) {
+			line[charno] = dev->buffer[charno];
+			if (charno == PAGER_LINE_BUFLEN-1) {
+				printf("Line buffer full.\n");
+				break;
+			}
+			charno++;
+		}
+		else
+			goto end;
+	} while (dev->buffer[charno] != '\n');
+	line[charno] = '\0';
+	prompt_puts(line);
+
+	/* wait for keypress */
+	dev->char_buffering_on = true;
+	ch = indev_pop_character(stdin);
+	dev->char_buffering_on = false;
+
+	while ((ch != 'q') && (ch != 'Q')) {
+		i = 0; charno++;
+		if (ch == U_DOWN_ARROW && charno != dev->counter-1) {
+			delete_prompt_message(wstr_length(line)+ret);
+			/* save the next line */
+			do {
+				if (charno < dev->counter) {
+					line[i] = dev->buffer[charno];
+					if (i == PAGER_LINE_BUFLEN-1) {
+						printf("Line buffer full.\n");
+						break;
+					}
+					charno++; i++;
+				}
+				else
+					goto end;
+			} while (dev->buffer[charno] != '\n');
+			/* update position in the buffer and print the line */
+			k++;  
+			ret = printpos(k, update_lineno(dev));
+			line[i] = '\0';
+			prompt_puts(line);
+			i = 0;
+		}
+		if (ch == U_UP_ARROW && k > 1) {
+			delete_prompt_message(wstr_length(line)+ret);
+			
+			/* trace back two newline characters 
+			 * if we're at the begining of the buffer
+			 * do nothing  
+			 */
+			if (charno != 0)
+				charno--;
+			int tmp_lineno = 0;
+			do {
+				if (charno != 0)
+					charno--;
+				if (dev->buffer[charno] == '\n')
+					tmp_lineno++;
+			} while (tmp_lineno < 2);
+			/* save next line */
+			do {
+				if (charno != 0)
+					charno++;
+				if (i == PAGER_LINE_BUFLEN-1) {
+					printf("Line buffer full.\n");
+					break;
+				}
+				line[i] = dev->buffer[charno];
+				i++; 
+			} while (dev->buffer[charno] != '\n');
+			/* update position in the buffer and print the line */
+			k--; 
+			ret = printpos(k, update_lineno(dev));			
+			line[i] = '\0';
+			prompt_puts(line);
+			i = 0;
+		}
+		/* wait for keypress */
+		dev->char_buffering_on = true;
+		ch = indev_pop_character(stdin);
+		dev->char_buffering_on = false;
+	}
+	end:
+	delete_prompt_message(wstr_length(line)+ret);
+	
+	/* Print messages that were buffered during indev_pop_character() */
+	while (tmp < dev->counter) {
+		putchar(dev->buffer[tmp]);
+		tmp++;
+	}
+}
+
+/** Prompt puts. Puts that ignores the last newline 
+ *  before the null character, if it's present.
+ *
+ * @param string
+ *
+ * @return number of characters printed
+ */
+int prompt_puts(const wchar_t *string)
+{
+	int i;
+
+	for (i = 0; string[i] != '\0'; i++) { 
+		if (string[i+1] == '\0' && string[i] == '\n') {
+			; /* do nothing */
+		}
+		else
+			putchar(string[i]);			
+	}
+
+	return i;
+}
+
+/** Delete prompt message from the console.
+ *  (TODO Doesn't work for long messages that are broken
+ *  in multiple lines on the output.)
+ *
+ * @param length of the string
+ *
+ * @return void
+ */
+void delete_prompt_message(int length)
+{
+	int i;
+
+	putchar('\r');
+	for (i = 0; i < length; i++)
+		putchar(' ');
+	putchar('\r');
+	
+	return;
+}
+
+/** Print the position in the buffer.
+ *
+ * @param pos - current position in the buffer
+ *
+ * @return number of printed characters
+ */
+int printpos(int pos, int buffer_length)
+{
+	int ret[3];
+
+	ret[0] = printf("(%d/", pos+1);
+	ret[1] = printf("%d ", buffer_length);
+	ret[2] = printf("%d%%) ", (int)((double)(pos+1)/(double)(buffer_length)*100.));
+
+	return ret[0]+ret[1]+ret[2];	
+}
+
+/** Update number of lines in the buffer.
+ *
+ * @param dev - Output
+ *
+ * @return number of lines in the buffer
+ */
+unsigned int update_lineno(outdev_t *dev)
+{
+	unsigned int i, lineno = 1;
+
+	for (i = 0; i < dev->counter; i++) {
+		if (dev->buffer[i] == '\n')
+			lineno++;
+	}
+
+	return lineno;
+}
+
 /** @}
  */

_______________________________________________
HelenOS-devel mailing list
[email protected]
http://lists.modry.cz/listinfo/helenos-devel

Reply via email to