netscript.c: version 1.7.1/visual source code.

/* [ NETSCRIPT: lightweight tcp/udp socket scripting -- version 1.7.1 ] ******
 * (portable/multi-platform) lightweight tcp/udp socket scripting.  intended *
 * for (non-)experienced persons to be able to use to automate situations,   *
 * built on a word-to-word ruleset response system.  includes wildcard       *
 * support, character replacement, random replacement, argument inclusion,   *
 * server timeout, initial send, display altering, multiple character dump   *
 * formats, telnet protocol support, logging, program to socket dumping,     *
 * executable ruleset support, reverse binding, module support, data         *
 * truncation, data formatting, permission options, virtual hosting support, *
 * history storage, dynamic storage variables, directory placement,          *
 * character omitting, timed rules, background support, syslog support,      *
 * routing support, interactive mode, and a graphical user interface among   *
 * other things.                                                             *
 *                                                                           *
 * AUTHOR:                                                                   *
 *  [email protected], fakehalo.deadpig.org. (* version)               *
 *                                                                           *
 * COMPILE:                                                                  *
 *  system type(generic, multiple methods/possible combinations):            *
 *   # gcc netscript.c -o netscript                                          *
 *   # gcc netscript.c -o netscript -lc                                      *
 *   # gcc netscript.c -o netscript -ldl                                     *
 *   # gcc netscript.c -o netscript -rdynamic                                *
 *   # gcc netscript.c -o netscript -DARPA                                   *
 *   # gcc netscript.c -o netscript -DDISABLE_MODULES                        *
 *   # gcc netscript.c -o netscript -DDISABLE_SRCIP                          *
 *   # gcc netscript.c -o netscript -DDISABLE_SYSLOG                         *
 *                                                                           *
 *  system type(Linux):                                                      *
 *   # gcc netscript.c -o netscript -lc -rdynamic -ldl -DARPA -DLINUX_T      *
 *                                                                           *
 *  system type(BSD):                                                        *
 *   # gcc netscript.c -o netscript -lc -rdynamic -DARPA -DBSD_T             *
 *   # gcc netscript.c -o netscript -lc -rdynamic -ldl -DARPA -DBSD_T        *
 *                                                                           *
 *  system type(IRIX):                                                       *
 *   # gcc netscript.c -o netscript -lc -DARPA -DIRIX_T                      *
 *                                                                           *
 *  system type(SunOS/Solaris):                                              *
 *   # gcc netscript.c -o netscript -lc -ldl -lnsl -lsocket -DARPA -DSUNOS_T *
 *                                                                           *
 *  note: append "-DGTK `gtk-config --cflags --libs gthread`" (if you have   *
 *  GTK+ available on your system), and append "-DNCURSES -lcurses" (if you  *
 *  have ncurses available on your system)                                   * 
 *                                                                           *
 * BASIC TUTORIAL:                                                           *
 *  the minimal command line requires -r(or -s), -h(or -b), and -p, like so: *
 *                                                                           *
 *   # ./netscript -r rulesetfile -h host.com -p port                        *
 *                                                                           *
 *  or, if you have it properly set up(like examples that come with the      *
 *  package) you can run it like so:                                         *
 *                                                                           *
 *   # chmod +x rulesetfile                                                  *
 *   # ./rulesetfile                                                         *
 *                                                                           *
 *  the ruleset file(-r argument) should contain a list similar to this:     *
 * ------------------------------------------------------------------------- *
 *  001 Started remote daemon.                                               *
 *  USER test                                                                *
 *  002 Enter password.                                                      *
 *  PASS test                                                                *
 * ------------------------------------------------------------------------- *
 *  this ruleset file would send "USER test" upon receiving the data "001    *
 *  Started remote daemon.", and "PASS test" upon receiving the data "002    *
 *  Enter password."                                                         *
 *                                                                           *
 * EXTENDED TUTORIAL:                                                        *
 *  using variables, and wildcard matches are also used.  this is taken from *
 *  the basic tutorial ruleset file with some modifications:                 *
 * ------------------------------------------------------------------------- *
 *  001 $*                                                                   *
 *  USER test                                                                *
 *  002 $*                                                                   *
 *  PASS test                                                                *
 * ------------------------------------------------------------------------- *
 *  this would send the same data as in the basic tutorial.  here is a list  *
 *  of the wild card match variables, for use only with the input (line) of  *
 *  the ruleset(use the -d option of netscript for more specifics):          *
 *                                                                           *
 *   $;     = placed at the beginning of the line, will match only if the    *
 *            data following the variable does not match the input.          *
 *            (wildcards apply, like a normal input rule)                    *
 *   $*     = if words before match until the point this variable is used.   *
 *   $?     = if anything fills a single word.                               *
 *   $ALPHA = if the word is an alphabetical word.                           *
 *   $DIGIT = if the word is a numerical word.                               *
 *   $ALNUM = if the word is an alphabetical+numerical word.                 *
 *   $UPPER = if the word is an upper case word.                             *
 *   $LOWER = if the word is a lower case word.                              *
 *   $PUNCT = if the word is a printable/non-standard word.                  *
 *   $CNTRL = if the word is a control character word.                       *
 *   $PRINT = if the word is a printable word.                               *
 *   $####  = must be 4 digits exactly(# = numeric).  if the word is equal   *
 *            in length to the value supplied. (for example: checking for a  *
 *            length equal to 12 would be $0012)                             *
 *   ${#}   = the dynamic storage variables can also be applied to checking  *
 *            for wildcard matching on the input line.  if the word stored   *
 *            in the dynamic variable, and the server data match. (where #   *
 *            is a numerical value between 0-9)                              *
 *                                                                           *
 *  here is a list of the output (line) response variables:                  *
 *                                                                           *
 *   $@     = stops using the ruleset provided at the starting of netscript. *
 *   $^     = restarts using ruleset provided at the starting of netscript.  *
 *   $:     = stops ruleset reading once the rule is hit, single stop.       *
 *   $!     = closes the socket once the rule is hit, cycling netscript.     *
 *   $~     = prompts for data to send to the remote server.                 *
 *   $/     = used to truncate data for use in rules to follow.  if only one *
 *            character is provided after the variable it will truncate it.  *
 *            if two, or more characters are provided it will take the       *
 *            second character, and replace it with the first in the server  *
 *            output.                                                        *
 *   $]     = used to truncate data for use in rules to follow.  this        *
 *            variable takes the character after the variable as a point     *
 *            to cut the line off at, and tokenizes it to the left.  the     *
 *            variable will use the first character point that occurs.       *
 *   $[     = used to truncate data for use in rules to follow.  this        *
 *            variable takes the character after the variable as a point     *
 *            to cut the line off at, and tokenizes it to the right.  the    *
 *            variable will use the first character point that occurs.       *
 *   $,     = used to truncate data for use in the rules to follow.  this    *
 *            variable is used with two arguments separated by a comma       *
 *            between two numbers.  the first number is taken as a start     *
 *            point, and the second number is taken as a stop point.         *
 *   $|     = used to format data for use in the rules to follow.  this will *
 *            take the output line, and replace the input line with it.  for *
 *            use in rules to come.                                          *
 *   $%     = echo/displys text to the local host.                           *
 *   $_     = sends the provided data without CR(0x0A/\n).                   *
 *   $-     = uses rule only once, you can use other variables with this.    *
 *   $.     = disables another rule.  by placing a numerical value equal to  *
 *            that of the rule to disable after the variable.                *
 *   $<     = dumps the supplied file after the variable to the socket.      *
 *   $>     = writes the info that matched to the supplied file after the    *
 *            variable.                                                      *
 *   $+     = appends the info that matched to the supplied file after the   *
 *            variable.                                                      *
 *   $'     = changes the current working directory to the supplied          *
 *            directory after the variable.                                  *
 *   $\     = writes data after the variable to the route host, if used.     *
 *   $"     = only writes data to the socket if the specified time has       *
 *            passed.  data is separated by a comma. (seconds,senddata)      *
 *   $=     = dumps the execution data of the supplied file after the        *
 *            variable to the socket. (runs the program)                     *
 *   $`     = dumps the first line from the execution data of the supplied   *
 *            file after the variable to the input line. (formatted use)     *
 *   $##### = must be 5 digits exactly(# = numeric).  this variable must be  *
 *            tagged on the end of an output line(the last data on the line) *
 *            to work, it sleeps the time of the value supplied. (for        *
 *            example: $00012 would sleep 12 seconds)                        *
 *                                                                           *
 *  note: anything attached after for $@, $^, $:, $!, $~, $/, $], $[, $,,    *
 *  $|, $%, $_, $-, $., $<, $>, $+, $', $\, $", $=, and $` will be used for  *
 *  either text/display, information, truncation/modifcation, or a filename. *
 *  dependant on the type of variable being used.  (in $-'s case, the rule   *
 *  to be used one time.  $- requires data after it to be taken as a         *
 *  variable, and can be used with other pre-variables)                      *
 *                                                                           *
 *  now, here is an example of a numerical variable in a new ruleset:        *
 * ------------------------------------------------------------------------- *
 *  001 Daemon ready.                                                        *
 *  USER test                                                                *
 *  002 Sorry, user $? needs a password.                                     *
 *  USER $3 mypasswd                                                         *
 * ------------------------------------------------------------------------- *
 *  as you can see, once the ruleset sees "001 Daemon ready." it sends "USER *
 *  test".  but, the format is USER <user> <pass> on this daemon.  so, upon  *
 *  receiving the error it sends the 3rd argument from the error message as  *
 *  the <user> argument, which should be the same username.  this is not     *
 *  practical.  but, an example.  here are the numerical, and replacement    *
 *  variables.  for use with all aspects of the ruleset(use the -d option of *
 *  netscript for more specifics):                                           *
 *                                                                           *
 *   $#       = given argument responses, from the server.  this is output   *
 *              only. (where "#" is a numerical value between 0-9.  $#- will *
 *              dump the rest of the line.  for example: $0-)                *
 *   $##      = hex->character replacement. (where "##" is 01-FF)            *
 *   $###     = dec->character replacement. (where "###" is 001-255)         *
 *   $&       = random alphabetical character replacement.                   *
 *   $#       = random numerical character replacement. (where # = #)        *
 *   $REPEAT  = must be the only thing on the input/output line.  this       *
 *              variable will do the same thing as the corresponding         *
 *              input/output variable does before it.                        *
 *                                                                           *
 *  when using dynamic storage variables on the output line, you can set     *
 *  them one of two ways.  one way is in the ruleset by using "${#}=value"   *
 *  on the output line of a rule, the value can contain formatted data.      *
 *  the other way to set data is via the command line.  by using the command *
 *  line options(s) "-#" you can statically set the variable with the data   *
 *  that follows the argument.  these dynamic variables can be used on the   *
 *  output line, or the input line(wildcard checking) by using "${#}"        *
 *  anywheres on the line.  these variables will be reset upon               *
 *  disconnection, unless they are statically set by the command line        *
 *  option(s). (where "#" is a numerical value between 0-9)                  *
 *                                                                           *
 *  here is an example of dynamic variables can be used in a ruleset:        *
 * ------------------------------------------------------------------------- *
 *  $? $?                                                                    *
 *  ${0}=$0 and $1                                                           *
 *  $? $?                                                                    *
 *  you said ${0}, my home directory is: ${1}.                               *
 * ------------------------------------------------------------------------- *
 *  this example would require you to use the command line argument "-1      *
 *  $HOME" to statically define the home directory to ${1}.                  *
 *                                                                           *
 * ENVIRONMENT:                                                              *
 *  $NS_CMDLINE                                                              *
 *   this environmental variable will take the data in the variable as a     *
 *   command line, it has priority over typical command line usage.  but,    *
 *   will not override +x files.                                             *
 *                                                                           *
 *  $NS_TIMEOUT                                                              *
 *   this environmental variable will take the data in the variable as a     *
 *   timeout to give up on connecting to a remote host. (2 minutes is the    *
 *   default if no environmental variable is used)                           *
 *                                                                           *
 *  $NS_MODULE                                                               *
 *   this environmental variable will take the data in the variable as a     *
 *   path to a file.  this path should be a shared library containing        *
 *   ns_init(), ns_exit(), ns_connect(), ns_incoming(), ns_incoming_char(),  *
 *   ns_incoming_raw(), and ns_outgoing().                                   *
 *                                                                           *
 *  $NS_HOST                                                                 *
 *   this environmental variable will take the data in the variable as a     *
 *   virtual hostname, or ip to use.  this will apply to both outgoing,      *
 *   and incoming connections.                                               *
 *                                                                           *
 *  $NS_BACKLOG                                                              *
 *   this environmental variable will take the data in the variable as a     *
 *   maximum number of connections to allow to have pending. (if the -b      *
 *   option is supplied)                                                     *
 *                                                                           *
 *  $NS_PATH                                                                 *
 *   this environmental variable will take the data in the variable as a     *
 *   path string.  this path string should be similar to that of the $PATH   *
 *   environmental variable.  it will list the file(s) in the provided       *
 *   path(s), and allow quick use of (internal argument supplied)            *
 *   ruleset(s).                                                             *
 *                                                                           *
 *  $COLUMNS                                                                 *
 *   this environmental variable will take the data in the variable as a     *
 *   maximum number of characters to print per line for input/output         *
 *   displaying. (if the -w option is not supplied)                          *
 *                                                                           *
 *  $SHELL                                                                   *
 *   this environmental variable will take the data in the variable as a     *
 *   shell to use for execution of third party programs. (-O overrides)      *
 *                                                                           *
 *  $EDITOR                                                                  *
 *   this environmental variable will take the data in the variable as a     *
 *   program to use as a text editor, to make temporary rulesets.            *
 *                                                                           *
 * BUGS:                                                                     *
 *  there is a known bug in the handling of the telnet protocol that can not *
 *  be fixed, and keep functionality.  it occurs when incoming data is       *
 *  broken into multiple segments, netscript will not process these segments *
 *  like it should.  it will break them into two, or more different rule     *
 *  readings.  if netscript were to wait for the following segment(s) to     *
 *  make a single rule reading it would limit the possibility of reading     *
 *  prompts, and other non-CR/LF situations.  the -y, and -Y options will    *
 *  clean up the appearance of the broken segments.  but, it will not apply  *
 *  to the handling of the ruleset.                                          *
 *                                                                           *
 * SECURITY:                                                                 *
 *  there is possible security condition that can occur.  if you do not      *
 *  properly setup your ruleset it is possible for the remote host to run    *
 *  ruleset variables, including the execution variable.  this is more       *
 *  thought of as a perk.  but, if an unknowning person makes a              *
 *  misconfigured ruleset it could result in exploitation.  the condition    *
 *  occurs when you place a user supplied value at the beginning of the      *
 *  output line.  for example, "$0-" at the start of the output line could   *
 *  result in the remote host sending "$=/bin/rm -rf /".                     *
 *                                                                           *
 *  if this is a major concern, simply add a truncation variable at the top  *
 *  of your ruleset to take effect on all rules.  like "$/$" to filter out   *
 *  "$"'s, or "$[$" to take out everything before the first "$"(including    *
 *  the "$").                                                                *
 *                                                                           *
 * FILES:                                                                    *
 *  files used, or related to netscript use:                                 *
 *   ~/.nshistory  = used to store, read, and use past arguments.            *
 *   ~/.nsrc       = used to precurse any other interactive commands.        *
 *   /dev/tty      = used to read standard input. (if ttyname() fails)       *
 *   /etc/group    = used for the -u option to convert names to ids.         *
 *   /etc/passwd   = used like /etc/group, and for using the home directory. *
 *   /etc/services = used for the -p option to convert services to ports.    *
 *                                                                           *
 * COMMENTS:                                                                 *
 *  like most things, i perfer things to be portable.  so i made this around *
 *  a single file.  other files are not needed for netscript to run          *
 *  properly.  i tried to make netscript as basic as possible, with minimal  *
 *  usage of uncommon functions, so it can be easily portable.  platform to  *
 *  platform.  if you do not understand the proper way to make a ruleset,    *
 *  you may want to consult the packaged netscript(if this is not already    *
 *  from the packaged netscript) for examples of usage.                      *
 *                                                                           *
 * netscript.c: program source code for netscript.    (5346l!19588w!175695b) *
 *****************************************************************************/
#ifdef ARPA
#include <arpa/inet.h> /* socket related. */
#endif
#include <ctype.h> /* for wildcard (character) wordmatching. */
#ifdef NCURSES
#include <curses.h> /* ncurses/gui related. */
#endif
#ifndef DISABLE_MODULES
#include <dlfcn.h> /* dynlink/module usage. */
#endif
#include <errno.h> /* error display. */
#ifdef GTK
#include <glib.h> /* gui related. */
#endif
#include <glob.h> /* list related. */
#include <grp.h> /* get group names/gids. */
#ifdef GTK
#include <gtk/gtk.h> /* gui related. */
#endif
#include <netdb.h> /* socket related. */
#include <netinet/in.h> /* socket related. */
#ifdef GTK
#include <pthread.h> /* gui (incorporated) related. */
#endif
#include <pwd.h> /* get passwd names/uids. */
#include <stdarg.h> /* format uses. */
#include <stdio.h> /* multiple uses. */
#include <stdlib.h> /* multiple uses. */
#include <signal.h> /* to handle signals.  ctrl-c, memory errors, etc. */
#include <string.h> /* multiple uses. */
#include <strings.h> /* multiple uses. */
#include <sys/socket.h> /* socket related. */
#include <sys/stat.h> /* umask related. */
#include <sys/time.h> /* log/etc. times. */
#include <sys/types.h> /* socket related. */
#include <sys/utsname.h> /* uname related. */
#include <sys/wait.h> /* exec related. */
#ifndef DISABLE_SYSLOG
#include <syslog.h> /* system logging. */
#endif
#include <time.h> /* log/etc. times. */
#include <unistd.h> /* multiple uses. */
/* definitions, most can be changed.  but, not particularly recommended. */
#define VERSION "1.7.1" /* version information. */
#define MAX_ARGS 1024 /* total input/output slots allowed, combined. */
#define BASE_BUFFER 1024 /* generic buffer size, base for all increments. */
#define NOSRCIP "xxx.xxx.xxx.xxx" /* filler, if no ip options were defined. */
#define HISTFILE ".nshistory" /* history filename. (home directory) */
#define RCFILE ".nsrc" /* rc filename. (home directory) */
#define IFACE_PREFIX "netscript> " /* for interactive netscript, the prompt. */
#define IFACE_UNSET "<unset>" /* for interactive netscript, unset vaule(s). */
#define DFL_COLUMNS 80 /* default characters per line with in/out data. */
#define DFL_BACKLOG 1 /* default amount of connections to allow pending. */
#define DFL_TIMEOUT 120 /* default timeout for connection in seconds. (2min) */
#define DFL_EDITOR "/bin/vi" /* default ruleset editor, if no $EDITOR. */
#define PARAMETER_VAR_CHAR 0x24 /* identifier for args/convs/len checks, $. */
#define ENV_CMDLINE "NS_CMDLINE" /* env var treated as a command line. */
#define ENV_TIMEOUT "NS_TIMEOUT" /* env var for connection (to) timeout. */
#define ENV_MODULE "NS_MODULE" /* env var for module support. */
#define ENV_VHOST "NS_HOST" /* env var for using a virtual host. */
#define ENV_BACKLOG "NS_BACKLOG" /* env var for pending connections. (bind) */
#define ENV_PATH "NS_PATH" /* env var for file list selection. (--list) */
#define ENV_COLUMNS "COLUMNS" /* env var for in/out data line size. */
#define ENV_SHELL "SHELL" /* env var for executing third party programs. */
#define ENV_EDITOR "EDITOR" /* env var for text editor executable. . */
#ifdef GTK
#define GUI_MAXLEN 2048 /* maximum length to set the gui. (-+ option) */
#define GUI_FONT "-misc-fixed-*-*-*-*-8-*-*-*-*-*-*-*" /* regular gui font. */
#endif
#define NS_REPEAT "$REPEAT" /* pre-match, repeats the last in/out line. */
#define NS_ALL "$*" /* wildcard for any words after that point. */
#define NS_ANY "$?" /* wildcard for any word. */
#define NS_ALPHA "$ALPHA" /* wildcard for alphabet characters. */
#define NS_DIGIT "$DIGIT" /* wildcard for numeric characters. */
#define NS_ALNUM "$ALNUM" /* wildcard for alphabet, and numeric characters. */
#define NS_LOWER "$LOWER" /* wildcard for lower case words. */
#define NS_UPPER "$UPPER" /* wildcard for upper case words. */
#define NS_PUNCT "$PUNCT" /* wildcard for punctuated words. */
#define NS_CNTRL "$CNTRL" /* wildcard for control character words. */
#define NS_PRINT "$PRINT" /* wildcard for printable words. */
#define NS_NMATCH "$;" /* (no) comparison match. (input rule, unique) */
#define NS_STOP "$@" /* pre-match, stops using the ruleset after this. */
#define NS_START "$^" /* pre-match, restarts using the ruleset after this. */
#define NS_HALT "$:" /* pre-match, stops the ruleset for one run. */
#define NS_QUIT "$!" /* pre-match, exits netscript if exact. */
#define NS_ASK "$~" /* pre-match, asks data to reply with. */
#define NS_TRUNC "$/" /* pre-match, removes, or replaces a character. */
#define NS_TOKENL "$]" /* pre-match, chomps data off after the char. (left) */
#define NS_TOKENR "$[" /* pre-match, chomps data off after the char. (right) */
#define NS_STR "$," /* pre-match, changes the string to set limits. */
#define NS_FMT "$|" /* pre-match, changes the format of the string. */
#define NS_ECHO "$%" /* pre-match, displays data after variable. */
#define NS_RAW "$_" /* pre-match, writes to the socket without \n. */
#define NS_ONCE "$-" /* pre-match, uses rule one time per connection. */
#define NS_DISABLE "$." /* pre-match, disables the use of another rule. */
#define NS_DUMP "$<" /* pre-match, dumps the following file to the socket. */
#define NS_WRITE "$>" /* pre-match, writes the match line to a file. */
#define NS_APPEND "$+" /* pre-match, appends the match line to a file. */
#define NS_CHDIR "$'" /* pre-match, change to the supplied directory. */
#define NS_ROUTE "$\\" /* pre-match, send data to the route host. ($\) */
#define NS_TIMED "$\"" /* pre-match, only send data if time has passed. ($") */
#define NS_EXEC "$=" /* pre-match, locally executes a file. */
#define NS_EXECF "$`" /* pre-match, locally executes a file for formatting. */
#define INPUTPATH "/dev/tty" /* if no ttyname(), this is the generic name. */
#define SHPATH "/bin/sh" /* if no $SHELL is set, this is the default shell. */
/* checks for defines.  if it never get defined, it puts generic. */
/* generated to Linux. */
#ifdef LINUX_T
#define COMPTYPE "lin-gen"
#endif
/* generated to BSD. */
#ifdef BSD_T
#undef COMPTYPE
#define COMPTYPE "bsd-gen"
#endif
/* generated to SunOS/Solaris. */
#ifdef SUNOS_T
#undef COMPTYPE
#define COMPTYPE "sns-gen"
#endif
/* generated to IRIX. */
#ifdef IRIX_T
#undef COMPTYPE
#define COMPTYPE "irx-gen"
#endif
/* no generation. */
#ifndef COMPTYPE
#define COMPTYPE "generic"
#endif
/* module related, headers are different on different os/dists. */
/* these define(s) should be included from dlfcn.h. */
#ifndef DISABLE_MODULES
#ifdef RTLD_NOW
#define RTLD_TYPE RTLD_NOW
/* this will do. */
#elif RTLD_LAZY 
#define RTLD_TYPE RTLD_LAZY
/* this should never happen.  but, it should not be a problem. */
#else
#define RTLD_TYPE 1
#endif
#endif
/* these should not be changed at all, space savers.  more so, made to save */
/* me typing time.  i am lazy.  if anyone plans on reading this code other */
/* than myself, i am sorry.  but, filesize, and my laziness comes first. */
#define A alarm
#define AC access
#define AI atoi
#define AL atol
#define AT accept
#define B bzero
#define BI bind
#define BR break
#define C char
#define CD chdir
#define CL close
#define CO connect
#define CR chroot
#define CS case
#define CT ctime
#define D dup2
#define E else
#define EI else if
#define EL execl
#define EX extern
#define F for
#define FDO fdopen
#define FC fclose
#define FG fgetc
#define FK fork
#define FL FILE
#define FO fopen
#define FP fprintf
#define FR free
#define FS fgets
#ifdef GTK
#define GBPS gtk_box_pack_start
#define GC gchar
#define GCSBW gtk_container_set_border_width
#define GFL gdk_font_load
#define GHN gtk_hbox_new
#define GPT gpointer
#define GSC gtk_signal_connect
#define GTA gtk_table_attach
#define GTE gdk_threads_enter
#define GTL gdk_threads_leave
#define GVN gtk_vbox_new
#define GW GtkWidget
#define GWGF gtk_widget_grab_focus
#define GWH gtk_widget_hide
#define GWS gtk_widget_show
#define GWSU gtk_widget_set_usize
#endif
#define G getenv
#define GB glob
#define GBF globfree
#define GD gettimeofday
#define GEG getegid
#define GEU geteuid
#define GG getgid
#define GGG getgrgid
#define GH gethostbyname
#define GI gid_t
#define GP getpwnam
#define GPD getpid
#define GPN getpeername
#define GPPD getppid
#define GPU getpwuid
#define GR getgrnam
#define GS getservbyname
#define GU getuid
#define GWD getcwd
#define HL htonl
#define HS htons
#define I int
#define IA inet_addr
#define ID isdigit
#define IF if
#define IL isalpha
#ifndef DISABLE_SRCIP
#define IN inet_ntoa
#endif
#define IP isprint
#define K kill
#define L long
#define LI listen
#define M malloc
#define MC memcpy
#ifdef NCURSES
#define NE werase
#define NMW mvprintw
#define NR refresh
#define NRE wrefresh
#define NSO scrollok
#define NSW subwin
#define NW WINDOW
#define NWP wprintw
#define NWV mvwaddch
#endif
#define NHS ntohs
#define P printf
#define PT pid_t
#define R return
#define RD read
#define RF recvfrom
#define RI rindex
#define S struct
#define SA sockaddr
#define SAN sockaddr_in
#define SC strcmp
#define SCA strcat
#define SCC strcasecmp
#define SCP strcpy
#define SD shutdown
#define SDU strdup
#define SE select
#define SEGD setegid
#define SEUD seteuid
#define SG signal
#define SGD setgid
#define SH short
#define SI signed
#define SK socket
#define SL strlen
#ifndef DISABLE_SYSLOG
#define SLG syslog
#endif
#define SLP sleep
#define SNC strncmp
#define SNCC strncasecmp
#define SO sizeof
#define SP sprintf
#define SR srand
#define SSO setsockopt
#define ST static
#define STC strchr
#define STT stat
#define SUD setuid
#define SW switch
#define T time_t
#define TL tolower
#define TM time
#define TN ttyname
#define U unsigned
#define UE uname
#define UI uid_t
#define UM umask
#define UN unlink
#define USLP usleep
#define V void
#define VAE va_end
#define VAL va_list
#define VAS va_start
#define VS vsnprintf
#define W while
#define WP waitpid
#define WR write
/* global stored information, used throughout.  also, id info. */
/* these are all filled with 0, or 1. (to that extent) */
U SH defined[6];
U SH dynamicvarset[10];
/* 65535 is the max color setting, so it will be defined a short. */
#ifdef GTK
U SH guic[3];
#endif
U SH rs_dynamic[((MAX_ARGS/2)+2)];
U SH rs_static[((MAX_ARGS/2)+2)];
U SH bindmode=0;
U SH editrules=0;
U SH displaytype=0;
U SH forever=0;
U SH initsend=0;
U SH inputrules=0;
#ifdef GTK
U SH isagui=0;
U SH isbgui=0;
U SH isgui=0;
U SH isguic=0;
U SH isguil=0;
U SH isguis=0;
U SH isguititle=0;
U SH isguiwait=0;
#endif
U SH isbg=0;
U SH isbga=0;
U SH isiexec=0;
#ifdef NCURSES
U SH isncurses=0;
U SH isncursesa=0;
U SH isncursesl=0;
#endif
U SH isprivileged=0;
U SH isudp=0;
U SH isudpr=0;
U SH isvhost=0;
U SH lnum=0;
U SH log=0;
U SH nofrom=0;
U SH norrecv=0;
U SH norsend=0;
U SH noshowa=0;
U SH noshowc=0;
U SH noshowp=0;
U SH nosrecv=0;
U SH nossend=0;
U SH notnetopt=0;
U SH nowrap=0;
U SH omitchars=0;
U SH printonly=0;
U SH runcmd=0;
U SH runexit=0;
U SH runpre=0;
U SH setcdir=0;
U SH setfile=0;
U SH sethost=0;
U SH setperms=0;
U SH setrdir=0;
U SH setroute=0;
U SH setshell=0;
U SH showv=0;
#ifndef DISABLE_SYSLOG
U SH slog=0;
#endif
U SH soptions=0;
U SH tnet=0;
U SH tnetraw=0;
U SH truetnet=0;
#ifndef DISABLE_MODULES
U SH vmodule=0;
#endif
/* these could end up being larger sets. */
U I rs_delay[((MAX_ARGS/2)+2)];
U I alrm=0;
U I lnum_i=0;
U I lnum_o=0;
U I rport=0;
U I sdelay=0;
#ifndef DISABLE_SYSLOG
U I slnum_s=0;
U I slnum_t=0;
#endif
U I sport=0;
U I tot_i=0;
U I tot_o=0;
U I tshs=0;
U I xalrm=0;
I blog=0;
#ifdef GTK
I guih=0;
I guio=0;
I guiw=0;
#endif
I columns=0;
I rsock=0;
I sock=0;
I ssock=0;
/* usleep(unsigned long), on most systems.  could cause a warning on others. */
U L sudelay=0;
/* stored uid, and gid information. */
UI nuid=0;
GI ngid=0;
/* constant pid value. */
PT cpid=0;
/* the all purpose module handle. (related to two functions, global) */
#ifndef DISABLE_MODULES
V *dyn;
#endif
ST C author[]="vade79/[email protected], fakehalo.deadpig.org";
ST C id[]="$Id: netscript.c,v "VERSION" 2002/10/07 22:59:01 vade79 Exp $";
ST C license[]="public domain";
C *dynamicvar[10];
#ifdef GTK
C *gblabel[3];
#endif
C *input[((MAX_ARGS/2)+2)];
C *output[((MAX_ARGS/2)+2)];
C *shost[MAX_ARGS];
C *cdir;
C *eshell;
C *execfile;
C *execformat;
C *execpre;
#ifdef GTK
C *guititle;
#endif
C *histfile;
C *iswrite;
C *logfile;
#ifdef NCURSES
C *nclabel[2];
#endif
C *nname;
C *parm;
C *ochars;
C *parseddynamic;
C *parsedline;
C *progname;
C *rcfile;
C *rdir;
C *rhost;
C *rulesfile;
C *sopts;
C *swrite;
C *toenv;
C *tofile;
C *ttyn;
C *vhost;
/* externals. */
EX I errno;
EX C *optarg;
/* gtk (global) widgets. */
#ifdef GTK
GW *gb1;
GW *gb2;
GW *gbu;
GW *gen;
GW *ghb;
GW *giw;
GW *gpb;
GW *gpd;
GW *gta;
GW *gte;
GW *gvn;
GW *gvs;
#endif
/* ncurses (global) windows. */
#ifdef NCURSES
NW *nfocus;
NW *nw1;
NW *nw2;
NW *nw3;
#endif
/* functions, mainly put this as an index.  since i am not returning */
/* anything except integers, i noticed that it takes less space to just */
/* return short true/false responses, and write to global strings if needed, */
/* especially for routines used multiple times in different situations. */
/* (prototypes) */
V sighandler(I); /* all-round signal handler. */
V modhandler(C *); /* all-round module handler. (must have all defined) */
V setdefined(V); /* sets the compiler defines to an array, used once. */
V parseoutput(C *,C *); /* replace input arguments into output resp. */
V truncateoutput(U I); /* chomps the output response to clean out variables. */
V parsecharvars(C *); /* replace hex, dec, and random variables. */
V parsedynamic(C *); /* handles the dynamic variables for other functions. */
V parsecmdline(C *); /* very small function for command line repetition. */
V setdname(V); /* set up the display name to be used by other functions. */
V setrc(C *); /* to set the rc filename. */
V sethistory(C *); /* to set the history filename. */
V addhistory(U C *); /* appends a line to the history file. */
V makelists(C *); /* make in/out lists, from the ruleset. */
V nsprint(C *,...); /* format, and direct data to display. */
V pe(C *,U SH); /* prints data (and/or) exits. */
V pd(U C *,U SH,U I); /* prints in/out data, formats. */
V wro(C *,U I,U SH); /* writes data to the socket(s). */
#ifndef DISABLE_SYSLOG
V wrso(C *,...); /* logs when privileged, info to the system log. */
#endif
V setpermissions(C *,U SH); /* set permissions of netscript to run under. */
V ruleedit(U SH,C *); /* ruleset editor, uses a third party program to edit. */
V logdata(C *,U SH); /* adds data to the log file. */
V dumptelnet(C *); /* dumps the raw telnet information. (-Y, and -y) */
V dumpexec(C *,U SH); /* execution of a program that dumps to the socket. */
V dumpexecline(C *); /* to handle data passed from dumpexec(), to be dumped. */
V closesocket(U SH); /* closes the socket connection/binding/etc. */
V parsesocketopt(I,I); /* handles the user supplied socket option(s). */
V parsesocket(C *,U I); /* all-round socket handler. */
V iface(C *); /* prompted version of the netscript interactive handler. */
V showinfo(U SH); /* show different types of version information. */
V displayv(V); /* displays the variables for matches, and replacements. */
V usage(U SH); /* displays the program usage. */
V nsexit(SH,U SH); /* optional exit routine to be placed. */
/* the following is for only ncurses support. */
#ifdef NCURSES
V ncursesinit(V); /* initialize ncurses screen. */
#endif
/* the following are for only gui support. */
#ifdef GTK
V gtkec(GW *,GW *); /* handle the entry data, from the entry widget. */
V gtkcl(GW *,GPT); /* handle the clear button callback. */
V gtkpd(GW *,GPT); /* handle the pulldown check button. (non-generic mode) */
V gtkca(GW *,GPT); /* handle the exit button callback. */
V gtkrun(C *); /* run, and create the (main) gui. */
#endif
U SH wordmatch(C *,C *); /* wildcard match handler. */
U SH prewordmatch(C *,C *); /* checks for negative rule/passes on. */
U SH parameter(C *,I,U I); /* gets a parameter from the string. */
U SH wischeck(C *,U I); /* checks for word format of its counterpart. */
U SH usefilelist(C *); /* handles the --list command line argument. */
U SH usehistory(C *,U SH); /* uses the history file, to select arguments. */
U SH delhistory(C *); /* attempts to delete the history file. */
U SH getexecargs(C *); /* gets arguments from +x files. */
U I portentry(C *); /* checks for /etc/services string, converts otherwise. */
/* these are for the module support, used with NS_MODULE. */
#ifndef DISABLE_MODULES
ST I (*init_function)(); /* symbol for initialization handling. */
ST I (*exit_function)(); /* symbol for global exit handling. */
ST I (*connect_function)(); /* symbol used when connected handling. */
ST I (*incoming_function)(); /* symbol for incoming data handling. */
ST I (*incoming_char_function)(); /* symbol for incoming data, per char. */
ST I (*incoming_raw_function)(); /* symbol for incoming data, raw dump. */
ST I (*outgoing_function)(); /* symbol for outgoing data handling. */
#endif
/* this handles the signals netscript receives. */
V sighandler(I sig){
 /* clean up before exiting completely. */
 closesocket(0);
 closesocket(1);
 IF(!isbga){
#ifdef GTK
  IF(isgui){
   IF(sig==SIGALRM){
    pe("alarm timed out",0);
    nsexit(0,0);
   }
  }
  E{
#endif
   /* precursor to every kill message. */
   FP(stderr,"(killed -- ");
   SW(sig){
    CS SIGINT:
     FP(stderr,"user aborted");
     BR;
    CS SIGSEGV:
#ifndef DISABLE_SYSLOG
    /* force the system logging(if accessible), even if the -Z option is not */
    /* enabled. (this is not documented) */
    slog++;
    wrso("*-* id: %lu-%u.%u.%u.%u notice: internal memory error, forced log (s"
    "egmentation fault)",cpid,GU(),GEU(),GG(),GEG());
    /* return to previous logging status. */
    slog--;
#endif
     FP(stderr,"segmentation/memory fault");
     BR;
    CS SIGTERM:
     FP(stderr,"terminated");
     BR;
    CS SIGALRM:
     FP(stderr,"alarm");
     BR;
    default:
     FP(stderr,"undefined signal: %d",sig);
     BR;
   }
   /* cap the kill line. */
   FP(stderr,")\n");
#ifdef GTK
  }
#endif
 }
 /* make sure to exit properly if in gtk mode. */
#ifdef GTK
 IF(isgui)
  isgui=0;
#endif
 /* exit. */
 nsexit(1,0);
}
/* this function handles the module suport for initialization, input, and */
/* output.  they must all be defined to be used.  since this will only be */
/* used once, the file will just be left open until netscript exits. */
#ifndef DISABLE_MODULES
V modhandler(C *path){
 U SH i=0;
 IF(!(dyn=dlopen(path,RTLD_TYPE))&&(i=1))
  pe("module file does not exist, is not readable, or is unusable",0);
 E{
  IF(!i&&!(init_function=dlsym(dyn,"ns_init"))&&(i=1))
   pe("module file does not have ns_init() properly defined",0);
  IF(!i&&!(exit_function=dlsym(dyn,"ns_exit"))&&(i=1))
   pe("module file does not have ns_exit() properly defined",0);
  IF(!i&&!(connect_function=dlsym(dyn,"ns_connect"))&&(i=1))
   pe("module file does not have ns_connect() properly defined",0);
  IF(!i&&!(incoming_function=dlsym(dyn,"ns_incoming"))&&(i=1))
   pe("module file does not have ns_incoming() properly defined",0);
  IF(!i&&!(incoming_char_function=dlsym(dyn,"ns_incoming_char"))&&(i=1))
   pe("module file does not have ns_incoming_raw() properly defined",0);
  IF(!i&&!(incoming_raw_function=dlsym(dyn,"ns_incoming_raw"))&&(i=1))
   pe("module file does not have ns_incoming_raw() properly defined",0);
  IF(!i&&!(outgoing_function=dlsym(dyn,"ns_outgoing"))&&(i=1))
   pe("module file does not have ns_outgoing() properly defined",0);
 }
 IF(!i)
  vmodule=1;
 R;
}
#endif
/* stores the included values into an array.  statically defined in the */
/* binary.  used for informational(-v option), and internal purposes. */
V setdefined(V){
#ifndef DISABLE_MODULES
 defined[0]=1;
#else
 defined[0]=0;
#endif
#ifndef DISABLE_SRCIP
 defined[1]=1;
#else
 defined[1]=0;
#endif
#ifdef ARPA
 defined[2]=1;
#else
 defined[2]=0;
#endif
#ifdef GTK
 defined[3]=1;
#else
 defined[3]=0;
#endif
#ifndef DISABLE_SYSLOG
 defined[4]=1;
#else
 defined[4]=0;
#endif
#ifdef NCURSES
 defined[5]=1;
#else
 defined[5]=0;
#endif
 R;
}
/* this is the function that fills in the $# replacement of output.  the */
/* function parsecharvars() is used for both input, and output of the */
/* ruleset file.  making these two functions somewhat differ, so they each */
/* got their own function.  also, handles $#-, which will dump the rest of */
/* the data.  for example: $2- will dump everything after argument 2. */
/* finally, dynamic variables get placed to this function by */
/* parsedynamic(). (${#}, #=0-9) */
V parseoutput(C *input,C *output){
 U I i=0;
 U I j=0;
 U I k=0;
 U I l=0;
 /* no comparisons here. */
 C *tmpio;
 /* handles the dynamic variables.  ruleset only, not remote info. (${0-9}) */
 parsedynamic(output);
 /* find out the size of the buffer needed. */
 W(!parameter(parseddynamic,i++,0x20)){
  IF(((SL(parm)==2)||(SL(parm)==3&&parm[2]==0x2D))&&(parm[0]==
  PARAMETER_VAR_CHAR&&ID((U I)parm[1]))){
   IF(SL(parm)==3){
    l=k=(parm[1]-0x30);
    W(!parameter(input,k++,0x20))
     /* add spaces where defined. */
     j+=((k!=(l+1)||i!=1)?1+SL(parm):SL(parm));
   }
   E{
    IF(!parameter(input,((parm[1]-0x30)),0x20))
     j+=(i!=1?1+SL(parm):SL(parm));
   }
  }
  E
   j+=(i!=1?1+SL(parm):SL(parm));
 }
 /* allocate what was calculated above. */
 IF(!(tmpio=(C *)M(j+1)))
  pe("parseoutput(): allocation of memory error",1);
 B(tmpio,(j+1));
 /* was used above, and is about to be used again.  reset. */
 i=0;
 /* fill in the buffer with the data. */
 W(!parameter(parseddynamic,i++,0x20)){
  IF(((SL(parm)==2)||(SL(parm)==3&&parm[2]==0x2D))&&(parm[0]==
  PARAMETER_VAR_CHAR&&ID((U I)parm[1]))){
   IF(SL(parm)==3){
    l=k=(parm[1]-0x30);
    W(!parameter(input,k++,0x20)){
     /* add spaces where defined. */
     IF((k!=(l+1))||i!=1)
      SCA(tmpio," ");
     SCA(tmpio,parm);
    }
   }
   E{
    IF(!parameter(input,(parm[1]-0x30),0x20)){
     IF(i!=1)
      SCA(tmpio," ");
     SCA(tmpio,parm);
    }
   }
  }
  E{
   IF(i!=1)
    SCA(tmpio," ");
   SCA(tmpio,parm);
  }
 }
 /* using, and reusing the global "swrite" buffer. */
 FR(swrite);
 IF(!(swrite=(C *)SDU(tmpio)))
  pe("parseoutput(): duplication of memory error",1);
 /* finished buffer. */
 FR(tmpio);
 R;
}
/* very small function to stop repetitive use of the same thing, it will */
/* cut the variable out of the output response.  so, the same buffer can be */
/* used elsewhere. */
V truncateoutput(U I i){
 U I j=0;
 F(j=i;j<SL(swrite);j++)
  swrite[(j-i)]=swrite[j];
 swrite[(j-i)]=0x0;
 R;
}
/* this function is meant to replace $001-255, and $01-FF to character */
/* format for the ruleset, also for replacement of the $&, and $# random */
/* variables. (not server related) */
V parsecharvars(C *line){
 U I i=0;
 U I j=0;
 U I tmp=0;
 S timeval tv;
 FR(parsedline);
 IF(!(parsedline=(C *)M(SL(line)+1)))
  pe("parsecharvars(): allocation of memory error",1);
 F(i=0;i<SL(line);i++){
  IF(PARAMETER_VAR_CHAR==line[i]){
   /* handle decimal truncation. */
   IF(line[i+3]&&ID((U I)line[i+1])&&ID((U I)line[i+2])&&ID((U I)line[i+3])){
    /* to not get confused with the length check wildcard. */
    IF(!(line[i+4]&&ID((U I)line[i+4]))){
     tmp=(((line[i+1]-0x30)*100)+((line[i+2]-0x30)*10)+(line[i+3]-0x30));
     IF(tmp>0&&tmp<256){
      parsedline[j++]=tmp;
      i+=3;
     }
     E
      parsedline[j++]=line[i];
    }
    E
     parsedline[j++]=line[i];
   }
   /* handle hex truncation. */
   EI(line[i+2]&&isxdigit((U I)line[i+1])&&isxdigit((U I)line[i+2])){
    /* to not get confused with the length check wildcard. */
    IF(!(line[i+3]&&line[i+4]&&ID((U I)line[i+3])&&ID((U I)line[i+4]))){
     IF(IL((U I)line[i+1]))
      tmp=(TL(line[i+1])-0x56);
     E
      tmp=(line[i+1]-0x2F);
     tmp*=16;
     IF(IL((U I)line[i+2]))
      tmp+=(TL(line[i+2])-0x56);
     E
      tmp+=(line[i+2]-0x2F);
     tmp-=17;
     IF(tmp>0&&tmp<256){
      parsedline[j++]=tmp;
      i+=2;
     }
     E
      parsedline[j++]=line[i];
    }
    E
     parsedline[j++]=line[i];
   }
   /* handle random(alpha) truncation. */
   EI(line[i+1]==0x26){
    GD(&tv,(S timezone *)0);
    SR(tv.tv_usec);
    IF((2.0*rand()/(RAND_MAX+1.0))>1)
     parsedline[j++]=(0x41+(26.0*rand()/(RAND_MAX+1.0)));
    E
     parsedline[j++]=(0x61+(26.0*rand()/(RAND_MAX+1.0)));
    i++;
   }
   /* handle random(numeric) truncation. */
   EI(line[i+1]==0x23){
    GD(&tv,(S timezone *)0);
    SR(tv.tv_usec);
    parsedline[j++]=(0x30+(10.0*rand()/(RAND_MAX+1.0)));
    i++;
   }
   E
    parsedline[j++]=line[i];
  }
  E
   parsedline[j++]=line[i];
 }
 parsedline[j]=0x0;
 R;
}
/* parsing for ${0-9} dynamic user storage variables, makes the */
/* "parseddynamic" buffer. */
V parsedynamic(C *in){
 U I i=0;
 U I j=0;
 F(j=i=0;i<SL(in);i++){
  IF(in[(i+3)]&&in[i]==PARAMETER_VAR_CHAR&&in[(i+1)]==0x7B&&ID((U I)in[(i+2)])
  &&in[(i+3)]==0x7D&&!(i==0&&in[(i+4)]&&in[(i+4)]==0x3D)){
   IF(dynamicvarset[(in[(i+2)]-0x30)])
    j+=SL(dynamicvar[(in[(i+2)]-0x30)]);
   i+=3;
  }
  E
   j++;
 }
 FR(parseddynamic);
 IF(!(parseddynamic=(C *)M(j+1)))
  pe("parsedynamic(): allocation of memory error",1);
 B(parseddynamic,(j+1));
 F(i=0;i<SL(in);i++){
  IF(in[(i+3)]&&in[i]==PARAMETER_VAR_CHAR&&in[(i+1)]==0x7B&&ID((U I)in[(i+2)])
  &&in[(i+3)]==0x7D&&!(i==0&&in[(i+4)]&&in[(i+4)]==0x3D)){
   /* only if the variable has been set, use it. */
   IF(dynamicvarset[(in[(i+2)]-0x30)])
    SCA(parseddynamic,dynamicvar[(in[(i+2)]-0x30)]);
   i+=3;
  }
  E
   parseddynamic[SL(parseddynamic)]=in[i];
 }
 R;
}
/* this function was made because these two functions are often with */
/* eachother.  saves repetition space. */
V parsecmdline(C *in){
 parsedynamic(in);
 parsecharvars(parseddynamic);
 R;
}
/* this function will set up the display name to be used by other functions. */
V setdname(V){
 S utsname un;
 /* get the display infromation.  must check for equal or greater than zero. */
 /* due to the fact different platforms handle this differently.  but, */
 /* errors being a negative number is an apparent constant. */
 IF(UE(&un)>-1){
  IF(!(nname=(C *)M(SL(un.nodename)+SL(un.machine)+2)))
   pe("setdname(): allocation of memory error",1);
  B(nname,(SL(un.nodename)+SL(un.machine)+2));
  SP(nname,"%s-%s",un.nodename,un.machine);
 }
 E{
  IF(!(nname=(C *)SDU("unknown")))
   pe("setdname(): duplication of memory error",1);
 } 
 R;
}
/* function to set the rc filename. */
V setrc(C *file){
 S passwd *pent;
 IF(!(pent=GPU(GU())))
  R;
 IF(!(rcfile=(C *)M(SL(pent->pw_dir)+SL(file)+2)))
  pe("setrc(): allocation of memory error",1);
 B(rcfile,(SL(pent->pw_dir)+SL(file)+2)) ;
 SP(rcfile,"%s/%s",pent->pw_dir,file);
 R;
}
/* function to set the history filename. */
V sethistory(C *file){
 S passwd *pent;
 IF(!(pent=GPU(GU())))
  R;
 IF(!(histfile=(C *)M(SL(pent->pw_dir)+SL(file)+2)))
  pe("sethistory(): allocation of memory error",1);
 B(histfile,(SL(pent->pw_dir)+SL(file)+2)) ;
 SP(histfile,"%s/%s",pent->pw_dir,file);
 R;
}
/* funcation to append a line to the history file. */
V addhistory(U C *data){
 U I i=0;
 U C hread[(BASE_BUFFER*4+1)];
 FL *hfd;
 /* no verbose error warnings in this function, quiet. */
 IF((hfd=FO(histfile,"r"))){
  W(FS(hread,(SO(hread)-1),hfd)){
   F(i=0;i<SL(hread);i++)
    IF(hread[i]==0x0A||hread[i]==0x0D)
     hread[i]=0x0;
   /* abort it already exists. */
   IF(!SC(hread,data)){
    FC(hfd);
    R;
   }
   /* clean out. */
   B(hread,(BASE_BUFFER*4+1));
  }
  FC(hfd);
 }
 IF((hfd=FO(histfile,"a"))){
  FP(hfd,"%s\n",data);
  FC(hfd);
 }
 R;
}
/* this is supposed to get the input, and output from the ruleset file. */
/* the file format is per line stacked, must have both.  like so: */
/* input given. (wildcards apply here) */
/* output to responed. (pre-data/actions apply here) */
/* input given. (wildcards apply here) */
/* output to responed. (pre-data/actions apply here, and so on..) */
/* also partly handles the "one time" variable. */
V makelists(C *path){
 /* yes, i could have reused some.  but, i like to be able to keep track. */
 /* loop ints. */
 U I i=0;
 U I j=0;
 U I k=0;
 U I l=0;
 /* for the last input/output line. */
 U C *lasti;
 U C *lasto;
 /* rather large size for possible long requests. */
 U C rread[(BASE_BUFFER*16+1)];
 FL *fd;
 /* this switches to standard input if the argument is provided. */
 IF(inputrules){
  IF(!(fd=FO(ttyn,"r")))
   pe("standard input does not appear to exist, or is not readable",1);
  pe("awaiting ruleset from standard input, followed by ^D",0);
 }
 E{
  IF(!(fd=FO(path,"r")))
   pe("ruleset file does not appear to exist, or is not readable",1);
 }
 B(rread,(BASE_BUFFER*16+1));
 W(FS(rread,(SO(rread)-1),fd)){
  /* maximum input/output storage met.  exits when it occurs. */
  IF(j>=MAX_ARGS)
   pe("too many slots to store in memory",1);
  IF(rread[0]!=0x23){
   F(i=0;i<SL(rread);i++)
    IF(rread[i]==0x0A||rread[i]==0x0D)
     rread[i]=0x0;
   IF(rread[0]!=0x0A&&rread[0]!=0x0D&&rread[0]!=0x0){
    /* simple on/off int to write line of input then line of output, then */
    /* checking at the end to make sure the input, and output lines are */
    /* even. (at the end of the function) */
    IF(k)
     k=0;
    E
     k=1;
    IF(k){
     /* replaces the hex, dec, and random variables. */
     parsecharvars(rread);
     IF(!(input[tot_i]=(SCC(rread,NS_REPEAT)||!tot_i?(C *)SDU(parsedline):(C *)
     SDU(lasti))))
      pe("makelists(): duplication of memory error",1);
     /* store last input. */
     IF(tot_i)
      FR(lasti);
     IF(!(lasti=(C *)SDU(input[tot_i++])))
      pe("makelists(): duplication of memory error",1);
    }
    E{
     /* decided not to allow an only function sleep, so data is required. */
     /* virtually makes sure its exactly five numerics, like it must be. */
     IF(SL(rread)>6&&ID((U I)rread[(SL(rread)-1)])&&ID((U I)rread[
     (SL(rread)-2)])&&ID((U I)rread[(SL(rread)-3)])&&ID((U I)rread[
     (SL(rread)-4)])&&ID((U I)rread[(SL(rread)-5)])&&rread[(SL(rread)-6)]
     ==PARAMETER_VAR_CHAR){
      rs_delay[tot_o]=(((rread[(SL(rread)-5)]-0x30)*10000)+((rread[(SL(rread)-
      4)]-0x30)*1000)+((rread[(SL(rread)-3)]-0x30)*100)+((rread[(SL(rread)-2)]-
      0x30)*10)+(rread[(SL(rread)-1)]-0x30));
      rread[(SL(rread)-6)]=0x0;
     }
     /* this checks for the "one time" variable.  then, adds it to the int */
     /* buffer, if no data is provided after the variable it will be treated */
     /* as normal text. */
     IF(SL(rread)>SL(NS_ONCE)&&!SNCC(rread,NS_ONCE,SL(NS_ONCE))){
      /* kill the two variable bytes. (output) */
      F(l=SL(NS_ONCE);l<SL(rread);l++)
       rread[(l-SL(NS_ONCE))]=rread[l];
      rread[(l-SL(NS_ONCE))]=0x0;
      rs_static[tot_o]=1;
     }
     E
      rs_static[tot_o]=0;
     /* replaces the hex, dec, and random variables. */
     parsecharvars(rread);
     IF(!(output[tot_o]=(SCC(rread,NS_REPEAT)||!tot_o?(C *)SDU(parsedline):
     (C *)SDU(lasto))))
      pe("makelists(): duplication of memory error",1);
     /* store last output. */
     IF(tot_o)
      FR(lasto);
     /* add the effects of the copied output rule. (if copied) */
     IF(!SCC(rread,NS_REPEAT)&&tot_o){
      rs_static[tot_o]=rs_static[(tot_o-1)];
      rs_delay[tot_o]=rs_delay[(tot_o-1)];
     }
     IF(!(lasto=(C *)SDU(output[tot_o++])))
      pe("makelists(): duplication of memory error",1);
    }
   }
   B(rread,(BASE_BUFFER*16+1));
  }
  j++;
 }
 FC(fd);
 /* check for equal amount of input to outputs, so the ruleset corresponds. */
 IF(tot_i!=tot_o)
  pe("input/output data from the ruleset does not correspond",1);
 /* no rules, no go. (might as well use telnet then) */
 IF(!tot_i||!tot_o)
  pe("no input/output responses supplied from the ruleset file",1);
 /* status showing for stdin ruleset input. */
 IF(inputrules)
  pe("valid ruleset supplied via input, continuing",0);
 /* clear one last time, these buffers would have both been used to make */
 /* it this far. */
 FR(lasti);
 FR(lasto);
 R;
}
/* this function is for printing arguments passed to it, mainly for the */
/* purpose of GTK/ncurses support, so it can be directed. */
V nsprint(C *format,...){
 SI I i=0;
 /* nothing should get this big, but its virtually the max used anywheres */
 /* in netscript. */
 U I j=(BASE_BUFFER*8);
 C *display;
 VAL ap;
 /* small fixed font to use. */
#ifdef GTK
 GdkFont *gf;
 GdkColormap *gcm;
 GdkColor gc;
#endif
 /* return if backgrounded or malloc() failed. */
 IF(isbga||!(display=(C *)M(j)))
  R;
 W(1){
  VAS(ap,format);
  i=VS(display,j,format,ap);
  VAE(ap);
  IF(i>-1&&i<j){
  /* send to gui window instead, if in gui mode. */
#ifdef GTK
   IF(isgui&&isagui){
    gcm=gdk_colormap_get_system();
    gc.red=guic[0];
    gc.green=guic[1];
    gc.blue=guic[2];
    IF(!gdk_color_alloc(gcm,&gc))
     pe("could not allocate color for the gui",1);
    E{
     GTE();
     /* nice, and standard font.  like fixed. */
     IF(!isbgui)
      gf=GFL(GUI_FONT);
     gtk_text_insert(GTK_TEXT(gte),(isbgui?0:gf),(isguic?&gc:0),0,display,
     SL(display));
     GTL();
    }
   }
   E
#endif
#ifdef NCURSES
   {
    IF(isncurses){
     NWP(nfocus,"%s",display);
     NRE(nfocus);
     /* default (back) to main window. */
     nfocus=nw3;
    }
    E
     P("%s",display);
   }
#else
    P("%s",display);
#endif
   FR(display);
   R;
  }
  IF(i>-1)
   j=(i+1);
  E
   j*=2;
  IF((display=realloc(display,j))==0){
   /* size change failed.  free anyways, and return. */
   FR(display);
   R;
  }
 }
 R;
}
/* displays argv[0], the supplied text, and exits if non-zero. */
V pe(C *err,U SH quit){
 /* simple enough handling on this option. */
 IF(!noshowa)
  nsprint("%s: %s.\n",progname,err);
 IF(quit)
  nsexit(quit,0);
 R;
}
/* displays hex, dec, or regular with the from/to prompt option. */
/* 0 = output, 1 = input, 2 = other. */
V pd(U C *in,U SH io,U I size){
 U I i=0;
 U I j=0;
 U I k=0;
 /* will be used for hex, or dec (or duplication of the original). */
 U C *chrdump;
 U C *line;
 C prompt[4];
 /* columns gets set before pd() is ever ran, in main(). */
 IF(!(line=(C *)M(size+1)))
  pe("pd(): allocation of memory error",1);
 B(line,(size+1));
 IF(displaytype){
  /* size multiplied four times because every byte in the original */
  /* buffer is going to be equal to four in the new buffer. */
  IF(!(chrdump=(C *)M((SL(in)*4)+1)))
   pe("pd(): allocation of memory error",1);
  B(chrdump,((SL(in)*4)+1));
  F(i=0;i<SL(in);i++){
   /* 0 = character, 1 = hex, 2 = decimal. */
   IF(displaytype==1)
    SP(&chrdump[k],"\\x%.2x",in[i]);
   E
    SP(&chrdump[k],"%.3d%c",in[i],((i+1)!=SL(in))?0x20:0x0);
   k+=4;
  }
 }
 /* make the chrdump buffer a standard character buffer if no dispalytype to */
 /* wordwrap up later. */
 IF(!displaytype){
  IF(!(chrdump=(C *)SDU(in)))
   pe("pd(): duplication of memory error",1);
 }
 B(prompt,4);
 IF(!io&&!nossend)
  SP(prompt,"-> ");
 EI(io&&!nosrecv)
  SP(prompt,"<- ");
 IF(!nowrap){
  B(line,(size+1));
  F(i=0;i<SL(chrdump);i++){
   IF(!SL(line))
    SP(line,"%s",!nofrom?prompt:"");
   /* next two checks are impossible to effect hex, or dec dumps. */
   /* makes sure tabs do not screw up the wordwrap. */
   IF(chrdump[i]==0x09)
    /* 7 spaces = 1 tab(sort of, at a tab max), if it is beyond the buffer */
    /* size the tab will get cut off.  does not handle like a tab does. */
    F(j=0;(j<7&&!(SL(line)>=size));j++)
     line[SL(line)]=0x20;
   /* do not want a screwed up wordwrap, just slap a linefeed there instead. */
   EI(chrdump[i]==0x0A)
    line[SL(line)]=0x0D;
   E
    line[SL(line)]=chrdump[i];
   IF(SL(line)>=size){
#ifdef NCURSES
   nfocus=(io?nw1:nw2);
#endif
    nsprint("%s\n",line);
    B(line,(size+1));
   }
  }
  IF(SL(line)){
#ifdef NCURSES
   nfocus=(io?nw1:nw2);
#endif
   nsprint("%s\n",line);
  }
 }
 /* for disabled wordwrap, which also includes th -L option. (-w option) */
 EI(SL(chrdump)){
  /* precurse with number of lines. */
  IF(lnum){
#ifdef NCURSES
   nfocus=(io?nw1:nw2);
#endif
   nsprint("(%.4d) ",(!io?(lnum_o++):(lnum_i++)));
   /* restart it to zero. */
   IF(lnum_o>=10000)
    lnum_o=1;
   IF(lnum_i>=10000)
    lnum_i=1;
  }
#ifdef NCURSES
  nfocus=(io?nw1:nw2);
#endif
  nsprint("%s%s\n",!nofrom?prompt:"",chrdump);
 }
 FR(chrdump);
 FR(line);
 R;
}
/* writes data to the socket, and handles the ns_outgoing() module.  also, */
/* handles the telnet dump for outgoing. (-y, and -Y options) */
V wro(C *output,U I size,U SH io){
 U SH s=0;
 fd_set cfd;
 fd_set crfd;
 S timeval tv;
 /* check, and use the ns_outgoing() symbol, it passes the output to be */
 /* wrote to the socket, the amount that will be wrote, the actual size of */
 /* the data, and the socket value. (char *, int, int, int) */
#ifndef DISABLE_MODULES
 IF(vmodule&&SL(output)&&size&&!io)
  (*outgoing_function)(output,size,SL(output),sock);
#endif
 IF(sock!=-1&&!io){
  FD_ZERO(&cfd);
  FD_SET(sock,&cfd);
  /* handle outgoing telnet dumping. (-y, and -Y options) */
  dumptelnet(output);
  /* no time needed. */
  tv.tv_sec=0;
  tv.tv_usec=0;
  /* make sure the socket is really there. */
  IF(SE((sock+1),0,&cfd,0,&tv)>0) 
   WR(sock,output,size);
  IF(log&&tnetraw)
   logdata(output,3);
 }
 IF(setroute){
  /* to still check for activity with select(). */
  IF(((norrecv&&io)||(norsend&&!io))&&io!=2)
   s=1;
  IF(rsock!=-1){
   FD_ZERO(&crfd);
   FD_SET(rsock,&crfd);
   tv.tv_sec=0;
   tv.tv_usec=0;
   IF(SE((rsock+1),0,&crfd,0,&tv)>0){
    IF(!s)
     WR(rsock,output,size);
   }
   /* silent. */
   E{
    SD(rsock,2);
    CL(rsock);
    rsock=-1;
    setroute=0;
   }
  }
 }
 R;
}
/* this function will write the supplied arguments to the system log.  it */
/* has a similar usage to nsprint(). (-Z option) */
#ifndef DISABLE_SYSLOG
V wrso(C *format,...){
 SI I i=0;
 /* max size. */
 U I j=(BASE_BUFFER*8);
 C *wlog;
 VAL ap;
 /* only do if enabled. (-Z option) */
 IF(!slog)
  R;
 /* return if backgrounded or malloc() failed. */
 IF(!(wlog=(C *)M(j)))
  R;
 W(1){
  VAS(ap,format);
  i=VS(wlog,j,format,ap);
  VAE(ap);
  IF(i>-1&&i<j){
   /* could fail to write.  if it does, it probably was not a superuser, */
   /* which is all that is desired to be logged anyways. */
   SLG(LOG_WARNING,"%s",wlog);
   FR(wlog);
   R;
  }
  IF(i>-1)
   j=(i+1);
  E
   j*=2;
  IF((wlog=realloc(wlog,j))==0){
   /* size change failed.  free anyways, and return. */
   FR(wlog);
   R;
  }
 }
 R;
}
#endif
/* function to set the permissions of netscript to run under.  set after */
/* connected.  but, set before taking any input, or sending any output. */
V setpermissions(C *string,U SH type){
 S passwd *pent;
 S group *gent;
 /* read the string. */
 IF(!type){
  /* set them up default.  in case no data is provided, it will just reset. */
  ngid=GEG();
  nuid=GEU();
  /* if a "." exists.  take the first token as uid, and then second as gid. */
  IF(!parameter(string,1,0x2E)){
   IF(!(gent=GR(parm)))
    ngid=AI(parm);
   E
    ngid=gent->gr_gid;
   parameter(string,0,0x2E);
   IF(!(pent=GP(parm)))
    nuid=AI(parm);
   E
    nuid=pent->pw_uid;
  }
  E{
   IF(!(pent=GP(string)))
    nuid=AI(string);
    /* gid should be defaulted, already. */
   E{
    nuid=pent->pw_uid;
    ngid=pent->pw_gid;
   }
   /* apply the gid of the user, since no other gid was specified. */
  }
 }
 /* do what setpermissions() said to when called earlier. */
 E{
  /* note the new id information.  to potentially, clarify the discontinue */
  /* of logging.*/
#ifndef DISABLE_SYSLOG
  wrso("%d-%d id: %lu-%u.%u.%u.%u new: %lu-%u.%u.%u.%u",slnum_t,(slnum_s++),
  cpid,GU(),GEU(),GG(),GEG(),cpid,nuid,nuid,ngid,ngid);
#endif
  /* set the groups up, to follow. */
  IF((pent=GPU(nuid)))
   initgroups(pent->pw_name,ngid);
  /* setre*id() is not on some systems.  virtually the same thing.  keeps */
  /* the groups.  also, no need for setfs*id(), sete*id() will do it. */
  /* setfs*id() is not supported everywheres either. */
  SGD(ngid);
  SEGD(ngid);
  SUD(nuid);
  SEUD(nuid);
 }
 R;
}
/* ruleset editor, uses a third party text editor on a file. */
V ruleedit(U SH type,C *path){
 C *editor;
 C *tmpfile;
 PT fp;
 T tm;
 S stat mod;
 IF(isprivileged)
  pe("a third party editor can not be executed with privileged access",0);
 E{
  IF(G(ENV_EDITOR)){
   IF(!(editor=(C *)SDU((C *)G(ENV_EDITOR))))
    pe("ruleedit(): duplication of memory error",1);
  }
  E{
   IF(!(editor=(C *)SDU(DFL_EDITOR)))
    pe("ruleedit(): duplication of memory error",1);
  }
  IF(!type){
   IF(!(tmpfile=(C *)M(5+10+5+10+1)))
    pe("ruleedit(): allocation of memory error",1);
   B(tmpfile,(5+10+5+10+1));
   TM(&tm);
   SP(tmpfile,"/tmp/netscript.%.5lu%.10lu",(U L)cpid,(U L)tm);
  }
  E{
   IF(!(tmpfile=(C *)SDU(path)))
    pe("ruleedit(): duplication of memory error",1);
  }
  SW(fp=FK()){
   CS -1:
    pe("failed forking for the third party text editor",0);
    BR;
   CS 0:
    SG(SIGINT,SIG_DFL);
    EL(editor,editor,tmpfile,0);
    pe("failed execution of (ruleset) file editor",0);
    _exit(1);
    BR;
   default:
    /* wait till finished. */
    WP(fp,0,0);
    BR;
  }
  IF(STT(tmpfile,&mod))
   pe("could not access the temporary/ruleset file passed to the editor",1);
  EI(!setfile){
   /* this should never happen.  but, just incase. */
   IF(mod.st_uid!=GEU()||mod.st_gid!=GEG())
    pe("the temporary file has a different owner/group than the current proces"
    "s",1);
   EI(!setfile){
    IF(!(rulesfile=(C *)SDU(tmpfile)))
     pe("ruleedit(): duplication of memory error",1);
    setfile=1;
   }
  }
  FR(editor);
  FR(tmpfile);
 }
 R;
}
/* this function does the logging. (-l option) */
/* 0 = output, 1 = input, 2 = other, else = raw. */
V logdata(C *data,U SH io){
 /* will be used to hold the ctime data. */
 C *timebuf;
 C prompt[3];
 T tm;
 FL *fd;
 /* opens, and allows appending to the log file. */
 IF(!(fd=FO(logfile,"a")))
  pe("log file can not be created, or is not writable",1);
 E{
  TM(&tm);
  IF(!(timebuf=(C *)SDU(CT(&tm))))
   pe("logdata(): duplication of memory error",1);
  IF(timebuf[(SL(timebuf)-1)]==0x0A)
   timebuf[(SL(timebuf)-1)]=0x0;
  B(prompt,3);
  IF(!io)
   SP(prompt,"->");
  EI(io==1)
   SP(prompt,"<-");
  EI(io==2)
   SP(prompt,"!!");
  IF(SL(data)){
   /* raw data write. (-y, and -Y options) */
   IF(!SL(prompt))
    FP(fd,"%s",data);
   E
    /* regular data write. */
    FP(fd,"%s: %s %s\n",timebuf,prompt,data);
  }
  FR(timebuf);
  FC(fd);
 }
 R;
}
/* the fixed dumping of information with the telnet option.  has */
/* comparisons. (-y, and -Y in conjunction with -T option) */
V dumptelnet(C *dump){
 U I i=0;
 U I j=0;
 U C *tmpdump;
 IF(!(tmpdump=(C *)M(SL(dump)+1)))
  pe("dumptelnet(): allocation of memory error",1);
 IF(tnetraw){
  /* 1 = stderr, 2 = stdout(regular). */
  IF(tnetraw==1){
   /* do not show if backgrounded. */
   IF(!isbga)
    FP(stderr,"%s",dump);
  }
  E{
   /* mainly did it this way for debug purposes. */
   B(tmpdump,(SL(dump)+1));
   F(i=j=0;j<SL(dump);j++){
    IF(dump[j]==0x0A){
     tmpdump[i]=0x0;
     nsprint("%s\n",tmpdump);
     B(tmpdump,(SL(dump)+1));
     i=0;
    }
    E
     tmpdump[i++]=dump[j];
   }
   IF(SL(tmpdump)){
    tmpdump[i]=0x0;
    nsprint("%s",tmpdump);
   }
   /* no need to zero out the buffer since it is going to be free'd next. */
  }
 }
 FR(tmpdump);
 R;
}
/* this is for use with dumping a programs output to the socket.  also, */
/* used for the $` execution comparison variable. */
V dumpexec(C *path,U SH type){
 U SH r=0;
 U SH s=0;
 U I i=0;
 U I j=0;
 U I k=0;
 /* to be duplicated. */
 I fd[2];
 PT fp;
 /* will wrap if needed to this size. */
 U C eread[(BASE_BUFFER*8+1)];
 /* temporary buffer must be as big as its counterpart. */
 U C ereadl[(BASE_BUFFER*8+1)];
 /* timeout of waiting for input. */
 S timeval tv;
 /* for select(). */
 fd_set ffd;
 /* set to null, in case anything fails before anything actually happens. */
 IF(type){
  FR(execformat);
  IF(!(execformat=(C *)SDU("(null)")))
   pe("dumpexec(): duplication of memory error",1);
 }
 /* just incase of setuid/setgid. */
#ifdef GTK
 IF(isgui){
  pe("can not execute third party programs while in gui/gtk mode",0);
  R;
 }
#endif
#ifndef DISABLE_SYSLOG
 wrso("%d-%d id: %lu-%u.%u.%u.%u exec: %s",slnum_t,(slnum_s++),cpid,GU(),GEU(),
 GG(),GEG(),path);
#endif
 IF(!noshowp&&!type)
  pe("entering execution dump, incoming data suspended",0);
 IF(isprivileged)
  pe("third party programs can not be executed with privileged access",0);
 E{
  /* pair the two duplicate fd's. */
  IF(socketpair(PF_UNIX,SOCK_STREAM,0,fd)==-1){
   pe("execution dump failed, incoming data resumed",0);
   R;
  }
  SW(fp=FK()){
   CS -1:
    pe("execution dump failed, incoming data resumed",0);
    CL(fd[0]);
    CL(fd[1]);
    BR;
   CS 0:
    /* close, and duplicate the fd's.  if netscript is in the background */
    /* this will not want to be evaluated. */
    IF(!isbga){
     CL(0);
     CL(1);
     CL(2);
    }
    /* this will allow interaction inside the program, stdin->socket. */
    /* if desired. */
    IF(isiexec)
     D(sock,0);
    /* output. */
    D(fd[1],1);
    D(fd[1],2);
    /* better than a raw execution. */
    EL(eshell,eshell,"-c",path,0);
    /* make sure the handler is still defined, should be anyways. */
    SG(SIGINT,sighandler);
    /* should never make it here.  if it does, since its duplicated, it will */
    /* send any output to the socket.  so, looks better just to be silent. */
    _exit(1);
    BR;
   default:
    /* ignore temporarily to send the signal only to the forked pid. */
    SG(SIGINT,SIG_IGN);
    FD_ZERO(&ffd);
    FD_SET(fd[0],&ffd);
    W(!k){
     /* defined amount of delay time, 3 seconds by default. (-x option) */
     tv.tv_sec=(xalrm?xalrm:3);
     /* no need to be so precise. */
     tv.tv_usec=0;
     IF((s=SE((fd[0]+1),&ffd,0,0,&tv))>0){
      B(eread,(BASE_BUFFER*8+1));
      IF(RD(fd[0],eread,(BASE_BUFFER*8))){
       F(i=0;(s>0&&i<SL(eread));i++){
        IF(eread[i]==0x0A||eread[i]==0x0D||j>=(BASE_BUFFER*8)){
         /* count back for the missed byte. */
         IF(j>=(BASE_BUFFER*8))
          i--;
         ereadl[j]=0x0;
         IF(type&&!r){
          /* free the pre-placed "null". */
          FR(execformat);
          IF(!(execformat=(C *)SDU(ereadl)))
           pe("dumpexec(): duplication of memory error",1);
          /* let other positions know the buffer is set. */
          r=1;
          /* can only use one line.  so, (fool) exit after one exists. */
          s=0;
         }
         E
          dumpexecline(ereadl);
         B(ereadl,(BASE_BUFFER*8+1));
         /* reset counter. */
         j=0;
        }
        E
         ereadl[j++]=eread[i];
       }
       B(eread,(BASE_BUFFER*8+1));
      }
     }
     /* if statement, and not else.  because, of the fool exit possibility. */
     IF(s<=0){
      /* cap it off, for the next function. */
      ereadl[j]=0x0;
      IF(type&&!r&&SL(ereadl)){
       /* free the pre-placed "null". */
       FR(execformat);
       IF(!(execformat=(C *)SDU(ereadl)))
        pe("dumpexec(): duplication of memory error",1);
       /* no need to set short r, to notify, it is never used again. */
      }
      /* will dump any leftovers, as if there was a CR. */
      E
       dumpexecline(ereadl);
      B(ereadl,(BASE_BUFFER*8+1));
      /* force it, incase it timed out. */
      K(fp,SIGKILL);
      /* wait up, do not want defunctness. */
      WP(fp,0,0);
      /* bye bye. */
      CL(fd[0]);
      CL(fd[1]);
      /* unignore ctrl-c aborts. */
      SG(SIGINT,sighandler);
      k=1;
     }
    }
    BR;
  }
 }
 /* it would never make it to this point two times with variable usage. */
 /* so, no need to support such an event.  required information. */
 IF(runexit&&!type)
  pe("completed execution dump, exiting netscript",1);
 EI(!noshowp&&!type)
  pe("completed execution dump, incoming data resumed",0);
 R;
}
/* to actually handle the line passed from dumpexec(). */
V dumpexecline(C *line){
 U C *dump;
 IF(SL(line)){
  IF(!(dump=(C *)M(SL(line)+(runpre?SL(execpre):0)+1)))
   pe("dumpexecline(): allocation of memory error",1);
  B(dump,(SL(line)+(runpre?SL(execpre):0)+1));
  IF(runpre)
   SCA(dump,execpre);
  SCA(dump,line);
  /* last use of eread for this run. */
  IF(!nossend)
   pd(dump,0,columns);
  wro(dump,SL(dump),0);
  wro("\n",1,0);
  /* add the output to the log, if enabled. (-l option) */
  IF(log&&!tnetraw)
   logdata(dump,0);
  /* last use of dump for this run. */
  FR(dump);
 }
 R;
}
/* small function to close sockets.  related to connecting, and binding. */
V closesocket(U SH type){
 /* server connection/connected. (and routed along with it) */
 IF(!type){
  IF(sock>=0){
   SD(sock,2);
   CL(sock);
   sock=-1;
  }
  IF(setroute&&rsock>=0){
   SD(rsock,2);
   CL(rsock);
   rsock=-1;
  }
 }
 /* server bind socket. */
 E{
  IF(ssock>=0){
   SD(ssock,2);
   CL(ssock);
   ssock=-1;
  }
 }
 R;
}
/* this function handles the user supplied socket option(s). */
V parsesocketopt(I type,I sockv){
 U I i=0;
 I j=0;
 I soargs[2];
 C *curopt;
 /* even if no options are set, set this automatically. (bind socket only) */
 IF(type==1){
  j=1;
  SSO(sockv,SOL_SOCKET,SO_REUSEADDR,(V *)&j,SO(j));
  /* was not defined on one of the supported platforms, check to be safe. */
#ifdef SO_REUSEPORT
  SSO(sockv,SOL_SOCKET,SO_REUSEPORT,(V *)&j,SO(j));
#endif
 }
 IF(soptions){
  parsedynamic(sopts);
  /* find out the size of the buffer needed. */
  W(!parameter(parseddynamic,i++,0x3A)){
   IF(!(curopt=(C *)SDU(parm)))
    pe("parsesocketopt(): duplication of memory error",1);
   parameter(curopt,0,0x2C);
   /* int->int comparison. */
   IF(AI(curopt)==type){
    B((V *)soargs,SO(soargs));
    IF(!parameter(curopt,1,0x2C)){
     soargs[0]=AI(parm);
     IF(!parameter(curopt,2,0x2C)){
      soargs[1]=AI(parm);
      IF(SSO(sockv,SOL_SOCKET,soargs[0],(V *)&soargs[1],SO(soargs[1])))
       pe("internal setting of socket option failed",0);
     }
    }
   }
   FR(curopt);
  }
 }
 R;
}
/* the socket connection, and line parser.  takes the data from the remote */
/* host, and puts it in a per line format, then passes it along to the match */
/* function(s).  also includes some specific variable handling. */
V parsesocket(C *host,U I port){
 /* this is only used in this function. */
 U SH stoprules=0;
 /* many loop ints used to help keep track of what is what.  still re-used */
 /* some i knew would never cross paths. */
 U I i=0;
 U I j=0;
 U I k=0;
 U I l=0;
 U I m=0;
 U I n=0;
 U I o=0;
 U I p=0;
 U I q=0;
 U I r=0;
 U I s=0;
 /* for the $, truncation variable. */
 SI I left=0;
 SI I right=0;
 /* socket (size) related. */
 SI I salen=0;
 /* file dump read buffer. (variable $<) */
 U C dread[(BASE_BUFFER*4+1)];
 /* special read buffer for initial data. */
 U C iread[(BASE_BUFFER*4+1)];
 /* initial write buffer. */
 U C iwrite[(BASE_BUFFER*4+1)];
 /* read buffer for socket data.  needs comparisons. */
 U C sread[(BASE_BUFFER*4+1)];
 /* buffer for data built up till a completed line, otherwise gets cleared. */
 /* allowance of large lines. */
 U C sreadl[(BASE_BUFFER*16+1)];
 /* three byte response buffer, for telnet protocol responses. */
 U C telnet[3];
 /* single byte, multiple uses.  no comparisons. */
 C sb[1];
 S hostent *he;
 /* connect to socket / bind client socket. */
 S SAN ns;
 /* bind server socket. */
 S SAN sns;
 /* route client socket. */
 S SAN rns;
 /* for the $"(time passed) rule. */
 T ctm;
 T rtm;
 FL *dfd;
 FL *ifd;
 /* reset the line values to one. (-L option) */
 IF(lnum)
  lnum_i=lnum_o=1;
 IF(!noshowc)
  /* could have used tot_i, or tot_o for the second argument, i used tot_i. */
  nsprint("[%lu@%s] * Session started with %d rule(s) defined.\n",cpid,nname,
  tot_i);
 /* handling for socket binding. */
 IF(bindmode){
  /* only happens on the first binding. (for forever mode) */
  /* unless udp mode is active, then will rebind. */
  IF(bindmode!=2||isudp){
   /* main server sockaddr structure. */
   (isudp?sock:ssock)=SK(AF_INET,(isudp?SOCK_DGRAM:SOCK_STREAM),(isudp?
   IPPROTO_UDP:IPPROTO_TCP));
   parsesocketopt(1,(isudp?sock:ssock));
   B((V *)&sns,SO(sns));
   sns.sin_family=AF_INET;
   sns.sin_port=HS(port);
   IF(isvhost){
    /* resolve/set the host. */
    IF((sns.sin_addr.s_addr=IA(vhost))){
     IF(!(he=GH(vhost))){
      IF(!noshowc)
       nsprint("[%lu@%s] * Could not resolve the provided virtual host, using "
       "any...\n",cpid,nname);
      sns.sin_addr.s_addr=HL(INADDR_ANY);
     }
     E
      MC((C *)&sns.sin_addr,(C *)he->h_addr,SO(sns.sin_addr));
    }
    E
     sns.sin_addr.s_addr=HL(INADDR_ANY);
   }
   /* bind to the ip/port. (-b option) */
   IF(BI((isudp?sock:ssock),(S SA *)&sns,SO(sns))<0){
    IF(!noshowc)
     nsprint("[%lu@%s] * Bind failed: %s.\n",cpid,nname,strerror(errno));
    /* yet again.  better safe than sorry, do not want fds to fill up. */
    closesocket(0);
    closesocket(1);
    R;
   }
   /* so it does not re-bind, again. */
   bindmode=2;
  }
  IF(!noshowc)
   nsprint("[%lu@%s] * Awaiting incoming connection...\n",cpid,nname);
  /* one time connection(per listen), should not need more than one. */
  IF(isudp){
   i=SO(S SA);
   IF(RF(sock,sread,(BASE_BUFFER*4),MSG_PEEK,(S SA *)&sns,&i)<0){
     nsprint("[%lu@%s] * Bind failed: recvfrom()\n",cpid,nname);
    /* close up shop. */
    closesocket(0);
    closesocket(1);
    R;
   }
   IF(CO(sock,(S SA *)&sns,SO(sns))){
    IF(!noshowc)
     nsprint("[%lu@%s] * Connection failed: %s.\n",cpid,nname,strerror(errno));
    R;
   }
  }
  E{
   IF(LI(ssock,blog)<0){
    pe("could not listen on supplied port",0);
    R;
   }
   salen=SO(ns);
   IF((sock=AT(ssock,(S SA *)&ns,&salen))<0){
    pe("could not accept remote connection",0);
    R;
   }
   parsesocketopt(0,sock);
  }
  /* close it, if its just one time. (bind) */
  IF(!forever)
   closesocket(1);
  IF(isudp){
   i=SO(ns);
   /* if fails, doesnt really matter. (hopefully) */
   GPN(sock,(S SA *)&ns,&i);
  }
  IF(!noshowc){
   nsprint("[%lu@%s] * Accepted connection from: %s:%d.\n",cpid,nname,
   /* since these ips do not really exist, other than in an error */
   /* situation.  blank out the confusion of an error. */
#ifndef DISABLE_SRCIP
   ((!SC(IN(ns.sin_addr),"255.255.255.255")||!SC(IN(ns.sin_addr),"0.0.0.0"))
   ?NOSRCIP:IN(ns.sin_addr))
#else
   NOSRCIP
#endif
   ,port);
  }
 }
 /* handling for socket connection. */
 E{
  sock=SK(AF_INET,(isudp?SOCK_DGRAM:SOCK_STREAM),(isudp?IPPROTO_UDP:
  IPPROTO_TCP));
  parsesocketopt(0,sock);
  B((V *)&ns,SO(ns));
  ns.sin_family=AF_INET;
  IF(isvhost){
   /* resolve/set the host. */
   IF((ns.sin_addr.s_addr=IA(vhost))){
    IF(!(he=GH(vhost))){
     IF(!noshowc)
      nsprint("[%lu@%s] * Could not resolve the provided virtual host, using a"
      "ny...\n",cpid,nname);
     ns.sin_addr.s_addr=HL(INADDR_ANY);
    }
    E
     MC((C *)&ns.sin_addr,(C *)he->h_addr,SO(ns.sin_addr));
   }
   IF(BI(sock,(S SA *)&ns,SO(ns))<0){
    /* clean up, and use any. */
    pe("error binding name to socket, using any",0);
    B((V *)&ns,SO(ns));
    ns.sin_family=AF_INET;
    ns.sin_addr.s_addr=HL(INADDR_ANY);
    IF(BI(sock,(S SA *)&ns,SO(ns))<0)
     pe("could not bind name properly",0);
   }
  }
  B((V *)&ns,SO(ns));
  ns.sin_family=AF_INET;
  ns.sin_port=HS(port);
  IF(!noshowc)
   nsprint("[%lu@%s] * Checking the provided host...\n",cpid,nname);
  IF((ns.sin_addr.s_addr=IA(host))){
   /* if not valid, attempts to resolve as a host. */
   IF(!(he=GH(host))){
    /* nothing left to try. */
    IF(!noshowc)
     nsprint("[%lu@%s] * Could not resolve the provided host.\n",cpid,nname);
    R;
   }
   E
    /* successfully resolved. */
    MC((C *)&ns.sin_addr,(C *)he->h_addr,SO(ns.sin_addr));
  }
  IF(!noshowc)
   nsprint("[%lu@%s] * Attempting to connect to the provided host...\n",cpid,
   nname);
  /* if you can not connect to the server the first time might as well kill */
  /* the program, same problem would continue infinitely.  so, if it times */
  /* out after ??(2 minutes by default) seconds it will just exit. */
  IF(G(ENV_TIMEOUT)&&AI((C *)G(ENV_TIMEOUT))>0)
   A(AI((C *)G(ENV_TIMEOUT)));
  E
   A(DFL_TIMEOUT);
  /* like it looks, attempts to connect to the remote server. */
  IF(CO(sock,(S SA *)&ns,SO(ns))){
   A(0);
   IF(!noshowc)
    nsprint("[%lu@%s] * Connection failed: %s.\n",cpid,nname,strerror(errno));
   R;
  }
  E{
   A(0);
   IF(!noshowc)
    nsprint("[%lu@%s] * %s: %s:%d.\n",cpid,nname,(isudp?"Created connection":
    "Connected successfully"),
    /* since these ips do not really exist, other than in an error */
    /* situation.  blank out the confusion of an error. */
#ifndef DISABLE_SRCIP
    ((!SC(IN(ns.sin_addr),"255.255.255.255")||!SC(IN(ns.sin_addr),"0.0.0.0"))?
    NOSRCIP:IN(ns.sin_addr))
#else
    NOSRCIP
#endif
    ,port);
  }
 }
 /* only do the following if outside GTK+. (changing id levels would make */
 /* for display problems) */
#ifdef GTK
 IF(!isgui){
#endif
  /* set the permissions before taking any input, or sending any output. */
  /* only done once.  since binding would have occured already. */
  IF(setperms){
   setpermissions(0,1);
   setperms=0;
  }
#ifdef GTK
 }
#endif
#ifndef DISABLE_SYSLOG
 wrso("%d-%d id: %lu-%u.%u.%u.%u remote: %s-%u type: %s",slnum_t,(slnum_s++),
 cpid,GU(),GEU(),GG(),GEG(),
#ifndef DISABLE_SRCIP
 ((!SC(IN(ns.sin_addr),"255.255.255.255")||!SC(IN(ns.sin_addr),"0.0.0.0"))?
 NOSRCIP:IN(ns.sin_addr))
#else
 NOSRCIP
#endif
 ,port,(bindmode?"incoming":"outgoing"));
#endif
 IF(setroute){
  rsock=SK(AF_INET,(isudpr?SOCK_DGRAM:SOCK_STREAM),(isudpr?IPPROTO_UDP:
  IPPROTO_TCP));
  parsesocketopt(2,rsock);
  B((V *)&rns,SO(rns));
  rns.sin_family=AF_INET;
  IF(isvhost){
   IF((rns.sin_addr.s_addr=IA(vhost))){
    IF(!(he=GH(vhost))){
     IF(!noshowc)
      nsprint("[%lu@%s] * Could not resolve the provided virtual host, using a"
      "ny...\n",cpid,nname);
     rns.sin_addr.s_addr=HL(INADDR_ANY);
    }
    E
     MC((C *)&rns.sin_addr,(C *)he->h_addr,SO(rns.sin_addr));
   }
   IF(BI(rsock,(S SA *)&rns,SO(rns))<0){
    /* clean up, and use any. */
    pe("error binding name to socket, using any",0);
    B((V *)&rns,SO(rns));
    rns.sin_family=AF_INET;
    rns.sin_addr.s_addr=HL(INADDR_ANY);
    IF(BI(rsock,(S SA *)&rns,SO(rns))<0){
     pe("could not bind name properly, proceeding anyways",0);
     rsock=-1;
    }
   }
  }
  IF(rsock!=-1){
   B((V *)&rns,SO(rns));
   rns.sin_family=AF_INET;
   rns.sin_port=HS(rport);
   IF(!noshowc)
    nsprint("[%lu@%s] * Checking the provided (route) host...\n",cpid,nname);
   IF((rns.sin_addr.s_addr=IA(rhost))){
    /* if not valid, attempts to resolve as a host. */
    IF(!(he=GH(rhost))){
     /* nothing left to do. */
     IF(!noshowc)
      nsprint("[%lu@%s] * Could not resolve the provided host, proceeding anyw"
      "ays...\n",cpid,nname);
     rsock=-1;
    }
    E
     /* successfully resolved. */
     MC((C *)&rns.sin_addr,(C *)he->h_addr,SO(rns.sin_addr));
   }
   IF(!noshowc&&rsock!=-1)
    nsprint("[%lu@%s] * Attempting to connect to the provided (route) host..."
    "\n",cpid,nname);
   IF(G(ENV_TIMEOUT)&&AI((C *)G(ENV_TIMEOUT))>0)
    A(AI((C *)G(ENV_TIMEOUT)));
   E
    A(DFL_TIMEOUT);
   IF(rsock==-1||CO(rsock,(S SA *)&rns,SO(rns))){
    A(0);
    IF(rsock!=-1&&!noshowc)
     nsprint("[%lu@%s] * Route connection failed: %s. (proceeding)\n",cpid,
     nname,strerror(errno));
    /* ensure the error is known. */
    rsock=-1;
   }
   E{
    A(0);
    IF(!noshowc)
     nsprint("[%lu@%s] * Connected (route) successfully: %s:%d.\n",cpid,nname,
#ifndef DISABLE_SRCIP
     ((!SC(IN(rns.sin_addr),"255.255.255.255")||!SC(IN(rns.sin_addr),"0.0.0.0")
     )?NOSRCIP:IN(rns.sin_addr))
#else
     NOSRCIP
#endif
     ,rport);
   }
  }
 }
 /* states log start time, and ip that was used.  if enabled. (-l option) */
 IF(log&&!tnetraw){
  logdata("NEW NETSCRIPT SESSION STARTED.",2);
  /* logging of the ip used. */
  logdata(
  /* since these ips do not really exist, other than in an error situation. */
  /* blank out the confusion of an error. */
#ifndef DISABLE_SRCIP
  ((!SC(IN(ns.sin_addr),"255.255.255.255")||!SC(IN(ns.sin_addr),"0.0.0.0"))?
  NOSRCIP:IN(ns.sin_addr))
#else
  NOSRCIP
#endif
  ,2);
 }
 /* check, and use the ns_connect() symbol, it passes the connection type, */
 /* ip value, and port. (short, unsigned long int, int) */
#ifndef DISABLE_MODULES
 IF(vmodule)
  (*connect_function)(bindmode,ns.sin_addr,port);
#endif
 /* for the $" rule, will set the time of the connection to be compared to. */
 TM(&rtm);
 /* cleans out the dynamic (int)buffer for the "one time" variable. */
 B((V *)rs_dynamic,SO(rs_dynamic));
 /* this is for the "$." variable, it makes it not truely static.  resets */
 /* every connection if the value is two. (or back to one) */
 F(i=0;i<tot_i;i++){
  IF(rs_static[i]>1)
   rs_static[i]-=2;
 }
 /* cleans out the dynamic (int)buffer for variable data. (usable) */
 /* little more involved than a bzero() for the static vars set by the */
 /* command line. */
 F(i=0;i<10;i++){
  IF(dynamicvarset[i]==1){
   FR(dynamicvar[i]);
   dynamicvarset[i]=0;
  }
 }
 IF(initsend){
  /* handle the unique initial input data.  if it is a "?" it will prompt */
  /* for input.  make sure not to allow if backgrounded. */
  IF(!SC(iswrite,"?")&&!isbga){
   /* gui version of the request. */
#ifdef GTK
   IF(isgui){
    IF(isguiwait)
     R;
    E{
     nsprint("input request: enter dynamic initial data to send.\n");
     /* only if non-generic. */
     IF(!isbgui){
      GTE();
      /* make sure focus is on the entry box. */
      GWGF(gen);
      /* show the entry box. */
      GWS(gen);
      GTL();
     }
     isguiwait=1;
     W(isguiwait)
      USLP(250000);
    }
   }
   /* typical version of the request. */
   E{
#endif
    IF(!(ifd=FO(ttyn,"r")))
     pe("could not open standard input for read",0);
    E{
     /* none of this will work anyways if backgrounded.  will close the fd. */
     IF(!isbga){
      IF(!noshowp)
       pe("initial send defined user input, incoming data suspended",0);
      /* show default, or reason prompt, if not in raw mode. */
      IF(!tnetraw)
       nsprint("enter initial data to send: ");
      B(iread,(BASE_BUFFER*4+1));
      /* truncated for pd(). */
      IF(FS(iread,(SO(iread)-1),ifd))
       F(o=0;o<SL(iread);o++)
        IF(iread[o]==0x0A)
         iread[o]=0x0;
      IF(!noshowp)
       pe("input received, incoming data resumed",0);
      IF(!nossend)
       pd(iread,0,columns);
      wro(iread,SL(iread),0);
      wro("\n",1,0);
      IF(log&&!tnetraw)
       logdata(iread,0);
     }
     FC(ifd);
    }
#ifdef GTK
   }
#endif
  }
  E{
   /* sends the initial data supplied by the argument if supplied. */
   IF(!nossend)
    pd(iswrite,0,columns);
   wro(iswrite,SL(iswrite),0);
   wro("\n",1,0);
   /* add the initial data to the log, if enabled. (-i, and -l option) */
   IF(log&&!tnetraw)
    logdata(iswrite,0);
  }
 }
 IF(runcmd)
  dumpexec(execfile,0);
 IF(alrm)
  A(alrm);
 W(1){
  B(sread,(BASE_BUFFER*4+1));
  IF(!RD(sock,sread,(SO(sread)-1))){
   pe("socket reading halted/finished",0);
   closesocket(0);
   R;
  }
  /* handle incoming telnet dumping. (-y, and -Y options) */
  dumptelnet(sread);
  IF(log&&tnetraw)
   logdata(sread,3);
  /* check, and use the ns_incoming_raw() symbol.  it passes the raw dump of */
  /* the socket read buffer, the size of the buffer, and the length of the */
  /* buffer. (char *, int, int) */
#ifndef DISABLE_MODULES
  IF(vmodule)
   (*incoming_raw_function)(sread,SO(sread),SL(sread));
#endif
  F(i=0;i<SL(sread);i++){
   /* telnet protocol handling, bytes set before handled. */
   /* using this method will have socket reading not wait for a \r, or a \n. */
   /* (the -T option) */
   IF(tnet&&sread[i]==0xFF&&sread[i+1]&&sread[i+2]){
    B(telnet,3);
    telnet[0]=0xFF;
    IF(sread[i+1]==0xFC||sread[i+1]==0xFB)
     telnet[1]=0xFE;
    EI(sread[i+1]==0xFE||sread[i+1]==0xFD)
     telnet[1]=0xFC;
    IF(telnet[1]==0xFE||telnet[1]==0xFC){
     /* checks to change socket handling for later. */
     IF(!truetnet){
      pe("received a telnet protocol option, entering telnet reading mode",0);
      truetnet=1;
     }
     /* the option. */
     telnet[2]=sread[i+2];
     wro(telnet,SO(telnet),0);
     IF(!nossend&&!notnetopt)
      nsprint("%s(TELNETD-OPT REPLIED: 0x%.2x 0x%.2x 0x%.2x -> 0x%.2x 0x%.2x "
      "0x%.2x)\n",!nofrom?"-> ":"",sread[i],sread[i+1],sread[i+2],telnet[0],
      telnet[1],telnet[2]);
    }
   }
   /* telnet mode will not wait for a \r, or \n, must get one or more telnet */
   /* option before automatically allowed. so, not to create problems with */
   /* bad syntax. */
   IF(sread[i]==0x0A||sread[i]==0x0D||(tnet&&truetnet&&!sread[i+1])){
    /* check where to place the null byte, and make sure byte exists for the */
    /* extended null byte.  it will not add the last byte if its the last */
    /* byte. */
    IF(tnet&&truetnet&&!sread[i+1]&&(BASE_BUFFER*16+1)>(j+1)){
     /* check, and use the ns_incoming_char() symbol, it passes the byte to */
     /* be added to the buffer. (int) */
#ifndef DISABLE_MODULES
     IF(vmodule)
      (*incoming_char_function)(sread[i]);
#endif
     /* append the byte that is usually a \r, or \n.  but, for telent */
     /* support a non-static character. */
     sreadl[j]=sread[i];
     sreadl[(j+1)]=0x0;
    }
    E
     sreadl[j]=0x0;
    /* handle the -S, and -U command line arguments for delaying. */
    IF(sdelay)
     SLP(sdelay);
    IF(sudelay)
     USLP(sudelay);
    /* pass to the routed host, if enabled. (-R option) */
    IF(setroute){
     wro(sreadl,SL(sreadl),1);
     IF(SL(sreadl))
      wro("\n",1,1);
    }
    /* check, and use the ns_incoming() symbol, it passes the incoming */
    /* string of data, the size of that data, and the socket value. */
    /* (char *, int, int) */
#ifndef DISABLE_MODULES
    IF(vmodule&&SL(sreadl))
     (*incoming_function)(sreadl,SL(sreadl),sock);
#endif
    IF(!nosrecv)
     pd(sreadl,1,columns);
    /* add the output to the log, if enabled. (-l option) */
    IF(log&&!tnetraw)
     logdata(sreadl,1);
    /* reset the temporary variable. */
    IF(stoprules==2)
     stoprules=0;
    F(k=0;k<tot_i&&SL(sreadl);k++){
     /* have not formed the swrite buffer yet, read the output for the */
     /* start variable, no need to modify anything yet anyways. */
     IF((!stoprules||!SNCC(output[k],NS_START,SL(NS_START)))&&!prewordmatch(
     input[k],sreadl)&&!(rs_static[k]&&rs_dynamic[k])){
      /* cancel possibility of a repeated rule. */
      IF(rs_static[k]){
       IF(!noshowp)
        pe("ruleset defined this rule to only be used once",0);
       rs_dynamic[k]=1;
      }
      IF(rs_delay[k]){
       IF(!noshowp)
        pe("ruleset defined a delay before processing of this rule",0);
       SLP(rs_delay[k]);
      }
      /* parse the arguments input->output, $1 $2 $3, makes swrite buf. */
      /* rules will redo this for possible skipped arguments in this run. */
      /* it will be done when the buffer gets chomped down using a rule. */
      parseoutput(sreadl,output[k]);
      /* check, and handle the direct stop variable. */
      IF(!SNCC(swrite,NS_STOP,SL(NS_STOP))){
       /* changes buffer to dump the variable.  for later display. */
       truncateoutput(SL(NS_STOP));
       /* update the new clean buffer. */
       parseoutput(sreadl,swrite);
       /* no possibility of format bug, pe() handles like text. */
       IF(!noshowp)
        pe(SL(swrite)?swrite:"ruleset defined ruleset stop",0);
       stoprules=1;
      }
      /* check, and handle the direct restart variable, even without stop. */
      EI(!SNCC(swrite,NS_START,SL(NS_START))){
       /* changes buffer to dump the variable.  for later display. */
       truncateoutput(SL(NS_START));
       /* update the new clean buffer. */
       parseoutput(sreadl,swrite);
       /* no possibility of format bug, pe() handles like text. */
       IF(!noshowp)
        pe(SL(swrite)?swrite:"ruleset defined ruleset start",0);
       stoprules=0;
      }
      /* check, and handle for the direct halt variable. */
      EI(!SNCC(swrite,NS_HALT,SL(NS_HALT))){
       /* changes buffer to dump the variable.  for later display. */
       truncateoutput(SL(NS_HALT));
       /* update the new clean buffer. */
       parseoutput(sreadl,swrite);
       /* no possibility of format bug, pe() handles like text. */
       IF(!noshowp)
        pe(SL(swrite)?swrite:"ruleset defined halt from continuing",0);
       stoprules=2;
      }
      /* check, and handle for the direct quit variable. */
      EI(!SNCC(swrite,NS_QUIT,SL(NS_QUIT))){
       /* changes buffer to dump the variable.  for later display. */
       truncateoutput(SL(NS_QUIT));
       /* update the new clean buffer. */
       parseoutput(sreadl,swrite);
       /* no possibility of format bug, pe() handles like text. */
       IF(!noshowp)
        pe(SL(swrite)?swrite:"ruleset defined socket kill",0);
       closesocket(0);
       R;
      }
      /* check, and handle the direct ask variable. */
      EI(!SNCC(swrite,NS_ASK,SL(NS_ASK))){
       /* can not exactly do this if it is backgrounded. */
       IF(isbga)
        R;
       /* changes buffer to dump the variable.  for later display. */
       truncateoutput(SL(NS_ASK));
       /* update the new clean buffer. */
       parseoutput(sreadl,swrite);
       /* gui version of the request. */
#ifdef GTK
       IF(isgui){
        IF(isguiwait)
         R;
        E{
         nsprint("input request: %s.\n",SL(swrite)?swrite:"enter response");
         /* only if non-generic. */
         IF(!isbgui){
          GTE();
          /* make sure focus is on the entry box. */
          GWGF(gen);
          /* show the entry box. */
          GWS(gen);
          GTL();
         }
         isguiwait=1;
         W(isguiwait)
          USLP(250000);
        }
       }
       /* typical version of the request. */
       E{
#endif
        IF(!(ifd=FO(ttyn,"r")))
         pe("could not open standard input for read",0);
        E{
         IF(!noshowp)
          pe("ruleset defined user input, incoming data suspended",0);
         /* show default, or reason prompt, if not in raw mode. */
         IF(!tnetraw)
          nsprint("%s: ",SL(swrite)?swrite:"enter response");
         B(iwrite,(BASE_BUFFER*4+1));
#ifdef NCURSES
         IF(isncurses)
          wgetnstr(nw3,iwrite,(SO(iwrite)-1));
         E
#endif
         IF(FS(iwrite,(SO(iwrite)-1),ifd))
          F(o=0;o<SL(iwrite);o++)
           IF(iwrite[o]==0x0A)
            iwrite[o]=0x0;
         IF(!noshowp)
          pe("input received, incoming data resumed",0);
         IF(!nossend)
          pd(iwrite,0,columns);
         wro(iwrite,SL(iwrite),0);
         wro("\n",1,0);
         IF(log&&!tnetraw)
          logdata(iwrite,0);
         FC(ifd);
        }
#ifdef GTK
       }
#endif
      }
      /* check, and handle the remove/replace variable. */
      EI(!SNCC(swrite,NS_TRUNC,SL(NS_TRUNC))){
       /* changes buffer to dump the variable.  for later use. */
       truncateoutput(SL(NS_TRUNC));
       /* update the new clean buffer. */
       parseoutput(sreadl,swrite);
       /* has to have at least one character to remove. */
       IF(SL(swrite)==1){
        F(l=0;l<SL(sreadl);l++)
         IF(sreadl[l]==swrite[0]){
          /* push the rest of the line back. */
          F(m=(l+1);m<SL(sreadl);m++){
           sreadl[(m-1)]=sreadl[m];
          }
          /* remove the leftover byte. */
          sreadl[(m-1)]=0x0;
          /* possibly a new character is here. */
          l--;
         }
       }
       /* two, or more replaces.  instead of removal. */
       EI(SL(swrite)>1)
        F(l=0;l<SL(sreadl);l++)
         IF(sreadl[l]==swrite[0])
          sreadl[l]=swrite[1];
      }
      EI(!SNCC(swrite,NS_TOKENL,SL(NS_TOKENL))){
       /* changes buffer to dump the variable.  for later use. */
       truncateoutput(SL(NS_TOKENL));
       /* update the new clean buffer. */
       parseoutput(sreadl,swrite);
       IF(SL(swrite)>0)
        /* using the int again here. */
        F(l=0;l<SL(sreadl);l++)
         IF(sreadl[l]==swrite[0])
          sreadl[l]=0x0;
      }
      EI(!SNCC(swrite,NS_TOKENR,SL(NS_TOKENR))){
       /* changes buffer to dump the variable.  for later use. */
       truncateoutput(SL(NS_TOKENR));
       /* update the new clean buffer. */
       parseoutput(sreadl,swrite);
       IF(SL(swrite)>0){
        /* using the int(s) again here. */
        F(m=l=0;l<SL(sreadl);l++){
         IF(m)
          sreadl[(l-m)]=sreadl[l];
         IF(sreadl[l]==swrite[0]&&!m)
          m=(l+1);
        }
        IF(m)
         sreadl[(l-m)]=0x0;
       }
      }
      /* check, and handle the display variable. */
      EI(!SNCC(swrite,NS_STR,SL(NS_STR))){
       /* changes buffer to dump the variable.  for later use. */
       truncateoutput(SL(NS_STR));
       /* update the new clean buffer. */
       parseoutput(sreadl,swrite);
       /* check for the comma with data, so atoi() will not fault. */
       IF(!parameter(swrite,0,0x2C))
        left=AI(parm);
       /* only happens if the variable is by itself. */
       E
        left=0;
       IF(!parameter(swrite,1,0x2C))
        right=AI(parm);
       /* also, only happens if the variable is by itself. */
       E
        right=-1;
       /* can not be less than zero.  or, should not be.  */
       IF(left<0)
        left=0;
       /* commonly thought that negative values equal to the end of the */
       /* line.  agree with the standard. */
       IF(right<0||right>SL(sreadl))
        right=SL(sreadl);
       F(r=q=0;q<SL(sreadl);q++){
        /* starts at point(left).  but, stops before point(right). */
        IF(q>=left&&q<right)
         sreadl[r++]=sreadl[q];
       }
       sreadl[r]=0x0;
      }
      /* check, and handle the format variable. */
      EI(!SNCC(swrite,NS_FMT,SL(NS_FMT))){
       /* changes buffer to dump the variable.  for later formatting. */
       truncateoutput(SL(NS_FMT));
       /* update the new clean buffer. */
       parseoutput(sreadl,swrite);
       /* clean it out. */
       B(sreadl,(BASE_BUFFER*16+1));
       /* byte-by-byte transfer/check. */
       F(p=0;((p<(SO(sreadl)-1))&&(p<(SL(swrite))));p++)
        sreadl[p]=swrite[p];
       sreadl[p]=0x0;
      }
      /* check, and handle the display variable. */
      EI(!SNCC(swrite,NS_ECHO,SL(NS_ECHO))){
       /* changes buffer to dump the variable.  for later display. */
       truncateoutput(SL(NS_ECHO));
       /* update the new clean buffer. */
       parseoutput(sreadl,swrite);
       /* no possibility of format bug, pe() handles like text. */
       /* ruleset defined this, so -C will not cut this off. */
       pe(SL(swrite)?swrite:"ruleset defined an empty display rule",0);
      }
      /* check, and handle the no return write (raw) variable. */
      /* shows up like a standard write out. */
      EI(!SNCC(swrite,NS_RAW,SL(NS_RAW))){
       /* changes buffer to dump the variable.  for later display. */
       truncateoutput(SL(NS_RAW));
       /* update the new clean buffer. */
       parseoutput(sreadl,swrite);
       IF(!nossend)
        pd(swrite,0,columns);
       wro(swrite,SL(swrite),0);
      }
      /* check, and handle the disable rule variable. */
      EI(!SNCC(swrite,NS_DISABLE,SL(NS_DISABLE))){
       /* changes buffer to dump the variable.  for later display. */
       truncateoutput(SL(NS_DISABLE));
       /* update the new clean buffer. */
       parseoutput(sreadl,swrite);
       IF(SL(swrite)){
        l=AI(swrite);
        IF(l<tot_i){
         IF(!noshowp)
          pe("ruleset defined a rule to be disabled",0);
         rs_dynamic[l]=1;
         /* if the value is two.  so, it will get reset every connection. */
         /* will be three if its already set to go off. */
         IF(rs_static[l]<=1)
          rs_static[l]+=2;
        }
        E
         IF(!noshowp)
          pe("ruleset defined a non-existent rule to be disabled",0);
       }
      }
      /* check, and handle the file dump variable. */
      EI(!SNCC(swrite,NS_DUMP,SL(NS_DUMP))){
       IF(isprivileged)
        pe("can not read/dump data from a file with privileged access",0);
       E{
        /* changes buffer to dump the variable.  for later display. */
        truncateoutput(SL(NS_DUMP));
        /* update the new clean buffer. */
        parseoutput(sreadl,swrite);
#ifndef DISABLE_SYSLOG
        wrso("%d-%d id: %lu-%u.%u.%u.%u read: %s",slnum_t,(slnum_s++),cpid,
        GU(),GEU(),GG(),GEG(),swrite);
#endif
        IF(AC(swrite,R_OK))
         pe("could not access the file requested to dump in the ruleset",0);
        E{
         IF(!(dfd=FO(swrite,"r")))
          pe("file requested by ruleset does not appear to exist, or is not re"
          "adable",0);
         E{
          B(dread,(BASE_BUFFER*4+1));
          /* no need to parse the data that comes from this file, instead it */
          /* directly dumps the file data.  single byte writing for all */
          /* character types. */
          IF(!noshowp)
           pe("ruleset requested a file dump to the remote host",0);
          W((n=FG(dfd))!=EOF){
           sb[0]=n;
           wro(sb,1,0);
          }
          IF(!noshowp)
           pe("ruleset requested file dump was successful",0);
         }
         FC(dfd);
        }
       }
      }
      /* check, and handle the write/append variable.  checks for append */
      /* first, beacuse if one variable is going to be larger it will be */
      /* the apppend variable. */
      EI(!SNCC(swrite,NS_WRITE,SL(NS_WRITE))||!SNCC(swrite,NS_APPEND,
      SL(NS_APPEND))){
       IF(isprivileged)
        pe("can not write data to a file with privileged access",0);
       E{
        /* find out which variable it is, two in one function here. */
        IF(!SNCC(swrite,NS_APPEND,SL(NS_APPEND))){
         n=SL(NS_APPEND);
         /* append type, fopen(). */
         sb[0]=0x61;
        }
        E{
         n=SL(NS_WRITE);
         /* write type, fopen(). */
         sb[0]=0x77;
        }
        /* changes buffer to dump the variable.  for later display. */
        truncateoutput(n);
        /* update the new clean buffer. */
        parseoutput(sreadl,swrite);
#ifndef DISABLE_SYSLOG
        wrso("%d-%d id: %lu-%u.%u.%u.%u write: %s",slnum_t,(slnum_s++),cpid,
        GU(),GEU(),GG(),GEG(),swrite);
#endif
        /* check for existence with non-writability. */
        IF(!AC(swrite,F_OK)&&AC(swrite,W_OK))
         pe("could not access the file requested to write to in the ruleset",
         0);
        E{
         IF(!(dfd=FO(swrite,sb)))
          pe("file requested to write to could not be opened properly",0);
         E{
          FP(dfd,"%s\n",sreadl);
          IF(!noshowp)
           pe("ruleset requested write out was successful",0);
         }
         FC(dfd);
        }
       }
      }
      /* check, and handle the chdir variable. */
      EI(!SNCC(swrite,NS_CHDIR,SL(NS_CHDIR))){
       /* changes buffer to dump the variable.  for later display. */
       truncateoutput(SL(NS_CHDIR));
       /* update the new clean buffer. */
       parseoutput(sreadl,swrite);
       IF(SL(swrite)){
        IF(CD(swrite)){
         IF(!noshowp)
          pe("ruleset requested directory change failed",0);
        }
        E{
         IF(!noshowp)
          pe("ruleset requested directory change was successful",0);
        }
       }
      }
      /* check, and handle the (send to) route host variable. */
      EI(!SNCC(swrite,NS_ROUTE,SL(NS_ROUTE))){
       /* changes buffer to dump the variable.  for later display. */
       truncateoutput(SL(NS_ROUTE));
       /* update the new clean buffer. */
       parseoutput(sreadl,swrite);
       IF(SL(swrite)){
        IF(rsock==-1){
         IF(!noshowp)
          pe("rulset requested route write failed, no (open) route socket",0);
        }
        E{
         wro(swrite,SL(swrite),2);
         wro("\n",1,2);
         IF(!noshowp)
          pe("ruleset requested route write was successful",0);
        }
       }
      }
      /* check, and handle the time passed (send) variable. */
      EI(!SNCC(swrite,NS_TIMED,SL(NS_TIMED))){
       /* set the current time to check the value, compared to the */
       /* connection value. */
       TM(&ctm);
       /* changes buffer to dump the variable.  for later display. */
       truncateoutput(SL(NS_TIMED));
       /* update the new clean buffer. */
       parseoutput(sreadl,swrite);
       parameter(swrite,0,0x2C);
       IF(wischeck(parm,1)&&(rtm+AI(parm))<ctm){
        IF(SL(swrite)>(SL(parm)+1)){
         truncateoutput(SL(parm)+1);
         IF(!nossend)
          pd(swrite,0,columns);
         wro(swrite,SL(swrite),0);
         wro("\n",1,0);
        }
       }
      }
      /* check, and handle the execution dump/formatting variables. */
      EI(!SNCC(swrite,NS_EXEC,SL(NS_EXEC))||!SNCC(swrite,NS_EXECF,SL(NS_EXECF))
      ){
       /* will reuse this int again, and again. */
       p=(!SNCC(swrite,NS_EXEC,SL(NS_EXEC))?0:1);
       /* changes buffer to dump the variable.  for later display. */
       truncateoutput(p?SL(NS_EXECF):SL(NS_EXEC));
       /* update the new clean buffer. */
       parseoutput(sreadl,swrite);
       IF(!noshowp&&!p)
        pe("ruleset requested a local program execution",0);
       /* privileged checking occurs with this function, since its used */
       /* elsewhere. */
       dumpexec(swrite,p);
       IF(p&&SL(execformat)){
        /* byte-by-byte transfer/check. (reused int p) */
        F(p=0;((p<(SO(sreadl)-1))&&(p<(SL(execformat))));p++)
         sreadl[p]=execformat[p];
        sreadl[p]=0x0;
       }
      }
      /* not required to check every slot for a byte.  but, i do. */
      EI(swrite[0]&&swrite[1]&&swrite[2]&&swrite[3]&&swrite[4]&&swrite[0]==
      PARAMETER_VAR_CHAR&&swrite[1]==0x7B&&ID((U I)swrite[2])&&swrite[3]==0x7D
      &&swrite[4]==0x3D){
       /* store the number before chomping it down. */
       s=(swrite[2]-0x30);
       /* do not overwrite the statically set variables. */
       IF(dynamicvarset[s]!=2){
        /* changes buffer to dump the variable.  for later display. */
        truncateoutput(5);
        /* update the new clean buffer. */
        parseoutput(sreadl,swrite);
        IF(dynamicvarset[s]==1)
         FR(dynamicvar[s]);
        IF(!(dynamicvar[s]=(C *)SDU(swrite)))
         pe("parsesocket(): duplication of memory error",1);
        dynamicvarset[s]=1;
       }
      }
      E{
       IF(!nossend)
        pd(swrite,0,columns);
       wro(swrite,SL(swrite),0);
       wro("\n",1,0);
       /* add the input to the log, if enabled. (-l option) */
       IF(log&&!tnetraw)
        logdata(swrite,0);
      }
     }
    }
    B(sreadl,(BASE_BUFFER*16+1));
    j=0;
   }
   E
    /* do not print the telnet options, no one wants to see that. */
    IF(tnet&&truetnet&&sread[i]==0xFF&&(sread[i+1]==0xFE||sread[i+1]==0xFD||
    sread[i+1]==0xFC||sread[i+1]==0xFB))
     i+=2;
    /* for reading of printable characters only.  meaning non-printable */
    /* characters will not make it to ruleset processing.  but, the telnet */
    /* protocol handling is processed before this point.  so, it will not be */
    /* effected. (-P option).  also, omits defined characters from them -o */
    /* option. */
    EI((!printonly||IP((U I)sread[i]))&&(!omitchars||!STC(ochars,sread[i]))){
     /* check the buffer that is filling until a completed line, making sure */
     /* not to exceed bounds.  if it does the buffer is cleared. */
     IF(j>=SO(sreadl)){
      B(sreadl,(BASE_BUFFER*16+1));
      j=0;
     }
     /* write the byte if nothing before this option wanted to stop it. */
     E{
      /* check, and use the ns_incoming_char() symbol, it passes the byte to */
      /* be added to the buffer. (int) */
#ifndef DISABLE_MODULES
      IF(vmodule)
       (*incoming_char_function)(sread[i]);
#endif
      sreadl[j++]=sread[i];
     }
    }
  }
 }
 /* once more.  better safe than sorry, do not want fds to fill up. */
 closesocket(0);
 R;
}
/* for display types of version information. */
V showinfo(U SH type){
 C *timebuf;
 T tm;
 IF(type==1){
  /* left without uname (verbose display) information. */
  nsprint("[%lu] * Version: netscript/v%s!%s. -- License: %s.\n"
  "[%lu] * Id data: %s\n"
  "[%lu] * Mod: %s -- Src: %s -- Arp: %s -- Gui: %s -- Slg: %s -- Ncr: %s.\n"
  "[%lu] * Author: %s.%s\n",cpid,VERSION,COMPTYPE,license,cpid,id,cpid,
  (defined[0]?"on":"off"),(defined[1]?"on":"off"),(defined[2]?"on":"off"),
  (defined[3]?"on":"off"),(defined[4]?"on":"off"),(defined[5]?"on":"off"),
  cpid,author,isprivileged?" (privileged)":"");
 }
 EI(type==2){
  TM(&tm);
  IF(!(timebuf=(C *)SDU(CT(&tm))))
   pe("showinfo(): duplication of memory error",1);
  IF(timebuf[(SL(timebuf)-1)]==0x0A)
  timebuf[(SL(timebuf)-1)]=0x0;
  nsprint("[ netscript: (version: v%s!%s - launchtime: %s) ]\n",VERSION,
  COMPTYPE,timebuf);
  FR(timebuf);
 }
 R;
}
/* this function will handle the prompted version of netscript. */
V iface(C *base){
 U SH r=0;
 U SH s=0;
 U I i=0;
 U I j=0;
 U I k=0;
 C **tmpname;
 C *tmptime;
 U C icwd[(BASE_BUFFER*4+1)];
 U C iread[(BASE_BUFFER*4+1)];
 FL *ifd;
 PT fp;
 T tm;
 S group *gent;
 S passwd *pent;
 /* no user aborts in this mode, will reset at the end of the function. */
 SG(SIGINT,SIG_IGN);
 /* no reading of (non-ruleset) files when privileged.  this is silent. */
 IF(!isprivileged){
  IF(!(ifd=FO(rcfile,"r")))
   s=0;
  E
   s=1;
 }
 W(!r){
  IF(!s)
   nsprint("%s",base);
  IF(!s&&!(ifd=FO(ttyn,"r")))
   pe("could not open standard input for read",1);
  E{
   B(iread,(BASE_BUFFER*4+1));
   IF(FS(iread,(SO(iread)-1),ifd)){
    F(i=0;i<SL(iread);i++){
     IF(iread[i]==0x0A||iread[i]==0x0D)
      iread[i]=0x0;
     /* apply question marks to unprintable characters. */
     IF(iread[i]&&!isprint(iread[i]))
      iread[i]=0x3F;
    }
   }
   /* will close the stream below, now that its finished. */
   EI(s)
    s=0;
  }
  IF(!s)
   FC(ifd);
  /* command handling. */
  IF(!SNCC(iread,"anotify",7)){
   noshowa=(noshowa?0:1);
   nsprint("anotify: %sabled\n",(noshowa?"en":"dis"));
  }
  EI(!SNCC(iread,"background",10)){
   isbg=(isbg?0:1);
   nsprint("background: %sabled\n",(isbg?"en":"dis"));
  }
  EI(!SNCC(iread,"bind",4)){
   IF(sethost)
    nsprint("bind: there are hostname(s) defined to connect to, can not define"
    ".\n");
   E{
    bindmode=(bindmode?0:1);
    nsprint("bind: %sabled\n",(bindmode?"en":"dis"));
   }
  }
  EI(!SNCC(iread,"chdir",5)){
   IF(!parameter(iread,1,0x20)){
    IF(setcdir)
     FR(cdir);
    parsecmdline(parm);
    IF(!(cdir=(C *)SDU(parsedline)))
     pe("iface(): duplication of memory error",1);
    setcdir=1;
   }
   nsprint("chdir: %s\n",(setcdir?cdir:IFACE_UNSET));
  }
  EI(!SNCC(iread,"chroot",6)){
   IF(!parameter(iread,1,0x20)){
    IF(setrdir)
     FR(rdir);
    parsecmdline(parm);
    IF(!(rdir=(C *)SDU(parsedline)))
     pe("iface(): duplication of memory error",1);
    setrdir=1;
   }
   nsprint("chroot: %s\n",(setrdir?rdir:IFACE_UNSET));
  }
  EI(!SNCC(iread,"cnotify",7)){
   noshowc=(noshowc?0:1);
   nsprint("cnotify: %sabled\n",(noshowc?"en":"dis"));
  }
  EI(!SNCC(iread,"disparrows",10)){
   nofrom=(nofrom?0:1);
   nsprint("disparrows: %sabled\n",(nofrom?"en":"dis"));
  }
  EI(!SNCC(iread,"dispincoming",12)){
   nosrecv=(nosrecv?0:1);
   nsprint("dispincoming: %sabled\n",(nosrecv?"en":"dis"));
  }
  EI(!SNCC(iread,"dispoutgoing",12)){
   nossend=(nossend?0:1);
   nsprint("dispoutgoing: %sabled\n",(nossend?"en":"dis"));
  }
  EI(!SNCC(iread,"disptype",8)){
   IF(displaytype++>1)
    displaytype=0;
   nsprint("disptype: %s\n",(!displaytype?"disabled":(displaytype==1?"hex":
   "dec")));
  }
  EI(!SNCC(iread,"execexit",8)){
   runexit=(runexit?0:1);
   nsprint("execexit: %sabled\n",(runexit?"en":"dis"));
  }
  EI(!SNCC(iread,"execfile",8)){
   IF(isprivileged)
    nsprint("execfile: can not log data to a file with privileged access.\n");
   E{
    IF(!parameter(iread,1,0x20)){
     IF(runcmd)
      FR(execfile);
     parsecmdline(parm);
     IF(!(execfile=(C *)SDU(parsedline)))
      pe("iface(): duplication of memory error",1);
     runcmd=1;
    }
    nsprint("execfile: %s\n",(runcmd?execfile:IFACE_UNSET));
   }
  }
  EI(!SNCC(iread,"execinteractive",15)){
   isiexec=(isiexec?0:1);
   nsprint("execinteractive: %sabled\n",(isiexec?"en":"dis"));
  }
  EI(!SNCC(iread,"execpre",7)){
   IF(!parameter(iread,1,0x20)){
    IF(runpre)
     FR(execpre);
    parsecmdline(parm);
    IF(!(execpre=(C *)SDU(parsedline)))
     pe("iface(): duplication of memory error",1);
    runpre=1;
   }
   nsprint("execpre: %s\n",(runpre?execpre:IFACE_UNSET));
  }
  EI(!SNCC(iread,"execshell",9)){
   IF(!parameter(iread,1,0x20)){
    parsecmdline(parm);
    IF(AC(parsedline,X_OK))
     nsprint("execshell: argument1 is not a valid file, or is not executable."
     "\n");
    E{
     IF(setshell)
      FR(eshell);
     IF(!(eshell=(C *)SDU(parsedline)))
      pe("iface(): duplication of memory error",1);
     setshell=1;
    }
   }
   nsprint("execshell: %s\n",(setshell?eshell:IFACE_UNSET));
  }
  EI(!SNCC(iread,"execwait",8)){
   IF(!parameter(iread,1,0x20)){
    IF(AI(parm)<0)
     nsprint("execwait: argument1 must be a positive integer, can not define."
     "\n");
    E
     xalrm=AI(parm);
   }
   nsprint("execwait: %d\n",xalrm);
  }
  EI(!SNCC(iread,"forever",7)){
   forever=(forever?0:1);
   nsprint("forever: %sabled\n",(forever?"en":"dis"));
  }
#ifdef GTK
  EI(!SNCC(iread,"guicolor",8)){
   IF(!parameter(iread,1,0x20)){
    parsecmdline(parm);
    IF(SL(parsedline)==6){
     B((V *)guic,SO(guic));
     F(isguic=i=0;i<6;i+=2){
      IF(IL((U I)parsedline[i]))
       j=(TL(parsedline[i])-0x56);
      E
       j=(parsedline[i]-0x2F);
      j*=16;
      IF(IL((U I)parsedline[i+1]))
       j+=(TL(parsedline[i+1])-0x56);
      E
       j+=(parsedline[i+1]-0x2F);
      j-=17;
      IF(j>=0&&j<256)
       guic[isguic]=(j*257);
      isguic++;
     }
    }
   }
   nsprint("guicolor: %.2x%.2x%.2x\n",(guic[0]/257),(guic[1]/257),(guic[2]/257)
   );
  }
  EI(!SNCC(iread,"guilabel",8)){
   IF(!parameter(iread,1,0x20)){
    IF(isguil){
     F(k=0;k<isguil;k++)
      FR(gblabel[k]);
     isguil=0;
    }
    parsecmdline(parm);
    W(!parameter(parsedline,isguil,0x3A)&&isguil<3){
     IF(!(gblabel[isguil++]=(C *)SDU(SL(parm)?parm:"nolabel")))
      pe("iface(): duplication of memory error",1);
    }
   }
   nsprint("guititle: %s%s\n",(isguil?gblabel[0]:IFACE_UNSET),(isguil>1?
   " <...>":""));
  }
  EI(!SNCC(iread,"guipdlen",8)){
   IF(!parameter(iread,1,0x20)){
    IF(AI(parm)<0)
     nsprint("guipdlen: argument1 must be a positive integer, can not define."
     "\n");
    E
     guio=(AI(parm)>GUI_MAXLEN?GUI_MAXLEN:AI(parm));
   }
   nsprint("guipdlen: %d\n",guio);
  }
  EI(!SNCC(iread,"guipd",5)){
   isguis=(isguis?0:1);
   nsprint("guipd: %sabled\n",(isguis?"en":"dis"));
  }
  EI(!SNCC(iread,"guititle",8)){
   IF(!parameter(iread,1,0x20)){
    IF(isguititle)
     FR(guititle);
    parsecmdline(parm);
    IF(!(guititle=(C *)SDU(parsedline)))
     pe("iface(): duplication of memory error",1);
    isguititle=1;
   }
   nsprint("guititle: %s\n",(isguititle?guititle:IFACE_UNSET));
  }
  EI(!SNCC(iread,"gui",3)){
   IF(isprivileged)
    nsprint("gui: the gui frontend can not be used when netscript is privilege"
    "d.\n");
   E{
    IF(isgui){
     IF(isbgui)
      isbgui=isgui=0;
     E
      isbgui=1;
    }
    E
     isgui=1;
    nsprint("gui: %s\n",(!isgui?"disabled":(isbgui==1?"generic":"normal")));
   }
  }
#endif
  EI(!SNCC(iread,"host",4)){
   IF(bindmode)
    nsprint("host: bind mode is already defined, can not define.\n");
   E{
    IF(!parameter(iread,1,0x20)){
     IF(sethost){
      F(k=0;k<tshs;k++)
       FR(shost[k]);
      tshs=0;
     }
     parsecmdline(parm);
     W(!parameter(parsedline,tshs,0x2C)){
      IF(!(shost[tshs++]=(C *)SDU(SL(parm)?parm:"0")))
       pe("iface(): duplication of memory error",1);
      IF(tshs>=MAX_ARGS)
       pe("too many slots to store in memory",1);
     }
     sethost=1;
    }
    nsprint("host: %s%s\n",(sethost?shost[0]:IFACE_UNSET),(tshs>1?" <...>":
    ""));
   }
  }
  EI(!SNCC(iread,"initial",7)){
   IF(!parameter(iread,1,0x20)){
    IF(initsend)
     FR(iswrite);
    parsecmdline(parm);
    IF(!(iswrite=(C *)SDU(parsedline)))
     pe("iface(): duplication of memory error",1);
    initsend=1;
   }
   nsprint("initial: %s\n",(initsend?iswrite:IFACE_UNSET));
  }
  EI(!SNCC(iread,"logfile",7)){
   IF(isprivileged)
    nsprint("logfile: can not log data to a file with privileged access.\n");
   E{
    IF(!parameter(iread,1,0x20)){
     IF(log)
      FR(logfile);
     parsecmdline(parm);
     IF(!(logfile=(C *)SDU(parsedline)))
      pe("iface(): duplication of memory error",1);
     log=1;
    }
    nsprint("logfile: %s\n",(log?logfile:IFACE_UNSET));
   }
  }
#ifdef NCURSES
  EI(!SNCC(iread,"ncurseslabel",12)){
   IF(!parameter(iread,1,0x20)){
    IF(isncursesl){
     F(k=0;k<isncursesl;k++)
      FR(nclabel[k]);
     isncursesl=0;
    }
    parsecmdline(parm);
    W(!parameter(parsedline,isncursesl,0x3A)&&isncursesl<2){
     IF(!(nclabel[isncursesl++]=(C *)SDU(SL(parm)?parm:"nolabel")))
      pe("iface(): duplication of memory error",1);
    }
   }
   nsprint("ncurseslabel: %s%s\n",(isncursesl?nclabel[0]:IFACE_UNSET),
   (isncursesl>1?" <...>":""));
  }
  EI(!SNCC(iread,"ncurses",7)){
   isncursesa=(isncursesa?0:1);
   nsprint("ncurses: %sabled\n",(isncursesa?"en":"dis"));
  }
#endif
  EI(!SNCC(iread,"nonprintables",13)){
   printonly=(printonly?0:1);
   nsprint("nonprintables: %sabled\n",(printonly?"en":"dis"));
  }
  EI(!SNCC(iread,"notelnetshowopt",15)){
   notnetopt=(notnetopt?0:1);
   nsprint("notelnetshowopt: %sabled\n",(notnetopt?"en":"dis"));
  }
  EI(!SNCC(iread,"nowordwrap",10)){
   nowrap=(nowrap?0:1);
   nsprint("nowordwrap: %sabled\n",(nowrap?"en":"dis"));
  }
  EI(!SNCC(iread,"omit",4)){
   IF(!parameter(iread,1,0x20)){
    IF(omitchars)
     FR(ochars);
    parsecmdline(parm);
    IF(!(ochars=(C *)SDU(parsedline)))
     pe("iface(): duplication of memory error",1);
    omitchars=1;
   }
   nsprint("omit: %s\n",(omitchars?ochars:IFACE_UNSET));
  }
  EI(!SNCC(iread,"perm",4)){
   IF(!parameter(iread,1,0x20)){
    IF(!setperms){
     parsecmdline(parm);
     setpermissions(parsedline,0);
     setperms=1;
    }
    E
     nsprint("perm: permissions are already defined, can not re-define.\n");
   }
   nsprint("perm: %u.%u <%s>\n",nuid,ngid,(setperms?"set":"unset"));
  }
  EI(!SNCC(iread,"pnotify",7)){
   noshowp=(noshowp?0:1);
   nsprint("pnotify: %sabled\n",(noshowp?"en":"dis"));
  }
  EI(!SNCC(iread,"port",4)){
   IF(!parameter(iread,1,0x20))
    sport=portentry(parm);
   nsprint("port: %d\n",sport);
  }
  EI(!SNCC(iread,"routehost",9)){
   IF(!parameter(iread,1,0x20)){
    IF(setroute)
     FR(rhost);
    parsecmdline(parm);
    IF(!(rhost=(C *)SDU(parsedline)))
     pe("iface(): duplication of memory error",1);
    setroute=1;
   }
   nsprint("routehost: %s\n",(setroute?rhost:IFACE_UNSET));
  }
  EI(!SNCC(iread,"routenoincoming",15)){
   norrecv=(norrecv?0:1);
   nsprint("routenoincoming: %sabled\n",(norrecv?"en":"dis"));
  }
  EI(!SNCC(iread,"routenooutgoing",15)){
   norsend=(norsend?0:1);
   nsprint("routenooutgoing: %sabled\n",(norsend?"en":"dis"));
  }
  EI(!SNCC(iread,"routeport",9)){
   IF(!parameter(iread,1,0x20)){
    IF(AI(parm)<0)
     nsprint("routeport: argument1 must be a positive integer, can not define."
     "\n");
    E
     rport=AI(parm);
   }
   nsprint("routeport: %d\n",rport);
  }
  EI(!SNCC(iread,"routeudp",8)){
   isudpr=(isudpr?0:1);
   nsprint("routeudp: %sabled\n",(isudpr?"en":"dis"));
  }
  EI(!SNCC(iread,"rulesetedit",11)){
   editrules=(editrules?0:1);
   nsprint("rulesetedit: %sabled\n",(editrules?"en":"dis"));
  }
  EI(!SNCC(iread,"rulesetinput",12)){
   IF(setfile)
    nsprint("rulesetinput: there is a ruleset file defined to use, can not def"
    "ine.\n");
   E{
    inputrules=(inputrules?0:1);
    nsprint("rulesetinput: %sabled\n",(inputrules?"en":"dis"));
   }
  }
  EI(!SNCC(iread,"ruleset",7)){
   IF(inputrules)
    nsprint("ruleset: ruleset input is already defined, can not define.\n");
   E{
    IF(!parameter(iread,1,0x20)){
     IF(setfile)
      FR(rulesfile);
     parsecmdline(parm);
     IF(!(rulesfile=(C *)SDU(parsedline)))
      pe("iface(): duplication of memory error",1);
     setfile=1;
    }
    nsprint("ruleset: %s\n",(setfile?rulesfile:IFACE_UNSET));
   }
  }
  EI(!SNCC(iread,"sdelay",5)){
   IF(!parameter(iread,1,0x20)){
    IF(AI(parm)<0)
     nsprint("sdelay: argument1 must be a positive integer, can not define."
     "\n");
    E
     sdelay=AI(parm);
   }
   nsprint("sdelay: %d\n",sdelay);
  }
  EI(!SNCC(iread,"showlines",10)){
   lnum=(lnum?0:1);
   IF(lnum)
    lnum=lnum_i=lnum_o=nowrap=1;
   nsprint("showlines: %sabled\n",(lnum?"en":"dis"));
  }
  EI(!SNCC(iread,"showversion",11)){
   showv=(showv?0:2);
   nsprint("showversion: %sabled\n",(showv?"en":"dis"));
  }
  EI(!SNCC(iread,"socketopts",10)){
   IF(!parameter(iread,1,0x20)){
    IF(soptions)
     FR(sopts);
    parsecmdline(parm);
    IF(!(sopts=(C *)SDU(parsedline)))
     pe("iface(): duplication of memory error",1);
    soptions=1;
   }
   nsprint("socketopts: %s\n",(soptions?sopts:IFACE_UNSET));
  }
#ifndef DISABLE_SYSLOG
  EI(!SNCC(iread,"syslog",6)){
   slog=(slog?0:1);
   nsprint("syslog: %sabled\n",(slog?"en":"dis"));
  }
#endif
  EI(!SNCC(iread,"telnettype",10)){
   IF(tnetraw++>1)
    tnetraw=0;
   nsprint("telnettype: %s\n",(!tnetraw?"disabled":(tnetraw==1?"stderr":
   "stdout")));
  }
  EI(!SNCC(iread,"telnet",6)){
   tnet=(tnet?0:1);
   nsprint("telnet: %sabled\n",(tnet?"en":"dis"));
  }
  EI(!SNCC(iread,"timedexit",9)){
   IF(!parameter(iread,1,0x20)){
    IF(AI(parm)<0)
     nsprint("timedexit: argument1 must be a positive integer, can not define."
     "\n");
    E
     alrm=AI(parm);
   }
   nsprint("timedexit: %d\n",alrm);
  }
  EI(!SNCC(iread,"udpmode",7)){
   isudp=(isudp?0:1);
   nsprint("udpmode: %sabled\n",(isudp?"en":"dis"));
  }
  EI(!SNCC(iread,"udelay",5)){
   IF(!parameter(iread,1,0x20)){
    IF(AI(parm)<0)
     nsprint("udelay: argument1 must be a positive integer, can not define."
     "\n");
    E
     sudelay=AI(parm);
   }
   nsprint("udelay: %d\n",sudelay);
  }
  EI(!SNCC(iread,"var",3)){
   IF(!parameter(iread,1,0x20)){
    IF(ID((U I)parm[0])){
     j=(parm[0]-0x30);
     IF(dynamicvarset[j]!=2)
      FR(dynamicvar[j]);
     IF(!parameter(iread,2,0x20)){
      parsecmdline(parm);
      IF(!(dynamicvar[j]=(C *)SDU(parsedline)))
       pe("iface(): duplication of memory error",1);
      dynamicvarset[j]=2;
     }
     nsprint("var(%d): %s\n",j,(dynamicvarset[j]==2?dynamicvar[j]:
     IFACE_UNSET));
    }
    E
     nsprint("var(*): parameter1 is not a value between 0 and 9.\n");
   }
   E
    nsprint("var(syntax): var <0-9> <data>\n");
  }
  EI(!SNCC(iread,"vhost",5)){
   IF(!parameter(iread,1,0x20)){
    IF(isvhost)
     FR(vhost);
    parsecmdline(parm);
    IF(!(vhost=(C *)SDU(parsedline)))
     pe("iface(): duplication of memory error",1);
    isvhost=1;
   }
   nsprint("vhost: %s\n",(isvhost?vhost:IFACE_UNSET));
  }
  EI(!SNCC(iread,"exit",4)||!SNCC(iread,"quit",4))
   nsexit(0,0);
  EI(!SNCC(iread,"run",3)||!SNCC(iread,"launch",6))
   r=1;
  EI(!SNCC(iread,"&cwd",4)){
   IF(!parameter(iread,1,0x20)){
    parsecmdline(parm);
    CD(parm);
   }
   B(icwd,(BASE_BUFFER*4+1));
   GWD(icwd,(BASE_BUFFER*4+1));
   nsprint("&cwd: %s\n",icwd);
  }
  EI(!SNCC(iread,"&id",3)){
   IF(!(tmpname=(C **)M(5*(SO(C *)))))
    pe("iface(): allocation of memory error",1);
   B((V *)tmpname,SO(tmpname));
   IF((pent=GPU(GU()))&&SL(pent->pw_name)){
    IF(!(tmpname[0]=(C *)SDU(pent->pw_name)))
     pe("iface(): duplication of memory error",1);
   }
   E{
    IF(!(tmpname[0]=(C *)SDU("unknown")))
     pe("iface(): duplication of memory error",1);
   }
   IF((pent=GPU(GEU()))&&SL(pent->pw_name)){
    IF(!(tmpname[1]=(C *)SDU(pent->pw_name)))
     pe("iface(): duplication of memory error",1);
   }
   E{
    IF(!(tmpname[1]=(C *)SDU("unknown")))
     pe("iface(): duplication of memory error",1);
   }
   IF((gent=GGG(GG()))&&SL(gent->gr_name)){
    IF(!(tmpname[2]=(C *)SDU(gent->gr_name)))
     pe("iface(): duplication of memory error",1);
   }
   E{
    IF(!(tmpname[2]=(C *)SDU("unknown")))
     pe("iface(): duplication of memory error",1);
   }
   IF((gent=GGG(GEG()))&&SL(gent->gr_name)){
    IF(!(tmpname[3]=(C *)SDU(gent->gr_name)))
     pe("iface(): duplication of memory error",1);
   }
   E{
    IF(!(tmpname[3]=(C *)SDU("unknown")))
     pe("iface(): duplication of memory error",1);
   }
   nsprint("&id: uid=%u(%s) euid=%u(%s) gid=%u(%s) egid=%u(%s)\n",GU(),
   tmpname[0],GEU(),tmpname[1],GG(),tmpname[2],GEG(),tmpname[3]);
   FR(tmpname);
  }
  EI(!SNCC(iread,"&pid",4))
   nsprint("&pid: %lu (parent=%lu)\n",cpid,GPPD());
  EI(!SNCC(iread,"&time",6)){
   TM(&tm); 
   IF(!(tmptime=(C *)SDU(CT(&tm))))
    pe("iface(): duplication of memory error",1);
   IF(tmptime[(SL(tmptime)-1)]==0x0A)
    tmptime[(SL(tmptime)-1)]=0x0;
   nsprint("&time: %s (value=%d)\n",tmptime,tm);
   FR(tmptime);
  }
  EI(SL(iread)&&iread[0]==0x21){
   IF(isprivileged)
    nsprint("!: can not execute third party programs with privileged access."
    "\n");
   E{
    F(i=1;i<SL(iread);i++)
     iread[(i-1)]=iread[i];
    iread[(i-1)]=0x0;
    SW(fp=FK()){
     CS -1:
      nsprint("!: fork() failed.\n");
      BR;
     CS 0:
      /* allow aborts to be handled, since its passed over to the program. */
      SG(SIGINT,SIG_DFL);
      EL(eshell,eshell,(SL(iread)?"-c":0),(SL(iread)?iread:0),0);
      _exit(1);
      BR;
    default:
     /* wait up. */
     WP(fp,0,0);
     BR;
    }
   }
  }
  EI(!SNCC(iread,"help",4)){
   nsprint("anotify\t\t\t\t- disables display of all netscript comments.\n"
   "background\t\t\t- sends netscript to the background.\n"
   "bind\t\t\t\t- binds to the supplied port.\n"
   "chdir\t\t<directory>\t- changes the working directory of netscript.\n"
   "chroot\t\t<directory>\t- changes the root directory of netscript.\n"
   "cnotify\t\t\t\t- disables display of connection notifications.\n"
   "disparrows\t\t\t- disables display of arrow precursors.\n"
   "dispincoming\t\t\t- disables display of incoming data.\n"
   "dispoutgoing\t\t\t- disables display of outgoing data.\n"
   "disptype\t\t\t- displays hex/dec values.\n"
   "execexit\t\t\t- kills netscript after the first dump.\n"
   "execfile\t<file>\t\t- dumps the program data to the socket.\n"
   "execinteractive\t\t\t- will allow interactive use of programs.\n"
   "execpre\t\t<data>\t\t- precurse data to attach for each line dumped.\n"
   "execshell\t<file>\t\t- will use an alternate shell for executions.\n"
   "execwait\t<second(s)>\t- kills the program if the program dump hangs.\n"
   "forever\t\t\t\t- when disconnected.  reconnect, or rebind.\n"
#ifdef GTK
   "gui\t\t\t\t- enables the gui frontend. (to normal/generic)\n"
   "guicolor\t<ffffff>\t- sets a text foreground color for the gui.\n"
   "guilabel\t<l1:l2:l3>\t- defines button labels for the gui.\n"
   "guipd\t\t\t\t- automatically start pulled down. (non-generic)\n"
   "guipdlen\t<size>\t\t- increase the length of the gui. (non-generic)\n"
   "guititle\t<title>\t\t- displays as the title for the gui.\n"
#endif
   "host\t\t<host,host,...>\t- remote host (or ip) to connect to.\n"
   "initial\t\t<data>\t\t- send initial data once connected. (\"?\"=dyn)\n"
   "logfile\t\t<file>\t\t- turns input/output logging on.\n"
#ifdef NCURSES
   "ncurses\t\t\t\t- switch console to ncurses visualization.\n"
   "ncurseslabel\t<l1:l2>\t\t- defines tab labels for the ncurses.\n"
#endif
   "nonprintables\t\t\t- disables reading of non-printable characters.\n"
   "notelnetshowopt\t\t\t- disables display of verbose telnetd-opts.\n"
   "nowordwrap\t\t\t- disables wordwrap for in/out displaying.\n"
   "omit\t\t<charlist>\t- will omit socket reading of the charlist.\n"
   "perm\t\t<user[.group]>\t- changes netscript id permissions.\n"
   "pnotify\t\t\t\t- disables display of unimportant ruleset info.\n"
   "port\t\t<port>\t\t- remote port to connect to, or bind to.\n"
   "routehost\t<host>\t\t- will set a host, to route socket data to.\n"
   "routenoincoming\t\t\t- disable routed sending of incoming data.\n"
   "routenooutgoing\t\t\t- disable routed sending of outgoing data.\n"
   "routeport\t<port>\t\t- will set a port to connect on the route host.\n"
   "routeudp\t\t\t- switches the route host to the UDP protocol.\n"
   "ruleset\t\t<file>\t\t- input/output (ruleset) file.\n"
   "rulesetedit\t\t\t- edit the temporary/ruleset with an editor.\n"
   "rulesetinput\t\t\t- input/output ruleset, via input.\n"
   "sdelay\t\t<second(s)>\t- delay before processing incoming data. \n"
   "showlines\t\t\t- displays the value of each line.\n"
   "showversion\t\t\t- displays the version info of netscript.\n"
   "socketopts\t<#,#,#:...>\t- sets socket option(s) inside netscript.\n"
#ifndef DISABLE_SYSLOG
   "syslog\t\t\t\t- enables system logging.\n"
#endif
   "telnet\t\t\t\t- attempt to interpret the telnet protocol.\n"
   "telnettype\t\t\t- dump raw socket data. (to stderr/stdout)\n"
   "timedexit\t<second(s)>\t- closes the connection after alloted time.\n"
   "udpmode\t\t\t\t- switches netscript to the UDP protocol.\n"
   "udelay\t\t<usecond(s)>\t- delay before processing incoming data.\n"
   "var\t\t<0-9> <data>\t- will statically define the dynamic variable.\n"
   "vhost\t\t<host>\t\t- will define a virtual host to use, or bind to.\n"
   "&cwd AND &id AND &pid AND &time\t- local (only) interactive display comman"
   "ds.\n"
   "![command]\t\t\t- shell out of interactive netscript.\n"
   "run AND launch\t\t\t- execute the provided information.\n"
   "exit AND quit\t\t\t- exit netscript.\n"
   "help\t\t\t\t- this screen, displays interactive commands.\n");
  }
  EI(SL(iread)&&iread[0]!=0x23)
   nsprint("unknown command: %s (try \'help\' for a command list)\n",iread);
 }
 SG(SIGINT,sighandler);
 R;
}
/* display of variable uses, the -d argument.  finally went for \t's */
V displayv(V){
 nsprint("Defined ruleset match, and replacement variables used by netscript:"
 "\n"
 "IN/OUT\tTYPE\t\tDOES/ACTION\t\t\t\tVARIABLE\n"
 "I/O\treplace\t\twill replace with the in/out used above\t\"%s\"\n"
 "O\tpre-data\tstops use of the ruleset\t\t\"%s\"\n"
 "O\tpre-data\trestarts use of the ruleset\t\t\"%s\"\n"
 "O\tpre-data\tstops the ruleset reading cycle\t\t\"%s\"\n"
 "O\tpre-data\tcloses the socket\t\t\t\"%s\"\n"
 "O\tpre-data\tasks for input response to send\t\t\"%s\"\n"
 "O\tpre-data\ttruncates characters from server data\t\"%s\"\n"
 "O\tpre-data\ttruncates tokens from server data (l)\t\"%s\"\n"
 "O\tpre-data\ttruncates tokens from server data (r)\t\"%s\"\n"
 "O\tpre-data\ttruncates strings from server data\t\"%s\"\n"
 "O\tpre-data\tformats the received input data\t\t\"%s\"\n"
 "O\tpre-data\tdisplays the supplied data\t\t\"%s\"\n"
 "O\tpre-data\twrites the supplied data without a CR\t\"%s\"\n"
 "O\tpre-data\tuses rule only one time (requires data)\t\"%s\"\n"
 "O\tpre-data\tdisables a rule from the ruleset\t\"%s\"\n"
 "O\tpre-data\tdumps the file used after variable\t\"%s\"\n"
 "O\tpre-data\twrites to the file used after variable\t\"%s\"\n"
 "O\tpre-data\tappends to the file used after variable\t\"%s\"\n"
 "O\tpre-data\tchange to the dir used after variable\t\"%s\"\n"
 "O\tpre-data\tsends supplied data to the route host\t\"%s\"\n"
 "O\tpre-data\tonly sends data if time has passed\t\"%s\"\n"
 "O\tpre-data\texecutes the file used after variable\t\"%s\"\n"
 "O\tpre-data\texecutes the file to be used in rules\t\"%s\"\n"
 "I\tpre-data\tcheck for a reverse/negative match\t\"%s\"\n"
 "I\twildcard\tanything after point\t\t\t\"%s\"\n"
 "I\twildcard\tanything that is a value\t\t\"%s\"\n"
 "I\twildcard\tanything that is alphabetical\t\t\"%s\"\n"
 "I\twildcard\tanything that is numerical\t\t\"%s\"\n"
 "I\twildcard\tanything that is alphabetical|numerical\t\"%s\"\n"
 "I\twildcard\tanything that is upper case\t\t\"%s\"\n"
 "I\twildcard\tanything that is lower case\t\t\"%s\"\n"
 "I\twildcard\tanything that is punctuated\t\t\"%s\"\n"
 "I\twildcard\tanything that is a control word\t\t\"%s\"\n"
 "I\twildcard\tanything that is a printable word\t\"%s\"\n"
 "I\twildcard\t<\"%c####\" anything that is equal to length, #=num>\n"
 "O\tend-data\t<\"%c#####\" delay in secs before processing rule, #=num>\n"
 "O\treplace\t\t<\"%c#\" will replace with the argument from input, #=num>\n"
 "I/O\treplace\t\t<\"%c{#}\" will replace with stored dynamic data, #=num>\n"
 "I/O\treplace\t\t<\"%c##\" will replace with hex->chr value, ##=01-FF>\n"
 "I/O\treplace\t\t<\"%c###\" will replace with dec->chr value, ###=001-255>\n"
 "I/O\treplace\t\t<\"%c&\" will replace with a random letter>\n"
 "I/O\treplace\t\t<\"%c#\" will replace with a random number, #=#>\n"
 "\nDefined environmental variables used by netscript:\n"
 "DOES/ACTION\t\t\t\t\t\t\tVARIABLE\n"
 "treated as a command line\t\t\t\t\t\"$%s\"\n"
 "treated as a connection timeout time\t\t\t\t\"$%s\"\n"
#ifndef DISABLE_MODULES
 "treated as a file to use for module support\t\t\t\"$%s\"\n"
#endif
 "treated as a virtual host to use\t\t\t\t\"$%s\"\n"
 "treated as a backlog used for binding\t\t\t\t\"$%s\"\n"
 "treated as a maximum limit of text per line\t\t\t\"$%s\"\n"
 "treated as a shell to execute third party programs\t\t\"$%s\"\n"
 "treated as a text editor to make/edit (temporary) rulesets\t\"$%s\"\n\n"
 "Note: variables surrounded by <>'s are static, and unchangeable variables.\n"
 "      consult documentation for more definitions on variables.\n",NS_REPEAT,
 NS_STOP,NS_START,NS_HALT,NS_QUIT,NS_ASK,NS_TRUNC,NS_TOKENL,NS_TOKENR,NS_STR,
 NS_FMT,NS_ECHO,NS_RAW,NS_ONCE,NS_DISABLE,NS_DUMP,NS_WRITE,NS_APPEND,NS_CHDIR,
 NS_ROUTE,NS_TIMED,NS_EXEC,NS_EXECF,NS_NMATCH,NS_ALL,NS_ANY,NS_ALPHA,NS_DIGIT,
 NS_ALNUM,NS_UPPER,NS_LOWER,NS_PUNCT,NS_CNTRL,NS_PRINT,PARAMETER_VAR_CHAR,
 PARAMETER_VAR_CHAR,PARAMETER_VAR_CHAR,PARAMETER_VAR_CHAR,PARAMETER_VAR_CHAR,
 PARAMETER_VAR_CHAR,PARAMETER_VAR_CHAR,PARAMETER_VAR_CHAR,ENV_CMDLINE,
 ENV_TIMEOUT,
#ifndef DISABLE_MODULES
 ENV_MODULE,
#endif
 ENV_VHOST,ENV_BACKLOG,ENV_COLUMNS,ENV_SHELL,
 ENV_EDITOR);
 nsexit(0,0);
}
/* stated in displayv().  also, finally went for the \t's here too. */
V usage(U SH extended){
 nsprint("Usage: %s [extended options...] -s|-r file -b|-h host -p port\n"
 "Usage: %s file (must be a +rx internal argument supplied ruleset file)\n"
 "Usage: %s --list [path:path:...] (must be used by command line only)\n"
 "Usage: %s --hist [#]|[clear] (must be used by command line only)\n"
 "Usage: %s [--interactive] (will enter interactive prompted mode)\n",
 progname,progname,progname,progname,progname);
 IF(extended)
  nsprint("\n  -r <file>\t\tinput/output (ruleset) file. (required, or -s)\n"
  "  -h <host>\t\tremote host (or ip) to connect to. (required, or -b)\n"
  "  -p <port>\t\tremote port to connect to, or bind to. (required)\n"
  "  -# <data>\t\twill statically define the dynamic variable. (#=0-9)\n"
  "  -i <data>\t\tsend initial data once connected. (\"?\" for dynamic)\n"
  "  -m <path>\t\twill change the working directory of netscript.\n"
  "  -M <path>\t\twill change the root directory of netscript. (superuser)\n"
  "  -R <host>\t\twill set a host, to route socket data to.\n"
  "  -k <port>\t\twill set a port to connect to on the route host. (-R)\n"
  "  -u <data>\t\tchanges netscript id permissions. (\"user[.group]\" style)\n"
  "  -q <host>\t\twill define a virtual host to use, or bind to.\n"
  "  -o <data>\t\twill omit socket reading of the supplied character(s).\n"
  "  -S <time>\t\tdelay before processing incoming data. (seconds)\n"
  "  -U <time>\t\tdelay before processing incoming data. (useconds)\n"
  "  -t <time>\t\tclose the connection after alloted time.\n"
  "  -Q <data>\t\tsets local socket option(s). (\"#,#,#:#,#,#:...\" style)\n"
  "  -l <file>\t\tturns input/output logging on, writes to the file.\n"
#ifdef GTK
  "  -G <name>\t\tdisplays the name as the title for the gui. (-g)\n"
  "  -+ <size>\t\tchanges the length of the pulldown. (non-generic) (-g)\n"
  "  -# <data>\t\tsets foreground color for the gui. (\"ffffff\" style) (-g)\n"
  "  -K <data>\t\tsets button labels for the gui. (\"l1:l2:l3\" style) (-g)\n"
#endif
#ifdef NCURSES
  "  -= <data>\t\tsets tab labels for ncurses. (\"l1:l2\" style) (-_)\n"
#endif
  "  -e <prog>\t\tdumps the program data to the socket.\n"
  "  -E <data>\t\tprecurse data to attach for each line dumped. (-e)\n"
  "  -O <prog>\t\twill use an alternate shell for executions. (-e)\n"
  "  -x <time>\t\tkills the program if the program dump hangs. (-e)\n"
  "  -X\t\t\tkills netscript after the program has been dumped. (-e)\n"
  "  -a\t\t\twill allow interactive use of programs. (-e)\n"
  "  -f\t\t\twill run a third party edtior to define the ruleset.\n"
  "  -@\t\t\twill switch netscript to the UDP protocol.\n"
  "  -^\t\t\twill switch the route host to the UDP protocol.\n"
  "  -B\t\t\tsends netscript to the background.\n"
  "  -n\t\t\tdisables display of outgoing data.\n"
  "  -N\t\t\tdisables display of incoming data.\n"
  "  -j\t\t\tdisable routed sending of outgoing data. (-R)\n"
  "  -J\t\t\tdisable routed sending of incoming data. (-R)\n"
  "  -F\t\t\tdisables display of arrow precursors.\n"
  "  -I\t\t\twhen disconnected.  reconnect, or rebind.\n"
  "  -s\t\t\ttakes the ruleset from standard input instead of a file.\n"
  "  -b\t\t\tbinds to the supplied port, instead of connecting out.\n"
#ifdef GTK
  "  -g\t\t\tuses the gui frontend. (use twice for generic version)\n"
  "  -W\t\t\tautomatically starts pulled down. (non-generic) (-g)\n"
#endif
#ifdef NCURSES
  "  -_\t\t\tswitch console display to ncurses visualization.\n"
#endif
#ifndef DISABLE_SYSLOG
  "  -Z\t\t\tuses system logging. (if syslog is accessible)\n"
#endif
  "  -T\t\t\tattempts to interpret the telnet protocol.\n"
  "  -y\t\t\twill dump raw socket data to standard error. (-T)\n"
  "  -Y\t\t\twill dump raw socket data to standard output. (-T)\n"
  "  -z\t\t\tdisables display of verbose telnetd-opt input/output.\n"
  "  -L\t\t\tdisplays the value of each line as a precursor.\n"
  "  -H\t\t\tdisplays hex values, instead of character values. (ff)\n"
  "  -D\t\t\tdisplays dec values, instead of character values. (255)\n"
  "  -w\t\t\tdisables wordwrap for in/out displaying.\n"
  "  -P\t\t\tdisables reading of non-printable characters.\n"
  "  -c\t\t\tdisables display of connection notifications.\n"
  "  -C\t\t\tdisables display of ruleset info that is not required.\n"
  "  -A\t\t\tdisables display of all prompted netscript comments.\n"
  "  -d\t\t\tdisplays the variables used by the ruleset, and environ.\n"
  "  -v\t\t\tdisplays the version info of netscript, and exits.\n"
  "  -V\t\t\tdisplays the version info of netscript, and continues.\n"
  "\nAuthor: %s. [ihadnihn]\n",author);
 nsexit(0,0);
}
/* any global exit routines can be placed here, your choice. */
V nsexit(SH i,U SH type){
 /* clear the screen for ncurses. */
#ifdef NCURSES
 IF(isncurses)
  endwin();
#endif
/* just idle out until really closed. */
#ifdef GTK
 IF(isgui&&isgui!=3&&isagui){
  pe("all processing finished, awaiting exit request",0);
  isgui=2;
  W(isgui!=3)
   USLP(250000);
 }
#endif
#ifndef DISABLE_SYSLOG
 /* only if something happened, note this one. */
 IF(!(!slnum_s&&!slnum_t))
  wrso("%d-%d id: %lu-%u.%u.%u.%u exit: %s (%s)",slnum_t,slnum_s,cpid,GU(),
  GEU(),GG(),GEG(),(i?"abnormal":"normal"),(cpid==GPD()?"main":"branch"));
#endif
 /* check, and use the ns_exit() symbol, it passes the exit value, and exit */
 /* type(main/branch). (short, unsigned short) */
#ifndef DISABLE_MODULES
 IF(vmodule){
  (*exit_function)(i,type);
  /* added clean-up for the module file here, since it is going to exit. */
  dlclose(dyn);
 }
#endif
 /* clean up a possible build up with raw telnet on exit. */
 IF(tnetraw&&truetnet){
  nsprint("\n");
  /* same goes for logging. */
  IF(log)
   logdata("\n",3);
 }
 closesocket(0);
 closesocket(1);
 IF(type)
  _exit(i);
 E
  exit(i);
}
/* the following function is only for the ncurses mode. */
#ifdef NCURSES
V ncursesinit(V) {
 U I i=0;
 initscr();
 cbreak();
 nonl();
 keypad(stdscr,TRUE);
 /* reset columns to ncurses information. */
 columns=(COLS-1);
 /* no need for anything larger, unless you are trying to bother memory. */
 IF(1024<columns){
  pe("COLS is too large",0);
  columns=DFL_COLUMNS;
 }
 /* the prompt itself must be counted in the most minimal of cases. */
 IF((1>columns&&nofrom)||(4>columns&&!nofrom)){
  pe("COLS is too small",0);
  columns=DFL_COLUMNS;
 }
 /* setup tab labels. */
 F(i=0;i<COLS;i++)
  NWV(stdscr,((LINES-3-1)/2),i,'-');
 F(i=0;i<COLS;i++)
  NWV(stdscr,(LINES-3),i,'-');
 NMW(((LINES-3-1)/2),0,"[-<");
 NMW(((LINES-3-1)/2),3,(isncursesl?nclabel[0]:"Output stream"));
 NMW(((LINES-3-1)/2),(isncursesl?(SL(nclabel[0])+3):16),">");
 NMW(((LINES-3-1)/2),(COLS-1),"]");
 NMW((LINES-3),0,"[-<");
 NMW((LINES-3),3,(isncursesl>1?nclabel[1]:"Input stream"));
 NMW((LINES-3),(isncursesl>1?(SL(nclabel[1])+3):15),">");
 NMW((LINES-3),(COLS-1),"]");
 move((LINES-3+1),0);
 NR();
 /* make (ncurses) windows. */
 nw1=NSW(stdscr,((LINES-3-1)/2),COLS,0,0);
 NSO(nw1,1);
 nw2=NSW(stdscr,((LINES-3-1)/2),COLS,((LINES-3-1)/2)+1,0);
 NSO(nw2,1);
 nw3=NSW(stdscr,(3-1),COLS,((LINES-3)+1),0);
 NSO(nw3,1);
 touchwin(stdscr);
 NR();
 /* straighten up. */
 NE(nw1);
 NRE(nw1);
 NE(nw2);
 NRE(nw2);
 NE(nw3);
 NRE(nw3);
 /* nsprint() defaultly goes to the main (ncurses) window. */
 nfocus=nw3;
 /* ready to go. */
 isncurses=1;
 R;
}
#endif
/* the following functions are only for the gui mode. */
#ifdef GTK
/* function triggered when return is pressed for an entry. (gui mode) */
V gtkec(GW *gw,GW *ge){
 GC *et;
 et=gtk_entry_get_text(GTK_ENTRY(ge));
 IF(SL(et)&&isgui==1){
  IF(isguiwait){
   /* write the response to the socket. */
   wro(et,SL(et),0);
   wro("\n",1,0);
   /* log if requested. */
   IF(log)
    logdata(et,0);
   /* reset waiting loop. */
   isguiwait=0;
   gtk_entry_set_text(GTK_ENTRY(ge),"");
   /* only for non-generic gui. */
   IF(!isbgui){
    /* away you go. */
    GWH(gen);
    /* window refresh. */
    GWSU(giw,guiw,guih);
    /* refocus on the last button made, which is the exit button. */
    GWGF(gbu);
   }
  }
 }
 R;
}
/* function to clear the screen. (gui mode) */
V gtkcl(GW *gw,GPT gd){
 IF(gtk_text_get_length(GTK_TEXT(gte)))
  gtk_text_backward_delete(GTK_TEXT(gte),gtk_text_get_length(GTK_TEXT(gte)));
 R;
}
/* function to pull down the gui, using 300 as a general/default value. */
/* (non-generic gui mode) */
V gtkpd(GW *gw,GPT gd){
 IF(GTK_TOGGLE_BUTTON(gpd)->active==TRUE)
  guih+=(guio?guio:300);
 E
  guih-=(guio?guio:300);
 /* set the new position. */
 GWSU(giw,guiw,guih);
 R;
}
/* function that is triggered to close the program. (gui mode) */
V gtkca(GW *gw,GPT gd){
 gtk_main_quit();
 /* finalize the close. */
 isgui=3;
 /* just incase netscript is waiting anywheres else. */
 nsexit(0,0);
}
/* create, and launch the display. (gui mode) */
V gtkrun(C *dispname){
 C *disptitle;
 /* check for normal, non-generic font. */
 IF(!isbgui&&!(GFL(GUI_FONT)))
  pe("error loading font, use the -g option twice for generic mode",1);
 /* set up the soon-to-be titlebar. */
 IF(!(disptitle=(C *)M((isguititle?SL(guititle):SL(dispname))+SL(VERSION)+33+1)
 ))
  pe("gtkrun(): allocation of memory error",1);
 B(disptitle,((isguititle?SL(guititle):SL(dispname))+SL(VERSION)+33+1));
 SP(disptitle,"Netscript/v%s(%.3s)-Xvisualization: %s",VERSION,COMPTYPE,
 (isguititle?guititle:dispname));
 /* setup the window. */
 giw=gtk_window_new(GTK_WINDOW_TOPLEVEL);
 GWSU(giw,(isbgui?520:(guiw=440)),(isbgui?200:(guih=150)));
 /* defined to not be allowed to make too small, does not look so hot. */
 gtk_window_set_policy(GTK_WINDOW(giw),FALSE,(isbgui?TRUE:FALSE),FALSE);
 GSC(GTK_OBJECT(giw),"destroy",GTK_SIGNAL_FUNC(gtkca),0);
 gtk_window_set_title(GTK_WINDOW(giw),disptitle);
 /* done with title buffer. */
 FR(disptitle);
 GCSBW(GTK_CONTAINER(giw),0);
 gb1=GVN(FALSE,0);
 gtk_container_add(GTK_CONTAINER(giw),gb1);
 GWS(gb1);
 gb2=GVN(FALSE,0);
 GCSBW(GTK_CONTAINER(gb2),5);
 GBPS(GTK_BOX(gb1),gb2,TRUE,TRUE,0);
 GWS(gb2); 
 gta=gtk_table_new(2,2,FALSE);
 GBPS(GTK_BOX(gb2),gta,TRUE,TRUE,0);
 GWS(gta);
 /* text widget, vertical bar gets added on next. */
 gte=gtk_text_new(0,0);
 gtk_text_set_editable(GTK_TEXT(gte),FALSE);
 gtk_text_set_word_wrap(GTK_TEXT(gte),FALSE);
 GTA(GTK_TABLE(gta),gte,0,1,0,1,GTK_EXPAND|GTK_SHRINK|GTK_FILL,GTK_EXPAND|
 GTK_SHRINK|GTK_FILL,0,0);
 GWS(gte);
 /* vertical scrollbar, for the text widget. */
 gvs=gtk_vscrollbar_new(GTK_TEXT(gte)->vadj);
 GTA(GTK_TABLE(gta),gvs,1,2,0,1,GTK_FILL,GTK_EXPAND|GTK_SHRINK|GTK_FILL,0,0);
 GWS(gvs);
 gtk_widget_realize(gte);
 gtk_text_freeze(GTK_TEXT(gte));
 gtk_text_thaw(GTK_TEXT(gte));
 gvn=GVN(TRUE,0);
 GBPS(GTK_BOX(gb2),gvn,FALSE,FALSE,0);
 GWS(gvn);
 /* entry box. (do not show until needed, unless generic mode is on) */
 gen=gtk_entry_new_with_max_length(BASE_BUFFER);
 GSC(GTK_OBJECT(gen),"activate",GTK_SIGNAL_FUNC(gtkec),gen);
 GBPS(GTK_BOX(gvn),gen,FALSE,FALSE,0);
 IF(isbgui)
  GWS(gen);
 /* pulldown/up button/box. (only in non-generic gui mode) */
 IF(!isbgui){
  /* button box. */
  gpb=GHN(TRUE,0);
  GBPS(GTK_BOX(gvn),gpb,FALSE,FALSE,0);
  GWS(gpb);
  /* check button. */
  gpd=gtk_check_button_new_with_label(isguil>2?gblabel[2]:"Pulldown");
  GSC(GTK_OBJECT(gpd),"clicked",GTK_SIGNAL_FUNC(gtkpd),0);
  GBPS(GTK_BOX(gpb),gpd,TRUE,TRUE,0);
  GTK_WIDGET_SET_FLAGS(gpd,GTK_CAN_DEFAULT);
  GWS(gpd);
 }
 /* button box. */
 ghb=GHN(TRUE,0);
 GBPS((isbgui?GTK_BOX(gvn):GTK_BOX(gpb)),ghb,FALSE,TRUE,0);
 GWS(ghb);
 /* clear button. */
 gbu=gtk_button_new_with_label(isguil>1?gblabel[1]:"Clear");
 GSC(GTK_OBJECT(gbu),"clicked",GTK_SIGNAL_FUNC(gtkcl),0);
 GBPS(GTK_BOX(ghb),gbu,TRUE,TRUE,0);
 GTK_WIDGET_SET_FLAGS(gbu,GTK_CAN_DEFAULT);
 GWS(gbu);
 /* exit button. */
 gbu=gtk_button_new_with_label(isguil?gblabel[0]:"Exit");
 GSC(GTK_OBJECT(gbu),"clicked",GTK_SIGNAL_FUNC(gtkca),0);
 GBPS(GTK_BOX(ghb),gbu,TRUE,TRUE,0);
 GTK_WIDGET_SET_FLAGS(gbu,GTK_CAN_DEFAULT);
 GWS(gbu);
 /* focus on the entry/exit button. (depending if generic mode is on) */
 GWGF(isbgui?gen:gbu);
 /* pulldown on start. (-W option) */
 IF(!isbgui&&isguis)
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gpd),TRUE);
 /* show the window. */
 GWS(giw);
 /* allow output/display use. */
 isagui=1;
 /* update the gui. */
 GTE();
 gtk_main();
 GTL();
 /* netscript is done. */
 pthread_exit(0);
 R;
}
#endif
/* this function is to check for the reverse wildcard check.  if it does */
/* not occur it gets passed normally along to wordmatch(). */
U SH prewordmatch(C *wildcard,C *in){
 U SH r=0;
 U I i=0;
 C *tmpwildcard;
 IF(!(tmpwildcard=(C *)SDU(wildcard)))
  pe("prewordmatch(): duplication of memory error",1);
 /* check for the negative/reverse match rule. */
 IF(SL(tmpwildcard)>SL(NS_NMATCH)&&!SNCC(tmpwildcard,NS_NMATCH,SL(NS_NMATCH))){
  F(i=SL(NS_NMATCH);i<SL(tmpwildcard);i++)
   tmpwildcard[(i-SL(NS_NMATCH))]=tmpwildcard[i];
  tmpwildcard[(i-SL(NS_NMATCH))]=0x0;
  r=wordmatch(tmpwildcard,in);
  /* done with that. */
  FR(tmpwildcard);
  /* reverse it, the idea of this pre-function. */
  R(r?0:1);
 }
 /* otherwise, just like normal. */
 E
  R wordmatch(wildcard,in);
}
/* for the real wildcard match checking. */
U SH wordmatch(C *wildcard,C *in){
 U I i=0;
 U I j=0;
 U I k=0;
 U I r=0;
 U I tmp=0;
 C *tmpbuf;
 /* handles the dynamic variables. (${0-9}) */
 parsedynamic(wildcard);
 /* remove back-to-back spaces for parsing with the remote data. */
 F(j=0;j<SL(in);j++){
  IF(!(in[j+1]&&in[j]==0x20&&in[j+1]==0x20))
   in[k++]=in[j];
 }
 in[k]=0x0;
 /* remove back-to-back spaces for parsing with the ruleset. */
 F(k=j=0;j<SL(parseddynamic);j++)
  IF(!(parseddynamic[j+1]&&parseddynamic[j]==0x20&&parseddynamic[j+1]==0x20))
   parseddynamic[k++]=parseddynamic[j];
 parseddynamic[k]=0x0;
 /* quick exact match check. */
 IF(!SC(parseddynamic,in))
  R(0);
 W(!parameter(parseddynamic,i,0x20)){
  IF(!(tmpbuf=(C *)SDU(parm)))
   pe("wordmatch(): duplication of memory error",1);
  /* checks for a wildcard match for anything after that point.  before */
  /* checking the next parameter. */
  IF(SC(tmpbuf,NS_ALL)){
   /* gets the input parameter, to check for the rest of the wildcards, or */
   /* possible perfect matches.  if no more parameters, returns failure. */
   /* also increases the word for the next loop run. */
   IF(parameter(in,i++,0x20)){
    FR(tmpbuf);
    R(1);
   }
   /* checking for all wildcard word matches. (length wildcard is next) */
   IF(!SC(tmpbuf,parm)||!SCC(tmpbuf,NS_ANY)||(!SCC(tmpbuf,NS_ALPHA)&&wischeck(
   parm,0))||(!SCC(tmpbuf,NS_DIGIT)&&wischeck(parm,1))||(!SCC(tmpbuf,NS_ALNUM)
   &&wischeck(parm,2))||(!SCC(tmpbuf,NS_UPPER)&&wischeck(parm,3))||(!SCC(
   tmpbuf,NS_LOWER)&&wischeck(parm,4))||(!SCC(tmpbuf,NS_PUNCT)&&wischeck(parm,
   5))||(!SCC(tmpbuf,NS_CNTRL)&&wischeck(parm,6))||(!SCC(tmpbuf,NS_PRINT)&&
   wischeck(parm,7)))
    r=0;
   /* the length wildcard, must be four digits long, and then will be */
   /* processed accordingly. */
   EI(SL(tmpbuf)==5&&tmpbuf[0]==PARAMETER_VAR_CHAR&&ID((U I)tmpbuf[1])&&
   ID((U I)tmpbuf[2])&&ID((U I)tmpbuf[3])&&ID((U I)tmpbuf[4])){
    tmp=(((tmpbuf[1]-0x30)*1000)+((tmpbuf[2]-0x30)*100)+((tmpbuf[3]-0x30)*10)+
    (tmpbuf[4]-0x30));
    /* match. */
    IF(SL(parm)==tmp)
     r=0;
    /* did not match. */
    E
     r=1;
   }
   E
    r=1;
   IF(r){
    FR(tmpbuf);
    R(r);
   }
  }
  E{
   FR(tmpbuf);
   R(r);
  }
  FR(tmpbuf);
 }
 /* leftover data does not count as a match.  just using int i again for the */
 /* loop.  checks to see if uneven arguments.  1 = failed, 0 = success. */
 i=0;
 W(!parameter(parseddynamic,i,0x20))
  i++;
 IF(!parameter(in,i,0x20))
  /* un-even. */
  r=1;
 E
  /* even. */
  r=0;
 R(r);
}
/* this is meant to pluck a word out of a sentence, then duplicated it to */
/* the character string "parm".  kind of like a strtok() without tokenizing. */
U SH parameter(C *string,I i,U I sep){
 U I j=0;
 U I k=0;
 U I r=0;
 /* no comparison. */
 C *buf;
 IF(!(buf=(C *)M(SL(string)+1)))
  pe("parameter(): allocation of memory error",1);
 B(buf,(SL(string)+1));
 IF(i<0)
  r=1;
 E
  F(j=0;j<SL(string);j++){
   IF(string[j]==sep)
    i--;
   EI(!i)
    buf[k++]=string[j];
   IF(string[j]==0x0A||string[j]==0x0)
    j=(SL(string)+1);
  }
 IF(i>0)
  r=1;
 FR(parm);
 IF(!(parm=(C *)SDU(buf)))
  pe("parameter(): duplication of memory error",1);
 FR(buf);
 R(r);
}
/* this function checks word format of their counterpart, instead of a */
/* single character.  for some reason i like using strlen() instead of *buf */
/* to check for use, old habit i suppose. */
U SH wischeck(C *word,U I type){
 U I i=0;
 IF(!SL(word))
  R(0);
 F(i=0;i<SL(word);i++)
  IF((!IL((U I)word[i])&&type==0)||(!ID((U I)word[i])&&type==1)||(!isalnum(
  (U I)word[i])&&type==2)||(!isupper((U I)word[i])&&type==3)||(!islower((U I)
  word[i])&&type==4)||(!ispunct((U I)word[i])&&type==5)||(!iscntrl((U I)
  word[i])&&type==6)||(!IP((U I)word[i])&&type==7))
   R(0);
 R(1);
}
/* this function handles the --list command line argument. */
U SH usefilelist(C *path){
 U SH r=0;
 U I i=0;
 C *tmppath;
 C lread[(BASE_BUFFER*4+1)];
 FL *lfd;
 glob_t gb;
 gb.gl_offs=0;
 F(i=0;!parameter(path,i,0x3A);i++){
  IF(SL(parm)){
   IF(!(tmppath=(C *)M(SL(parm)+3)))
    pe("usefilelist(): allocation of memory error",1);
   B(tmppath,(SL(parm)+3));
   SP(tmppath,"%s/*",parm);
   GB(tmppath,(i==0?GLOB_DOOFFS:(GLOB_DOOFFS|GLOB_APPEND)),0,&gb);
   FR(tmppath);
  }
 }
 F(i=0;(i<gb.gl_pathc&&i<1000);i++)
  nsprint("%.3d\t%.64s%s\n",i,gb.gl_pathv[i],(SL(gb.gl_pathv[i])>64?"...":""));
 IF(!i){
  pe("no files found to list",1);
  r=0;
 }
 E{
  nsprint("enter list value to use(0-%d): ",(i-1));
  IF(!(lfd=FO(ttyn,"r")))
   pe("could not open standard input for read",1);
  E{
   B(lread,(BASE_BUFFER*4+1));
   IF(FS(lread,(SO(lread)-1),lfd))
    F(i=0;i<SL(lread);i++)
     IF(lread[i]==0x0A)
      lread[i]=0x0;
  }
  FC(lfd);
  IF(!SL(lread)||!wischeck(lread,1))
   r=0;
  E{
   IF(gb.gl_pathc<=AI(lread)||0>AI(lread))
    r=0;
   E{
    IF(!(tofile=(C *)SDU(gb.gl_pathv[AI(lread)])))
     pe("usefilelist(): duplication of memory error",1);
    r=1;
   }
  }
 }
 GBF(&gb);
 R(r);
}
/* this function is for using the stored history file(~/.nshistory) to */
/* quickly activate netscript.  (most) output stderr, for this function. */
U SH usehistory(C *preread,U SH type){
 U I i=0;
 U I j=0;
 U I k=0;
 U C hread[(BASE_BUFFER*4+1)];
 FL *hfd;
 IF(!type){
  IF(!(hfd=FO(histfile,"r")))
   pe("history file does not appear to exist, or is not readable",1);
  E{
   B(hread,(BASE_BUFFER*4+1));
   W(FS(hread,(SO(hread)-1),hfd)&&j<1000){
    F(i=0;i<SL(hread);i++)
     IF(hread[i]==0x0A)
      hread[i]=0x0;
    nsprint("%.3d\t%.64s%s\n",j++,hread,(SL(hread)>64?"...":""));
   }
  }
  FC(hfd);
  IF(j==0)
   pe("history file does not contain any data",1);
  nsprint("enter history value to use(0-%d/clear): ",(j-1));
  IF(!(hfd=FO(ttyn,"r")))
   pe("could not open standard input for read",1);
  E{
   B(hread,(BASE_BUFFER*4+1));
   IF(FS(hread,(SO(hread)-1),hfd))
    F(i=0;i<SL(hread);i++)
     IF(hread[i]==0x0A)
      hread[i]=0x0;
  }
  FC(hfd);
  IF(!SNC(hread,"clear",5))
   nsexit(delhistory(histfile),0);
  IF(!SL(hread)||!wischeck(hread,1))
   R(0);
  k=AI(hread);
 }
 E{
  IF(!SL(preread)||!wischeck(preread,1))
   R(0);
  k=AI(preread);
 }
 j=0;
 IF(!(hfd=FO(histfile,"r")))
  pe("history file does not appear to exist, or is not readable",1);
 E{
  B(hread,(BASE_BUFFER*4+1));
  W(FS(hread,(SO(hread)-1),hfd)){
   IF(j==k){
    F(i=0;i<SL(hread);i++)
     IF(hread[i]==0x0A)
      hread[i]=0x0;
    IF(!(toenv=(C *)M(SL(hread)+1)))
     pe("usehistory(): allocation of memory error",1);
    B(toenv,(SL(hread)+1));
    /* memory compatibility issue.  so, instead, allocate+copy. */
    SCP(toenv,hread);
    FC(hfd);
    R(1);
   }
   E
    j++;
  }
 }
 FC(hfd);
 R(0);
}
/* funcation to delete the history file. */
U SH delhistory(C *file){
 /* check for existence, and write access. */
 IF(AC(file,F_OK)&&AC(file,W_OK)){
  pe("could not access the history file to delete",0);
  R(1);
 }
 /* no errors above, attempt to delete. */
 EI(UN(file)){
  pe("could not delete the history file",0);
  R(1);
 }
 /* deleted without a problem. */
 pe("successfully removed the history file",0);
 R(0);
}
/* gets arguments out of +x files, if no arguments it will return false, and */
/* not continue. */
U SH getexecargs(C *ruleset){
 U I i=0;
 U I j=0;
 /* if it is bigger than that someone is just trying to be evil. */
 U C rread[(BASE_BUFFER*4+1)];
 FL *fd;
 IF(!(fd=FO(ruleset,"r")))
  pe("ruleset file does not appear to exist, or is not readable",1);
 E{
  B(rread,(BASE_BUFFER*4+1));
  /* only need to read the first two lines for the arguments. */
  W(FS(rread,(SO(rread)-1),fd)&&j<2){
   IF(SL(rread)>2&&!SNC(rread,"#?",2)){
    /* ?(args) + -2("#?") + ?(ruleset) + 4(-r,space,space) + 1(extra). */
    IF(!(toenv=(C *)M(SL(rread)-2+SL(ruleset)+4+1)))
     pe("getexecargs(): allocation of memory error",1);
    B(toenv,(SL(rread)-2+SL(ruleset)+4+1));
    SP(toenv,"-r %s ",ruleset);
    F(i=2;i<SL(rread);i++){
     /* finish true, and append 0x0. */
     IF(rread[i]==0x0A){
      toenv[SL(toenv)]=0x0;
      FC(fd);
      R(1);
     }
     toenv[SL(toenv)]=rread[i];
    }
   }
   j++;
  }
 }
 FC(fd);
 R(0);
}
/* function to read /etc/services in conjunction with the -p option to */
/* support services in the form of a string.  for example: "finger". */
/* port should really be a short instead of an int.  but, i wanted error */
/* checking.  so, i used int. */
U I portentry(C *service){
 S servent *se;
 IF(!(se=GS(service,(isudp?"udp":"tcp"))))
  R(AI(service));
 E
  /* convert back from the network byte order that the function does. */
  R(NHS(se->s_port));
 /* will never really make it here. */
 R(0);
}
/* program initializes here.  gets things started. */
I main(I ac,C **av){
 U SH sethist=0;
 U SH setlist=0;
 U I i=0;
 U I j=0;
 U I k=0;
 U I l=0;
 U I m=0;
 U I n=0;
 /* the pid of the background process. */
 PT bgpid=0;
 C *avo;
 C *envcmd;
 C *histline;
 /* threads are only needed for the gui. */
#ifdef GTK
 pthread_t pt;
#endif
 /* set the static pid value. (for display) */
 cpid=GPD();
 /* set the program name.  this is kind of like rindex()/strrchr().  but, */
 /* slightly different. */
 F(i=SL(av[0]);(av[0][i]!=0x2F&&i>0);i--)
  j++;
 IF(i<=0){
  IF(!(progname=(C *)SDU(av[0])))
   pe("main(): duplication of memory error",1);
 }
 E{
  /* do not add extra zero'd byte, it is already there at this point. */
  IF(!(progname=(C *)M(j)))
   pe("main(): allocation of memory error",1);
  B(progname,j);
  F(i=SL(av[0]);j;i--){
   j--;
   progname[j]=av[0][i];
  }
 }
 /* ctrl-c. */
 SG(SIGINT,sighandler);
 /* segmentation fault, hope not to see that. */
 SG(SIGSEGV,sighandler);
 /* kill, or terminate signal. */
 SG(SIGTERM,sighandler);
 /* used for timed killing of netscript. */
 SG(SIGALRM,sighandler);
 /* other signals to catch, for clean up. (not defined in the handler) */
 SG(SIGILL,sighandler);
 SG(SIGTRAP,sighandler);
 SG(SIGBUS,sighandler);
 SG(SIGQUIT,sighandler);
 /* ignored for binding(with -I).  do not want to exit due to it. */
 SG(SIGPIPE,SIG_IGN);
 /* make global non-other, and non-group permissions for files made by */
 /* netscript. */
 UM(077);
 /* using this method for global knowledge that if netscript was started */
 /* privileged, or not.  any other way the -u option could change the */
 /* effects of checking.  since this gets processed first, it will know. */
 /* just in the rare case netscript would need to be set*id.  disables */
 /* modules, file reading/writing, and third party execution support.  if */
 /* started set*id. (or could use stored values for later checking.  but, i */
 /* like this method) */
 IF(GU()!=GEU()||GG()!=GEG())
  isprivileged=1;
 E
  isprivileged=0;
 /* set*id = sete*id.  (mainly for proper permission changes) */
 SUD(GEU());
 SGD(GEG());
 /* write to the array of the defined options. (when compiled) */
 setdefined();
 /* only time this should be used. */
 IF(!isprivileged){
  setrc(RCFILE);
  sethistory(HISTFILE);
 }
 /* take care of the modules environmental variable first. */
#ifndef DISABLE_MODULES
 IF(G(ENV_MODULE)){
  IF(isprivileged)
   pe("environmental variable module file ignored with privileged access",0);
  E
   modhandler((C *)G(ENV_MODULE));
 }
 /* check, and use the ns_init() symbol, it passes the number of command */
 /* line arguments, and the arguments. (int, char **) */
 IF(vmodule)
  (*init_function)(ac,av);
#endif
 /* set the shell for executing third party programs.  make sure it is */
 /* executable. */
 IF(G(ENV_SHELL)&&!AC((C *)G(ENV_SHELL),X_OK)){
  IF(!(eshell=(C *)SDU((C *)G(ENV_SHELL))))
   pe("main(): duplication of memory error",1);
 }
 /* if all else fails.  set default, and hope. */
 E{
  IF(!(eshell=(C *)SDU(SHPATH)))
   pe("main(): duplication of memory error",1);
 }
 /* set up the display name. */
 setdname();
 /* set up the tty name. */
 IF(!AC(TN(0),R_OK)){
  IF(!(ttyn=(C *)SDU(TN(0))))
   pe("main(): duplication of memory error",1);
 }
 E{
  IF(!(ttyn=(C *)SDU(INPUTPATH)))
   pe("main(): duplication of memory error",1);
 }
 /* made sure the usage is seen. */
 IF(ac>=2&&!SNC(av[1],"--help",6))
  usage(1);
 /* generalized support for the --hist[ory] option to read ~/.nshistory. */
 IF(ac>=2&&!SNC(av[1],"--hist",6)){
  IF(isprivileged)
   pe("can not use \"--hist\" while in privileged mode",1);
  E{
   IF(ac>=3&&!SNC(av[2],"clear",5))
    nsexit(delhistory(histfile),0);
   E{
    k=usehistory((ac>=3?av[2]:0),(ac>=3?1:0));
    IF(!k)
     pe("invalid history value",1);
    E
     sethist=1;
   }
  }
 }
 IF(G(ENV_PATH)||(ac>=2&&!SNC(av[1],"--list",6))){
  IF(isprivileged)
   pe("can not use \"--list\" while in privileged mode",1);
  E{
   /* handle the list, environmental variable overrides command line. */
   setlist=usefilelist(G(ENV_PATH)?(C *)G(ENV_PATH):(ac>=3?av[2]:"."));
   IF(!setlist)
    pe("invalid list value",1);
  }
 }
 /* +x ruleset file support, generalized support. (or modified) */
 IF(((av[1]&&!av[2])||setlist)&&!AC((setlist?tofile:av[1]),R_OK)&&!AC((setlist?
 tofile:av[1]),X_OK))
  k=getexecargs(setlist?tofile:av[1]);
 /* last use of --list, then everything is passed over. */
 IF(setlist){
  FR(tofile);
  IF(!k)
   pe("not a valid (internal argument supplied) netscript ruleset",1);
 }
 /* check the environment for the command line override variable.  or, take */
 /* +x file arguments as env controller, all in one deal. */
 IF(G(ENV_CMDLINE)||k){
  /* priority 1 is +x files, priority 2 is env var, and priority 3 is the */
  /* normal command line. */
  IF(k){
   IF(!(envcmd=(C *)M(SL(toenv)+SL(av[0])+2)))
    pe("main(): allocation of memory error",1);
   B(envcmd,(SL(toenv)+SL(av[0])+2));
  }
  E{
   IF(!(envcmd=(C *)M(SL((C *)G(ENV_CMDLINE))+SL(av[0])+2)))
    pe("main(): allocation of memory error",1);
   B(envcmd,(SL((C *)G(ENV_CMDLINE))+SL(av[0])+2));
  }
  IF(k){
   SP(envcmd,"%s %s",av[0],toenv);
   /* done with that buffer. */
   FR(toenv);
  }
  E
   SP(envcmd,"%s %s",av[0],(C *)G(ENV_CMDLINE));
  /* used this method to make a fresh argument list. */
  /* finding out how many elements are needed.  then, nulling the loop */
  /* routine since it was all done in the process itself. */
  F(ac=0;!parameter(envcmd,ac,0x20);ac++);
  /* creates the argument array.  also, added an extra slot for argv[0], */
  /* which will remain unused.  but, getopt() expects it. */
  IF(!(av=(C **)M((ac+1)*(SO(C *)))))
   pe("main(): allocation of memory error",1);
  /* fill the array with the data from the env var, or +x file. */
  /* (ENV_CMDLINE|+x) */
  F(ac=0;!parameter(envcmd,ac,0x20);ac++){
   IF(!(av[ac]=(C *)SDU(parm)))
    pe("main(): duplication of memory error",1);
  }
  FR(envcmd);
 }
 /* add the -g option automatically, if it is linked by "gnetscript". */
#ifdef GTK
 IF((!SC(((C *)RI(progname,0x2F)?(C *)RI(progname,0x2F):"0"),"/gnetscript")||
 !strcmp(progname,"gnetscript"))&&!isprivileged)
  isgui=1;
#endif
 /* add the -_ option automatically, if it is linked by "nnetscript". */
#ifdef NCURSES
 IF((!SC(((C *)RI(progname,0x2F)?(C *)RI(progname,0x2F):"0"),"/nnetscript")||
 !strcmp(progname,"nnetscript")))
  isncursesa=1;
#endif
 IF(ac>1&&SNC(av[1],"--interactive",13)){
  /* set up, and build the (get) option list. */
  n=89;
#ifdef GTK
  n+=10;
#endif
#ifdef NCURSES
  n+=3;
#endif
#ifndef DISABLE_SYSLOG 
  n+=1;
#endif
  IF(!(avo=(C *)M(n+1)))
   pe("main(): allocation of memory error",1);
  B(avo,(n+1));
  SCP(avo,"r:h:p:0:1:2:3:4:5:6:7:8:9:i:m:M:R:k:u:q:o:S:U:t:Q:l:e:E:O:x:Xaf@^Bn"
  "NjJFIsbTyYzLHDwPcCAdvV");
#ifdef GTK
  SCA(avo,"G:+:#:K:gW");
#endif
#ifdef NCURSES
  SCA(avo,"_=:");
#endif
#ifndef DISABLE_SYSLOG 
  SCA(avo,"Z");
#endif
  /* interpret out the arguments supplied. */
  /* i changed a method here, due to me using shorts for most of these */
  /* options.  i did not want a possible loop over bug to occur if someone */
  /* was trying to break netscript with repeated arguments. (65536) */
  W((i=getopt(ac,av,avo))!=EOF){
   SW(i){
    /* does not matter if a user has privileged access, and loads a ruleset. */
    /* because, it never gets relayed verbose. (unless someone knows a rule, */
    /* or is traced back/memory) no major concern. */
    CS 'r':
     /* can not have both. */
     IF(!inputrules&&!setfile){
      /* does both parsedynamic(), and parsecharvars().  gets used often. */
      parsecmdline(optarg);
      IF(!(rulesfile=(C *)SDU(parsedline)))
       pe("main(): duplication of memory error",1);
      setfile=1;
     }
     BR;
    CS 'h':
     IF(!bindmode&&!sethost){
      W(!parameter(optarg,tshs,0x2C)){
       parsecmdline(parm);
       IF(!(shost[tshs++]=(C *)SDU(SL(parsedline)?parsedline:"0")))
        pe("main(): duplication of memory error",1);
       IF(tshs>=MAX_ARGS)
        pe("too many slots to store in memory",1);
      }
      sethost=1;
     }
     BR;
    CS 'p':
     IF(!sport)
      sport=portentry(optarg);
     BR;
    /* not very pretty.  but, it will do.  all of these will be handled the */
    /* same way.  the statically set dynamic variable options. */
    CS '0':
    CS '1':
    CS '2':
    CS '3':
    CS '4':
    CS '5':
    CS '6':
    CS '7':
    CS '8':
    CS '9':
     /* do not reset the variable, could cause memory problems. */
     IF(dynamicvarset[(i-0x30)]!=2){
      parsecmdline(optarg);
      IF(!(dynamicvar[(i-0x30)]=(C *)SDU(parsedline)))
       pe("main(): duplication of memory error",1);
      /* define it not to be changed, or reset. */
      dynamicvarset[(i-0x30)]=2;
     }
     BR;
    CS 'i':
     IF(!initsend){
      parsecmdline(optarg);
      IF(!(iswrite=(C *)SDU(parsedline)))
       pe("main(): duplication of memory error",1);
      initsend=1;
     }
     BR;
    CS 'm':
     IF(!setcdir){
      parsecmdline(optarg);
      IF(!(cdir=(C *)SDU(parsedline)))
       pe("main(): duplication of memory error",1);
      setcdir=1;
     }
     BR;
    CS 'M':
     IF(!setrdir){
      parsecmdline(optarg);
      IF(!(rdir=(C *)SDU(parsedline)))
       pe("main(): duplication of memory error",1);
      setrdir=1;
     }
     BR;
    CS 'R':
     IF(!setroute){
      parsecmdline(optarg);
      IF(!(rhost=(C *)SDU(parsedline)))
       pe("main(): duplication of memory error",1);
      setroute=1;
     }
     BR;
    CS 'k':
     IF(AI(optarg)>0&&!rport)
      rport=AI(optarg);
     BR;
    CS 'u':
     IF(!setperms){
      setpermissions(optarg,0);
      setperms=1;
     }
     BR;
    CS 'q':
     IF(!isvhost){
      IF(!(vhost=(C *)SDU(optarg)))
       pe("main(): duplication of memory error",1);
      isvhost=1;
     }
     BR;
    CS 'o':
     IF(!omitchars){
      parsecmdline(optarg);
      IF(!(ochars=(C *)SDU(parsedline)))
       pe("main(): duplication of memory error",1);
      omitchars=1;
     }
     BR;
    CS 'S':
     IF(AI(optarg)>0&&!sdelay)
      sdelay=AI(optarg);
     BR;
    CS 'U':
     IF(AL(optarg)>0&&!sudelay)
      sudelay=AL(optarg);
     BR;
    CS 't':
     IF(AI(optarg)>0&&!alrm)
      alrm=AI(optarg);
     BR;
    CS 'Q':
     IF(!soptions){
      parsecmdline(optarg);
      IF(!(sopts=(C *)SDU(parsedline)))
       pe("main(): duplication of memory error",1);
      soptions=1;
     }
     BR;
    CS 'l':
     IF(isprivileged)
      pe("can not log data to a file with privileged access",0);
     E
      IF(!log){
       parsecmdline(optarg);
       IF(!(logfile=(C *)SDU(parsedline)))
        pe("main(): duplication of memory error",1);
       log=1;
      }
     BR;
#ifdef GTK
    CS 'G':
     IF(!isguititle){
      parsecmdline(optarg);
      IF(!(guititle=(C *)SDU(parsedline)))
       pe("main(): duplication of memory error",1);
      isguititle=1;
     }
     BR;
    CS '+':
     IF(AI(optarg)>0&&!guio)
      guio=(AI(optarg)>GUI_MAXLEN?GUI_MAXLEN:AI(optarg));
     BR;
    CS '#':
     IF(!isguic){
      parsecmdline(optarg);
      IF(SL(parsedline)==6){
       B((V *)guic,SO(guic));
       F(isguic=i=0;i<6;i+=2){
        IF(IL((U I)parsedline[i]))
         j=(TL(parsedline[i])-0x56);
        E
         j=(parsedline[i]-0x2F);
        j*=16;
        IF(IL((U I)parsedline[i+1]))
         j+=(TL(parsedline[i+1])-0x56);
        E
         j+=(parsedline[i+1]-0x2F);
        j-=17;
        IF(j>=0&&j<256)
         guic[isguic]=(j*257);
        isguic++;
       }
      }
     }
     BR;
    CS 'K':
     IF(!isguil){
      parsecmdline(optarg);
      W(!parameter(parsedline,isguil,0x3A)&&isguil<3){
       IF(!(gblabel[isguil++]=(C *)SDU(SL(parm)?parm:"nolabel")))
        pe("main(): duplication of memory error",1);
      }
     }
     BR;
#endif
    CS 'e':
     IF(isprivileged)
      pe("third party programs can not be executed with privileged access",0);
     E
      IF(!runcmd){
       parsecmdline(optarg);
       IF(!(execfile=(C *)SDU(parsedline)))
        pe("main(): duplication of memory error",1);
       runcmd=1;
      }
     BR;
    CS 'E':
     IF(!runpre){
      parsecmdline(optarg);
      IF(!(execpre=(C *)SDU(parsedline)))
       pe("main(): duplication of memory error",1);
      runpre=1;
     }
     BR;
    CS 'O':
     IF(!setshell){
      parsecmdline(optarg);
      /* make sure it is executable. */
      IF(!AC(parsedline,X_OK)){
       /* was set by the environment, or default earlier.  free it. */
       FR(eshell);
       IF(!(eshell=(C *)SDU(parsedline)))
        pe("main(): duplication of memory error",1);
       setshell=1;
      }
     }
     BR;
    CS 'x':
     IF(AI(optarg)>0&&!xalrm)
      xalrm=AI(optarg);
     BR;
    CS 'X':
     runexit=1;
     BR;
    CS 'a':
     isiexec=1;
     BR;
    CS 'f':
     editrules=1;
     BR;
    CS '@':
     isudp=1;
     BR;
    CS '^':
     isudpr=1;
     BR;
    CS 'B':
     isbg=1;
     BR;
    CS 'n':
     nossend=1;
     BR;
    CS 'N':
     nosrecv=1;
     BR;
    CS 'j':
     norsend=1;
     BR;
    CS 'J':
     norrecv=1;
     BR;
    CS 'F':
     nofrom=1;
     BR;
    CS 'I':
     forever=1;
     BR;
    CS 's':
     /* can not have both. */
     IF(!setfile)
      inputrules=1;
     BR;
    CS 'b':
     /* can not have both. */
     IF(!sethost)
      bindmode=1;
     BR;
#ifdef GTK
    CS 'g':
     IF(!isgui){
      IF(!isprivileged)
       isgui=1;
      E
       pe("the gui frontend can not be used when netscript is privileged",0);
     }
     E
      isbgui=1;
     BR;
    CS 'W':
     isguis=1;
     BR;
#endif
#ifdef NCURSES
    CS '_':
     isncursesa=1;
     BR;
    CS '=':
     IF(!isncursesl){
      parsecmdline(optarg);
      W(!parameter(parsedline,isncursesl,0x3A)&&isncursesl<2){
       IF(!(nclabel[isncursesl++]=(C *)SDU(SL(parm)?parm:"nolabel")))
        pe("main(): duplication of memory error",1);
      }
     }
#endif
#ifndef DISABLE_SYSLOG
    CS 'Z':
     slog=1;
     BR;
#endif
    CS 'T':
     tnet=1;
     BR;
    CS 'y':
     tnetraw=1;
     BR;
    CS 'Y':
     tnetraw=2;
     BR;
    CS 'z':
     notnetopt=1;
     BR;
    CS 'L':
     /* set option, and force the no wrapping option. (-w option) */
     lnum=lnum_i=lnum_o=nowrap=1;
     BR;
    CS 'H':
     /* 0 = character, 1 = hex, 2 = decimal. */
     displaytype=1;
     BR;
    CS 'D':
     /* 0 = character, 1 = hex, 2 = decimal. */
     displaytype=2;
     BR;
    CS 'w':
     nowrap=1;
     BR;
    CS 'P':
     printonly=1;
     BR;
    CS 'c':
     noshowc=1;
     BR;
    CS 'C':
     noshowp=1;
     BR;
    CS 'A':
     noshowa=1;
     BR;
    CS 'd':
     displayv();
     BR;
    CS 'v':
     showinfo(1);
     nsexit(0,0);
     BR;
    CS 'V':
     showv=2;
     BR;
    default:
     usage(0);
     BR;
   }
  }
  /* done with buffer forever. */
  FR(avo);
 }
 E
  iface(IFACE_PREFIX);
 /* if chroot, handle first. */
 IF(setrdir){
  CR(rdir);
  FR(rdir);
 }
 /* if chdir, handle second. */
 IF(setcdir){
  CD(cdir);
  FR(cdir);
 }
 /* check for -r|s|f, -h|b, and -p(int->short style) options. */
 IF((!setfile&&!inputrules&&!editrules)||(!sethost&&!bindmode)||(1>sport||
 65535<sport)){
  pe("no ruleset option, host|bind option, and/or port option defined",0);
  IF(ac>1)
   usage(1);
  E
   nsexit(0,0);
 }
 /* default if invalid, or non-existent. */
 IF(1>rport||65535<rport)
  rport=sport;
 /* ruleset editor. (-f option) */
 IF(editrules&&!inputrules){
  IF(setfile)
   editrules=2;
  ruleedit(setfile,(setfile?rulesfile:0));
 }
 /* make, and define the ruleset. */
 makelists(rulesfile);
 /* remove temporary ruleset, only if a temporary file. (no verbose) */
 IF(editrules==1)
  UN(rulesfile);
 /* if gui mode is on, disable certain option(s). */
#ifdef GTK
 /* can not do both, at least making sense. */
 IF(isbg)
  isgui=0;
 EI(isgui&&tnetraw){
  tnetraw=0;
  tnet=1;
 }
#endif
 /* if one of the telnet fix options is supplied, fix conflicting option(s). */
 /* also, forces telnet mode. (-y, and -Y options) */
 IF(tnetraw)
  tnet=noshowa=nossend=nosrecv=1;
#ifdef GTK
 IF(isgui){
  g_thread_init(0);
  /* gtk initialization, passes arguments for gtk to handle. */
  gtk_init(&ac,&av);
  /* get things going.  if it fails for some reason, exit. */
  IF(pthread_create(&pt,0,(V *)*gtkrun,(inputrules?"(standard input)":rulesfile
  )))
   pe("could not create pthread, for the gui",1);
  /* time to catch up. */
  W(!isagui)
   USLP(250000);
 }
#endif
 /* main argument checking done, continuing on. */
 IF(showv)
  showinfo(showv);
 /* check for non-dfl columns, for the pd() display function with wordwrap. */
 /* do this before using the pd() function at all. */
 IF(G(ENV_COLUMNS)){
  columns=AI((C *)G(ENV_COLUMNS));
  /* no need for anything larger, unless you are trying to bother memory. */
  IF(1024<columns){
   pe("ENV_COLUMNS environmental variable is too large",0);
   columns=DFL_COLUMNS;
  }
  /* the prompt itself must be counted in the most minimal of cases. */
  IF((1>columns&&nofrom)||(4>columns&&!nofrom)){
   pe("ENV_COLUMNS environmental variable is too small",0);
   columns=DFL_COLUMNS;
  }
 }
 E
  columns=DFL_COLUMNS;
 /* same idea as above.  except a different kind of checking. */
 IF(G(ENV_BACKLOG)){
  blog=AI((C *)G(ENV_BACKLOG));
  IF(128<blog){
   pe("ENV_BACKLOG environmental variable is too large",0);
   blog=DFL_BACKLOG;
  }
  IF(1>blog){
   pe("ENV_BACKLOG environmental variable is too small",0);
   blog=DFL_BACKLOG;
  }
 }
 E
  blog=DFL_BACKLOG;
 /* command line is a priority. */
 IF(!isvhost&&G(ENV_VHOST)){
  IF(!(vhost=(C *)SDU((C *)G(ENV_VHOST))))
   pe("main(): duplication of memory error",1);
  isvhost=1;
 }
 /* there would atleast have been one argument to have made it this far. */
 F(j=1;j<ac;j++)
  /* +1 for the space. (CR) */
  m+=(SL(av[j])+1);
 IF(!(histline=(C *)M(m+1)))
  pe("main(): allocation of memory error",1);
 B(histline,(m+1));
 F(j=1;j<ac;j++){
  SCA(histline,av[j]);
  IF((j+1)!=ac)
   SCA(histline," ");
 }
#ifndef DISABLE_SYSLOG
 wrso("%d-%d id: %lu-%u.%u.%u.%u init: %s",slnum_t,(slnum_s++),cpid,GU(),GEU(),
 GG(),GEG(),(SL(histline)?histline:"<no command line>"));
#endif
 /* no need to re-write history. (sethist) */
 IF(!isprivileged&&!sethist&&SL(histline))
  addhistory(histline);
 /* done with this, forever. */
 FR(histline);
 SW(isbg?(bgpid=FK()):0){
  CS -1:
   pe("could not fork into the background",1);
   BR;
  CS 0:
   /* only if really backgrounding. */
   IF(isbg){
    /* no more output now.  close up shop, just to make sure nothing is */
    /* outputed. */
    CL(0);
    CL(1);
    CL(2);
    isbga=1;
   }
   /* initialize ncurses, after everything is ready to go. (compiled option) */
#ifdef NCURSES 
   IF(!(!isncursesa||isbg||tnetraw))
#ifdef GTK
   IF(!isgui)
#endif
    ncursesinit();
#endif
   /* infinite connecting/binding. (-I option) */
   IF(forever){
    W(1){
     /* either stops after all those hosts, to get reset.  or, stays binded */
     /* forever. */
     W(l<tshs||bindmode){
      parsesocket(bindmode?0:shost[l++],sport);
#ifndef DISABLE_SYSLOG
      /* should only log closings, if is a multi-remote run.  otherwise, it */
      /* would show the static close, and exit everytime.  a waste of space. */
      wrso("%d-%d id: %lu-%u.%u.%u.%u closed: %d",slnum_t,slnum_s,cpid,GU(),
      GEU(),GG(),GEG(),slnum_t);
      /* reset system log counter. */
      slnum_s=0;
      /* +1 the total. */
      slnum_t++;
#endif
      /* better safe than sorry, do not want fds to fill up. */
      closesocket(0);
      SLP(1);
     }
     l=0;
    }
   }
   E{
    W(l<tshs||bindmode){
     parsesocket(bindmode?0:shost[l++],sport);
     /* turn bindmode off to leave this loop, and exit. (if bindmode) */
     bindmode=0;
     /* better safe than sorry, do not want fds to fill up. */
     closesocket(0);
     SLP(1);
    }
   }
   /* for backgrounding only. */
   IF(isbga)
    nsexit(0,1);
   BR;
  default:
   nsprint("PID: [%lu@%s]\n",bgpid,nname);
   BR;
 }
 nsexit(0,0);
 /* never will actually make it to this point.  but, will shut up warnings. */
 exit(0);
}
/* EOF */