]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/jdb/jdb_core.cpp
Update
[l4.git] / kernel / fiasco / src / jdb / jdb_core.cpp
1 INTERFACE:
2
3 #include "static_init.h"
4 #include "jdb_module.h"
5
6 /**
7  * The core of the modularized Jdb.
8  * @see Jdb_module
9  * @see Jdb_category
10  *
11  * This class provides the core functions for handling
12  * Jdb_modules and providing them with the right input.
13  *
14  */
15 class Jdb_core
16 {
17 public:
18
19   /**
20    * The command structure for Jdb_core.
21    *
22    * This structure consists of a pointer to the Jdb_module
23    * and a Jdb_module::Cmd structure. It is used in exec_cmd()
24    * and returned from has_cmd().
25    */
26   struct Cmd
27   {
28     /**
29      * Pointer to the module providing this command.
30      */
31     Jdb_module            *mod;
32
33     /**
34      * The Jdb_module::Cmd structure, describing the command.
35      *
36      * If this is a null pointer the command is invalid.
37      * @see Jdb_module
38      * @see Jdb_module::Cmd
39      */
40     Jdb_module::Cmd const *cmd;
41
42     /**
43      * Create a Jdb_core::Cmd.
44      * @param _mod the Jdb_module providing the command.
45      * @param _cmd the command structure (see Jdb_module::Cmd).
46      */
47     Cmd(Jdb_module *_mod, Jdb_module::Cmd const *_cmd = 0)
48       : mod(_mod), cmd(_cmd)
49     {}
50   };
51
52   /**
53    * Get the command structure accoring to the given name.
54    * @param cmd the command to look for.
55    * @return A valid Cmd structure if cmd was found, or a
56    *         Cmd structure where Cmd::cmd is a null pointer if
57    *         no module provides such a command.
58    */
59   static Cmd has_cmd(char const *cmd);
60
61   /**
62    * Execute the command according to cmd.
63    * @param cmd the command structure (see Jdb_core::Cmd), which
64    *        describes the command to execute.
65    * @return 0 if Jdb_module::action() returned LEAVE
66    *         1 if Jdb_module::action() returned NOTHING
67    *         2 if Jdb_module::action() returned GO_BACK (KEY_HOME entered)
68    *         3 if the input was aborted (KEY_ESC entered) or was invalid
69    *
70    * This method is actually responsible for reading the input
71    * with respect to the commands format string and calling
72    * the Jdb_module::action() method after that.
73    *
74    */
75   static int exec_cmd(Cmd const cmd, char const *str, int push_next_char = -1);
76
77   /**
78    * Overwritten getchar() to be able to handle next_char.
79    */
80   static int getchar(void);
81
82   /**
83    * Call this function every time a `\n' is written to the
84    *        console and it stops output when the screen is full.
85    * @return 0 if user wants to abort the output (escape or 'q' pressed)
86    */
87   static int new_line(unsigned &line);
88
89   static void prompt_start();
90   static void prompt_end();
91   static void prompt();
92   static int  prompt_len();
93   static void update_prompt();
94   static int set_prompt_color(char v);
95
96   /**
97    * Like strlen but do not count ESC sequences.
98    */
99   static int print_len(const char *s);
100
101   static char esc_prompt[];
102
103   static void (*wait_for_input)();
104
105 public:
106   static bool short_mode;
107   typedef int (Input_fmt)(char fmt, int *size, char const *cmd_str, void *buf);
108
109 private:
110   static int  next_char;
111   static Input_fmt *_fmt_list[26];
112 };
113
114 #define JDB_ANSI_black        "30"
115 #define JDB_ANSI_gray         "30;1"
116 #define JDB_ANSI_red          "31"
117 #define JDB_ANSI_lightred     "31;1"
118 #define JDB_ANSI_green        "32"
119 #define JDB_ANSI_lightgreen   "32;1"
120 #define JDB_ANSI_brown        "33"
121 #define JDB_ANSI_yellow       "33;1"
122 #define JDB_ANSI_blue         "34"
123 #define JDB_ANSI_lightblue    "34;1"
124 #define JDB_ANSI_magenta      "35"
125 #define JDB_ANSI_lightmagenta "35;1"
126 #define JDB_ANSI_cyan         "36"
127 #define JDB_ANSI_lightcyan    "36;1"
128 #define JDB_ANSI_white        "37"
129 #define JDB_ANSI_brightwhite  "37;1"
130 #define JDB_ANSI_default      ""
131
132 #define JDB_ANSI_COLOR(color)   "\033[" JDB_ANSI_##color "m"
133
134 #define JDB_ANSI_END          "\033[m"
135
136
137 IMPLEMENTATION:
138
139 #include <cstring>
140 #include <cstdarg>
141 #include <cstdio>
142 #include <cstdlib>
143 #include <cctype>
144 #include <simpleio.h>
145
146 #include "div32.h"
147 #include "l4_types.h"
148 #include "kernel_console.h"
149 #include "keycodes.h"
150 #include "jdb_prompt_ext.h"
151 #include "jdb_screen.h"
152 #include "processor.h"
153
154 bool Jdb_core::short_mode = true;
155 int  Jdb_core::next_char  = -1;
156 char Jdb_core::esc_prompt[32] = JDB_ANSI_COLOR(green);
157 Jdb_core::Input_fmt *Jdb_core::_fmt_list[26]; 
158 void (*Jdb_core::wait_for_input)();
159
160 PUBLIC static
161 bool
162 Jdb_core::add_fmt_handler(char fmt, Input_fmt* hdlr)
163 {
164   if (fmt < 'a' || (fmt - 'a') >= (int)(sizeof(_fmt_list)/sizeof(_fmt_list[0])))
165     return false;
166
167   if (_fmt_list[fmt - 'a'])
168     return false;
169
170   _fmt_list[fmt - 'a'] = hdlr;
171   return true;
172 }
173
174 PUBLIC static
175 void
176 Jdb_core::print_prompt()
177 { prompt_start(); prompt(); prompt_end(); }
178
179 IMPLEMENT
180 void Jdb_core::update_prompt()
181 {
182   Jdb_prompt_ext::update_all();
183 }
184
185 IMPLEMENT
186 void Jdb_core::prompt_start()
187 {
188   putstr(esc_prompt);
189 }
190
191 IMPLEMENT
192 void Jdb_core::prompt_end()
193 {
194   putstr(JDB_ANSI_END);
195 }
196
197 IMPLEMENT
198 void Jdb_core::prompt()
199 {
200   Jdb_prompt_ext::do_all();
201   if (short_mode)
202     putstr("jdb: ");
203   else
204     putstr("jdb# ");
205 }
206
207 IMPLEMENT
208 int Jdb_core::prompt_len()
209 {
210   return 5;
211 }
212
213 IMPLEMENT
214 int Jdb_core::print_len(const char *s)
215 {
216   int l = 0;
217   while (*s)
218     {
219       if (s[0] == '\033' && s[1] == '[')
220         {
221           s += 2;
222           while (*s && *s != 'm')
223             s++;
224           if (*s)
225             s++; // skip 'm'
226         }
227       else
228         {
229           l++;
230           s++;
231         }
232     }
233   return l;
234 }
235
236 PUBLIC static
237 int
238 Jdb_core::get_ansi_color(char c)
239 {
240   switch(c)
241     {
242     case 'N': case 'n': return 30;
243     case 'R': case 'r': return 31;
244     case 'G': case 'g': return 32;
245     case 'Y': case 'y': return 33;
246     case 'B': case 'b': return 34;
247     case 'M': case 'm': return 35;
248     case 'C': case 'c': return 36;
249     case 'W': case 'w': return 37;
250     default:  return 0;
251     }
252 }
253
254
255 IMPLEMENT
256 int Jdb_core::set_prompt_color(char x)
257 {
258   unsigned pc = get_ansi_color(x);
259
260   if (pc == 0)
261     return 0;
262
263   if (x >= 'A' && x <= 'Z')
264     snprintf(esc_prompt, sizeof(esc_prompt) - 1, "\033[%u;%dm", pc, 1);
265   else
266     snprintf(esc_prompt, sizeof(esc_prompt) - 1, "\033[%um", pc);
267
268   return 1;
269
270 }
271
272 IMPLEMENT
273 Jdb_core::Cmd Jdb_core::has_cmd(char const *cmd)
274 {
275   for (Jdb_module::List::Const_iterator m = Jdb_module::modules.begin();
276        m != Jdb_module::modules.end(); ++m)
277     {
278       Cmd c(*m);
279       c.cmd = m->has_cmd(cmd, short_mode);
280       if (c.cmd)
281         return c;
282     }
283
284   return Cmd(0);
285 }
286
287 PRIVATE static
288 unsigned
289 Jdb_core::match_len(char const *a, char const *b, unsigned l)
290 {
291   unsigned p = 0;
292   while (*a && *b && p < l && *a == *b)
293     {
294       ++a; ++b; ++p;
295     }
296   return p;
297 }
298
299 PUBLIC static
300 unsigned
301 Jdb_core::print_alternatives(char const *prefix)
302 {
303   unsigned prefix_len = 0;
304   char const *match = 0;
305   typedef Jdb_module::List::Const_iterator Iter;
306   for (Iter m = Jdb_module::modules.begin(); m != Jdb_module::modules.end(); ++m)
307     {
308       unsigned sc_max = m->num_cmds();
309       Jdb_module::Cmd const *cmds = m->cmds();
310       for (unsigned sc = 0; sc < sc_max; ++sc)
311         {
312           if (!Jdb_module::match(cmds[sc].cmd, prefix, false))
313             continue;
314
315           if (!match)
316             {
317               match = cmds[sc].cmd;
318               prefix_len = strlen(match);
319             }
320           else
321             prefix_len = match_len(match, cmds[sc].cmd, prefix_len);
322           printf("%s %s\n", cmds[sc].cmd, cmds[sc].fmt);
323         }
324     }
325   return prefix_len;
326 }
327
328 PUBLIC static
329 Jdb_core::Cmd
330 Jdb_core::complete_cmd(char const *prefix, bool &multi_match)
331 {
332   Cmd match(0,0);
333   multi_match = false;
334   typedef Jdb_module::List::Const_iterator Iter;
335   for (Iter m = Jdb_module::modules.begin(); m != Jdb_module::modules.end(); ++m)
336     {
337       unsigned sc_max = m->num_cmds();
338       Jdb_module::Cmd const *cmds = m->cmds();
339       for (unsigned sc = 0; sc < sc_max; ++sc)
340         {
341           if (!Jdb_module::match(cmds[sc].cmd, prefix, false))
342             continue;
343
344           if (match.cmd)
345             multi_match = true;
346           else
347             match = Cmd(*m, cmds + sc);
348         }
349     }
350
351   return match;
352 }
353
354 IMPLEMENT
355 int Jdb_core::getchar()
356 {
357   if (next_char != -1)
358     {
359       int c = next_char;
360       next_char = -1;
361       return c;
362     }
363
364   int c;
365
366   while (1)
367     {
368       c = Kconsole::console()->getchar(false);
369       if (c != -1)
370         return c;
371
372       if (wait_for_input)
373         wait_for_input();
374       else
375         Proc::pause();
376     }
377 }
378
379 PUBLIC static
380 int
381 Jdb_core::cmd_getchar(char const *&str)
382 {
383   if (next_char != -1)
384     {
385       int c = next_char;
386       next_char = -1;
387       return c;
388     }
389
390   if (short_mode)
391     return getchar();
392
393   if (!str)
394     return KEY_RETURN;
395
396   if (!*str)
397     return KEY_RETURN;
398
399   return *(str++);
400 }
401
402 PUBLIC static inline NEEDS[<cstdio>]
403 void
404 Jdb_core::cmd_putchar(int c)
405 { if (short_mode) putchar(c); }
406
407 IMPLEMENT
408 int Jdb_core::exec_cmd(Cmd const cmd, char const *str, int push_next_char = -1)
409 {
410   char const* f = cmd.cmd->fmt;
411   char const* f1;
412
413   //char args[256];
414   void *argbuf = (void*)cmd.cmd->argbuf;
415
416   enum {
417     NORMAL,
418     UNSIGNED,
419     MULTI,
420   } num_mode;
421
422   int num_base = 10;
423   int num_pos = 0, num_digit = 0;
424   int max_len = 0, max_digit = 0;
425   int c, cv;
426   char fm;
427
428   next_char = push_next_char;
429
430   do {
431
432     char *next_arg = (char*)argbuf;
433     char const *old_f = f;
434     while(*f)
435       {
436         f1 = f;
437
438         while(*f && *f!='%')
439           ++f;
440
441         if (short_mode)
442           putnstr( f1, (f-f1) );
443
444         if(*(f++))
445           {
446             int long_fmt = sizeof(long long int) == sizeof(void*);
447             bool negative = false;
448             long long int val = 0;
449             max_len = 0;
450
451           next_fmt:
452             if(*(f))
453               switch((fm=*(f++)))
454                 // Attention: Each case statement must finish with "continue"
455                 //            else it falls through to int_done!
456                 {
457                 case '0': case '1': case '2':
458                 case '3': case '4': case '5':
459                 case '6': case '7': case '8':
460                 case '9':
461                   max_len = max_len * 10 + fm - '0';
462                   goto next_fmt;
463                 case '%':
464                   cmd_putchar('%');
465                   continue;
466
467                 case 'l':
468                   long_fmt++;
469                   if(long_fmt > 2) 
470                     long_fmt = 2;
471                   goto next_fmt;
472
473                 case 'h':
474                   long_fmt--;
475                   if(long_fmt < -1)
476                     long_fmt = -1;
477                   goto next_fmt;
478
479                 case 'i':
480                   num_base = 10;
481                   num_mode = MULTI;
482                   goto input_num;
483
484                 case 'p':
485                   num_base = 16;
486                   num_mode = UNSIGNED;
487                   if(sizeof(short int)==sizeof(void*))
488                     long_fmt = -1;
489                   else if(sizeof(int)==sizeof(void*))
490                     long_fmt = 0;
491                   else if(sizeof(long int)==sizeof(void*))
492                     long_fmt = 1;
493                   else //if(sizeof(long long int)==sizeof(void*))
494                     long_fmt = 2;
495                   goto input_num;
496
497                 case 'o':
498                   cmd_putchar('0');
499                   num_mode = UNSIGNED;
500                   num_base = 8;
501                   goto input_num;
502
503                 case 'X':
504                 case 'x':
505                   num_mode = UNSIGNED;
506                   num_base = 16;
507                   goto input_num;
508
509                 case 'd':
510                   num_mode = NORMAL;
511                   num_base = 10;
512                   goto input_num;
513
514                 case 'u':
515                   num_mode = UNSIGNED;
516                   num_base = 10;
517                   goto input_num;
518
519                 input_num:
520                   num_pos = num_digit = 0;
521                   max_digit = 0;
522                   if (num_base == 16)
523                     {
524                       if (long_fmt == -1)
525                         max_digit = 2*sizeof(short int);
526                       else if (long_fmt == 0)
527                         max_digit = 2*sizeof(int);
528                       else if (long_fmt == 1)
529                         max_digit = 2*sizeof(long int);
530                       else
531                         max_digit = 2*sizeof(long long int);
532                     }
533                   while((c = cmd_getchar(str)) != ' ' && c!=KEY_RETURN)
534                     {
535                       if(c==KEY_ESC)
536                         return 3;
537                       
538                       if(c==KEY_BACKSPACE && num_pos>0)
539                         {
540                           putstr("\b \b");
541                           if(num_pos == 1 && negative)
542                             negative = false;
543                           else if(num_pos==1 && num_mode==MULTI && num_base==8)
544                             num_base = 10;
545                           else if(num_pos==2 && num_mode==MULTI && num_base==16)
546                             num_base = 8;
547                           else
548                             {
549                               val = div32(val, num_base);
550                               num_digit--;
551                             }
552
553                           num_pos--;
554                           continue;
555                         }
556                       else if(num_mode!=UNSIGNED && num_pos==0 && c=='-')
557                         {
558                           num_pos++;
559                           cmd_putchar(c);
560                           negative = true;
561                           continue;
562                         }
563                       else if(num_mode==MULTI && num_pos==0 && c=='0')
564                         {
565                           num_pos++;
566                           cmd_putchar(c);
567                           num_base = 8;
568                           continue;
569                         }
570                       else if(num_mode==MULTI && num_pos==1 && num_base==8 
571                               && (c=='x' || c=='X'))
572                         {
573                           num_pos++;
574                           cmd_putchar(c);
575                           num_base = 16;
576                           continue;
577                         }
578                       else if(num_pos==1 && (c=='x' || c=='X'))
579                         {
580                           // ignore 0x to allow direct pasting of addresses
581                           num_pos--;
582                           num_digit--;
583                           putstr("\b \b");
584                           continue;
585                         }
586                       else if(c>='0' && c<='9')
587                         {
588                           cv = c-'0';
589                         }
590                       else if((c|0x20)>='a' && (c|0x20)<='f')
591                         {
592                           cv = (c|0x20) - 'a' + 10;
593                         }
594                       else
595                         continue;
596
597                       if(cv < num_base)
598                         {
599                           num_pos++;
600                           num_digit++;
601                           cmd_putchar(c);
602                           val = val * num_base + cv;
603                           if ((max_len   != 0 && num_pos   >= max_len) ||
604                               (max_digit != 0 && num_digit >= max_digit))
605                             break;
606                         }
607                     }
608                   if (num_pos == 0)
609                     return 3;
610                   goto int_done;
611
612                 case 't': printf("%%t is not supported...\n");
613                   continue;
614
615                 case 'C':
616                 case 'c':
617                   {
618                     int c = cmd_getchar(str);
619                     if(c==KEY_ESC)
620                       return 3;
621
622                     if(fm == 'c' && isprint(c))
623                       cmd_putchar(c);
624                     if(next_arg)
625                       *next_arg++ = c;
626                   }
627                   continue;
628
629                 case 's':
630                 case 'S':
631                   if(!max_len)
632                     continue;
633
634                   num_pos = 0;
635                   while((c = cmd_getchar(str)) != KEY_RETURN && c!=' ')
636                     {
637                       if(c==KEY_ESC)
638                         return 3;
639
640                       if(c==KEY_BACKSPACE && num_pos)
641                         {
642                           putstr("\b \b");
643                           num_pos--;
644                         }
645                       else if(isprint(c) && (num_pos+1 < max_len))
646                         {
647                           cmd_putchar(c);
648                           next_arg[num_pos++] = c;
649                         }
650
651                       next_arg[num_pos] = '\0';
652
653                       if (fm=='S')
654                         {
655                           int oldlen = num_pos;
656                           (**(Jdb_module::Gotkey**)(next_arg+max_len))
657                             (next_arg, max_len, c);
658                           int newlen = strlen(next_arg);
659                           if (newlen > oldlen)
660                             printf("%s", next_arg + oldlen);
661                           else if (newlen < oldlen)
662                             for (int i=newlen; i<oldlen; i++)
663                               putstr("\b \b");
664                           num_pos = newlen;
665                         }
666                     }
667                   next_arg[num_pos] = '\0';
668                   next_arg[max_len-1] = 0;
669                   next_arg += max_len;
670                   continue;
671
672                 default:
673                   if (fm > 'a'
674                       && (fm - 'a') < (int)(sizeof(_fmt_list)/sizeof(_fmt_list[0]))
675                       && _fmt_list[fm -'a'])
676                     {
677                       int size = sizeof(int);
678                       switch(long_fmt)
679                         {
680                         default: break;
681                         case 1: size = sizeof(long int); break;
682                         case 2: size = sizeof(long long int);  break;
683                         case -1: size = sizeof(short int); break;
684                         }
685                       int res = (_fmt_list[fm - 'a'])(fm, &size, str, next_arg);
686                       if (res)
687                         return res;
688
689                       next_arg += size;
690                       continue;
691                     }
692
693                   puts(" unknown format! ");
694                   return 3;
695                 }
696
697           int_done:
698             if(negative) val = -val;
699
700             if(next_arg)
701               switch(long_fmt)
702                 {
703                 default:
704                     {
705                       int *v = (int*)next_arg;
706                       *v = val;
707                       next_arg += sizeof(int);
708                     }
709                   break;
710                 case 1:
711                     {
712                       long int *v = (long int*)next_arg;
713                       *v = val;
714                       next_arg += sizeof(long int);
715                     }
716                   break;
717                 case 2:
718                     {
719                       long long int *v = (long long int*)next_arg;
720                       *v = val;
721                       next_arg += sizeof(long long int);
722                     }
723                   break;
724                 case -1:
725                     {
726                       short int *v = (short int*)next_arg;
727                       *v = val;
728                       next_arg += sizeof(short int);
729                     }
730                   break;
731                 }
732           }
733         else
734           break;
735       }
736
737     f = old_f;
738
739     switch (cmd.mod->action(cmd.cmd->id, argbuf, f, next_char))
740       {
741       case Jdb_module::EXTRA_INPUT:
742         // more input expected
743         next_char = -1;
744         // fall through
745       case Jdb_module::EXTRA_INPUT_WITH_NEXTCHAR:
746         // more input expected, also consider previously entered key
747         break;
748       case Jdb_module::LEAVE:
749         // leave kernel debugger
750         return 0;
751       case Jdb_module::GO_BACK:
752         // user entered KEY_HOME
753         return 2;
754       case Jdb_module::NOTHING:
755         // finished successfully
756         return 1;
757       default:
758         // there was an error
759         return 3;
760       }
761   } while(1);
762
763 }
764
765 IMPLEMENT
766 int
767 Jdb_core::new_line( unsigned &line )
768 {
769   if (line++ > Jdb_screen::height()-3)
770     {
771       putstr("--- CR: line, SPACE: page, ESC: abort ---");
772       int a = getchar();
773       putstr("\r\033[K");
774
775       switch (a)
776         {
777         case KEY_ESC:
778         case 'q':
779         case '^':
780           cmd_putchar('\n');
781           return 0;
782         case KEY_RETURN:
783           line--;
784           return 1;
785         default:
786           line=0;
787           return 1;
788         }
789     }
790   return 1;
791 }
792
793
794 //===================
795 // Std JDB modules
796 //===================
797
798
799 /**
800  * Private 'go' module.
801  * 
802  * This module handles the 'go' or 'g' command 
803  * that continues normal program execution.
804  */
805 class Go_m : public Jdb_module
806 {
807 public:
808   Go_m() FIASCO_INIT;
809 };
810
811 static Go_m go_m INIT_PRIORITY(JDB_MODULE_INIT_PRIO);
812
813 IMPLEMENT
814 Go_m::Go_m()
815   : Jdb_module("GENERAL")
816 {}
817
818 PUBLIC
819 Jdb_module::Action_code Go_m::action( int, void *&, char const *&, int & )
820 {
821   return LEAVE;
822 }
823
824 PUBLIC
825 int Go_m::num_cmds() const
826
827   return 1;
828 }
829
830 PUBLIC
831 Jdb_module::Cmd const * Go_m::cmds() const
832 {
833   static Cmd cs[] =
834     { 
835         { 0, "g", "go", "",
836            "g\tleave kernel debugger\n"
837            "Return\tshow debug message", 0 },
838     };
839
840   return cs;
841 }
842
843
844 /**
845  * Private 'help' module.
846  * 
847  * This module handles the 'help' or 'h' command and 
848  * prints out a help screen.
849  */
850 class Help_m : public Jdb_module
851 {
852 public:
853   Help_m() FIASCO_INIT;
854 };
855
856
857 static Help_m help_m INIT_PRIORITY(JDB_MODULE_INIT_PRIO);
858
859
860 PUBLIC
861 Jdb_module::Action_code Help_m::action( int, void *&, char const *&, int & )
862 {
863   size_t const tab_width = 27;
864
865   if(Jdb_category::categories.begin() == Jdb_category::categories.end())
866     {
867       printf("No debugger commands seem to have been registered\n");
868       return NOTHING;
869     }
870
871   unsigned line = 0;
872
873   puts("");
874   for (Jdb_category::List::Const_iterator c = Jdb_category::categories.begin();
875        c != Jdb_category::categories.end(); ++c)
876     {
877       bool first = true;
878       for (Jdb_module::List::Const_iterator m = Jdb_module::modules.begin();
879            m != Jdb_module::modules.end(); ++m)
880         {
881           if (m->category() != *c)
882             continue;
883
884           if(first)
885             {
886               if(!Jdb_core::new_line(line))
887                 return NOTHING;
888               printf("\033[1m[%s]\033[0m %s", c->name(), c->description());
889               putchar('\n');
890               if(!Jdb_core::new_line(line))
891                 return NOTHING;
892               first = false;
893             }
894           unsigned ncmds = m->num_cmds();
895           Jdb_module::Cmd const *cmds = m->cmds();
896           for(unsigned x = 0; x < ncmds; x++)
897             {
898               char const *descr = cmds[x].descr;
899               if (!descr)
900                 continue;
901               size_t pos = strcspn(descr, "\n\t");
902               size_t xpos = 2;
903               putstr("  ");
904               while( pos  < strlen(descr))
905                 {
906                   putnstr(descr, pos);
907                   xpos += pos;
908                   switch(descr[pos])
909                     {
910                     case '\n':
911                       putchar('\n');
912                       if(!Jdb_core::new_line(line))
913                         return NOTHING;
914                       putstr("  ");
915                       xpos = 2;
916                       break;
917                     case '\t':
918                       if(xpos<tab_width)
919                         {
920                           putnstr("                            ",
921                                   tab_width-xpos);
922                           xpos = tab_width;
923                         }
924                       else
925                         {
926                           putchar(' ');
927                           xpos++;
928                         }
929                       break;
930                     default:
931                       break;
932                     }
933                   descr = descr + pos + 1;
934                   pos = strcspn(descr, "\n\t");
935                 }
936
937               putstr(descr);
938               putchar('\n');
939               if(!Jdb_core::new_line(line))
940                 return NOTHING;
941             }
942         }
943     }
944
945   putchar('\n');
946
947   return NOTHING;
948 }
949
950 PUBLIC
951 int Help_m::num_cmds() const
952
953   return 2;
954 }
955
956 PUBLIC
957 Jdb_module::Cmd const * Help_m::cmds() const
958 {
959   static Cmd cs[] =
960     {
961         { 0, "h", "help", "", "h\tShow this help screen.", 0 },
962         { 0, "?", 0, "", 0, 0 },
963     };
964
965   return cs;
966 }
967
968 IMPLEMENT
969 Help_m::Help_m()
970   : Jdb_module("GENERAL")
971 {}
972
973 #define CAT(n, x...) static Jdb_category INIT_PRIORITY(JDB_CATEGORY_INIT_PRIO) n(x)
974 CAT(a, "GENERAL",    "general debugger commands",      0);
975 CAT(b, "INFO",       "information about kernel state", 1);
976 CAT(c, "MONITORING", "monitoring kernel events",       2);
977 CAT(d, "DEBUGGING",  "real debugging stuff",           3);
978 #undef CAT