Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

postscript debugger. now with comments!

101 views
Skip to first unread message

luserXtrog

unread,
Sep 18, 2010, 5:32:40 AM9/18/10
to
Dedicated to all the postscript hackers!
Control all the lasers!

529(1)04:22 AM:ps 0> echo; echo; for i in db4.ps ds.ps; do echo %---
sof: $i -----; cat $i; echo %--- eof: $i -----; echo; echo; done;


%--- sof: db4.ps -----
%!
{ currentfile token pop /ENDINTRO eq { exit } if } loop

debug.ps - A postscript debugger


Switches
--------
stepon enable single-stepping
stepoff disable single-stepping
traceon enable tracing (echo each token before executing)
traceoff disable tracing

Procedures
----------
(filename) debug
Begin debugging file.

resume
Resume debugging after 'exit'ing from an error prompt.
or 'q'uiting from single-step.

object wherevalue dict key true
false
Perform reverse lookup for object in the dictionary stack.

dicttrace
Display the dictionary stack using wherevalue to obtain
dictionary names.

skip
Skip the current object.


Caveats
-------
You cannot change single-stepping directly from the error prompt.
But you may 'exit' and then 'stepoff resume'.


ENDINTRO

(ds.ps) run


%
% internal names
% will be undef'ed later
%

% print error message
/errmsg {
(Error /) print
$error /errorname get ( ) cvs print
( occurred attempting to execute ) print
} def

% the debugger state
/dbdict <<
/nexttoken null
/stack 250 array
/ptr -1
/trace false
/step false
>> def

% push a source (file or array or even string)
% on the debug stack (defined as the typical size of the
% exec stack from the red book).
/pushs { % proc|file pushs
//dbdict /ptr 2 copy get 1 add put
//dbdict /stack get
//dbdict /ptr get
3 -1 roll put
} def

% remove a source from the stack
% if it's a file, close it.
/pops {
//dbdict /stack get
//dbdict /ptr get
get dup type /filetype eq {
closefile
}{ pop } ifelse
//dbdict /ptr 2 copy get 1 sub put
} def

% fetch the next token from the
% array on top of the stack
% TODO: there's some duplication here with getnexttoken
/pulltoken { % pulltoken token true | false
//dbdict /stack get
//dbdict /ptr get
get
dup length 0 gt {
dup 0 get exch
dup length 1 gt {
//dbdict /stack get
//dbdict /ptr get
3 -1 roll
1 1 index length 1 sub getinterval put
}{ %else length 1 eq
pop
//dbdict /stack get
//dbdict /ptr get {} put
//pops exec
} ifelse
true
}{
//pops exec
false
} ifelse
} def

% fetch next token from
% top of stack
/getnexttoken {
//dbdict /nexttoken get
dup null eq {
pop
//dbdict /stack get
//dbdict /ptr get
get
dup type /filetype eq {
token
}{
pop
pulltoken
} ifelse
}{ true } ifelse
} def

% print message and present the error prompt
%
% some things you can do at the error prompt:
% skip - this will clear the /nexttoken entry in the dictionary
% so the next loop will fetch the next thing (may cause
problems later)
% exit - you're only in one loop so the regular postscript exit
operator
% will exit the debugger function and take you back to the
outer
% level from which you invoked the debug procedure.
% you can 'skip' here too and then 'resume' to restart the
debugger
% where you left off.
% This is a legitimate postscript prompt. So if the error is a
typecheck or
% a stack underflow, you can fix the stack, hit enter, and the
debugger will
% try again to execute the token.
/handerr {
$error /errorname get /invalidexit eq {
exit
}{
(db:) print
//errmsg exec
//dbdict /nexttoken get ==
(Stack:\n) print
pstack
dicttrace
(\n) print
{
(db-error>) print flush
(%lineedit) (r) file cvx
exec
} stopped {
$error /errorname get /invalidexit ne
$error /errorname get /undefinedfilename ne
and {
//errmsg exec (user command\n) print
} if
exit
} if
} ifelse
} def

% these are the single-character codes for the stepwise prompt
% you can type full words if you like. it only reads the first
character.
% the default is yes, whereupon the token in question will
% be executed in a stopped context in the big loop.
/stepcommands <<
/default { }
(y) 0 get { }
(s) 0 get { skip }
(q) 0 get { exit }
>> def

% present the step prompt,
% read a line from the user
% execute the matching step command
/stepquiz {
%(db: Stack: \n) print pstack
(db: Step ) print
dup ==
(defined as ) print
{ load == } stopped { (/undefined\n) print } if
(db: execute step? ([(y)es]/(s)kip/(q)uit)?) print flush
(%lineedit) (r) file read {
//stepcommands
exch 2 copy known not { pop /default } if
get exec
} if
} def

% the big ol' loop
% i can't even fit the whole thing on my screen.
% this probably needs more factoring
/debugloop {
//getnexttoken exec
{ %ifelse
//dbdict /nexttoken 3 -1 roll put
{ %stopped
//dbdict /nexttoken get
//dbdict /trace get { dup == } if

dup xcheck {
dup type /arraytype ne {
//dbdict /step get { //stepquiz exec }{ pop }
ifelse
//dbdict /nexttoken get dup null eq {
pop
}{
dup /run cvx eq {
pop (r) file pushs
}{
load
dup type /arraytype eq {
dup length 0 eq {
pop
}{
pushs
} ifelse
}{
exec
} ifelse
} ifelse
} ifelse
} if %else leave on stack
} if %else leave on stack

//dbdict /nexttoken null put
} stopped //handerr if
}{ %else no tokens
pops
//dbdict /nexttoken null put
(db: finished\n) print
//dbdict /ptr get 0 lt { exit } if
} ifelse
} def


%
% External names
%

% the main entry point
% i could probably modify this to take a proc as well
/debug { % (filename) debug -
//dbdict /trace get { (db: tracing ) }{ (db: debugging ) } ifelse
print
dup print (\n) print
{ %stopped
(r) file
} stopped { %if
(db:Error unable to open input file\n) print
//errmsg exec (\n) print
}{
pushs
(db: starting\n) print
//debugloop loop
}
ifelse
} def

% resume exit'ed session
/resume {
//dbdict /ptr get 0 lt {
(db: nothing to resume.\n) print
}{
(db: resuming) print
//dbdict /trace get { ( trace) print } if
(\n) print
//debugloop loop
} ifelse
} def

% skip the current token
% if used outside the debug loop (having exit'ed),
% it will clear the current token so the 'resume'ing
% will fetch the next thing from whereever it's
% getting them (array or file).
/skip { //dbdict /nexttoken null put } def

% enable/disable tracing/stepwise-prompting
/traceon { //dbdict /trace true put } def
/traceoff { //dbdict /trace false put } def
/stepon { //dbdict /step true put } def
/stepoff { //dbdict /step false put } def


%
% interfaced builtin operator
%

/oldcurrentfile /currentfile load def
/currentfile {
//dbdict /ptr get dup 0 ge {
-1 0 { %for
//dbdict /stack get exch get
dup type /filetype eq {
exit
} if
pop
} for
}{
oldcurrentfile
} ifelse
} def


% removing internal names

currentdict /dbdict undef
currentdict /errmsg undef
currentdict /handerr undef
currentdict /stepcommnds undef
currentdict /stepquiz undef
currentdict /steploop undef
currentdict /debugloop undef

%stepon traceon (db3.ps) debug % debug crashes upon attempting to
debug its own array
%eof
%--- eof: db4.ps -----


%--- sof: ds.ps -----
%!

/wherevalue { % value wherevalue dict key true | false
MYDICT
countdictstack array dictstack % val my-dict [ds]
exch begin
/found false def

dup length 1 sub -1 0 { % val [ds] i
1 index exch get % val [ds] dict

dup { % val [ds] dict key val
dup type /stringtype eq {
dup rcheck {
4 index eq {
/found true def
exit
} if
}{
pop
} ifelse
}{
4 index eq { % val [ds] dict key
/found true def
exit %forall
} if % val [ds] dict key
} ifelse
pop % val [ds] dict
} forall % val [ds] (found: dict key) (not: dict)

found {
exit %for
} if % val [ds] dict
pop % val [ds]

} for % (found: val [ds] dict key) (not: val [ds])

found {
4 2 roll pop pop true
}{
pop pop false
} ifelse
end
} dup 0 1 dict put def

/dicttrace {
(Dict Stack:\n) print
countdictstack array dictstack
dup length 1 sub -1 0 {
1 index exch get
wherevalue {
exch pop ( ) cvs print
(\n) print
}{
(-anonymous- ) print
} ifelse
} for
pop
} def

%--- eof: ds.ps -----


530(1)04:24 AM:ps 0>

luserXtrog

unread,
Sep 18, 2010, 6:09:20 AM9/18/10
to

Dammitall! Usenet broke my lines!
How narrow does it have to be?
Where do you look that up? Some kind of ARPA memo from '79?
Some "RFC" from '83? Is it POSIX? ANSI? A consensus of
implementations?

I feel like such a jerk posting things twice.

Oh, well. Not knowing the exact line width,
I've trimmed lines to no wider than the left
shoulder of the ballerina on the my desktop.
The should at least be easy to remember :)


535(1)05:05 AM:ps 0> echo; echo; for i in db4.ps ds.ps; do echo %---


sof: $i -----; cat $i; echo %--- eof: $i -----; echo; echo; done;


%--- sof: db4.ps -----
%!
{ currentfile token pop /ENDINTRO eq { exit } if } loop

debug.ps - A postscript debugger

The debugger takes a file and executes each token within
a stopped context, catching errors and retaining the rest
of the program. It requires an interactive session with
the interpreter.


ENDINTRO

(ds.ps) run

/steppstack true
>> def

% in the dictionary so the next loop


% will fetch the next thing

% (may cause problems later if the
% program needed this to happen)


% exit - you're only in one loop so the regular

% postscript exit operator will exit the
% debugger function and take you back to
% the outer level from which you invoked
% the debug procedure.you can 'skip' here
% too and then 'resume' to restart the
% debugger where you left off.


% This is a legitimate postscript prompt. So if the error

% is a typecheck or a stack underflow, you can fix the
% stack, hit enter, and the debugger will try again to
% execute the token. If the stack manipulation succeeds,
% then that's what the program needs to do at that point.
% TODO: make a separate trace output that inserts
% error commands into the text of the debugged program.


/handerr {
$error /errorname get /invalidexit eq {
exit
}{
(db:) print
//errmsg exec
//dbdict /nexttoken get ==
(Stack:\n) print
pstack
dicttrace
(\n) print
{
(db-error>) print flush
(%lineedit) (r) file cvx
exec
} stopped {
$error /errorname get /invalidexit ne
$error /errorname get /undefinedfilename ne
and {
//errmsg exec (user command\n) print
} if
exit
} if
} ifelse
} def

% these are the single-character codes for the stepwise

% prompt you can type full words if you like. it only
% reads the first character. the default is yes,
% whereupon the token in question will be executed
% in a stopped context in the big loop.


/stepcommands <<
/default { }
(y) 0 get { }
(s) 0 get { skip }
(q) 0 get { exit }
>> def

% present the step prompt,
% read a line from the user
% execute the matching step command
/stepquiz {

//dbdict /steppstack get { (db: Stack: \n) print pstack } if

/steppstackon { //dbdict /steppstack true put } def
/steppstackoff { /dbdict /steppstack false put } def

%
% interfaced builtin operator
%

/oldcurrentfile /currentfile load def
/currentfile {
//dbdict /ptr get dup 0 ge {
-1 0 { %for
//dbdict /stack get exch get
dup type /filetype eq {
exit
} if
pop
} for
}{
oldcurrentfile
} ifelse
} def


% removing internal names

currentdict /dbdict undef
currentdict /errmsg undef
currentdict /handerr undef
currentdict /stepcommnds undef
currentdict /stepquiz undef
currentdict /steploop undef
currentdict /debugloop undef

% debug crashes upon attempting to debug its own array
%stepon traceon (db3.ps) debug


%eof
%--- eof: db4.ps -----


%--- sof: ds.ps -----
%!

% value wherevalue dict key true
% false
% find dictionary and key for value
/wherevalue {

% print names of dictionaries on the stack


/dicttrace {
(Dict Stack:\n) print
countdictstack array dictstack
dup length 1 sub -1 0 {
1 index exch get
wherevalue {
exch pop ( ) cvs print
(\n) print
}{
(-anonymous- ) print
} ifelse
} for
pop
} def

%--- eof: ds.ps -----


536(1)05:05 AM:ps 0>

A D

unread,
Sep 18, 2010, 9:40:41 AM9/18/10
to
On 18/09/10 20:09, luserXtrog wrote:
>
> Dammitall! Usenet broke my lines!
> How narrow does it have to be?
> Where do you look that up? Some kind of ARPA memo from '79?
> Some "RFC" from '83? Is it POSIX? ANSI? A consensus of
> implementations?

I don't believe so. Most email clients now set 72 chars as the wrap
point. It used to be adjustable, but thunderbird now hides that
setting, but it does a WYSIWYG display of it as you type. Insertion of
a text file may do something else though.


Matti Vuori

unread,
Sep 18, 2010, 6:11:53 PM9/18/10
to
A D <amd...@fastmail.com.au> wrote in news:4c94c15a$0$11089$c3e8da3
@news.astraweb.com:

Of course, an _email_ client has nothing to do with Usenet... Nor does
Thunderbird have much relevance as a newsreader - I suspect that it does't
have much of a market share in that purpose.

tlvp

unread,
Sep 18, 2010, 10:36:14 PM9/18/10
to

You're right, of course, Matti, but there are several bits of kit available,
thought of by most as email clients, that are actually dual-purpose items
serving as both email and NNTP clients (and perhaps more still). Thunderbird
is one such; SeaMonkey is another; Opera is yet another; and surely there are
even more.

So please forgive folks for their over-simplifying of their statements, and
accept, please, that "email clients" may well refer to software that is also
NNTP client at the same time.

Cheers, -- tlvp


--
Avant de repondre, jeter la poubelle, SVP

A D

unread,
Sep 19, 2010, 9:33:32 AM9/19/10
to

SMTP and NNTP are just back ends to GUI utilities these days.
And yes, I have used CLI email and newsreader clients in the past,
probably when you were still wearing nappies.

0 new messages