Archives

Sunday November 2, 2008

Multiple sets of Terminal windows in Mac OS X (Leopard)

Back in May 2006, I wrote a blog entry about creating multiple sets of Terminal windows in Mac OS X. This weekend, I finally got around to upgrading my desktop machine to Mac OS X 10.5 (Leopard). Leopard's version of Terminal is a great improvement over the old one, but it still is only a single application. So, if I hide it, all of its windows go away.

Although my old blog entry explains how to create multiple Terminal instances, the part that dealt with editing the executable binary no longer worked. Because the solution involved Real Code (TM), I decided to create a new blog entry...

Back in 2006, I was able to edit the Terminal.app executable (using BBEdit) and then run the resulting application. When I tried this with the Leopard version, the app simply refused to run. My guess is that this has to do with "code signing", a security measure intended to keep modified applications from running. This is a Really Good Idea (for reasons of security), so I didn't want to disable it.

Besides, I wanted an approach that didn't involve munging a binary. After all, the only reason for the hack was to tell my shell session which instance of Terminal I'm running. How hard could that be? (:-) And, with some help from the folks on #macdev and a bit of Ruby scripting, I now have a cleaner and more robust solution.

The following script goes in either a local or personal bin directory (eg, /usr/local/bin, $HOME/bin). When called, it walks up the stack of ancestor processes, looking for a command whose name looks like ".../Terminal.app/...", ".../Terminal_2.app/...", etc. If it finds this, it prints the basename of the path element (eg, "Terminal"). Otherwise, it prints "???".

#!/usr/bin/env ruby
#
# get_term_name - get the name of the current instance of Terminal.app.
#
# Note: This is rather fragile code, which depends on the name used for the
#       Terminal app (eg, Terminal, Terminal_2) and the way that Mac OS X
#       Leopard starts up shell sessions under Terminal.app.  That said...
#
# The process relationship is as follows:
#
#   Terminal -> login -> shell -> ... -> $$
#
# So, we can walk up the process stack from $$, get the pathname of the
# Terminal app being used, and print out the basename (eg, Terminal_2).
#
# Written by Rich Morin, CFCL, 2008

  # Disable "warning: Insecure world writable dir /Volumes, mode 041777".
  #
  $VERBOSE = nil

  # Get the process id (pid), parent process id (ppid), and command text
  # (cmd) for all current processes, then create an ancestry hash.

  lines = `ps ax -o pid -o ppid -o command`
  h_cmd = {}
  h_pid = {}

  lines.each do |line|

    if (line =~ /^\s*(\d+)       # pid
                  \s+(\d+)       # ppid
                  \s+(.+)$/x )   # cmd

      pid, ppid, cmd = $1, $2, $3
      ipid           = pid.to_i
      h_pid[ipid]    = ppid.to_i
      h_cmd[ipid]    = cmd.dup
    end
  end

  # Walk the ancestry, by parent process id, from the current process.

  pid = $$
  while (pid) do
    pid = h_pid[pid]
    cmd = h_cmd[pid]

    if (cmd =~ %r{/(Terminal.*)\.app/} )
      puts $1
      exit
    end
  end

  puts '???'  # No match; punt...
In case anyone is curious, the (C-shell) code that uses this script looks like:
...
  set tn = `get_term_name`
  switch ($tn)
    case Terminal:
      set ts = 1;   breaksw;
    case Terminal_2:
      set ts = 2;   breaksw;
    case Terminal_3:
      set ts = 3;   breaksw;
    case Terminal_4:
      set ts = 4;   breaksw;
    default
      set ts = '?'; breaksw;
  endsw

  switch ($uid)
    case 0:
      set prompt = "(SU) $USER@$HOST [%c02] \!: "
      if ($?XTERM_COMPAT) then
        alias precmd "settitle TS$ts \(SU\) _${HOST}_ $tt \(SU\)"
      endif
      breaksw
    default
      set prompt = "($USER@$HOST) [%c02] \!: "
      if ($?XTERM_COMPAT) then
        alias precmd "settitle TS$ts _${HOST}_ $tt"
      endif
      breaksw
  endsw
endif
...
This code sets the prompt string and grab bar text to my taste...

Multiple sets of Terminal windows in Mac OS X (Leopard) in Computers , Technology - posted at Sun, 02 Nov, 21:44 Pacific | «e» | TrackBack


Post a comment

Note: All comments are subject to approval. Spam will be deleted before anyone ever sees it. Excessive use of URLs may cause comments to be auto-junked. You have been warned.

Any posted comments will be viewable by all visitors. Please try to stay relevant ;-) If you simply want to say something to me, please send me email.