On 02.08.2013 18:22:34, Vojtech Horky wrote:
> The code formatting looks okay to me, except we do not start "else"
> on a separate line.
I fixed that.
> The patch does not compile on all architectures (you can use
> ./tools/check.sh to compile HelenOS for all supported targets) - it
> seems to me that the percentage computation in the scroll mode is at
> fault. We do not have floating point numbers in kernel.
I removed the percentage computation and it still fails. I then removed
all of my patch from the current mainline revision and it still fails
on most architectures. Are you sure that the compile errors were not
there before my patch? If yes, then I don't know what am I doing wrong.
> I was not able to use the pager for some commands. For example,
> running pager followed by test slab2 would not invoke the pager
> correctly (in QEMU on ia32).
It was a problem with a static buffer size. I changed it to dynamic, it
should work now.
> Maybe your approach is too complex because you try to solve more
> problems all at once. For example, the up/down scrolling is a very
> nice feature but for the kernel console, the paging alone could be
> enough. Especially if it works for any command issued.
I removed the scroll/prompt mode. It does not work with long lines
anyway.
I'm attaching the new patch with dynamic buffer allocation. Maybe it
will be useful, or maybe someone will see where are the errors when
compiling for other architectures. I don't know how to fix that.
=== modified file 'kernel/Makefile'
--- kernel/Makefile 2013-06-11 14:12:45 +0000
+++ kernel/Makefile 2013-08-05 06:17:19 +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-08-05 08:18:33 +0000
@@ -88,6 +88,15 @@
/** Fields suitable for multiplexing. */
link_t link;
list_t list;
+
+ wchar_t *buffer;
+ unsigned int 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-08-05 06:17:19 +0000
@@ -52,6 +52,12 @@
} \
} while (0)
+#define PAGER_PAGE_SIZE 30
+
+extern void pager(outdev_t *outdev);
+wchar_t pager_wait_keypress(outdev_t *outdev, indev_t *indev, const char *prompt);
+void delete_prompt_message(int length);
+
extern indev_t *stdin;
extern outdev_t *stdout;
=== 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-08-05 06:17:19 +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->buflen = 512;
+ 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);
+ 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-08-05 06:17:19 +0000
@@ -572,6 +572,15 @@
.argv = NULL
};
+static int cmd_pager(cmd_arg_t *argv);
+static cmd_info_t pager_info = {
+ .name = "pager",
+ .description = "Enable/disable paging.",
+ .func = cmd_pager,
+ .argc = 0,
+ .argv = NULL
+};
+
static cmd_info_t *basic_commands[] = {
&call0_info,
&mcall0_info,
@@ -612,6 +621,7 @@
&pio_write_8_info,
&pio_write_16_info,
&pio_write_32_info,
+ &pager_info,
NULL
};
@@ -1578,6 +1588,32 @@
return 1;
}
+/** Command enabling/disabling paging
+ *
+ * @param argv Ignored
+ *
+ * @return 1 on success, 0 on failure
+ */
+int cmd_pager(cmd_arg_t *argv)
+{
+
+ if (!stdout->char_buffering_on) {
+ stdout->buffer = (wchar_t *) malloc(sizeof(wchar_t) * stdout->buflen, 0);
+ if (stdout->buffer == NULL) {
+ printf("Error allocating memory for the output buffer.\n");
+ return 0;
+ }
+ stdout->char_buffering_on = true;
+ printf("Paging enabled for the next command.\n");
+ } else {
+ stdout->char_buffering_on = false;
+ printf("Paging disabled.\n");
+ free(stdout->buffer);
+ }
+
+ 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-08-05 08:29:35 +0000
@@ -122,12 +122,33 @@
list_append(&outdev->link, &stdout->list);
}
+static unsigned int stdout_buffer_allocs = 1;
+
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) {
+ /* If the buffer is full. */
+ if (outdev_push_character(dev, ch) == -1) {
+ dev->buflen *= ++stdout_buffer_allocs;
+ dev->buffer = (wchar_t *) realloc(dev->buffer,
+ sizeof(wchar_t) * dev->buflen, 0);
+ if (dev->buffer == NULL) {
+ dev->char_buffering_on = false;
+ printf("[%s] error allocating memory for the output buffer.\n", dev->name);
+ /* Repeat unbuffered. */
+ stdout_write(dev, ch);
+ return;
+ }
+ /* Push the failed character into buffer again. */
+ outdev_push_character(dev, ch);
+ }
+ } 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-08-05 06:17:19 +0000
@@ -756,8 +756,20 @@
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. */
+ printf("Buffering standard output ... \n");
+ 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);
+
+ 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-08-05 06:53:10 +0000
@@ -0,0 +1,144 @@
+/*
+ * 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 <putchar.h>
+#include <str.h>
+#include <mm/slab.h>
+
+/** Pager main routine.
+ *
+ * @param dev - Output
+ *
+ * @return void
+ */
+void pager(outdev_t *dev)
+{
+ size_t i;
+ unsigned int lineno = 1, pageno = 0, ret;
+ const char *help = "ENTER scroll one line. SPACE next page.";
+ wchar_t ch;
+
+ 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;
+ free(dev->buffer);
+ dev->buflen = 512;
+
+ 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;
+}
+
+/** 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;
+}
+
+/** @}
+ */
_______________________________________________
HelenOS-devel mailing list
[email protected]
http://lists.modry.cz/listinfo/helenos-devel