    // @(#)root/net:$Name:  $:$Id: TUrl.cxx,v 1.17 2004/03/02 13:03:31 rdm Exp $
    // Author: Fons Rademakers   17/01/97

    /*************************************************************************
     * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
     * All rights reserved.                                                  *
     *                                                                       *
     * For the licensing terms see $ROOTSYS/LICENSE.                         *
     * For the list of contributors see $ROOTSYS/README/CREDITS.             *
     *************************************************************************/

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    // TUrl                                                                 //
    //                                                                      //
    // This class represents a WWW compatible URL.                          //
    // It provides member functions to return the different parts of        //
    // an URL. The supported url format is:                                 //
    //  [proto://][user[:passwd]@]host[:port]/file.ext[#anchor][?options]   //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    #include <stdlib.h>
    #include "TUrl.h"
    #include "TObjArray.h"
    #include "TObjString.h"
    #include "TEnv.h"

    TObjArray *TUrl::fgSpecialProtocols;

    ClassImp(TUrl)

    //______________________________________________________________________________
     TUrl::TUrl(const char *url, Bool_t defaultIsFile)
    {
       // Parse url character string and split in its different subcomponents.
       // Use IsValid() to check if URL is legal.
       //
       // url: [proto://][user[:passwd]@]host[:port]/file.ext[#anchor][?options]
       //
       // Known protocols: http, root, proof, ftp, news, file, rfio, hpss, castor,
       // dcache and dcap.
       // The default protocol is http, unless defaultIsFile is true in which
       // case the url is assumed to be of type file.
       // If a passwd contains a @ it must be escaped by a \, e.g.
       // "pip@" becomes "pip\@".
       //
       // Default ports: http=80, root=1094, proof=1093, ftp=20, news=119.
       // Port #1093 has been assigned by IANA (www.iana.org) to proofd.
       // Port #1094 has been assigned by IANA (www.iana.org) to rootd.

       if (!url || !strlen(url)) {
          fPort = -1;
          return;
       }

       // Set defaults
       fUrl      = "";
       fProtocol = "http";
       fUser     = "";
       fPasswd   = "";
       fHost     = "";
       fPort     = 80;
       fFile     = "/";
       fAnchor   = "";
       fOptions  = "";

       // Find protocol
       char *s, sav;

       // Handle special protocol cases: file:, rfio:, etc.
       for (int i = 0; i < GetSpecialProtocols()->GetEntries(); i++) {
          TObjString *os = (TObjString*) GetSpecialProtocols()->UncheckedAt(i);
          TString &s = os->String();
          int l = s.Length();
          if (!strncmp(url, s, l)) {
             fProtocol = s(0, l-1);
             if (!strncmp(url+5, "//", 2))
                fFile = url+l+2;
             else
                fFile = url+l;
             fPort = 0;
             return;
          }
       }

       char *u0, *u = StrDup(url);
       u0 = u;

       Bool_t isWin32File = kFALSE;
    #ifdef R__WIN32
       isWin32File = defaultIsFile;
    #endif

       char *t, *s2;
       if ((s = strstr(u, ":/")) && !isWin32File) {
          if (*(s+2) != '/') {
             Error("TUrl", "malformed, URL must contain \"://\"");
             fPort = -1;
             goto cleanup;
          }
          sav = *s;
          *s = 0;
          fProtocol = u;
          *s = sav;
          if (!fProtocol.CompareTo("http"))
             fPort = 80;
          else if (fProtocol.BeginsWith("proof"))  // can also be proofs or proofk
             fPort = 1093;
          else if (fProtocol.BeginsWith("root"))   // can also be roots or rootk
             fPort = 1094;
          else if (!fProtocol.CompareTo("ftp"))
             fPort = 20;
          else if (!fProtocol.CompareTo("news"))
             fPort = 119;
          else {
             // generic protocol (no default port)
             fPort = 0;
          }
          s += 3;
          if (!*s) {
             // error if we are at end of string
             fPort = -1;
             goto cleanup;
          }
       } else {
          if (defaultIsFile) {
             fProtocol = "file";
             fFile = u;
             fPort = 0;
             goto cleanup;
          }
          s = u;
       }

       // Find user and passwd
       u = s;
       t = s;
    again:
       if ((s = strchr(t, '@'))) {
          if (*(s-1) == '\') {
             t = s+1;
             goto again;
          }
          sav = *s;
          *s = 0;
          if ((s2 = strchr(u, ':'))) {
             *s2 = 0;
             fUser = u;
             *s2 = ':';
             s2++;
             if (*s2) {
                fPasswd = s2;
                fPasswd.ReplaceAll("\@", "@");
             }
          } else
             fUser = u;
          *s = sav;
          s++;
       } else
          s = u;

       // Find host
       u = s;
       if ((s = strchr(u, ':')) || (s = strchr(u, '/'))) {
          if ((strchr (u, ':') > strchr(u, '/')) && (strchr (u, '/')))
    	s = strchr(u, '/');
          sav = *s;
          *s = 0;
          fHost = u;
          *s = sav;
          if (sav == ':') {
             s++;
             // Get port #
             if (!*s) {
                fPort = -1;
                goto cleanup;
             }
             u = s;
             if ((s = strchr(u, '/'))) {
                sav = *s;
                *s = 0;
                fPort = atoi(u);
                *s = sav;
             } else {
                fPort = atoi(u);
                goto cleanup;
             }
          }
       } else {
          fHost = u;
          goto cleanup;
       }

       if (!*s) goto cleanup;

       // Find file
       u = s;
       if ((s = strchr(u, '#')) || (s = strchr(u, '?'))) {
          sav = *s;
          *s = 0;
          fFile = u;
          *s = sav;
          s++;
          if (sav == '#') {
             // Get anchor
             if (!*s) {
                fPort = -1;
                goto cleanup;
             }
             u = s;
             if ((s = strchr(u, '?'))) {
                sav = *s;
                *s = 0;
                fAnchor = u;
                *s = sav;
                s++;
             } else {
                fAnchor = u;
                goto cleanup;
             }
          }
          if (!*s) {
             // error if we are at end of string
             fPort = -1;
             goto cleanup;
          }
       } else {
          fFile = u;
          goto cleanup;
       }

       // Set option
       fOptions = s;

    cleanup:
       delete [] u0;
    }

    //______________________________________________________________________________
     TUrl::TUrl(const TUrl &url) : TObject(url)
    {
       // TUrl copt ctor.

       fUrl      = url.fUrl;
       fProtocol = url.fProtocol;
       fUser     = url.fUser;
       fPasswd   = url.fPasswd;
       fHost     = url.fHost;
       fFile     = url.fFile;
       fAnchor   = url.fAnchor;
       fOptions  = url.fOptions;
       fPort     = url.fPort;
    }

    //______________________________________________________________________________
    TUrl &TUrl::operator=(const TUrl &rhs)
    {
       // TUrl assignment operator.

       if (this != &rhs) {
          TObject::operator=(rhs);
          fUrl      = rhs.fUrl;
          fProtocol = rhs.fProtocol;
          fUser     = rhs.fUser;
          fPasswd   = rhs.fPasswd;
          fHost     = rhs.fHost;
          fFile     = rhs.fFile;
          fAnchor   = rhs.fAnchor;
          fOptions  = rhs.fOptions;
          fPort     = rhs.fPort;
       }
       return *this;
    }

    //______________________________________________________________________________
     const char *TUrl::GetUrl()
    {
       // Return full URL.

       if (IsValid() && fUrl == "") {
          // Handle special protocol cases: file:, rfio:, etc.
          for (int i = 0; i < GetSpecialProtocols()->GetEntries(); i++) {
             TObjString *os = (TObjString*) GetSpecialProtocols()->UncheckedAt(i);
             TString &s = os->String();
             int l = s.Length();
             if (fProtocol == s(0, l-1)) {
                fUrl = fProtocol + ":" + fFile;
                return fUrl;
             }
          }

          Bool_t deflt = kFALSE;
          if ((!fProtocol.CompareTo("http")   && fPort == 80)   ||
              (!fProtocol.CompareTo("proof")  && fPort == 1093) ||
              (!fProtocol.CompareTo("proofs") && fPort == 1093) ||
              (!fProtocol.CompareTo("proofk") && fPort == 1093) ||
              (!fProtocol.CompareTo("root")   && fPort == 1094) ||
              (!fProtocol.CompareTo("roots")  && fPort == 1094) ||
              (!fProtocol.CompareTo("rootk")  && fPort == 1094) ||
              (!fProtocol.CompareTo("ftp")    && fPort == 20)   ||
              (!fProtocol.CompareTo("news")   && fPort == 119)  ||
               fPort == 0)
             deflt = kTRUE;

          fUrl = fProtocol + "://";
          if (fUser != "") {
             fUrl += fUser;
             if (fPasswd != "") {
                fUrl += ":";
                TString passwd = fPasswd;
                passwd.ReplaceAll("@", "\@");
                fUrl += passwd;
             }
             fUrl += "@";
          }
          if (!deflt) {
             char p[10];
             sprintf(p, "%d", fPort);
             fUrl = fUrl + fHost + ":" + p + fFile;
          } else
             fUrl = fUrl + fHost + fFile;
          if (fAnchor != "") {
             fUrl += "#";
             fUrl += fAnchor;
          }
          if (fOptions != "") {
             fUrl += "?";
             fUrl += fOptions;
          }
       }

       return fUrl;
    }

    //______________________________________________________________________________
     void TUrl::Print(Option_t *) const
    {
       // Print URL on stdout.

       if (fPort == -1)
          Printf("Illegal URL");

       Printf("%s", ((TUrl*)this)->GetUrl());
    }

    //______________________________________________________________________________
     TObjArray *TUrl::GetSpecialProtocols()
    {
       // Read the list of special protocols from the rootrc files.
       // These protocols will be parsed in a protocol and a file part,
       // no host or other info will be determined. This is typically
       // used for legacy file descriptions like: rfio:host:/path/file.root.

       if (!fgSpecialProtocols)
          fgSpecialProtocols = new TObjArray;

       if (fgSpecialProtocols->GetEntries() > 0 || !gEnv)
          return fgSpecialProtocols;

       const char *protos = gEnv->GetValue("Url.Special", "file: rfio: hpss: castor: dcache: dcap:");

       if (protos) {
          Int_t cnt = 0;
          char *p = StrDup(protos);
          while (1) {
             TObjString *proto = new TObjString(strtok(!cnt ? p : 0, " "));
             if (proto->String().IsNull()) {
                delete proto;
                break;
             }
             fgSpecialProtocols->Add(proto);
             cnt++;
          }
          delete [] p;
       }
       return fgSpecialProtocols;
    }