Index: stage2/shared.h
===================================================================
RCS file: /cvs/grub/stage2/shared.h,v
retrieving revision 1.67
diff -r1.67 shared.h
807a808,810
> char **grub_strsplit (char *str);
> void clear_arg_part_array ();
> 
Index: stage2/builtins.c
===================================================================
RCS file: /cvs/grub/stage2/builtins.c,v
retrieving revision 1.96
diff -r1.96 builtins.c
367a368,370
> /* pause_func is used by cat_func, so we have to declare it here */
> static int pause_func(char *arg, int flags);
> 
372a376
>   /* c is the character to be read consecutively from the file */
374a379,427
>   /*
>    * line_buffer holds the last line displayed
>    * if we clear the screen, we redisplay it
>    */
>   char line_buffer[81];
> 
>   /* col indicates the position in line_buffer where the next character goes to */
>   int col = 0;
> 
>   /* old_row and new_row are used to detect a line change while printing to the screen */
>   int old_row = getxy() & 0xFF;
>   int new_row;
> 
>   /* split the argument line into it's parts */
>   char **parts = grub_strsplit(arg);
> 
>   /* ptr is a pointer used to walk through the parts to determine there number */
>   char **ptr = parts;
> 
>   /* num is the number of number of parts of the argument line */
>   int num = 0;
> 
>   /* flag to determine, if we should pause after each page */
>   int more = 0;
> 
>   /* count the parts */
>   while (*ptr)
>   {
>     num++;
>     ptr++;
>   }
> 
>   /*
>    * determine if the argument line ends with "|more" where there can be an arbitrary number
>    * of whitespaces between "|" and "more"
>    */
>   if ((num >= 2) && ! grub_strcmp (parts[num - 2], "|") && ! grub_strcmp (parts[num - 1], "more"))
>     more = 1;
>   else if (num >= 1)
>   {
>     if (! grub_strcmp (parts[num - 1], "|more"))
>       more = 1;
>     else if (grub_strstr (parts[num - 1], "|more") - parts[num - 1] == strlen (parts[num - 1]) - 5)
>     {
>       more = 1;
>       parts[num - 1][strlen (parts[num - 1]) - 5] = 0;
>     }
>   }
> 
378a432
>   {
380a435,463
>     if (more)
>     {
>       line_buffer[col++] = c;
> 
>       new_row = getxy () & 0xFF;
> 
>       if (new_row >= 23)
>       {
>         int i;
> 
>         if (pause_func ("Press any key to continue, ESC to abort ...", flags))
>           break;
> 
>         cls();
> 
>         for (i = 0; i < col; i++)
>           grub_putchar (line_buffer[i]);
>       }
> 
>       if (new_row > old_row)
>       {
>         grub_memset (line_buffer, 0, col);
>         col = 0;
>       }
> 
>       old_row = new_row;
>     }
>   }
> 
390,391c473,477
<   "cat FILE",
<   "Print the contents of the file FILE."
---
>   "cat FILE [| more]",
>   "Print the contents of the file FILE. If \"| more\" is specified, grub will"
>   " print the contents one page at a time and ask for a key to continue or ESC"
>   " to abort. \"|\" and \"more\" don't have to be separated from each other or"
>   " from the other arguments by whitespace."
Index: stage2/char_io.c
===================================================================
RCS file: /cvs/grub/stage2/char_io.c,v
retrieving revision 1.39
diff -r1.39 char_io.c
1313a1314,1406
> 
> /* grub_strsplit */
> 
> /*
>  * Since we cannot use dynamic memory, we have to fix the maximum number
>  * of argument parts an argument line may contain.
>  * If an argument line contains more parts, the remaining parts are lost
>  * after splitting.
>  */
> #define MAX_ARG_PARTS 100
> 
> /*
>  * array to hold the pointers to the beginnings of the parts of the
>  * arguments. A NUL pointer indicates, that there are no more parts.
>  */
> static char *arg_part_array[MAX_ARG_PARTS + 1];
> 
> /*
>  * Splits an argument line into separate parts.
>  * To achieve this, we iterate through the argument line skipping all
>  * white space until we reach the first non-white space character. We
>  * add its address to the arg_part_array, then iterate further until
>  * we reach the next whitespace character (which determines the end of
>  * this argument part) and replace it with a '\0'. Thus this argument
>  * part is terminated.
>  * When there are no more argument parts, we add 0 to arg_part_array
>  * to indicate the end of the argument parts.
>  */
> char **
> grub_strsplit (char *str)
> {
>   int parts = 0;
>   int i = 0;
>   char *ptr = str;
> 
>   while (*ptr) {
>     while (*ptr && grub_isspace(*ptr)) {
>       ptr++;
>     }
>     if (*ptr) {
>       parts++;
>       while (*ptr && !grub_isspace(*ptr)) {
>         ptr++;
>       }
>     }
>   }
> 
>   if (parts > MAX_ARG_PARTS) {
>     parts = MAX_ARG_PARTS;
>   }
> 
>   ptr = str;
>   while (*ptr) {
>     while (*ptr && grub_isspace(*ptr)) {
>       ptr++;
>     }
>     if (*ptr) {
>       arg_part_array[i++] = ptr;
> 
>       if (i == parts) {
>         break;
>       }
> 
>       while (*ptr && !grub_isspace(*ptr)) {
>         ptr++;
>       }
> 
>       *ptr = 0;
>     }
> 
>     ptr++;
>   }
> 
>   while ( (i < MAX_ARG_PARTS + 1) && arg_part_array[i]) {
>     arg_part_array[i++] = 0;
>   }
> 
>   return arg_part_array;
> }
> 
> /*
>  * This function clears the arg_part_array. You should call
>  * this function if you split something like an argument line
>  * containing passwords ...
>  */
> void
> clear_arg_part_array ()
> {
>   int i = 0;
>   while ( (i < MAX_ARG_PARTS + 1) && arg_part_array[i]) {
>     arg_part_array[i++] = 0;
>   }
> }
