New applet "less", from Rob Sullivan.
diff --git a/miscutils/Config.in b/miscutils/Config.in
index 1f14d21..a104880 100644
--- a/miscutils/Config.in
+++ b/miscutils/Config.in
@@ -104,6 +104,53 @@
 	help
 	  'last' displays a list of the last users that logged into the system.
 
+config CONFIG_LESS
+	bool "less"
+	default n
+	help
+	  'less' is a pager, meaning that it displays text files. It possesses
+	  a wide array of features, and is an improvement over 'more'.
+
+config CONFIG_FEATURE_LESS_BRACKETS
+	bool "  Enable bracket searching"
+	default y
+	depends on CONFIG_LESS
+	help
+	  This option adds the capability to search for matching left and right
+	  brackets, facilitating programming.
+
+config CONFIG_FEATURE_LESS_FLAGS
+	bool "  Enable extra flags"
+	default y
+	depends on CONFIG_LESS
+	help
+	  The extra flags provided do the following:
+
+	  The -M flag enables a more sophisticated status line.
+	  The -m flag enables a simpler status line with a percentage.
+
+config CONFIG_FEATURE_LESS_FLAGCS
+	bool "  Enable flag changes"
+	default n
+	depends on CONFIG_LESS
+	help
+	  This enables the ability to change command-line flags within
+	  less itself.
+
+config CONFIG_FEATURE_LESS_MARKS
+	bool "  Enable marks"
+	default n
+	depends on CONFIG_LESS
+	help
+	  Marks enable positions in a file to be stored for easy reference.
+
+config CONFIG_FEATURE_LESS_REGEXP
+	bool "  Enable regular expressions"
+	default n
+	depends on CONFIG_LESS
+	help
+	  Enable regular expressions, allowing complex file searches.
+
 config CONFIG_HDPARM
 	bool "hdparm"
 	default n
diff --git a/miscutils/Makefile.in b/miscutils/Makefile.in
index ee1cc75..ba8069c 100644
--- a/miscutils/Makefile.in
+++ b/miscutils/Makefile.in
@@ -33,6 +33,7 @@
 MISCUTILS-$(CONFIG_EJECT)       += eject.o
 MISCUTILS-$(CONFIG_HDPARM)      += hdparm.o
 MISCUTILS-$(CONFIG_LAST)        += last.o
+MISCUTILS-${CONFIG_LESS}	+= less.o
 MISCUTILS-$(CONFIG_MAKEDEVS)    += makedevs.o
 MISCUTILS-$(CONFIG_MOUNTPOINT)	+= mountpoint.o
 MISCUTILS-$(CONFIG_MT)          += mt.o
diff --git a/miscutils/less.c b/miscutils/less.c
new file mode 100644
index 0000000..43bc673
--- /dev/null
+++ b/miscutils/less.c
@@ -0,0 +1,1262 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini less implementation for busybox
+ *
+ *
+ * Copyright (C) 2005 by Rob Sullivan <cogito.ergo.cogito@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ *
+ *	This program needs a lot of development, so consider it in a beta stage
+ *	at best.
+ *
+ *	TODO: 
+ *	- Add more regular expression support - search modifiers, certain matches, etc.
+ *	- Add more complex bracket searching - currently, nested brackets are
+ *	not considered.
+ *	- Add support for "F" as an input. This causes less to act in
+ *	a similar way to tail -f.
+ *	- Check for binary files, and prompt the user if a binary file
+ *	is detected.
+ *	- Allow horizontal scrolling. Currently, lines simply continue onto
+ *	the next line, per the terminal's discretion
+ *
+ *	Notes:
+ *	- filename is an array and not a pointer because that avoids all sorts
+ *	of complications involving the fact that something that is pointed to
+ *	will be changed if the pointer is changed.
+ *	- the inp file pointer is used so that keyboard input works after
+ *	redirected input has been read from stdin
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <regex.h>
+#include <ctype.h>
+#include "busybox.h"
+
+/* These are the escape sequences corresponding to special keys */
+#define REAL_KEY_UP 'A'
+#define REAL_KEY_DOWN 'B'
+#define REAL_KEY_RIGHT 'C'
+#define REAL_KEY_LEFT 'D'
+#define REAL_PAGE_UP '5'
+#define REAL_PAGE_DOWN '6'
+
+/* These are the special codes assigned by this program to the special keys */
+#define PAGE_UP 20
+#define PAGE_DOWN 21
+#define KEY_UP 22
+#define KEY_DOWN 23
+#define KEY_RIGHT 24
+#define KEY_LEFT 25
+
+/* The escape codes for highlighted and normal text */
+#define HIGHLIGHT "\033[7m"
+#define NORMAL "\033[0m"
+
+/* The escape code to clear the screen */
+#define CLEAR "\033[2J"
+
+/* Maximum number of lines in a file */
+#define MAXLINES 10000
+
+/* Get height and width of terminal */
+#define tty_width_height()		get_terminal_width_height(0, &width, &height)
+
+/* Function prototypes */
+static void set_tty_cooked(void);
+static void set_tty_raw(void);
+static void tless_exit(int code);
+static int tless_getch(void);
+static void move_cursor(int x, int y);
+static void clear_line(void);
+static void data_readlines(void);
+static void free_flines(void);
+#ifdef CONFIG_FEATURE_LESS_FLAGS
+static int calc_percent(void);
+#endif
+static int reverse_percent(int percentage);
+#ifdef CONFIG_FEATURE_LESS_FLAGS
+static void m_status_print(void);
+static void medium_status_print(void);
+#endif
+static void status_print(void);
+static void buffer_print(void);
+static void buffer_init(void);
+static void buffer_down(int nlines);
+static void buffer_up(int nlines);
+static void buffer_line(int linenum);
+static void keypress_process(int keypress);
+static void colon_process(void);
+static void number_process(int first_digit);
+#ifdef CONFIG_FEATURE_LESS_FLAGCS
+static void flag_change(void);
+static void show_flag_status(void);
+#endif
+static void examine_file(void);
+static void next_file(void);
+static void previous_file(void);
+static void first_file(void);
+static void remove_current_file(void);
+static void full_repaint(void);
+static void add_linenumbers(void);
+static void save_input_to_file(void);
+#ifdef CONFIG_FEATURE_LESS_MARKS
+static void	add_mark(void);
+static void goto_mark(void);
+#endif
+#ifdef CONFIG_FEATURE_LESS_REGEXP
+static void regex_process(void);
+char *process_regex_on_line(char *line, regex_t *pattern);
+char *insert_highlights(char *line, int start, int end);
+static void goto_match (int match);
+static void search_backwards(void);
+#endif
+#ifdef CONFIG_FEATURE_LESS_BRACKETS
+static char opp_bracket (char bracket);
+static void match_right_bracket (char bracket);
+static void match_left_bracket (char bracket);
+#endif
+int less_main(int argc, char *argv[]);
+
+static int height;
+static int width;
+static char **files;
+static char filename[256];
+static char buffer[100][256];
+static char *flines[MAXLINES];
+static int current_file = 1;
+static int line_pos = 0;
+static int num_flines;
+static int num_files = 1;
+static int past_eof = 0;
+
+/* Command line options */
+static int E_FLAG = 0;
+static int M_FLAG = 0;
+static int N_FLAG = 0;
+static int m_FLAG = 0;
+static int TILDE_FLAG = 0;
+
+/* This is needed so that program behaviour changes when input comes from
+   stdin */
+static int inp_stdin = 0;
+/* This is required so that when a file is requested to be examined after
+   input has come from stdin (e.g. dmesg | less), the input stream from 
+   the keyboard still stays the same. If it switched back to stdin, keyboard
+   input wouldn't work. */
+static int ea_inp_stdin = 0;
+
+#ifdef CONFIG_FEATURE_LESS_MARKS
+static int mark_lines[15][2];
+static int num_marks = 0;
+#endif
+
+#ifdef CONFIG_FEATURE_LESS_REGEXP
+static int match_found = 0;
+static int match_lines[100];
+static int match_pos = 0;
+static int num_matches = 0;
+static int match_backwards = 0;
+static int num_back_match = 1;
+#endif
+
+/* Needed termios structures */
+static struct termios term_orig, term_vi;
+
+/* File pointer to get input from */
+static FILE *inp;
+
+/* Reset terminal input to normal */
+static void set_tty_cooked() {
+        fflush(stdout);
+        tcsetattr(0, TCSANOW, &term_orig);
+}
+
+/* Set terminal input to raw mode */
+static void set_tty_raw() {
+       tcgetattr(0, &term_orig);
+        term_vi = term_orig;
+        term_vi.c_lflag &= (~ICANON & ~ECHO); 
+        term_vi.c_iflag &= (~IXON & ~ICRNL);
+        term_vi.c_oflag &= (~ONLCR);
+        term_vi.c_cc[VMIN] = 1;
+        term_vi.c_cc[VTIME] = 0;
+        tcsetattr(0, TCSANOW, &term_vi);
+}
+
+/* Exit the program gracefully */
+static void tless_exit(int code) {
+	
+	/* TODO: We really should save the terminal state when we start,
+	 	 and restore it when we exit. Less does this with the
+		 "ti" and "te" termcap commands; can this be done with
+		 only termios.h? */
+	
+	putchar('\n');
+	exit(code);
+}
+
+/* Grab a character from input without requiring the return key. If the
+   character is ASCII \033, get more characters and assign certain sequences
+   special return codes. Note that this function works best with raw input. */ 
+int tless_getch() {
+	
+	set_tty_raw();
+	char input_key[3];
+	
+	input_key[0] = getc(inp);
+	/* Detect escape sequences (i.e. arrow keys) and handle
+	   them accordingly */
+	
+	if (input_key[0] == '\033') {
+		input_key[1] = getc(inp);
+		input_key[2] = getc(inp);
+		set_tty_cooked();
+		if (input_key[1] == '[') {
+			if (input_key[2] == REAL_KEY_UP)
+				return KEY_UP;
+			else if (input_key[2] == REAL_KEY_DOWN)
+				return KEY_DOWN;
+			else if (input_key[2] == REAL_KEY_RIGHT)
+				return KEY_RIGHT;
+			else if (input_key[2] == REAL_KEY_LEFT)
+				return KEY_LEFT;
+			else if (input_key[2] == REAL_PAGE_UP)
+				return PAGE_UP;
+			else if (input_key[2] == REAL_PAGE_DOWN)
+				return PAGE_DOWN;
+		}
+	}
+	/* The input is a normal ASCII value */
+	else {
+		set_tty_cooked();
+		return input_key[0];
+	}
+	return 0;
+}
+
+/* Move the cursor to a position (x,y), where (0,0) is the 
+   top-left corner of the console */
+static void move_cursor(int x, int y) {
+	printf("\033[%i;%iH", x, y);
+}
+
+static void clear_line() {
+	move_cursor(height, 0);
+	printf("\033[K");
+}
+
+static void data_readlines() {
+	
+	int i;
+	char current_line[256];
+	FILE *fp;
+	
+	fp = (inp_stdin) ? stdin : bb_xfopen(filename, "rt");
+	
+	for (i = 0; (!feof(fp)) && (i <= MAXLINES); i++) {
+		strcpy(current_line, "");
+		fgets(current_line, 256, fp);
+		bb_xferror(fp, filename);
+		flines[i] = (char *) bb_xstrndup(current_line, (strlen(current_line) + 1) * sizeof(char));
+	}
+	num_flines = i - 2;
+
+/* Reset variables for a new file */
+	
+	line_pos = 0;
+	past_eof = 0;
+	
+	fclose(fp);
+
+	if (inp_stdin)
+		inp = fopen(CURRENT_TTY, "r");
+	else
+		inp = stdin;
+
+	if (ea_inp_stdin) {
+		fclose(inp);
+		inp = fopen(CURRENT_TTY, "r");
+	}
+	
+	if (N_FLAG)
+		add_linenumbers();
+}
+
+/* Free the file data */
+static void free_flines() {
+	
+	int i;
+	
+	for (i = 0; i <= num_flines; i++)
+		free(flines[i]);
+}
+
+#ifdef CONFIG_FEATURE_LESS_FLAGS
+/* Calculate the percentage the current line position is through the file */
+int calc_percent() {
+	return ((100 * (line_pos + height - 2) / num_flines) + 1);
+}
+#endif
+
+/* Turn a percentage into a line number */
+int reverse_percent(int percentage) {
+	double linenum = percentage;
+	linenum = ((linenum / 100) * num_flines) - 1;
+	return(linenum);
+}
+
+#ifdef CONFIG_FEATURE_LESS_FLAGS
+/* Print a status line if -M was specified */
+static void m_status_print() {
+
+	int percentage;
+	
+	if (!past_eof) {
+		if (!line_pos) {
+			if (num_files > 1)
+				printf("%s%s %s%i%s%i%s%i-%i/%i ", HIGHLIGHT, filename, "(file ", current_file, " of ", num_files, ") lines ", line_pos + 1, line_pos + height - 1, num_flines + 1);
+			else {
+				printf("%s%s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
+			}
+		}
+		else {
+			printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
+		}
+	
+		if (line_pos == num_flines - height + 2) {
+			printf("(END) %s", NORMAL);
+			if ((num_files > 1) && (current_file != num_files))
+				printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL);
+		}
+		else {
+			percentage = calc_percent();
+			printf("%i%s %s", percentage, "%", NORMAL);
+		}
+	}
+	else {
+		printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename, line_pos + 1, num_flines + 1, num_flines + 1);
+		if ((num_files > 1) && (current_file != num_files))
+			printf("- Next: %s", files[current_file]);
+		printf("%s", NORMAL);
+	}
+}
+
+/* Print a status line if -m was specified */
+static void medium_status_print() {
+
+	int percentage;
+	percentage = calc_percent();
+	
+	if (!line_pos)
+		printf("%s%s %i%s%s", HIGHLIGHT, filename, percentage, "%", NORMAL);
+	else if (line_pos == num_flines - height + 2)
+		printf("%s(END)%s", HIGHLIGHT, NORMAL);
+	else
+		printf("%s%i%s%s", HIGHLIGHT, percentage, "%", NORMAL);
+}
+#endif
+
+/* Print the status line */
+static void status_print() {
+	
+	/* Change the status if flags have been set */
+#ifdef CONFIG_FEATURE_LESS_FLAGS	
+	if (M_FLAG)	
+		m_status_print();
+	else if (m_FLAG)
+		medium_status_print();
+	/* No flags set */
+	else {
+#endif
+		if (!line_pos) {
+			printf("%s%s %s", HIGHLIGHT, filename, NORMAL);
+			if (num_files > 1)
+				printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ", current_file, " of ", num_files, ")", NORMAL);
+		}
+		else if (line_pos == num_flines - height + 2) {
+			printf("%s%s %s", HIGHLIGHT, "(END)", NORMAL);
+			if ((num_files > 1) && (current_file != num_files))
+				printf("%s%s%s%s", HIGHLIGHT, "- Next: ", files[current_file], NORMAL);
+		}
+		else {
+			printf("%c", ':');
+		}
+#ifdef CONFIG_FEATURE_LESS_FLAGS
+	}
+#endif
+}
+
+/* Print the buffer */
+static void buffer_print() {
+	
+	int i;
+	
+	if (num_flines >= height - 2) {
+		printf("%s", CLEAR);
+		move_cursor(0,0);
+		for (i = 0; i < height - 1; i++)
+			printf("%s", buffer[i]);
+		status_print();
+	}
+	else {
+		printf("%s", CLEAR);
+		move_cursor(0,0);
+		for (i = 1; i < (height - 1 - num_flines); i++)
+			putchar('\n');
+		for (i = 0; i < height - 1; i++)
+			printf("%s", buffer[i]);
+		status_print();
+	}
+}
+
+/* Initialise the buffer */
+static void buffer_init() {
+	
+	int i;
+	
+	for (i = 0; i < (height - 1); i++)
+		memset(buffer[i], '\0', 256);
+	
+	/* Fill the buffer until the end of the file or the 
+	   end of the buffer is reached */
+	for (i = 0; (i < (height - 1)) && (i <= num_flines); i++) {
+		strcpy(buffer[i], flines[i]);
+	}
+	
+	/* If the buffer still isn't full, fill it with blank lines */
+	for (; i < (height - 1); i++) {
+		strcpy(buffer[i], "");
+	}
+}
+
+/* Move the buffer up and down in the file in order to scroll */
+static void buffer_down(int nlines) {
+	
+	int i;
+	
+	if (!past_eof) {
+		if (line_pos + (height - 3) + nlines < num_flines) {
+			line_pos += nlines;
+			for (i = 0; i < (height - 1); i++)
+				strcpy(buffer[i], flines[line_pos + i]);
+		}
+		else {
+			/* As the number of lines requested was too large, we just move
+		   	to the end of the file */
+			while (line_pos + (height - 3) + 1 < num_flines) {	
+				line_pos += 1;
+				for (i = 0; i < (height - 1); i++)
+					strcpy(buffer[i], flines[line_pos + i]);
+			}
+		}
+
+		/* We exit if the -E flag has been set */
+		if (E_FLAG && (line_pos + (height - 2) == num_flines))
+			tless_exit(0);
+	}
+}
+
+static void buffer_up(int nlines) {
+	
+	int i;
+	int tilde_line;
+	
+	if (!past_eof) {
+		if (line_pos - nlines >= 0) {
+			line_pos -= nlines;
+			for (i = 0; i < (height - 1); i++)
+				strcpy(buffer[i], flines[line_pos + i]);
+		}
+		else {
+		/* As the requested number of lines to move was too large, we
+		   move one line up at a time until we can't. */
+			while (line_pos != 0) {
+				line_pos -= 1;
+				for (i = 0; i < (height - 1); i++)
+					strcpy(buffer[i], flines[line_pos + i]);
+			}
+		}
+	}
+	else {
+		/* Work out where the tildes start */
+		tilde_line = num_flines - line_pos + 3;
+		
+		line_pos -= nlines;
+		/* Going backwards nlines lines has taken us to a point where
+		   nothing is past the EOF, so we revert to normal. */
+		if (line_pos < num_flines - height + 3) {
+			past_eof = 0;
+			buffer_up(nlines);
+		}
+		else {
+			/* We only move part of the buffer, as the rest
+		   	is past the EOF */
+			for (i = 0; i < (height - 1); i++) {
+				if (i < tilde_line - nlines + 1)
+					strcpy(buffer[i], flines[line_pos + i]);
+				else {
+					if (line_pos >= num_flines - height + 2)
+						strcpy(buffer[i], "~\n");
+				}
+			}
+		}		
+	}
+}
+
+static void buffer_line(int linenum) {
+	
+	int i;
+
+	past_eof = 0;
+
+	if (linenum < 1 || linenum > num_flines) {
+		clear_line();
+		printf("%s%s%i%s", HIGHLIGHT, "Cannot seek to line number ", linenum, NORMAL);
+	}
+	else if (linenum < (num_flines - height - 2)) {
+		for (i = 0; i < (height - 1); i++)
+			strcpy(buffer[i], flines[linenum + i]);
+		line_pos = linenum;
+	}
+	else {
+		for (i = 0; i < (height - 1); i++) {
+			if (linenum + i < num_flines + 2)
+				strcpy(buffer[i], flines[linenum + i]);
+			else
+				strcpy(buffer[i], (TILDE_FLAG) ? "\n" : "~\n");
+		}
+		line_pos = linenum;
+		/* Set past_eof so buffer_down and buffer_up act differently */
+		past_eof = 1;
+	}
+}
+
+static void keypress_process(int keypress) {
+	switch (keypress) {
+		case KEY_DOWN: case 'e': case 'j': case '\015':
+			buffer_down(1);
+			buffer_print();
+			break;
+		case KEY_UP: case 'y': case 'k':
+			buffer_up(1);
+			buffer_print();
+			break;
+		case PAGE_DOWN: case ' ': case 'z':
+			buffer_down(height - 1);
+			buffer_print();
+			break;
+		case PAGE_UP: case 'w': case 'b':
+			buffer_up(height - 1);
+			buffer_print();
+			break;
+		case 'd':
+			buffer_down((height - 1) / 2);
+			buffer_print();
+			break;
+		case 'u':
+			buffer_up((height - 1) / 2);
+			buffer_print();
+			break;
+		case 'g': case 'p': case '<': case '%':
+			buffer_up(num_flines + 1);
+			buffer_print();
+			break;
+		case 'G': case '>':
+			buffer_down(num_flines + 1);
+			buffer_print();
+			break;
+		case 'q': case 'Q':
+			tless_exit(0);
+			break;
+#ifdef CONFIG_FEATURE_LESS_MARKS
+		case 'm':
+			add_mark();
+			buffer_print();
+			break;
+		case '\'':
+			goto_mark();
+			buffer_print();
+			break;
+#endif
+		case 'r':
+			buffer_print();
+			break;
+		case 'R':
+			full_repaint();
+			break;
+		case 's':
+			if (inp_stdin)
+				save_input_to_file();
+			break;
+		case 'E':
+			examine_file();
+			break;
+#ifdef CONFIG_FEATURE_LESS_FLAGS
+		case '=':
+			clear_line();
+			m_status_print();
+			break;
+#endif
+#ifdef CONFIG_FEATURE_LESS_REGEXP
+		case '/':
+			regex_process();
+			buffer_print();
+			break;
+		case 'n':
+			goto_match(match_pos + 1);
+			buffer_print();
+			break;
+		case 'N':
+			goto_match(match_pos - 1);
+			buffer_print();
+			break;
+		case '?':
+			search_backwards();
+			buffer_print();
+			break;
+#endif
+#ifdef CONFIG_FEATURE_LESS_FLAGCS
+		case '-':
+			flag_change();
+			buffer_print();
+			break;
+		case '_':
+			show_flag_status();
+			break;
+#endif
+#ifdef CONFIG_FEATURE_LESS_BRACKETS
+		case '{': case '(': case '[':
+			match_right_bracket(keypress);
+			break;
+		case '}': case ')': case ']':
+			match_left_bracket(keypress);
+			break;
+#endif			
+		case ':': 
+			colon_process();
+			break;
+		default:
+			break;
+	}
+	if (isdigit(keypress))
+		number_process(keypress);
+}
+
+static void colon_process() {
+	
+	int keypress;
+	
+	/* Clear the current line and print a prompt */
+	clear_line();
+	printf(" :");
+	
+	keypress = tless_getch();
+	switch (keypress) {
+		case 'd':
+			remove_current_file();
+			break;
+		case 'e':
+			examine_file();
+			break;
+#ifdef CONFIG_FEATURE_LESS_FLAGS
+		case 'f':
+			clear_line();
+			m_status_print();
+			break;
+#endif
+		case 'n':
+			next_file();
+			break;
+		case 'p':
+			previous_file();
+			break;
+		case 'q':
+			tless_exit(0);
+			break;
+		case 'x':
+			first_file();
+			break;
+		default:
+			break;
+	}
+}
+
+static void number_process(int first_digit) {
+	
+	int i = 1;
+	int num;
+	char num_input[80];
+	char keypress;
+	num_input[0] = first_digit;
+	
+	/* Clear the current line, print a prompt, and then print the digit */
+	clear_line();
+	printf(":%c", first_digit);
+	
+	/* Receive input until a letter is given */
+	while((num_input[i] = tless_getch()) && isdigit(num_input[i])) {
+		printf("%c",num_input[i]);
+		i++;
+	}
+	
+	/* Take the final letter out of the digits string */
+	keypress = num_input[i];
+	num_input[i] = '\0';
+	i--;
+	num = atoi(num_input);
+
+	/* We now know the number and the letter entered, so we process them */
+	switch (keypress) {
+		case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
+			buffer_down(num);
+			buffer_print();
+			break;
+		case KEY_UP: case 'b': case 'w': case 'y': case 'u':
+			buffer_up(num);
+			buffer_print();
+			break;
+		case 'g': case '<': case 'G': case '>':
+			if (num_flines >= height - 2)
+				buffer_line(num - 1);
+			buffer_print();
+			break;
+		case 'p': case '%':
+			buffer_line(reverse_percent(num));
+			buffer_print();
+			break;
+#ifdef CONFIG_FEATURE_LESS_REGEXP
+		case 'n':
+			goto_match(match_pos + num - 1);
+			buffer_print();
+			break;
+		case '/':
+			regex_process();
+			goto_match(num - 1);
+			buffer_print();
+			break;
+		case '?':
+			num_back_match = num;
+			search_backwards();
+			buffer_print();
+			break;
+#endif
+		default:
+			break;
+	}
+}
+
+#ifdef CONFIG_FEATURE_LESS_FLAGCS
+static void flag_change() {
+	
+	int keypress;
+	
+	clear_line();
+	printf("-");
+	keypress = tless_getch();
+	
+	switch (keypress) {
+		case 'M':
+			M_FLAG = !M_FLAG;
+			break;
+		case 'm':
+			m_FLAG = !m_FLAG;
+			break;
+		case 'E':
+			E_FLAG = !E_FLAG;
+			break;
+		case '~':
+			TILDE_FLAG = !TILDE_FLAG;
+			break;
+		default:
+			break;
+	}
+}
+
+static void show_flag_status() {
+	
+	int keypress;
+	int flag_val;
+	
+	clear_line();
+	printf("_");
+	keypress = tless_getch();
+
+	switch (keypress) {
+		case 'M':
+			flag_val = M_FLAG;
+			break;
+		case 'm':
+			flag_val = m_FLAG;
+			break;
+		case '~':
+			flag_val = TILDE_FLAG;
+			break;
+		case 'N':
+			flag_val = N_FLAG;
+			break;
+		case 'E':
+			flag_val = E_FLAG;
+			break;
+		default:
+			flag_val = 0;
+			break;
+	}
+	
+	clear_line();
+	printf("%s%s%i%s", HIGHLIGHT, "The status of the flag is: ", flag_val, NORMAL);
+}
+#endif
+
+static void examine_file() {
+
+	int newline_offset;
+	
+	clear_line();
+	printf("Examine: ");
+	fgets(filename, 256, inp);
+	
+	/* As fgets adds a newline to the end of an input string, we
+	   need to remove it */
+	newline_offset = strlen(filename) - 1;
+	filename[newline_offset] = '\0';
+	
+	files[num_files] = bb_xstrndup(filename, (strlen(filename) + 1) * sizeof(char));
+	current_file = num_files + 1;
+	num_files++;	
+
+	inp_stdin = 0;
+	ea_inp_stdin = 1;
+	free_flines();
+	data_readlines();
+	buffer_init();
+	buffer_print();
+}
+
+static void next_file() {
+	if (current_file != num_files) {
+		current_file++;
+		strcpy(filename, files[current_file - 1]);
+		free_flines();
+		data_readlines();
+		buffer_init();
+		buffer_print();
+	}
+	else {
+		clear_line();
+		printf("%s%s%s", HIGHLIGHT, "No next file", NORMAL);
+	}
+}
+
+static void previous_file() {
+	if (current_file != 1) {
+		current_file--;
+		strcpy(filename, files[current_file - 1]);
+		
+		free_flines();
+		data_readlines();
+		buffer_init();
+		buffer_print();
+	}
+	else {
+		clear_line();
+		printf("%s%s%s", HIGHLIGHT, "No previous file", NORMAL);
+	}
+}
+
+static void first_file() {
+	if (current_file != 1) {
+		current_file = 1;
+		strcpy(filename, files[current_file - 1]);
+		free_flines();
+		data_readlines();
+		buffer_init();
+		buffer_print();
+	}
+}
+
+static void remove_current_file() {
+	
+	int i;
+	
+	if (current_file != 1) {
+		previous_file();
+		for (i = 3; i <= num_files; i++)
+			files[i - 2] = files[i - 1];
+		num_files--;
+		buffer_print();
+	}
+	else {
+		next_file();
+		for (i = 2; i <= num_files; i++)
+			files[i - 2] = files[i - 1];
+		num_files--;
+		current_file--;
+		buffer_print();
+	}
+}
+
+static void full_repaint() {
+
+	int temp_line_pos = line_pos;
+	data_readlines();
+	buffer_init();
+	buffer_line(temp_line_pos);
+	buffer_print();
+}
+
+/* This adds line numbers to every line, as the -N flag necessitates */
+static void add_linenumbers() {
+
+	char current_line[256];
+	int i;
+	
+	for (i = 0; i <= num_flines; i++) {
+		safe_strncpy(current_line, flines[i], 256);
+		flines[i] = xrealloc(flines[i], strlen(current_line) + 7 );
+		sprintf(flines[i],"%5d %s", i+1, current_line);
+	}
+}
+
+static void save_input_to_file() {
+	
+	char current_line[256];
+	int i;
+	FILE *fp;
+	
+	clear_line();
+	printf("Log file: ");
+	fgets(current_line, 256, inp);
+	current_line[strlen(current_line) - 1] = '\0';
+	if (strlen(current_line)) {
+		fp = bb_xfopen(current_line, "w");
+		for (i = 0; i < num_flines; i++)
+			fprintf(fp, "%s", flines[i]);
+		fclose(fp);
+		buffer_print();
+	}
+	else
+		printf("%sNo log file%s", HIGHLIGHT, NORMAL);
+}
+
+#ifdef CONFIG_FEATURE_LESS_MARKS
+static void add_mark() {
+
+	int letter;
+	int mark_line;
+	
+	clear_line();
+	printf("Mark: ");
+	letter = tless_getch();
+	
+	if (isalpha(letter)) {
+		mark_line = line_pos;
+	
+		/* If we exceed 15 marks, start overwriting previous ones */
+		if (num_marks == 14)
+			num_marks = 0;
+
+		mark_lines[num_marks][0] = letter;
+		mark_lines[num_marks][1] = line_pos;
+		num_marks++;
+	}
+	else {
+		clear_line();
+		printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
+	}
+}
+
+static void goto_mark() {
+
+	int letter;
+	int i;
+	
+	clear_line();
+	printf("Go to mark: ");
+	letter = tless_getch();
+	if (isalpha(letter)) {
+		for (i = 0; i <= num_marks; i++)
+			if (letter == mark_lines[i][0]) {
+				buffer_line(mark_lines[i][1]);
+				break;
+			}
+		if ((num_marks == 14) && (letter != mark_lines[14][0])) {
+			clear_line();
+			printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
+		}		
+	}
+	else {
+		clear_line();
+		printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
+	}
+}
+#endif
+
+#ifdef CONFIG_FEATURE_LESS_REGEXP
+/* The below two regular expression handler functions NEED development. */
+
+/* Get a regular expression from the user, and then go through the current
+   file line by line, running a processing regex function on each one. */
+static void regex_process() {
+	
+	char uncomp_regex[100];
+	char current_line[256];
+	int i;
+	int j = 0;
+	regex_t *pattern;
+
+	/* Reset variables */
+	match_lines[0] = -1;
+	match_pos = 0;
+	num_matches = 0;
+	match_found = 0;
+	
+	pattern = (regex_t *) malloc(sizeof(regex_t));
+	memset(pattern, 0, sizeof(regex_t));
+	
+	/* Get the uncompiled regular expression from the user */
+	clear_line();
+	if (match_backwards)
+		printf("?");
+	else
+		printf("/");
+	scanf("%s", uncomp_regex);
+
+	/* Compile the regex and check for errors */
+	xregcomp(pattern, uncomp_regex, 0);
+	
+	/* Run the regex on each line of the current file here */
+	for (i = 0; i <= num_flines; i++) {
+		strcpy(current_line, process_regex_on_line(flines[i], pattern));
+		flines[i] = (char *) bb_xstrndup(current_line, sizeof(char) * (strlen(current_line)+1));
+		
+		if (match_found) {
+			match_lines[j] = i;
+			j++;
+		}
+	}
+
+	num_matches = j;
+	
+	if ((match_lines[0] != -1) && (num_flines > height - 2))
+		buffer_line(match_lines[0]);
+	else
+		buffer_init();
+}
+
+char *process_regex_on_line(char *line, regex_t *pattern) {
+	/* This function takes the regex and applies it to the line.
+	   Each part of the line that matches has the HIGHLIGHT
+	   and NORMAL escape sequences placed around it by 
+	   insert_highlights, and then the line is returned. */
+	
+	int match_status;
+	char *line2 = (char *) malloc((sizeof(char) * (strlen(line) + 1)) + 64);
+	char sub_line[256];
+	int prev_eo = 0;
+	memset(sub_line, 0, 256);
+	strcpy(line2, line);
+	regmatch_t match_structs;
+	
+	match_found = 0;
+	match_status = regexec(pattern, line2, 1, &match_structs, 0);
+	
+	while (match_status == 0) {
+		
+		memset(sub_line, 0, 256);
+		
+		if (match_found == 0)	
+			match_found = 1;
+		
+		line2 = insert_highlights(line2, match_structs.rm_so + prev_eo, match_structs.rm_eo + prev_eo);
+		if (match_structs.rm_eo + 11 + prev_eo < strlen(line2))
+			strcat(sub_line, line2 + match_structs.rm_eo + 11 + prev_eo);
+
+		prev_eo	+= match_structs.rm_eo + 11;
+		match_status = regexec(pattern, sub_line, 1, &match_structs, REG_NOTBOL);
+	}
+	
+	return line2;
+}
+
+char *insert_highlights (char *line, int start, int end) {
+	
+	char *new_line = (char *) malloc((sizeof(char) * (strlen(line) + 1)) + 10);
+	memset(new_line, 0, ((sizeof(char) * (strlen(line) + 1)) + 10));
+	strncat(new_line, line, start);
+	strcat(new_line, HIGHLIGHT);
+	strncat(new_line, line + start, end - start);
+	strcat(new_line, NORMAL);
+	strncat(new_line, line + end, strlen(line) - end);
+	
+	return new_line;
+}
+
+static void goto_match(int match) {
+	
+	/* This goes to a specific match - all line positions of matches are
+	   stored within the match_lines[] array. */
+	if ((match < num_matches) && (match >= 0)) {
+		buffer_line(match_lines[match]);
+		match_pos = match;
+	}
+}
+
+static void search_backwards() {
+	
+	int current_linepos = line_pos; 
+	int i;
+	
+	match_backwards = 1;
+	regex_process();
+		
+	for (i = 0; i < num_matches; i++) {
+		if (match_lines[i] > current_linepos) {
+			buffer_line(match_lines[i - num_back_match]);
+			break;
+		}
+	}
+	
+	/* Reset variables */
+	match_backwards = 0;
+	num_back_match = 1;
+	
+}
+#endif
+
+#ifdef CONFIG_FEATURE_LESS_BRACKETS
+
+static char opp_bracket (char bracket) {
+
+	switch (bracket) {
+		case '{': case '[':
+			return bracket + 2;
+			break;
+		case '(':
+			return ')';
+			break;
+		case '}': case ']':
+			return bracket - 2;
+			break;
+		case ')':
+			return '(';
+			break;
+		default:
+			return 0;
+			break;
+	}
+}
+
+static void match_right_bracket(char bracket) {
+	
+	int bracket_line = -1;
+	int i;
+	
+	if (strchr(flines[line_pos], bracket) == NULL) {
+		clear_line();
+		printf("%s%s%s", HIGHLIGHT, "No bracket in top line", NORMAL);
+	}
+	else {
+		for (i = line_pos + 1; i < num_flines; i++) {
+			if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
+				bracket_line = i;
+				break;
+			}
+		}
+
+		if (bracket_line == -1) {
+			clear_line();
+			printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
+		}
+
+		buffer_line(bracket_line - height + 2);
+		buffer_print();
+	}
+}
+
+static void match_left_bracket (char bracket) {
+	
+	int bracket_line = -1;
+	int i;
+
+	if (strchr(flines[line_pos + height - 2], bracket) == NULL) {
+		clear_line();
+		printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL);
+		printf("%s", flines[line_pos + height]);
+		sleep(4);
+	}
+	else {
+		for (i = line_pos + height - 2; i >= 0; i--) {
+			if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
+				bracket_line = i;
+				break;
+			}
+		}
+
+		if (bracket_line == -1) {
+			clear_line();
+			printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
+		}
+	
+		buffer_line(bracket_line);
+		buffer_print();
+	}
+}
+
+#endif
+
+int less_main(int argc, char **argv) {
+	
+	unsigned long flags;
+	int keypress;
+	
+	flags =  bb_getopt_ulflags(argc, argv, "EMNm~");
+	E_FLAG = (flags & 1);
+	M_FLAG = (flags & 2);
+	N_FLAG = (flags & 4);
+	m_FLAG = (flags & 8);
+	TILDE_FLAG = (flags & 16);
+
+	argc -= optind;
+	argv += optind;
+	files = argv;
+	num_files = argc;
+	
+	if (!num_files) {
+		if (ttyname(STDIN_FILENO) == NULL)
+			inp_stdin = 1;
+		else {
+			bb_error_msg("Missing filename");
+			bb_show_usage();
+		}
+	}
+	
+	strcpy(filename, (inp_stdin) ? "stdin" : files[0]);
+	tty_width_height();
+	data_readlines();
+	buffer_init();
+	buffer_print();
+	
+	while (1) {
+		keypress = tless_getch();
+		keypress_process(keypress);
+	}
+}