فهرست منبع

* Fixed a few bugs in cmd_help
* Added (flag) to 'help cmd'
* Added makehelp for converting eggdrop style help file (misc/help.txt)
to a usable help.h
* Added sorthelp to sort help.txt
* Added S_GARBLEHELP for garbling help strings


svn: 406

Bryan Drewery 22 سال پیش
والد
کامیت
997ec2dc29
9فایلهای تغییر یافته به همراه1020 افزوده شده و 92 حذف شده
  1. 27 6
      Makefile.in
  2. 536 0
      misc/help.txt
  3. 2 0
      pack/conf.h
  4. 13 4
      src/Makefile.in
  5. 12 7
      src/cmds.c
  6. 0 72
      src/help.h
  7. 206 0
      src/makehelp.c
  8. 210 0
      src/sorthelp.c
  9. 14 3
      src/stringfix.c

+ 27 - 6
Makefile.in

@@ -70,11 +70,11 @@ MAKE_CONFIG = $(MAKE) 'MAKE=$(MAKE)'
 
 all:
 	@echo ""
-	@echo "Use the build script doofus."
+	@echo "Use the build script."
 	@echo ""
 
 cleanutils:
-	@rm -f src/stringfix src/makesettings src/makesalt
+	@rm -f src/stringfix src/makesettings src/makesalt src/sorthelp src/makehelp
 	@misc/dostrings l
 
 clean:  cleanutils
@@ -82,7 +82,7 @@ clean:  cleanutils
 	+@cd src/compat && $(MAKE) clean
 	+@cd src/mod && $(MAKE) distclean
 	@rm -f $(HUBEXEC) $(LEAFEXEC) *.stamp core DEBUG *~ src/*~ key configure.temp .mangled
-	@rm -f $(HUBEXEC).* $(LEAFEXEC).* src/settings.c src/salt.h utctime
+	@rm -f $(HUBEXEC).* $(LEAFEXEC).* src/settings.c src/salt.h src/help.h utctime
 
 distclean: clean clean-modconfig
 	@rm -f Makefile src/Makefile src/compat/Makefile src/mod/Makefile
@@ -142,6 +142,21 @@ salt: pack/salt.h makesalt stringfix
 	echo "done."; \
 	fi)
 
+sort: sorthelp
+	@cp -f misc/help.txt help.txt~
+	@(src/sorthelp misc/help.txt misc/help.txt || (cp -f help.txt~ misc/help.txt; echo "Sort failed, restoring backup."))
+	@rm -f help.txt~
+
+help: makehelp stringfix
+	@src/makehelp misc/help.txt src/help.h.tmp~
+	@(if [ ! -f src/help.h ]; then \
+	touch src/help.h; \
+	fi)
+	@src/stringfix src/help.h.tmp~ src/help.h~ 1
+	@(if test "x`diff -qurN src/help.h~ src/help.h`" != "x"; then \
+	cp -f src/help.h~ src/help.h; \
+	fi)
+
 config:
 	@$(modconf) modules-still-exist
 	@$(modconf) detect-modules
@@ -156,7 +171,7 @@ clean-modconfig:
 reconfig: clean-modconfig config
 
 
-stringfix: src/stringfix.c
+stringfix: src/stringfix.c pack/conf.h
 	+@cd src && ${MAKE_UTILS} stringfix
 
 makesalt: src/makesalt.c
@@ -165,7 +180,13 @@ makesalt: src/makesalt.c
 makesettings: src/makesettings.c
 	+@cd src && ${MAKE_UTILS} makesettings
 
-utils: makesettings makesalt stringfix
+makehelp: src/makehelp.c pack/conf.h
+	+@cd src && ${MAKE_UTILS} makehelp
+
+sorthelp: src/sorthelp.c
+	+@cd src && ${MAKE_UTILS} sorthelp
+
+utils: makesettings makesalt stringfix makehelp
 
 dostrings: stringfix
 	@misc/dostrings
@@ -180,7 +201,7 @@ dephub:
 	+@cd src/mod && $(MAKE_DEPEND) dephub
 	+@cd src/compat && $(MAKE_DEPEND) dephub
 
-general: dostrings salt packconf config
+general: dostrings salt packconf config help
 
 leaf:	general
 	@echo ""

+ 536 - 0
misc/help.txt

@@ -0,0 +1,536 @@
+::+ban
+###  %b+ban%b <hostmask> [channel] [%%<XdXhXm>] [comment]
+   Adds a ban to the list of bans stored on the bot, with optional comment and
+   ban time. This ban is stored with your handle as the creator, and will be
+   in effect for every channel if no channel is specified. Prefixing a comment
+   with '@' will make it only visible within the bot, and not used as the ban
+   reason. Ban time has to be expressed in days, hours, and/or minutes.
+ 
+See also: bans, -ban, stick, unstick
+
+::+exempt
+###  %b+exempt%b <hostmask> [channel] [%%<XdXhXm>] [comment]
+   Adds an exempt to the list of exempts stored on the bot, with optional
+   comment and exempt time. This exempt is stored with your handle as the
+   creator, and will be in effect for every channel if no channel is specified.
+   Exempt time has to be expressed in days, hours, and/or minutes.
+ 
+See also: exempts, -exempt, stick, unstick
+
+::+host
+###  %b+host%b [handle] <hostmask>
+   Adds a hostmask to a user's user record. Hostmasks are used to identify your
+   handle on IRC. If a handle is not specified, the hostmask will be added to
+   YOUR user record.
+ 
+See also: -host
+
+::+ignore
+###  %b+ignore%b <hostmask> [%%<XdXhXm>] [comment]
+   Adds an ignore to the list of ignores stored on the bot, with optional
+   comment and ignore time. This ignore is stored with your handle as the
+   creator. Ignore time has to be expressed in days, hours, and/or minutes.
+ 
+See also: -ignore, ignores
+
+::+invite
+###  %b+invite%b <hostmask> [channel] [%%<XdXhXm>] [comment]
+   Adds an invite to the list of invites stored on the bot, with optional
+   comment and invite time. This invite is stored with your handle as the
+   creator, and will be in effect for every channel if no channel is specified.
+   Invite time has to be expressed in days, hours, and/or minutes.
+ 
+See also: invites, -invite, stick, unstick
+
+::+user
+###  %b+user%b <handle> [hostmask]
+   Creates a new user record for the handle given. The new user record will
+   have no flags, an optional hostmask, and a random pass/secpass.
+ 
+See also: -user, +host, -host%(hub)%{+n}, newleaf
+
+:hub:-bot
+###  %b-bot%b <bot>
+   This is exactly the same as %b'%d-user'%b (it removes a user record). It is
+   included for convenience.
+ 
+See also: +user, -user%{+n}, newleaf
+
+::-host
+###  %b-host%b <hostmask>
+   Removes a host from your handle.
+%{+m|m}
+###  %b-host%b <handle> <hostmask>
+   Removes a hostmask from a user's user record.
+%{-}
+ 
+See also: +host
+
+::-ignore
+###  %b-ignore%b <hostmask/number>
+   Removes the specified ignore from the list of ignores stored on the bot. You
+   may also reference the ignore by the number shown by the 'ignores' command.
+ 
+See also: +ignore, ignores
+
+::-user
+###  %b-user%b <handle>
+   Removes the specified handle's user record.
+ 
+See also: +user%(hub)%{+a}, -bot%{+n}, newleaf
+
+::addlog
+###  %baddlog%b <text>
+   Adds your comment to the bot's logfile. Bot masters can go back later and
+   review the log, and will see your comment (with your handle attached). This
+   is useful for explaining confusing activity.
+ 
+
+::away
+###  %baway%b [reason]
+   Marks you as "away" on the party line. Your away message will show up in
+   the %b'%dwho'%b list, and will be displayed to anyone who tries to send you
+   a note. Your notes will be stored, and then displayed to you as soon as you
+   are no longer away. Saying something on the party line will automatically
+   remove your "away" status, or you can type %b'%dback'%b or %b'%daway'%b by
+   itself.
+ 
+See also: back
+
+::back
+###  %bback%b
+   This marks you as no longer away on the party line.
+ 
+See also: away
+
+:hub:backup
+###  %bbackup%b
+   This makes the bot write a backup of its entire user list to the disk. 
+   This is useful if you feel the need to backup the userfile and channel
+   settings.
+ 
+See also: reload, save
+
+:hub:binds
+###  %bbinds%b [type/match] [all]
+   Shows the Tcl bindings in effect, in a list similar to this:
+      Command bindings:
+        TYPE FLGS COMMAND              HITS BINDING (TCL)
+        msg  -|-  rose                    0 msg_rose
+        msg  -|-  go                      0 msg_go
+        dcc  m|-  bind                    0 cmd_bind
+        pub  -|-  gross                   0 pub_gross
+   The fields should be self-explanatory, except for HITS, which records the
+   number of times that binding has been called. If not, go read the file
+   %b'doc/tcl-commands.doc'%b for help on the Tcl bind command. Note that the
+   builtin commands are now shown. You may also specify a type of binding to
+   show (i.e. %b'%dbinds msg'%b) or you can specify a wild card match (i.e.
+   %b'%dbinds *seen*'%b) if you want to narrow the field a bit. The wild card
+   matches will match against the TYPE, COMMAND and BINDING fields. You can
+   view more binds by adding 'all' to the end of the command.
+ 
+See also: tcl
+
+:hub:boot
+###  %bboot%b <handle[@bot]> [reason]
+   Kicks a user off the party line and displays the reason, if you specify
+   one. You can also specify a bot, and attempt to boot someone from another
+   bot on the botnet, though it may be rejected if that bot does not allow
+   remote boots. You can not boot a bot owner.
+
+:hub:botinfo
+###  %bbotinfo%b
+   Requests information from every bot currently on the botnet. Each bot should
+   eventually send you one line containing its version, network setting,
+   channels, and uptime.
+ 
+See also: %{+n}bottree, vbottree%{+m}, bots
+
+:hub:bots
+###  %bbots%b
+   Shows the list of bots currently on the botnet.
+   Example:
+      Bots: cEvin, ruthie, Killa1
+   There is no indication of which bots are directly connected to this current
+   bot. %{+n}Use %b'%dwho'%b or %b'%dbottree'%b for that information.%{-}
+ 
+See also: downbots%{+n}, bottree, vbottree%{-}, botinfo
+
+:hub:bottree
+###  %bbottree%b
+   Shows a tree-format diagram of the bots currently on the botnet. It's just a
+   nice way to get a feel for how the bots are physically connected. If 2 bots
+   are sharing, a + will be indicated, or a ? if nothing is known.
+ 
+See also: bots, downbots, botinfo, vbottree
+
+:hub:chaddr
+###  %bchaddr%b <bot> <address[:bot port[/user port]]>
+   Changes the address for a bot. This is the address your bot will try to
+   connect to when linking. If the bot has a separate port for bots and users,
+   they should be separated  by a slash (/).
+%{+a}
+   It is not recommneded that this is used, you should change your source,
+   and update all bots instead.
+%{-}
+ 
+See also: link
+
+::chat
+###  %bchat%b <on/off>
+###  %bchat%b <[*]channel number/name>
+   Changes your current channel on the partyline. When you first connect to
+   the partyline, it places you on channel 0 (the main party line).
+   Some channels may have assigned names if the assoc module is loaded. For
+   these, you can specify the channel by name instead of channel number if
+   you wish.
+   %b'%dchat off'%b removes you from all channels, including the main party line.
+   You can still use bot commands and see the console, but you can't talk to
+   anyone except via %b'%dnote'%b. %b'%dchat on'%b returns you to the main party
+   line (channel 0) if you were elsewhere.
+   If you prefix the channel with a '*', you will join a local channel.
+ 
+See also: console
+
+::chattr
+###  %bchattr%b <handle> [flags] [channel]
+   This lets you view and change the flags for a user.
+   For example, to give Lamer the p and f flags:
+      .chattr Lamer +pf
+   To remove Denali from the global op list:
+      .chattr Denali -o
+   You may also do any combination of the above:
+      .chattr Fred1 -m+xj-o
+   You can also change the flags for Usagi on a specific channel by supplying
+   the channel after the attributes:
+      .chattr Usagi -m+dk-o #blah
+   Changing global and channel specific flags within the same command line is
+   also possible (global +f, +o #lamer):
+      .chattr Bill f|o #lamer
+   Whether or not you change any flags, it will show you the user's attributes
+   afterwards. To get a list of all possible flags, see %b'%dhelp whois'%b.
+   %bNOTES:%b Only the owner may add or remove the 'a' (admin) or 'n' (owner) flags.
+          It is pointless to -a a permanent owner.
+          You must remove the permanent owner in the binary.
+ 
+See also: whois
+
+:hub:chhandle
+###  %bchhandle%b <oldhandle> <newhandle>
+   Changes the handle of a user. For example, to change the handle of user
+   'gavroche' to 'jamie', you would use 'chhandle gavroche jamie'.
+ 
+See also: chpass%{+n}, chsecpass
+
+:hub:chpass
+###  %bchpass%b <handle> [newpassword]
+   Changes a user's password. If you do not specify the new password, the user
+   effectively no longer has a password set. A password is needed to get ops,
+   join the party line, etc. If the newpassword is 'rand', a random password
+   will be used.
+ 
+See also: chhandle%{+n}, chsecpass
+
+::comment
+###  %bcomment%b <user> <comment>
+   Creates or changes the comment field for a user. The comment field can only
+   be seen via 'whois' or 'match'. Non-masters cannot see the comment field.
+   Using the comment 'none' will clear a user's comment.
+
+:hub:dccstat
+###  %bdccstat%b
+   Displays a table-format list of all "dcc" connections in use on the bot.
+   Dcc stands for "Direct Client-to-client Communication", and Eggdrop expands
+   this to cover every open socket. Any type of network connection to the bot
+   is considered a "dcc" connection.
+   The headings of the table are:
+     %bSOCK%b  the socket number of this connection (always unique)
+     %bADDR%b  the ip address mask of the host the bot is connected to, if
+           applicable
+     %bPORT%b  the port number being used for this connection
+     %bNICK%b  the handle of the user or bot, if applicable
+     %bHOST%b  the hostname corresponding to the IP address, if available
+     %bTYPE%b  the type of dcc connection (see below)
+   The types of connections currently possible are as follows (but more are
+   being added all the time):
+     %bCHAT%b  dcc-chat partyline user
+     %bPASS%b  user entering dcc chat (being asked for password)
+     %bSEND%b  user sending a file
+     %bGET%b   sending a file to a user
+     %bGETP%b  pending get (waiting for the user to acknowledge)
+     %bLSTN%b  telnet listening port (in place of a hostname, it will show the
+           callback procedure name, or a mask of acceptable handles)
+     %bT-IN%b  incoming telnet user (being asked for handle)
+     %bFILE%b  user in dcc-chat file area
+     %bBOT%b   connected bot (botnet connection)
+     %bBOT*%b  pending bot link (waiting for acknowledgement)
+     %bRELA%b  user in relay connection to another bot
+     %b>RLY%b  bot being relay'd to (one for each "RELA")
+     %bCONN%b  pending telnet connection (chat, relay, bot-link, etc)
+     %bNEW%b   new user via telnet (entering a handle)
+     %bNEWP%b  new user via telnet (entering a password)
+   In addition, 'CHAT' and 'BOT' have flags listed for each connection. Capital
+   letters mean that the flag is on, and lowercase letters mean that the flag
+   is off. The flags for 'CHAT' are:
+     %bC%b  in file area, but allowed to return to party line
+     %bP%b  party line access only
+     %bT%b  telnet connection (instead of dcc chat)
+     %bE%b  echo is on
+     %bP%b  paging is on
+     %b6%b  socket is ipv6
+   The flags for 'BOT' are:
+     %bP%b  ping sent, waiting for reply
+     %bU%b  user-file sharing is active
+     %bC%b  local bot initiated the connection
+     %bO%b  user-file offered, waiting for reply
+     %bS%b  in the process of sending the user-file
+     %bG%b  in the process of getting the user-file
+     %bW%b  warned this bot to stop hubbing
+     %bL%b  leaf-only bot (not allowed to be a hub)
+     %bI%b  bot is currently in the 'linking' stage
+     %bA%b  bot is being aggressively shared with
+   For 'CHAT' users, the party-line channel is also listed.
+
+::debug
+###  %bdebug%b
+   Display a dump of memory allocation information, assuming the bot was
+   compiled with memory debugging. It's useless to anyone but developers
+   trying to find memory leaks.
+
+::die
+###  %bdie%b [reason]
+   This kills the bot. The bot goes offline immediately, logging who issued
+   the 'die' command. You shouldn't have to use this too often. If you specify
+   a reason, it is logged, otherwise the reason is "authorized by <handle>".
+
+::echo
+###  %becho%b <on/off>
+   Specifies whether you want your messages echoed back to you. If it's on,
+   then when you say something on the party line, it will be displayed to you
+   just like everyone else will see it. If it's off, then it won't happen.
+ 
+See also: color
+
+::fixcodes
+### %bfixcodes%b
+     This is for use in situations where the bot gets mixed up about the type
+     of connection you have with it. For example, you /CTCP CHAT the bot and
+     it thinks you are connecting via telnet, and you see text displayed as
+     "Local time is now 17:17" for example instead of "Local time is
+     now 17:17". Use this to turn telnet codes on or off/change the display
+     mode.
+
+::handle
+###  %bhandle%b <new-handle>
+   Changes your handle on the bot. This is the handle (nickname) that the
+   bot will know you as from this point forward. It is used to log into the
+   bot.
+ 
+See also: newpass%{+m}, %(hub)chhand, chpass%{+n}, chsecpass
+
+::ignores
+###  %bignores%b [wildcard]
+   Shows a list of hostmasks from which the bot is currently ignoring msgs,
+   notices, etc. There are two types of ignores: permanent and temporary.
+   Permanent ignores never automatically expire. You must use %b'%d-ignore'%b
+   to remove them.
+   Here is a sample permanent ignore:
+      [  1] *!*@217.156.44.184 (perm)
+            Wcc: go away
+            Started 523 days ago
+   The number (1) can be used to reference the ignore if you wish to remove it
+   (see %b'%dhelp -ignore'%b). Next is the actual hostmask being ignored. The
+   "(perm)" means that the ignore is "permanent": that is, it doesn't
+   automatically expire. The second line of the ignore entry is the comment
+   ("go away"), and who set the ban (Wcc). The last line shows when the ignore
+   was added.
+   Here is a sample temporary ignore:
+      [ 10] blah!blah@blah.cc (expires in 1 day)
+            Wcc: requested
+            Started 18:02
+   Here, you see the "perm" in the parentheses next to the hostmask is instead
+   an expire time. This means that the ignore will expire automatically in one
+   day.
+   If you use %b'%dignores <wildcard>'%b, it will list all the ignores
+   that match against your wildcard.
+ 
+See also: +ignore, -ignore
+
+:hub:link
+###  %blink%b [via-bot] <bot-to-link>
+   Attempts to link to another hub.  This command is deprecated and not 
+   recommended for use.
+ 
+See also: unlink, newleaf%{+a}, -bot
+
+::match
+###  %bmatch%b <attr> [channel] [[start] limit]
+   This displays all user records with the attributes requested.
+   "attr" is of the form:  <+/-><global>[&/|<channel>[&/|<bot>]]
+   Specifying "&" as the separator will cause AND style matching.
+   For example:
+      .match p&o
+   This will match all users with both the "p" global flag and the "o" channel
+   flag on your current console channel.
+   Specifying "|" as the separator will cause OR style matching.
+   For example:
+     .match p|o
+   This will match all users with either the "p" global flag or the "o" channel
+   flag on your current console channel. If you specify a channel, it will be
+   used instead of the current console channel.
+   For example:
+     .match p|o #eggdrop
+   This will match all users with either the "p" global flag or the "o" channel
+   flag on the channel #eggdrop. You can also match bot flags.
+   For example:
+     .match o|o|h
+   This will match all bots with either the "o" global flag, the "o" channel
+   flag on the current console channel, or the "h" botflag. You can also limit
+   the number of total results returned by specifying a limit at the end of the
+   command. A starting point can also be specified.
+   For example:
+     .match p&o #eggdrop 16 25
+   This would show results 16 through 25 matching any users with the "p" global
+   flag or the "o" channel flag on #eggdrop.
+###  %bmatch%b <wildcard-string> [[start] limit]
+   This displays all user records where the user's handle or any of the user's
+   hostmasks match the specified wildcard string. You can also limit the number
+   of total results returned by specifying a limit at the end of the command. A
+   starting point can also be specified.
+   For example:
+     .match *.edu 16 25
+   This would show results 16 through 25 matching any users with a hostmask
+   that ends with ".edu".
+
+::me
+###  %bme%b <text>
+   Performs an action on the party line. This appears as "* Wcc is leaving",
+   etc.
+
+::motd
+###  %bmotd%b %{+m}<message>%{-}
+   This redisplays the partyline Message Of The Day, which was shown when you
+   first joined the partyline.%{m) Inclue a message to set the motd.
+
+::newpass
+###  %bnewpass%b <password>
+   Changes your password on the bot. This is similar to the '/msg <bot> pass'
+   command, except you don't need to specify your old password.
+ 
+%(hub)%{+m}See also: chpass%{+n}, chsecpass
+
+::note
+###  %bnote%b <nickname[@bot]> <message>
+   Sends a private note to a user on the partyline. If that user is currently
+   logged in and not marked as away, the message will be received immediately.
+   Otherwise, it will be stored and displayed the next time that user joins the
+   partyline. To send a note to someone on a different bot, add "nick@bot" to
+   the nickname.
+ 
+See also: whom, notes
+
+::page
+### %bpage%b <number/off>
+   This allows you to slow down the number of lines the bot sends you at once
+   via the partyline. When enabled, any commands that send greater than the
+   specified number of lines will stop when that number is reached and wait for
+   you to type another command (or press enter) to continue. If you have too
+   many pending lines, you may be booted off the bot.
+
+::quit
+###  %bquit%b [comment]
+   This disconnects you from the partyline. If you specify a comment, it will
+   be displayed to other partyline users as you leave.
+
+::relay
+###  %brelay%b <botname>
+   Relays you via telnet to another bot, whether or not it is currently linked.
+   The local bot must, however, have a bot record for the bot you wish to
+   relay to. Typing .quit or "*bye*" on a line by itself will end the relay.
+ 
+See also: bots%{+n}, newleaf%{+a}, -bot
+
+::reload
+###  %breload%b
+   Reloads the bot's user file, discarding any changes made since the last
+   %b'%dsave'%b command or hourly user file save. Sharebots should probably
+   never do this.
+ 
+See also: save
+
+::restart
+###  %brestart%b
+   Restarts the Tcl interpreter, wipes all timers, reloads all modules, and
+   reloads the config file for the bot (which resets any changes made via the
+   %b'%dset'%b command and reloads any Tcl scripts your config loads). As with
+   the %b'%drehash'%b command, it also saves and reloads the user file from disk.
+ 
+See also: reload, save
+
+:hub:save
+###  %bsave%b
+   This makes the bot write its entire userfile to disk. This is useful if you
+   think the bot is about to crash or something, since the user file is only
+   written to disk about once an hour.
+ 
+See also: reload, backup
+
+::simul
+###  %bsimul%b <handle> <text>
+   This allows you to simulate the specified handle typing the given text.
+   For example:
+      %dsimul dweeb%d.quit
+   This would appear just as if "dweeb" typed "%dquit". This command will not
+   work unless eggdrop has simul enabled in the config file. 
+ 
+See also: su
+
+::strip
+###  %bstrip%b [modes]
+   Allows you to remove embedded 'attribute' codes your partyline output. Valid
+   options are:
+      %bb%b - remove all boldface codes
+      %bc%b - remove all color codes
+      %br%b - remove all reverse video codes
+      %bu%b - remove all underline codes
+      %ba%b - remove all ANSI codes
+      %bg%b - remove all ctrl-g (bell) codes
+   The mode can also be a modifier like '+c' or '-bu' or '+ru-c'. If
+   you omit modes, it will show your current setting.
+ 
+See also: fixcodes, color, echo
+%{+m}
+###  %bstrip%b <user> [modes]
+   Set the strip level of another user. A master can't set their own strip
+   flags without prefixing the modes with a '+' or '-'.
+
+::su
+###  %bsu%b <user>
+   Lets you assume the identity of another user. If you are a global owner,
+   this does not require a password. Otherwise, you will be asked for the
+   user's password. %b%dquit%b returns you to your original handle.
+
+:hub:trace
+###  %btrace%b <bot>
+   Sends out a trace signal to another bot. If/when the trace signal returns,
+   (and it should!) you will get an output that looks something like this:
+      Trace result -> Valis:Stonewall:NoBoty:SomeBoty
+   This is a list of the bots connected between you and the destination bot.
+   It should also return the time in seconds taken for the trace to occur.
+ 
+See also: bots, bottree, vbottree
+
+:hub:traffic
+###  %btraffic%b
+   Shows total and daily net traffic stats since the last %b'%drestart'%b.
+   Stats groups are IRC, Botnet, Partyline, Transfer.mod and Misc.
+ 
+%{+m}See also: restart
+
+::whoami
+###  %bwhoami%b
+   Shows your current handle and to what bot you are connected.
+ 
+See also: whom
+
+::end

+ 2 - 0
pack/conf.h

@@ -14,6 +14,8 @@
 #define S_AUTOAWAY	/*  yes		random autoaway/return on IRC 			*/
 #define S_AUTOLOCK      /*  yes         will lock channels upon certain coniditions     */
 #define S_DCCPASS	/*  yes		DCC command passwords 				*/
+#undef S_GARBLEHELP	/*  no		This will garble all help info, bloating binary *
+			 *		You may want this if you are paranoid.		*/
 #define S_GARBLESTRINGS	/*  yes		encrypt strings in binary			*/
 #define S_HIJACKCHECK   /*  yes		checks for a common fbsd process hijacker	*/
 #define S_LASTCHECK	/*  yes		checks every few seconds for logins with `last` */

+ 13 - 4
src/Makefile.in

@@ -22,17 +22,26 @@ tcluser.o userent.o userrec.o users.o
 MAKE_GENERIC = $(MAKE) 'MAKE=$(MAKE)' 'CC=$(CC)' 'LD=$(LD)' \
 'STRIP=$(STRIP)' 'CFLGS=$(CFLGS)'
 
-doofus:
+blah:
 	@echo ""
-	@echo "Let's try this from the right directory..."
+	@echo "Use the build script."
 	@echo ""
-	@cd .. && $(MAKE)
 
 makesalt: makesalt.c
 	@echo -e "Compiling: \033[1mmakesalt\033[0m"
 	@$(CC) $(CFLAGS) -I$(top_srcdir) -I$(top_srcdir)/pack $(CPPFLAGS) makesalt.c -o makesalt
 	@$(STRIP) makesalt
 
+makehelp: makehelp.c ../pack/conf.h
+	@echo -e "Compiling: \033[1mmakehelp\033[0m"
+	@$(CC) $(CFLAGS) -I$(top_srcdir) -I$(top_srcdir)/pack $(CPPFLAGS) makehelp.c -o makehelp
+	@$(STRIP) makehelp
+
+sorthelp: sorthelp.c
+	@echo -e "Compiling: \033[1msorthelp\033[0m"
+	@$(CC) $(CFLAGS) -I$(top_srcdir) -I$(top_srcdir)/pack $(CPPFLAGS) sorthelp.c -o sorthelp
+	@$(STRIP) sorthelp
+
 makesettings: makesettings.c
 	@echo -e "Compiling: \033[1mmakesettings\033[0m"
 	@$(CC) $(CFLAGS) -I$(top_srcdir) -I$(top_srcdir)/pack $(CPPFLAGS) makesettings.c -o makesettings
@@ -169,7 +178,7 @@ misc.o: misc.c stringfix main.h ../config.h ../pack/conf.h lang.h eggdrop.h flag
  ../lush.h misc_file.h cmdt.h tclegg.h tclhash.h chan.h users.h \
  compat/compat.h compat/inet_aton.h compat/inet_ntop.h ../src/main.h compat/snprintf.h \
  compat/memset.h compat/memcpy.h compat/strcasecmp.h compat/strftime.h \
- stat.h help.h
+ stat.h
 misc_file.o: misc_file.c stringfix main.h ../config.h ../pack/conf.h lang.h eggdrop.h flags.h \
  proto.h ../lush.h misc_file.h cmdt.h tclegg.h tclhash.h chan.h \
  users.h compat/compat.h compat/inet_aton.h compat/inet_ntop.h ../src/main.h \

+ 12 - 7
src/cmds.c

@@ -765,7 +765,7 @@ static void cmd_nohelp(struct userrec *u, int idx, char *par)
   char *buf = nmalloc(1);
   buf[0] = 0;
 
-  qsort(cmds, cmdi - 1, sizeof(mycmds), (int (*)()) &my_cmp);
+  qsort(cmds, cmdi, sizeof(mycmds), (int (*)()) &my_cmp);
   
   for (i = 0; i < (cmdi - 1); i++) {
     int o, found = 0;
@@ -786,7 +786,7 @@ static void cmd_nohelp(struct userrec *u, int idx, char *par)
 static void cmd_help(struct userrec *u, int idx, char *par)
 {
   char flg[100];
-  int fnd = 0, done = 0, o = 0, nowild = 0;
+  int fnd = 0, done = 0, nowild = 0;
   struct flag_record fr = {FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0};
   char *fcats, temp[100], buf[2046], match[20];
 
@@ -805,8 +805,7 @@ static void cmd_help(struct userrec *u, int idx, char *par)
   }
   if (!nowild)
     dprintf(idx, STR("Showing help topics matching '%s' for flags: (%s)\n"), match, flg);
-  o = cmdi - 1;  
-  qsort(cmds, o, sizeof(mycmds), (int (*)()) &my_cmp);
+  qsort(cmds, cmdi, sizeof(mycmds), (int (*)()) &my_cmp);
   buf[0] = 0;
   /* even if we have nowild, we loop to conserve code/space */
   while (!done) {
@@ -817,15 +816,21 @@ static void cmd_help(struct userrec *u, int idx, char *par)
     if (!flag[0]) 
       done = 1;
 
-    for (n = 0; n < o ; n++) { /* loop each command */
-      if (!flagrec_ok(&cmds[n].flags, &fr) || !wild_match(match,cmds[n].name))
+    for (n = 0; n < cmdi; n++) { /* loop each command */
+      if (!flagrec_ok(&cmds[n].flags, &fr) || !wild_match(match, cmds[n].name))
         continue;
       fnd++;
       if (nowild) {
-        dprintf(idx, STR("Showing you help for '%s':"), match);
+        flg[0] = 0;
+        build_flags(flg, &(cmds[n].flags), NULL);
+        dprintf(idx, STR("Showing you help for '%s' (%s):"), match, flg);
         for (hi = 0; (help[hi].cmd) && (help[hi].desc); hi++) {
           if (!egg_strcasecmp(match, help[hi].cmd)) {
+#ifdef S_GARBLEHELP
+            showhelp(idx, &fr, degarble(help[hi].garble, help[hi].desc));
+#else /* !S_GARBLEHELP */
             showhelp(idx, &fr, help[hi].desc);
+#endif /* S_GARBLEHELP */
           }
         }
         done = 1;

+ 0 - 72
src/help.h

@@ -1,72 +0,0 @@
-#ifndef HELP_H
-#define HELP_H
-typedef struct {
-  int type;
-  char *cmd;
-  char *desc;
-} help_t;
-
-help_t help[] =
-{
-{2, "cmd",
-"Usage: cmd <blah> \n \
-	Help text goes here \n \
-	more here \n \
-	etc .... \n"},
-{2, "echo",
-"	Testing the help system \n \
-blah blah blah blah \n \
-[12:42:49] <drewballs> hey, would it be possible to create 3 more vhosts with drewballs.org with different nicks? \n \
-[12:43:20] <drewballs> but things like, got.0wned.at.drewballs.org \n"},
-{2, "whois",
-"Usage: whois <handle> \n \
-    will show you the bot information about a user record. there are \n \
-    five headings: \n \
-    HANDLE the handle (nickname) of the user \n \
-    PASS 'Yes' if they have a password set, 'No' if not \n \
-    NOTES number of notes stored waiting for the user \n \
-    FLAGS the list of flags for this user (see below) \n \
-    LAST the time or date that the user was last on the channel \n \
-    the valid flags under FLAGS are: \n \
-      a administrator (user is higher than an owner but not a perm owner) \n \
-      b bot (user is another bot) \n \
-      c leaf 'chat hub' (bot will accept MSG cmds and dcc chat from users) \n \
-      d global deop (user cannot get ops) \n \
-      i hub access (user can .relay to hubs) \n \
-      j leaf 'chat hub' access (user has DCC access to leaf 'chat hubs') \n \
-      k global auto-kick (user kicked & banned automatically) \n \
-      l bot flag - sets limit (bot will set limit in all chans) \n \
-      m master (user is a bot master) \n \
-      n owner (user is the bot owner) \n \
-      o global op (bot will op this user on any channel upon request) \n \
-      p party-line (user has party-line chat access [Not required for dcc, see: +j]) \n \
-      q quiet (user never gets +v on channels) \n \
-      v global voice (user get +v automatically) \n \
-      w wasop-test (needs wasop test for +stopnethack procedure) \n \
-      x flood exempt (user will not be punished for flooding) \n \
-      y bot flag - voices users (bot will voice +v users in all chans) \n \
-    each channel that the user has joined will have a specific record \n \
-    for it, with the channel-specific flags and possibly an info \n \
-    line. the channel-specific flags are: \n \
-      d deop (bot will not allow this user to become a chanop) \n \
-      k kick (user is auto kicked-banned from chan) \n \
-      l bot flag - sets limit (bot will set limit in all chans) \n \
-      m master (user is a master for the channel) \n \
-      n owner (user is an owner for the channel) \n \
-      o op (bot will give this user chanop) \n \
-      q quiet (user never gets +v in chan) \n \
-      v voice (user gets +v automatically on chan) \n \
-      w wasop-test (needs wasop test for +stopnethack procedure) \n \
-      x flood exempt (user will not be punished for flooding) \n \
-      y bot flag - voices users (bot will voice +v users in all chans) \n \
-    hostmasks for the user are displayed on the following lines. \n \
-    (not on leaf bots though) \n \
-    if the user entry is for a bot, there will be a line below which \n \
-    says 'ADDRESS:' and gives the bot's telnet address. some user \n \
-    entries may have 'EMAIL:' and 'INFO:' entries too. \n \
-    %{+m} masters: if there is a comment, you will see it under 'COMMENT:' \n \
-      %{-} See also: match \n"},
-
-{0, NULL}	/* end of the list */
-};
-#endif /* HELP_H */

+ 206 - 0
src/makehelp.c

@@ -0,0 +1,206 @@
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include "conf.h"
+
+char *replace(char *string, char *oldie, char *newbie)
+{
+  static char newstring[1024] = "";
+  int str_index, newstr_index, oldie_index, end, new_len, old_len, cpy_len;
+  char *c;
+  if (string == NULL) return "";
+  if ((c = (char *) strstr(string, oldie)) == NULL) return string;
+  new_len = strlen(newbie);
+  old_len = strlen(oldie);
+  end = strlen(string) - old_len;
+  oldie_index = c - string;
+  newstr_index = 0;
+  str_index = 0;
+  while(str_index <= end && c != NULL) {
+    cpy_len = oldie_index-str_index;
+    strncpy(newstring + newstr_index, string + str_index, cpy_len);
+    newstr_index += cpy_len;
+    str_index += cpy_len;
+    strcpy(newstring + newstr_index, newbie);
+    newstr_index += new_len;
+    str_index += old_len;
+    if((c = (char *) strstr(string + str_index, oldie)) != NULL)
+     oldie_index = c - string;
+  }
+  strcpy(newstring + newstr_index, string + str_index);
+  return (newstring);
+}
+
+
+char *step_thru_file(FILE *fd)
+{
+  const int tempBufSize = 1024;
+  char tempBuf[tempBufSize];
+  char *retStr = NULL;
+  if (fd == NULL) {
+    return NULL;
+  }
+  retStr = NULL;
+  while (!feof(fd)) {
+    fgets(tempBuf, tempBufSize, fd);
+    if (!feof(fd)) {
+      if (retStr == NULL) {
+        retStr = malloc(strlen(tempBuf) + 2);
+        strcpy(retStr, tempBuf);
+      } else {
+        retStr = realloc(retStr, strlen(retStr) + strlen(tempBuf));
+        strcat(retStr, tempBuf);
+      }
+      if (retStr[strlen(retStr)-1] == '\n') {
+        retStr[strlen(retStr)-1] = 0;
+        break;
+      }
+    }
+  }
+  return retStr;
+}
+
+char *newsplit(char **rest)
+{
+  register char *o, *r;
+
+  if (!rest)
+    return *rest = "";
+  o = *rest;
+  while (*o == ' ')
+    o++;
+  r = o;
+  while (*o && (*o != ' '))
+    o++;
+  if (*o)
+    *o++ = 0;
+  *rest = o;
+  return r;
+}
+
+int skipline (char *line, int *skip) {
+  static int multi = 0;
+  if ((!strncmp(line, "//", 2))) {
+    (*skip)++;
+  } else if ( (strstr(line, "/*")) && (strstr(line, "*/")) ) {
+    multi = 0;
+    (*skip)++;
+  } else if ( (strstr(line, "/*")) ) {
+    (*skip)++;
+    multi = 1;
+  } else if ( (strstr(line, "*/")) ) {
+    multi = 0;
+  } else {
+    if (!multi) (*skip) = 0;
+  }
+  return (*skip);
+}
+
+int parse_help(char *infile, char *outfile) {
+  FILE *in = NULL, *out = NULL;
+  char *buffer = NULL, *cmd = NULL;
+  int skip = 0, line = 0, leaf = 0, hub = 0;
+  if (!(in = fopen(infile, "r"))) {
+    printf("Error: Cannot open '%s' for reading\n", infile);
+    return 1;
+  }
+  if (!(out = fopen(outfile, "w"))) {
+    printf("Error: Cannot open '%s' for writing\n", outfile);
+    return 1;
+  }
+  printf("Parsing help file '%s'", infile);
+  fprintf(out, "/* DO NOT EDIT THIS FILE, EDIT misc/help.txt INSTEAD */\n#ifndef HELP_H\n\
+#define HELP_H\n\
+#define STR(x) x\n\
+typedef struct { \n\
+  int type; \n\
+  char *cmd; \n\
+  int garble; \n\
+  char *desc; \n\
+} help_t; \n\
+\n\
+help_t help[] = \n\
+{ \n");
+  while ((!feof(in)) && ((buffer = step_thru_file(in)) != NULL) ) {
+    line++;
+    if ((*buffer)) {
+      if (strchr(buffer, '\n')) *(char*)strchr(buffer, '\n') = 0;
+      if ((skipline(buffer, &skip))) continue;
+      if (buffer[0] == ':') { //New cmd 
+        char *ifdef = malloc(strlen(buffer) + 1), *p;
+        int cl = 0, doleaf = 0, dohub = 0;
+
+        buffer++;
+        ifdef[0] = 0;
+        strcpy(ifdef, buffer);
+        p = strchr(ifdef, ':');
+        *p = 0;
+        if (ifdef && ifdef[0]) {
+          if (!strcasecmp(ifdef, "leaf")) {
+            if (hub) { cl = 1; hub = 0; }
+            if (!leaf) {
+              doleaf = leaf = 1;
+            }
+          } else if (!strcasecmp(ifdef, "hub")) {
+            if (leaf) { cl = 1; hub = 0; }
+            if (!hub) {
+              dohub = hub = 1;
+            }
+          }
+        } else { if (leaf || hub) { cl = 1; } leaf = 0; hub = 0;  }
+
+        if (cmd && cmd[0]) {		/* CLOSE LAST CMD */
+#ifdef S_GARBLEHELP
+          fprintf(out,"\")},\n");
+#else /* !S_GARBLEHELP */
+          fprintf(out,"\"},\n");
+#endif /* S_GARBLEHELP */
+          if (cl) { cl = 0; fprintf(out, "#endif\n"); }
+          if (dohub) { dohub = 0; fprintf(out, "#ifdef HUB\n"); }
+          else if (doleaf) { doleaf = 0; fprintf(out, "#ifdef LEAF\n"); }
+          free(cmd);
+        }
+        p = strchr(buffer, ':');
+        p++;
+        if (strcmp(p, "end")) {		/* NEXT CMD */
+          cmd = malloc(strlen(p) + 1);
+          strcpy(cmd, p);
+          printf(".");
+          if (dohub) { dohub = 0; fprintf(out, "#ifdef HUB\n"); }
+          else if (doleaf) { doleaf = 0; fprintf(out, "#ifdef LEAF\n"); }
+#ifdef S_GARBLEHELP
+          fprintf(out, "  {2, \"%s\", STR(\"", cmd);
+#else /* !S_GARBLEHELP */
+          fprintf(out, "  {2, \"%s\", 0, \"", cmd);
+#endif /* S_GARBLEHELP */
+        } else {			/* END */
+          fprintf(out, "  {0, NULL}\n};\n");
+        }
+      } else {				/* CMD HELP INFO */
+        fprintf(out, "%s\\n", replace(buffer, "\"", "\\\""));
+      }
+    }
+    buffer = NULL;
+  }
+  fprintf(out, "#endif /* HELP_H */\n");
+  printf(" Success\n");
+  if (in) fclose(in);
+  if (out) fclose(out);
+  return 0;
+}
+
+int main(int argc, char **argv) {
+  char *in, *out;
+  int ret = 0;
+
+  if (argc < 3) return 1;
+  in = malloc(strlen(argv[1]) + 1);
+  strcpy(in, argv[1]);
+  out = malloc(strlen(argv[2]) + 1);
+  strcpy(out, argv[2]);
+  ret = parse_help(in, out);
+  free(in);
+  free(out);
+  return ret;
+}

+ 210 - 0
src/sorthelp.c

@@ -0,0 +1,210 @@
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+typedef struct {
+  int leaf;
+  int hub;
+  char *name;
+  char *txt;
+} cmds;
+
+cmds cmdlist[900];
+int cmdi = 0;
+
+char *replace(char *string, char *oldie, char *newbie)
+{
+  static char newstring[6024] = "";
+  int str_index, newstr_index, oldie_index, end, new_len, old_len, cpy_len;
+  char *c;
+  if (string == NULL) return "";
+  if ((c = (char *) strstr(string, oldie)) == NULL) return string;
+  new_len = strlen(newbie);
+  old_len = strlen(oldie);
+  end = strlen(string) - old_len;
+  oldie_index = c - string;
+  newstr_index = 0;
+  str_index = 0;
+  while(str_index <= end && c != NULL) {
+    cpy_len = oldie_index-str_index;
+    strncpy(newstring + newstr_index, string + str_index, cpy_len);
+    newstr_index += cpy_len;
+    str_index += cpy_len;
+    strcpy(newstring + newstr_index, newbie);
+    newstr_index += new_len;
+    str_index += old_len;
+    if((c = (char *) strstr(string + str_index, oldie)) != NULL)
+     oldie_index = c - string;
+  }
+  strcpy(newstring + newstr_index, string + str_index);
+  return (newstring);
+}
+
+char *step_thru_file(FILE *fd)
+{
+  const int tempBufSize = 6024;
+  char tempBuf[tempBufSize];
+  char *retStr = NULL;
+  if (fd == NULL) {
+    return NULL;
+  }
+  retStr = NULL;
+  while (!feof(fd)) {
+    fgets(tempBuf, tempBufSize, fd);
+    if (!feof(fd)) {
+      if (retStr == NULL) {
+        retStr = malloc(strlen(tempBuf) + 2);
+        strcpy(retStr, tempBuf);
+      } else {
+        retStr = realloc(retStr, strlen(retStr) + strlen(tempBuf));
+        strcat(retStr, tempBuf);
+      }
+      if (retStr[strlen(retStr)-1] == '\n') {
+        retStr[strlen(retStr)-1] = 0;
+        break;
+      }
+    }
+  }
+  return retStr;
+}
+
+char *newsplit(char **rest)
+{
+  register char *o, *r;
+
+  if (!rest)
+    return *rest = "";
+  o = *rest;
+  while (*o == ' ')
+    o++;
+  r = o;
+  while (*o && (*o != ' '))
+    o++;
+  if (*o)
+    *o++ = 0;
+  *rest = o;
+  return r;
+}
+
+int skipline (char *line, int *skip) {
+  static int multi = 0;
+  if ((!strncmp(line, "//", 2))) {
+    (*skip)++;
+  } else if ( (strstr(line, "/*")) && (strstr(line, "*/")) ) {
+    multi = 0;
+    (*skip)++;
+  } else if ( (strstr(line, "/*")) ) {
+    (*skip)++;
+    multi = 1;
+  } else if ( (strstr(line, "*/")) ) {
+    multi = 0;
+  } else {
+    if (!multi) (*skip) = 0;
+  }
+  return (*skip);
+}
+
+
+int my_cmp (const cmds *c1, const cmds *c2)
+{
+  return strcmp (c1->name, c2->name);
+}
+
+int parse_help(char *infile, char *outfile) {
+  FILE *in = NULL, *out = NULL;
+  char *buffer = NULL, my_buf[6024], *fulllist = malloc(1);
+  int skip = 0, line = 0, i = 0, leaf = 0, hub = 0;
+  if (!(in = fopen(infile, "r"))) {
+    printf("Error: Cannot open '%s' for reading\n", infile);
+    return 1;
+  }
+  printf("Sorting help file '%s'", infile);
+  while ((!feof(in)) && ((buffer = step_thru_file(in)) != NULL) ) {
+    line++;
+    if ((*buffer)) {
+      if (strchr(buffer, '\n')) *(char*)strchr(buffer, '\n') = 0;
+      if ((skipline(buffer, &skip))) continue;
+      if (buffer[0] == ':') { //New cmd 
+        char *ifdef = malloc(strlen(buffer) + 1), *p;
+
+        buffer++;
+        ifdef[0] = 0;
+        strcpy(ifdef, buffer);
+        p = strchr(ifdef, ':');
+        *p = 0;
+        if (ifdef && ifdef[0]) {
+          if (!strcasecmp(ifdef, "leaf"))
+            leaf++;
+          else if (!strcasecmp(ifdef, "hub"))
+            hub++;
+        }
+
+        /* finish last command */
+        if (my_buf && my_buf[0]) {
+          my_buf[strlen(my_buf)] = 0;
+          cmdlist[cmdi].txt = malloc(strlen(my_buf) + 1);
+          strcpy(cmdlist[cmdi].txt, my_buf);
+          i++;
+          cmdi++;
+        }
+	/* move on to next cmd now */
+        p = strchr(buffer, ':');
+        p++;
+        if (strcmp(p, "end")) {		/* NEXT CMD */
+          printf(".");
+          my_buf[0] = 0;
+          cmdlist[cmdi].leaf = leaf;
+          cmdlist[cmdi].hub = hub;
+          hub = leaf = 0;
+          cmdlist[cmdi].name = malloc(strlen(p) + 1);
+          strcpy(cmdlist[cmdi].name, p);
+        } else {			/* END */
+          break;
+        }
+      } else {				/* CMD HELP INFO */
+/*        strcat(my_buf, replace(buffer, "\"", "\\\"")); */
+        strcat(my_buf, buffer);
+        strcat(my_buf, "\\n");
+      }
+    }
+    buffer = NULL;
+  }
+  if (in) fclose(in);
+  if (!(out = fopen(outfile, "w"))) {
+    printf("Error: Cannot open '%s' for writing\n", outfile);
+    return 1;
+  }
+  qsort(cmdlist, cmdi, sizeof(cmds), (int (*)()) &my_cmp);
+  for (i = 0; i < cmdi; i++ ) {
+    fulllist = realloc(fulllist, strlen(fulllist) + strlen(cmdlist[i].name) + 2);
+    strcat(fulllist, cmdlist[i].name);
+    strcat(fulllist, " ");
+    fprintf(out, ":");
+    if (cmdlist[i].leaf) fprintf(out, "leaf");
+    else if (cmdlist[i].hub) fprintf(out, "hub");
+    fprintf(out, ":%s\n", cmdlist[i].name);
+    fprintf(out, "%s\n", replace(cmdlist[i].txt, "\\n", "\n"));
+  }
+  fprintf(out, "::end\n");
+  if (out) fclose(out);
+  printf(" Success\n");
+  fulllist[strlen(fulllist)] = 0;
+  printf("Sorted (%d): %s\n", cmdi, fulllist);
+  return 0;
+}
+
+int main(int argc, char **argv) {
+  char *in, *out;
+  int ret = 0;
+
+  if (argc < 3) return 1;
+  in = malloc(strlen(argv[1]) + 1);
+  strcpy(in, argv[1]);
+  out = malloc(strlen(argv[2]) + 1);
+  strcpy(out, argv[2]);
+  ret = parse_help(in, out);
+  free(in);
+  free(out);
+  return ret;
+}

+ 14 - 3
src/stringfix.c

@@ -10,7 +10,9 @@
 #include "conf.h"
 
 #define WTF 16384
-
+#ifdef S_GARBLEHELP
+int help = 0;
+#endif /* S_GARBLEHELP */
 #ifdef S_GARBLESTRINGS
 void garble(char **inptr, char **outptr)
 {
@@ -81,7 +83,12 @@ void garble(char **inptr, char **outptr)
       out += 4;
       *out = 0;
     }
-    sprintf(*outptr, "degarble(%d,\"%s\")", chars, obuf);
+#ifdef S_GARBLEHELP
+    if (help)
+      sprintf(*outptr, "%d,\"%s\"", chars, obuf);
+    else
+#endif /* S_GARBLEHELP */
+      sprintf(*outptr, "degarble(%d,\"%s\")", chars, obuf);
     *outptr += strlen(*outptr);
     in = p + 2;
   } else {
@@ -138,8 +145,12 @@ int main(int argc, char *argv[0])
   int insize;
   char *buf;
 
-  if (argc != 3)
+  if (argc != 3 && argc != 4)
     return 1;
+#ifdef S_GARBLEHELP
+  if (argc == 4)
+    help = 1;
+#endif /* S_GARBLEHELP */
   if (!(f = fopen(argv[1], "r")))
     return 1;
   fseek(f, 0, SEEK_END);