* 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.
+ * 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
+ * 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
+ * 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>
#define NORMAL "\033[0m"
/* The escape code to clear the screen */
-#define CLEAR "\033[2J"
+#define CLEAR "\033[H\033[J"
/* Maximum number of lines in a file */
#define MAXLINES 10000
static int width;
static char **files;
static char filename[256];
-static char buffer[100][256];
-static char *flines[MAXLINES];
+static char **buffer;
+static char **flines;
static int current_file = 1;
static int line_pos;
static int num_flines;
static int past_eof;
/* Command line options */
-static int E_FLAG;
-static int M_FLAG;
-static int N_FLAG;
-static int m_FLAG;
-static int TILDE_FLAG;
+static unsigned long flags;
+#define FLAG_E 1
+#define FLAG_M (1<<1)
+#define FLAG_m (1<<2)
+#define FLAG_N (1<<3)
+#define FLAG_TILDE (1<<4)
/* This is needed so that program behaviour changes when input comes from
stdin */
static int inp_stdin;
-/* 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;
#ifdef CONFIG_FEATURE_LESS_MARKS
static int mark_lines[15][2];
/* Reset terminal input to normal */
static void set_tty_cooked(void) {
fflush(stdout);
- tcsetattr(0, TCSANOW, &term_orig);
+ tcsetattr(fileno(inp), TCSANOW, &term_orig);
}
-/* Set terminal input to raw mode */
+/* Set terminal input to raw mode (taken from vi.c) */
static void set_tty_raw(void) {
- 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);
+ tcsetattr(fileno(inp), TCSANOW, &term_vi);
}
/* Exit the program gracefully */
special return codes. Note that this function works best with raw input. */
static int tless_getch(void) {
+ int input;
+
set_tty_raw();
- char input_key[3];
- input_key[0] = getc(inp);
+ input = 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);
+ if (input == '\033' && getc(inp) == '[') {
+ input = 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;
- }
+ if (input == REAL_KEY_UP)
+ return KEY_UP;
+ else if (input == REAL_KEY_DOWN)
+ return KEY_DOWN;
+ else if (input == REAL_KEY_RIGHT)
+ return KEY_RIGHT;
+ else if (input == REAL_KEY_LEFT)
+ return KEY_LEFT;
+ else if (input == REAL_PAGE_UP)
+ return PAGE_UP;
+ else if (input == REAL_PAGE_DOWN)
+ return PAGE_DOWN;
}
/* The input is a normal ASCII value */
else {
set_tty_cooked();
- return input_key[0];
+ return input;
}
return 0;
}
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);
+ flines[i] = bb_xasprintf("%5d %s", i + 1, current_line);
}
}
FILE *fp;
fp = (inp_stdin) ? stdin : bb_xfopen(filename, "rt");
-
- for (i = 0; (!feof(fp)) && (i <= MAXLINES); i++) {
+ flines = NULL;
+ for (i = 0; (feof(fp)==0) && (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));
+ if(fp != stdin)
+ bb_xferror(fp, filename);
+ flines = xrealloc(flines, (i+1) * sizeof(char *));
+ flines[i] = bb_xstrdup(current_line);
}
num_flines = i - 2;
-/* Reset variables for a new file */
+ /* 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(inp == NULL)
+ inp = (inp_stdin) ? fopen(CURRENT_TTY, "r") : stdin;
- if (N_FLAG)
+ if (flags & FLAG_N)
add_linenumbers();
}
-/* Free the file data */
-static void free_flines(void) {
-
- 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 */
-static int calc_percent(void) {
- return ((100 * (line_pos + height - 2) / num_flines) + 1);
-}
-#endif
-
/* Turn a percentage into a line number */
static int reverse_percent(int percentage) {
double linenum = percentage;
}
#ifdef CONFIG_FEATURE_LESS_FLAGS
+
+/* Interestingly, writing calc_percent as a function and not a prototype saves around 32 bytes
+ * on my build. */
+static int calc_percent(void) {
+ return ((100 * (line_pos + height - 2) / num_flines) + 1);
+}
+
/* Print a status line if -M was specified */
static void m_status_print(void) {
}
else {
percentage = calc_percent();
- printf("%i%s %s", percentage, "%", NORMAL);
+ printf("%i%% %s", percentage, NORMAL);
}
}
else {
percentage = calc_percent();
if (!line_pos)
- printf("%s%s %i%s%s", HIGHLIGHT, filename, percentage, "%", NORMAL);
+ printf("%s%s %i%%%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);
+ printf("%s%i%%%s", HIGHLIGHT, percentage, NORMAL);
}
#endif
/* Change the status if flags have been set */
#ifdef CONFIG_FEATURE_LESS_FLAGS
- if (M_FLAG)
+ if (flags & FLAG_M)
m_status_print();
- else if (m_FLAG)
+ else if (flags & FLAG_m)
medium_status_print();
/* No flags set */
else {
printf("%s%s%s%s", HIGHLIGHT, "- Next: ", files[current_file], NORMAL);
}
else {
- printf("%c", ':');
+ putchar(':');
}
#ifdef CONFIG_FEATURE_LESS_FLAGS
}
int i;
+ printf("%s", CLEAR);
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();
}
+
+ status_print();
}
/* Initialise the buffer */
int i;
- for (i = 0; i < (height - 1); i++)
- memset(buffer[i], '\0', 256);
+ if(buffer == NULL) {
+ /* malloc the number of lines needed for the buffer */
+ buffer = xrealloc(buffer, height * sizeof(char *));
+ } else {
+ for (i = 0; i < (height - 1); i++)
+ free(buffer[i]);
+ }
/* 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]);
+ buffer[i] = bb_xstrdup(flines[i]);
}
/* If the buffer still isn't full, fill it with blank lines */
for (; i < (height - 1); i++) {
- strcpy(buffer[i], "");
+ buffer[i] = bb_xstrdup("");
}
}
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]);
+ for (i = 0; i < (height - 1); i++) {
+ free(buffer[i]);
+ buffer[i] = bb_xstrdup(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]);
+ for (i = 0; i < (height - 1); i++) {
+ free(buffer[i]);
+ buffer[i] = bb_xstrdup(flines[line_pos + i]);
+ }
}
}
/* We exit if the -E flag has been set */
- if (E_FLAG && (line_pos + (height - 2) == num_flines))
+ if ((flags & FLAG_E) && (line_pos + (height - 2) == num_flines))
tless_exit(0);
}
}
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]);
+ for (i = 0; i < (height - 1); i++) {
+ free(buffer[i]);
+ buffer[i] = bb_xstrdup(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]);
+ for (i = 0; i < (height - 1); i++) {
+ free(buffer[i]);
+ buffer[i] = bb_xstrdup(flines[line_pos + i]);
+ }
}
}
}
/* We only move part of the buffer, as the rest
is past the EOF */
for (i = 0; i < (height - 1); i++) {
+ free(buffer[i]);
if (i < tilde_line - nlines + 1)
- strcpy(buffer[i], flines[line_pos + i]);
+ buffer[i] = bb_xstrdup(flines[line_pos + i]);
else {
if (line_pos >= num_flines - height + 2)
- strcpy(buffer[i], "~\n");
+ buffer[i] = bb_xstrdup("~\n");
}
}
}
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]);
+ for (i = 0; i < (height - 1); i++) {
+ free(buffer[i]);
+ buffer[i] = bb_xstrdup(flines[linenum + i]);
+ }
line_pos = linenum;
}
else {
for (i = 0; i < (height - 1); i++) {
+ free(buffer[i]);
if (linenum + i < num_flines + 2)
- strcpy(buffer[i], flines[linenum + i]);
+ buffer[i] = bb_xstrdup(flines[linenum + i]);
else
- strcpy(buffer[i], (TILDE_FLAG) ? "\n" : "~\n");
+ buffer[i] = bb_xstrdup((flags & FLAG_TILDE) ? "\n" : "~\n");
}
line_pos = linenum;
/* Set past_eof so buffer_down and buffer_up act differently */
}
}
+/* Reinitialise everything for a new file - free the memory and start over */
+static void reinitialise(void) {
+
+ int i;
+
+ for (i = 0; i <= num_flines; i++)
+ free(flines[i]);
+ free(flines);
+
+ data_readlines();
+ buffer_init();
+ buffer_print();
+}
+
static void examine_file(void) {
int newline_offset;
newline_offset = strlen(filename) - 1;
filename[newline_offset] = '\0';
- files[num_files] = bb_xstrndup(filename, (strlen(filename) + 1) * sizeof(char));
+ files[num_files] = bb_xstrdup(filename);
current_file = num_files + 1;
num_files++;
inp_stdin = 0;
- ea_inp_stdin = 1;
- free_flines();
- data_readlines();
- buffer_init();
- buffer_print();
+ reinitialise();
}
-
-static void next_file(void) {
- 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(void) {
- if (current_file != 1) {
- current_file--;
+/* This function changes the file currently being paged. direction can be one of the following:
+ * -1: go back one file
+ * 0: go to the first file
+ * 1: go forward one file
+*/
+static void change_file (int direction) {
+ if (current_file != ((direction > 0) ? num_files : 1)) {
+ current_file = direction ? current_file + direction : 1;
strcpy(filename, files[current_file - 1]);
-
- free_flines();
- data_readlines();
- buffer_init();
- buffer_print();
+ reinitialise();
}
else {
clear_line();
- printf("%s%s%s", HIGHLIGHT, "No previous file", NORMAL);
- }
-}
-
-static void first_file(void) {
- if (current_file != 1) {
- current_file = 1;
- strcpy(filename, files[current_file - 1]);
- free_flines();
- data_readlines();
- buffer_init();
- buffer_print();
+ printf("%s%s%s", HIGHLIGHT, (direction > 0) ? "No next file" : "No previous file", NORMAL);
}
}
int i;
if (current_file != 1) {
- previous_file();
+ change_file(-1);
for (i = 3; i <= num_files; i++)
files[i - 2] = files[i - 1];
num_files--;
buffer_print();
}
else {
- next_file();
+ change_file(1);
for (i = 2; i <= num_files; i++)
files[i - 2] = files[i - 1];
num_files--;
break;
#endif
case 'n':
- next_file();
+ change_file(1);
break;
case 'p':
- previous_file();
+ change_file(-1);
break;
case 'q':
tless_exit(0);
break;
case 'x':
- first_file();
+ change_file(0);
break;
default:
break;
static 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;
+ return bb_xasprintf("%.*s%s%.*s%s%s", start, line, HIGHLIGHT,
+ end - start, line + start, NORMAL, line + end);
}
static char *process_regex_on_line(char *line, regex_t *pattern) {
char sub_line[256];
int prev_eo = 0;
regmatch_t match_structs;
-
+
memset(sub_line, 0, 256);
strcpy(line2, line);
/* Get the uncompiled regular expression from the user */
clear_line();
- if (match_backwards)
- printf("?");
- else
- printf("/");
- scanf("%s", uncomp_regex);
+ putchar((match_backwards) ? '?' : '/');
+ uncomp_regex[0] = 0;
+ fgets(uncomp_regex, sizeof(uncomp_regex), stdin);
+ i = strlen(uncomp_regex);
+ if(i > 0) {
+ if(uncomp_regex[i-1] == '\n')
+ uncomp_regex[i-1] = '\0';
+ else
+ while((i = getchar()) != '\n' && i != EOF);
+ }
/* 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));
-
+ flines[i] = bb_xstrdup(current_line);
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
int num;
char num_input[80];
char keypress;
+ char *endptr;
+
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]);
+ /* Receive input until a letter is given (max 80 chars)*/
+ while((i < 80) && (num_input[i] = tless_getch()) && isdigit(num_input[i])) {
+ putchar(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);
+ num = strtol(num_input, &endptr, 10);
+ if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES)
+ goto END;
/* 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;
}
+END:
+ buffer_print();
}
#ifdef CONFIG_FEATURE_LESS_FLAGCS
int keypress;
clear_line();
- printf("-");
+ putchar('-');
keypress = tless_getch();
switch (keypress) {
case 'M':
- M_FLAG = !M_FLAG;
+ flags ^= FLAG_M;
break;
case 'm':
- m_FLAG = !m_FLAG;
+ flags ^= FLAG_m;
break;
case 'E':
- E_FLAG = !E_FLAG;
+ flags ^= FLAG_E;
break;
case '~':
- TILDE_FLAG = !TILDE_FLAG;
+ flags ^= FLAG_TILDE;
break;
default:
break;
int flag_val;
clear_line();
- printf("_");
+ putchar('_');
keypress = tless_getch();
switch (keypress) {
case 'M':
- flag_val = M_FLAG;
+ flag_val = flags & FLAG_M;
break;
case 'm':
- flag_val = m_FLAG;
+ flag_val = flags & FLAG_m;
break;
case '~':
- flag_val = TILDE_FLAG;
+ flag_val = flags & FLAG_TILDE;
break;
case 'N':
- flag_val = N_FLAG;
+ flag_val = flags & FLAG_N;
break;
case 'E':
- flag_val = E_FLAG;
+ flag_val = flags & FLAG_E;
break;
default:
flag_val = 0;
clear_line();
printf("Go to mark: ");
letter = tless_getch();
+ clear_line();
+
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();
+ if ((num_marks == 14) && (letter != mark_lines[14][0]))
printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
- }
}
- else {
- clear_line();
+ else
printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
- }
}
#endif
int bracket_line = -1;
int i;
- if (strchr(flines[line_pos], bracket) == NULL) {
- clear_line();
+ clear_line();
+
+ if (strchr(flines[line_pos], bracket) == NULL)
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) {
}
}
- if (bracket_line == -1) {
- clear_line();
+ if (bracket_line == -1)
printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
- }
buffer_line(bracket_line - height + 2);
buffer_print();
int bracket_line = -1;
int i;
+ clear_line();
+
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);
}
}
- if (bracket_line == -1) {
- clear_line();
+ if (bracket_line == -1)
printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
- }
buffer_line(bracket_line);
buffer_print();
default:
break;
}
+
if (isdigit(keypress))
number_process(keypress);
}
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);
+ flags = bb_getopt_ulflags(argc, argv, "EMmN~");
argc -= optind;
argv += optind;
}
}
- strcpy(filename, (inp_stdin) ? "stdin" : files[0]);
+ strcpy(filename, (inp_stdin) ? bb_msg_standard_input : files[0]);
tty_width_height();
data_readlines();
+ tcgetattr(fileno(inp), &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;
buffer_init();
buffer_print();