From 9c4c0a1e366b9d932e4ab2ce03a0e80126d93d9b Mon Sep 17 00:00:00 2001 From: Steve Slaven Date: Mon, 4 Nov 2019 22:26:14 -0800 Subject: reorganizing files diff --git a/Config.demo b/Config.demo deleted file mode 100644 index 7eb537e..0000000 --- a/Config.demo +++ /dev/null @@ -1,401 +0,0 @@ -#savefile-version 1 -#("We use an old version number to force creation of sane default #binds") - -#init ={#(@oldxp=0);mapend;#option +compact -info -echo} - -#("Connect to MUME") -#host mume.pvv.org 4242 - -#("Some aliases to use labeled #actions") -#alias ac=#action $0 -#alias <=#action <$0 -#alias \==#action =$0 -#alias >=#action >$0 -#alias +=#action +$0 -#alias -=#action -$0 - -#("Some variables") -#(@emergency = 1, @xp = -0) -#($E_col = "bold") -#($Elbereth = "Grima Slinket Crug Camaro") -#($S_col = "inverse") -#($Sauron = "Homie Aridhol Quigley Mournblade ") - -#("Some useful binds -- they all are on numeric keypad") -#alias ?=#history 1 -#alias ??=#history $0 -#bind * ^[Oj=stat -#bind + ^[Ol=open honeycomb -#bind - ^[Om=close honeycomb -#bind . ^[On=sneak -#bind / ^[Oo=! -#bind 0 ^[Op=? -#bind 1 ^[Oq=hide -#bind 2 ^[Or=s -#bind 3 ^[Os=d -#bind 4 ^[Ot=w -#bind 5 ^[Ou=exits -#bind 6 ^[Ov=e -#bind 7 ^[Ow=look -#bind 8 ^[Ox=n -#bind 9 ^[Oy=u - -#alias plusminus={#bind +=open $1;#bind -=close $1} -#action >+close The $1 is open={#print;plusminus $1} -#action >+open2 The $1 seems to be closed={#print;plusminus $1} -#action >+open1 The $1 is closed={#print;plusminus $1} - -#("Bindings for function keys") -#alias HELP=#alias HELP={remove ring;narrate HELP! EMERGENCY TRANSFER! rings off!} -#bind F01 ^[OP=HELP -#alias wi=change wimpy $0 -#alias wq=wi 100 -#alias ww=wi 1000 -#bind F02 ^[OQ=ww -#bind F03 ^[OR=wq -#bind F04 ^[OS=examine -#bind F05 ^[m=now -#bind F06 ^[[17~=#option compact -#bind F07 ^[[18~=listen all -#bind F08 ^[[19~=listen none -#alias ck=change mood berserk -#alias ca=change mood aggressive -#alias cb=change mood brave -#alias cn=change mood normal -#alias cp=chan mood prudent -#alias cw=change mood wimpy -#bind F09 ^[[20~=cw -#bind F10 ^[[21~=ca -#alias pi={remove sword;draw} -#alias sla={sheath;wield sword} -#bind F11 ^[[23~=pi -#bind F12 ^[[24~=sla - -#("Two nice aliases") -#alias ,e=emote leaves east -#alias emsay=emote says '$0' - -#("Teleporting") -#alias ll=cast 'locate life' $0 -#alias tp=cast 'teleport' $0 -#alias portal=cast 'portal' $0 -#alias go={#if (!*\$0:>2) #(\$0+="tp"); #else #if (\$0:>2=="portal") #(\$0+=" u"); #exe (\$0:>2+" "+\$0:1)} - -#alias loc={#var $talias=$1; #action +tkey; ll $2} -#action >tkey Key: '$1'={#print; #action -tkey; #alias ${talias}=go $1 \$0} - -#alias zharlond=go whicewhile $0 -#alias zmichdelv=go sillelega $0 -#alias zbree=go dorwhimile $0 -#alias 1=zHarlond -#alias 2=zMichDelv -#alias 3=zBree - -#("An emote for enchanting weapons") -#alias ench=cast 'enchant weapon' $0 -#alias enchant={, makes a swift gesture and the $0 starts floating in mid air...;EnchSet +;ench $0} -#alias EnchSet={#action $1EnchSucc;#action $1EnchFail;#action $1BackFire} -#action >-BackFire Your spell backfired!={#print;, starts swearing loudly;EnchSet -} -#action >-EnchFail You lost your concentration!={#print;, frowns and exclaims, 'This cannot be! I failed!';EnchSet -} -#action >-EnchSucc $1 glows blue.={#print;, gestures again, and arcane runes flare on the $1!;EnchSet -} - -#("Keep count of game time and also put it in the prompt") - #mark Game time =underline -#("length of a game tick in milliseconds :") - #(@tick = 61500) -#("this is to synchronize") - #action >+clock ^The current time is $1:$2 $3.={#print;#if (\@0=$1*@tick+$2*@tick/60, \$3=="pm" ^^ $1==12) #(\@0+=12*@tick); #settimer (\@0);settick} -#("print time") - #alias time={#if ((\@1=timer%@tick*60/@tick) < 10) #(\$1=":0"); #else #(\$1=":");#print ("Game time "+%(timer/@tick%24)+\$1+%\@1)} -#("manually set time") - #alias now={#if (*\$1) #(\@0=$1*@tick+0$2*@tick/60); #else #if ((\@0=timer-(\@1=timer%@tick)), \@1 >= @tick/2) #(\@0+=@tick); #settimer (\@0)} -#("put time into the prompt. works for MUME.") -#("change to use appropriate patterns for other MUDs") - #prompt %+empty ^[o\\*]>={#isprompt -1; timeprompt} - #prompt %+full ^[o\\*] [^>]*>={#isprompt -1; timeprompt} - #alias timeprompt={#if ((\@1=timer%@tick*60/@tick) < 10) #(\$1=":0"); #else #(\$1=":");#($prompt=$prompt.1+" "+%(timer/@tick%24)+\$1+%\@1+$prompt.>2)} -#("automatically print time at every tick. type `settick' to activate") -#("or change #alias `now' and #action `clock' to run it") - #alias settick=set tick 0 time - #alias set=#exe ("#in $1 ("+%((-timer-1)%@tick+2)+") {"+\$0:>3+";#in $1 ((-timer-1)%@tick+2)}") - -#("Code to help mapping mazes -- keeps track of room number") -#("using binary numbers -- 1 copper == 1, 2 coppers == 0") -#("doesn't work anymore as you now get `six pile of coins' etc. tough luck.") -#alias d1=drop 1 copper -#alias d2=drop 2 copper -#alias m=#for (\@1=$1; \@1; \@1/=2) #if (\@1&1) d1; #else d2 -#alias mapend={- copper-penny;- pile-of-coins;#(@map=0)} -#alias mapstart={+ copper-penny;+ pile-of-coins;#(@map=0);#in map-timeout (-1) {#print ("Room: "+%@map);#(@map=0)}} -#alias mapcount={#(@map*=2, @map+=$1);#in map-timeout (100)} -#action >-pile-of-coins ^A pile of coins=mapcount 0 -#action >-copper-penny ^One miserable copper penny=mapcount 1 - -#("Spells and fight") -#($self = "Cosmos") -#alias self=#($self=$(0)) -#alias k=kill $0 -#alias f=flee -#alias ff=order followers assist ${self} -#alias fw=order followers assist waran -#alias of=order followers $0 -#alias ol=of k lord -#alias on=of k noble -#alias he={#if (!*$(0)) #($(0)=$self);#send ("cast 'heal' "+$(0))} -#alias in={#if (!*$(0)) #($(0)=$self);#send ("cast 'invisibility' "+$(0))} -#alias rp={#if (!*$(0)) #($(0)=$self);#send ("cast 'remove poison' "+$(0))} -#alias sct={#if (!*$(0)) #($(0)=$self);#send ("cast 'sanctuary' "+$(0))} -#alias str={#if (!*$(0)) #($(0)=$self);#send ("cast 'strength' "+$(0))} -#alias ar={#if (!*$(0)) #($(0)=$self);#send ("cast 'armour' "+$(0))} -#alias sspell=#exe ("#alias $1={#if (!*$(0)) #($(0)=$self);#send (\\"cast '"+\$0:>2+"' \\"+$(0))}") -#alias sto=cast 'store' $0 -#alias stb=sto fireball -#alias stl=sto call lightning -#alias stp=sto teleport -#alias stq=sto earthquake -#alias sty=sto colour spray -#alias bd=cast 'block door' $0 -#alias bl=cast 'blindness' $0 -#alias bob=cast 'breath of briskness' -#alias ch=cast 'charm' $0 -#alias cwt=cast 'control weather' $0 -#alias b=cast 'fireball' $0 -#alias j=cast 'lightning bolt' $0 -#alias li=cast 'call lightning' $0 -#alias sl=cast 'sleep' $0 -#alias sle=sl $0 - -#($target = "assassin") -#alias .=$0 ${target} -#alias bl.=bl ${target} -#alias b.=b ${target} -#alias j.=j ${target} -#alias k.=k ${target} -#alias li.=li ${target} -#alias sl.=sl ${target} -#alias blo=bl orc -#alias blt=bl troll -#alias jo=j orc -#alias jt=j troll -#alias ka=k assassin -#alias kw=k wolf - -#alias tr=track $0 -#alias to=tr orc -#alias tt=tr troll -#alias tr.=tr ${target} - -#alias bs=backstab $0 -#alias bs.=bs ${target} -#alias nn=$0 Necromancer - -#("Misc stuff") -#($water = "skin") -#alias dw=drink water -#alias dk=drink ${water} -#alias lk=look in ${water} -#alias po=pour water ${water} -#alias sk=sip ${water} -#alias cv=cast 'create water' $0 -#alias cvk=cv ${water} - -#($pack = "backpack") -#alias gp=get $0 ${pack} -#alias pp=put $0 ${pack} -#alias lp=look in ${pack} - -#($light = "lantern") -#alias hl=wear ${light} -#alias rl=remove ${light} -#alias vl=cover ${light} -#alias ul=uncover ${light} -#alias cl=cast 'create light' $0 -#alias cll=cl ${light} - -#alias lg=look in moneybag -#alias gg=get $1 gold moneybag -#alias pg=put $1 gold moneybag -#alias ggj=get ruby moneybag -#alias pgj=put ruby moneybag - -#alias cf=cast 'create food' -#alias gf=get food -#alias ef=eat food -#alias food={cf;gf;ef} - -#alias bc=#if (*$(1)) butcher $1.corpse; #else butcher corpse -#alias lc=#if (*$(1)) look in $1.corpse; #else look in corpse -#alias ga=#if (*$(1)) get all $1.corpse; #else get all corpse -#alias gc=#if (*$(1)) get coins $1.corpse; #else get coins corpse -#alias gx=#if (*$(2)) get $1 $2.corpse; #else get $1 corpse -#alias xc={hl;gc $0;rl} -#alias gl=get all -#alias glc={get all.coins;get all.copper} -#action >-greed is dead! R.I.P.={#print;gc} - -#alias ldh=#if (*($1)) lead $1.horse; #else lead horse -#alias rdh=#if (*$(1)) ride $0.horse; #else ride horse -#alias ldp=#if (*($1)) lead $1.pony; #else lead pony -#alias rdp=#if (*$(1)) ride $1.pony; #else ride pony -#alias ds=dismount -#action >-Ct ^Clip-clop...the riding horse= -#action >-Cp ^Clip-clop...a pony= -#action >-Cb ^The Horse= -#action >-At ^A riding horse= -#action >-Ap ^A cute and docile pony= -#action >-Ab ^A beautiful horse= - -#alias re=t Alsbreth $0 -#action >+reply ^$1 tells you '={#print;#alias re=t $1 \$0} -#action >-xeval ^$1 tells you 'calc &2'={#print;#alias re=t $1 \$0;if (\$2?";" || \$2?"$" || \$2?"@") {tell $1 *grin*;#()}; #else #send ("t $1 "+%($2))} - -#alias rep={+ report;score} -#action >-report $1 hit, $2 mana and $3 moves.={#print;, has $1 hit, $2 mana and $3 moves.;- report} -#alias rd={#opt +speedwalk;4ensenwdnenne;#opt -speedwalk} -#alias fd={#opt +speedwalk;wsswsuwwdww;#opt -speedwalk} - -#alias lr={look n;look s;look e;look w;look u;look d} -#alias x=#alias $0 -#alias kk=#bind $0 -#alias l=examine $0 -#alias gr=group $0 -#alias le=group ${self} -#alias mm=#mark $0 -#alias sa=#save -#alias se=search $0 -#alias mo=help month -#alias ng=, nods gravely. -#alias nh=, nods happily. -#alias ns=, nods sadly. -#alias p=' open -#alias rf=rem staff -#alias rn=read next -#alias rs=rem stone -#alias rt=rem ticket -#alias sms=, smiles sadly. -#alias ss=spam -#alias fade=emote quickly wraps in his cloak and vanishes. -#alias matz=emote unwraps his cloak and materializes. -#alias fun={fade;$0;matz} -#alias tele={fade;tp $0;matz} -#alias title=chan title the Hungry -#alias tm=t Meryaten $0 -#alias wavall=, waves good-bye to you. -#alias wf=weather fog -#alias wg=weather global -#alias wh=where human -#alias wk={wake;st} -#alias wl=weather local -#alias wr={wake;rest} -#alias grz=na --- === *** C O N G R A T U L A T I O N S !! *** === --- -#alias lin={#lines $0;chan height $0} -#alias lv=na --- === *** L E V E L !! *** === --- -#alias macintosh={#bind ~home ^[OH=eq;#bind ~~home ^[O@=eq;#bind ~F01 ^[OP=#key F01;#bind ~F02 ^[OQ=ww;#bind ~F03 ^[OR=wq;#bind ~F04 ^[OS=examine;#bind ~F05 ^[m=now;#bind ~+ ^[Ok=open patch;#bind ~~F05 ^[[16~=now} - - -#alias listadd=#for (\@0=1; *$$1:\@0 && $$1:\@0!=*13; \@0++) #exe ("#mark "+$$1:\\@0+"="+$$2)} -#alias listclear={#for (\@0=1; *$$1:\@0 && $$1:\@0!=*13; \@0++) #exe ("#mark "+$$1:\\@0+"=");#($$1="")} -#alias elb={listclear Elbereth;#ac +elbereth;t elbereth list} -#alias elblist=#print ("Elf Enemies: "+$Elbereth) -#alias sauronclean={#for (\@0=1; *\$-1:\@0; \@0++) #($-1=$-1:<\\@0<.2+" "+$-1:>(\\@0+1))} -#alias pk={listclear Sauron;#ac +sauron0;#ac +sauron1;t sauron list} -#alias pklist=#print ("PKillers: "+$Sauron) -#action >-elbereth ^Elbereth Gilthoniel tells you 'Elf Enemies: &1'={#($Elbereth=\$1);listadd -5 -6;#ac -elbereth;#print} -#action >-sauron2 ^ &1more to come.'={#($Sauron+=$-1=\$1<.3+",");sauronclean;#ac -sauron2;listadd -3 -4;#($Sauron+=$-1, $-1="");#print} -#action >-sauron1 ^Sauron tells you 'My Envoys: &1={#($Sauron=$-1=\$1<.2+",");sauronclean;#ac -sauron0;#ac -sauron1;#ac +sauron2;listadd -3 -4;#($Sauron=$-1);#print} -#action >-sauron0 ^Sauron tells you 'My Envoys: &1more to come.'={#($-1=\$1<.3+",");sauronclean;#ac -sauron0;#ac -sauron1;listadd -3 -4;#($Sauron=$-1);#print} - -#action >+disappear disappears into nothing.= -#action >+exp ^Needed: $1 xp={#print;#if (\@1=-$1, !@oldxp) #(@oldxp=@xp=\@1);#print ("Exp: "+%(\@1-@xp)+" from last score, "+%(\@1-@oldxp)+" from start of session");#(@xp=\@1)} -#action >+hunt *&1* leaves $2={#mark $2=bold;#print;#mark $2=} -#action >+save Saving=#print ("---> "+\$0+" <---") -#action >-someone ^Your blood freezes as you hear $1 =#if (\$1!="The" && \$1!="the" && \$1!="A" && \$1!="a" && \$1!="An" && \$1!="an") #print -#action >+Willow A sudden drowsiness overcomes you, you yawn and...={#print;wk} - -#mark sitting=bold -#mark resting=bold -#mark sleeping=bold -#mark Room: =bold -#mark tells you=underline -#mark YOU=bold red -#mark is dead! R.I.P.=bold -#mark glows with a bright light!=bold -#mark victim shocked=bold -#mark You feel a watchful eye=bold - -#mark The corpse of *&*=bold -#mark *an Orc*=bold yellow on red -#mark *$ the Orc*=bold yellow on red -#mark *a Troll*=bold cyan on yellow -#mark *$ the Troll*=bold cyan on yellow -#mark *a Human*=blue on cyan -#mark *$ the Human*=blue on cyan -#mark *$ the Numenorean*=blue on cyan -#mark *$ the Black Numenorean*=blue on cyan -#mark *a Dwarf*=bold cyan on magenta -#mark *$ the Dwarf*=bold cyan on magenta -#mark *an Elf*=blue on green -#mark *$ the Elf*=blue on green -#mark *a Hobbit*=bold yellow on blue -#mark *$ the Hobbit*=bold yellow on blue -#alias mumecolor={change col all default;change col shout bold yellow;change col damage bold red;change col hit bold blue} -#alias nocolors={#mark *an Orc*=inverse;#mark *$ the Orc*=inverse;#mark *a Troll*=inverse;#mark *$ the Troll*=inverse;#mark *a Human*=inverse;#mark *$ the Human*=inverse;#mark *$ the Numenorean*=inverse;#mark *$ the Black Numenorean*=inverse;#mark *a Dwarf*=inverse;#mark *$ the Dwarf*=inverse;#mark *an Elf*=inverse;#mark *$ the Elf*=inverse;#mark *a Hobbit*=inverse;#mark *$ the Hobbit*=inverse} -#alias colors={#mark *an Orc*=bold yellow on red;#mark *$ the Orc*=bold yellow on red;#mark *a Troll*=bold cyan on yellow;#mark *$ the Troll*=bold cyan on yellow;#mark *a Human*=blue on cyan;#mark *$ the Human*=blue on cyan;#mark *$ the Numenorean*=blue on cyan;#mark *$ the Black Numenorean*=blue on cyan;#mark *a Dwarf*=bold cyan on magenta;#mark *$ the Dwarf*=bold cyan on magenta;#mark *an Elf*=blue on green;#mark *$ the Elf*=blue on green;#mark *a Hobbit*=bold yellow on blue;#mark *$ the Hobbit*=bold yellow on blue} - -#bind >del ^[[P=&del-char-right -#bind A-bs ^[[071q=&clear-line -#bind A-del ^[[M=&kill-to-eol -#bind A-left ^[[160q=&begin-of-line -#bind A-return ^[[100q=&to-history -#bind A-right ^[[169q=&end-of-line -#bind M-B ^[b=&prev-word -#bind M-D ^[d=&del-word-right -#bind M-F ^[f=&next-word -#bind M-backsp ^[=&del-word-left -#bind M-bs ^[^H=&del-word-left -#bind M-tab ^[^I=&complete-line -#bind ^2 ^[[186q={s;hide} -#bind ^3 ^[[194q={d;hide} -#bind ^4 ^[[174q={w;hide} -#bind ^6 ^[[192q={e;hide} -#bind ^8 ^[[182q={n;hide} -#bind ^9 ^[[190q={u;hide} -#bind ^A ^A=&begin-of-line -#bind ^B ^B=&prev-char -#bind ^D ^D=&del-char-right -#bind ^E ^E=&end-of-line -#bind ^F ^F=&next-char -#bind ^K ^K=&kill-to-eol -#bind ^L ^L=&redraw-line -#bind ^N ^N=&next-line -#bind ^P ^P=&prev-line -#bind ^Q ^Q=&clear-line -#bind ^S ^S=#capture -#bind ^T ^T=&transpose -#bind ^V ^V=#stop -#bind ^W ^W=&to-history -#bind ^X ^X=#exe ("#capture emergency"+%@emergency++) -#bind ^Z ^Z=&suspend -#bind ^bs ^[[085q=&del-word-left -#bind ^del ^[[142q=&del-word-right -#bind ^left ^[[159q=&prev-word -#bind ^right ^[[168q=&next-word -#bind ^tab ^[[072q=&complete-line -#bind backsp =&del-char-left -#bind bs ^H=&del-char-left -#bind del ^[[3~=jo -#bind down ^[OB=&next-line -#bind end ^[[4~=jt -#bind enter ^[OM=flee -#bind home ^[[1~=stat -#bind ins ^[[2~=time -#bind left ^[OD=&prev-char -#bind lf ^J=&enter-line -#bind pgdwn ^[[6~=jh -#bind pgup ^[[5~=score -#bind return ^M=&enter-line -#bind right ^[OC=&next-char -#bind tab ^I=&complete-word -#bind up ^[OA=&prev-line -#bind ~^Q ^[~q=&clear-line -#bind ~^S ^[~s=#capture diff --git a/Hacking b/Hacking deleted file mode 100644 index 2063884..0000000 --- a/Hacking +++ /dev/null @@ -1,48 +0,0 @@ -Some notices for you who would like to hack the source in order to -improve powwow, fix bugs or just have fun: - -* Try not to run large chunks of existing code through your C - beautifier if the indentation style doesn't please you. This will - only greatly confuse the original author (if he can't recognize and - maintain his own code, who can?). If you write any new functions, - ok, use your own style as long as it's clear and consistent. - -* Document your changes! A brief report of changes in the Changelog file is - absolutely necessary. So is updating the doc files (powwow.doc, powwow_help - and README) Also, sending an e-mail to the code author/mantainer documenting - your changes will be appreciated. - -Report from cancan's "Hacking": - -******************************************************************************* - Remember, that although I (Yorick) am the original author, I don't dictate - the code; it is explicitly in the public domain. I am merely trying to keep - some kind of order; if versions are permitted to diverge, they are very - painful to merge later. Also, different version branches are very confusing, - and makes it hard to define the "latest and best" version. Please try to - synchronize your hacks into one - the latest - version! -******************************************************************************* - -Well, Yorick refused to merge Powwow and Cancan, so now they both exist -indipendently... Now I (Cosmos) say exactly the same thing Yorick said -('do not let version diverge'), but I fear this will happen again :( -Only difference is that powwow is now GPL-ed and not Public Domain. -I hope this will help a little. - -* Rather than you just distributing your hacked version, I _really_ prefer you - to send back to me your changes, so that I can put them into the following - release. You lose nothing doing so, since even in case I refuse to include - your changes you can still distribute your version if you absolutely want :) - - Even if you decide to distribute such a modified version, please - make it clear that it is not the original version, use a version number - which can't be confused with an original version, and put your name - in Changelog and in POWWOW_VERSION. - -* I've tried to make the code obey the following rules for modularity: - - All exported things from one .c file is declared in its .h file. - - Conversely, symbols are imported by #including .h files, - not by local extern declarations. - - Everything that can be static should be (this is not quite true yet) - - Please obey these rules (or improve them). diff --git a/Makefile.am b/Makefile.am index c1e37e6..929b50b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,43 +1,3 @@ -# Default help file directory -AM_CPPFLAGS=-D_XOPEN_SOURCE=700 -DPOWWOW_DIR=\"$(pkgdatadir)\" \ - -DPLUGIN_DIR=\"$(plugindir)\" +SUBDIRS=src doc man -bin_PROGRAMS = powwow powwow-muc powwow-movieplay -powwow_SOURCES = beam.c cmd.c log.c edit.c cmd2.c eval.c \ - utils.c main.c tcp.c list.c map.c tty.c \ - ptr.c -powwow_LDFLAGS = @dl_ldflags@ -powwowdir = $(pkgincludedir) -powwow_HEADERS = beam.h cmd.h log.h edit.h cmd2.h eval.h \ - utils.h main.h tcp.h list.h map.h tty.h \ - ptr.h defines.h feature/regex.h -powwow_muc_SOURCES = powwow-muc.c -powwow_movieplay_SOURCES = powwow-movieplay.c - -install-exec-hook: - (cd $(DESTDIR)$(bindir) && \ - rm -f powwow-movie2ascii$(EXEEXT) && \ - $(LN_S) powwow-movieplay$(EXEEXT) powwow-movie2ascii$(EXEEXT)) - -noinst_PROGRAMS = catrw follow -follow_SOURCES = follow.c -catrw_SOURCES = catrw.c - -man_MANS = powwow.6 -pkgdata_DATA = powwow.doc powwow.help COPYING -EXTRA_DIST = README README.follow README.term README.modules \ - TODO Hacking Config.demo \ - powwow.doc powwow.help powwow.6 plugtest.c debian/rules \ - debian/control debian/changelog powwow.6.utf-8 - -CLEANFILES = powwow.6 - -powwow.6: powwow.6.utf-8 -if MAN_PAGE_ENCODING_IS_UTF_8 - cp $< $@ -else - iconv -f UTF-8 -t "$(MAN_PAGE_ENCODING)" < $< > $@ -endif - -plugtest.so: plugtest.c - gcc -shared -o plugtest.so plugtest.c +pkgdata_DATA = COPYING diff --git a/README.follow b/README.follow deleted file mode 100644 index 9ef923b..0000000 --- a/README.follow +++ /dev/null @@ -1,18 +0,0 @@ - - follow 0.1 README - -This mini-program is mainly intended for debugging powwow. -It is completely standalone, and needs to be called with a file-name -(example: "follow mycapture"). -Hitting ESC or ^C quits "follow", hitting RETURN prints the next line -of the file, any other key prints the next character (only one) of the file - -I know it is a _stupid_ program, but it is useful if you find screen-related -bugs in powwow, i.e. powwow does not print some lines or prints something -wrong. -In that case, if you redirect powwow output to a file and reproduce the bug, -you can use "follow your-filename" to examine the file and see what is wrong - -How to send a copy of powwow output to a file: -powwow your-arguments | tee your-filename - diff --git a/README.modules b/README.modules deleted file mode 100644 index 8f8eae8..0000000 --- a/README.modules +++ /dev/null @@ -1,34 +0,0 @@ -There is a sample module included to test module support. To build -it run: - -make plugtest.so - -from your powwow source directory. This will build a test plugin that -can be loaded using: - -#module plugtest - -If it works, when you do "#help" you will see a new command called -"#plugtest" that is listed. It will just echo the arguments back -to the terminal. This is the minimum required to make a functional -module for powwow 1.2.7 and later. - -Modules are loaded using the libdl interface with RTLD_LAZY so that -symbol resolution is deferred until as late as possible. This means -that you should test any commands you create since just because a -module loads, that doesn't mean that there are no undefined references. - -The basic requirements for a module are: - -1) define a cmdstruct with your command name, help text, and function to - call -2) add your cmdstruct to the global command list via cmd_add_command in - your powwow_init function. You can also initialize anything else you - need here, it will be called once at module load time. -3) perform whatever extentions you want at runtime in your custom functions - -For a more complete example, you should look at the powwow-perl package -which creates the bindings from powwow using the #perl command and allows -access to powwow internals from perl via the powwow:: namespace in perl. -It also includes autoconf support and is probably the most complete -module for powwow currently. diff --git a/README.term b/README.term deleted file mode 100644 index 6dfad6c..0000000 --- a/README.term +++ /dev/null @@ -1,26 +0,0 @@ - -'term' was written by Michael O'Reilly. - -If you don't know what term is, then you can ignore this file. :) - -Otherwise, if you want term support, read on... - -You need to edit the Makefile and edit the TERMDIR variable to the -directory of your term source and client.a. - -If you don't want 'term' compression on for powwow, take out the --DTERM_COMPRESS in the CFLAGS variable in the Makefile. - -Edit any of the other variables you find neccessary. - -Read the original README for information you might need to know. - -Then do "make termpowwow" - -Note: Unfortunately one of the setbacks of termpowwow is that it won't -detect unknown hosts, nor will it detect if the host refused the connection. -If you don't get any output from a host after a few moments, then you'll -have to ^C. I don't think there's any way around this. - -(note - term support does not allow multiple sessions...) - diff --git a/TODO b/TODO deleted file mode 100644 index 6112fe9..0000000 --- a/TODO +++ /dev/null @@ -1,46 +0,0 @@ --*-indented-text-*- - -From the authors of cancan: - - THINGS TO DO - Cancan should know the codes of at least some keys - by looking in the TERMCAP. (especially control keys are trivial!) - - - Run (a)lint! - - - There seems to be problems with running cancan on exotic terminals. - This defeats the very purpose of all the carefully terminal- - independent code with TERMCAP, and is generally embarrassing. - - - more system calls should be checked for EINTR for sysV unices - - - negotiate the telnet BINARY option - - - support the telnet IAC GA command to identify prompts - (But what for? Prompts have to be identified as such in the absence - of GA anyway, and there doesn't seem to be a need for it right - now.) - - - implement reverse-incremental-search (like ^R in GNU Readline) - - KNOWN BUGS - - Deleting more than one character with deletechar() might leave - ugly traces on lines that are entirely deleted. - - - using #hilite with coloured background might have effects beyond the - input line, depending on the terminal (don't do that, then :-) - - FUTURE POSSIBLE IMPROVEMENTS - Do some more for portability (clean up the terminal mess) - The source should be more modular - Filtering output through shell command (grep etc) - - -THINGS TO DO/PLANNED IMPROVEMENTS on powwow: - - make a better #help - - add termcap "end of bold", "end of blink" etc. - - If you have ideas, suggestions or whatever, e-mail them to me - (max@Linuz.sns.it) and if I find them interesting, I will add them. - Also, I am trying to keep powwow updated with further releases - of cancan. diff --git a/beam.c b/beam.c deleted file mode 100644 index cff503d..0000000 --- a/beam.c +++ /dev/null @@ -1,480 +0,0 @@ -/* - * beam.c -- code to beam texts across the TCP connection following a - * special protocol - * - * Copyright (C) 1998 by Massimiliano Ghilardi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "defines.h" -#include "main.h" -#include "utils.h" -#include "beam.h" -#include "map.h" -#include "tcp.h" -#include "tty.h" -#include "edit.h" -#include "eval.h" - -editsess *edit_sess; /* head of session list */ - -char edit_start[BUFSIZE]; /* messages to send to host when starting */ -char edit_end[BUFSIZE]; /* or leaving editing sessions */ - -static void write_message(char *s) -{ - clear_input_line(opt_compact); - if (!opt_compact) { - tty_putc('\n'); - status(1); - } - tty_puts(s); -} - -/* - * Process editing protocol message from buf with len remaining chars. - * Return number of characters used in the message. - */ -int process_message(char *buf, int len) -{ - int msglen, i, l, used; - char *text, *from, *to; - char msg[BUFSIZE]; - int got_iac; - - status(1); - - msglen = atoi(buf + 1); - for (i = 1; i < len && isdigit(buf[i]); i++) - ; - - if (i < len && buf[i] == '\r') i++; - if (i >= len || buf[i] != '\n') { - write_message ("#warning: MPI protocol error\n"); - return 0; - } - - l = len - ++i; - - text = (char *)malloc(msglen); - if (!text) { - errmsg("malloc"); - return i; - } - - /* only doing trivial (IAC IAC) processing; this should all be - * rewritten */ - got_iac = 0; - from = buf + i; - to = text; - for (i = 0; i < l && (to - text) < msglen; ++i) { - if (got_iac) { - if (*from != (char)IAC) - errmsg("got IAC in MPI message; treating as IAC IAC"); - else - ++from; - got_iac = 0; - } else { - got_iac = (*to++ = *from++) == (char)IAC; - } - } - i = to - text; - used = from - buf; - - while (i < msglen) { - /* read() might block here, but it should't be long */ - while ((l = read(tcp_fd, text + i, msglen - i)) == -1 && errno == EINTR) - ; - if (l == -1) { - errmsg("read message from socket"); - return used > len ? len : used; - } - from = to = text + i; - for (i = 0; i < l; ++i) { - if (got_iac) { - if (*from != (char)IAC) - errmsg("got IAC in MPI message; treating as IAC IAC"); - else - ++from; - got_iac = 0; - } else { - got_iac = (*to++ = *from++) == (char)IAC; - } - } - i = to - text; - tty_printf("\rgot %d chars out of %d", i, msglen); - tty_flush(); - } - tty_printf("\rread all %d chars.%s\n", msglen, tty_clreoln); - - switch(*buf) { - case 'E': - message_edit(text, msglen, 0, 0); - break; - case 'V': - message_edit(text, msglen, 1, 0); - break; - default: - sprintf(msg, "#warning: received a funny message (0x%x)\n", *buf); - write_message(msg); - free(text); - break; - } - - return used; -} - -/* - * abort an editing session when - * the MUD socket it comes from gets closed - */ -void abort_edit_fd(int fd) -{ - editsess **sp, *p; - - if (fd < 0) - return; - - for (sp = &edit_sess; *sp; sp = &(*sp)->next) { - p = *sp; - if (p->fd != fd) - continue; - - if (kill(p->pid, SIGKILL) < 0) { /* Editicide */ - errmsg("kill editor child"); - continue; - } - - PRINTF("#aborted \"%s\" (%u)\n", p->descr, p->key); - p->cancel = 1; - } -} - -/* - * cancel an editing session; does not free anything - * (the child death signal handler will remove the session from the list) - */ -void cancel_edit(editsess *sp) -{ - char buf[BUFSIZE]; - char keystr[16]; - - if (kill(sp->pid, SIGKILL) < 0) { /* Editicide */ - errmsg("kill editor child"); - return; - } - PRINTF("#killed \"%s\" (%u)\n", sp->descr, sp->key); - sprintf(keystr, "C%u\n", sp->key); - sprintf(buf, "%sE%d\n%s", MPI, (int)strlen(keystr), keystr); - tcp_write(sp->fd, buf); - sp->cancel = 1; -} - -static ssize_t read_file(int fd, void *buf, size_t count) -{ - size_t result = 0; - while (count > 0) { - int r; - do { - r = read(fd, buf, count); - } while (r < 0 && errno == EINTR); - if (r < 0) - return -1; - if (r == 0) - break; - result += r; - buf += r; - count -= r; - } - return result; -} - -/* - * send back edited text to server, or cancel the editing session if the - * file was not changed. - */ -static void finish_edit(editsess *sp) -{ - char *realtext = NULL, *text; - int fd, txtlen, hdrlen; - struct stat sbuf; - char keystr[16], buf[256], hdr[65]; - - if (sp->fd == -1) - goto cleanup_file; - - fd = open(sp->file, O_RDONLY); - if (fd == -1) { - errmsg("open edit file"); - goto cleanup_file; - } - if (fstat(fd, &sbuf) == -1) { - errmsg("fstat edit file"); - goto cleanup_fd; - } - - txtlen = sbuf.st_size; - - if (!sp->cancel && (sbuf.st_mtime > sp->ctime || txtlen != sp->oldsize)) { - /* file was changed by editor: send back result to server */ - - realtext = (char *)malloc(txtlen + 65); - /* +1 is for possible LF, +64 for header */ - if (!realtext) { - errmsg("malloc"); - goto cleanup_fd; - } - - text = realtext + 64; - txtlen = read_file(fd, text, txtlen); - if (txtlen < 0) - goto cleanup_text; - - if (txtlen && text[txtlen - 1] != '\n') { - /* If the last line isn't LF-terminated, add an LF; - * however, empty files must remain empty */ - text[txtlen] = '\n'; - txtlen++; - } - - sprintf(keystr, "E%u\n", sp->key); - - sprintf(hdr, "%sE%d\n%s", MPI, (int)(txtlen + strlen(keystr)), keystr); - - text -= (hdrlen = strlen(hdr)); - memcpy(text, hdr, hdrlen); - - /* text[hdrlen + txtlen] = '\0'; */ - tcp_write_escape_iac(sp->fd, text, hdrlen + txtlen); - - sprintf(buf, "#completed session %s (%u)\n", sp->descr, sp->key); - write_message(buf); - } else { - /* file wasn't changed, cancel editing session */ - sprintf(keystr, "C%u\n", sp->key); - sprintf(hdr, "%sE%d\n%s", MPI, (int) strlen(keystr), keystr); - - tcp_raw_write(sp->fd, hdr, strlen(hdr)); - - sprintf(buf, "#cancelled session %s (%u)\n", sp->descr, sp->key); - write_message(buf); - } - -cleanup_text: if (realtext) free(realtext); -cleanup_fd: close(fd); -cleanup_file: if (unlink(sp->file) < 0) - errmsg("unlink edit file"); -} - -/* - * start an editing session: process the EDIT/VIEW message - * if view == 1, text will be viewed, else edited - */ -void message_edit(char *text, int msglen, char view, char builtin) -{ - char tmpname[BUFSIZE], command_str[BUFSIZE], buf[BUFSIZE]; - char *errdesc = "#warning: protocol error (message_edit, no %s)\n"; - int tmpfd, i, childpid; - unsigned int key; - editsess *s; - char *editor, *descr; - char *args[4]; - int waitforeditor; - - status(1); - - args[0] = "/bin/sh"; args[1] = "-c"; - args[2] = command_str; args[3] = 0; - - if (view) { - key = (unsigned int)-1; - i = 0; - } else { - if (msglen < 1 || text[0] != 'M') { - tty_printf(errdesc, "M"); - free(text); - return; - } - for (i = 1; i < msglen && isdigit(text[i]); i++) - ; - if (text[i++] != '\n' || i >= msglen) { - tty_printf(errdesc, "\\n"); - free(text); - return; - } - key = strtoul(text + 1, NULL, 10); - } - descr = text + i; - while (i < msglen && text[i] != '\n') i++; - if (i >= msglen) { - tty_printf(errdesc, "desc"); - free(text); - return; - } - text[i++] = '\0'; - - sprintf(tmpname, "/tmp/powwow.%u.%d%d", key, getpid(), abs(rand()) >> 8); - if ((tmpfd = open(tmpname, O_WRONLY | O_CREAT, 0600)) < 0) { - errmsg("create temp edit file"); - free(text); - return; - } - if (write(tmpfd, text + i, msglen - i) < msglen - i) { - errmsg("write to temp edit file"); - free(text); - close(tmpfd); - return; - } - close(tmpfd); - - s = (editsess*)malloc(sizeof(editsess)); - if (!s) { - errmsg("malloc"); - return; - } - s->ctime = time((time_t*)NULL); - s->oldsize = msglen - i; - s->key = key; - s->fd = (view || builtin) ? -1 : tcp_fd; /* MUME doesn't expect a reply. */ - s->cancel = 0; - s->descr = my_strdup(descr); - s->file = my_strdup(tmpname); - free(text); - - /* send a edit_start message (if wanted) */ - if ((!edit_sess) && (*edit_start)) { - error = 0; - parse_instruction(edit_start, 0, 0, 1); - history_done = 0; - } - - if (view) { - if (!(editor = getenv("POWWOWPAGER")) && !(editor = getenv("PAGER"))) - editor = "more"; - } else { - if (!(editor = getenv("POWWOWEDITOR")) && !(editor = getenv("EDITOR"))) - editor = "emacs"; - } - - if (editor[0] == '&') { - waitforeditor = 0; - editor++; - } else - waitforeditor = 1; - - if (waitforeditor) { - tty_quit(); - /* ignore SIGINT since interrupting the child would interrupt us too, - if we are in the same tty group */ - sig_permanent(SIGINT, SIG_IGN); - sig_permanent(SIGCHLD, SIG_DFL); - } - - switch(childpid = fork()) { /* let's get schizophrenic */ - case 0: - sprintf(command_str, "%s %s", editor, s->file); - sprintf(buf, "TITLE=%s", s->descr); - putenv(buf); - /* setenv("TITLE", s->descr, 1);*/ - execvp((char *)args[0], (char **)args); - syserr("execve"); - break; - case -1: - errmsg("fork"); - free(s->descr); - free(s->file); - free(s); - return; - } - s->pid = childpid; - if (waitforeditor) { - while ((i = waitpid(childpid, (int*)NULL, 0)) == -1 && errno == EINTR) - ; - - signal_start(); /* reset SIGINT and SIGCHLD handlers */ - tty_start(); - - if (s->fd != -1) { - tty_gotoxy(0, lines - 1); - tty_putc('\n'); - } - - if (i == -1) - errmsg("waitpid"); - else - finish_edit(s); - - free(s->descr); - free(s->file); - if (i != -1 && !edit_sess && *edit_end) { - error = 0; - parse_instruction(edit_end, 0, 0, 1); - history_done = 0; - } - - free(s); - - } else { - s->next = edit_sess; - edit_sess = s; - } -} - -/* - * Our child has snuffed it. check if it was an editor, and update the - * session list if that is the case. - */ -void sig_chld_bottomhalf(void) -{ - int fd, pid, ret; - editsess **sp, *p; - - /* GH: while() instead of just one check */ - while ((pid = waitpid(-1, &ret, WNOHANG)) > 0) { - - /* GH: check for WIFSTOPPED unnecessary since no */ - /* WUNTRACED to waitpid() */ - for (sp = &edit_sess; *sp && (*sp)->pid != pid; sp = &(*sp)->next) - ; - if (*sp) { - finish_edit(*sp); - p = *sp; *sp = p->next; - fd = p->fd; - free(p->descr); - free(p->file); - free(p); - - /* GH: only send message if found matching session */ - - /* send the edit_end message if this is the last editor... */ - if ((!edit_sess) && (*edit_end)) { - int otcp_fd = tcp_fd; /* backup current socket fd */ - tcp_fd = fd; - error = 0; - parse_instruction(edit_end, 0, 0, 1); - history_done = 0; - tcp_fd = otcp_fd; - } - } - } -} - diff --git a/beam.h b/beam.h deleted file mode 100644 index 84189d9..0000000 --- a/beam.h +++ /dev/null @@ -1,17 +0,0 @@ -/* public things from beam.c */ - -#ifndef _BEAM_H_ -#define _BEAM_H_ - -int process_message(char *buf, int len); -void cancel_edit(editsess *sp); -void abort_edit_fd(int fd); -void message_edit(char *text, int msglen, char view, char builtin); -void sig_chld_bottomhalf(void); - -extern char edit_start[]; -extern char edit_end[]; -extern editsess *edit_sess; - -#endif /* _BEAM_H_ */ - diff --git a/catrw.c b/catrw.c deleted file mode 100644 index 40d06e3..0000000 --- a/catrw.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * catrw.c -- open a file with O_RDWR and print it. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ -#include -#include -#include -#include -#include -#include - -#define BUFSIZE 4096 -char buf[BUFSIZE]; - -void catrw(int fd) { - int i; - for (;;) { - while ( (i = read(fd, buf, BUFSIZE)) < 0 && errno == EINTR) - ; - if (i <= 0) - break; - write(1, buf, i); - } -} - -int main(int argc, char *argv[]) { - int fd; - if (argc == 1) - catrw(0); - else { - while (--argc && (fd = open(*++argv, O_RDWR))) { - catrw(fd); - close(fd); - } - } - return 0; -} - diff --git a/cmd.c b/cmd.c deleted file mode 100644 index f11dd53..0000000 --- a/cmd.c +++ /dev/null @@ -1,2713 +0,0 @@ -/* - * cmd.c -- functions for powwow's built-in #commands - * - * (created: Finn Arne Gangstad (Ilie), Dec 25th, 1993) - * - * Copyright (C) 1998 by Massimiliano Ghilardi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_LIBDL -#include -#endif - -#include "defines.h" -#include "main.h" -#include "utils.h" -#include "beam.h" -#include "cmd.h" -#include "cmd2.h" -#include "edit.h" -#include "list.h" -#include "map.h" -#include "tcp.h" -#include "tty.h" -#include "eval.h" -#include "log.h" - -/* local function declarations */ -#define F(name) cmd_ ## name(char *arg) - -static void F(help), F(shell), F(action), F(add), - F(addstatic), F(alias), F(at), F(beep), F(bind), - F(cancel), F(capture), F(clear), F(connect), F(cpu), - F(do), F(delim), F(edit), F(emulate), F(exe), - F(file), F(for), F(hilite), F(history), F(host), - F(identify), F(if), F(in), F(init), F(isprompt), - F(key), F(keyedit), - F(load), F(map), F(mark), F(movie), - F(net), F(nice), F(option), - F(prefix), F(print), F(prompt), F(put), - F(qui), F(quit), F(quote), - F(rawsend), F(rawprint), F(rebind), F(rebindall), F(rebindALL), - F(record), F(request), F(reset), F(retrace), - F(save), F(send), F(setvar), F(snoop), F(spawn), F(stop), - F(time), F(var), F(ver), F(while), F(write), - F(eval), F(zap), F(module), F(group), F(speedwalk), F(groupdelim); - -#ifdef BUG_TELNET -static void F(color); -#endif - -#undef F - -/* This must be init'd now at runtime */ -cmdstruct *commands = NULL; - -#define C(name, func, help) { NULL, name, help, func, NULL } - -/* The builtin commands */ -cmdstruct default_commands[] = -{ - C("help", cmd_help, - "[keys|math|command]\tthis text, or help on specified topic"), - C("17", (function_str)0, - "command\t\t\trepeat \"command\" 17 times"), -#ifndef NO_SHELL - C("!", cmd_shell, - "shell-command\t\texecute a shell command using /bin/sh"), -#endif - C("action", cmd_action, - "[[<|=|>|%][+|-]name] [{pattern|(expression)} [=[command]]]\n" - "\t\t\t\tdelete/list/define actions"), - C("add", cmd_add, - "{string|(expr)}\t\tadd the string to word completion list"), - C("addstatic", cmd_addstatic, - "{string|(expr)}\t\tadd the string to the static word completion list"), - C("alias", cmd_alias, - "[name[=[text]]]\t\tdelete/list/define aliases"), - C("at", cmd_at, - "[name [(time-string) [command]]\n\t\t\t\tset time of delayed label"), - C("beep", cmd_beep, - "\t\t\t\tmake your terminal beep (like #print (*7))"), - C("bind", cmd_bind, - "[edit|name [seq][=[command]]]\n" - "\t\t\t\tdelete/list/define key bindings"), - C("cancel", cmd_cancel, - "[number]\t\tcancel editing session"), - C("capture", cmd_capture, - "[filename]\t\tbegin/end of capture to file"), - C("clear", cmd_clear, - "\t\t\tclear input line (use from spawned programs)"), -#ifdef BUG_TELNET - C("color", cmd_color, - "attr\t\t\tset default colors/attributes"), -#endif - C("connect", cmd_connect, - "[connect-id [initstr] [address port]\n" - "\t\t\t\topen a new connection"), - C("cpu", cmd_cpu, - "\t\t\t\tshow CPU time used by powwow"), - C("delim", cmd_delim, - "[normal|program|{custom [chars]}]\n" - "\t\t\t\tset word completion delimeters"), - C("do", cmd_do, - "(expr) command\t\trepeat \"command\" (expr) times"), - C("edit", cmd_edit, - "\t\t\t\tlist editing sessions"), - C("emulate", cmd_emulate, - "[<|!]{text|(expr)}\tprocess result as if received from host"), - C("exe", cmd_exe, - "[<|!]{text|(string-expr)}\texecute result as if typed from keyboard"), - C("file", cmd_file, - "[=[filename]]\t\tset/show powwow definition file"), - C("for", cmd_for, - "([init];check;[loop]) command\n" - "\t\t\t\twhile \"check\" is true exec \"command\""), - C("group", cmd_group, - "[name] [on|off|list]\tgroup alias/action manipulation"), - C("groupdelim", cmd_groupdelim, - "[delimiter]\tchange delimiter for action/alias groups"), - C("hilite", cmd_hilite, - "[attr]\t\t\thighlight your input line"), - C("history", cmd_history, - "[{number|(expr)}]\tlist/execute commands in history"), - C("host", cmd_host, - "[hostname port]]\tset/show address of default host"), - C("identify", cmd_identify, - "[startact [endact]]\tsend MUME client identification"), - C("if", cmd_if, - "(expr) instr1 [; #else instr2]\n" - "\t\t\t\tif \"expr\" is true execute \"instr1\",\n" - "\t\t\t\totherwise execute \"instr2\""), - C("in", cmd_in, - "[label [(delay) [command]]]\tdelete/list/define delayed labels"), - C("init", cmd_init, - "[=[command]]\t\tdefine command to execute on connect to host"), - C("isprompt", cmd_isprompt, - "\t\t\trecognize a prompt as such"), - C("key", cmd_key, - "name\t\t\texecute the \"name\" key binding"), - C("keyedit", cmd_keyedit, - "editing-name\t\trun a line-editing function"), - C("load", cmd_load, - "[filename]\t\tload powwow settings from file"), - C("map", cmd_map, - "[-[number]|walksequence]\tshow/clear/edit (auto)map"), - C("mark", cmd_mark, - "[string[=[attr]]]\t\tdelete/list/define markers"), -#ifdef HAVE_LIBDL - C("module", cmd_module, - "[name]\t\t\tload shared library extension"), -#endif - C("movie", cmd_movie, - "[filename]\t\tbegin/end of movie record to file"), - C("net", cmd_net, - "\t\t\t\tprint amount of data received from/sent to host"), - C("nice", cmd_nice, - "[{number|(expr)}[command]]\n" - "\t\t\t\tset/show priority of new actions/marks"), - C("option", cmd_option, - "[[+|-|=]name]|list\tlist or view various options"), - C("prefix", cmd_prefix, - "string\t\t\tprefix all lines with string"), - C("", cmd_eval, - "(expr)\t\t\tevaluate expression, trashing result"), - C("print", cmd_print, - "[<|!][text|(expr)]\tprint text/result on screen, appending a \\n\n" - "\t\t\t\tif no argument, prints value of variable $0"), - C("prompt", cmd_prompt, - "[[<|=|>|%][+|-]name] [{pattern|(expression)} [=[prompt-command]]]\n" - "\t\t\t\tdelete/list/define actions on prompts"), - C("put", cmd_put, - "{text|(expr)}\t\tput text/result of expression in history"), - C("qui", cmd_qui, - "\t\t\t\tdo nothing"), - C("quit", cmd_quit, - "\t\t\t\tquit powwow"), - C("quote", cmd_quote, - "[on|off]\t\t\ttoggle verbatim-flag on/off"), - C("rawsend", cmd_rawsend, - "{string|(expr)}\tsend raw data to the MUD"), - C("rawprint", cmd_rawprint, - "{string|(expr)}\tsend raw data to the screen"), - C("rebind", cmd_rebind, - "name [seq]\t\tchange sequence of a key binding"), - C("rebindall", cmd_rebindall, - "\t\t\trebind all key bindings"), - C("rebindALL", cmd_rebindALL, - "\t\t\trebind ALL key bindings, even trivial ones"), - C("record", cmd_record, - "[filename]\t\tbegin/end of record to file"), - C("request", cmd_request, - "[editor][prompt][all]\tsend various identification strings"), - C("reset", cmd_reset, - "\t\tclear the whole defined list and reload default"), - C("retrace", cmd_retrace, - "[number]\t\tretrace the last number steps"), - C("save", cmd_save, - "[filename]\t\tsave powwow settings to file"), - C("send", cmd_send, - "[<|!]{text|(expr)}\teval expression, sending result to the MUD"), - C("setvar", cmd_setvar, - "name[=text|(expr)]\tset/show internal limits and variables"), - C("snoop", cmd_snoop, - "connect-id\t\ttoggle output display for connections"), - C("spawn", cmd_spawn, - "connect-id command\ttalk with a shell command"), - C("speedwalk", cmd_speedwalk, - "[speedwalk sequence]\texecute a speedwalk sequence explicitly"), - C("stop", cmd_stop, - "\t\t\t\tremove all delayed commands from active list"), - C("time", cmd_time, - "\t\t\t\tprint current time and date"), - C("var", cmd_var, - "variable [= [<|!]{string|(expr)} ]\n" - "\t\t\t\twrite result into the variable"), - C("ver", cmd_ver, - "\t\t\t\tshow powwow version"), - C("while", cmd_while, - "(expr) instr\t\twhile \"expr\" is true execute \"instr\""), - C("write", cmd_write, - "[>|!](expr;name)\t\twrite result of expr to \"name\" file"), - C("zap", cmd_zap, - "connect-id\t\t\tclose a connection"), - { NULL } -}; - -char *_cmd_sort_name( cmdstruct *cmd ) { - if( cmd -> sortname == NULL ) - return( cmd -> name ); - else - return( cmd -> sortname ); -} - -/* Adds a cmd to commands (inserts the ptr in the list, DO NOT FREE IT) */ -void cmd_add_command( cmdstruct *cmd ) { - /* do insert/sort */ - cmdstruct *c = commands; - - /* - * make sure it doesn't override another commmand - * this is important not just because it'd be irritating, - * but if a module defined the command in the global static - * space, it would create an infinite loop because the -> next - * ptr would point at itself - * - * doing it up front because based on the sortname, we may never see - * the dup item if we do it at sort time - */ - for( c = commands; c != NULL; c = c -> next ) { - if( strcmp( cmd -> name, c -> name ) == 0 ) { - PRINTF( "#error %s is already defined\n", c -> name ); - return; - } - } - - - /* catch insertion to head of list */ - if( commands == NULL ) { - /* no commands yet */ - commands = cmd; - cmd -> next = NULL; - return; - } - - if( strcmp( _cmd_sort_name( commands ), _cmd_sort_name( cmd ) ) > 0 ) { - /* this is lower in sort than every item, so - * make it the head of the list */ - cmd -> next = commands; - commands = cmd; - return; - } - - for( c = commands; c != NULL; c = c -> next ) { - if( strcmp( _cmd_sort_name( cmd ), _cmd_sort_name( c ) ) >= 0 ) { - /* Need second check to handle empty string case */ - if( c -> next == NULL || strcmp( _cmd_sort_name( cmd ), _cmd_sort_name( c -> next ) ) <= 0 ) { - /*PRINTF( "Inserting %s after %s\n", cmd -> name, c -> name ); */ - - /* insert after this one, it is greater than this - * entry but less than the next */ - cmd -> next = c -> next; - c -> next = cmd; - return; - } - } - } - - PRINTF( "ERROR INSERTING COMMAND\n" ); -} - -/* Init the command listing, called from main */ -void initialize_cmd(void) { - int i; - - /* Now add the default command list */ - for( i = 0; default_commands[ i ].name; i++ ) - cmd_add_command( &default_commands[ i ] ); -} - -#ifdef HAVE_LIBDL -static void cmd_module(char *arg) { - char libname[1024]; - void *lib; - void (*func)(); - - int pindex; - struct stat junk; - char *prefixes[] = { - PLUGIN_DIR, - ".", - "/lib/powwow", - "/usr/lib/powwow", - "/usr/local/lib/powwow", - "$HOME/.powwow/lib" /* this doesn't work, but is here to remind me :p */ - }; - - arg = skipspace(arg); - - /* I changed it to work this way so that you can have libs in multiple places and - * also eventually to allow it to use .dll instead of .so under the cygwin environment */ - for( pindex = 0; pindex < 5; pindex++ ) { - memset( libname, 0, sizeof libname ); - - /* don't look for name without .so, it breaks if you have a file - * with the same name in the current dir and making it .so for sure - * will skip these files since they are probably not libs to load - snprintf( libname, 1024, "%s/%s", prefixes[ pindex ], arg ); - if( stat( libname, &junk ) == 0 ) { - break; - } - */ - - snprintf( libname, 1024, "%s/%s.so", prefixes[ pindex ], arg ); - if( stat( libname, &junk ) == 0 ) { - break; - } - } - - /* open lib */ - lib = dlopen( libname, RTLD_GLOBAL | RTLD_LAZY ); - if( ! lib ) { - PRINTF( "#module error: %s\n", dlerror() ); - return; - }else{ - PRINTF( "#module loaded %s\n", libname ); - } - - func = dlsym( lib, "powwow_init" ); - if( func ) { - (*func)(); - }else{ - PRINTF( "#module error: %s\n", dlerror() ); - } -} -#endif - -static void cmd_group(char *arg) { - char *group; - int active; - aliasnode *p; - actionnode *a; - - arg = skipspace(arg); - - if( *arg ) { - arg = first_regular( group = arg, ' '); - *arg = '\0'; - arg = skipspace( arg + 1 ); - - if( strcmp( arg, "on" ) == 0 ) { - active = 1; - }else if( strcmp( arg, "off" ) == 0 ) { - active = 0; - }else if( strcmp( arg, "list" ) == 0 ) { - PRINTF( "#not implemented\n" ); - return; - }else{ - PRINTF( "#unknown group command, use off/on/list\n" ); - return; - } - - /* Now loop over all aliases/actions by groupname and toggle */ - for( p = sortedaliases; p; p = p -> snext ) { - if( p -> group && strcmp( p -> group, group ) == 0 ) { - p -> active = active; - } - } - - /* Same for actions */ - for( a = actions; a; a = a -> next ) { - if( a -> group && strcmp( a -> group, group ) == 0 ) { - a -> active = active; - } - } - }else{ - PRINTF( "#group name required\n" ); - } -} - -static void cmd_groupdelim(char *arg) { - if( *arg != 0 ) { - free( group_delim ); - group_delim = my_strdup( arg ); - PRINTF( "#group delimiter is now '%s'\n", group_delim ); - } -} - -static void cmd_help(char *arg) -{ - int i, size; - char *text, *tmp; - FILE *f; - char line[BUFSIZE]; - int len; - cmdstruct *c; - - arg = skipspace(arg); - if (*arg == '#') arg++; - if (!*arg) { - size = 25; - for( c = commands; c != NULL; c = c -> next ) - size += strlen(c -> name) + strlen(c -> help) + 5; - - text = tmp = (char *)malloc(size); - if (!text) { - errmsg("malloc"); - return; - } - - /* do not use sprintf() return value, almost every OS returns a different thing. */ - sprintf(tmp, "#help\n#commands available:\n"); - tmp += strlen(tmp); - - for( c = commands; c != NULL; c = c -> next ) { - sprintf(tmp, "#%s %s\n", c -> name, c -> help); - tmp += strlen(tmp); - } - - message_edit(text, strlen(text), 1, 1); - return; - } - - if (!strncmp(arg, "copyright", strlen(arg))) { - int fd, left, got = 0; - struct stat stbuf; - - if (stat(copyfile, &stbuf) < 0) { - errmsg("stat(copyright file)"); - return; - } - - if (!(text = (char *)malloc(left = stbuf.st_size))) { - errmsg("malloc"); - return; - } - if ((fd = open(copyfile, O_RDONLY)) < 0) { - errmsg("open(copyright file)"); - free(text); - return; - } - while (left > 0) { - while ((i = read(fd, text + got, left)) < 0 && errno == EINTR) - ; - if (i < 0 && errno == EINTR) { - errmsg("read (copyright file)"); - free(text); - close(fd); - return; - } - if (i == 0) - break; - left -= i, got += i; - } - close(fd); - message_edit(text, strlen(text), 1, 1); - return; - } - - /* !copyright */ - - f = fopen(helpfile, "r"); - if (!f) { - PRINTF("#cannot open help file \"%s\": %s\n", - helpfile, strerror(errno)); - return; - } - - while ((tmp = fgets(line, BUFSIZE, f)) && - (line[0] != '@' || strncmp(line + 1, arg, strlen(arg)))) - ; - - if (!tmp) { - PRINTF("#no entry for \"%s\" in the help file.\n", arg); - fclose(f); - return; - } - - if (!(text = (char *)malloc(size = BUFSIZE))) { - errmsg("malloc"); - fclose(f); - return; - } - - /* the first line becomes $TITLE */ - tmp = strchr(line, '\n'); - if (tmp) *tmp = '\0'; - i = sprintf(text, "Help on '%s'\n", line + 1); - - /* allow multiple commands to share the same help */ - while (fgets(line, BUFSIZE, f) && line[0] == '@') ; - - do { - if ((len = strlen(line)) >= size - i) { - /* Not enough space in current buffer */ - - if (!(tmp = (char *)malloc(size += BUFSIZE))) { - errmsg("malloc"); - free(text); - fclose(f); - return; - } else { - memcpy(tmp, text, i); - free(text); - text = tmp; - } - } - memcpy(text + i, line, len); - i += len; - } while (fgets(line, BUFSIZE, f) && line[0] != '@'); - - fclose(f); - text[i] = '\0'; /* safe, there is space */ - message_edit(text, strlen(text), 1, 1); -} - -static void cmd_clear(char *arg) -{ - if (line_status == 0) { - clear_input_line(opt_compact); - if (!opt_compact) { - tty_putc('\n'); - col0 = 0; - } - status(1); - } -} - -#ifndef NO_SHELL -static void cmd_shell(char *arg) -{ - if (!*arg) { - if (opt_info) { - PRINTF("#that's easy.\n"); - } - } else { - tty_quit(); - - if (system(arg) == -1) { - perror("system()"); - } - - tty_start(); - tty_gotoxy(col0 = 0, line0 = lines -1); - tty_puts(tty_clreoln); - } -} -#endif - -static void cmd_alias(char *arg) -{ - arg = skipspace(arg); - if (!*arg) - show_aliases(); - else - parse_alias(arg); -} - -static void cmd_action(char *arg) -{ - arg = skipspace(arg); - if (!*arg) - show_actions(); - else - parse_action(arg, 0); -} - -static void cmd_prompt(char *arg) -{ - arg = skipspace(arg); - if (!*arg) - show_prompts(); - else - parse_action(arg, 1); -} - -static void cmd_beep(char *arg) -{ - tty_putc('\007'); -} - -/* - * create/list/edit/delete bindings - */ -static void cmd_bind(char *arg) -{ - arg = skipspace(arg); - if (!*arg) - show_binds(0); - else if (!strcmp(arg, "edit")) - show_binds(1); - else - parse_bind(arg); -} - -static void cmd_delim(char *arg) -{ - char buf[BUFSIZE]; - int n; - - arg = skipspace(arg); - if (!*arg) { - PRINTF("#delim: \"%s\" (%s)\n", delim_name[delim_mode], DELIM); - return; - } - - arg = split_first_word(buf, BUFSIZE, arg); - n = 0; - while (n < DELIM_MODES && strncmp(delim_name[n], buf, strlen(buf)) != 0) - n++; - - if (n >= DELIM_MODES) { - PRINTF("#delim [normal|program|{custom }\n"); - return; - } - - if (n == DELIM_CUSTOM) { - if (!strchr(arg, ' ')) { - my_strncpy(buf+1, arg, BUFSIZE-2); - *buf = ' '; /* force ' ' in the delims */ - arg = buf; - } - unescape(arg); - set_custom_delimeters(arg); - } else - delim_mode = n; -} - -static void cmd_do(char *arg) -{ - int type; - long result; - - arg = skipspace(arg); - if (*arg != '(') { - PRINTF("#do: "); - print_error(error=MISMATCH_PAREN_ERROR); - return; - } - arg++; - - type = evall(&result, &arg); - if (REAL_ERROR) return; - - if (type != TYPE_NUM) { - PRINTF("#do: "); - print_error(error=NO_NUM_VALUE_ERROR); - return; - } - - if (*arg == ')') { /* skip the ')' */ - if (*++arg == ' ') - arg++; - } - else { - PRINTF("#do: "); - print_error(error=MISSING_PAREN_ERROR); - return; - } - - if (result >= 0) - while (!error && result--) - (void)parse_instruction(arg, 1, 0, 1); - else { - PRINTF("#do: bogus repeat count \"%ld\"\n", result); - } -} - -static void cmd_hilite(char *arg) -{ - int attr; - - arg = skipspace(arg); - attr = parse_attributes(arg); - if (attr == -1) { - PRINTF("#attribute syntax error.\n"); - if (opt_info) - show_attr_syntax(); - } else { - attr_string(attr, edattrbeg, edattrend); - - edattrbg = ATTR(attr) & ATTR_INVERSE ? 1 - : BACKGROUND(attr) != NO_COLOR || ATTR(attr) & ATTR_BLINK; - - if (opt_info) { - PRINTF("#input highlighting is now %so%s%s.\n", - edattrbeg, (attr == NOATTRCODE) ? "ff" : "n", - edattrend); - } - } -} - -static void cmd_history(char *arg) -{ - int num = 0; - long buf; - - arg = skipspace(arg); - - if (history_done >= MAX_HIST) { - print_error(error=HISTORY_RECURSION_ERROR); - return; - } - history_done++; - - if (*arg == '(') { - arg++; - num = evall(&buf, &arg); - if (!REAL_ERROR && num != TYPE_NUM) - error=NO_NUM_VALUE_ERROR; - if (REAL_ERROR) { - PRINTF("#history: "); - print_error(error=NO_NUM_VALUE_ERROR); - return; - } - num = (int)buf; - } else - num = atoi(arg); - - if (num > 0) - exe_history(num); - else - show_history(-num); -} - -static void cmd_host(char *arg) -{ - char newhost[BUFSIZE]; - - arg = skipspace(arg); - if (*arg) { - arg = split_first_word(newhost, BUFSIZE, arg); - if (*arg) { - my_strncpy(hostname, newhost, BUFSIZE-1); - portnumber = atoi(arg); - if (opt_info) { - PRINTF("#host set to: %s %d\n", hostname, portnumber); - } - } else { - PRINTF("#host: missing portnumber.\n"); - } - } else if (*hostname) - sprintf(inserted_next, "#host %.*s %d", BUFSIZE-INTLEN-8, - hostname, portnumber); - else { - PRINTF("#syntax: #host hostname port\n"); - } -} - -static void cmd_request(char *arg) -{ - char *idprompt = "~$#EP2\nG\n"; - char *ideditor = "~$#EI\n"; - char buf[256]; - int all, len; - - if (tcp_fd == -1) { - PRINTF("#not connected to a MUD!\n"); - return; - } - while (*(arg = skipspace(arg))) { - arg = split_first_word(buf, 256, arg); - if (*buf) { - all = !strcmp(buf, "all"); - len = strlen(buf); - if ((all || !strncmp(buf, "editor", len))) { - tcp_raw_write(tcp_fd, ideditor, strlen(ideditor)); - CONN_LIST(tcp_fd).flags |= IDEDITOR; - if (opt_info) { - PRINTF("#request editor: %s done!\n", ideditor); - } - } - if ((all || !strncmp(buf, "prompt", len))) { - tcp_raw_write(tcp_fd, idprompt, strlen(idprompt)); - CONN_LIST(tcp_fd).flags |= IDPROMPT; - if (opt_info) { - PRINTF("#request prompt: %s done!\n", idprompt); - } - } - } - } - -} - -static void cmd_identify(char *arg) -{ - edit_start[0] = edit_end[0] = '\0'; - if (*arg) { - char *p = strchr(arg, ' '); - if (p) { - *(p++) = '\0'; - my_strncpy(edit_end, p, BUFSIZE-1); - } - my_strncpy(edit_start, arg, BUFSIZE-1); - } - cmd_request("editor"); -} - -static void cmd_in(char *arg) -{ - char *name; - long millisec, buf; - int type; - delaynode **p; - - arg = skipspace(arg); - if (!*arg) { - show_delays(); - return; - } - - arg = first_regular(name = arg, ' '); - if (*arg) - *arg++ = 0; - - unescape(name); - - p = lookup_delay(name, 0); - if (!*p) p = lookup_delay(name, 1); - - if (!*arg && !*p) { - PRINTF("#unknown delay label, cannot show: \"%s\"\n", name); - return; - } - if (!*arg) { - show_delaynode(*p, 1); - return; - } - if (*arg != '(') { - PRINTF("#in: "); - print_error(error=MISMATCH_PAREN_ERROR); - return; - } - arg++; /* skip the '(' */ - - type = evall(&buf, &arg); - if (!REAL_ERROR) { - if (type!=TYPE_NUM) - error=NO_NUM_VALUE_ERROR; - else if (*arg != ')') - error=MISSING_PAREN_ERROR; - } - if (REAL_ERROR) { - PRINTF("#in: "); - print_error(error); - return; - } - - arg = skipspace(arg+1); - millisec = buf; - if (*p && millisec) - change_delaynode(p, arg, millisec); - else if (!*p && millisec) { - if (*arg) - new_delaynode(name, arg, millisec); - else { - PRINTF("#cannot create delay label without a command.\n"); - } - } else if (*p && !millisec) { - if (opt_info) { - PRINTF("#deleting delay label: %s %s\n", name, (*p)->command); - } - delete_delaynode(p); - } else { - PRINTF("#unknown delay label, cannot delete: \"%s\"\n", name); - } -} - -static void cmd_at(char *arg) -{ - char *name, *buf = NULL; - char dayflag=0; - struct tm *twhen; - int num, hour = -1, minute = -1, second = -1; - delaynode **p; - long millisec; - ptr pbuf = (ptr)0; - - arg = skipspace(arg); - if (!*arg) { - show_delays(); - return; - } - - arg = first_regular(name = arg, ' '); - if (*arg) - *arg++ = 0; - - unescape(name); - - p = lookup_delay(name, 0); - if (!*p) p = lookup_delay(name, 1); - - if (!*arg && !*p) { - PRINTF("#unknown delay label, cannot show: \"%s\"\n", name); - return; - } - if (!*arg) { - show_delaynode(*p, 2); - return; - } - if (*arg != '(') { - PRINTF("#in: "); - print_error(error=MISMATCH_PAREN_ERROR); - return; - } - arg++; /* skip the '(' */ - - (void)evalp(&pbuf, &arg); - if (REAL_ERROR) { - print_error(error); - ptrdel(pbuf); - return; - } - if (pbuf) { - /* convert time-string into hour, minute, second */ - buf = skipspace(ptrdata(pbuf)); - if (!*buf || !isdigit(*buf)) { - PRINTF("#at: "); - print_error(error=NO_NUM_VALUE_ERROR); - ptrdel(pbuf); - return; - } - num = atoi(buf); - second = num % 100; - minute = (num /= 100) % 100; - hour = num / 100; - } - if (hour < 0 || hour>23 || minute < 0 || minute>59 - || second < 0 || second>59) { - - PRINTF("#at: #error: invalid time \"%s\"\n", - pbuf && buf ? buf : (char *)""); - error=OUT_RANGE_ERROR; - ptrdel(pbuf); - return; - } - ptrdel(pbuf); - - if (*arg == ')') { /* skip the ')' */ - if (*++arg == ' ') - arg++; - } - else { - PRINTF("#at: "); - print_error(error=MISSING_PAREN_ERROR); - return; - } - - arg = skipspace(arg); - update_now(); - twhen = localtime((time_t *)&now.tv_sec); - /* put current year, month, day in calendar struct */ - - if (hour < twhen->tm_hour || - (hour == twhen->tm_hour && - (minute < twhen->tm_min || - (minute == twhen->tm_min && - second <= twhen->tm_sec)))) { - dayflag = 1; - /* it is NOT possible to define an #at refering to the past */ - } - - /* if you use a time smaller than the current, it refers to tomorrow */ - - millisec = (hour - twhen->tm_hour) * 3600 + (minute - twhen->tm_min) * 60 + - second - twhen->tm_sec + (dayflag ? 24*60*60 : 0); - millisec *= mSEC_PER_SEC; /* Comparing time with current calendar, - we finally got the delay */ - millisec -= now.tv_usec / uSEC_PER_mSEC; - - if (*p) - change_delaynode(p, arg, millisec); - else - if (*arg) - new_delaynode(name, arg, millisec); - else { - PRINTF("#cannot create delay label without a command.\n"); - } -} - -static void cmd_init(char *arg) -{ - arg = skipspace(arg); - - if (*arg == '=') { - if (*++arg) { - my_strncpy(initstr, arg, BUFSIZE-1); - if (opt_info) { - PRINTF("#init: %s\n", initstr); - } - } else { - *initstr = '\0'; - if (opt_info) { - PRINTF("#init cleared.\n"); - } - } - } else - sprintf(inserted_next, "#init =%.*s", BUFSIZE-8, initstr); -} - -static void cmd_isprompt(char *arg) -{ - if (tcp_fd == tcp_main_fd) { - int i; - long l; - arg = skipspace(arg); - if (*arg == '(') { - arg++; - i = evall(&l, &arg); - if (!REAL_ERROR) { - if (i!=TYPE_NUM) - error=NO_NUM_VALUE_ERROR; - else if (*arg != ')') - error=MISSING_PAREN_ERROR; - } - if (REAL_ERROR) { - PRINTF("#isprompt: "); - print_error(error); - return; - } - i = (int)l; - } else - i = atoi(arg); - - if (i == 0) - surely_isprompt = -1; - else if (i < 0) { - if (i > -NUMPARAM && *VAR[-i].str) - ptrtrunc(prompt->str, surely_isprompt = ptrlen(*VAR[-i].str)); - } else - ptrtrunc(prompt->str, surely_isprompt = i); - } -} - -static void cmd_key(char *arg) -{ - keynode *q=NULL; - - arg = skipspace(arg); - if (!*arg) - return; - - if ((q = *lookup_key(arg))) - q->funct(q->call_data); - else { - PRINTF("#no such key: \"%s\"\n", arg); - } -} - -static void cmd_keyedit(char *arg) -{ - int function; - char *param; - - arg = skipspace(arg); - if (!*arg) - return; - - if ((function = lookup_edit_name(arg, ¶m))) - internal_functions[function].funct(param); - else { - PRINTF("#no such editing function: \"%s\"\n", arg); - } -} - -static void cmd_map(char *arg) -{ - arg = skipspace(arg); - if (!*arg) /* show map */ - map_show(); - else if (*arg == '-') /* retrace steps without walking */ - map_retrace(atoi(arg + 1), 0); - else - map_walk(arg, 1, 1); -} - -static void cmd_retrace(char *arg) -{ - map_retrace(atoi(arg), 1); -} - -static void cmd_mark(char *arg) -{ - if (!*arg) - show_marks(); - else - parse_mark(arg); -} - -static void cmd_nice(char *arg) -{ - int nnice = a_nice; - arg = skipspace(arg); - if (!*arg) { - PRINTF("#nice: %d\n", a_nice); - return; - } - if (isdigit(*arg)) { - a_nice = 0; - while (isdigit(*arg)) { - a_nice *= 10; - a_nice += *arg++ - '0'; - } - } - else if (*arg++=='(') { - long buf; - int type; - - type = evall(&buf, &arg); - if (!REAL_ERROR && type!=TYPE_NUM) - error=NO_NUM_VALUE_ERROR; - else if (!REAL_ERROR && *arg++ != ')') - error=MISSING_PAREN_ERROR; - if (REAL_ERROR) { - PRINTF("#nice: "); - print_error(error); - return; - } - a_nice = (int)buf; - if (a_nice<0) - a_nice = 0; - } - arg = skipspace(arg); - if (*arg) { - parse_instruction(arg, 0, 0, 1); - a_nice = nnice; - } -} - -static void cmd_prefix(char *arg) -{ - strcpy(prefixstr, arg); - if (opt_info) { - PRINTF("#prefix %s.\n", *arg ? "set" : "cleared"); - } -} - -static void cmd_quote(char *arg) -{ - arg = skipspace(arg); - if (!*arg) - verbatim ^= 1; - else if (!strcmp(arg, "on")) - verbatim = 1; - else if (!strcmp(arg, "off")) - verbatim = 0; - if (opt_info) { - PRINTF("#%s mode.\n", verbatim ? "verbatim" : "normal"); - } -} - -/* - * change the escape sequence of an existing binding - */ -static void cmd_rebind(char *arg) -{ - parse_rebind(arg); -} - -static void cmd_rebindall(char *arg) -{ - keynode *kp; - char *seq; - - for (kp = keydefs; kp; kp = kp->next) { - seq = kp->sequence; - if (kp->seqlen == 1 && seq[0] < ' ') - ; - else if (kp->seqlen == 2 && seq[0] == '\033' && isalnum(seq[1])) - ; - else { - parse_rebind(kp->name); - if (error) - break; - } - } -} - -static void cmd_rebindALL(char *arg) -{ - keynode *kp; - - for (kp = keydefs; kp; kp = kp->next) { - parse_rebind(kp->name); - if (error) - break; - } -} - -static void cmd_reset(char *arg) -{ - char all = 0; - arg = skipspace(arg); - if (!*arg) { - PRINTF("#reset: must specify one of:\n"); - tty_puts(" alias, action, bind, in (or at), mark, prompt, var, all.\n"); - return; - } - all = !strcmp(arg, "all"); - if (all || !strcmp(arg, "alias")) { - int n; - for (n = 0; n < MAX_HASH; n++) { - while (aliases[n]) - delete_aliasnode(&aliases[n]); - } - if (!all) - return; - } - if (all || !strcmp(arg, "action")) { - while (actions) - delete_actionnode(&actions); - if (!all) - return; - } - if (all || !strcmp(arg, "bind")) { - while (keydefs) - delete_keynode(&keydefs); - tty_add_initial_binds(); - tty_add_walk_binds(); - if (!all) - return; - } - if (all || !strcmp(arg, "in") || !strcmp(arg, "at")) { - while (delays) - delete_delaynode(&delays); - while (dead_delays) - delete_delaynode(&dead_delays); - if (!all) - return; - } - if (all || !strcmp(arg, "mark")) { - while (markers) - delete_marknode(&markers); - if (!all) - return; - } - if (all || !strcmp(arg, "prompt")) { - while (prompts) - delete_promptnode(&prompts); - if (!all) - return; - } - if (all || !strcmp(arg, "var")) { - int n; - varnode **first; - - for (n = 0; n < MAX_HASH; n++) { - while (named_vars[0][n]) - delete_varnode(&named_vars[0][n], 0); - first = &named_vars[1][n]; - while (*first) { - if (is_permanent_variable(*first)) - first = &(*first)->next; - else - delete_varnode(first, 1); - } - } - - for (n = 0; n < NUMVAR; n++) { - *var[n].num = 0; - ptrdel(*var[n].str); - *var[n].str = NULL; - } - if (!all) - return; - } -} - -static void cmd_snoop(char *arg) -{ - if (!*arg) { - PRINTF("#snoop: which connection?\n"); - } else - tcp_togglesnoop(arg); -} - -static void cmd_stop(char *arg) -{ - delaynode *dying; - - if (delays) - update_now(); - - while (delays) { - dying = delays; - delays = dying->next; - dying->when.tv_sec = now.tv_sec; - dying->when.tv_usec = now.tv_usec; - dying->next = dead_delays; - dead_delays = dying; - } - if (opt_info) { - PRINTF("#all delayed labels are now disabled.\n"); - } -} - -static void cmd_time(char *arg) -{ - struct tm *s; - char buf[BUFSIZE]; - - update_now(); - s = localtime((time_t *)&now.tv_sec); - (void)strftime(buf, BUFSIZE - 1, "%a, %d %b %Y %H:%M:%S", s); - PRINTF("#current time is %s\n", buf); -} - -static void cmd_ver(char *arg) -{ - printver(); -} - -static void cmd_emulate(char *arg) -{ - char kind; - FILE *fp; - long start, end, i = 1; - int len; - ptr pbuf = (ptr)0; - - arg = redirect(arg, &pbuf, &kind, "emulate", 0, &start, &end); - if (REAL_ERROR || !arg) - return; - - if (kind) { - char buf[BUFSIZE]; - - fp = (kind == '!') ? popen(arg, "r") : fopen(arg, "r"); - if (!fp) { - PRINTF("#emulate: #error opening \"%s\"\n", arg); - print_error(error=SYNTAX_ERROR); - ptrdel(pbuf); - return; - } - status(-1); /* we're pretending we got something from the MUD */ - while (!error && (!start || i<=end) && fgets(buf, BUFSIZE, fp)) - if (!start || i++>=start) - process_remote_input(buf, strlen(buf)); - - if (kind == '!') pclose(fp); else fclose(fp); - } else { - status(-1); /* idem */ - /* WARNING: no guarantee there is space! */ - arg[len = strlen(arg)] = '\n'; - arg[++len] = '\0'; - process_remote_input(arg, len); - } - ptrdel(pbuf); -} - -static void cmd_eval(char *arg) -{ - arg = skipspace(arg); - if (*arg=='(') { - arg++; - (void)evaln(&arg); - if (*arg != ')') { - PRINTF("#(): "); - print_error(error=MISSING_PAREN_ERROR); - } - } - else { - PRINTF("#(): "); - print_error(error=MISMATCH_PAREN_ERROR); - } -} - -static void cmd_exe(char *arg) -{ - char kind; - char *clear; - long offset, start, end, i = 1; - FILE *fp; - ptr pbuf = (ptr)0; - - arg = redirect(arg, &pbuf, &kind, "exe", 0, &start, &end); - if (REAL_ERROR || !arg) - return; - - if (kind) { - char buf[BUFSIZE]; - - fp = (kind == '!') ? popen(arg, "r") : fopen(arg, "r"); - if (!fp) { - PRINTF("#exe: #error opening \"%s\"\n", arg); - error = SYNTAX_ERROR; - ptrdel(pbuf); - return; - } - offset = 0; - /* We may go in to a loop if a single function is more than 4k, but if that's - * the case then maybe you should break it down a little bit :p */ - while (!error && (!start || i<=end) && fgets(buf + offset, BUFSIZE - offset, fp)) - /* If it ends with \\\n then it's a line continuation, so clear - * the \\\n and do another fgets */ - if (buf[offset + strlen(buf + offset) - 2] == '\\') { - /* Clear \n prefixed with a literal backslash '\\' */ - if ((clear = strstr(buf + offset, "\\\n"))) - *clear = '\0'; - offset += strlen(buf + offset); - } else { - if (!start || i++ >= start) { - buf[strlen(buf)-1] = '\0'; - parse_user_input(buf, 0); - offset = 0; - } - } - - if (kind == '!') pclose(fp); else fclose(fp); - } else - parse_user_input(arg, 0); - ptrdel(pbuf); -} - -static void cmd_print(char *arg) -{ - char kind; - long start, end, i = 1; - FILE *fp; - ptr pbuf = (ptr)0; - - clear_input_line(opt_compact); - - if (!*arg) { - smart_print(*VAR[0].str ? ptrdata(*VAR[0].str) : (char *)"", 1); - return; - } - - arg = redirect(arg, &pbuf, &kind, "print", 1, &start, &end); - if (REAL_ERROR || !arg) - return; - - if (kind) { - char buf[BUFSIZE]; - fp = (kind == '!') ? popen(arg, "r") : fopen(arg, "r"); - if (!fp) { - PRINTF("#print: #error opening \"%s\"\n", arg); - error=SYNTAX_ERROR; - ptrdel(pbuf); - return; - } - while (!error && (!start || i <= end) && fgets(buf, BUFSIZE, fp)) - if (!start || i++>=start) - tty_puts(buf); - tty_putc('\n'); - - if (kind == '!') pclose(fp); else fclose(fp); - } else - smart_print(arg, 1); - ptrdel(pbuf); -} - -static void cmd_send(char *arg) -{ - char *newline, kind; - long start, end, i = 1; - FILE *fp; - ptr pbuf = (ptr)0; - - arg = redirect(arg, &pbuf, &kind, "send", 0, &start, &end); - if (REAL_ERROR ||!arg) - return; - - if (kind) { - char buf[BUFSIZE]; - fp = (kind == '!') ? popen(arg, "r") : fopen(arg, "r"); - if (!fp) { - PRINTF("#send: #error opening \"%s\"\n", arg); - error = SYNTAX_ERROR; - ptrdel(pbuf); - return; - } - while (!error && (!start || i<=end) && fgets(buf, BUFSIZE, fp)) { - if ((newline = strchr(buf, '\n'))) - *newline = '\0'; - - if (!start || i++>=start) { - if (opt_echo) { - PRINTF("[%s]\n", buf); - } - tcp_write(tcp_fd, buf); - } - } - if (kind == '!') pclose(fp); else fclose(fp); - } else { - if (opt_echo) { - PRINTF("[%s]\n", arg); - } - tcp_write(tcp_fd, arg); - } - ptrdel(pbuf); -} - -static void cmd_rawsend(char *arg) -{ - char *tmp = skipspace(arg); - - if (*tmp=='(') { - ptr pbuf = (ptr)0; - arg = tmp + 1; - (void)evalp(&pbuf, &arg); - if (REAL_ERROR) { - print_error(error); - ptrdel(pbuf); - } else if (pbuf) - tcp_raw_write(tcp_fd, ptrdata(pbuf), ptrlen(pbuf)); - } else { - int len; - if ((len = memunescape(arg, strlen(arg)))) - tcp_raw_write(tcp_fd, arg, len); - } -} - -static void cmd_rawprint(char *arg) -{ - char *tmp = skipspace(arg); - - if (*tmp=='(') { - ptr pbuf = (ptr)0; - arg = tmp + 1; - (void)evalp(&pbuf, &arg); - if (REAL_ERROR) { - print_error(error); - ptrdel(pbuf); - } else if (pbuf) - tty_raw_write(ptrdata(pbuf), ptrlen(pbuf)); - } else { - int len; - if ((len = memunescape(arg, strlen(arg)))) - tty_raw_write(arg, len); - } -} - - -static void cmd_write(char *arg) -{ - ptr p1 = (ptr)0, p2 = (ptr)0; - char *tmp = skipspace(arg), kind; - FILE *fp; - - kind = *tmp; - if (kind == '!' || kind == '>') - arg = ++tmp; - else - kind = 0; - - if (*tmp=='(') { - arg = tmp + 1; - (void)evalp(&p1, &arg); - if (REAL_ERROR) - goto write_cleanup; - - if (*arg == CMDSEP) { - arg++; - (void)evalp(&p2, &arg); - if (!REAL_ERROR && !p2) - error = NO_STRING_ERROR; - if (REAL_ERROR) - goto write_cleanup; - } else { - PRINTF("#write: "); - error=SYNTAX_ERROR; - goto write_cleanup; - } - if (*arg != ')') { - PRINTF("#write: "); - error=MISSING_PAREN_ERROR; - goto write_cleanup; - } - arg = ptrdata(p2); - - fp = (kind == '!') ? popen(arg, "w") : fopen(arg, kind ? "w" : "a"); - if (!fp) { - PRINTF("#write: #error opening \"%s\"\n", arg); - error=SYNTAX_ERROR; - goto write_cleanup2; - } - fprintf(fp, "%s\n", p1 ? ptrdata(p1) : (char *)""); - fflush(fp); - if (kind == '!') pclose(fp); else fclose(fp); - } else { - PRINTF("#write: "); - error=MISMATCH_PAREN_ERROR; - } - -write_cleanup: - if (REAL_ERROR) - print_error(error); -write_cleanup2: - ptrdel(p1); - ptrdel(p2); -} - -static void cmd_var(char *arg) -{ - char *buf, *expr, *tmp, kind, type, right = 0, deleting = 0; - varnode **p_named_var = NULL, *named_var = NULL; - FILE *fp; - long start, end, i = 1; - int len, idx; - ptr pbuf = (ptr)0; - - arg = skipspace(arg); - expr = first_regular(arg, '='); - - if (*expr) { - *expr++ = '\0'; /* skip the = */ - if (!*expr) - deleting = 1; - else { - right = 1; - if (*expr == ' ') - expr++; - } - } - - if (*arg == '$') - type = TYPE_TXT_VAR; - else if (*arg == '@') - type = TYPE_NUM_VAR; - else if (*arg) { - print_error(error=INVALID_NAME_ERROR); - return; - } else { - show_vars(); - return; - } - - kind = *++arg; - if (isalpha(kind) || kind == '_') { - /* found a named variable */ - tmp = arg; - while (*tmp && (isalnum(*tmp) || *tmp == '_')) - tmp++; - if (*tmp) { - print_error(error=INVALID_NAME_ERROR); - return; - } - kind = type==TYPE_TXT_VAR ? 1 : 0; - p_named_var = lookup_varnode(arg, kind); - if (!*p_named_var) { - /* it doesn't (yet) exist */ - if (!deleting) { - /* so create it */ - named_var = add_varnode(arg, kind); - if (REAL_ERROR) - return; - if (opt_info) { - PRINTF("#new variable: \"%s\"\n", arg - 1); - } - } else { - print_error(error=UNDEFINED_VARIABLE_ERROR); - return; - } - } else - /* it exists, hold on */ - named_var = *p_named_var; - - idx = named_var->index; - } else { - /* not a named variable, may be - * an unnamed variable or an expression */ - kind = type==TYPE_TXT_VAR ? 1 : 0; - tmp = skipspace(arg); - if (*tmp == '(') { - /* an expression */ - arg = tmp+1; - idx = evalp(&pbuf, &arg); - if (!REAL_ERROR && idx != TYPE_TXT) - error=NO_STRING_ERROR; - if (REAL_ERROR) { - PRINTF("#var: "); - print_error(error); - ptrdel(pbuf); - return; - } - if (pbuf) - buf = ptrdata(pbuf); - else { - print_error(error=INVALID_NAME_ERROR); - return; - } - char err_det; - if (isdigit(*buf) || *buf=='-' || *buf=='+') { - if (sscanf(buf, "%d%c", &idx, &err_det)==1) { - if (idx < -NUMVAR || idx >= NUMPARAM) { - print_error(error=OUT_RANGE_ERROR); - ptrdel(pbuf); - return; - } - } else { - print_error(error=INVALID_NAME_ERROR); - return; - } - } else { - if (!isalpha(*buf) && *buf!='_') { - print_error(error=INVALID_NAME_ERROR); - return; - } - tmp = buf + 1; - while (*tmp && (isalnum(*tmp) || *tmp=='_')) - tmp++; - if (*tmp) { - print_error(error=INVALID_NAME_ERROR); - return; - } - if (!(named_var = *(p_named_var = lookup_varnode(buf, kind)))) { - if (!deleting) { - named_var = add_varnode(buf, kind); - if (REAL_ERROR) { - print_error(error); - ptrdel(pbuf); - return; - } - if (opt_info) { - PRINTF("#new variable: %c%s\n", kind - ? '$' : '@', buf); - } - } else { - print_error(error=UNDEFINED_VARIABLE_ERROR); - ptrdel(pbuf); - return; - } - } - idx = named_var->index; - } - } else { - /* an unnamed var */ - long buf2; - - idx = evall(&buf2, &arg); - if (!REAL_ERROR && idx != TYPE_NUM) - error=NO_STRING_ERROR; - if (REAL_ERROR) { - PRINTF("#var: "); - print_error(error); - return; - } - idx = (int)buf2; - if (idx < -NUMVAR || idx >= NUMPARAM) { - print_error(error=OUT_RANGE_ERROR); - return; - } - /* ok, it's an unnamed var */ - } - } - - - if (type == TYPE_TXT_VAR && right && !*VAR[idx].str) { - /* create it */ - *VAR[idx].str = ptrnew(PARAMLEN); - if (MEM_ERROR) { - print_error(error); - ptrdel(pbuf); - return; - } - } - - if (deleting) { - /* R.I.P. named variables */ - if (named_var) { - if (is_permanent_variable(named_var)) { - PRINTF("#cannot delete variable: \"%s\"\n", arg - 1); - } else { - delete_varnode(p_named_var, kind); - if (opt_info) { - PRINTF("#deleted variable: \"%s\"\n", arg - 1); - } - } - } else if ((type = TYPE_TXT_VAR)) { - /* R.I.P. unnamed variables */ - if (*VAR[idx].str) { - if (idx < 0) { - ptrdel(*VAR[idx].str); - *VAR[idx].str = 0; - } else - ptrzero(*VAR[idx].str); - } - } else - *VAR[idx].num = 0; - ptrdel(pbuf); - return; - } else if (!right) { - /* no right-hand expression, just show */ - if (named_var) { - if (type == TYPE_TXT_VAR) { - pbuf = ptrescape(pbuf, *VAR[idx].str, 0); - if (REAL_ERROR) { - print_error(error); - ptrdel(pbuf); - return; - } - sprintf(inserted_next, "#($%.*s = \"%.*s\")", - BUFSIZE - 10, named_var->name, - BUFSIZE - (int)strlen(named_var->name) - 10, - pbuf ? ptrdata(pbuf) : (char *)""); - } else { - sprintf(inserted_next, "#(@%.*s = %ld)", - BUFSIZE - 8, named_var->name, - *VAR[idx].num); - } - } else { - if (type == TYPE_TXT_VAR) { - pbuf = ptrescape(pbuf, *VAR[idx].str, 0); - sprintf(inserted_next, "#($%d = \"%.*s\")", idx, - BUFSIZE - INTLEN - 10, - pbuf ? ptrdata(pbuf) : (char *)""); - } else { - sprintf(inserted_next, "#(@%d = %ld)", idx, - *VAR[idx].num); - } - } - ptrdel(pbuf); - return; - } - - /* only case left: assign a value to a variable */ - arg = redirect(expr, &pbuf, &kind, "var", 1, &start, &end); - if (REAL_ERROR || !arg) - return; - - if (kind) { - char buf2[BUFSIZE]; - fp = (kind == '!') ? popen(arg, "r") : fopen(arg, "r"); - if (!fp) { - PRINTF("#var: #error opening \"%s\"\n", arg); - error=SYNTAX_ERROR; - ptrdel(pbuf); - return; - } - len = 0; - i = 1; - while (!error && (!start || i<=end) && fgets(buf2+len, BUFSIZE-len, fp)) - if (!start || i++>=start) - len += strlen(buf2 + len); - - if (kind == '!') pclose(fp); else fclose(fp); - if (len>PARAMLEN) - len = PARAMLEN; - buf2[len] = '\0'; - arg = buf2; - } - - if (type == TYPE_NUM_VAR) { - arg = skipspace(arg); - type = 1; - len = 0; - - if (*arg == '-') - arg++, type = -1; - else if (*arg == '+') - arg++; - - if (isdigit(kind=*arg)) while (isdigit(kind)) { - len*=10; - len+=(kind-'0'); - kind=*++arg; - } - else { - PRINTF("#var: "); - print_error(error=NO_NUM_VALUE_ERROR); - } - *VAR[idx].num = len * type; - } - else { - *VAR[idx].str = ptrmcpy(*VAR[idx].str, arg, strlen(arg)); - if (MEM_ERROR) - print_error(error); - } - ptrdel(pbuf); -} - -static void cmd_setvar(char *arg) -{ - char *name; - int i, func = 0; /* show */ - long buf; - - name = arg = skipspace(arg); - arg = first_regular(arg, '='); - if (*arg) { - *arg++ = '\0'; - if (*arg) { - func = 1; /* set */ - if (*arg == '(') { - arg++; - i = evall(&buf, &arg); - if (!REAL_ERROR && i != TYPE_NUM) - error=NO_NUM_VALUE_ERROR; - else if (!REAL_ERROR && *arg != ')') - error=MISSING_PAREN_ERROR; - } else - buf = strtol(arg, NULL, 0); - } else - buf = 0; - - if (REAL_ERROR) { - PRINTF("#setvar: "); - print_error(error); - return; - } - } - - i = strlen(name); - if (i && !strncmp(name, "timer", i)) { - vtime t; - update_now(); - if (func == 0) - sprintf(inserted_next, "#setvar timer=%ld", - diff_vtime(&now, &ref_time)); - else { - t.tv_usec = ((-buf) % mSEC_PER_SEC) * uSEC_PER_mSEC; - t.tv_sec = (-buf) / mSEC_PER_SEC; - ref_time.tv_usec = now.tv_usec; - ref_time.tv_sec = now.tv_sec; - add_vtime(&ref_time, &t); - } - } - else if (i && !strncmp(name, "lines", i)) { - if (func == 0) - sprintf(inserted_next, "#setvar lines=%d", lines); - else { - if (buf > 0) - lines = (int)buf; - if (opt_info) { - PRINTF("#setvar: lines=%d\n", lines); - } - } - } - else if (i && !strncmp(name, "mem", i)) { - if (func == 0) - sprintf(inserted_next, "#setvar mem=%d", limit_mem); - else { - if (buf == 0 || buf >= PARAMLEN) - limit_mem = buf <= INT_MAX ? (int)buf : INT_MAX; - if (opt_info) { - PRINTF("#setvar: mem=%d%s\n", limit_mem, - limit_mem ? "" : " (unlimited)"); - } - } - } - else if (i && !strncmp(name, "buffer", i)) { - if (func == 0) - sprintf(inserted_next, "#setvar buffer=%d", log_getsize()); - else - log_resize(buf); - } else { - update_now(); - PRINTF("#setvar buffer=%d\n#setvar lines=%d\n#setvar mem=%d\n#setvar timer=%ld\n", - log_getsize(), lines, limit_mem, diff_vtime(&now, &ref_time)); - } -} - -static void cmd_if(char *arg) -{ - long buf; - int type; - - arg = skipspace(arg); - if (*arg!='(') { - PRINTF("#if: "); - print_error(error=MISMATCH_PAREN_ERROR); - return; - } - arg++; /* skip the '(' */ - - type = evall(&buf, &arg); - if (!REAL_ERROR) { - if (type!=TYPE_NUM) - error=NO_NUM_VALUE_ERROR; - if (*arg != ')') - error=MISSING_PAREN_ERROR; - else { /* skip the ')' */ - if (*++arg == ' ') - arg++; - } - } - if (REAL_ERROR) { - PRINTF("#if: "); - print_error(error); - return; - } - - if (buf) - (void)parse_instruction(arg, 0, 0, 1); - else { - arg = get_next_instr(arg); - if (!strncmp(arg = skipspace(arg), "#else ", 6)) - (void)parse_instruction(arg + 6, 0, 0, 1); - } -} - -static void cmd_for(char *arg) -{ - int type = TYPE_NUM, loop=MAX_LOOP; - long buf; - char *check, *tmp, *increm = 0; - - arg = skipspace(arg); - if (*arg != '(') { - PRINTF("#for: "); - print_error(error=MISMATCH_PAREN_ERROR); - return; - } - push_params(); - if (REAL_ERROR) - return; - - arg = skipspace(arg + 1); /* skip the '(' */ - if (*arg != CMDSEP) - (void)evaln(&arg); /* execute */ - - check = arg + 1; - - if (REAL_ERROR) - ; - else if (*arg != CMDSEP) { - PRINTF("#for: "); - print_error(error=MISSING_SEPARATOR_ERROR); - } - else while (!error && loop - && (increm=check, (type = evall(&buf, &increm)) == TYPE_NUM - && !error && *increm == CMDSEP && buf)) { - - tmp = first_regular(increm + 1, ')'); - if (*tmp) - (void)parse_instruction(tmp + 1, 1, 1, 1); - else { - PRINTF("#for: "); - print_error(error=MISSING_PAREN_ERROR); - } - - if (!error) { - tmp = increm + 1; - if (*tmp != ')') - (void)evaln(&tmp); - } - - loop--; - } - if (REAL_ERROR) - ; - else if (increm && *increm != CMDSEP) - error=MISSING_SEPARATOR_ERROR; - else if (!loop) - error=MAX_LOOP_ERROR; - else if (type != TYPE_NUM) - error=NO_NUM_VALUE_ERROR; - if (REAL_ERROR) { - PRINTF("#for: "); - print_error(error); - } - if (error!=DYN_STACK_UND_ERROR && error!=DYN_STACK_OV_ERROR) - pop_params(); -} - -static void cmd_while(char *arg) -{ - int type = TYPE_NUM, loop=MAX_LOOP; - long buf; - char *check, *tmp; - - arg = skipspace(arg); - if (!*arg) { - PRINTF("#while: "); - print_error(error=MISMATCH_PAREN_ERROR); - return; - } - push_params(); - - check = ++arg; /* skip the '(' */ - while (!error && loop - && (arg=check, (type = evall(&buf, &arg)) == TYPE_NUM && - !error && *arg == ')' && buf)) { - - if (*(tmp = arg + 1) == ' ') /* skip the ')' */ - tmp++; - if (*tmp) - (void)parse_instruction(tmp, 1, 1, 1); - loop--; - } - if (REAL_ERROR) - ; - else if (*arg != ')') - error=MISSING_PAREN_ERROR; - else if (!loop) - error=MAX_LOOP_ERROR; - else if (type != TYPE_NUM) - error=NO_NUM_VALUE_ERROR; - if (REAL_ERROR) { - PRINTF("#while: "); - print_error(error); - } - if (error!=DYN_STACK_UND_ERROR && error!=DYN_STACK_OV_ERROR) - pop_params(); -} - -static void cmd_capture(char *arg) -{ - arg = skipspace(arg); - - if (!*arg) { - if (capturefile) { - log_flush(); - fclose(capturefile); - capturefile = NULL; - if (opt_info) { - PRINTF("#end of capture to file.\n"); - } - } else { - PRINTF("#capture to what file?\n"); - } - } else { - if (capturefile) { - PRINTF("#capture already active.\n"); - } else { - short append = 0; - /* Append to log file, if the name starts with '>' */ - if (*arg == '>') { - arg++; - append = 1; - } - if ((capturefile = fopen(arg, (append) ? "a" : "w")) == NULL) { - PRINTF("#error writing file \"%s\"\n", arg); - } else if (opt_info) { - PRINTF("#capture to \"%s\" active, \"#capture\" ends.\n", arg); - } - } - } -} - -static void cmd_movie(char *arg) -{ - arg = skipspace(arg); - - if (!*arg) { - if (moviefile) { - log_flush(); - fclose(moviefile); - moviefile = NULL; - if (opt_info) { - PRINTF("#end of movie to file.\n"); - } - } else { - PRINTF("#movie to what file?\n"); - } - } else { - if (moviefile) { - PRINTF("#movie already active.\n"); - } else { - if ((moviefile = fopen(arg, "w")) == NULL) { - PRINTF("#error writing file \"%s\"\n", arg); - } else { - if (opt_info) { - PRINTF("#movie to \"%s\" active, \"#movie\" ends.\n", arg); - } - update_now(); - movie_last = now; - log_clearsleep(); - } - } - } -} - -static void cmd_record(char *arg) -{ - arg = skipspace(arg); - - if (!*arg) { - if (recordfile) { - fclose(recordfile); - recordfile = NULL; - if (opt_info) { - PRINTF("#end of record to file.\n"); - } - } else { - PRINTF("#record to what file?\n"); - } - } else { - if (recordfile) { - PRINTF("#record already active.\n"); - } else { - if ((recordfile = fopen(arg, "w")) == NULL) { - PRINTF("#error writing file \"%s\"\n", arg); - } else if (opt_info) { - PRINTF("#record to \"%s\" active, \"#record\" ends.\n", arg); - } - } - } -} - -static void cmd_edit(char *arg) -{ - editsess *sp; - - if (edit_sess) { - for (sp = edit_sess; sp; sp = sp->next) { - PRINTF("# %s (%u)\n", sp->descr, sp->key); - } - } else { - PRINTF("#no active editors.\n"); - } -} - -static void cmd_cancel(char *arg) -{ - editsess *sp; - - if (!edit_sess) { - PRINTF("#no editing sessions to cancel.\n"); - } else { - if (*arg) { - for (sp = edit_sess; sp; sp = sp->next) - if (strtoul(arg, NULL, 10) == sp->key) { - cancel_edit(sp); - break; - } - if (!sp) { - PRINTF("#unknown editing session %d\n", atoi(arg)); - } - } else { - if (edit_sess->next) { - PRINTF("#several editing sessions active, use #cancel \n"); - } else - cancel_edit(edit_sess); - } - } -} - -static void cmd_net(char *arg) -{ - PRINTF("#received from host: %ld chars, sent to host: %ld chars.\n", - received, sent); -} - - -#ifndef CLOCKS_PER_SEC -# define CLOCKS_PER_SEC uSEC_PER_SEC -#endif -/* hope it works.... */ - -static void cmd_cpu(char *arg) -{ - float f, l; - update_now(); - f = (float)((cpu_clock = clock()) - start_clock) / (float)CLOCKS_PER_SEC; - l = (float)(diff_vtime(&now, &start_time)) / (float)mSEC_PER_SEC; - PRINTF("#CPU time used: %.3f sec. (%.2f%%)\n", f, - (l > 0 && l > f) ? f * 100.0 / l : 100.0); -} - -void show_stat(void) -{ - cmd_net(NULL); - cmd_cpu(NULL); -} - -#ifdef BUG_TELNET -static void cmd_color(char *arg) -{ - int attrcode; - - arg = skipspace(arg); - if (!*arg) { - strcpy(tty_modenorm, tty_modenormbackup); - tty_puts(tty_modenorm); - if (opt_info) { - PRINTF("#standard color cleared.\n"); - } - return; - } - - attrcode = parse_attributes(arg); - if (attrcode == -1) { - PRINTF("#invalid attribute syntax.\n"); - if (opt_info) - show_attr_syntax(); - } else { - int bg = BACKGROUND(attrcode), fg = FOREGROUND(attrcode); - if (fg >= COLORS || bg >= COLORS) { - PRINTF("#please specify foreground and background colors.\n"); - } else { - sprintf(tty_modenorm, "\033[;%c%d;%s%dm", - fg= width) { - prefix = "\n#option"; - len = -1; - } else { - prefix = ""; - } - } - if (file) { - fputc('\n', file); - } else { - tty_putc('\n'); - status(1); - } - return 1; -} - -static void cmd_option(char *arg) -{ - char buf[BUFSIZE]; - int count = 0; - - arg = skipspace(arg); - if (!*arg) { - print_all_options(NULL); - return; - } - - while ((arg = skipspace(split_first_word(buf, BUFSIZE, arg))), *buf) { - enum { MODE_ON, MODE_OFF, MODE_TOGGLE, MODE_REP } mode; - char *varp = NULL; - char *p = buf; - char c = *p; - int len = strlen(p); - int i; - - switch (c) { - case '=': mode = MODE_REP; p++; break; - case '+': mode = MODE_ON; p++; break; - case '-': mode = MODE_OFF; p++; break; - default: mode = MODE_TOGGLE; break; - } - count++; - for (i = 0; options[i].name; i++) { - if (strncmp(options[i].name, p, len) == 0) { - varp = options[i].option; - break; - } - } - if (varp == NULL) { - if (strncmp("list", p, len) == 0) { - tty_puts("#list of options:\n"); - for (i = 0; options[i].name; ++i) { - tty_printf("#option %c%-12s %s\n", - *options[i].option ? '+' : '-', - options[i].name, - options[i].doc); - } - } else { - tty_puts("#syntax: #option [[+|-|=]] | list\n"); - } - status(1); - return; - } - - switch (mode) { - case MODE_REP: - sprintf(inserted_next, "#option %c%s", *varp ? '+' : '-', - p); - break; - case MODE_ON: *varp = 1; break; - case MODE_OFF: *varp = 0; break; - case MODE_TOGGLE: *varp ^= 1; break; - } - /* - * reset the reprint buffer if changing its status - */ - if (varp == &opt_reprint) - reprint_clear(); - - /* as above, but always print status if - * "#option info" alone was typed */ - if (mode != MODE_REP && !*arg && count==1 && - (opt_info || (mode == MODE_TOGGLE && varp==&opt_info))) { - PRINTF("#option %s is now o%s.\n", - options[i].name, - *varp ? "n" : "ff"); - } - } -} - -static void cmd_file(char *arg) -{ - arg = skipspace(arg); - if (*arg == '=') { - set_deffile(++arg); - if (opt_info) { - if (*arg) { - PRINTF("#save-file set to \"%s\"\n", deffile); - } else { - PRINTF("#save-file is now undefined.\n"); - } - } - } else if (*deffile) { - sprintf(inserted_next, "#file =%.*s", BUFSIZE-8, deffile); - } else { - PRINTF("#save-file not defined.\n"); - } -} - -static void cmd_save(char *arg) -{ - arg = skipspace(arg); - if (*arg) { - set_deffile(arg); - if (opt_info) { - PRINTF("#save-file set to \"%s\"\n", deffile); - } - } else if (!*deffile) { - PRINTF("#save-file not defined.\n"); - return; - } - - if (*deffile && save_settings() > 0 && opt_info) { - PRINTF("#settings saved to file.\n"); - } -} - -static void cmd_load(char *arg) -{ - int res; - - arg = skipspace(arg); - if (*arg) { - set_deffile(arg); - if (opt_info) { - PRINTF("#save-file set to \"%s\"\n", deffile); - } - } - else if (!*deffile) { - PRINTF("#save-file not defined.\n"); - return; - } - - res = read_settings(); - - if (res > 0) { - /* success */ - if (opt_info) { - PRINTF("#settings loaded from file.\n"); - } - } else if (res < 0) { - /* critical error */ - while (keydefs) - delete_keynode(&keydefs); - tty_add_initial_binds(); - tty_add_walk_binds(); - limit_mem = 1048576; - PRINTF("#emergency loaded default settings.\n"); - } -} - -static char *trivial_eval(ptr *pbuf, char *arg, char *name) -{ - char *tmp = skipspace(arg); - - if (!pbuf) - return NULL; - - if (*tmp=='(') { - arg = tmp + 1; - (void)evalp(pbuf, &arg); - if (!REAL_ERROR && *arg != ')') - error=MISSING_PAREN_ERROR; - if (REAL_ERROR) { - PRINTF("#%s: ", name); - print_error(error); - return NULL; - } - if (*pbuf) - arg = ptrdata(*pbuf); - else - arg = ""; - } - else - unescape(arg); - - return arg; -} - -static void do_cmd_add(char *arg, int is_static) -{ - ptr pbuf = (ptr)0; - char buf[BUFSIZE]; - - arg = trivial_eval(&pbuf, arg, "add"); - if (!REAL_ERROR) - while (*arg) { - arg = split_first_word(buf, BUFSIZE, arg); - if (strlen(buf) >= MIN_WORDLEN) - (is_static ? put_static_word : put_word)(buf); - } - ptrdel(pbuf); -} - -static void cmd_add(char *arg) -{ - do_cmd_add(arg, 0); -} - -static void cmd_addstatic(char *arg) -{ - do_cmd_add(arg, 1); -} - -static void cmd_put(char *arg) -{ - ptr pbuf = (ptr)0; - arg = trivial_eval(&pbuf, arg, "put"); - if (!REAL_ERROR && *arg) - put_history(arg); - ptrdel(pbuf); -} - - diff --git a/cmd.h b/cmd.h deleted file mode 100644 index 74cf53f..0000000 --- a/cmd.h +++ /dev/null @@ -1,26 +0,0 @@ -/* public things from cmd.c */ - -#ifndef _CMD_H_ -#define _CMD_H_ - -typedef struct cmdstruct { - char *sortname; /* set to NULL if you want to sort by - * command name */ - char *name; /* command name */ - char *help; /* short help */ - function_str funct; /* function to call */ - struct cmdstruct *next; -} cmdstruct; - -extern cmdstruct *commands; - -void show_stat(void); - -void cmd_add_command( cmdstruct *cmd ); - -void initialize_cmd(void); - -int print_all_options(FILE *file); - -#endif /* _CMD_H_ */ - diff --git a/cmd2.c b/cmd2.c deleted file mode 100644 index dbd264a..0000000 --- a/cmd2.c +++ /dev/null @@ -1,1676 +0,0 @@ -/* - * cmd2.c -- back-end for the various #commands - * - * (created: Massimiliano Ghilardi (Cosmos), Aug 14th, 1998) - * - * Copyright (C) 1998 by Massimiliano Ghilardi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int strcasecmp(); -int select(); - -#include "defines.h" -#include "main.h" -#include "feature/regex.h" -#include "utils.h" -#include "beam.h" -#include "edit.h" -#include "list.h" -#include "map.h" -#include "tcp.h" -#include "tty.h" -#include "eval.h" - -/* anyone knows if ANSI 6429 talks about more than 8 colors? */ -static char *colornames[] = { - "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", - "BLACK", "RED", "GREEN", "YELLOW", "BLUE", "MAGENTA", "CYAN", "WHITE", "none" -}; - -/* - * show defined aliases - */ -void show_aliases(void) -{ - aliasnode *p; - char buf[BUFSIZE]; - - PRINTF("#%s alias%s defined%c\n", sortedaliases ? "the following" : "no", - (sortedaliases && !sortedaliases->snext) ? " is" : "es are", - sortedaliases ? ':' : '.'); - reverse_sortedlist((sortednode **)&sortedaliases); - for (p = sortedaliases; p; p = p->snext) { - escape_specials(buf, p->name); - tty_printf("#alias %s%s%s%s=%s\n", - p->active ? "" : "(disabled) ", - buf, group_delim, p->group == NULL ? "*" : p->group, p->subst); - } - reverse_sortedlist((sortednode **)&sortedaliases); -} - -/* - * check if an alias name contains dangerous things. - * return 1 if illegal name (and print reason). - * if valid, print a warning for unbalanced () {} and "" - */ -static int check_alias(char *name) -{ - char *p = name, c; - enum { NORM, ESCAPE } state = NORM; - int quotes=0, paren=0, braces=0, ok = 1; - - if (!*p) { - PRINTF("#illegal alias name: is empty!\n"); - error = INVALID_NAME_ERROR; - return 1; - } - if (*p == '{') { - PRINTF("#illegal beginning '{' in alias name: \"%s\"\n", name); - error = INVALID_NAME_ERROR; - return 1; - } - if (strchr(name, ' ')) { - PRINTF("#illegal spaces in alias name: \"%s\"\n", name); - error = INVALID_NAME_ERROR; - return 1; - } - - for (; ok && (c = *p); p++) switch (state) { - case NORM: - if (c == ESC) - state = ESCAPE; - else if (quotes) { - if (c == '\"') - quotes = 0; - } - else if (c == '\"') - quotes = 1; - else if (c == ')') - paren--; - else if (c == '(') - paren++; - else if (c == '}') - braces--; - else if (c == '{') - braces++; - else if (c == CMDSEP && !paren && !braces) - ok = 0; - break; - case ESCAPE: - if (c == ESC) - state = ESCAPE; - else /* if (c == ESC2 || c != ESC2) */ - state = NORM; - default: - break; - } - - if (!ok) { - PRINTF("#illegal non-escaped ';' in alias name: \"%s\"\n", name); - error = INVALID_NAME_ERROR; - return 1; - } - - if (quotes || paren || braces) { - PRINTF("#warning: unbalanced%s%s%s in alias name \"%s\" may cause problems\n", - quotes ? " \"\"" : "", paren ? " ()" : "", braces ? " {}" : "", name); - } - - return 0; -} - - -/* - * parse the #alias command - */ -void parse_alias(char *str) -{ - char *left, *right, *group; - aliasnode **np, *p; - - left = str = skipspace(str); - - str = first_valid(str, '='); - - if (*str == '=') { - *str = '\0'; - right = ++str; - unescape(left); - - /* break out group name (if present) */ - group = strstr( left, group_delim ); - if( group ) { - *group = 0; - group += strlen( group_delim ); - } - - if (check_alias(left)) - return; - p = *(np = lookup_alias(left)); - if (!*str) { - /* delete alias */ - if (p) { - if (opt_info) { - PRINTF("#deleting alias: %s=%s\n", left, p->subst); - } - delete_aliasnode(np); - } else { - PRINTF("#unknown alias, cannot delete: \"%s\"\n", left); - } - } else { - /* add/redefine alias */ - - /* direct recursion is supported (alias CAN be defined by itself) */ - if (p) { - free(p->subst); - p->subst = my_strdup(right); - } else - add_aliasnode(left, right); - - /* get alias again to add group (if needed) - * don't take the lookup penalty though if not changing groups */ - if( group != NULL ) { - np = lookup_alias(left); - if( (*np)->group != NULL ) - free((*np)->group); - - if ( *group == '\0' || strcmp(group,"*") == 0 ) - group = NULL; - - (*np)->group = my_strdup(group); - } - - if (opt_info) { - PRINTF("#%s alias in group '%s': %s=%s\n", p ? "changed" : "new", - group == NULL ? "*" : group, left, right); - } - } - } else { - /* show alias */ - - *str = '\0'; - unescape(left); - if (check_alias(left)) - return; - np = lookup_alias(left); - if (*np) { - char buf[BUFSIZE]; - escape_specials(buf, left); - snprintf(inserted_next, BUFSIZE, "#alias %s%s%s=%s", - buf, - group_delim, - (*np)->group == NULL ? "*" : (*np)->group, - (*np)->subst); - } else { - PRINTF("#unknown alias, cannot show: \"%s\"\n", left); - } - } -} - -/* - * delete an action node - */ -static void delete_action(actionnode **nodep) -{ - if (opt_info) { - PRINTF("#deleting action: >%c%s %s\n", (*nodep)->active ? - '+' : '-', (*nodep)->label, (*nodep)->pattern); - } - delete_actionnode(nodep); -} - -/* - * delete a prompt node - */ -static void delete_prompt(actionnode **nodep) -{ - if (opt_info) { - PRINTF("#deleting prompt: >%c%s %s\n", (*nodep)->active ? - '+' : '-', (*nodep)->label, (*nodep)->pattern); - } - delete_promptnode(nodep); -} - -/* - * create new action - */ -static void add_new_action(char *label, char *pattern, char *command, int active, int type, void *q) -{ - add_actionnode(pattern, command, label, active, type, q); - if (opt_info) { - PRINTF("#new action: %c%c%s %s=%s\n", - action_chars[type], - active ? '+' : '-', label, - pattern, command); - } -} - -/* - * create new prompt - */ -static void add_new_prompt(char *label, char *pattern, char *command, int active, int type, void *q) -{ - add_promptnode(pattern, command, label, active, type, q); - if (opt_info) { - PRINTF("#new prompt: %c%c%s %s=%s\n", - action_chars[type], - active ? '+' : '-', label, - pattern, command); - } -} - -/* - * add an action with numbered label - */ -static void add_anonymous_action(char *pattern, char *command, int type, void *q) -{ - static int last = 0; - char label[16]; - do { - sprintf(label, "%d", ++last); - } while (*lookup_action(label)); - add_new_action(label, pattern, command, 1, type, q); -} - -#define ONPROMPT (onprompt ? "prompt" : "action") - -/* - * change fields of an existing action node - * pattern or commands can be NULL if no change - */ -static void change_actionorprompt(actionnode *node, char *pattern, char *command, int type, void *q, int onprompt) -{ -#ifdef USE_REGEXP - if (node->type == ACTION_REGEXP && node->regexp) { - regfree((regex_t *)(node->regexp)); - free(node->regexp); - } - node->regexp = q; -#endif - if (pattern) { - free(node->pattern); - node->pattern = my_strdup(pattern); - node->type = type; - } - if (command) { - free(node->command); - node->command = my_strdup(command); - } - - if (opt_info) { - PRINTF("#changed %s %c%c%s %s=%s\n", ONPROMPT, - action_chars[node->type], - node->active ? '+' : '-', - node->label, node->pattern, node->command); - } -} - -/* - * show defined actions - */ -void show_actions(void) -{ - actionnode *p; - - PRINTF("#%s action%s defined%c\n", actions ? "the following" : "no", - (actions && !actions->next) ? " is" : "s are", actions ? ':' : '.'); - for (p = actions; p; p = p->next) - tty_printf("#action %c%c%s%s%s %s=%s\n", - action_chars[p->type], - p->active ? '+' : '-', p->label, - group_delim, - p->group == NULL ? "*" : p->group, - p->pattern, - p->command); -} - -/* - * show defined prompts - */ -void show_prompts(void) -{ - promptnode *p; - - PRINTF("#%s prompt%s defined%c\n", prompts ? "the following" : "no", - (prompts && !prompts->next) ? " is" : "s are", prompts ? ':' : '.'); - for (p = prompts; p; p = p->next) - tty_printf("#prompt %c%c%s %s=%s\n", - action_chars[p->type], - p->active ? '+' : '-', p->label, - p->pattern, p->command); -} - -/* - * parse the #action and #prompt commands - * this function is too damn complex because of the hairy syntax. it should be - * split up or rewritten as an fsm instead. - */ -void parse_action(char *str, int onprompt) -{ - char *p, label[BUFSIZE], pattern[BUFSIZE], *command, *group; - actionnode **np = NULL; - char sign, assign, hastail; - char active, type = ACTION_WEAK, kind; - void *regexp = 0; - - sign = *(p = skipspace(str)); - if (!sign) { - PRINTF("%s: no arguments given\n", ONPROMPT); - return; - } - - str = p + 1; - - switch (sign) { - case '+': - case '-': /* edit */ - case '<': /* delete */ - case '=': /* list */ - assign = sign; - break; - case '%': /* action_chars[ACTION_REGEXP] */ - type = ACTION_REGEXP; - /* falltrough */ - case '>': /* action_chars[ACTION_WEAK] */ - - /* define/edit */ - assign = '>'; - sign = *(p + 1); - if (!sign) { - PRINTF("#%s: label expected\n", ONPROMPT); - return; - } else if (sign == '+' || sign == '-') - str++; - else - sign = '+'; - break; - default: - assign = 0; /* anonymous action */ - str = p; - break; - } - - /* labelled action: */ - if (assign != 0) { - p = first_regular(str, ' '); - if ((hastail = *p)) - *p = '\0'; - - /* break out group name (if present) */ - group = strstr( str, group_delim ); - if( group ) { - *group = 0; - group += strlen( group_delim ); - } - - my_strncpy(label, str, BUFSIZE-1); - if (hastail) - *p++ = ' '; /* p points to start of pattern, or to \0 */ - - if (!*label) { - PRINTF("#%s: label expected\n", ONPROMPT); - return; - } - - - if (onprompt) - np = lookup_prompt(label); - else - np = lookup_action(label); - - /* '<' : remove action */ - if (assign == '<') { - if (!np || !*np) { - PRINTF("#no %s, cannot delete label: \"%s\"\n", - ONPROMPT, label); - } - else { - if (onprompt) - delete_prompt(np); - else - delete_action(np); - } - - /* '>' : define action */ - } else if (assign == '>') { -#ifndef USE_REGEXP - if (type == ACTION_REGEXP) { - PRINTF("#error: regexp not allowed\n"); - return; - } -#endif - - if (sign == '+') - active = 1; - else - active = 0; - - if (!*label) { - PRINTF("#%s: label expected\n", ONPROMPT); - return; - } - - p = skipspace(p); - if (*p == '(') { - ptr pbuf = (ptr)0; - p++; - kind = evalp(&pbuf, &p); - if (!REAL_ERROR && kind != TYPE_TXT) - error=NO_STRING_ERROR; - if (REAL_ERROR) { - PRINTF("#%s: ", ONPROMPT); - print_error(error=NO_STRING_ERROR); - ptrdel(pbuf); - return; - } - if (pbuf) { - my_strncpy(pattern, ptrdata(pbuf), BUFSIZE-1); - ptrdel(pbuf); - } else - pattern[0] = '\0'; - if (*p) - p = skipspace(++p); - if ((hastail = *p == '=')) - p++; - } - else { - p = first_regular(command = p, '='); - if ((hastail = *p)) - *p = '\0'; - my_strncpy(pattern, command, BUFSIZE-1); - - if (hastail) - *p++ = '='; - } - - if (!*pattern) { - PRINTF("#error: pattern of #%ss must be non-empty.\n", ONPROMPT); - return; - } - -#ifdef USE_REGEXP - if (type == ACTION_REGEXP && hastail) { - int errcode; - char unesc_pat[BUFSIZE]; - - /* - * HACK WARNING: - * we unescape regexp patterns now, instead of doing - * jit+unescape at runtime, as for weak actions. - */ - strcpy(unesc_pat, pattern); - unescape(unesc_pat); - - regexp = malloc(sizeof(regex_t)); - if (!regexp) { - errmsg("malloc"); - return; - } - - if ((errcode = regcomp((regex_t *)regexp, unesc_pat, REG_EXTENDED))) { - int n; - char *tmp; - n = regerror(errcode, (regex_t *)regexp, NULL, 0); - tmp = (char *)malloc(n); - if (tmp) { - if (!regerror(errcode, (regex_t *)regexp, tmp, n)) - errmsg("regerror"); - else { - PRINTF("#regexp error: %s\n", tmp); - } - free(tmp); - } else { - error = NO_MEM_ERROR; - errmsg("malloc"); - } - regfree((regex_t *)regexp); - free(regexp); - return; - } - } -#endif - command = p; - - if (hastail) { - if (np && *np) { - change_actionorprompt(*np, pattern, command, type, regexp, onprompt); - (*np)->active = active; - } else { - if (onprompt) - add_new_prompt(label, pattern, command, active, - type, regexp); - else - add_new_action(label, pattern, command, active, - type, regexp); - } - - if( group != NULL ) { - /* I don't know why but we need to clip this because somehow - * the original string is restored to *p at some point instead - * of the null-clipped one we used waaaay at the top. */ - p = first_regular(group, ' '); - *p = '\0'; - np = lookup_action(label); - if( (*np)->group != NULL ) - free( (*np)->group ); - - if ( *group == '\0' || strcmp(group,"*") == 0 ) - group = NULL; - - (*np) -> group = my_strdup( group ); - } - } - - /* '=': list action */ - } else if (assign == '='){ - if (np && *np) { - int len = (int)strlen((*np)->label); - sprintf(inserted_next, "#%s %c%c%.*s %.*s=%.*s", ONPROMPT, - action_chars[(*np)->type], (*np)->active ? '+' : '-', - BUFSIZE - 6 /*strlen(ONPROMPT)*/ - 7, (*np)->label, - BUFSIZE - 6 /*strlen(ONPROMPT)*/ - 7 - len, (*np)->pattern, - BUFSIZE - 6 /*strlen(ONPROMPT)*/ - 7 - len - (int)strlen((*np)->pattern), - (*np)->command); - } else { - PRINTF("#no %s, cannot list label: \"%s\"\n", ONPROMPT, label); - } - - /* '+', '-': turn action on/off */ - } else { - if (np && *np) { - (*np)->active = (sign == '+'); - if (opt_info) { - PRINTF("#%s %c%s %s is now o%s.\n", ONPROMPT, - action_chars[(*np)->type], - label, - (*np)->pattern, (sign == '+') ? "n" : "ff"); - } - } else { - PRINTF("#no %s, cannot turn o%s label: \"%s\"\n", ONPROMPT, - (sign == '+') ? "n" : "ff", label); - } - } - - /* anonymous action, cannot be regexp */ - } else { - - if (onprompt) { - PRINTF("#anonymous prompts not supported.\n#please use labelled prompts.\n"); - return; - } - - command = first_regular(str, '='); - - if (*command == '=') { - *command = '\0'; - - my_strncpy(pattern, str, BUFSIZE-1); - *command++ = '='; - np = lookup_action_pattern(pattern); - if (*command) - if (np && *np) - change_actionorprompt(*np, NULL, command, ACTION_WEAK, NULL, 0); - else - add_anonymous_action(pattern, command, ACTION_WEAK, NULL); - else if (np && *np) - delete_action(np); - else { - PRINTF("#no action, cannot delete pattern: \"%s\"\n", - pattern); - return; - } - } else { - np = lookup_action_pattern(str); - if (np && *np) { - sprintf(inserted_next, "#action %.*s=%.*s", - BUFSIZE - 10, (*np)->pattern, - BUFSIZE - (int)strlen((*np)->pattern) - 10, - (*np)->command); - } else { - PRINTF("#no action, cannot show pattern: \"%s\"\n", str); - } - } - } -} - -#undef ONPROMPT - -/* - * display attribute syntax - */ -void show_attr_syntax(void) -{ - int i; - PRINTF("#attribute syntax:\n\tOne or more of:\tbold, blink, underline, inverse\n\tand/or\t[foreground] [ON background]\n\tColors: "); - for (i = 0; i < COLORS; i++) - tty_printf("%s%s", colornames[i], - (i == LOWCOLORS - 1 || i == COLORS - 1) ? "\n\t\t" : ","); - tty_printf("%s\n", colornames[i]); -} - -/* - * put escape sequences to turn on/off an attribute in given buffers - */ -void attr_string(int attrcode, char *begin, char *end) -{ - int fg = FOREGROUND(attrcode), bg = BACKGROUND(attrcode), - tok = ATTR(attrcode); - char need_end = 0; - *begin = *end = '\0'; - - if (tok > (ATTR_BOLD | ATTR_BLINK | ATTR_UNDERLINE | ATTR_INVERSE) - || fg > COLORS || bg > COLORS || attrcode == NOATTRCODE) - return; /* do nothing */ - - if (fg < COLORS) { - if (bg < COLORS) { - sprintf(begin, "\033[%c%d;%s%dm", - fg (ATTR_BOLD | ATTR_BLINK | ATTR_UNDERLINE | ATTR_INVERSE) || fg > COLORS || bg > COLORS) - return name; /* error! */ - - if (tok & ATTR_BOLD) - strcat(name, "bold "); - if (tok & ATTR_BLINK) - strcat(name, "blink "); - if (tok & ATTR_UNDERLINE) - strcat(name, "underline "); - if (tok & ATTR_INVERSE) - strcat(name, "inverse "); - - if (fg < COLORS || (fg == bg && fg == COLORS && !tok)) - strcat(name, colornames[fg]); - - if (bg < COLORS) { - strcat(name, " on "); - strcat(name, colornames[bg]); - } - - if (!*name) - strcpy(name, "none"); - - return name; -} - -/* - * show defined marks - */ -void show_marks(void) -{ - marknode *p; - PRINTF("#%s marker%s defined%c\n", markers ? "the following" : "no", - (markers && !markers->next) ? " is" : "s are", - markers ? ':' : '.'); - for (p = markers; p; p = p->next) - tty_printf("#mark %s%s=%s\n", p->mbeg ? "^" : "", - p->pattern, attr_name(p->attrcode)); -} - - -/* - * parse arguments to the #mark command - */ -void parse_mark(char *str) -{ - char *p; - marknode **np, *n; - char mbeg = 0; - - if (*str == '=') { - PRINTF("#marker must be non-null.\n"); - return; - } - p = first_regular(str, '='); - if (!*p) { - if (*str == '^') - mbeg = 1, str++; - unescape(str); - np = lookup_marker(str, mbeg); - if ((n = *np)) { - ptr pbuf = (ptr)0; - char *name; - pbuf = ptrmescape(pbuf, n->pattern, strlen(n->pattern), 0); - if (MEM_ERROR) { ptrdel(pbuf); return; } - name = attr_name(n->attrcode); - sprintf(inserted_next, "#mark %s%.*s=%.*s", n->mbeg ? "^" : "", - BUFSIZE-(int)strlen(name)-9, pbuf ? ptrdata(pbuf) : "", - BUFSIZE-9, name); - ptrdel(pbuf); - } else { - PRINTF("#unknown marker, cannot show: \"%s\"\n", str); - } - - } else { - int attrcode, wild = 0; - char pattern[BUFSIZE], *p2; - - *(p++) = '\0'; - p = skipspace(p); - if (*str == '^') - mbeg = 1, str++; - my_strncpy(pattern, str, BUFSIZE-1); - unescape(pattern); - p2 = pattern; - while (*p2) { - if (ISMARKWILDCARD(*p2)) { - wild = 1; - if (ISMARKWILDCARD(*(p2 + 1))) { - error=SYNTAX_ERROR; - PRINTF("#error: two wildcards (& or $) may not be next to eachother\n"); - return; - } - } - p2++; - } - - np = lookup_marker(pattern, mbeg); - attrcode = parse_attributes(p); - if (attrcode == -1) { - PRINTF("#invalid attribute syntax.\n"); - error=SYNTAX_ERROR; - if (opt_info) show_attr_syntax(); - } else if (!*p) - if ((n = *np)) { - if (opt_info) { - PRINTF("#deleting mark: %s%s=%s\n", n->mbeg ? "^" : "", - n->pattern, attr_name(n->attrcode)); - } - delete_marknode(np); - } else { - PRINTF("#unknown marker, cannot delete: \"%s%s\"\n", - mbeg ? "^" : "", pattern); - } - else { - if (*np) { - (*np)->attrcode = attrcode; - if (opt_info) { - PRINTF("#changed"); - } - } else { - add_marknode(pattern, attrcode, mbeg, wild); - if (opt_info) { - PRINTF("#new"); - } - } - if (opt_info) - tty_printf(" mark: %s%s=%s\n", mbeg ? "^" : "", - pattern, attr_name(attrcode)); - } - } -} - -/* - * turn ASCII description of a sequence - * into raw escape sequence - * return pointer to end of ASCII description - */ -static char *unescape_seq(char *buf, char *seq, int *seqlen) -{ - char c, *start = buf; - enum { NORM, ESCSINGLE, ESCAPE, CARET, DONE } state = NORM; - - for (; (c = *seq); seq++) { - switch (state) { - case NORM: - if (c == '^') - state = CARET; - else if (c == ESC) - state = ESCSINGLE; - else if (c == '=') - state = DONE; - else - *(buf++) = c; - break; - case CARET: - /* - * handle ^@ ^A ... ^_ as expected: - * ^@ == 0x00, ^A == 0x01, ... , ^_ == 0x1f - */ - if (c > 0x40 && c < 0x60) - *(buf++) = c & 0x1f; - /* special case: ^? == 0x7f */ - else if (c == '?') - *(buf++) = 0x7f; - state = NORM; - break; - case ESCSINGLE: - case ESCAPE: - /* - * GH: \012 ==> octal number - */ - if (state == ESCSINGLE && - ISODIGIT(seq[0]) && ISODIGIT(seq[1]) && ISODIGIT(seq[2])) { - *(buf++) =(((seq[0] - '0') << 6) | - ((seq[1] - '0') << 3) | - (seq[2] - '0')); - seq += 2; - } else { - *(buf++) = c; - if (c == ESC) - state = ESCAPE; - else - state = NORM; - } - break; - default: - break; - } - if (state == DONE) - break; - } - *buf = '\0'; - *seqlen = buf - start; - return seq; -} - -/* - * read a single character from tty, with timeout in milliseconds. - * timeout == 0 means wait indefinitely (no timeout). - * return char or -1 if timeout was reached. - */ -static int get_one_char(int timeout) -{ - struct timeval timeoutbuf; - fd_set fds; - int n; - char c; - - again: - FD_ZERO(&fds); - FD_SET(tty_read_fd, &fds); - timeoutbuf.tv_sec = 0; - timeoutbuf.tv_usec = timeout * uSEC_PER_mSEC; - n = select(tty_read_fd + 1, &fds, NULL, NULL, - timeout ? &timeoutbuf : NULL); - if (n == -1 && errno == EINTR) - return -1; - if (n == -1) { - errmsg("select"); - return -1; - } - do { - n = tty_read(&c, 1); - } while (n < 0 && errno == EINTR); - if (n == 1) - return (unsigned char)c; - if (n < 0) { - if (errno != EAGAIN) - errmsg("read from tty"); - return -1; - } - /* n == 0 */ - if (timeout == 0) - goto again; - return -1; -} - -/* - * print an escape sequence in human-readably form. - */ -void print_seq(char *seq, int len) -{ - while (len--) { - unsigned char ch = *(seq++); - if (ch == '\033') { - tty_puts("esc "); - continue; - } - if (ch < ' ') { - tty_putc('^'); - ch |= '@'; - } - if (ch == ' ') - tty_puts("space "); - else if (ch == 0x7f) - tty_puts("del "); - else if (ch & 0x80) - tty_printf("\\%03o ", ch); - else - tty_printf("%c ", ch); - } -} - -/* - * return a static pointer to escape sequence made printable, for use in - * definition-files - */ -char *seq_name(char *seq, int len) -{ - static char buf[CAPLEN*4]; - char *p = buf; - /* - * rules: control chars are written as ^X, where - * X is char | 64 - * - * GH: codes > 0x80 ==> octal \012 - * - * special case: 0x7f is written ^? - */ - while (len--) { - unsigned char c = *seq++; - if (c == '^' || (c && strchr(SPECIAL_CHARS, c))) - *(p++) = ESC; - - if (c < ' ') { - *(p++) = '^'; - *(p++) = c | '@'; - } else if (c == 0x7f) { - *(p++) = '^'; - *(p++) = '?'; - } else if (c & 0x80) { - /* GH: save chars with high bit set in octal */ - sprintf(p, "\\%03o", (int)c); - p += strlen(p); - } else - *(p++) = c; - } - *p = '\0'; - return buf; -} - -/* - * read a single escape sequence from the keyboard - * prompting user for it; return static pointer - */ -char *read_seq(char *name, int *len) -{ - static char seq[CAPLEN]; - int i = 1, tmp; - - PRINTF("#please press the key \"%s\" : ", name); - tty_flush(); - - if ((tmp = get_one_char(0)) >= 0) - seq[0] = tmp; - else { - tty_puts("#unable to get key. Giving up.\n"); - return NULL; - } - - while (i < CAPLEN - 1 && - (tmp = get_one_char(KBD_TIMEOUT)) >= 0) - seq[i++] = tmp; - *len = i; - print_seq(seq, i); - - tty_putc('\n'); - if (seq[0] >= ' ' && seq[0] <= '~') { - PRINTF("#that is not a redefinable key.\n"); - return NULL; - } - return seq; -} - -/* - * show full definition of one binding, - * with custom message - */ -static void show_single_bind(char *msg, keynode *p) -{ - if (p->funct == key_run_command) { - PRINTF("#%s %s %s=%s\n", msg, p->name, - seq_name(p->sequence, p->seqlen), p->call_data); - } else { - PRINTF("#%s %s %s=%s%s%s\n", msg, p->name, - seq_name(p->sequence, p->seqlen), - internal_functions[lookup_edit_function(p->funct)].name, - p->call_data ? " " : "", - p->call_data ? p->call_data : ""); - } -} - -/* - * list keyboard bindings - */ -void show_binds(char edit) -{ - keynode *p; - int count = 0; - for (p = keydefs; p; p = p->next) { - if (edit != (p->funct == key_run_command)) { - if (!count) { - if (edit) { - PRINTF("#line-editing keys:\n"); - } else { - PRINTF("#user-defined keys:\n"); - } - } - show_single_bind("bind", p); - ++count; - } - } - if (!count) { - PRINTF("#no key bindings defined right now.\n"); - } -} - - -/* - * interactively create a new keybinding - */ -static void define_new_key(char *name, char *command) -{ - char *seq, *arg; - keynode *p; - int seqlen, function; - - seq = read_seq(name, &seqlen); - if (!seq) return; - - for (p = keydefs; p; p = p->next) - /* GH: don't allow binding of supersets of another bind */ - if (!memcmp(p->sequence, seq, MIN2(p->seqlen, seqlen))) { - show_single_bind("key already bound as:", p); - return; - } - - function = lookup_edit_name(command, &arg); - if (function) - add_keynode(name, seq, seqlen, - internal_functions[function].funct, arg); - else - add_keynode(name, seq, seqlen, key_run_command, command); - - if (opt_info) { - PRINTF("#new key binding: %s %s=%s\n", - name, seq_name(seq, seqlen), command); - } -} - -/* - * parse the #bind command non-interactively. - */ -static void parse_bind_noninteractive(char *arg) -{ - char rawseq[CAPLEN], *p, *seq, *params; - int function, seqlen; - keynode **kp; - - p = strchr(arg, ' '); - if (!p) { - PRINTF("#syntax error: \"#bind %s\"\n", arg); - return; - } - *(p++) = '\0'; - seq = p = skipspace(p); - - p = unescape_seq(rawseq, p, &seqlen); - if (!p[0] || !p[1]) { - PRINTF("#syntax error: \"#bind %s %s\"\n", arg, seq); - return; - } - *p++ = '\0'; - - kp = lookup_key(arg); - if (kp && *kp) - delete_keynode(kp); - - if ((function = lookup_edit_name(p, ¶ms))) - add_keynode(arg, rawseq, seqlen, - internal_functions[function].funct, params); - else - add_keynode(arg, rawseq, seqlen, key_run_command, p); - - if (opt_info) { - PRINTF("#%s: %s %s=%s\n", - (kp && *kp) ? "redefined key" : "new key binding", - arg, seq, p); - } -} - -/* - * parse the argument of the #bind command (interactive) - */ -void parse_bind(char *arg) -{ - char *p, *q, *command, *params; - char *name = arg; - keynode **npp, *np; - int function; - - p = first_valid(arg, '='); - q = first_valid(arg, ' '); - q = skipspace(q); - - if (*p && *q && p > q) { - parse_bind_noninteractive(arg); - return; - } - - if (*p) { - *(p++) = '\0'; - np = *(npp = lookup_key(name)); - if (*p) { - command = p; - if (np) { - if (np->funct == key_run_command) - free(np->call_data); - if ((function = lookup_edit_name(command, ¶ms))) { - np->call_data = my_strdup(params); - np->funct = internal_functions[function].funct; - } else { - np->call_data = my_strdup(command); - np->funct = key_run_command; - } - if (opt_info) { - PRINTF("#redefined key: %s %s=%s\n", name, - seq_name(np->sequence, np->seqlen), - command); - } - } else - define_new_key(name, command); - } else { - if (np) { - if (opt_info) - show_single_bind("deleting key binding:", np); - delete_keynode(npp); - } else { - PRINTF("#no such key: \"%s\"\n", name); - } - } - } else { - np = *(npp = lookup_key(name)); - if (np) { - char *seqname; - int seqlen; - seqname = seq_name(np->sequence, np->seqlen); - seqlen = strlen(seqname); - - if (np->funct == key_run_command) - sprintf(inserted_next, "#bind %.*s %s=%.*s", - BUFSIZE-seqlen-9, name, seqname, - BUFSIZE-seqlen-(int)strlen(name)-9, - np->call_data); - else { - p = internal_functions[lookup_edit_function(np->funct)].name; - sprintf(inserted_next, "#bind %.*s %s=%s%s%.*s", - BUFSIZE-seqlen-10, name, seqname, p, - np->call_data ? " " : "", - BUFSIZE-seqlen-(int)strlen(name)-(int)strlen(p)-10, - np->call_data ? np->call_data : ""); - } - } else { - PRINTF("#no such key: \"%s\"\n", name); - } - } -} - -void parse_rebind(char *arg) -{ - char rawseq[CAPLEN], *seq, **old; - keynode **kp, *p; - int seqlen; - - arg = skipspace(arg); - if (!*arg) { - PRINTF("#rebind: missing key.\n"); - return; - } - - seq = first_valid(arg, ' '); - if (*seq) { - *seq++ = '\0'; - seq = skipspace(seq); - } - - kp = lookup_key(arg); - if (!kp || !*kp) { - PRINTF("#no such key: \"%s\"\n", arg); - return; - } - - if (!*seq) { - seq = read_seq(arg, &seqlen); - if (!seq) - return; - } else { - (void)unescape_seq(rawseq, seq, &seqlen); - seq = rawseq; - } - - for (p = keydefs; p; p = p->next) { - if (p == *kp) - continue; - if (!memcmp(p->sequence, seq, MIN2(seqlen, p->seqlen))) { - show_single_bind("key already bound as:", p); - return; - } - } - - old = &((*kp)->sequence); - if (*old) - free(*old); - *old = (char *)malloc((*kp)->seqlen = seqlen); - memmove(*old, seq, seqlen); - - if (opt_info) - show_single_bind("redefined key:", *kp); -} - -/* - * evaluate an expression, or unescape a text. - * set value of start and end line if <(expression...) or !(expression...) - * if needed, use/malloc "pbuf" as buffer (on error, also free pbuf) - * return resulting char * - */ -char *redirect(char *arg, ptr *pbuf, char *kind, char *name, int also_num, long *start, long *end) -{ - char *tmp = skipspace(arg), k; - int type, i; - - if (!pbuf) { - print_error(error=INTERNAL_ERROR); - return NULL; - } - - k = *tmp; - if (k == '!' || k == '<') - arg = ++tmp; - else - k = 0; - - *start = *end = 0; - - if (*tmp=='(') { - - arg = tmp + 1; - type = evalp(pbuf, &arg); - if (!REAL_ERROR && type!=TYPE_TXT && !also_num) - error=NO_STRING_ERROR; - if (REAL_ERROR) { - PRINTF("#%s: ", name); - print_error(error); - ptrdel(*pbuf); - return NULL; - } - for (i=0; i<2; i++) if (*arg == CMDSEP) { - long buf; - - arg++; - if (!i && *arg == CMDSEP) { - *start = 1; - continue; - } - else if (i && *arg == ')') { - *end = LONG_MAX; - continue; - } - - type = evall(&buf, &arg); - if (!REAL_ERROR && type != TYPE_NUM) - error=NO_NUM_VALUE_ERROR; - if (REAL_ERROR) { - PRINTF("#%s: ", name); - print_error(error); - ptrdel(*pbuf); - return NULL; - } - if (i) - *end = buf; - else - *start = buf; - } - if (*arg != ')') { - PRINTF("#%s: ", name); - print_error(error=MISSING_PAREN_ERROR); - ptrdel(*pbuf); - return NULL; - } - if (!*pbuf) { - /* make space to add a final \n */ - *pbuf = ptrsetlen(*pbuf, 1); - ptrzero(*pbuf); - if (REAL_ERROR) { - print_error(error); - ptrdel(*pbuf); - return NULL; - } - } - arg = ptrdata(*pbuf); - if (!*start && *end) - *start = 1; - } else - unescape(arg); - - *kind = k; - return arg; -} - -void show_vars(void) -{ - varnode *v; - int i, type; - ptr p = (ptr)0; - - PRINTF("#the following variables are defined:\n"); - - for (type = 0; !REAL_ERROR && type < 2; type++) { - reverse_sortedlist((sortednode **)&sortednamed_vars[type]); - v = sortednamed_vars[type]; - while (v) { - if (type == 0) { - tty_printf("#(@%s = %ld)\n", v->name, v->num); - } else { - p = ptrescape(p, v->str, 0); - if (REAL_ERROR) { - print_error(error); - break; - } - tty_printf("#($%s = \"%s\")\n", v->name, - p ? ptrdata(p) : ""); - } - v = v->snext; - } - reverse_sortedlist((sortednode **)&sortednamed_vars[type]); - } - for (i = -NUMVAR; !REAL_ERROR && i < NUMPARAM; i++) { - if (*VAR[i].num) - tty_printf("#(@%d = %ld)\n", i, *VAR[i].num); - } - for (i = -NUMVAR; !REAL_ERROR && i < NUMPARAM; i++) { - if (*VAR[i].str && ptrlen(*VAR[i].str)) { - p = ptrescape(p, *VAR[i].str, 0); - if (p && ptrlen(p)) - tty_printf("#($%d = \"%s\")\n", i, ptrdata(p)); - } - } - ptrdel(p); -} - -void show_delaynode(delaynode *p, int in_or_at) -{ - long d; - struct tm *s; - char buf[BUFSIZE]; - - update_now(); - d = diff_vtime(&p->when, &now); - s = localtime((time_t *)&p->when.tv_sec); - /* s now points to a calendar struct */ - if (in_or_at) { - - if (in_or_at == 2) { - /* write time in buf */ - (void)strftime(buf, BUFSIZE - 1, "%H%M%S", s); - sprintf(inserted_next, "#at %.*s (%s) %.*s", - BUFSIZE - 15, p->name, buf, - BUFSIZE - 15 - (int)strlen(p->name), p->command); - } - else - sprintf(inserted_next, "#in %.*s (%ld) %.*s", - BUFSIZE - LONGLEN - 9, p->name, d, - BUFSIZE - LONGLEN - 9 - (int)strlen(p->name), p->command); - } else { - (void)strftime(buf, BUFSIZE - 1, "%H:%M:%S", s); - PRINTF("#at (%s) #in (%ld) \"%s\" %s\n", buf, d, p->name, p->command); - } -} - -void show_delays(void) -{ - delaynode *p; - int n = (delays ? delays->next ? 2 : 1 : 0) + - (dead_delays ? dead_delays->next ? 2 : 1 : 0); - - PRINTF("#%s delay label%s defined%c\n", n ? "the following" : "no", - n == 1 ? " is" : "s are", n ? ':' : '.'); - for (p = delays; p; p = p->next) - show_delaynode(p, 0); - for (p = dead_delays; p; p = p->next) - show_delaynode(p, 0); -} - -void change_delaynode(delaynode **p, char *command, long millisec) -{ - delaynode *m=*p; - - *p = m->next; - m->when.tv_usec = (millisec % mSEC_PER_SEC) * uSEC_PER_mSEC; - m->when.tv_sec = millisec / mSEC_PER_SEC; - update_now(); - add_vtime(&m->when, &now); - if (*command) { - if (strlen(command) > strlen(m->command)) { - free((void*)m->command); - m->command = my_strdup(command); - } - else - strcpy(m->command, command); - } - if (millisec < 0) - add_node((defnode*)m, (defnode**)&dead_delays, rev_time_sort); - else - add_node((defnode*)m, (defnode**)&delays, time_sort); - if (opt_info) { - PRINTF("#changed "); - show_delaynode(m, 0); - } -} - -void new_delaynode(char *name, char *command, long millisec) -{ - vtime t; - delaynode *node; - - t.tv_usec = (millisec % mSEC_PER_SEC) * uSEC_PER_mSEC; - t.tv_sec = millisec / mSEC_PER_SEC; - update_now(); - add_vtime(&t, &now); - node = add_delaynode(name, command, &t, millisec < 0); - if (opt_info && node) { - PRINTF("#new "); - show_delaynode(node, 0); - } -} - -void show_history(int count) -{ - int i = curline; - - if (!count) count = lines - 1; - if (count >= MAX_HIST) count = MAX_HIST - 1; - i -= count; - if (i < 0) i += MAX_HIST; - - while (count) { - if (hist[i]) { - PRINTF("#%2d: %s\n", count, hist[i]); - } - count--; - if (++i == MAX_HIST) i = 0; - } -} - -void exe_history(int count) -{ - int i = curline; - char buf[BUFSIZE]; - - if (count >= MAX_HIST) - count = MAX_HIST - 1; - i -= count; - if (i < 0) - i += MAX_HIST; - if (hist[i]) { - strcpy(buf, hist[i]); - parse_user_input(buf, 0); - } -} - diff --git a/cmd2.h b/cmd2.h deleted file mode 100644 index 96cb067..0000000 --- a/cmd2.h +++ /dev/null @@ -1,32 +0,0 @@ -/* public things from cmd2.c */ - -void show_aliases(void); -void parse_alias(char *str); - -void show_actions(void); -void show_prompts(void); -void parse_action(char *str, int onprompt); - -void show_attr_syntax(void); -void attr_string(int attrcode, char *begin, char *end); -int parse_attributes(char *line); -char *attr_name(int attrcode); -void show_marks(void); -void parse_mark(char *str); - -char *seq_name(char *seq, int len); -void show_binds(char edit); -void parse_bind(char *arg); -void parse_rebind(char *arg); - -char *redirect(char *arg, ptr *pbuf, char *kind, char *name, int also_num, long *start, long *end); - -void show_vars(void); -void show_delaynode(delaynode *p, int in_or_at); -void show_delays(void); -void change_delaynode(delaynode **p, char *command, long millisec); -void new_delaynode(char *name, char *command, long millisec); - -void show_history(int count); -void exe_history(int count); - diff --git a/configure.ac b/configure.ac index cdd1d67..3798599 100644 --- a/configure.ac +++ b/configure.ac @@ -150,7 +150,7 @@ AC_C_CONST # Checks for library functions. AC_FUNC_MALLOC -AC_OUTPUT(Makefile) +AC_OUTPUT(Makefile src/Makefile doc/Makefile man/Makefile) AC_OUTPUT diff --git a/defines.h b/defines.h deleted file mode 100644 index 33a96ad..0000000 --- a/defines.h +++ /dev/null @@ -1,307 +0,0 @@ -/* - * common definition and typedefs - */ - -#ifndef _DEFINES_H_ -#define _DEFINES_H_ - -#if !defined(SYS_TIME_H) && !defined(_H_SYS_TIME) -# include -#endif - -#ifdef AIX -# include -#endif - -#define memzero(a,b) memset((a), 0, (b)) - -#ifdef USE_RANDOM -# define get_random random -# define init_random srandom -#else -# define get_random lrand48 -# define init_random srand48 -#endif - -#define uSEC_PER_SEC ((long)1000000) /* microseconds in a second */ -#define mSEC_PER_SEC ((long)1000) /* milliseconds in a second */ -#define uSEC_PER_mSEC ((long)1000) /* microseconds in a millisecond */ - -#undef MIN2 -#undef MAX2 -#undef ABS -#undef SIGN -#undef SWAP2 -#define MIN2(a,b) ((a)<(b) ? (a) : (b)) -#define MAX2(a,b) ((a)>(b) ? (a) : (b)) -#define ABS(a) ((a)> 0 ? (a) :(-a)) -#define SIGN(a) ((a)> 0 ? 1 : (a) ? -1 : 0) -#define SWAP2(a,b,c) ((c)=(b), (b)=(a), (a)=(c)) - -/* macros to match parentheses */ -#define ISRPAREN(c) ((c) == ')' || (c) == ']' || (c) == '}') -#define ISLPAREN(c) ((c) == '(' || (c) == '[' || (c) == '{') -#define LPAREN(c) ((c) == ')' ? '(' : ((c) == ']' ? '[' : '{')) - -#define ISODIGIT(c) ((c) >= '0' && (c) <= '7') - -#define PRINTF status(1), tty_printf - -#define INTLEN (3*(1+(int)sizeof(int))) - /* max length of a string representation - * of an int */ -#define LONGLEN (3*(1+(int)sizeof(long))) - /* max length of a string representation - * of a long */ -#define ESC '\\' /* special escape char */ -#define STRESC "\\" -#define ESC2 '`' /* other special escape char */ -#define STRESC2 "`" -#define CMDSEP ';' /* command separator character */ -#define SPECIAL_CHARS "{}();\"=" /* specials chars needing escape */ -#define MPI "~$#E" /* MUME protocol introducer */ -#define MPILEN 4 /* strlen(MPI) */ - -#ifdef NR_OPEN -# define MAX_FDSCAN NR_OPEN -#else -# define MAX_FDSCAN 256 /* max number of fds */ -#endif - -#define MAX_CONNECTS 32 /* max number of open connections. must fit in a byte */ - -#define CAPLEN 20 /* max length of a terminal capability */ -#define BUFSIZE 4096 /* general buffer size */ -#define SOCKBUFSIZE BUFSIZE /* socket buffer size for read */ -#define PARAMLEN 99 /* initial length of text strings */ -#define MAX_MAPLEN 1000 /* maximum length of automapped path */ -#define MIN_WORDLEN 3 /* the minimum length for history words */ -#define MAX_WORDS 512 /* number of words kept for TAB-completion */ -#define MAX_HIST 128 /* number of history lines kept */ -#define LOG_MAX_HASH 7 -#define MAX_HASH (1<(max) ? (max) : (a)) - -#define ISMARKWILDCARD(c) ((c) == '&' || (c) == '$') - -/* - * Attribute codes: bit 0-4 for foreground color, 5-9 for - * background color, 10-13 for effects. - * Color #16 is "none", so 0x0210 is "no attribute". - */ -#define COLORS 16 /* number of colors on HFT terminal */ -#define LOWCOLORS 8 /* number of ANSI colors */ -#define NO_COLOR COLORS /* no color change, use default */ -#define BITS_COLOR 5 /* bits used for a color entry - (16==none is a valid color) */ -#define BITS_2COLOR 10 /* bits used for 2 color entries */ -#define COLOR_MASK 0x1F /* 5 (BITS_COLOR) bits set to 1, others 0 */ - -#define ATTR_BOLD 0x01 -#define ATTR_BLINK 0x02 -#define ATTR_UNDERLINE 0x04 -#define ATTR_INVERSE 0x08 - -/* - * WARNING: colors and attributes are currently using 14 bits: - * 4 for attributes, 5 for foreground color and 5 for background. - * type used is int and -1 is used as 'invalid attribute' - * so in case ints are 16 bits, there is only 1 bit left unused. - * In case ints are 32 bits, no problem. - */ - -/* constructors / accessors for attribute codes */ -#define ATTRCODE(attr, fg, bg) \ - (((attr) << BITS_2COLOR) | ((bg) << BITS_COLOR) | (fg)) -#define FOREGROUND(attrcode) ((attrcode) & COLOR_MASK) -#define BACKGROUND(attrcode) (((attrcode) >> BITS_COLOR) & COLOR_MASK) -#define ATTR(attrcode) ((attrcode) >> BITS_2COLOR) - -#define NOATTRCODE ATTRCODE(0, NO_COLOR, NO_COLOR) - -/* - * NCSA telnet 2.2 doesn't reset the color when it receives "esc [ m", - * so we must know what the normal colors are in order to reset it. - * These colors can be changed with the #color command. - */ -#ifdef BUG_TELNET -# define DEFAULTFG 7 /* make default white text */ -# define DEFAULTBG 4 /* on blue background */ -#endif - -#define LM_NOECHO 1 /* no local echo */ -#define LM_CHAR 2 /* char-by-char mode (no line editing) */ - - - - - -typedef unsigned char byte; - -typedef void (*function_any) (); /* generic function pointer */ - -typedef void (*function_int)(int i); - -typedef function_int function_signal; - -typedef void (*function_str)(char *arg); - -typedef struct timeval vtime; /* needs #include */ - -#include "ptr.h" - - - - -/* generic linked list node (never actually created) */ -typedef struct defnode { - struct defnode *next; - char *sortfield; -} defnode; - -/* - * twin linked list node: used to build pair of parallel lists, - * one sorted and one not, with the same nodes - */ -typedef struct sortednode { - struct sortednode *next; - char *sortfield; - struct sortednode *snext; -} sortednode; - -/* - * linked list nodes: keep "next" first, then string to sort by, - * then (eventually) `snext' - */ -typedef struct aliasnode { - struct aliasnode *next; - char *name; - struct aliasnode *snext; - char *subst; - char *group; - int active; -} aliasnode; - -typedef struct marknode { - struct marknode *next; - char *pattern; - int attrcode; - char *start, *end; - char mbeg; - char wild; -} marknode; - -typedef struct triggernode { - struct triggernode *next; - char *command, *label; - int active; - int type; /* GH: allow regexp */ - char *pattern; -#ifdef USE_REGEXP - void *regexp; /* 0 if type == ACTION_WEAK */ -#endif - char *group; -} triggernode; - -/* - * HACK WARNING : - * actionnode and promptnode must be the same type - * or search_action_or_prompt() in main.c won't work. - */ -typedef triggernode actionnode; -typedef triggernode promptnode; - -typedef int (*function_sort)(defnode *node1, defnode *node2); - -typedef struct keynode { - struct keynode *next; - char *name; /* key name */ - char *sequence; /* escape sequence sent by terminal */ - int seqlen; /* GH: length of esc seq to allow \0 in seq */ - function_str funct; /* function called when key pressed */ - char *call_data; /* data passed to function */ -} keynode; - -typedef struct delaynode { - struct delaynode *next; - char *name; - char *command; - vtime when; /* structure containing time when */ - /* command must be executed */ -} delaynode; - -/* Variable struct definitions */ - -typedef struct varnode { /* for named variables */ - struct varnode *next; - char *name; - struct varnode *snext; - int index; - long num; - ptr str; -} varnode; - -typedef struct { /* for unnamed vars */ - long num; - ptr str; -} unnamedvar; - -typedef struct { /* stack of local vars */ - unnamedvar p[MAX_STACK][NUMPARAM]; - int curr; -} param_stack; - -typedef struct { /* pointers to all variables */ - long *num; - ptr *str; -} vars; - -/* editing session control */ -typedef struct editsess { - struct editsess *next; - unsigned int key; /* session identifier */ - int pid; /* pid of child */ - int fd; /* MUD socket to talk with (-1 if non-MUD text) */ - char *descr; /* short description of what we are editing */ - char *file; /* name of temporary file */ - time_t ctime; /* time when temp file was created (upper bound) */ - long oldsize; /* original file size */ - char cancel; /* 1 if cancelled */ -} editsess; - -#endif /* _DEFINES_H_ */ - diff --git a/doc/Config.demo b/doc/Config.demo new file mode 100644 index 0000000..7eb537e --- /dev/null +++ b/doc/Config.demo @@ -0,0 +1,401 @@ +#savefile-version 1 +#("We use an old version number to force creation of sane default #binds") + +#init ={#(@oldxp=0);mapend;#option +compact -info -echo} + +#("Connect to MUME") +#host mume.pvv.org 4242 + +#("Some aliases to use labeled #actions") +#alias ac=#action $0 +#alias <=#action <$0 +#alias \==#action =$0 +#alias >=#action >$0 +#alias +=#action +$0 +#alias -=#action -$0 + +#("Some variables") +#(@emergency = 1, @xp = -0) +#($E_col = "bold") +#($Elbereth = "Grima Slinket Crug Camaro") +#($S_col = "inverse") +#($Sauron = "Homie Aridhol Quigley Mournblade ") + +#("Some useful binds -- they all are on numeric keypad") +#alias ?=#history 1 +#alias ??=#history $0 +#bind * ^[Oj=stat +#bind + ^[Ol=open honeycomb +#bind - ^[Om=close honeycomb +#bind . ^[On=sneak +#bind / ^[Oo=! +#bind 0 ^[Op=? +#bind 1 ^[Oq=hide +#bind 2 ^[Or=s +#bind 3 ^[Os=d +#bind 4 ^[Ot=w +#bind 5 ^[Ou=exits +#bind 6 ^[Ov=e +#bind 7 ^[Ow=look +#bind 8 ^[Ox=n +#bind 9 ^[Oy=u + +#alias plusminus={#bind +=open $1;#bind -=close $1} +#action >+close The $1 is open={#print;plusminus $1} +#action >+open2 The $1 seems to be closed={#print;plusminus $1} +#action >+open1 The $1 is closed={#print;plusminus $1} + +#("Bindings for function keys") +#alias HELP=#alias HELP={remove ring;narrate HELP! EMERGENCY TRANSFER! rings off!} +#bind F01 ^[OP=HELP +#alias wi=change wimpy $0 +#alias wq=wi 100 +#alias ww=wi 1000 +#bind F02 ^[OQ=ww +#bind F03 ^[OR=wq +#bind F04 ^[OS=examine +#bind F05 ^[m=now +#bind F06 ^[[17~=#option compact +#bind F07 ^[[18~=listen all +#bind F08 ^[[19~=listen none +#alias ck=change mood berserk +#alias ca=change mood aggressive +#alias cb=change mood brave +#alias cn=change mood normal +#alias cp=chan mood prudent +#alias cw=change mood wimpy +#bind F09 ^[[20~=cw +#bind F10 ^[[21~=ca +#alias pi={remove sword;draw} +#alias sla={sheath;wield sword} +#bind F11 ^[[23~=pi +#bind F12 ^[[24~=sla + +#("Two nice aliases") +#alias ,e=emote leaves east +#alias emsay=emote says '$0' + +#("Teleporting") +#alias ll=cast 'locate life' $0 +#alias tp=cast 'teleport' $0 +#alias portal=cast 'portal' $0 +#alias go={#if (!*\$0:>2) #(\$0+="tp"); #else #if (\$0:>2=="portal") #(\$0+=" u"); #exe (\$0:>2+" "+\$0:1)} + +#alias loc={#var $talias=$1; #action +tkey; ll $2} +#action >tkey Key: '$1'={#print; #action -tkey; #alias ${talias}=go $1 \$0} + +#alias zharlond=go whicewhile $0 +#alias zmichdelv=go sillelega $0 +#alias zbree=go dorwhimile $0 +#alias 1=zHarlond +#alias 2=zMichDelv +#alias 3=zBree + +#("An emote for enchanting weapons") +#alias ench=cast 'enchant weapon' $0 +#alias enchant={, makes a swift gesture and the $0 starts floating in mid air...;EnchSet +;ench $0} +#alias EnchSet={#action $1EnchSucc;#action $1EnchFail;#action $1BackFire} +#action >-BackFire Your spell backfired!={#print;, starts swearing loudly;EnchSet -} +#action >-EnchFail You lost your concentration!={#print;, frowns and exclaims, 'This cannot be! I failed!';EnchSet -} +#action >-EnchSucc $1 glows blue.={#print;, gestures again, and arcane runes flare on the $1!;EnchSet -} + +#("Keep count of game time and also put it in the prompt") + #mark Game time =underline +#("length of a game tick in milliseconds :") + #(@tick = 61500) +#("this is to synchronize") + #action >+clock ^The current time is $1:$2 $3.={#print;#if (\@0=$1*@tick+$2*@tick/60, \$3=="pm" ^^ $1==12) #(\@0+=12*@tick); #settimer (\@0);settick} +#("print time") + #alias time={#if ((\@1=timer%@tick*60/@tick) < 10) #(\$1=":0"); #else #(\$1=":");#print ("Game time "+%(timer/@tick%24)+\$1+%\@1)} +#("manually set time") + #alias now={#if (*\$1) #(\@0=$1*@tick+0$2*@tick/60); #else #if ((\@0=timer-(\@1=timer%@tick)), \@1 >= @tick/2) #(\@0+=@tick); #settimer (\@0)} +#("put time into the prompt. works for MUME.") +#("change to use appropriate patterns for other MUDs") + #prompt %+empty ^[o\\*]>={#isprompt -1; timeprompt} + #prompt %+full ^[o\\*] [^>]*>={#isprompt -1; timeprompt} + #alias timeprompt={#if ((\@1=timer%@tick*60/@tick) < 10) #(\$1=":0"); #else #(\$1=":");#($prompt=$prompt.1+" "+%(timer/@tick%24)+\$1+%\@1+$prompt.>2)} +#("automatically print time at every tick. type `settick' to activate") +#("or change #alias `now' and #action `clock' to run it") + #alias settick=set tick 0 time + #alias set=#exe ("#in $1 ("+%((-timer-1)%@tick+2)+") {"+\$0:>3+";#in $1 ((-timer-1)%@tick+2)}") + +#("Code to help mapping mazes -- keeps track of room number") +#("using binary numbers -- 1 copper == 1, 2 coppers == 0") +#("doesn't work anymore as you now get `six pile of coins' etc. tough luck.") +#alias d1=drop 1 copper +#alias d2=drop 2 copper +#alias m=#for (\@1=$1; \@1; \@1/=2) #if (\@1&1) d1; #else d2 +#alias mapend={- copper-penny;- pile-of-coins;#(@map=0)} +#alias mapstart={+ copper-penny;+ pile-of-coins;#(@map=0);#in map-timeout (-1) {#print ("Room: "+%@map);#(@map=0)}} +#alias mapcount={#(@map*=2, @map+=$1);#in map-timeout (100)} +#action >-pile-of-coins ^A pile of coins=mapcount 0 +#action >-copper-penny ^One miserable copper penny=mapcount 1 + +#("Spells and fight") +#($self = "Cosmos") +#alias self=#($self=$(0)) +#alias k=kill $0 +#alias f=flee +#alias ff=order followers assist ${self} +#alias fw=order followers assist waran +#alias of=order followers $0 +#alias ol=of k lord +#alias on=of k noble +#alias he={#if (!*$(0)) #($(0)=$self);#send ("cast 'heal' "+$(0))} +#alias in={#if (!*$(0)) #($(0)=$self);#send ("cast 'invisibility' "+$(0))} +#alias rp={#if (!*$(0)) #($(0)=$self);#send ("cast 'remove poison' "+$(0))} +#alias sct={#if (!*$(0)) #($(0)=$self);#send ("cast 'sanctuary' "+$(0))} +#alias str={#if (!*$(0)) #($(0)=$self);#send ("cast 'strength' "+$(0))} +#alias ar={#if (!*$(0)) #($(0)=$self);#send ("cast 'armour' "+$(0))} +#alias sspell=#exe ("#alias $1={#if (!*$(0)) #($(0)=$self);#send (\\"cast '"+\$0:>2+"' \\"+$(0))}") +#alias sto=cast 'store' $0 +#alias stb=sto fireball +#alias stl=sto call lightning +#alias stp=sto teleport +#alias stq=sto earthquake +#alias sty=sto colour spray +#alias bd=cast 'block door' $0 +#alias bl=cast 'blindness' $0 +#alias bob=cast 'breath of briskness' +#alias ch=cast 'charm' $0 +#alias cwt=cast 'control weather' $0 +#alias b=cast 'fireball' $0 +#alias j=cast 'lightning bolt' $0 +#alias li=cast 'call lightning' $0 +#alias sl=cast 'sleep' $0 +#alias sle=sl $0 + +#($target = "assassin") +#alias .=$0 ${target} +#alias bl.=bl ${target} +#alias b.=b ${target} +#alias j.=j ${target} +#alias k.=k ${target} +#alias li.=li ${target} +#alias sl.=sl ${target} +#alias blo=bl orc +#alias blt=bl troll +#alias jo=j orc +#alias jt=j troll +#alias ka=k assassin +#alias kw=k wolf + +#alias tr=track $0 +#alias to=tr orc +#alias tt=tr troll +#alias tr.=tr ${target} + +#alias bs=backstab $0 +#alias bs.=bs ${target} +#alias nn=$0 Necromancer + +#("Misc stuff") +#($water = "skin") +#alias dw=drink water +#alias dk=drink ${water} +#alias lk=look in ${water} +#alias po=pour water ${water} +#alias sk=sip ${water} +#alias cv=cast 'create water' $0 +#alias cvk=cv ${water} + +#($pack = "backpack") +#alias gp=get $0 ${pack} +#alias pp=put $0 ${pack} +#alias lp=look in ${pack} + +#($light = "lantern") +#alias hl=wear ${light} +#alias rl=remove ${light} +#alias vl=cover ${light} +#alias ul=uncover ${light} +#alias cl=cast 'create light' $0 +#alias cll=cl ${light} + +#alias lg=look in moneybag +#alias gg=get $1 gold moneybag +#alias pg=put $1 gold moneybag +#alias ggj=get ruby moneybag +#alias pgj=put ruby moneybag + +#alias cf=cast 'create food' +#alias gf=get food +#alias ef=eat food +#alias food={cf;gf;ef} + +#alias bc=#if (*$(1)) butcher $1.corpse; #else butcher corpse +#alias lc=#if (*$(1)) look in $1.corpse; #else look in corpse +#alias ga=#if (*$(1)) get all $1.corpse; #else get all corpse +#alias gc=#if (*$(1)) get coins $1.corpse; #else get coins corpse +#alias gx=#if (*$(2)) get $1 $2.corpse; #else get $1 corpse +#alias xc={hl;gc $0;rl} +#alias gl=get all +#alias glc={get all.coins;get all.copper} +#action >-greed is dead! R.I.P.={#print;gc} + +#alias ldh=#if (*($1)) lead $1.horse; #else lead horse +#alias rdh=#if (*$(1)) ride $0.horse; #else ride horse +#alias ldp=#if (*($1)) lead $1.pony; #else lead pony +#alias rdp=#if (*$(1)) ride $1.pony; #else ride pony +#alias ds=dismount +#action >-Ct ^Clip-clop...the riding horse= +#action >-Cp ^Clip-clop...a pony= +#action >-Cb ^The Horse= +#action >-At ^A riding horse= +#action >-Ap ^A cute and docile pony= +#action >-Ab ^A beautiful horse= + +#alias re=t Alsbreth $0 +#action >+reply ^$1 tells you '={#print;#alias re=t $1 \$0} +#action >-xeval ^$1 tells you 'calc &2'={#print;#alias re=t $1 \$0;if (\$2?";" || \$2?"$" || \$2?"@") {tell $1 *grin*;#()}; #else #send ("t $1 "+%($2))} + +#alias rep={+ report;score} +#action >-report $1 hit, $2 mana and $3 moves.={#print;, has $1 hit, $2 mana and $3 moves.;- report} +#alias rd={#opt +speedwalk;4ensenwdnenne;#opt -speedwalk} +#alias fd={#opt +speedwalk;wsswsuwwdww;#opt -speedwalk} + +#alias lr={look n;look s;look e;look w;look u;look d} +#alias x=#alias $0 +#alias kk=#bind $0 +#alias l=examine $0 +#alias gr=group $0 +#alias le=group ${self} +#alias mm=#mark $0 +#alias sa=#save +#alias se=search $0 +#alias mo=help month +#alias ng=, nods gravely. +#alias nh=, nods happily. +#alias ns=, nods sadly. +#alias p=' open +#alias rf=rem staff +#alias rn=read next +#alias rs=rem stone +#alias rt=rem ticket +#alias sms=, smiles sadly. +#alias ss=spam +#alias fade=emote quickly wraps in his cloak and vanishes. +#alias matz=emote unwraps his cloak and materializes. +#alias fun={fade;$0;matz} +#alias tele={fade;tp $0;matz} +#alias title=chan title the Hungry +#alias tm=t Meryaten $0 +#alias wavall=, waves good-bye to you. +#alias wf=weather fog +#alias wg=weather global +#alias wh=where human +#alias wk={wake;st} +#alias wl=weather local +#alias wr={wake;rest} +#alias grz=na --- === *** C O N G R A T U L A T I O N S !! *** === --- +#alias lin={#lines $0;chan height $0} +#alias lv=na --- === *** L E V E L !! *** === --- +#alias macintosh={#bind ~home ^[OH=eq;#bind ~~home ^[O@=eq;#bind ~F01 ^[OP=#key F01;#bind ~F02 ^[OQ=ww;#bind ~F03 ^[OR=wq;#bind ~F04 ^[OS=examine;#bind ~F05 ^[m=now;#bind ~+ ^[Ok=open patch;#bind ~~F05 ^[[16~=now} + + +#alias listadd=#for (\@0=1; *$$1:\@0 && $$1:\@0!=*13; \@0++) #exe ("#mark "+$$1:\\@0+"="+$$2)} +#alias listclear={#for (\@0=1; *$$1:\@0 && $$1:\@0!=*13; \@0++) #exe ("#mark "+$$1:\\@0+"=");#($$1="")} +#alias elb={listclear Elbereth;#ac +elbereth;t elbereth list} +#alias elblist=#print ("Elf Enemies: "+$Elbereth) +#alias sauronclean={#for (\@0=1; *\$-1:\@0; \@0++) #($-1=$-1:<\\@0<.2+" "+$-1:>(\\@0+1))} +#alias pk={listclear Sauron;#ac +sauron0;#ac +sauron1;t sauron list} +#alias pklist=#print ("PKillers: "+$Sauron) +#action >-elbereth ^Elbereth Gilthoniel tells you 'Elf Enemies: &1'={#($Elbereth=\$1);listadd -5 -6;#ac -elbereth;#print} +#action >-sauron2 ^ &1more to come.'={#($Sauron+=$-1=\$1<.3+",");sauronclean;#ac -sauron2;listadd -3 -4;#($Sauron+=$-1, $-1="");#print} +#action >-sauron1 ^Sauron tells you 'My Envoys: &1={#($Sauron=$-1=\$1<.2+",");sauronclean;#ac -sauron0;#ac -sauron1;#ac +sauron2;listadd -3 -4;#($Sauron=$-1);#print} +#action >-sauron0 ^Sauron tells you 'My Envoys: &1more to come.'={#($-1=\$1<.3+",");sauronclean;#ac -sauron0;#ac -sauron1;listadd -3 -4;#($Sauron=$-1);#print} + +#action >+disappear disappears into nothing.= +#action >+exp ^Needed: $1 xp={#print;#if (\@1=-$1, !@oldxp) #(@oldxp=@xp=\@1);#print ("Exp: "+%(\@1-@xp)+" from last score, "+%(\@1-@oldxp)+" from start of session");#(@xp=\@1)} +#action >+hunt *&1* leaves $2={#mark $2=bold;#print;#mark $2=} +#action >+save Saving=#print ("---> "+\$0+" <---") +#action >-someone ^Your blood freezes as you hear $1 =#if (\$1!="The" && \$1!="the" && \$1!="A" && \$1!="a" && \$1!="An" && \$1!="an") #print +#action >+Willow A sudden drowsiness overcomes you, you yawn and...={#print;wk} + +#mark sitting=bold +#mark resting=bold +#mark sleeping=bold +#mark Room: =bold +#mark tells you=underline +#mark YOU=bold red +#mark is dead! R.I.P.=bold +#mark glows with a bright light!=bold +#mark victim shocked=bold +#mark You feel a watchful eye=bold + +#mark The corpse of *&*=bold +#mark *an Orc*=bold yellow on red +#mark *$ the Orc*=bold yellow on red +#mark *a Troll*=bold cyan on yellow +#mark *$ the Troll*=bold cyan on yellow +#mark *a Human*=blue on cyan +#mark *$ the Human*=blue on cyan +#mark *$ the Numenorean*=blue on cyan +#mark *$ the Black Numenorean*=blue on cyan +#mark *a Dwarf*=bold cyan on magenta +#mark *$ the Dwarf*=bold cyan on magenta +#mark *an Elf*=blue on green +#mark *$ the Elf*=blue on green +#mark *a Hobbit*=bold yellow on blue +#mark *$ the Hobbit*=bold yellow on blue +#alias mumecolor={change col all default;change col shout bold yellow;change col damage bold red;change col hit bold blue} +#alias nocolors={#mark *an Orc*=inverse;#mark *$ the Orc*=inverse;#mark *a Troll*=inverse;#mark *$ the Troll*=inverse;#mark *a Human*=inverse;#mark *$ the Human*=inverse;#mark *$ the Numenorean*=inverse;#mark *$ the Black Numenorean*=inverse;#mark *a Dwarf*=inverse;#mark *$ the Dwarf*=inverse;#mark *an Elf*=inverse;#mark *$ the Elf*=inverse;#mark *a Hobbit*=inverse;#mark *$ the Hobbit*=inverse} +#alias colors={#mark *an Orc*=bold yellow on red;#mark *$ the Orc*=bold yellow on red;#mark *a Troll*=bold cyan on yellow;#mark *$ the Troll*=bold cyan on yellow;#mark *a Human*=blue on cyan;#mark *$ the Human*=blue on cyan;#mark *$ the Numenorean*=blue on cyan;#mark *$ the Black Numenorean*=blue on cyan;#mark *a Dwarf*=bold cyan on magenta;#mark *$ the Dwarf*=bold cyan on magenta;#mark *an Elf*=blue on green;#mark *$ the Elf*=blue on green;#mark *a Hobbit*=bold yellow on blue;#mark *$ the Hobbit*=bold yellow on blue} + +#bind >del ^[[P=&del-char-right +#bind A-bs ^[[071q=&clear-line +#bind A-del ^[[M=&kill-to-eol +#bind A-left ^[[160q=&begin-of-line +#bind A-return ^[[100q=&to-history +#bind A-right ^[[169q=&end-of-line +#bind M-B ^[b=&prev-word +#bind M-D ^[d=&del-word-right +#bind M-F ^[f=&next-word +#bind M-backsp ^[=&del-word-left +#bind M-bs ^[^H=&del-word-left +#bind M-tab ^[^I=&complete-line +#bind ^2 ^[[186q={s;hide} +#bind ^3 ^[[194q={d;hide} +#bind ^4 ^[[174q={w;hide} +#bind ^6 ^[[192q={e;hide} +#bind ^8 ^[[182q={n;hide} +#bind ^9 ^[[190q={u;hide} +#bind ^A ^A=&begin-of-line +#bind ^B ^B=&prev-char +#bind ^D ^D=&del-char-right +#bind ^E ^E=&end-of-line +#bind ^F ^F=&next-char +#bind ^K ^K=&kill-to-eol +#bind ^L ^L=&redraw-line +#bind ^N ^N=&next-line +#bind ^P ^P=&prev-line +#bind ^Q ^Q=&clear-line +#bind ^S ^S=#capture +#bind ^T ^T=&transpose +#bind ^V ^V=#stop +#bind ^W ^W=&to-history +#bind ^X ^X=#exe ("#capture emergency"+%@emergency++) +#bind ^Z ^Z=&suspend +#bind ^bs ^[[085q=&del-word-left +#bind ^del ^[[142q=&del-word-right +#bind ^left ^[[159q=&prev-word +#bind ^right ^[[168q=&next-word +#bind ^tab ^[[072q=&complete-line +#bind backsp =&del-char-left +#bind bs ^H=&del-char-left +#bind del ^[[3~=jo +#bind down ^[OB=&next-line +#bind end ^[[4~=jt +#bind enter ^[OM=flee +#bind home ^[[1~=stat +#bind ins ^[[2~=time +#bind left ^[OD=&prev-char +#bind lf ^J=&enter-line +#bind pgdwn ^[[6~=jh +#bind pgup ^[[5~=score +#bind return ^M=&enter-line +#bind right ^[OC=&next-char +#bind tab ^I=&complete-word +#bind up ^[OA=&prev-line +#bind ~^Q ^[~q=&clear-line +#bind ~^S ^[~s=#capture diff --git a/doc/Hacking b/doc/Hacking new file mode 100644 index 0000000..2063884 --- /dev/null +++ b/doc/Hacking @@ -0,0 +1,48 @@ +Some notices for you who would like to hack the source in order to +improve powwow, fix bugs or just have fun: + +* Try not to run large chunks of existing code through your C + beautifier if the indentation style doesn't please you. This will + only greatly confuse the original author (if he can't recognize and + maintain his own code, who can?). If you write any new functions, + ok, use your own style as long as it's clear and consistent. + +* Document your changes! A brief report of changes in the Changelog file is + absolutely necessary. So is updating the doc files (powwow.doc, powwow_help + and README) Also, sending an e-mail to the code author/mantainer documenting + your changes will be appreciated. + +Report from cancan's "Hacking": + +******************************************************************************* + Remember, that although I (Yorick) am the original author, I don't dictate + the code; it is explicitly in the public domain. I am merely trying to keep + some kind of order; if versions are permitted to diverge, they are very + painful to merge later. Also, different version branches are very confusing, + and makes it hard to define the "latest and best" version. Please try to + synchronize your hacks into one - the latest - version! +******************************************************************************* + +Well, Yorick refused to merge Powwow and Cancan, so now they both exist +indipendently... Now I (Cosmos) say exactly the same thing Yorick said +('do not let version diverge'), but I fear this will happen again :( +Only difference is that powwow is now GPL-ed and not Public Domain. +I hope this will help a little. + +* Rather than you just distributing your hacked version, I _really_ prefer you + to send back to me your changes, so that I can put them into the following + release. You lose nothing doing so, since even in case I refuse to include + your changes you can still distribute your version if you absolutely want :) + + Even if you decide to distribute such a modified version, please + make it clear that it is not the original version, use a version number + which can't be confused with an original version, and put your name + in Changelog and in POWWOW_VERSION. + +* I've tried to make the code obey the following rules for modularity: + - All exported things from one .c file is declared in its .h file. + - Conversely, symbols are imported by #including .h files, + not by local extern declarations. + - Everything that can be static should be (this is not quite true yet) + + Please obey these rules (or improve them). diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..f5fc784 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,5 @@ +pkgdata_DATA = powwow.doc powwow.help +EXTRA_DIST = README.follow README.term README.modules \ + TODO Hacking Config.demo \ + powwow.doc powwow.help + diff --git a/doc/README.follow b/doc/README.follow new file mode 100644 index 0000000..9ef923b --- /dev/null +++ b/doc/README.follow @@ -0,0 +1,18 @@ + + follow 0.1 README + +This mini-program is mainly intended for debugging powwow. +It is completely standalone, and needs to be called with a file-name +(example: "follow mycapture"). +Hitting ESC or ^C quits "follow", hitting RETURN prints the next line +of the file, any other key prints the next character (only one) of the file + +I know it is a _stupid_ program, but it is useful if you find screen-related +bugs in powwow, i.e. powwow does not print some lines or prints something +wrong. +In that case, if you redirect powwow output to a file and reproduce the bug, +you can use "follow your-filename" to examine the file and see what is wrong + +How to send a copy of powwow output to a file: +powwow your-arguments | tee your-filename + diff --git a/doc/README.modules b/doc/README.modules new file mode 100644 index 0000000..8f8eae8 --- /dev/null +++ b/doc/README.modules @@ -0,0 +1,34 @@ +There is a sample module included to test module support. To build +it run: + +make plugtest.so + +from your powwow source directory. This will build a test plugin that +can be loaded using: + +#module plugtest + +If it works, when you do "#help" you will see a new command called +"#plugtest" that is listed. It will just echo the arguments back +to the terminal. This is the minimum required to make a functional +module for powwow 1.2.7 and later. + +Modules are loaded using the libdl interface with RTLD_LAZY so that +symbol resolution is deferred until as late as possible. This means +that you should test any commands you create since just because a +module loads, that doesn't mean that there are no undefined references. + +The basic requirements for a module are: + +1) define a cmdstruct with your command name, help text, and function to + call +2) add your cmdstruct to the global command list via cmd_add_command in + your powwow_init function. You can also initialize anything else you + need here, it will be called once at module load time. +3) perform whatever extentions you want at runtime in your custom functions + +For a more complete example, you should look at the powwow-perl package +which creates the bindings from powwow using the #perl command and allows +access to powwow internals from perl via the powwow:: namespace in perl. +It also includes autoconf support and is probably the most complete +module for powwow currently. diff --git a/doc/README.term b/doc/README.term new file mode 100644 index 0000000..6dfad6c --- /dev/null +++ b/doc/README.term @@ -0,0 +1,26 @@ + +'term' was written by Michael O'Reilly. + +If you don't know what term is, then you can ignore this file. :) + +Otherwise, if you want term support, read on... + +You need to edit the Makefile and edit the TERMDIR variable to the +directory of your term source and client.a. + +If you don't want 'term' compression on for powwow, take out the +-DTERM_COMPRESS in the CFLAGS variable in the Makefile. + +Edit any of the other variables you find neccessary. + +Read the original README for information you might need to know. + +Then do "make termpowwow" + +Note: Unfortunately one of the setbacks of termpowwow is that it won't +detect unknown hosts, nor will it detect if the host refused the connection. +If you don't get any output from a host after a few moments, then you'll +have to ^C. I don't think there's any way around this. + +(note - term support does not allow multiple sessions...) + diff --git a/doc/TODO b/doc/TODO new file mode 100644 index 0000000..6112fe9 --- /dev/null +++ b/doc/TODO @@ -0,0 +1,46 @@ +-*-indented-text-*- + +From the authors of cancan: + + THINGS TO DO + Cancan should know the codes of at least some keys + by looking in the TERMCAP. (especially control keys are trivial!) + + - Run (a)lint! + + - There seems to be problems with running cancan on exotic terminals. + This defeats the very purpose of all the carefully terminal- + independent code with TERMCAP, and is generally embarrassing. + + - more system calls should be checked for EINTR for sysV unices + + - negotiate the telnet BINARY option + + - support the telnet IAC GA command to identify prompts + (But what for? Prompts have to be identified as such in the absence + of GA anyway, and there doesn't seem to be a need for it right + now.) + + - implement reverse-incremental-search (like ^R in GNU Readline) + + KNOWN BUGS + - Deleting more than one character with deletechar() might leave + ugly traces on lines that are entirely deleted. + + - using #hilite with coloured background might have effects beyond the + input line, depending on the terminal (don't do that, then :-) + + FUTURE POSSIBLE IMPROVEMENTS + Do some more for portability (clean up the terminal mess) + The source should be more modular + Filtering output through shell command (grep etc) + + +THINGS TO DO/PLANNED IMPROVEMENTS on powwow: + - make a better #help + - add termcap "end of bold", "end of blink" etc. + + If you have ideas, suggestions or whatever, e-mail them to me + (max@Linuz.sns.it) and if I find them interesting, I will add them. + Also, I am trying to keep powwow updated with further releases + of cancan. diff --git a/doc/powwow.doc b/doc/powwow.doc new file mode 100644 index 0000000..0404328 --- /dev/null +++ b/doc/powwow.doc @@ -0,0 +1,2219 @@ + P O W W O W + + doc for version 1.2.21 + last modified Oct 09, 2007 + +INTRODUCTION + + powwow is a client program, which replaces telnet for the lazy + mudder who wants some (only some?) extra features. + It is primarily designed for DikuMUDs, but nothing prevents its use + for other types of muds. powwow is based on another client, cancan, + and cancan was originally inspired by tintin (yet another client) + by Peter Unold (pjunold@daimi.aau.dk), but is entirely re-written. + powwow also implements the MUME remote editing protocol, which + enables you to edit texts on the mud using your own favourite + editor, several texts at once if you have a windowing terminal. + +STARTING POWWOW + + Powwow maintains definition files where aliases, actions and + mud addresses are saved. Powwow is then called by: + + $ powwow definition-file + + (the $ above stands for your prompt, do NOT type it) + + If the definition file doesn't exist (as the first time you + use powwow) you are asked for a host name and port number. + This will then be stored in the file. The file will be updated + every time you type '#save' or when powwow terminates. + The file is in plain text, so you can edit it if you want. + It makes sense to have one file for each mud you play, or for + each set of definitions you use in the same mud. + + If the environment variable POWWOWDIR exists, that directory + is first searched for definition files and new files are + created there rather than in the current directory, unless + definition-file contains a slash ("/"). + + The file 'Config.demo' is a demonstrative definition file provided + together with powwow to help you building your own configuration. + Look at it: there are many useful examples and non-trivial code + to fully use powwow. + + You may also start powwow with: + + $ powwow netaddress portnumber + + but nothing will then be saved. + + In alternative you can simply type: + + $ powwow + + and you will enter test-mode: you will be able to use internal commands + but you will need to start a session manually + (#connect main
) if you want to connect to a MUD. + + To solve the problem met in MUME, where you may have to try many + different servers to connect to the game, a fourth syntax has been + added: + + $ powwow definition-file netaddress portnumber + + With this syntax powwow reads settings from definition file, + but overwrites the old address with the newly specified one. + + Note: If you have several sessions (different invokations) + with the same definition file active simultaneously, the + settings changed in one session can be overwritten by a change + in another session. + +QUITTING POWWOW + + At any time, you can type #quit to exit from powwow. Be + careful, as #quit brutally closes the connection to the mud, + without renting your character. Normally, you want to log out + gracefully from the mud before doing that. If you wish to cut + your link manually, you can also press the break key on your + system twice (usually Control-C or DEL) (You must hit it twice + because hitting only once is used to stop command parsing - + see the bottom of this file). + +POWWOW TEST MODE + + There are various ways to enter powwow test mode: + + 1) typing `powwow' from you command shell + (You will have to load your definition file manually if you need it, + using the following command: #load ) + + 2) starting powwow with a definition file that does not contain + a '#host' line or contains a '#host' alone, without any address + + 3) starting powwow opening one or more MUD connections, + then closing them all. (You'll need to have #option quit + disabled or powwow will exit when closing the last connection) + + Remember that to connect to a MUD from test mode you must use: + #connect main
+ +COMMAND CHAINING + + Powwow allows you to type several commands on one line + separated by the ; character. If you want to use a semicolon + literally, prefix it by a backslash (\). (Backslashes must of + course be escaped too if used literally.) + + Examples: + > n;get coins;s rapidly rescue some money + > say No beer? \;) semicolon must be escaped + + In addition, you must also surround every list of commands by braces: + '{' and '}'. The only exception (as you can see from the example above) + is when you type multiple commands from keyboard: in that case, + and only in that, braces are not needed. + Also, if you want to use a { or } literally, prefix it with a backslash + as you do with semicolons. + + Another use of semicolons is to send a character to the MUD + you are connected to. Infact powwow sends a every time it + meets a null command (0 chars) followed by a semicolon. + + Examples: + > press only: send a + > ; press ; and then : same effect + > ;; send two + > ;;; three ... + + Of course multiple are considered as multiple commands, + so you usually have to surrond them with braces. If directly typed + from keyboard, the braces are optional. + + > {} do nothing + > {;} send a + > {;;} send two + > {;;;} and so on... + + The verbatim mode disables this special meaning of semicolons and + braces, and the #quote command lets you switch between verbatim and + normal mode. + +COMMAND CONTINUATION + + Long lines can be continued by ending each intermediate line with a + backslash (\). This way long and complicated aliases can be written + a somewhat readable format. For example: + + #al drau={#send ("get tail "+$hcon);#send ("get fur "+$hcon);#send ("get vial "+$hcon);#send ("get tarragon "+$hcon);put tail kit;put fur kit;put vial kit;put tarragon kit} + + Could be written (somewhat) more legibly as: + + #al drau={\ + #send ("get tail "+$hcon);\ + #send ("get fur "+$hcon);\ + #send ("get vial "+$hcon);\ + #send ("get tarragon "+$hcon);\ + put tail kit;\ + put fur kit;\ + put vial kit;\ + put tarragon kit\ + } + + Note that when powwow saves your aliases (e.g. you leave #file defined) + that it will not preserve this formatting, so it is most useful if + you use a file that is not saved over on exit. For example, if + you use a single configuration file that calls several smaller + configuration files that are grouped by similar intent, like having + an xp counter script, a prompt script, etc. + +SPECIAL COMMANDS: ALIASES AND ACTIONS + + Powwow parses all lines beginning with the character '#' as + special commands. To send a line that begins with '#' itself, precede + it with a backslash. The commands are case-sensitive. + Commands can be abbreviated as much as you want. + ----------------------------------------------------------- + Alias definition + #alias [name[=[command]]] + + Aliases are abbreviations for longer, frequently used commands. + As all powwow commands, they are only recognized at the beginning + of a line or directly after a semicolon, an open or closed brace. + When an alias is used, the first word following the alias name + is placed in the variable $1, the second in $2, etc... up to $9. + also, the whole string following alias name is placed in $0. + + Then, before executing , every $n in is replaced + by its contents. + + Example: + #alias summ=cast 'summon' $0 (then "summ thor" is replaced by + "cast 'summon' thor") + #alias cs=cast '$1' $2 ("cs summon thor" is expanded to + "cast 'summon' thor") + #alias summ (lets you edit the definition of summ) + #alias summ= (removes the alias summ) + #alias (display all defined aliases) + #alias ws={wake;stand} (note that you must use braces) + + As noted above, aliases are saved automatically if you started powwow + with a file name as argument. + Aliases can contain other aliases, and an alias can also contain itself + (be careful, or powwow will enter a long - but not infinite - loop) + Aliases are not expanded in verbatim mode. + + Where aliases contain other aliases, one or more '\' can be prepended + to $n to delay text substition or to use the symbol $n literally. + + Example: + #alias ac=#alias $1=cast '$2' \$0 + (then "ac sm summon" will give you a new alias + "#alias sm=cast 'summon' $0") + + #alias \==score + (this defines = as an alias for 'score', Note that you must + escape the first = to force powwow to consider it literally) + + #alias \`=#history $0 + (this defines \ as an alias for '#history'. Note that you must + use ` to escape the \ . Just doing \= would escape the = ) + + Aliases starting with `#' are allowed and do work, + but cannot override any special command. + ----------------------------------------------------------- + Automatic command execution triggered on output + #action [[<|=|>|%][+|-]label] [{pattern | (expression)}=[command]] + + When 'pattern' is found in a line from the remote host, the + 'command' is automatically executed. If the pattern contains $n, + powwow matches one word of the remote line and puts it in the + variable $n. If the pattern contains &n, powwow matches a string + (possibly more than one word) of the shortest possible length + from the remote line and puts it in the variable $n. + + As in aliases, $n can be from $1 to $9. NOTE: $0 is a variable as well, + but #action automatically places in it the whole line from remote host. + As well, &n can be from &1 to &9. + + Warning: powwow does NOT print on screen lines from MUD that are + intercepted with an #action. So if you want to print them, you + must explicitly use a #print or set the #option +autoprint + (the help on #print and #option is below in this text) + If you want to intercept a prompt, use #prompt instead of #action. + (the help on #prompt is near the end of this file) + + If the first character of the pattern is ^ (caret), the match + will only be possible at the beginning of a line. + The match is case-sensitive. + If 'label' is specified, the action is labeled; otherwise it is + numbered. + + If an is used insted of pattern, + powwow evaluates the expression with the inline calculator (see below) + and then uses the result as pattern. + + Examples: + #action ^You are hungry=eat bread + #action ^You are hungry= (removes the action) + #action ^$1 hugs you={#print;kiss $1} + #action ^$1 says &2=say $1 said $2 (note you must use $, not & + after the =) + #action Jehova={#print;say Who said that? Stone him!} + + Labeled actions: + > means define, < means delete, = is edit, + is turn on, - is turn off. + Also, >- means define and turn off, while >+ means define and turn on + ( > alone is the same as >+ ) + + #action >fount ^There is a fountain here={#print;drink water} + (named action) + #action -fount (turns off action) + #action +fount (turns it back on) + #action =fount (lets you edit it) + #action -loot is dead! R.I.P.={#print;get all from corpse} + (define and turn off the action) + + #action >joke ("^$1 says '&2;)'")= wink $1 + (you must either use this syntax or escape the + ; to force it to be considered literally) + + #action >argh ^$1 tells you 'hello \`=tell $1 I heard you + (as in #alias, \ must be followed by ` when you + need the \ to be followed by a special char + and you do not want this char to be escaped) + + If you have compiled powwow with -DUSE_REGEXP and use % instead of > + you define an action that uses an Extended POSIX regexp to match + instead of the standard matcher. + + #action %first ^([[:alpha:]]+) ([[:digit:]]+)=#print $2 counted $3. + (matches abc 123) + + Note that if the pattern starts with '(', it is evaluated, which means + that a regexp that starts with '(' has either to be surrounded by + ("...") or to be prepended by a backslash. + Also note that powwow requires parentheses to be balanced: + for example, \(.+|) would be a valid regexp pattern as the backslash + gets removed by the unescaping, but powwow will choke on it + as the first parenthesis is escaped while the second is not. + + #action %second ("(..+)-\\1")=#print Double $1 + (matches xyz-xyz) + #action %third \(..+\)-\\1=#print Double $1 + (same as above) + For regexp actions, $0 = the line, $1 = the whole match and $2... + contain the submatches. + + Actions and aliases can run other powwow commands, including + #action and #alias. + Example: + #alias calc=#! echo '$0' | bc -l + #alias hungryon=#action ^You are hungry=eat bread + + As with aliases, additional \'s can be prepended to $n to + delay text substitution in actions. + Example: + #action >reply ^$1 tells you={#print; #alias reply=tell $1 \$0} + ----------------------------------------------------------- + +ACTION GROUPS + + Groups allow related triggers to be enabled and disabled together + using a single command, instead of toggling them all on or off + individually. To put actions in to a group specify the group name + after an '@' when the action is defined. For example: + + #action >+auto-ride@non-pk ZBLAM! A &1 doesn't want you={#print;stand;ride} + + Would create the action auto-ride in the non-pk group. Then if you + wanted to toggle off all non-pk actions, you could do it using: + + #group non-pk off + + And then later to turn them back on, use: + + #group non-pk on + +MISSING: #PROMPT + + There is another special command quite similar to #action: + #prompt [[<|=|>|%][+|-]label] [{pattern | (expression)}=[command]] + + You will need to use it only if you want to mess with the prompt + that your MUD sends, which is often not needed. Also, to use it + you need to understand exactly how powwow recognizes prompts, + so the description is considered an advanced topic and is placed + near the end of this file, in the "ADVANCED TOPIC: #PROMPT" section. + +MISSING: SUBSTITUTION AND UNESCAPING + + Also, only intuitive definitions of substitution, delayed substition + and escaping/unescaping have been given so far. If you want the real, + rigorous thing, read the "ADVANCED TOPIC: SUBSTITUTIONS AND UNESCAPING" + section near the end of this file. That section also explains + `just in time' substitution, not yet introduced. + ----------------------------------------------------------- + +SPECIAL COMMANDS: ALL OTHERS + + This is the rest of #commands recognized by powwow. + ----------------------------------------------------------- + Toggle verbatim mode + #quote [on | off] + + In verbatim mode, no alias expansion takes place and + semicolons, escapes, braces and ` are sent as typed, + unless the command line begins with a #, so you can still issue + powwow - specific commands. + This is useful if you want to paste a text from an editor into + a powwow session. + Type #quote to turn on the verbatim mode, and type #quote again + to turn it off. + ----------------------------------------------------------- + Show/execute commands in history + #history [number] + + #history alone shows you the last commands in history, up to the number + of lines in your screen. + #history -n shows the last n commands in history, and + #history n executes the n-th command of the history. + + Recursive #history commands (i.e. an #history calling itself or + another #history ) are allowed only up to levels of 128 recursions. + + Example: + + #history 1 (repeat last command) + ----------------------------------------------------------- + Add a text or expression to word completion list (not to history) + #add {text | (expression)} + + #add puts the text or result of expression (calculator is used to + evaluate the expression) in the word completion list. + Useful if used together with #action. + Example: + + #action >reply ^$1 tells you={#print;#add $1} + (from now on, you can use TAB to complete that name) + ----------------------------------------------------------- + Put a text or expression into history (and to word completion list) + #put {text | (expression)} + + #put puts the text or result of expression (uses calculator) in the + history, so that you can use cursor-up key to recall the text as if + typed from keyboard. + Also, you can execute the text using the #history command. + Example: + + #action >safeflee ^You flee head over heels.= + {#print;#put #print You have already fled away!} + + (If you type 'flee' from keyboard, you can keep trying to flee using + cursor-up (which gets the last command in history) and key. + When you finally manage to flee, the message above is put in history, + so that further attempts to flee do not lead you again in danger) + ----------------------------------------------------------- + Bind keys to enter commands + #bind [edit | name [sequence][=[command]]] + + You can bind most function keys and control keys to enter a command + for you when the key is pressed. Also, you can redefine a key already + used for an editing function (such as the arrow keys). + 'name' is the label of the key you want to define; you can just use + what is written upon it. When defining a new key binding, you will + be asked to press it so powwow can record the control sequence + your terminal sends. + If you want, you can specify the control sequence directly in the #bind + command instead of having to press the key on your keyboard. + + Examples: + + #bind (lists all user key bindings) + #bind edit (lists all line editing keys) + #bind f1=recite scroll (you'll have to press f1 then) + #bind f1=cast 'sanctuary' (change existing definition) + #bind f1 (lets you edit the definition) + #bind f1= (removes the key) + #bind f1 [[A=cast 'heal' (also tell powwow that f1 on your + keyboard sends ESC [ [ A, so you + do not have to press it) + + NOTE: if there is already something on your input line, powwow + does not ruin it when you press f1 (or any other #bind), but executes + the command you want and then shows you again the input line. + + #bind f5=&prev-line (&prev-line is one of the reserved commands for + line-editing functions, see bottom + of this file) + #bind Up=u (Up is an editing key, but can be redefined) + + By default, the vt100 numeric keypad is partially used to walk around + with: + Key string sent + 2 s + 3 d + 4 w + 5 exits + 6 e + 7 look + 8 n + 9 u + + The reserved names that powwow identifies as line-editing functions + are at the end of this file, together with the default keys used for + them. + + Remember that ALL keys can be redefined... + ----------------------------------------------------------- + Change the keyboard sequence associated to an existing key binding + #rebind name [sequence] + + If you just want to change the control sequence of a binding, but not + its name or its effect, you can just tell powwow to 'rebind' it. + If #rebind is invoked with only the name of a binding, you are asked + to press the key you want to rebind. + Of course, you will not be asked to press the key if you specify + its control codes in the #rebind command. + Examples: + + #rebind f1 ^[OP (tell powwow that your f1 key sends ESC O P + and not ESC [ [ A) + #rebind f1 (you are asked to press again f1, useful if you + changed terminal in the meanwhile) + ----------------------------------------------------------- + Change the keyboard sequence of all existing key bindings + #rebindall + #rebindALL + + #rebindall runs #rebind on most key bindings (skips trivial ones like + ^A, ^B, etc.), asking you to press each corresponding key. + + #rebindALL does the same, but for really every key binding. + ----------------------------------------------------------- + Execute a key as if pressed on keyboard + #key name + + If 'name' is the label of one of the key defined with #bind, + (see above) #key executes the corresponding command. + + Example: + If you have already typed + #bind f1=cast 'heal' + + At any time, then, you can either: + - Press your f1 key on you keyboard + - Execute the command "#key f1" + and powwow will execute the command "cast 'heal'" for you. + + Using #key, for example, can be useful from inside an + #alias or #action, since powwow cannot press f1 for you. + + Since 1.1.5, powwow allows #key also for editing functions. + If you have already + #bind Up=&prev-line + and you execute + #key Up + powwow will do what you expect: step to the previous line in history. + + Warning: powwow does not distinguish at all between a real key pressed + on the keyboard and one faked with #key, so commands executed with #key + will also be written in the #record file. + + ----------------------------------------------------------- + Execute an editing function as if pressed on keyboard + #keyedit function + + If 'function' is the name of one of the reserved commands + for line-editing functions, #keyedit function will run it. + + For example, if you already have + #bind Up=&prev-line + the following are all equivalent: + * pressing the key Up on your keyboard + * executing #key Up + * executing #keyedit &prev-line + + Anyway, if you type #key or #keyedit directly from the keyboard + the effect is slightly different, as you have to press ENTER + to run them and the function &enter-line (which is executed by ENTER) + has a few side effects. + ----------------------------------------------------------- + Clear aliases, actions or what you specify + #reset {all | name of a list} + + Argument: Effect: + all clear everything (apply all lines below) + alias clear all aliases + action clear all actions + bind clear all key bindings and restart with default + settings. Note that also editing keys are resetted + to default function. + at clear all delayed commands + in (same thing) + mark clear all markers + prompt clear all prompts + var clear all variables + ----------------------------------------------------------- + Mark certain output + #mark [pattern[=[attribute]]] + + This command highlights a part of a line in your output in the way you + choose (if your terminal supports it). + See the section "ATTRIBUTES: COLORS AND OTHER HILIGHTINGS" + about the syntax of attributes. + + Wildcards are allowed in the pattern, and syntax is very similar to + #action: $ matches a single word, & matches any string. + + Examples: + #mark Sizzler=bold (mark `Sizzler' in bold) + #mark Sizzler (lets you edit the above definition) + #mark Sizzler= (Sizzler is no longer put in bold) + #mark (lists all markers) + #mark {&}=inverse (mark in reverse any string in { } + note that also the { } are highlited) + #mark ^You=yellow (mark `You' in yellow only if it appears + at the beginning of a line) + #mark \^=on blue (mark a literal ^ ) + ----------------------------------------------------------- + #module [module name] + + This loads a shared library module by name, if supported by your system. The + name of the module should be included in the documentation that came with the + powwow extension, for example to load the perl module you would use: + + #module perl + + Which gives you the perl command that can be used like: + + #perl powwow::exec( "say it is " . scalar(localtime()) ) + + The commands added and their syntax varies depending on the module. + ----------------------------------------------------------- + Set/show priority for new actions/marks + #nice [{number | (expression)} [command]] + + When #nice is 0 (default) powwow puts new actions at the bottom of the + action list (and same thing for marks). If you want to put new + actions/marks in another point of the list, just set #nice to the + corresponding value: + + If you specify a command after 'number', the new #nice value is used + only for that command, then the old value is restored. + + Examples: + + #nice 12 (tells powwow to put new actions/marks in the 12th + (place of the list) + + #nice 4 #mark Grizzly=red on blue (put the mark in the 4th place of + the list) + + Note that #nice works only for new actions/marks: if an action/mark + is already defined, you cannot change its place in the list. + ----------------------------------------------------------- + Input highlighting + #hilite [attribute] + + This sets the attribute of your entered text to the given attribute. + Just #hilite turns it off. + See "ATTRIBUTES: COLORS AND OTHER HILIGHTINGS" below for more syntax. + ----------------------------------------------------------- + Set standard colours + #color [attrib] + + (This command exists only if BUG_TELNET is defined, to cope with + deficiencies of NCSA telnet 2.2) + Set your standard foreground and background to the colours you specify. + #color returns to the default colors for your screen + ----------------------------------------------------------- + Capture output to file + #capture [[>]filename] + + This captures all output from the main MUD connection and your typed + commands to a local disk file. To close the file and end the + capturing, type #capture without argument. + If the filename starts with a '>', new text will be appended to the + end of the file instead of overwriting it. + You can only capture output to one file at a time. + Example: + > #capture message + > look at board + > #capture + + It is possible to capture in the #capture file even text that you have + _already_ received: see #setvar buffer. + ----------------------------------------------------------- + Record typed commands to file + #record [filename] + + This records all text typed from keyboard to a local disk file. + (output from remote host is not recorded) + To close the file and end the recording, type #record without argument. + You can only record typed text to one file at a time, but #capture and + #record can be active at the same time on different files. + Example: + > #record walk-home + > n;e;e;u;n;w;s + > open door + > s + > close door + > sleep + > #record + ----------------------------------------------------------- + Capture output to file, with timestamps + #movie [filename] + + This is similar to #capture, but adds timestamps to each line + received from the main MUD connection or typed from the keyboard, + to allow replay at correct speed. + The program `powwow-movieplay' for replay is included with powwow sources. + Usage: `powwow-movieplay ' + To convert a movie to plain ASCII, the program `powwow-movie2ascii' + is included too. + Usage: `powwow-movie2ascii '. + + It is possible to capture in the #movie file even text that you have + _already_ received: see #setvar buffer. + ----------------------------------------------------------- + Execute a shell command + #! command + + Executes a command using /bin/sh. Powwow waits until your shell + finishes, but you can put jobs in the background with & as usual. + Example: + > #! who | sort | less + > #! nethack + > #! xbiff & + + Note that semicolons, escapes and braces need to be escaped if they + are to be sent to the shell. + If your shell has job control, you can also suspend powwow + with ^Z as usual. + ----------------------------------------------------------- + Put a string in the edit buffer automatically + #prefix [string] + + Each new line you type will automatically begin with the prefix string. + You can of course edit or delete the inserted string as usual. To + remove the prefix, just issue a #prefix command without arguments. + This is handy when you are talking to someone, for example. + > #prefix tell arthur + ----------------------------------------------------------- + Help + #help [keys | math | command] + + Shows a list of powwow's commands. + '#help keys' shows the editing keys. + '#help math' show help on inline calculator. + You can also have help on specific commands, using for example + '#help alias' or in general '#help '. + A help file is needed and provided for this last feature of #help, + and powwow will look for the file "powwow_help" in the directory + specified by the environment variable POWWOWHELP. If this variable + does not exist, powwow looks in current directory. + ----------------------------------------------------------- + Command repetition + #n command + + This repeats the command n times. Example: + > #5 buy bread (buy five breads) + + + Alternatively, you can use this syntax to repeat a command n times: + #do (expr) command + + In this case, powwow evaluates the expression, and uses the result + as counter. + Example: + > #do (3*8) north (go north 24 times) + ----------------------------------------------------------- + Iterate a command + #while (expression) command + + This syntax repeats a command while expression (evaluated with + calculator) keeps true. (see below for help about calculator) + As with #alias and #action, the $n and @n in command are + replaced by their values. (Even if you can, using @n after the = + in #action and #alias is useless, because you have no way to + assign them a non-zero value. This is the reason why we did not talk + about them in the #alias and #action section) + + Example: + + #while (@0<13) {read @0;#(\@0++)} read messages 0-12 + As you can see, the last @0 is escaped to avoid it to be + substituted with its value. (We want to increase the variable!) + ----------------------------------------------------------- + Iterate a command + #for ([init];check;[loop]) command + + Directly copied from C language, this command evaluates 'init' + with calculator (if specified), then repeats the following cycle: + 1) evaluate 'check', if result is 0 (false) stop repetition + 2) execute 'command' + 3) evaluate 'loop' (if specified) + 4) restart from 1) + + As with #while, #for performs the parameter substitution in 'command', + so the only significative difference between #while and #for is that + #for allows you to execute an initialization before the repeat cycle. + + Example: + + #for (@1=32; @1<=47; @1++) read @0 (read messages 32-47) + ----------------------------------------------------------- + Branch execution command + #if (expression) command1 [; #else command2] + + Evaluate the expression: if result is 'true' execute command1, + otherwise (if there is an #else) execute command2. + If expression is false and there is no #else, execute nothing. + remember that you must use braces {} if command1 or command2 + contain more than one instruction. + + Note that nested #if-#else are allowed, and that #if-#else itself + is not a multiple command. + + WARNING: using an alias for #if is very dangerous and will cause + powwow to make confusion when the full #if-#else syntax is used. + ----------------------------------------------------------- + Automapping control + #map [-[number] | walksequence] + + With no argument, "#map" shows the map of the directions you + travelled up to now. "#map -" clears the last number of steps + from the map. + + Example: + #map (displays "#current map: e3su" after above walk) + #map -1 (leaves the map as "esss") + #map - (clears the whole map and starts fresh) + #map nsssue (add the list of directions to map) + ----------------------------------------------------------- + Retrace steps + #retrace [number] + + This command walks you in the reverse direction of the last + number of steps. If number is 0 or left blank, you walk all + the way back to where automapping started. + ----------------------------------------------------------- + Explicitly execute a speedwalk + #speedwalk [walksequence] + + This command can be used to execute a speedwalk sequence even + if you have the speedwalk option disabled. This is useful if you + do not like to have typos evaluated as speedwalk commands but still + want to be able to easily execute a speedwalk. + ----------------------------------------------------------- + Connect initialization string + #init [=[command]] + + This command sets up the initialization string to send to + the host on establishing a connection. + + Example: + #init (shows the string) + #init ={#identify;#speedwalk} (sets the string) + #init = (clears the whole string) + ----------------------------------------------------------- + Identify as an editing client + #identify [startedit [endedit]] + + This command sends an identification string to the server, to + indicate that the client supports editing functions. It is + best attached to an action trigged by a string in the login + screen, but can also be entered by hand. + This command must be issued for the cooperative editing to work + on servers that support it (currently only MUME). + The startedit/endedit parameters are commands issued when an editing + session starts/ends (for changing your title, emoting etc). + + Example: + + #action >mume *** MUME=#identify + #identify foo bar + (where foo and bar are aliases that do something useful) + ----------------------------------------------------------- + Identify as a IAC GA compliant client + #request prompt + + This command sends an identification string to the server, to + indicate that the client supports (and wants) the special sequence + IAC GA at the end of each prompt. This helps the client + to automatically detect the prompt, and can be used as alternative + to #prompt / #isprompt if all you want with the prompt is detecting + it (and not altering it) + + BIG WARNING: + this is experimental and not tested! + + Example: + + #action >mume *** MUME={#print;#identify;#request prompt} + #request prompt + ----------------------------------------------------------- + List all editing sessions + #edit + + This command shows your active editing sessions, with a brief + description and their number. + ----------------------------------------------------------- + Cancel an editing session + #cancel [number] + + Without an argument, all editing sessions are cancelled; + otherwise, only the given session is cancelled. The corresponding + editor processes are brutally killed. + ----------------------------------------------------------- + List/turn various options on/off + #option [[+|-|=]option-name] + + Currently available option names are: + exit, history, wrap, compact, debug, echo, info, keyecho, + speedwalk, wrap, autoprint, buffer, reprint, sendsize, + autoclear + + #option +name turns an option on + #option -name turns it off + #option name toggles it + #option =name reports its status + + ------------- + #option exit + + If the `exit' option is on, powwow automatically quits when the last + connection is closed. Otherwise, to quit powwow you need to manually + type `#quit' + ------------- + #option history + + With `history' option on, powwow writes into your savefile also + all your commands in history + ------------- + #option words + + With `words' option on, powwow writes into your savefile also + your word completion list + ------------- + #option compact + + Normally, powwow does not touch the prompts on screen while you play. + In `compact' mode, instead, lines containing only a prompt are deleted + when further messages arrive from the remote host. + + WARNING: this command works correctly only if you have #prompts which + correctly run #isprompt. Otherwise it may occasionally erase + some lines from the screen. + ------------- + #option debug + + Normally, powwow does not print on screen the command lines it + executes. When `debug' is on, every line executed by powwow is also + echoed on screen, so that you can check if your code works correctly + (warning: this prints LOTS of lines on your screen) + ------------- + #option echo + + Normally, powwow echoes on your screen each command sent to remote + host but not directly typed (example: aliases and actions sending text + to the MUD). When `echo' is off, such commands are still sent to host, + but not echoed on screen. + ------------- + #option info + + Normally, powwow prints on screen some messages each time you + define/edit/delete an #alias, #action, #bind and similar. + When `info' is off, those messages are not typed at all. + (But errors are still printed on screen) + ------------- + #option keyecho + + Normally, powwow echoes on your screen the commands sent to remote host + when you hit a key associated to a #bind. When `keyecho' is off, such + commands are still sent to host, but not echoed on screen. + ------------- + #option speedwalk + + With `speedwalk' on, a command consisting of only lowercase + n, e, s, w, u, d and numeric digits is considered to be a + walk sequence. The numeric digits specify the number of + times to repeat the direction immediately following. + + Example: + esssu (walk east, 3 south, up) + e3su (same as above) + ------------- + #option wrap + + Normally, powwow wraps words that would have been cut by the right + margin to the next line. This command lets you turn it off and on. + ------------- + #option autoprint + + If `autoprint' is on, powwow prints lines matched by an #action + even without an explicit #print. + ------------- + #option sendsize + + Usually powwow does not send the window size to the MUD unless asked. + If you want to send the window size automatically upon connecting, + you may enable this option. + ------------- + #option autoclear + + Powwow normally erases the input line before executing commands + from spawned programs, but that is slow and causes flicker. + If autoclear is disabled flicker reduces to minimum, + on the other hand spawned programs must then execute #clear + before sending anything to screen. + ------------- + #option reprint + + If `reprint' is on (off by default), powwow prints again commands + sent to the mud but not yet executed. + WARNING: it works only if you use #prompts which correctly run + #isprompt. + + ++++ example: ++++ + *>look + south + down + + The High Path + + *>Path Climbing a Hill + + *> + Alas, you cannot go that way. + + *> + ++++ becomes: ++++ + *>look + south + down + + The High Path + + *>(south) + Path Climbing a Hill + + *>(down) + Alas, you cannot go that way. + + *> + ----------------------------------------------------------- + Show current version + #ver + + Displays the current version, some compile options and (if + your compiler supports it) when powwow was compiled. + ----------------------------------------------------------- + Multiple connections handling commands + #connect [session-id [initstr] [host port]] + connect a new session / list sessions + #snoop session-id toggle output display for session + #zap session-id disconnect a session + ## set as default session + ## command execute command on session + + No docs here. If multiplaying is allowed on you MUD (and many + do NOT allow) you can experiment a little to find how they work. + Or you can open two connections to two different MUDs :) + ----------------------------------------------------------- + Spawn an external program + #spawn session-id command + + Creates a new session, connected to a shell command instead of a MUD. + Writing to ## sends data to the command's standard input, + while the command's standard output is executed as if typed + from keyboard. Useful if you are a programmer and you want to create + very complex filters or robots, for which #actions are too limited. + Command's standard output *MUST* terminate with a newline ('\n') in + order for powwow to execute it. + You can send multiple commands at once terminating each of them + by either a semi-colon ';' or a newline '\n', except for the last one + which (I repeat) *MUST* terminate with a newline. + + You can close these sessions with #zap and list them with #connect + as usual. + + Depending on how lazy you are, you can choose two different ways + to have spawned programs print to screen: + + The first is automatic, but slow: with `#option +autoclear' + powwow clears the input line before executing every line received + from the program. This is of course slow and causes flickering. + + The second is more complex, but faster and reduces flickering to the + minimum: set `#option -autoclear' from the beginning, then have + the program execute `#clear' before printing. + ----------------------------------------------------------- + Exit from powwow + #quit + + Very little to say here. Just remember that #quit brutally + closes all mud connections that are still open, without + renting your characters. Quite likely, you want to rent them all + before quitting. + ----------------------------------------------------------- + Set definition-file and save settings to it. + #save [definition-file] + + Useful after you write large chunks of code. + Remember anyway that powwow automatically saves the settings + before exiting. + + #save actually writes settings to a temporary file and overwrites + the definition file only if write succeeds. This is to avoid wiping out + definition file in case of `disk full' or other errors. + ----------------------------------------------------------- + Set definition-file and load settings from it. + #load [definition-file] + + Useful if you want to undo the changes in your settings. + + NOTE: current settings are completely erased before actually loading + from file. In case of errors, powwow reloads the default editing keys. + + If you just want to add the contents of a file to your current settings + use #exe disint ^&1 disintegrates &2=#print $1 DISINTEGRATES $2 + put the text in upper case + #action >disint ^&1 disintegrates &2=#print ($(1)+" DISINTEGRATES "+$(2)) + same thing, but using calculator + + #print to of its output. + + Also here, you can use expressions instead of Bourne shell command, + start and end, and powwow still begins from first line if + is not specified and/or stops at the end of the output if + is not specified. + + Both these special features are supported ALSO in #send, #exe, + #emulate and #var. + ----------------------------------------------------------- + Send text or result of an expression to MUD + #send [< | !]{text | (expression)} + + The simplest use of #send is to evaluate an expression and to send + the result to the MUD. More generally, #send is very similar to #print, + with the only difference that the final text is sent to the MUD rather + than displayed on screen. + The meaning of < and ! is the same, and #send does the expected things + when they are used. + + Example: + + #send | !](expression ; file) + + Evaluate expression and get result, then write result into file. + By default, text is appended at the end of the file. + + If > is specified, #write deletes the contents of the file before + actually writing the text. + + If ! is specified, #write assumes second parameter to be + a Bourne shell command (instead of a file name) that is executed + using the text as its input. + + Example: + + #write ($test; "myfile") (append contents of $test to myfile) + + #write !("55+12";"bc -l") (execute 'bc -l' writing text to its + standard input) + + Advanced `#write' usage: + + If you are using a terminal allowing multiple windows (an X11 graphic + terminal for example) it is possible to duplicate/split powwow output + to multiple windows using #write. This is more a UNIX topic rather + than a powwow-specific one, but that's it. Here is a brief summary: + + First, compile the `catrw' mini-program i.e. type + $ make_it catrw + if the above worked, type + $ mkfifo fifo + This will create a special file named `fifo' in the directory + (any other name would do, of course) + Then you have to open another window. This depends on the terminal + you're using, but for X11 terminals the following works: + $ xterm & + On the second window, type + $ exec catrw fifo + (in case this gives you an error, try just `catrw fifo') + Now return to the first window and start powwow normally. + To send text to the second window from within powwow, type: + #write ("some text"; "fifo") + You should see `some text' (without the quotes) immediately + appear in the second window. + + Of course you may now want to send text automatically + to the second window: just use #write ( ; "fifo") + from within an #alias, #action or whatever you like. + + P.S.: + for experienced users: if you are using the `bash' shell, + you don't need `catrw' as you can use + $ exec cat <> fifo + instead of the above + $ exec catrw fifo + ----------------------------------------------------------- + Set/show internal variables + #setvar name[={number|(expr)}] + + Evaluate the expression and get result, then set the internal + variable `name' to that value. + + Known internal variables are: + + buffer with `buffer' different from zero, powwow saves + the most recent text from the MUD in a circular list + (which is `buffer' bytes long) and writes it + at the beginning of #capture and #movie files when + you open them. This is useful if something important + happens suddenly and you want to log it somewhere: + you can start #capture and/or #movie even _after_ the event + has happened and it will still get written to the file. + + if `buffer' is zero (default), powwow starts logging + text from the MUD only at the moment you activate + #capture or #movie. + + To discard the text stored in memory by `buffer', + change its value (for example, set it to zero + and then back to a non-zero value). + + lines the number of lines your terminal has. Powwow usually + autodetects it correctly, but on few terminals you may + have to set it manually. + + mem the maximum length of a text or string, in bytes. + The default is 0 (zero) which means no limit. + I added it only to prevent bringing down the whole system + with things like + #while (1) #($foo += $foo + "x") + Consider it an emergency setting, as powwow _discards_ text + and strings longer than the limit. + The failsafe limit set when loading a savefile from an older + version is 1Megabyte, which won't give any problem + (like increased memory usage) as powwow allocates memory + only when it *has* to. + + timer the number of milliseconds since program start. + It can be changed to synchronize with an external clock + like MUD ticks. + + Example: + + #setvar timer=0 (reset internal timer to 0) + #setvar timer=20000 (make internal timer restart from + 20000 milliseconds) + #setvar timer (show timer and let you edit it) + + #setvar mem=1048576 (max strings length is now 1Megabyte) + ----------------------------------------------------------- + Send raw data to MUD + #rawsend {text | (expression)} + + This is mostly a MUD debugging tool, but it can be useful in some cases. + Like its cousin #send, #rawsend evaluates the expression (or unescapes + the text) and sends the result to the MUD. The difference is that + #rawsend does NOT add a final newline, nor does IAC escaping to protect + ASCII 255 characters. On the other hand, #rawsend can handle ASCII 0 + characters, while plain #send can't. + ----------------------------------------------------------- + Send raw data to screen + #rawprint {text | (expression)} + + Like its cousin #print, #rawprint evaluates the expression (or + unescapes the text) and sends the result to the screen. The difference + is that #rawprint does NOT add a final newline. On the other hand, + #rawprint can handle ASCII 0 characters, while plain #print can't. + ----------------------------------------------------------- + +INLINE CALCULATOR: + + The inline calculator is used to evaluate expressions inside + #(), #print (), #exe (), #send (), #if (), #while(), #for (), #do (), + expressions in pattern of #actions and in other commands allowing () + + The inline calculator recognizes the following objects: + + numbers (only integers are supported) + decimal numbers: + simply write them. + + hexadecimal numbers: + use '#' as prefix: #F is 15, #a0 is 160, and so on. + + numbers in any other base: + use base# as prefix: 2#101 means 101 in base 2 (that gives 5) + 7#14 gives 11, etc... + + if you use negative non-decimal numbers, you must put '-' + before the base: - 2#101 is -5, 2#-101 causes an error. + + it is possible to chain more than one '#': + 3#12#100 = (3#12)#100 = 5#100 = 25 + + both base and argument must be numbers, not variables: + things like 7#@count or @count#7 are not allowed, you will + have to use an #exe for that. + + quoted-strings (i.e.: strings in " ") + + NOTE: + since version 0.6d, powwow performs unescaping on quoted strings + when they are evaluated. For example "\"" is the string that contains + the character " only. + + timer (number of milliseconds since last timer reset) + + map (string containing the last 999 steps you walked, + as the #map command) + + variables: + @n with n within -50 and 9, are numeric-variables + $n with n within -50 and 9, are string-variables + + Since version 0.8, also named variables are supported: + + @any_name1 + $any_name2 + + The name following @ or $ can contain any of these chars: + uppercase or lowercase letters ('A'...'Z' and 'a'...'z') + underscore ('_') + numbers ('0'...'9') + Anyway, the first char of the name must NOT be a number. + + Remember that powwow is case sensitive: + $test and $Test are NOT the same variable + + Named variables are created the first time you use them + and can be deleted only using the #var command + + A special named variable is $prompt, which contains + the current prompt. It cannot be deleted. + Another special variable is $last_line, which contains + the last non-empty line received from the MUD. Again, + it cannot be deleted. + + Difference between the various kind of variables: + + Numbered variables with negative counter (@-50..@-1 and $-50..$-1) + and named variables are global: + They can be accessed at any time, but cannot be used for the + substitution performed by #alias, #action, #while and #for. + + Instead, numbered variables with positive counter (@0..@9 and + $0..$9) are local: + A new set is created (and initialized to zero) every time powwow + executes an #alias, #action, #while or #for, and the old set + is made invisible. After the #alias (or #action, #while, #for) + is executed, the old set is restored. + Note that also @0..@9 can be used for parameter substitution, + and not only $0..$9. + + Variable names as expressions: + + The symbols $ and @ are implemented as normal operators, + which means that variable names can be arbitrary expressions. + For example, + $(1-3) is the numbered variable $-2 + @("foo"+"bar") is the named variable @foobar + $$1 is the variable whose name is in $1 + + operators between numbers: + ++ -- + - + * / % + + - + << >> + < <= > >= == != + & | ^ + && || ^^ + = *= /= %= += -= <<= >>= &= ^= |= &&= ^^= ||= + , + ( ) + (% and %= always return non-negative values) + (no help on these operators, see a C-language manual) + (note: unlike C, operators &&, ^^ and || always eval both arguments) + + random number generator: + + rand positive-number (return a random number between 0 and n-1) + + operators between strings: + + chain two strings + = assign a string to a string-variable + += append a string to a string-variable + - compare two strings: result -1 if s1s2, + 0 if s1==s2 + < <= > >= == != compare two strings + .? number of chars in a string + :? number of words in a string + ? position of first occurrence of second string in the first + * convert first char of a string into its ASCII code or vice versa + % convert string into its numeric value or vice versa + + operators between a string and a number: + (string is first argument) + : n-th word of a string + . n-th char of a string + :< :> <: >: .< .> <. >. return part of a string, in this way: + : before > or < means 'mark the n-th word from the left' + . before > or < means 'mark the n-th char from the left' + : after > or < means 'mark the n-th word from the right' + . after > or < means 'mark the n-th char from the right' + > means: return from marked word/char to end + < means: return from start to marked word/char + + so we get: + :< n first n words + :> n from the n-th word (include) to the end + <: n from the begin to the n-th word (included) + >: n last n words + + and similarly for .< .> <. >. + + * repeat a string n times: "ab" * 3 gives "ababab" + *= usual shortcut: `$x *= n' is the same as `$x = $x * n' + + functions for low-level color handling: + + noattr (string containing the escape sequence to reset terminal + colors and attributes -- bold, underline, inverse) + + attr "quoted-string" + (return the escape sequence needed to turn on + the attributes and colors in the string. + Syntax of the string is the same as #mark, #hilite, etc) + + + Examples: + + #print ($5="Hello, world") (assign "Hello, world" to $5 + and print it) + #print ("This is a test">:3) (print from the 3rd word from the right + till the end of the string) + Result: "is a test" is printed on screen + + #action >+exp ^You have scored $1 exp={#print;#print ("You gained " + + ( $1 - @-5) + " exp. points since last score"); #(@-5 = $1)} + + (when you type 'info' in MUME, one of the lines you get is: + You have scored xxx exp. points ... + The #action above intercepts this line, prints it, prints the + difference between your current score and the contents of + variable @-5, then copies your current score in @-5) + + #print ($5 = (attr "bold green") + "Hello, world!" + noattr) + + (same as first example, but with colors/attributes. + Rememeber to print noattr at the end of every colored line, + or everything appearing on the screen after your line + will be colored as well) + ----------------------------------------------------------- + +HOW INLINE CALCULATOR IS IMPLEMENTED + + Info and hints to get the most out of calculator and/or hack it. + + The structure `op_list[]' defined in xeval.c contains definitions for + all the implemented operators, one for each line. Have a look at it + to find things like: + + precedence (first number in each line) + associativity (LEFT or RIGHT) + LEFT means that 1+2+3 actually is (1+2)+3 + RIGHT means that 1+2+3 actually is 1+(2+3) + (replace numbers and operators with what you are actually using) + if it is unary, i.e. needs ONE argument + PRE_UNARY means that the operator comes before its argument, + POST_UNARY is the opposite + or binary i.e. needs TWO arguments + + Note that stuff like `attr', `rand', `@' and `$' are actually + implemented as PRE_UNARY operators (named variables are treated as an + exception to this), thus `$(1+5)' and `attr ("bold"+" "+"inverse")' + are fully allowed. Also note that using `$(5)' is a good way to avoid + the parameter substitution performed by aliases, #action, #for, #while + and use instead the actual variables. + + `timer', `map', `noattr' are implemented as read-only values: + the calculator simply substitutes them with their value + + Remember that there is a , (comma) operator: + Instead of `#(foo);#(bar)' you can use `#(foo, bar)' + Using comma operator is easier for both you and powwow, since it uses + a single command instead of two. + ----------------------------------------------------------- + +ATTRIBUTES: COLORS AND OTHER HILIGHTINGS + + Some commands use attributes to specify the visual appearance of text. + The following attributes are available: + bold, blink, underline, inverse + -- the obvious effects + reverse -- same as inverse + [color] [on color] -- foreground and/or background + Colors are: + black, red, green, yellow, blue, magenta, cyan, white and + BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, none + ('none' means to use default and is implemented as a color) + + Examples: The following are all valid attributes: + none -- no attribute, use default + green -- only foreground + on white -- only background + yellow on cyan -- foreground and background + inverse bold -- + blink red on blue -- you can use multiple attributes, + but you must put 'bold' 'inverse' + and/or 'underline' BEFORE colors + + Observe that bold, blink, underline and reverse work with all terminals + that support these attributes, but colors only on terminals that + support ANSI color escape sequences. + + Capitalized colors (BLACK..WHITE) are non-ANSI high intensity colors, + and will not work on all terminals (They work on the 'aixterm' terminal + emulator under AIX, but they may work on other terminals as well. + Let me know if you find other terminals that support them). + + Notes for IBM PC and compatibles with VGA or SVGA cards in text mode: + -- yellow is actually brown + -- bold is usually represented as high intensity + -- blink can be represented either with actual blink or + high intensity background - depends from card configuration + (it is possible to reprogram it - I can send details if asked) + ----------------------------------------------------------- + +HISTORY + + Powwow keeps in memory the last 127 lines you typed from keyboard. + If you want to recall one of them (to correct a mistake for example) + you just need to press your arrow-up key or control-p to scroll through + the whole history, one step at time (see COMMAND LINE EDITING below + in the text for details about editing keys). + + Another way to find a certain line in the history is to type the first + few chars of it, and then press M-TAB to tell powwow to complete the + line for you. If you hit M-TAB repeatedly, powwow will cycle + through all the possible completions. + + Also, you can use the '#put' command to add a line to history + as if you typed it (see above for help on #put). + ----------------------------------------------------------- + +WORD COMPLETION LIST + + Powwow also remembers the last 512 words you typed from keyboard. + This list of words is named `word completion list'. If you have already + typed a long or difficult word, you can type the first few chars of it + and then press TAB key to ask powwow to complete it for you. + Again, if you hit TAB repeatedly powwow will cycle through + all the possible completions. + + Powwow can also complete the name of any built-in command even if + not present in the word completion list. + + Also, you can use the '#add' command to add a word to word completion + list (see above for help on #add). + ----------------------------------------------------------- + +COMMAND LINE EDITING + + The default key bindings for line editing follow quite closely Emacs: + These are all the keys, together with the reserved names that identify + their function (they can be listed typing '#bind edit'): + + Key: Function name: Description: + + ^A &begin-of-line beginning of line + ^E &end-of-line end of line + ^B &prev-char backward one character + ^F &next-char forward one character + ^P &prev-line use previous line in history (step backward) + ^N &next-line use next line in history (step forward) + ^D &del-char-right delete character under cursor + BS &del-char-left delete character left of cursor + ^K &kill-to-eol kill to end of line + ^T &transpose transpose previous character with next + (if at the end of the line, the two last) + ^L &redraw-line redraw your command line. + This is useful if something garbles your input + line. + ^Q &clear-line clear input line. + ^W &to-history put current line in history and clear input + line. This is useful when you are typing a long + line and need to send another urgent command + first. + ^Z &suspend suspend powwow in the background + Tab &complete-word complete the word being typed to the last + matching word in the history (or added with an + #add command; see above). + Hit multiple times to browse the possible + completions. + This is similar to the GNU Emacs M-/ command. + M-Tab &complete-line complete the line being typed to the last + matching line in the history. + Hit multiple times to browse the possible + completions. + M-f &next-word forward one word + M-k &redraw-line-noprompt + redraw command line, discarding prompt + M-b &prev-word backward one word + M-d &del-word-right delete word right of cursor + M-BS &del-word-left delete word left of cursor + M-l &downcase-word turn word to lowercase + M-t &transpose-words transpose previous word with next + M-u &upcase-word turn word to uppercase + Ret &enter-line the most obvious: execute the typed line + LF &enter-line same thing, but for ^J key (some terminals + send this when you hit return) + (none) &insert-string insert on command line the specified chars + + M-x means pressing the META or Alt key at the same time as x, + or pressing and releasing the escape key, then typing x. The former + way doesn't work on all terminals. + + ^x means pressing the Control key at the same time as x. + + If your terminal has arrow keys, they can be used to move the + cursor and step around in history. In addition, you can define your + own key bindings for sending quick commands (see the #bind command). + If you have a vt100-compatible terminal, the numeric keypad is + predefined for movement (keys 2, 3, 4, 5, 6, 7, 8 and 9). + + Remember that ALL keys can be redefined... + + + A brief note about &insert-string: + + By default no key is bound to this function, and it works somewhat + differently than other editing functions. + + For example, say you don't have `{' and `}' on you keyboard + (it happens on all italian keyboards -- like mine -- and other ones). + Obviously, typing { or } gets quite difficult. A solution is: + + #bind F11=&insert-string \173 + #bind F12=&insert-string \175 + + where \173 and \175 are typed normally: a backslash and three digits. + Once you defined these two bindings, hitting F11 will be exactly like + typing { and hitting F12 will be exactly like typing } . + + Another possible use is to enter strange characters or strings: + + #bind F10=&insert-string Ro\353ntgen + does exactly what you expect: insert "Roëntgen" on the input line + ( ë is the ASCII char (octal)353 or (decimal)234 ) + as if you typed it (of course you could also type the first few chars + of the name then hit TAB if that name is already in the word completion + list...). + ----------------------------------------------------------- + +SECURITY + + When you define an #action that automatically sends something back to + the MUD you are connected to, you must be VERY careful since you may + allow other players to force you to execute commands. + Let's explain better: Suppose you define the following #action: + + #action >+autogroup ^&1 starts following you.={#print;group $1} + + Even though this may look harmless, such an action is potentially + lethal, for the following reason: + If you receive a text from the MUD containing something like + + Cauldron ;remove all;drop all;kill dragon starts following you. + (for example it may be an emote, many MUDs allow it) + + powwow will realize that the line matches with the action you defined + (remember that &n can match text of every length, even if containing + spaces or ; ) and will execute this: + + {#print;group Cauldron ;remove all;drop all;kill dragon} + + The consequences of such a command can be easily imagined... + There are two strategies to avoid such embarassing situations: + 1) Use #send and calculator. In fact this is NOT dangerous: + + #action >+autogroup ^&1 starts following you.= + {#print;#send ("group "+$(1))} + + (in the worst case you will send some semicolon-separated commands + to the MUD, but I saw no MUDs accepting multiple commands as clients + do...): + + 2) Try to use $n instead of &n, so that semicolons and spaces + are skipped. + + #action >+autogroup ^$1 starts following you.= + {#print;group $1} + + WARNING: + versions older than 0.7a were bugged and they did NOT skip + semicolons (but they skipped spaces), so also using $n was + dangerous! + + If you really need to use a &n, check you are not losing security, + and if you cannot write safe code, use calculator as in point 1). + Note that this is NOT dangerous too: + + #action >+autogroup ^&1 starts following you.=group $1 + + since if someone tries to force you as explained above + it will not work, because #action allows only ONE command to follow + the pattern and you did not place braces around "group $1", + so only the first command (in this case "group ") + will be executed. + + In every case, remember the best strategy is: check what you are doing, + and do not lose control. If you are not sure a command is safe, better + not to use it. + ----------------------------------------------------------- + +LIMITS + + Powwow has the following limitations: + + Numeric variables are defined as 'long', that means 32-bit integers + on most systems. + + String variables, text lines and commands by default have no length + limits. If you want, you _can_ set a max limit with `#setvar mem'. + Powwow discards text and strings longer than such a limit. + + Exceptions: the labels/patterns of #aliases, #actions, #prompts, + #marks, #in/#at etc. cannot be longer than 4095 chars. + The same limit (4095 chars) applies for the input line. + (the number can be changed by modifying the symbol BUFSIZE) + + Unnamed ('numbered') variables must have indexes from -50 to 9. + (the 50 can be changed modifying the symbol NUMVAR, the 9 cannot + be increased due to hardcoded limits) + + Inline calculator can hold up to 100 suspended operations, due to + parentheses and/or inverted priority of operators. + (the number can be changed by modifying the symbol MAX_STACK) + + The depth of nested/recursive aliases, actions, prompts, #while and + #for commands is limited to 100 nested calls. + (the number can be changed by modifying the symbol MAX_STACK) + + The number of loops of a #while or #for is limited to 10000. + (the number can be changed by modifying the symbol MAX_LOOP) + + Automap can be at most 999 steps. + (the number can be changed by modifying the symbol MAX_MAPLEN) + + History can contain at most 127 lines. + (the number can be changed by modifying the symbol MAX_HIST) + #history commands can execute other #history commands, up to + MAX_HIST levels of recursion. + + Word completion list can contain at most 512 words. + (the number can be changed by modifying the symbol MAX_WORDS) + + Up to 32 MUD (or spawned) connections can be open simultaneously. + (the number can be changed by modifying the symbol MAX_FDSCAN) + + For all other resources, the only limit is the available memory. + ----------------------------------------------------------- + +THE BREAK KEY + + It is usually Control-C or DEL (it depends from the terminal you use). + + Powwow cannot redefine it, but you need to hit it twice in a row + to actually stop powwow. + This is because hitting it only once is used to stop command parsing: + if you enter a long loop using internal commands + (for example: #while (1) drop 1 coin) + you can simply press your break key and powwow will immediatly exit + from the loop with this message: `#interrupted. Press again to quit.' + + If you press the break key again, you will exit powwow. + Otherwise, if you first type something, then you press break key once + more, you will get again: `#interrupted. Press again to quit.' + ----------------------------------------------------------- + +ADVANCED TOPIC: SUBSTITUTIONS AND UNESCAPING + + WARNING: + this is a bit complicated and not recommended for beginners, + as the explanation given at the beginning about $n and \'s might + suffice in many cases. So you might skip this paragraph if you want. + + Still reading? Ok, this is it: + + We described in the beginning that adding \'s to $n delays text + substitution in aliases and actions. Actually, every time powwow + is asked to execute a command, it can make one or more of the + following operations on the command itself before executing it: + + Step (a) : `PARAMETER SUBSTITUTION' or simply `substitution' + + (a1) place in $1..$9 the correct words + + (a2) replace every occurrence of $1..$9 with the contents of the + corresponding variable. Also replace every occurrence of @1..@9 + with the contents of the corresponding variable. + Note that if one or more \ are preceding a $n or @n, + it will be NOT substituted. + + Step (b) : `JUST IN TIME SUBSTITUTION' or `jit' in short + + (b1) replace every occurence of #{expression} with the value of the + expression. Also replace every occurrence of ${name} and @{name} + with the contents of the corresponding variable. Again, + if one or more \ are preceding a #{expr}, ${name} or @{name}, + it will NOT be substituted. This substitution works also + for numbered variables ${number} and @{number}. + + Step (c) : `UNESCAPING' + + (c1) Remove ONE \ from every list of consecutive escapes, + unless they are followed by one or more ` (i.e. reverse-escaped) + For example, \\\$1 is replaced with \\$1 + + (c2) Remove ONE ` from every list of consecutive escapes immediately + followed by a list of consecutive ` + For example, @``` is not modified, + while \\` is replaced with \\ + and \\``` is replaced with \\`` + + The steps actually executed vary from command to command, + but are always ran in order: + if both present, (a) always precedes (b) + if both present, (a) always precedes (c) + if both present, (b) always precedes (c). + + ----------------------------------------------------------- + + When each step is performed/not performed: + + Step (a) (substitution) is performed when executing one of the + following: + aliases, actions, prompts, #for or #while + + Step (b) (jit) is performed when executing _any_ command that allows + a single instruction, and is executed on that instruction before + running it. The list is: + #alias, #action, #prompt, #at, #bind, #connect, #do, #for, #identify, + #if-#else, #in, #init, #nice, #while. + + Also performed on normal (not yet implemented for regexp) patterns + of #actions before matching them. On regexp patterns, step (c) + is made instead. + + Step (c) (unescaping) is performed any time that step (a) + and/or step (b) are performed. + + In addition, unescaping is also performed on text + (not on expressions) following all #commands that allow plain text: + #add, #emulate, #exe, #mark, #print, #put, #send, #var + + on labels of all #commands that allow labels: + #alias, #action, #prompt, #at, #in + + and last, on text that is not a #command nor an alias + before sending it to the MUD, unless the last operation on the + text was _already_ an unescaping. + + Examples: + + #alias fb=cast 'fireball' ${target} + #var $target=troll + fb (effect: cast 'fireball' troll) + #var $target=dragon + fb (effect: cast 'fireball' dragon) + + #action >chase ^${target} leaves $1={#print; #alias f=$1} + (whenever `dragon' leaves the room, + the alias 'f' is set to follow it) + + #action >chase2 ^\${target} leaves $1={#print; #alias f=$1} + (the text `${target}' will be matched + literally) + WARNINGS: + + Step (b) is NOT automatically performed on text typed from the keyboard + so for example `#print ${target}' just prints literally `${target}' + and not the contents of the variable. + If you need step (b) on text you type, you can do something like: + #alias /=$0 + and then prepend all commands with `/ ' : + / #print ${target} + + Step (b) is not yet implemented for regexp actions/prompt due to + internal difficulties. As a workaround, step (c) (unescaping) + is instead performed on regexp patterns. + + Since powwow 1.1.3, unescaping is performed also on the text coming + from substition and jit. This causes subtle incompatibilities with + previous versions in case $n contains any \ or \` . + I tried to avoid this incompatibility, but it is really complicated + to do since I want the text coming from substitution to be subject + to jit as well. So you (and me) will have to live with it :-( + ----------------------------------------------------------- + +ADVANCED TOPIC: SPECIAL COMMAND #PROMPT + + Automatic command execution triggered on prompts + #prompt [[<|=|>|%][+|-]label] [{pattern | (expression)}=[command]] + + WARNING: + this is quite a complicated topic too. You will only need to read this + paragraph if you want to mess with prompts in strange ways, as powwow + usually handles prompts correctly. + + Second warning: + #prompt works only on the main MUD connection. + + O.K, since you are still reading, let's get a bit technical about + powwow internals: + + (WARNING: this changed since powwow 1.1.7:) + + Unless you use #actions, powwow sends immediately to the screen + whatever text it receives from the MUD. It sends to screen both + newline-ended lines (we'll name these `full lines') + and lines not ended with a newline (`incomplete lines'). + Now, there are two problems: + 1) there's no way to know whether an incomplete line is actually + finished or there is a continuation we still have to receive. + 2) powwow cannot know if the line, or an initial part of it, + is a prompt. + + When powwow receives a line (either full or incomplete), + its beginning part may be a prompt, so it matches #prompts on the line. + If the beginning part is _actually_ a prompt, #prompt should + execute #isprompt on it, to let powwow recognize the prompt as such. + + To be exact #isprompt must also specify how long the initial prompt is, + so that powwow can split it from the rest of the line. + For this reason, #isprompt is invoked with a numerical argument: + #isprompt + or + #isprompt (expression) + + a)If the number (or the result of the expression) is positive + and equals to (n), #isprompt declares that the initial prompt + is (n) characters long. + b)If the number is negative and equals to (-n), #isprompt declares + that the initial prompt is the same length as the parameter $n. + c)If the number is 0 (or is missing), #isprompt declares + the whole line as a prompt. + + Also, if a #prompt does not run #isprompt, it is interpreted as + 'this text is not a prompt' + + Putting #isprompt in a #prompt inhibits further attempts to match + that part of the line against both #prompts and #actions + (so should be used only on a complete prompt, not on a part of it) + + NOTE: Since a prompt may be followed by other text, when using + regexp patterns in #prompt it is important not to end the pattern + with $ (which matches the 'end of line') + + Examples: + + On MUME the prompt starts with either `o' or `*' and finishes with `>' + So the regexp pattern ^[o\*].*> will match all prompts and nothing else + To do the same using normal patterns, one should use two patterns + (and two #prompts): ^o&1> and ^*&1> + + On other MUDs of course the possible prompts will vary, so one must + find which pattern (or patterns) will match all the possible prompts. + If it also matches strings that are not prompts, care is required + _not_ to run #isprompt in such cases. + + Let's continue with the MUME example: using regexp patterns, + a correct #prompt is: + + #prompt %default ^[o\\*][^>]*>= + {#isprompt -1; #($prompt = "xyz " + attr "bold" + $prompt + noattr)} + + Note that the pattern contains _two_ backslashes instead of one, + as powwow unescapes regexp patterns. + Also, [^>]*> is used instead of .*> to stop matching at the _first_ `>' + (regexp by default would match the longest text possible, + stopping at the _last_ `>' in the line) + + The #prompt above correctly matches every MUME prompt, + runs #isprompt -1 on it + (which declares that the prompt is as long as $1 + since in regexp patterns $1 is the whole match, it is a good choice) + then modifies the prompt in a custom way + (puts it in bold then appends it to "xyz ") + + Of course #prompts may do whatever one wants, but with a limitation: + they must run #isprompt _before_ modifying the prompt, or unpredictable + things could happen. + + To have the same effect with normal patterns, the following + would be needed: + + #prompt >default1 ^o&1>= + {#isprompt (2+.?$(1)); #($prompt = "xyz " + attr "bold" + $prompt + noattr)} + + #prompt >default2 ^*&1>= + {#isprompt (2+.?$(1)); #($prompt = "xyz " + attr "bold" + $prompt + noattr)} + + The expression after #isprompt meanxs "2 plus the length of $1" + which is obviously the correct length, as $1 does not contain + `o' (or `*') and `>'. + + Final note: + If the prompt is longer than a whole line, it may be drawn incorrectly + and may interfere with the input line (yep, it's a bug). + + MUME players who happen to own a Valar+ character will find this + useful too: + #prompt >default3 ^+&1>={#isprompt (2+.?$(1))} + or, to use regexp patterns: + #prompt %default ^[o\\*\\+][^>]*>={#isprompt -1} + + ----------------------------------------------------------- diff --git a/doc/powwow.help b/doc/powwow.help new file mode 100644 index 0000000..82c7399 --- /dev/null +++ b/doc/powwow.help @@ -0,0 +1,590 @@ +@alias +#alias [name[=[command]]] + +Aliases are abbreviations for longer, frequently used commands. +As all powwow commands, they are only recognized at the beginning +of a line or directly after a semicolon, an open or a closed brace. +When an alias is used, the first word following the alias name +is placed in the variable $1, the second in $2, etc... up to $9. +also, the whole string following alias name is placed in $0. + +Then, before executing , every $n in is replaced by its +value. Examples: + +#alias summ=cast 'summon' $0 (then "summ thor" is replaced by + "cast 'summon' thor") +#alias cs=cast '$1' $2 ("cs summon thor" is expanded to + "cast 'summon' thor") +#alias summ (lets you edit the definition of summ) +#alias summ= (removes the alias summ) +#alias (displays all defined aliases) +#alias ws={wake;stand} (note that you must use braces) + +Aliases are saved automatically if you started powwow with a file name as +argument. Aliases are not expanded in verbatim mode. +@action +#action [[<|=|>|%][+|-]label] [{pattern | (expression)}=[command]] + +When 'pattern' is found in a line from the remote host, the 'command' is +automatically executed. If the pattern contains $n, powwow matches one word +of the remote line and puts it in the variable $n. Instead, if pattern contains +&n, powwow places the shortest possible text (can be more than one word, less, +or even one word) in the corresponding $n (NOT in &n). + +As in #alias, before executing , every $n in is replaced +by its value. If the first character of the pattern is ^ (caret), the match +will only be possible at the beginning of a line. +If 'label' is specified, the action is labeled; otherwise it is numbered. +The match is case-sensitive. Examples: + +#action >fount ^There is a fountain here={#print;drink water} +#action -fount (turns off action) +#action +fount (turns it back on) +#action =fount (lets you edit it) +#action , you define an action that uses an Extended POSIX +regexp to match instead of the standard matcher. + +#action %first ^([[:alpha:]]+) ([[:digit:]]+)=#print $2 counted $3. + (matches abc 123) + +Note that if the pattern starts with '(', it is evaluated, which means that +a regexp that starts with '(' has to be surrounded by ("...") + +#action %second ("(..+)-\\1")=#print Double $1 + (matches xyz-xyz) +For regexp actions, $0 = the line, $1 = the whole match and $2... contain the +submatches. +@bind +#bind [edit|key[=[command]]] + +You can bind most function keys and control keys to enter a command for you +when the key is pressed. You cannot (yet) redefine a key already used for an +editing function (such as the arrow keys). 'key' is the name of the key you +want to define; When defining a new key, you will be asked to press it so +powwow can record the control sequence your terminal sends. Examples: + +#bind (lists all key bindings) +#bind f1=recite scroll of recall (you'll have to press f1 then) +#bind f1=cast 'sanctuary' (change exiting definition) +#bind f1 (lets you edit the definition) +#bind f1= (removes the key) +#bind edit (lists editing keys) + +By default, the vt100 numeric keypad is partially used to walk around +@key +#key name + +If 'name' is the name of one of the key defined with #bind, #key executes the +corresponding command. Example: + +#bind f1=flee (binds the f1 key to the 'flee' command) +At any time, then, you can either: +- Press your f1 key on you keyboard +- Execute the command '#key f1' +and powwow will execute the command 'flee' for you. +@reset +#reset {all|list-name} + +Argument: Effect: +all clear everything (apply all lines below this) +alias clear all aliases +action clear all actions +bind clear all key bindings and restart with default + settings. Note that also editing keys are resetted + to default function. +at clear all delayed commands +in (same thing) +mark clear all markers +var clear all variables +@mark +#mark [text[=[attribute]]] + +This command highlights a text in your output in the way you choose +(if your terminal supports it). Attributes: +one or more of bold, blink, inverse, underline and/or +[] [on ], where the colors are: +black, red, green, yellow, blue, magenta, cyan, white and +BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, none. +Wildcards are allowed ($ for a single word, & for any string). Examples: + +#mark Sizzler=bold (mark Sizzler in bold) +#mark Sizzler (lets you edit the above definition) +#mark Sizzler= (Sizzler is no longer put in bold) +#mark (lists all markers) +#mark *&*=inverse (mark in reverse any string in * *) +@module +#module [module name] + +This loads a shared library module by name, if supported by your system. The +name of the module should be included in the documentation that came with the +powwow extension, for example to load the perl module you would use: + +#module perl + +Which gives you the perl command that can be used like: + +#perl powwow::exec( "say it is " . scalar(localtime()) ) + +The commands added and their syntax varies depending on the module. + +@history +#history [number] + +#history alone shows you the last commands in history, up to the number +of lines in your screen. +#history -n shows the last n commands in history, and +#history n executes the n-th command of the history. + +#history commands are not placed in history. +@hilite +#hilite [attribute] + +This sets the attribute of your entered text to the given attribute. +Just #hilite turns it off. + +Attributes: one or more of bold, blink, inverse, underline and/or +[] [on ], where the colors are: +black, red, green, yellow, blue, magenta, cyan, white and +BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, none. +@host +#host [hostname port] + +Sets the default host that powwow shall try to connect to when you use +#connect. +If you type #host without any arguments, the current host and port are +shown (provided that you have specified them). +@color +#color [attrib] + +(This command exists only if BUG_TELNET is defined, to cope with + deficiencies of NCSA telnet 2.2) +Set your standard foreground and background to the colours you specify. +#color sets default colors +@connect +#connect [session-id [initstr] [address port]] + +Connect a new session. + +To connect your first session, use: +#connect main
+where
and indicate the host you want to connect to. + +If no address/port is defined, you either get connected to the default +host and port (if you have no open connection) or a list of open +connections is shown. + +Last opened session will be the default one. + +If is specified, it is sent to host on connection. +@keys +Default editing keys: + +^A &begin-of-line +^E &end-of-line +^B &prev-char M-b &prev-word +^F &next-char M-f &next-word +^P &prev-line +^N &next-line +^D &del-char-right M-d &del-word-right +BS &del-char-left M-BS &del-word-left +^K &kill-to-eol M-k &redraw-line-noprompt +^L &redraw-line M-l &downcase-word +^T &transpose M-t &transpose-words +^Q &clear-line + M-u &upcase-word +^W &to-history +^Z &suspend +Tab &complete-word M-Tab &complete-line +Ret &enter-line +LF &enter-line + +not assigned: &insert-string + +M- means press escape and then , or press meta/alt key at the same +time as . ^ means control. +If your terminal has arrow keys, they are set for default to move cursor and to +step around history. +@wrap +#wrap [on|off] + +Normally, powwow wraps words that would have been cut by the right +margin to the next line. This command lets you turn it off and on. +@compact +#compact [on|off] + +Normally, powwow does not touch the prompts on screen while you play. +In #compact mode, instead, lines containing only a prompt are deleted when +further messages arrive from the remote host. +@echo +#echo [on|off] + +Normally, powwow echoes on your screen each command sent to remote +host but not directly typed (example: aliases and actions sending text +to the MUD). When #echo is off, such commands are still sent to host, +but not echoed on screen. +@info +#info [on|off] + +Normally, powwow prints on screen some messages each time you +define/edit/delete an #alias, #action, #bind and similar. +When #info is off, those messages are not typed at all. +(But errors are still printed on screen) +@debug +#debug [on|off] + +Normally, powwow does not print on screen the command lines it +executes. When #debug is on, every line executed by powwow is also +echoed on screen (warning: this prints LOTS of lines on your screen) +@delim +#delim [normal|program|{custom } + +By default, when powwow adds words to the word completion buffer, +it uses only SPACE (' ') and SEMI-COLON (';') as delimeters. You +can change this behaviour by specifying "program" or "custom" mode. +The "program" mode uses ' <>!=(),.;"'{}[]+-/*%=' as separators, which +is suitable if you are doing some kind of on-line programming. +You can also make a customized setting with the "custom" mode (space +is always used as a delimeter). +This setting also affects &transpose-words (usually bound as M-t) +@for +#for ([init];check;[loop]) command + +Directly copied from C language, this command evaluates 'init' +(if specified), then repeats the following cycle: + 1) evaluate 'check', if result is 0 (false) stop repetition + 2) execute 'command' + 3) evaluate 'loop' (if specified) + 4) restart from 1) + +As with #while, #for performs the parameter substitution in 'command'. Example: +#for (@0=32; @0<=47; @0++) read @0 (read messages 32-47) +@at +@in +#at [label [(time-expression) [command]]] +or +#in [label [(delay in millisec.) [command]]] + +If you want to tell powwow to execute the command 'kill wolf' +2 seconds after you type it, use this command: +#in attack (2000) kill wolf + +If you do not specify the command, powwow assumes the label is already +defined, and changes its delay. +A delay less than zero means the delayed label is disabled. +A delay of zero deletes the delayed label. + +If you specify only a label, powwow lists it. +If you specify nothing, all delayed labels are listed. + +The #at command is almost the same as #in, but assumes the expression +in ( ) is a time. +For example (114520) means 11:45:20 and ("114520") is the same. + +After executing a delayed command, powwow does not delete it, but simply +disables it. +@stop +#stop + +Disables all delayed commands (not delete). +Useful if you start an infinite loop with a self-reactivating delayed command +@add +#add {text|(expression)} + +Add the text or result of expression (calculator is used to evaluate the +expression) to the word completion list. Example: + +#action >reply ^$1 tells you={#print;#add $1} + (from now on, you can use TAB to complete that name) +@addstatic +#addstatic {text|(expression)} + +Add the text or result of expression (calculator is used to evaluate the +expression) to the static word completion list. Example: + +#addstatic Tintin Milou (from now on you can always use Tab to complete + these two names) + +Note that the static list is not saved when you run #save, and its +words will never be overwritten as new words are added. + +It is best used from your #init command, for example: + +#init ={#identify;#exe | list + +This command lets you list or change various options of powwow. + + #option lists all current option settings + #option list long list of current options + #option =