1412 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			1412 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
| -- front
 | |
| 6.828 Shells Lecture
 | |
| 
 | |
| Hello.
 | |
| 
 | |
| -- intro
 | |
| Bourne shell
 | |
| 
 | |
| Simplest shell: run cmd arg arg ... 
 | |
| 	fork
 | |
| 		exec in child
 | |
| 		wait in parent
 | |
| 
 | |
| More functionality:
 | |
| 	file redirection: cmd >file
 | |
| 		open file as fd 1 in child before exec
 | |
| 
 | |
| Still more functionality:
 | |
| 	pipes: cmd | cmd | cmd ...
 | |
| 		create pipe,
 | |
| 		run first cmd with pipe on fd 1,
 | |
| 		run second cmd with other end of pipe on fd 0
 | |
| 
 | |
| More Bourne arcana:
 | |
| 	$* - command args
 | |
| 	"$@" - unexpanded command args
 | |
| 	environment variables
 | |
| 	macro substitution
 | |
| 	if, while, for 
 | |
| 	|| 
 | |
| 	&& 
 | |
| 	"foo $x"
 | |
| 	'foo $x'
 | |
| 	`cat foo`
 | |
| 
 | |
| -- rc
 | |
| Rc Shell
 | |
| 
 | |
| 
 | |
| No reparsing of input (except explicit eval).
 | |
| 
 | |
| Variables as explicit lists.
 | |
| 
 | |
| Explicit concatenation.
 | |
| 
 | |
| Multiple input pipes <{cmd} - pass /dev/fd/4 as file name.
 | |
| 
 | |
| Syntax more like C, less like Algol.
 | |
| 
 | |
| diff <{echo hi} <{echo bye}
 | |
| 
 | |
| -- es
 | |
| Es shell
 | |
| 
 | |
| 
 | |
| rc++
 | |
| 
 | |
| Goal is to override functionality cleanly.
 | |
| 
 | |
| Rewrite input like cmd | cmd2 as %pipe {cmd} {cmd2}.
 | |
| 
 | |
| Users can redefine %pipe, etc.
 | |
| 
 | |
| Need lexical scoping and let to allow new %pipe refer to old %pipe.
 | |
| 
 | |
| Need garbage collection to collect unreachable code.
 | |
| 
 | |
| Design principle: 
 | |
| 	minimal functionality + good defaults
 | |
| 	allow users to customize implementations
 | |
| 	
 | |
| 	emacs, exokernel
 | |
| 
 | |
| -- apps
 | |
| Applications
 | |
| 
 | |
| Shell scripts are only as good as the programs they use.
 | |
| 	(What good are pipes without cat, grep, sort, wc, etc.?)
 | |
| 
 | |
| The more the scripts can access, the more powerful they become.
 | |
| 
 | |
| -- acme
 | |
| Acme, Plan 9 text editor
 | |
| 
 | |
| Make window system control files available to
 | |
| everything, including shell.
 | |
| 
 | |
| Can write shell scripts to script interactions.
 | |
| 
 | |
| /home/rsc/bin/Slide
 | |
| /home/rsc/bin/Slide-
 | |
| /home/rsc/bin/Slide+
 | |
| 
 | |
| /usr/local/plan9/bin/adict
 | |
| 
 | |
| win
 | |
| 
 | |
| -- javascript
 | |
| JavaScript
 | |
| 
 | |
| Very powerful
 | |
| 	- not because it's a great language
 | |
| 	- because it has a great data set
 | |
| 	- Google Maps
 | |
| 	- Gmail
 | |
| 	- Ymail
 | |
| 	- etc.
 | |
| 
 | |
| -- greasemonkey
 | |
| GreaseMonkey
 | |
| 
 | |
| // ==UserScript==
 | |
| // @name            Google Ring
 | |
| // @namespace       http://swtch.com/greasemonkey/
 | |
| // @description     Changes Google Logo
 | |
| // @include         http://*.google.*/*
 | |
| // ==/UserScript==
 | |
| 
 | |
| (function() {
 | |
| 	for(var i=0; i<document.images.length; i++){
 | |
| 		if(document.images[i].src == "http://www.google.com/intl/en/images/logo.gif")
 | |
| 			document.images[i].src = "http://swtch.com/googlering.png";
 | |
| 	}
 | |
| })();
 | |
| 
 | |
| -- webscript0
 | |
| Webscript
 | |
| 
 | |
| Why can't I script my web interactions?
 | |
| 
 | |
| /home/rsc/plan9/bin/rc/fedex
 | |
| 
 | |
| webscript /home/rsc/src/webscript/a3
 | |
| 	/home/rsc/src/webscript/a2
 | |
| 
 | |
| -- acid
 | |
| Acid, a programmable (scriptable) debugger
 | |
| 
 | |
| defn stopped(pid)
 | |
| {
 | |
| 	pfixstop(pid);
 | |
| 	pstop(pid);
 | |
| }
 | |
| 
 | |
| defn pfixstop(pid)
 | |
| {
 | |
| 	if *fmt(*PC-1, 'b') == 0xCC then {
 | |
| 		// Linux stops us after the breakpoint, not at it
 | |
| 		*PC = *PC-1;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /usr/local/plan9/acid/port:/^defn.bpset
 | |
| /usr/local/plan9/acid/port:/^defn.step
 | |
| 
 | |
| defn checkpdb(pdb) 
 | |
| {
 | |
| 	loop 1,768 do { 
 | |
| 		if *pdb != 0 then { print(pdb\X, " ", *pdb\X, "\n"); }
 | |
| 		pdb = pdb +4;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| -- guis
 | |
| GUIs
 | |
| 
 | |
| Can we script guis?  Not as clear.
 | |
| 
 | |
| Acme examples show one way: 
 | |
| 	turn events into file (pipe) to read.
 | |
| 
 | |
| Tcl/tk is close too.
 | |
| 
 | |
| Eventually everyone turns to C.
 | |
| 
 | |
| -- others
 | |
| Honorable Mentions
 | |
| 
 | |
| Scheme
 | |
| 
 | |
| Lisp
 | |
| 
 | |
| AutoCAD
 | |
| 
 | |
| Viaweb RTML
 | |
| 
 | |
| -- c
 | |
| "Real" programming languages vs. Scripts
 | |
| 
 | |
| Why does everyone eventually rewrite scripts in C?
 | |
| 	(aka C++, C#, any compiled language)
 | |
| 
 | |
| What do you need C for now?
 | |
| 
 | |
| How could you make it accessible to a script language?
 | |
| 
 | |
| -- /home/rsc/bin/Slide
 | |
| #!/usr/local/plan9/bin/rc
 | |
| 
 | |
| echo name `{pwd}^/$1 | 9p write acme/$winid/ctl
 | |
| echo clean | 9p write acme/$winid/ctl
 | |
| echo get | 9p write acme/$winid/ctl
 | |
| 
 | |
| -- /home/rsc/bin/Slide-
 | |
| #!/usr/local/plan9/bin/rc
 | |
| 
 | |
| name=$%
 | |
| current=`{basename $name}
 | |
| currentx=`{9 grep -n '^'$current'([ 	]|$)' index | sed 's/:.*//'}
 | |
| 
 | |
| pagex=`{echo $currentx - 1 | hoc}
 | |
| if(~ $pagex 0){
 | |
| 	echo no such page
 | |
| 	exit 0
 | |
| }
 | |
| page=`{sed -n $pagex^p index | awk '{print $1}'}
 | |
| if(~ $#page 0){
 | |
| 	echo no such page
 | |
| 	exit 0
 | |
| }
 | |
| 
 | |
| Slide $page
 | |
| -- /home/rsc/bin/Slide+
 | |
| #!/usr/local/plan9/bin/rc
 | |
| 
 | |
| name=$%
 | |
| current=`{basename $name}
 | |
| currentx=`{9 grep -n '^'$current'([ 	]|$)' index | sed 's/:.*//'}
 | |
| 
 | |
| pagex=`{echo $currentx + 1 | hoc}
 | |
| page=`{sed -n $pagex^p index | awk '{print $1}'}
 | |
| if(~ $#page 0){
 | |
| 	echo no such page
 | |
| 	exit 0
 | |
| }
 | |
| 
 | |
| Slide $page
 | |
| -- /usr/local/plan9/bin/adict
 | |
| #!/usr/local/plan9/bin/rc
 | |
| 
 | |
| . 9.rc
 | |
| . $PLAN9/lib/acme.rc
 | |
| 
 | |
| fn event {
 | |
| 	# $1 - c1 origin of event
 | |
| 	# $2 - c2 type of action
 | |
| 	# $3 - q0 beginning of selection
 | |
| 	# $4 - q1 end of selection
 | |
| 	# $5 - eq0 beginning of expanded selection
 | |
| 	# $6 - eq1 end of expanded selection
 | |
| 	# $7 - flag
 | |
| 	# $8 - nr number of runes in $7
 | |
| 	# $9 - text
 | |
| 	# $10 - chorded argument
 | |
| 	# $11 - origin of chorded argument
 | |
| 
 | |
| 	switch($1$2){
 | |
| 	case E*	# write to body or tag
 | |
| 	case F*	# generated by ourselves; ignore
 | |
| 	case K*	# type away we do not care
 | |
| 	case Mi	# mouse: text inserted in tag
 | |
| 	case MI	# mouse: text inserted in body
 | |
| 	case Md	# mouse: text deleted from tag
 | |
| 	case MD	# mouse: text deleted from body
 | |
| 
 | |
| 	case Mx MX	# button 2 in tag or body
 | |
| 		winwriteevent $*
 | |
| 
 | |
| 	case Ml ML	# button 3 in tag or body
 | |
| 		{
 | |
| 			if(~ $dict NONE)
 | |
| 				dictwin /adict/$9/ $9
 | |
| 			if not
 | |
| 				dictwin /adict/$dict/$9 $dict $9
 | |
| 		} &
 | |
| 	}
 | |
| }
 | |
| 
 | |
| fn dictwin {
 | |
| 	newwindow
 | |
| 	winname $1
 | |
| 	switch($#*){
 | |
| 	case 1
 | |
| 		dict -d '?' >[2=1] | sed 1d | winwrite body
 | |
| 	case 2
 | |
| 		dict=$2
 | |
| 	case 3
 | |
| 		dict=$2
 | |
| 		dict -d $dict $3 >[2=1] | winwrite body
 | |
| 	}
 | |
| 	winctl clean
 | |
| 	wineventloop
 | |
| }
 | |
| 
 | |
| dict=NONE
 | |
| if(~ $1 -d){
 | |
| 	shift
 | |
| 	dict=$2
 | |
| 	shift
 | |
| }
 | |
| if(~ $1 -d*){
 | |
| 	dict=`{echo $1 | sed 's/-d//'}
 | |
| 	shift
 | |
| }
 | |
| if(~ $1 -*){
 | |
| 	echo 'usage: adict [-d dict] [word...]' >[1=2]
 | |
| 	exit usage
 | |
| }
 | |
| 
 | |
| switch($#*){
 | |
| case 0
 | |
| 	if(~ $dict NONE)
 | |
| 		dictwin /adict/
 | |
| 	if not
 | |
| 		dictwin /adict/$dict/ $dict
 | |
| case *
 | |
| 	if(~ $dict NONE){
 | |
| 		dict=`{dict -d'?' | 9 sed -n 's/^   ([^\[ 	]+).*/\1/p' | sed 1q}
 | |
| 		if(~ $#dict 0){
 | |
| 			echo 'no dictionaries present on this system' >[1=2]
 | |
| 			exit nodict
 | |
| 		}
 | |
| 	}
 | |
| 	for(i)
 | |
| 		dictwin /adict/$dict/$i $dict $i
 | |
| }
 | |
| 
 | |
| -- /usr/local/plan9/lib/acme.rc
 | |
| fn newwindow {
 | |
| 	winctl=`{9p read acme/new/ctl}
 | |
| 	winid=$winctl(1)
 | |
| 	winctl noscroll
 | |
| }
 | |
| 
 | |
| fn winctl {	
 | |
| 	echo $* | 9p write acme/acme/$winid/ctl
 | |
| }
 | |
| 
 | |
| fn winread {
 | |
| 	9p read acme/acme/$winid/$1
 | |
| }
 | |
| 
 | |
| fn winwrite {
 | |
| 	9p write acme/acme/$winid/$1
 | |
| }
 | |
| 
 | |
| fn windump {
 | |
| 	if(! ~ $1 - '')
 | |
| 		winctl dumpdir $1
 | |
| 	if(! ~ $2 - '')
 | |
| 		winctl dump $2
 | |
| }
 | |
| 
 | |
| fn winname {
 | |
| 	winctl name $1
 | |
| }
 | |
| 
 | |
| fn winwriteevent {
 | |
| 	echo $1$2$3 $4 | winwrite event
 | |
| }
 | |
| 
 | |
| fn windel {
 | |
| 	if(~ $1 sure)
 | |
| 		winctl delete
 | |
| 	if not
 | |
| 		winctl del
 | |
| }
 | |
| 
 | |
| fn wineventloop {
 | |
| 	. <{winread event >[2]/dev/null | acmeevent}
 | |
| }
 | |
| -- /home/rsc/plan9/rc/bin/fedex
 | |
| #!/bin/rc
 | |
| 
 | |
| if(! ~ $#* 1) {
 | |
| 	echo usage: fedex 123456789012 >[1=2]
 | |
| 	exit usage
 | |
| }
 | |
| 
 | |
| rfork e
 | |
| 
 | |
| fn bgrep{
 | |
| pattern=`{echo $1 | sed 's;/;\\&;'}
 | |
| shift
 | |
| 
 | |
| @{ echo 'X {
 | |
| $
 | |
| a
 | |
| 
 | |
| .
 | |
| }
 | |
| X ,x/(.+\n)+\n/ g/'$pattern'/p' |
 | |
| sam -d $* >[2]/dev/null
 | |
| }
 | |
| }
 | |
| 
 | |
| fn awk2 {
 | |
| 	awk 'NR%2==1 { a=$0; } 
 | |
| 		NR%2==0 { b=$0; printf("%-30s %s\n", a, b); }
 | |
| 	' $*
 | |
| }
 | |
| 
 | |
| fn awk3 {
 | |
| 	awk '{line[NR] = $0}
 | |
| 	END{
 | |
| 		i = 4;
 | |
| 		while(i < NR){
 | |
| 			what=line[i++];
 | |
| 			when=line[i];
 | |
| 			comment="";
 | |
| 			if(!(when ~ /..\/..\/.... ..:../)){
 | |
| 				# out of sync
 | |
| 				printf("%s\n", what);
 | |
| 				continue;
 | |
| 			}
 | |
| 			i++;
 | |
| 			if(!(line[i+1] ~ /..\/..\/.... ..:../) &&
 | |
| 				(i+2 > NR || line[i+2] ~ /..\/..\/.... ..:../)){
 | |
| 				what = what ", " line[i++];
 | |
| 			}
 | |
| 			printf("%s  %s\n", when, what);
 | |
| 		}
 | |
| 	}' $*
 | |
| }
 | |
| 
 | |
| # hget 'http://www.fedex.com/cgi-bin/track_it?airbill_list='$1'&kurrent_airbill='$1'&language=english&cntry_code=us&state=0' |
 | |
| hget 'http://www.fedex.com/cgi-bin/tracking?action=track&language=english&cntry_code=us&initial=x&mps=y&tracknumbers='$1 |
 | |
| 	htmlfmt >/tmp/fedex.$pid
 | |
| sed -n '/Tracking number/,/^$/p' /tmp/fedex.$pid | awk2
 | |
| echo
 | |
| sed -n '/Reference number/,/^$/p' /tmp/fedex.$pid | awk2
 | |
| echo
 | |
| sed -n '/Date.time/,/^$/p' /tmp/fedex.$pid | sed 1,4d | fmt -l 4000 | sed 's/ [A-Z][A-Z] /&\n/g'
 | |
| rm /tmp/fedex.$pid
 | |
| -- /home/rsc/src/webscript/a3
 | |
| #!./o.webscript
 | |
| 
 | |
| load "http://www.ups.com/WebTracking/track?loc=en_US"
 | |
| find textbox "InquiryNumber1"
 | |
| input "1z30557w0340175623"
 | |
| find next checkbox
 | |
| input "yes"
 | |
| find prev form
 | |
| submit
 | |
| if(find "Delivery Information"){
 | |
| 	find outer table
 | |
| 	print
 | |
| }else if(find "One or more"){
 | |
| 	print
 | |
| }else{
 | |
| 	print "Unexpected results."
 | |
| 	find page
 | |
| 	print
 | |
| }
 | |
| -- /home/rsc/src/webscript/a2
 | |
| #load "http://apc-reset/outlets.htm"
 | |
| load "apc.html"
 | |
| print
 | |
| print "\n=============\n"
 | |
| find "yoshimi"
 | |
| find outer row
 | |
| find next select
 | |
| input "Immediate Reboot"
 | |
| submit
 | |
| print
 | |
| -- /usr/local/plan9/acid/port
 | |
| // portable acid for all architectures
 | |
| 
 | |
| defn pfl(addr)
 | |
| {
 | |
| 	print(pcfile(addr), ":", pcline(addr), "\n");
 | |
| }
 | |
| 
 | |
| defn
 | |
| notestk(addr)
 | |
| {
 | |
| 	local pc, sp;
 | |
| 	complex Ureg addr;
 | |
| 
 | |
| 	pc = addr.pc\X;
 | |
| 	sp = addr.sp\X;
 | |
| 
 | |
| 	print("Note pc:", pc, " sp:", sp, " ", fmt(pc, 'a'), " ");
 | |
| 	pfl(pc);
 | |
| 	_stk({"PC", pc, "SP", sp, linkreg(addr)}, 1);
 | |
| }
 | |
| 
 | |
| defn
 | |
| notelstk(addr)
 | |
| {
 | |
| 	local pc, sp;
 | |
| 	complex Ureg addr;
 | |
| 
 | |
| 	pc = addr.pc\X;
 | |
| 	sp = addr.sp\X;
 | |
| 
 | |
| 	print("Note pc:", pc, " sp:", sp, " ", fmt(pc, 'a'), " ");
 | |
| 	pfl(pc);
 | |
| 	_stk({"PC", pc, "SP", sp, linkreg(addr)}, 1);
 | |
| }
 | |
| 
 | |
| defn params(param)
 | |
| {
 | |
| 	while param do {
 | |
| 		sym = head param;
 | |
| 		print(sym[0], "=", itoa(sym[1], "%#ux"));
 | |
| 		param = tail param;
 | |
| 		if param then
 | |
| 			print (",");
 | |
| 	}	
 | |
| }
 | |
| 
 | |
| stkprefix = "";
 | |
| stkignore = {};
 | |
| stkend = 0;
 | |
| 
 | |
| defn locals(l)
 | |
| {
 | |
| 	local sym;
 | |
| 
 | |
| 	while l do {
 | |
| 		sym = head l;
 | |
| 		print(stkprefix, "\t", sym[0], "=", itoa(sym[1], "%#ux"), "\n");
 | |
| 		l = tail l;
 | |
| 	}	
 | |
| }
 | |
| 
 | |
| defn _stkign(frame)
 | |
| {
 | |
| 	local file;
 | |
| 
 | |
| 	file = pcfile(frame[0]);
 | |
| 	s = stkignore;
 | |
| 	while s do {
 | |
| 		if regexp(head s, file) then
 | |
| 			return 1;
 | |
| 		s = tail s;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| // print a stack trace
 | |
| //
 | |
| // in a run of leading frames in files matched by regexps in stkignore,
 | |
| // only print the last one.
 | |
| defn _stk(regs, dolocals)
 | |
| {
 | |
| 	local stk, frame, pc, fn, done, callerpc, paramlist, locallist;
 | |
| 
 | |
| 	stk = strace(regs);
 | |
| 	if stkignore then {
 | |
| 		while stk && tail stk && _stkign(head tail stk) do
 | |
| 			stk = tail stk;
 | |
| 	}
 | |
| 
 | |
| 	callerpc = 0;
 | |
| 	done = 0;
 | |
| 	while stk && !done do {
 | |
| 		frame = head stk;
 | |
| 		stk = tail stk;
 | |
| 		fn = frame[0];
 | |
| 		pc = frame[1];
 | |
| 		callerpc = frame[2];
 | |
| 		paramlist = frame[3];
 | |
| 		locallist = frame[4];
 | |
| 
 | |
| 		print(stkprefix, fmt(fn, 'a'), "(");
 | |
| 		params(paramlist);
 | |
| 		print(")");
 | |
| 		if pc != fn then
 | |
| 			print("+", itoa(pc-fn, "%#ux"));
 | |
| 		print(" ");
 | |
| 		pfl(pc);
 | |
| 		if dolocals then
 | |
| 			locals(locallist);
 | |
| 		if fn == var("threadmain") || fn == var("p9main") then
 | |
| 			done=1;
 | |
| 		if fn == var("threadstart") || fn == var("scheduler") then
 | |
| 			done=1;
 | |
| 		if callerpc == 0 then
 | |
| 			done=1;
 | |
| 	}
 | |
| 	if callerpc && !done then {
 | |
| 		print(stkprefix, fmt(callerpc, 'a'), " ");
 | |
| 		pfl(callerpc);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| defn findsrc(file)
 | |
| {
 | |
| 	local lst, src;
 | |
| 
 | |
| 	if file[0] == '/' then {
 | |
| 		src = file(file);
 | |
| 		if src != {} then {
 | |
| 			srcfiles = append srcfiles, file;
 | |
| 			srctext = append srctext, src;
 | |
| 			return src;
 | |
| 		}
 | |
| 		return {};
 | |
| 	}
 | |
| 
 | |
| 	lst = srcpath;
 | |
| 	while head lst do {
 | |
| 		src = file(head lst+file);
 | |
| 		if src != {} then {
 | |
| 			srcfiles = append srcfiles, file;
 | |
| 			srctext = append srctext, src;
 | |
| 			return src;
 | |
| 		}
 | |
| 		lst = tail lst;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| defn line(addr)
 | |
| {
 | |
| 	local src, file;
 | |
| 
 | |
| 	file = pcfile(addr);
 | |
| 	src = match(file, srcfiles);
 | |
| 
 | |
| 	if src >= 0 then
 | |
| 		src = srctext[src];
 | |
| 	else
 | |
| 		src = findsrc(file);
 | |
| 
 | |
| 	if src == {} then {
 | |
| 		print("no source for ", file, "\n");
 | |
| 		return {};
 | |
| 	}
 | |
| 	line = pcline(addr)-1;
 | |
| 	print(file, ":", src[line], "\n");
 | |
| }
 | |
| 
 | |
| defn addsrcdir(dir)
 | |
| {
 | |
| 	dir = dir+"/";
 | |
| 
 | |
| 	if match(dir, srcpath) >= 0 then {
 | |
| 		print("already in srcpath\n");
 | |
| 		return {};
 | |
| 	}
 | |
| 
 | |
| 	srcpath = {dir}+srcpath;
 | |
| }
 | |
| 
 | |
| defn source()
 | |
| {
 | |
| 	local l;
 | |
| 
 | |
| 	l = srcpath;
 | |
| 	while l do {
 | |
| 		print(head l, "\n");
 | |
| 		l = tail l;
 | |
| 	}
 | |
| 	l = srcfiles;
 | |
| 
 | |
| 	while l do {
 | |
| 		print("\t", head l, "\n");
 | |
| 		l = tail l;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| defn Bsrc(addr)
 | |
| {
 | |
| 	local lst;
 | |
| 
 | |
| 	lst = srcpath;
 | |
| 	file = pcfile(addr);
 | |
| 	if file[0] == '/' && access(file) then {
 | |
| 		rc("B "+file+":"+itoa(pcline(addr)));
 | |
| 		return {};
 | |
| 	}
 | |
| 	while head lst do {
 | |
| 		name = head lst+file;
 | |
| 		if access(name) then {
 | |
| 			rc("B "+name+":"+itoa(pcline(addr)));
 | |
| 			return {};
 | |
| 		}
 | |
| 		lst = tail lst;
 | |
| 	}
 | |
| 	print("no source for ", file, "\n");
 | |
| }
 | |
| 
 | |
| defn srcline(addr)
 | |
| {
 | |
| 	local text, cline, line, file, src;
 | |
| 	file = pcfile(addr);
 | |
| 	src = match(file,srcfiles);
 | |
| 	if (src>=0) then
 | |
| 		src = srctext[src];
 | |
| 	else
 | |
| 		src = findsrc(file);
 | |
| 	if (src=={}) then
 | |
| 	{
 | |
| 		return "(no source)";
 | |
| 	}
 | |
| 	return src[pcline(addr)-1];
 | |
| }
 | |
| 
 | |
| defn src(addr)
 | |
| {
 | |
| 	local src, file, line, cline, text;
 | |
| 
 | |
| 	file = pcfile(addr);
 | |
| 	src = match(file, srcfiles);
 | |
| 
 | |
| 	if src >= 0 then
 | |
| 		src = srctext[src];
 | |
| 	else
 | |
| 		src = findsrc(file);
 | |
| 
 | |
| 	if src == {} then {
 | |
| 		print("no source for ", file, "\n");
 | |
| 		return {};
 | |
| 	}
 | |
| 
 | |
| 	cline = pcline(addr)-1;
 | |
| 	print(file, ":", cline+1, "\n");
 | |
| 	line = cline-5;
 | |
| 	loop 0,10 do {
 | |
| 		if line >= 0 then {
 | |
| 			if line == cline then
 | |
| 				print(">");
 | |
| 			else
 | |
| 				print(" ");
 | |
| 			text = src[line];
 | |
| 			if text == {} then
 | |
| 				return {};
 | |
| 			print(line+1, "\t", text, "\n");
 | |
| 		}
 | |
| 		line = line+1;
 | |
| 	}	
 | |
| }
 | |
| 
 | |
| defn step()					// single step the process
 | |
| {
 | |
| 	local lst, lpl, addr, bput;
 | |
| 
 | |
| 	bput = 0;
 | |
| 	if match(*PC, bplist) >= 0 then {	// Sitting on a breakpoint
 | |
| 		bput = fmt(*PC, bpfmt);
 | |
| 		*bput = @bput;
 | |
| 	}
 | |
| 
 | |
| 	lst = follow(*PC);
 | |
| 
 | |
| 	lpl = lst;
 | |
| 	while lpl do {				// place break points
 | |
| 		*(head lpl) = bpinst;
 | |
| 		lpl = tail lpl;
 | |
| 	}
 | |
| 
 | |
| 	startstop(pid);				// do the step
 | |
| 
 | |
| 	while lst do {				// remove the breakpoints
 | |
| 		addr = fmt(head lst, bpfmt);
 | |
| 		*addr = @addr;
 | |
| 		lst = tail lst;
 | |
| 	}
 | |
| 	if bput != 0 then
 | |
| 		*bput = bpinst;
 | |
| }
 | |
| 
 | |
| defn bpset(addr)				// set a breakpoint
 | |
| {
 | |
| 	if status(pid) != "Stopped" then {
 | |
| 		print("Waiting...\n");
 | |
| 		stop(pid);
 | |
| 	}
 | |
| 	if match(addr, bplist) >= 0 then
 | |
| 		print("breakpoint already set at ", fmt(addr, 'a'), "\n");
 | |
| 	else {
 | |
| 		*fmt(addr, bpfmt) = bpinst;
 | |
| 		bplist = append bplist, addr;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| defn bptab()					// print a table of breakpoints
 | |
| {
 | |
| 	local lst, addr;
 | |
| 
 | |
| 	lst = bplist;
 | |
| 	while lst do {
 | |
| 		addr = head lst;
 | |
| 		print("\t", fmt(addr, 'X'), " ", fmt(addr, 'a'), "  ", fmt(addr, 'i'), "\n");
 | |
| 		lst = tail lst;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| defn bpdel(addr)				// delete a breakpoint
 | |
| {
 | |
| 	local n, pc, nbplist;
 | |
| 
 | |
| 	if addr == 0 then {
 | |
| 		while bplist do {
 | |
| 			pc = head bplist;
 | |
| 			pc = fmt(pc, bpfmt);
 | |
| 			*pc = @pc;
 | |
| 			bplist = tail bplist;
 | |
| 		}
 | |
| 		return {};
 | |
| 	}
 | |
| 
 | |
| 	n = match(addr, bplist);
 | |
| 	if n < 0  then {
 | |
| 		print("no breakpoint at ", fmt(addr, 'a'), "\n");
 | |
| 		return {};
 | |
| 	}
 | |
| 
 | |
| 	addr = fmt(addr, bpfmt);
 | |
| 	*addr = @addr;
 | |
| 
 | |
| 	nbplist = {};				// delete from list
 | |
| 	while bplist do {
 | |
| 		pc = head bplist;
 | |
| 		if pc != addr then
 | |
| 			nbplist = append nbplist, pc;
 | |
| 		bplist = tail bplist;
 | |
| 	}
 | |
| 	bplist = nbplist;			// delete from memory
 | |
| }
 | |
| 
 | |
| defn cont()					// continue execution
 | |
| {
 | |
| 	local addr;
 | |
| 
 | |
| 	addr = fmt(*PC, bpfmt);
 | |
| 	if match(addr, bplist) >= 0 then {	// Sitting on a breakpoint
 | |
| 		*addr = @addr;
 | |
| 		step();				// Step over
 | |
| 		*addr = bpinst;
 | |
| 	}
 | |
| 	startstop(pid);				// Run
 | |
| }
 | |
| 
 | |
| defn stopped(pid)		// called from acid when a process changes state
 | |
| {
 | |
| 	pfixstop(pid);
 | |
| 	pstop(pid);		// stub so this is easy to replace
 | |
| }
 | |
| 
 | |
| defn procs()			// print status of processes
 | |
| {
 | |
| 	local c, lst, cpid;
 | |
| 
 | |
| 	cpid = pid;
 | |
| 	lst = proclist;
 | |
| 	while lst do {
 | |
| 		np = head lst;
 | |
| 		setproc(np);
 | |
| 		if np == cpid then
 | |
| 			c = '>';
 | |
| 		else
 | |
| 			c = ' ';
 | |
| 		print(fmt(c, 'c'), np, ": ", status(np), " at ", fmt(*PC, 'a'), " setproc(", np, ")\n");
 | |
| 		lst = tail lst;
 | |
| 	}
 | |
| 	pid = cpid;
 | |
| 	if pid != 0 then
 | |
| 		setproc(pid);
 | |
| }
 | |
| 
 | |
| _asmlines = 30;
 | |
| 
 | |
| defn asm(addr)
 | |
| {
 | |
| 	local bound;
 | |
| 
 | |
| 	bound = fnbound(addr);
 | |
| 
 | |
| 	addr = fmt(addr, 'i');
 | |
| 	loop 1,_asmlines do {
 | |
| 		print(fmt(addr, 'a'), " ", fmt(addr, 'X'));
 | |
| 		print("\t", @addr++, "\n");
 | |
| 		if bound != {} && addr > bound[1] then {
 | |
| 			lasmaddr = addr;
 | |
| 			return {};
 | |
| 		}
 | |
| 	}
 | |
| 	lasmaddr = addr;
 | |
| }
 | |
| 
 | |
| defn casm()
 | |
| {
 | |
| 	asm(lasmaddr);
 | |
| }
 | |
| 
 | |
| defn xasm(addr)
 | |
| {
 | |
| 	local bound;
 | |
| 
 | |
| 	bound = fnbound(addr);
 | |
| 
 | |
| 	addr = fmt(addr, 'i');
 | |
| 	loop 1,_asmlines do {
 | |
| 		print(fmt(addr, 'a'), " ", fmt(addr, 'X'));
 | |
| 		print("\t", *addr++, "\n");
 | |
| 		if bound != {} && addr > bound[1] then {
 | |
| 			lasmaddr = addr;
 | |
| 			return {};
 | |
| 		}
 | |
| 	}
 | |
| 	lasmaddr = addr;
 | |
| }
 | |
| 
 | |
| defn xcasm()
 | |
| {
 | |
| 	xasm(lasmaddr);
 | |
| }
 | |
| 
 | |
| defn win()
 | |
| {
 | |
| 	local npid, estr;
 | |
| 
 | |
| 	bplist = {};
 | |
| 	notes = {};
 | |
| 
 | |
| 	estr = "/sys/lib/acid/window '0 0 600 400' "+textfile;
 | |
| 	if progargs != "" then
 | |
| 		estr = estr+" "+progargs;
 | |
| 
 | |
| 	npid = rc(estr);
 | |
| 	npid = atoi(npid);
 | |
| 	if npid == 0 then
 | |
| 		error("win failed to create process");
 | |
| 
 | |
| 	setproc(npid);
 | |
| 	stopped(npid);
 | |
| }
 | |
| 
 | |
| defn win2()
 | |
| {
 | |
| 	local npid, estr;
 | |
| 
 | |
| 	bplist = {};
 | |
| 	notes = {};
 | |
| 
 | |
| 	estr = "/sys/lib/acid/transcript '0 0 600 400' '100 100 700 500' "+textfile;
 | |
| 	if progargs != "" then
 | |
| 		estr = estr+" "+progargs;
 | |
| 
 | |
| 	npid = rc(estr);
 | |
| 	npid = atoi(npid);
 | |
| 	if npid == 0 then
 | |
| 		error("win failed to create process");
 | |
| 
 | |
| 	setproc(npid);
 | |
| 	stopped(npid);
 | |
| }
 | |
| 
 | |
| printstopped = 1;
 | |
| defn new()
 | |
| {
 | |
| 	local a;
 | |
| 	
 | |
| 	bplist = {};
 | |
| 	newproc(progargs);
 | |
| 	a = var("p9main");
 | |
| 	if a == {} then
 | |
| 		a = var("main");
 | |
| 	if a == {} then
 | |
| 		return {};
 | |
| 	bpset(a);
 | |
| 	while *PC != a do
 | |
| 		cont();
 | |
| 	bpdel(a);
 | |
| }
 | |
| 
 | |
| defn stmnt()			// step one statement
 | |
| {
 | |
| 	local line;
 | |
| 
 | |
| 	line = pcline(*PC);
 | |
| 	while 1 do {
 | |
| 		step();
 | |
| 		if line != pcline(*PC) then {
 | |
| 			src(*PC);
 | |
| 			return {};
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| defn func()			// step until we leave the current function
 | |
| {
 | |
| 	local bound, end, start, pc;
 | |
| 
 | |
| 	bound = fnbound(*PC);
 | |
| 	if bound == {} then {
 | |
| 		print("cannot locate text symbol\n");
 | |
| 		return {};
 | |
| 	}
 | |
| 
 | |
| 	pc = *PC;
 | |
| 	start = bound[0];
 | |
| 	end = bound[1];
 | |
| 	while pc >= start && pc < end do {
 | |
| 		step();
 | |
| 		pc = *PC;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| defn next()
 | |
| {
 | |
| 	local sp, bound, pc;
 | |
| 
 | |
| 	sp = *SP;
 | |
| 	bound = fnbound(*PC);
 | |
| 	if bound == {} then {
 | |
| 		print("cannot locate text symbol\n");
 | |
| 		return {};
 | |
| 	}
 | |
| 	stmnt();
 | |
| 	pc = *PC;
 | |
| 	if pc >= bound[0] && pc < bound[1] then
 | |
| 		return {};
 | |
| 
 | |
| 	while (pc < bound[0] || pc > bound[1]) && sp >= *SP do {
 | |
| 		step();
 | |
| 		pc = *PC;
 | |
| 	}
 | |
| 	src(*PC);
 | |
| }
 | |
| 
 | |
| defn maps()
 | |
| {
 | |
| 	local m, mm;
 | |
| 
 | |
| 	m = map();
 | |
| 	while m != {} do {
 | |
| 		mm = head m;
 | |
| 		m = tail m;
 | |
| 		print(mm[2]\X, " ", mm[3]\X, " ", mm[4]\X, " ", mm[0], " ", mm[1], "\n");
 | |
| 	}
 | |
| }
 | |
| 
 | |
| defn dump(addr, n, fmt)
 | |
| {
 | |
| 	loop 0, n do {
 | |
| 		print(fmt(addr, 'X'), ": ");
 | |
| 		addr = mem(addr, fmt);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| defn mem(addr, fmt)
 | |
| {
 | |
| 
 | |
| 	local i, c, n;
 | |
| 
 | |
| 	i = 0;
 | |
| 	while fmt[i] != 0 do {
 | |
| 		c = fmt[i];
 | |
| 		n = 0;
 | |
| 		while '0' <= fmt[i] && fmt[i] <= '9' do {
 | |
| 			n = 10*n + fmt[i]-'0';
 | |
| 			i = i+1;
 | |
| 		}
 | |
| 		if n <= 0 then n = 1;
 | |
| 		addr = fmt(addr, fmt[i]);
 | |
| 		while n > 0 do {
 | |
| 			print(*addr++, " ");
 | |
| 			n = n-1;
 | |
| 		}
 | |
| 		i = i+1;
 | |
| 	}
 | |
| 	print("\n");
 | |
| 	return addr;
 | |
| }
 | |
| 
 | |
| defn symbols(pattern)
 | |
| {
 | |
| 	local l, s;
 | |
| 
 | |
| 	l = symbols;
 | |
| 	while l do {
 | |
| 		s = head l;
 | |
| 		if regexp(pattern, s[0]) then
 | |
| 			print(s[0], "\t", s[1], "\t", s[2], "\t", s[3], "\n");
 | |
| 		l = tail l;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| defn havesymbol(name)
 | |
| {
 | |
| 	local l, s;
 | |
| 
 | |
| 	l = symbols;
 | |
| 	while l do {
 | |
| 		s = head l;
 | |
| 		l = tail l;
 | |
| 		if s[0] == name then
 | |
| 			return 1;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| defn spsrch(len)
 | |
| {
 | |
| 	local addr, a, s, e;
 | |
| 
 | |
| 	addr = *SP;
 | |
| 	s = origin & 0x7fffffff;
 | |
| 	e = etext & 0x7fffffff;
 | |
| 	loop 1, len do {
 | |
| 		a = *addr++;
 | |
| 		c = a & 0x7fffffff;
 | |
| 		if c > s && c < e then {
 | |
| 			print("src(", a, ")\n");
 | |
| 			pfl(a);
 | |
| 		}			
 | |
| 	}
 | |
| }
 | |
| 
 | |
| defn acidtypes()
 | |
| {
 | |
| 	local syms;
 | |
| 	local l;
 | |
| 
 | |
| 	l = textfile();
 | |
| 	if l != {} then {
 | |
| 		syms = "acidtypes";
 | |
| 		while l != {} do {
 | |
| 			syms = syms + " " + ((head l)[0]);
 | |
| 			l = tail l;
 | |
| 		}
 | |
| 		includepipe(syms);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| defn getregs()
 | |
| {
 | |
| 	local regs, l;
 | |
| 
 | |
| 	regs = {};
 | |
| 	l = registers;
 | |
| 	while l != {} do {
 | |
| 		regs = append regs, var(l[0]);
 | |
| 		l = tail l;
 | |
| 	}
 | |
| 	return regs;
 | |
| }
 | |
| 
 | |
| defn setregs(regs)
 | |
| {
 | |
| 	local l;
 | |
| 
 | |
| 	l = registers;
 | |
| 	while l != {} do {
 | |
| 		var(l[0]) = regs[0];
 | |
| 		l = tail l;
 | |
| 		regs = tail regs;
 | |
| 	}
 | |
| 	return regs;
 | |
| }
 | |
| 
 | |
| defn resetregs()
 | |
| {
 | |
| 	local l;
 | |
| 
 | |
| 	l = registers;
 | |
| 	while l != {} do {
 | |
| 		var(l[0]) = register(l[0]);
 | |
| 		l = tail l;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| defn clearregs()
 | |
| {
 | |
| 	local l;
 | |
| 
 | |
| 	l = registers;
 | |
| 	while l != {} do {
 | |
| 		var(l[0]) = refconst(~0);
 | |
| 		l = tail l;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| progargs="";
 | |
| print(acidfile);
 | |
| 
 | |
| -- /usr/local/plan9/acid/386
 | |
| // 386 support
 | |
| 
 | |
| defn acidinit()			// Called after all the init modules are loaded
 | |
| {
 | |
| 	bplist = {};
 | |
| 	bpfmt = 'b';
 | |
| 
 | |
| 	srcpath = {
 | |
| 		"./",
 | |
| 		"/sys/src/libc/port/",
 | |
| 		"/sys/src/libc/9sys/",
 | |
| 		"/sys/src/libc/386/"
 | |
| 	};
 | |
| 
 | |
| 	srcfiles = {};			// list of loaded files
 | |
| 	srctext = {};			// the text of the files
 | |
| }
 | |
| 
 | |
| defn linkreg(addr)
 | |
| {
 | |
| 	return {};
 | |
| }
 | |
| 
 | |
| defn stk()				// trace
 | |
| {
 | |
| 	_stk({"PC", *PC, "SP", *SP}, 0);
 | |
| }
 | |
| 
 | |
| defn lstk()				// trace with locals
 | |
| {
 | |
| 	_stk({"PC", *PC, "SP", *SP}, 1);
 | |
| }
 | |
| 
 | |
| defn gpr()		// print general(hah hah!) purpose registers
 | |
| {
 | |
| 	print("AX\t", *AX, " BX\t", *BX, " CX\t", *CX, " DX\t", *DX, "\n");
 | |
| 	print("DI\t", *DI, " SI\t", *SI, " BP\t", *BP, "\n");
 | |
| }
 | |
| 
 | |
| defn spr()				// print special processor registers
 | |
| {
 | |
| 	local pc;
 | |
| 	local cause;
 | |
| 
 | |
| 	pc = *PC;
 | |
| 	print("PC\t", pc, " ", fmt(pc, 'a'), "  ");
 | |
| 	pfl(pc);
 | |
| 	print("SP\t", *SP, " ECODE ", *ECODE, " EFLAG ", *EFLAGS, "\n");
 | |
| 	print("CS\t", *CS, " DS\t ", *DS, " SS\t", *SS, "\n");
 | |
| 	print("GS\t", *GS, " FS\t ", *FS, " ES\t", *ES, "\n");
 | |
| 	
 | |
| 	cause = *TRAP;
 | |
| 	print("TRAP\t", cause, " ", reason(cause), "\n");
 | |
| }
 | |
| 
 | |
| defn regs()				// print all registers
 | |
| {
 | |
| 	spr();
 | |
| 	gpr();
 | |
| }
 | |
| 
 | |
| defn mmregs()
 | |
| {
 | |
| 	print("MM0\t", *MM0, " MM1\t", *MM1, "\n");
 | |
| 	print("MM2\t", *MM2, " MM3\t", *MM3, "\n");
 | |
| 	print("MM4\t", *MM4, " MM5\t", *MM5, "\n");
 | |
| 	print("MM6\t", *MM6, " MM7\t", *MM7, "\n");
 | |
| }
 | |
| 
 | |
| defn pfixstop(pid)
 | |
| {
 | |
| 	if *fmt(*PC-1, 'b') == 0xCC then {
 | |
| 		// Linux stops us after the breakpoint, not at it
 | |
| 		*PC = *PC-1;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| defn pstop(pid)
 | |
| {
 | |
| 	local l;
 | |
| 	local pc;
 | |
| 	local why;
 | |
| 
 | |
| 	pc = *PC;
 | |
| 
 | |
| 	// FIgure out why we stopped.
 | |
| 	if *fmt(pc, 'b') == 0xCC then {
 | |
| 		why = "breakpoint";
 | |
| 		
 | |
| 		// fix up instruction for print; will put back later
 | |
| 		*pc = @pc;
 | |
| 	} else if *(pc-2\x) == 0x80CD then {
 | |
| 		pc = pc-2;
 | |
| 		why = "system call";
 | |
| 	} else
 | |
| 		why = "stopped";
 | |
| 
 | |
| 	if printstopped then {
 | |
| 		print(pid,": ", why, "\t");
 | |
| 		print(fmt(pc, 'a'), "\t", *fmt(pc, 'i'), "\n");
 | |
| 	}
 | |
| 	
 | |
| 	if why == "breakpoint" then
 | |
| 		*fmt(pc, bpfmt) = bpinst;
 | |
| 	
 | |
| 	if printstopped && notes then {
 | |
| 		if notes[0] != "sys: breakpoint" then {
 | |
| 			print("Notes pending:\n");
 | |
| 			l = notes;
 | |
| 			while l do {
 | |
| 				print("\t", head l, "\n");
 | |
| 				l = tail l;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| aggr Ureg
 | |
| {
 | |
| 	'U' 0 di;
 | |
| 	'U' 4 si;
 | |
| 	'U' 8 bp;
 | |
| 	'U' 12 nsp;
 | |
| 	'U' 16 bx;
 | |
| 	'U' 20 dx;
 | |
| 	'U' 24 cx;
 | |
| 	'U' 28 ax;
 | |
| 	'U' 32 gs;
 | |
| 	'U' 36 fs;
 | |
| 	'U' 40 es;
 | |
| 	'U' 44 ds;
 | |
| 	'U' 48 trap;
 | |
| 	'U' 52 ecode;
 | |
| 	'U' 56 pc;
 | |
| 	'U' 60 cs;
 | |
| 	'U' 64 flags;
 | |
| 	{
 | |
| 	'U' 68 usp;
 | |
| 	'U' 68 sp;
 | |
| 	};
 | |
| 	'U' 72 ss;
 | |
| };
 | |
| 
 | |
| defn
 | |
| Ureg(addr) {
 | |
| 	complex Ureg addr;
 | |
| 	print("	di	", addr.di, "\n");
 | |
| 	print("	si	", addr.si, "\n");
 | |
| 	print("	bp	", addr.bp, "\n");
 | |
| 	print("	nsp	", addr.nsp, "\n");
 | |
| 	print("	bx	", addr.bx, "\n");
 | |
| 	print("	dx	", addr.dx, "\n");
 | |
| 	print("	cx	", addr.cx, "\n");
 | |
| 	print("	ax	", addr.ax, "\n");
 | |
| 	print("	gs	", addr.gs, "\n");
 | |
| 	print("	fs	", addr.fs, "\n");
 | |
| 	print("	es	", addr.es, "\n");
 | |
| 	print("	ds	", addr.ds, "\n");
 | |
| 	print("	trap	", addr.trap, "\n");
 | |
| 	print("	ecode	", addr.ecode, "\n");
 | |
| 	print("	pc	", addr.pc, "\n");
 | |
| 	print("	cs	", addr.cs, "\n");
 | |
| 	print("	flags	", addr.flags, "\n");
 | |
| 	print("	sp	", addr.sp, "\n");
 | |
| 	print("	ss	", addr.ss, "\n");
 | |
| };
 | |
| sizeofUreg = 76;
 | |
| 
 | |
| aggr Linkdebug
 | |
| {
 | |
| 	'X' 0 version;
 | |
| 	'X' 4 map;
 | |
| };
 | |
| 
 | |
| aggr Linkmap
 | |
| {
 | |
| 	'X' 0 addr;
 | |
| 	'X' 4 name;
 | |
| 	'X' 8 dynsect;
 | |
| 	'X' 12 next;
 | |
| 	'X' 16 prev;
 | |
| };
 | |
| 
 | |
| defn
 | |
| linkdebug()
 | |
| {
 | |
| 	local a;
 | |
| 
 | |
| 	if !havesymbol("_DYNAMIC") then
 | |
| 		return 0;
 | |
| 	
 | |
| 	a = _DYNAMIC;
 | |
| 	while *a != 0 do {
 | |
| 		if *a == 21 then // 21 == DT_DEBUG
 | |
| 			return *(a+4);
 | |
| 		a = a+8;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| defn
 | |
| dynamicmap()
 | |
| {
 | |
| 	if systype == "linux"  || systype == "freebsd" then {
 | |
| 		local r, m, n;
 | |
| 	
 | |
| 		r = linkdebug();
 | |
| 		if r then {
 | |
| 			complex Linkdebug r;
 | |
| 			m = r.map;
 | |
| 			n = 0;
 | |
| 			while m != 0 && n < 100 do {
 | |
| 				complex Linkmap m;
 | |
| 				if m.name && *(m.name\b) && access(*(m.name\s)) then
 | |
| 					print("textfile({\"", *(m.name\s), "\", ", m.addr\X, "});\n");
 | |
| 				m = m.next;
 | |
| 				n = n+1;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| defn
 | |
| acidmap()
 | |
| {
 | |
| //	dynamicmap();
 | |
| 	acidtypes();
 | |
| }
 | |
| 
 | |
| print(acidfile);
 | 
