"""$Id $
Handles signal handlers within Pydb.
"""
import signal

def lookup_signum(name):
    """Find the corresponding signal number for 'name'."""
    try:
        exec 'from signal import %s' % name
    except ImportError:
        return
    return eval(name)

# XXX Rocky, do you think the do_* methods are confusing? I.e. that someone
# will think they work the same way as the do_* method from cmd.Cmd?
# I used it because print and pass are keywords and trying to strip out the
# 'i' and 'a' from print and pass respectively, when this code is used in
# another file is just.. mad.. 'do' made sense to me.

class SigHandler:

    """Store information about what we do when we handle a signal,

    - Do we print/not print when signal is caught
    - Do we pass/not pass the signal to the program
    - Do we stop/not stop when signal is caught

    All the methods to change these attributes return None on error, or
    True or False if we have set the action (pass/print/stop) for a signal
	handler.
    """
    def __init__(self, signum):

		# This list contains tuples made up of four items, one tuple for
		# every available signal number on this system. The tuples contain
		# (signal_num, stop, print, pass)
		self._sig_attr = []

		for i in range(1, NSIG):
			try:
				d_handler = signal.getsignal(i)
				self._sig_nums.append((i, True, True, True))
			except RuntimeError:
				# A runtime exception can be raised if we ask for the signal
				# handler for a signal that doesn't exist.
				continue

	def _get_sig(self, num):
		for i in self._sig_attr:
			if i[0] == num:
				return i[1:]

	def _set_sig(self, num, (st, pr, pa)):
		for i in self._sig_attr:
			if i[0] == num:
				self._sig_attr.pop(self._sig_attr.index(i))
		self._sig_attr.append((num, st, pr, pa))

    def do_stop(self, signum, change):
        """Change whether we stop or not when this signal is caught.
        If 'change' is True your program will stop when this signal
        happens."""
        if not isinstance(change, bool):
            return
		old_attr = self._get_sig(signum)
		st, pr, pa = change, old_attr[1], old_attr[2]
        if st:
            pr = True
		self._set_sig(signum, (st, pr, pa))
        return change

    def do_pass(self, signum, change):
        """Change whether we pass this signal to the program (or not)
        when this signal is caught. If change is True, Pydb should allow
        your program to see this signal.
        """
        if not isinstance(change, bool):
            return
		old_attr = self._get_sig(signum)
		st, pr, pa = old_attr[0], old_attr[1], change
		self._set_sig(signum, (st, pr, pa))
        return change

    # ignore is a synonym for nopass and noignore is a synonym for pass
    def do_ignore(self, signum, change):
        if not isinstance(change, bool):
            return
        self.do_pass(not change)
        return change

    def do_print(self, signum, change):
        """Change whether we print or not when this signal is caught."""
        if not isinstance(change, bool):
            return
		old_attr = self._get_sig(signum)
		st, pr, pa = old_attr[0], change, old_attr[2]
        if not change:
			# noprint implies nostop
			st = False
		self._set_sig(signum, (st, pr, pa))
        return change

    def handle(self, signum, frame):
        """This method is called when a signal is received."""
		st, pa, pr = self._get_sig(signum)
        if pr:
            print 'Signal %d received' % self._signum
        if pa:
            # pass signal to program and stop
            pass
            
    
