Subject: UNC names and ange-ftp, how I fixed it

From: Mary Holstege <[email-masked]>
Date: Mon, 18 Nov 96 16:23:35 PST

To recap, my problem was that certain file operations failed when
I was performing them from a buffer that was being managed via ange-ftp
magic.  So, for example, if I was looking at the file
    /holstege@unixbox:/home/holstege/work/stuff.txt
and then I tried to access a file with a UNC path, such as
    //ntbox/shared/holstege/more.txt
I would end up looking at
    /holstege@unixbox:/home/holstege/work/ntbox/shared/holstege/more.txt

This also interfered with my printing setup, as I was just doing a
ps-print-buffer-with-faces direct to the printer, e.g. "//printhost/printer"
So to print I would have to copy the file into a local buffer and then print
it.  

Various kind people suggested I upgrade ange-ftp, or sent me versions they
had tweaked and were using successfully.  I thank you, but none of this
made the slightest bit of difference.

I don't fully understand it all, but it boiled down to this:
(expand-file-name "//node/path") returned "node/path" from remote ange-ftp
buffers and "//node/path" from local buffers.

I ended up changing the function ange-ftp-real-expand-file-name-actual
itself.  This function is used only on Win95/NT and chops off spurious drive
letters.  It seems that it assumes those drive letters will always be there and
in some cases they are not.  Here is the fixed function (it is way down at
the end of ange-ftp.el):

(defun ange-ftp-real-expand-file-name-actual (&rest args) 
  (setq old-name (car args)) 
  (setq new-name (ange-ftp-run-real-handler 'expand-file-name args)) 
  (setq drive-letter (substring new-name 0 2) ) 
  ;; I'd like to distill the following lines into one (if) statement 
  ;;   removing the need for the temp final variable 
  (setq final new-name) 
  (if ( not (equal (substring old-name 0 1) "~") )
      (if (and (or (< (length old-name) 2) 
						 (not (string-match "/[a-zA-Z]:" old-name) ) )
					(string-match "[a-zA-Z]:" new-name))
	  (setq final (substring new-name 2) ) ) ) 
  final) 

I hope this isn't something so bizarre in my configuration that this isn't
of use to someone else!

	-- Mary
	   [email-masked]

| Mary Holstege, PhD
| First Floor Software                    (415) 254-5161
| 444 Castro Street, Suite 200            (415) 968-1193 (Fax)
| Mountain View, CA 94041
| 
| http://www-cs-students.stanford.edu/~holstege

---

From: Milt Epstein <[email-masked]>
Date: Tue, 19 Nov 1996 09:25:39 -0600 (CST)

On Mon, 18 Nov 1996, Mary Holstege wrote:

[ ... ]
> I ended up changing the function
> ange-ftp-real-expand-file-name-actual itself.  This function is used
> only on Win95/NT and chops off spurious drive letters.  It seems
> that it assumes those drive letters will always be there and in some
> cases they are not.  Here is the fixed function (it is way down at
> the end of ange-ftp.el):
> 
>   (if (not (equal (substring old-name 0 1) "~"))
>                  (not (string-match "/[a-zA-Z]:" old-name)))
>       (setq final (substring new-name 2))))
>   final)
> 

I know basically nothing about ange-ftp, but I do know lisp, and this
function looks quite odd to me (i.e. commenting more from a
syntactic/programming point of view rather than a semantic one).  It
mostly has to do with emacs-lisp's concept of global/local variables
(OK, OK, it really has to do with scope/extent, but those are trickier
concepts, and it can be intuitively understood by thinking of them as
global/local variables).

Basically, when you see something like:

(setq old-name ...)

*where old-name has not been mentioned previously in the function*, it
is referencing/changing the global variable old-name.  Now in many
cases in emacs-lisp, this is something you do intentionally (e.g. it
allows using user-settable variables/options).  However, in many
cases, it is a very bad thing to do.  For example, one thing you don't
want to use it for is to create temporary/local variables in a
function (this can have nasty, unintended, hard-to-debug
side-effects).  And it looks like that is what it is being used for
above.

For example, I could find no reference to the variables old-name,
new-name, and drive-letter anywhere else in the emacs-lisp code I
have.  And especially no declaration of them via defvar (which is
typical for emacs-lisp global variables).  They seem only to be being
used as temporary variables.  (Actually, it's doubly odd that
drive-letter is not even used later in the function.)

So, basically there's two possibilities here.  First, these variables
were really intended to be global (and must be being used somewhere
else that I can't find, e.g. perhaps in a code file that I don't
have).  If this is the case, however, these variables should be
defvar'ed (which could be happening in that other file), and more
specific/descriptive names should be used.

If they're not, then this function should be rewritten to avoid the
use of global variables.  (And from the comment about the temp final
variable, it kind of indicates that this is what is happening here.)

The way to use local variable in emacs-lisp is to use let (and it's
cousin, let*).  So, assuming that we really do want to use local
variables here, this is how I'd rewrite the above function (like I
said, I can't really comment on the semantics of the function):

(defun ange-ftp-real-expand-file-name-actual (&rest args)
  (let* ( (old-name (car args))
          (new-name (ange-ftp-run-real-handler 'expand-file-name args))
          (drive-letter (substring new-name 0 2)) )
    (if (not (equal (substring old-name 0 1) "~"))
      (if (and (or (< (length old-name) 2)
                   (not (string-match "/[a-zA-Z]:" old-name) ) )
               (string-match "[a-zA-Z]:" new-name))
         (setq new-name (substring new-name 2))))
    new-name))

Note: Once a local variable has been created/declared via a let, you
can freely use setq on it in the body of that let.

Milt Epstein
Software Engineer
Central Data Corporation
[email-masked]
WWW: http://www.cd.com/
FTP: ftp.cd.com

---

From: Ronan Waide <[email-masked]>
Date: Wed, 20 Nov 96 09:32:29 +0000 (GMT)

>          (setq new-name (substring new-name 2))))

Which is all very nice and neatly reproduces the effects of the
original, but unfortunately the original mods seem to have broken
.netrc completion - actually, I think the original (virtunix)
distributed version has/had the same problem, but I had at some point
managed to fix this. *sigh* Back to the drawing board.

Waider.
-- 
[email-masked] / AutoDealing Software Ltd / +353-1-6766455

Someone got their mojo working and all I got was this Indian spirit guide.