path-utils.lisp Unix DownloadWindows Download
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -*- Mode: Lisp -*- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; File		     - path-utils.lisp
;; Description	     - tfb's path utils
;; Author	     - Tim Bradshaw (tfb at lostwithiel)
;; Created On	     - Sun Jan 30 13:38:02 2000
;; Last Modified On  - Mon Jun 25 12:56:19 2001
;; Last Modified By  - Gail Anderson (ga at lostwithiel)
;; Update Count	     - 9
;; Status	     - Unknown
;; 
;; $Id: path-utils.lisp,v 1.1 2003/01/23 22:19:15 colin Exp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; Copyright (C) 2000, 2001 Cley Limited
;;; 
;;; This program is free software; you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 2 of the License, or (at
;;; your option) any later version.
;;; 
;;; This program is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;;; General Public License for more details.
;;; 
;;; You should have received a copy of the GNU General Public License
;;; along with this program; if not, write to the Free Software
;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
;;; USA.
;;; 
;;; You can contact Cley Limited by emailing cley@cley.com.


(in-package :upt)

(defun split-string (string char)
  ;; split STRING on CHAR, returning a list of substrings.  Return the
  ;; list of substrings, and 2 additional values: whether the first
  ;; substring is null and whether the last one was.  This is not a
  ;; general tool.
  (declare (type string string)
	   (type character char))
  (let ((start 0))
    (flet ((next-chunk ()
	     (if (null start)
		 nil
		 (let ((next-break (position char string :start start)))
		   (if (null next-break)
		       (prog1
			   (subseq string start)
			 (setf start nil))
		       (prog1
			   (subseq string start next-break)
			 (setf start (1+ next-break))))))))
      (loop for tok = (next-chunk)
	  while tok
	  for last-null-p = (zerop (length tok))
	  for first-null-p = last-null-p then first-null-p
	  collect tok into toks
	  finally (return (values toks first-null-p last-null-p))))))

(defstruct urlpath
  (name nil)
  (directory '(:absolute)))

;;; this pair of functions are pretty ugly.
;;;

(defun urlpath->path-name (urlpath)
  "Return a string that corresponds to URLPATH."
  (declare (type urlpath urlpath))
  (let ((directory (urlpath-directory urlpath))
	(name (urlpath-name urlpath)))
    (labels ((doconc (length tail initial-slash-p)
	       (if (null tail)
		   (let* ((flen (length name))
			  (s (make-string (+ length flen 1))))
		     (setf (subseq s (1+ length)) name)
		     (setf (subseq s length) "/")
		     (values s length))
		   (let* ((cur (let ((the-cur (first tail)))
				 (case the-cur
				   ((:up :back) "..")
				   ((:wild) "*")
				   ((:wild-inferiors) "**")
				   (otherwise the-cur))))
			  (l (length cur))
			  (not-lastp (cdr tail))
			  (o (if not-lastp 1 0)))
		     (multiple-value-bind (s a)
			 (doconc (+ length l o) (cdr tail) nil)
		       (if not-lastp (setf (subseq s (1- a)) "/"))
		       (setf (subseq s (- a o l)) cur)
		       (if initial-slash-p
			   (progn
			     (setf (subseq s 0) "/")
			     (values s (- a l 1 o)))
			   (values s (- a l o))))))))
      (values (ecase (car directory)
		((:absolute)
		 (if (cdr directory)
		     (doconc 1 (cdr directory) t)
		     (doconc 0 nil nil)))
		((:relative)
		 (doconc 0 (cdr directory) nil)))))))


(defun path-name->urlpath (path-name)
  "Construct and return a URLPATH object corresponding to the string
PATH-NAME. A path-name starting with `/' is an absolute path-name;
one ending with `/' represents a directory. `.' components are
elided, but `..' components are not"
  (declare (type string path-name))
  (multiple-value-bind (split absolutep directoryp)
      (split-string path-name #\/)
    (when absolutep
      (if (null (cdr split))
	  (setf absolutep nil
		split '())
	  (setf split (cdr split))))
    ;; this is way fiddly.  SPLIT can be NULL or length 1, both of
    ;; which we must special case
    (let ((name
	   (let ((length (length split)))
	     (case length
	       ((0) nil)		;Bad if DIRECTORYP is non-NULL
	       ((1)
		(prog1 (if directoryp
			   nil
			   (car split))
		  (setf split '())))
	       (otherwise		;length > 1
		(let ((penultimate (nthcdr (- length 2) split)))
		  (prog1
		      (if directoryp
			  nil
			  (cadr penultimate))
		    (setf (cdr penultimate) nil))))))))
      (make-urlpath :name name
		 :directory (cons (if absolutep :absolute :relative)
				  (mapcan #'(lambda (e)
					      (cond ((or (zerop (length e))
							 (string= e "."))
						     nil)
						    ((string= e "..")
						     (list ':back))
						    (t (list e))))
					  split))))))