/* help.c -- online help for dbx */

#include "defs.h"
#include "tree.h"
#include "symbols.h"
#include "keywords.h"
#include "process.h"
#include "runtime.h"
#include "source.h"
#include "object.h"
#include "mappings.h"
#include "y.tab.h"

static String help_commands[] ={
    "alias", "assign", "call",
    "catch", "clear", "cleari", "cont", "debug", "delete", /*"detach", */
    "down", "dump", "edit", "file", "func", "goto", "gotoi", 
#ifdef	MAINTAINER
    "gripe",
#endif	/* MAINTAINER */
    "help", "ignore", "list", "listi", /*"multproc",*/
    "next", "nexti", "print", /*"prompt",*/ "psym", "quit",
    "registers", "rerun", "return", "run", /*"screen",*/ "set", "sh", 
    "source", "status", "step", "stepi", "stop", "stopi", "trace",
    "tracei", "unalias", "unset", "up", "use", /*"watch",*/ "whatis", "when",
    "where", "whereis", "which", /*"wins", "wini",*/ 0
};

static String help_topics[] = {
   "execution", "expressions", 
   "files", "printing", 
   "usage", "variables",
   0
};

static void
help_help()
{
   String *p;
   int len, i;

   puts("Commands:");
   i = len = 0;
   for ( p = help_commands; *p; p++ ){
      printf(" %s ",*p);
      i = strlen(*p); /* there is a bug in printf!! on the RT */
      while ( i++ < 11 )	
	printf(" ");
      len += 11;
      if ( len >= 65 ){
	 printf("\n");
	 len = 0;
      }
   }
   puts("\nTopics:");
   for ( p = help_topics; *p; p++ ){
      printf(" %s ",*p);
   }
   puts("\n\nType \"help <command>\" or \"help <topic>\" for help on a command or topic.\n");
}

/*
 * help_topic:
 * See if the user wants help on a given topic
 */

private help_topic(s)
   char *s;
{
   if ( !strncmp(s,"exe",3) ){
      puts("Execution controlling commands:");
      puts("See: run, rerun, stop, status, catch, ignore, cont");
      puts("step, next, return and call commands.");
   }
   else if ( !strncmp(s,"exp",3) ){
      puts("Expressions are specified with an approximately common");
      puts("subset of C and Pascal syntax. Indirection can be denoted");
      puts("using either a prefix \"*\" or a postfix \"^\" and array");
      puts("expressions are subscripted by brackets (\"[\" \"]\"). The");
      puts("field reference operator (\".\") can be used with pointers");
      puts("as well as structures, unions, and records, making the C");
      puts("operator \"->\" unnecessary (although it is supported).");
      puts("");
      puts("Types of expressions are checked; the type of an expression");
      puts("may be overridden by using \"type-name(expression)\". When");
      puts("there is no corresponding named type the special constructs");
      puts("\"$type-name\" and \"$$tag-name\" can be used to represent a");
      puts("pointer to a named type or C structure tag.");
   }
   else if ( !strncmp(s,"fil",3) ){
      puts("Accessing source files:");
      puts("/<regular-expression>[/]");
      puts("?<regular-expression>[?]");
      puts("\tSearch (respectively) forward or backward in the current");
      puts("\tsource file for the given <regular-expression>.");
      puts("See also:");
      puts("\tedit, file, func, list and use commands.");
   }
   else if ( !strncmp(s,"pri",3) ){
      puts("Address level printing.");
      puts("<address> , <address>/ [<mode>]");
      puts("<address> / [<count>]  [<mode>]");
      puts("\tPrint the contents of memory starting at the first");
      puts("\t<address> up to the second <address> or until <count>");
      puts("\titems are printed. If the address is \".\", the address");
      puts("\tfollowing the one most recently printed is used. The mode");
      puts("\tspecifies how memory is to be printed; if it is omitted the");
      puts("\tprevious mode specified is used. The initial mode is \"X\".");
      puts("\tThe following modes are supported:");
      puts("");
      puts("\ti   print the machine instruction");
      puts("\td   print a short word in decimal");
      puts("\tD   print a long word in decimal");
      puts("\to   print a short word in octal");
      puts("\tO   print a long word in octal");
      puts("\tx   print a short word in hexadecimal");
      puts("\tX   print a long word in hexadecimal");
      puts("\tb   print a byte in octal");
      puts("\tc   print a byte as a character");
      puts("\ts   print a string (terminated by a null byte)");
      puts("\tf   print a single precision real number");
      puts("\tg   print a double precision real number");
      puts("");
      puts("Symbolic addresses are specified by preceding the name with");
      puts("an \"&\". Registers are denoted by \"$rN\" where N is the");
      puts("number of the register. Addresses may be expressions made");
      puts("up of other addresses and the operators \"+\", \"-\", and");
      puts("indirection ( unary \"*\").");
   }
   else if ( !strncmp(s,"usa",3) ){
      puts("Basic dbx command usage:\n");
      puts("run                    - begin execution of the program");
      puts("print <exp>            - print the value of the expression");
      puts("where                  - print currently active procedures");
      puts("stop at <line>         - suspend execution at the line");
      puts("stop in <proc>         - suspend execution when <proc> is called");
      puts("cont                   - continue execution");
      puts("step                   - single step one line");
      puts("next                   - step to next line (skip over calls)");
      puts("trace <line#>          - trace execution of the line");
      puts("trace <proc>           - trace calls to the procedure");
      puts("trace <var>            - trace changes to the variable");
      puts("trace <exp> at <line#> - print <exp> when <line> is reached");
      puts("status                 - print trace/stop's in effect");
      puts("delete <number>        - remove trace or stop of given number");
      puts("call <proc>            - call a procedure in program");
      puts("whatis <name>          - print the declaration of the name");
      puts("list <line>, <line>    - list source lines");
      puts("registers              - display register set");
      puts("quit                   - exit dbx");
   }
   else if ( !strncmp(s,"var",3) ){
      puts("\tThe following \"set\" variables have special meanings:");
      puts("");
      puts("\t$frame");
      puts("\t\tSetting this variable to an address alters dbx's");
      puts("\t\tidea of the current stack frame.");
      puts("");
      puts("\t$expandunions");
      puts("\t\tCauses dbx to display values of each part of variant");
      puts("\t\trecords or unions.");
      puts("");
      puts("\t$hexchars $hexints $hexstrings");
      puts("\t\tWhen set, dbx prints characters, integers or");
      puts("\t\tcharacter pointers in hexadecimal.");
      puts("");
      puts("\t$listwindow");
      puts("\t\tSpecifies the number of lines to list and listi commands.");
      puts("");
      puts("\t$mapaddrs");
      puts("\t\tControls address mapping, useful for kernel debugging.");
      puts("");
      puts("\t$unsafecall $unsafeassign");
      puts("\t\tTurn off type checking for calls and assignments.");
   }
   else   printf("%s is not a known help topic or command\n",s);
}

private help_command(op,cmd)
   int op;
   char *cmd;
{
   switch ( op ){

    case ALIAS:
      puts("alias");
      puts("alias <name> <name>");
      puts("alias <name> \"<string>\"");
      puts("alias <name> ( <parameters> ) \"<string>\"");
      puts("\tWhen commands are processed, dbx first checks to see if");
      puts("\tthe word is an alias for either a command or a string.");
      puts("\tIf it is an alias, then dbx treats the input as though");
      puts("\tthe corresponding string (with values substituted for ");
      puts("\tany parameters) had been entered.");
      puts("\tAlias with no arguments prints the alias definition list.");
      break;

    case ASSIGN:
      puts("assign <variable> = <expression>");
      puts("\tAssign the value of the expression to the variable.");
      break;

    case CALL:
      puts("call <procedure> ( <parameters> )");
      puts("\tExecute the object code associated with the named");
      puts("\tprocedure or function");
      break;
#ifdef 0
    case CASE:
      puts("case");
      puts("case mixed");
      puts("case lower");
      puts("case upper");
      puts("case default");
      puts("\tSet character case sensitivity.");
      puts("\tCase with no arguments prints the current case sensitivity.");
      break;
#endif
    case CATCH:
    case IGNORE:
      puts("catch <signal-number>");
      puts("catch <signal-name>");
      puts("ignore <signal-number>");
      puts("ignore <signal-name>");
      puts("\tStart or stop trapping a signal before it is sent to");
      puts("\tthe program. This is useful when a program being");
      puts("\tdebugged handles signals such as interrupts. A signal");
      puts("\tmay be specified by number or name.");
      puts("\tSignal names are by default case insensitive and the \"SIG\"");
      puts("\tprefix is optional. By default all signals are trapped");
      puts("\texcept SIGCONT, SIGCHILD, SIGALRM and SIGKILL.");
      break;

    case CONT:
      puts("cont");
      puts("cont <signal-number>");
      puts("cont <signal-name>");
      puts("\tContinue execution from where it stopped. If a signal");
      puts("\tis specified, the process continues as though it");
      puts("\treceived the signal. Otherwise, the process is continued");
      puts("\tas though it had not been stopped.");
      break;

    case DELETE:
      puts("delete <command-number>");
      puts("delete all");
      puts("\tThe traces or stops corresponding to the given numbers");
      puts("\tare removed. The numbers associated with traces and stops");
      puts("\tare printed with the \"status\" command.");
      break;

    case DOWN:
    case UP:
      puts("up");
      puts("down");
      puts("up <count>");
      puts("down <count>");
      puts("\tMove the current function, which is used for resolving");
      puts("\tnames, up or down the stack <count> levels. The default");
      puts("\t<count> is one.");
      break;

    case DUMP:
      puts("dump");
      puts("dump .");
      puts("dump <procedure> [ > <filename> ]");
      puts("\tPrint the names and values of variables in the given");
      puts("\tprocedure, or the current one if none is specified. If");
      puts("\tthe procedure given is \'.\', then all active variables");
      puts("\tare dumped.");
      break;

    case EDIT:
      puts("edit");
      puts("edit <filename>");
      puts("edit <procedure>");
      puts("\tInvoke an editor on <filename> or the current source file");
      puts("\tif none is specified. If a procedure or function name");
      puts("\tis specified the editor is invoked on the file that");
      puts("\tcontains it. Which editor is invoked by default depends");
      puts("\ton the installation. The default can be overridden by");
      puts("\tsetting the environment variable EDITOR to the name of");
      puts("\tthe desired editor.");
      break;

    case FILE:
      puts("file");
      puts("file <filename>");
      puts("\tChange the current source file name to <filename>. If");
      puts("\tnone is specified then the current source file name is");
      puts("\tprinted.");
      break;

    case FUNC:
      puts("func");
      puts("func <procedure>");
      puts("\tChange the current function. If none is specified then");
      puts("\tprint the current function. Changing the current function");
      puts("\timplicitly changes the current source file to the");
      puts("\tone that contains the function; it also changes the");
      puts("\tcurrent scope used for name resolution.");
      break;

    case CLEAR:
      puts("clear <line-number>");
      puts("\tRemove all breakpoints at a given line number.");
      puts("\tSee also: delete");
      break;

    case CLEARI:
      puts("cleari <address>");
      puts("\tRemove all breakpoints at a given address.");
      puts("\tSee also: delete");
      break;

    case DEBUG:
      puts("debug <number>");
      puts("\tdbx internal debug switches");
      break;

    case GOTOI:
    case GOTO:
      puts("goto <line-number>");
      puts("goto \"<filename>\" : <line-number>");
      puts("gotoi <address>");
      puts("\tChange the program counter to <address> or an address");
      puts("\tnear <line-number>. The variable $unsafegoto must be set");
      puts("\tif a goto out of the current function is desired.");
      break;

    case HELP:
      puts("help");
      puts("help <command>");
      puts("help <topic>");
      puts("\tPrint information about command or topic.");
      break;

    case LIST:
      puts("list [ <source-line-number> [, <source-line-number> ]]");
      puts("list <procedure>");
      puts("\tList the lines in the current source file from the");
      puts("\tfirst line number to the second inclusive. If no lines");
      puts("\tare specified, the next 10 lines are listed. If the");
      puts("\tname of a procedure or function is given lines n-k to");
      puts("\tn+k are listed where n is the first statement in the");
      puts("\tprocedure or function and k is defined by $listwindow");
      break;

    case LISTI:
      puts("listi");
      puts("listi <source-line-number>");
      puts("listi <address>");
      puts("listi <procedure>");
      puts("\tList the instructions from the current program counter");
      puts("\tlocation or given address, line number or procedure.");
      puts("\tThe number of instructions printed is controlled by");
      puts("\tthe dbx internal variable $listwindow.");
      break;

    case NEXT:
      puts("next");
      puts("\tExecute up to the next source line. The difference between");
      puts("\tthis and \"step\" is that if the line contains a call");
      puts("\tto a procedure or function the \"step\" command will");
      puts("\tstop at the beginning of that block, while the \"next\"");
      puts("\tcommand will not.");
      break;

    case NEXTI:
      puts("nexti");
      puts("\tSingle step as in \"next\", but do a single instruction");
      puts("\trather than source line.");
      break;

    case PRINT:
      puts("print <expression> [, <expression>]");
      puts("\tNames are resolved first using the static scope of the");
      puts("\tcurrent function, then using the dynamic scope if the name");
      puts("\tis not defined in the static scope. If static and dynamic");
      puts("\tsearches do not yield a result, an arbitrary symbol is");
      puts("\tchosen and the message \"[using <qualified-name>]\" is");
      puts("\tprinted. The name resolution procedure may be overridden by");
      puts("\tqualifying an identifier with a block name, e.g.,");
      puts("\t\"module.variable\". For C, source files are treated as");
      puts("\tmodules named by the file name without \".c\"");
      puts("");
      break;
#ifdef 0
    case PROMPT:
      puts("prompt");
      puts("\tDisplays the dbx prompt.");
      break;
#endif
    case PSYM:
      puts("psym <name>");
      puts("\tPrint the dbx symbol table entry for <name>.");
      break;

    case QUIT:
      puts("quit");
      puts("\tExit dbx.");
      break;

    case REGISTERS:
      puts("registers");
      puts("\tPrint the contents of all machine registers.");
      break;

    case RERUN:
    case RUN:
      puts("run [<arguments>] [< <filename>] [> <filename>] ");
      puts("rerun [<arguments>] [< <filename>] [> <filename>] ");
      puts("\tStart executing the object file, passing arguments as");
      puts("\tcommand line arguments; < or > can be used to redirect");
      puts("\tinput or output in a shell-like manner. When \"rerun\"");
      puts("\tis used without any arguments the previous argument list");
      puts("\tis passed to the program; otherwise it is identical to run.");
      puts("\tIf the object file has been written to since the last time");
      puts("\tthe symbolic information was read in, dbx will read in the");
      puts("\tnew information");
      break;

    case RETURN:
      puts("return");
      puts("return <procedure>");
      puts("\tContinue until a return to <procedure> is executed, or");
      puts("\tuntil the current procedure returns if none is specified.");
      break;

    case SET:
      puts("set <name>");
      puts("set <name> = <expression>");
      puts("\tThe set command defines values for dbx variables.");
      puts("\tThe names of these variables cannot conflict with names");
      puts("\tin the program being debugged, and are expanded to the");
      puts("\tcorresponding expression within other commands.");
      puts("\tSee also \"help variables\" for definitions of predefined");
      puts("\tset variables.");
      break;

    case SH:
      puts("sh <command-line>");
      puts("\tPass the command line to the shell for execution.");
      puts("\tThe SHELL environment variable determines which shell is used.");
      break;

    case SOURCE:
      puts("source <filename>");
      puts("\tRead dbx commands from the given file.");
      break;

    case STATUS:
      puts("status [> <filename>]");
      puts("\tPrint out the currently active trace and stop commands.");
      break;

    case STEP:
      puts("step");
      puts("\tExecute one source line.");
      break;

    case STEPI:
      puts("stepi");
      puts("\tExecute a single instruction");
      break;

    case STOP:
      puts("stop if <condition>");
      puts("stop at <line-number> [if <condition>]");
      puts("stop in <procedure>   [if <condition>]");
      puts("stop <variable>       [if <condition>]");
      puts("\tStop execution when the given line is reached,");
      puts("\tprocedure or function entered, variable changed,");
      puts("\tor condition true.");
      break;

    case STOPI:
      puts("stopi <address>    [if <condition>]");
      puts("stopi at <address> [if <condition>]");
      puts("\t\"stopi at <address>\" stops execution when the given");
      puts("\t<address> is reached.");
      puts("\t\"stopi <address>\" stops execution when the value located");
      puts("\tat the given <address> changes.");
      break;

    case UNALIAS:
      puts("unalias <name>");
      puts("\tRemove the alias for <name>.");
      break;

    case UNSET:
      puts("unset <name>");
      puts("\tRemove the definition for <name>.");
      break;

    case USE:
      puts("use <directory-list>");
      puts("\tSet the list of directories to be searched when looking");
      puts("\tfor source files.");
      break;

    case WHATIS:
      puts("whatis <name>");
      puts("\tPrint the declaration of the given name.");
      break;

    case WHERE:
      puts("where");
      puts("\tPrint out a stack trace of the currently active procedures");
      puts("\tand functions.");
      break;

    case WHEREIS:
      puts("whereis <name>");
      puts("\tPrint the full qualification of all symbols whose name");
      puts("\tmatches <name>.");
      break;

    case WHICH:
      puts("which <name>");
      puts("\tPrint the full qualification of the given <name>,");
      puts("\ti.e the outer blocks that the name is associated with.");
      break;

    case TRACE:
      puts("trace [in <procedure>]              [if <condition>]");
      puts("trace <line-number>                 [if <condition>]");
      puts("trace <procedure> [in <procedure>]  [if <condition>]");
      puts("trace <variable>  [in <procedure>]  [if <condition>]");
      puts("trace <expression> at <line-number> [if <condition>]");
      puts("\tHave tracing information printed when the program is");
      puts("\texecuted. A number is associated with the command so");
      puts("\tthat tracing can be turned off with the delete command.");
      break;

    case TRACEI:
      puts("tracei [<address>] [if <condition>]");
      puts("tracei [<variable>] [at <address>] [if <condition>]");
      puts("\tTurn on tracing using a machine instruction address.");
      puts("See trace command.");
      break;

    case WHEN:
      puts("when in <procedure>   { <actions> }");
      puts("when at <line-number> { <actions> }");
      puts("when at <address>     { <actions> }");
      puts("when <expression>     { <actions> }");
      puts("\tWhen <expression> is true, execute <actions>.");
      puts("\tWhere <actions> is a list of dbx commands.");
      break;
#ifdef 0
    case SCREEN:
      puts("screen");
      puts("\tSwitch dbx to another virtual terminal.");
      break;

    case WATCH:
      puts("watch <expression> [if <condition>]");
      puts("watch <expression> [in <procedure>]  [if <condition>]");
      puts("watch <expression> [at <line-number>] [if <condition>]");
      puts("\tSee trace command.");
      break;

    case MULTPROC:
    case DETACH:
    case WINS:
    case WINI:
      printf("%s is currently undocumented\n",cmd);
      break;
#endif
    default:
      printf("no help available for %s\n",cmd);
   }
   puts("");
}

/*
 * Give the user some help.
 */

public help(n)
   Node n;
{
   Node name;
   int op;

   name = n->value.arg[0];

   if ( name == nil ){
     help_help();
     return;
  }
   /* resolve aliasing first ...???

   if ( findalias(name->value.name,&s1,&s2) ){
       expand(p,str);
   }

   */

   op = findkeyword(name->value.name,0);

   if ( op )
      help_command(op,ident(name->value.name));
   else 
      help_topic(ident(name->value.name));
}
