Back to index

lightning-sunbird  0.9+nobinonly
nsURLParsers.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Darin Fisher (original author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include <string.h>
00040 #include "nsURLParsers.h"
00041 #include "nsURLHelper.h"
00042 #include "nsIURI.h"
00043 #include "prtypes.h"
00044 #include "nsString.h"
00045 #include "nsCRT.h"
00046 #include "netCore.h"
00047 
00048 //----------------------------------------------------------------------------
00049 
00050 static PRUint32
00051 CountConsecutiveSlashes(const char *str, PRInt32 len)
00052 {
00053     PRUint32 count = 0;
00054     while (len-- && *str++ == '/') ++count;
00055     return count;
00056 }
00057 
00058 //----------------------------------------------------------------------------
00059 // nsBaseURLParser implementation
00060 //----------------------------------------------------------------------------
00061 
00062 // The URL parser service does not have any internal state; however, it can
00063 // be called from multiple threads, so we must use a threadsafe AddRef and
00064 // Release implementation.
00065 NS_IMPL_THREADSAFE_ISUPPORTS1(nsBaseURLParser, nsIURLParser)
00066 
00067 #define SET_RESULT(component, pos, len) \
00068     PR_BEGIN_MACRO \
00069         if (component ## Pos) \
00070            *component ## Pos = PRUint32(pos); \
00071         if (component ## Len) \
00072            *component ## Len = PRInt32(len); \
00073     PR_END_MACRO
00074 
00075 #define OFFSET_RESULT(component, offset) \
00076     PR_BEGIN_MACRO \
00077         if (component ## Pos) \
00078            *component ## Pos += offset; \
00079     PR_END_MACRO
00080 
00081 NS_IMETHODIMP
00082 nsBaseURLParser::ParseURL(const char *spec, PRInt32 specLen,
00083                           PRUint32 *schemePos, PRInt32 *schemeLen,
00084                           PRUint32 *authorityPos, PRInt32 *authorityLen,
00085                           PRUint32 *pathPos, PRInt32 *pathLen)
00086 {
00087     NS_PRECONDITION(spec, "null pointer");
00088 
00089     if (specLen < 0)
00090         specLen = strlen(spec);
00091 
00092     const char *stop = nsnull;
00093     const char *colon = nsnull;
00094     const char *slash = nsnull;
00095     const char *p;
00096     PRInt32 len = specLen;
00097     for (p = spec; len && *p && !colon && !slash; ++p, --len) {
00098         // skip leading whitespace and control characters
00099         if (*p > '\0' && *p <= ' ') {
00100             spec++;
00101             specLen--;
00102             continue;
00103         }
00104         switch (*p) {
00105             case ':':
00106                 if (!colon)
00107                     colon = p;
00108                 break;
00109             case '/': // start of filepath
00110             case '?': // start of query
00111             case '#': // start of ref
00112             case ';': // start of param
00113                 if (!slash)
00114                     slash = p;
00115                 break;
00116             case '@': // username@hostname
00117             case '[': // start of IPv6 address literal
00118                 if (!stop)
00119                     stop = p;
00120                 break;
00121         }
00122     }
00123     // disregard the first colon if it follows an '@' or a '['
00124     if (colon && stop && colon > stop)
00125         colon = nsnull;
00126 
00127     // if the spec only contained whitespace or control characters...
00128     if (specLen == 0) {
00129         SET_RESULT(scheme, 0, -1);
00130         SET_RESULT(authority, 0, 0);
00131         SET_RESULT(path, 0, 0);
00132         return NS_OK;
00133     }
00134 
00135     // ignore trailing whitespace and control characters
00136     for (p = spec + specLen - 1; ((unsigned char) *p <= ' ') && (p != spec); --p)
00137         ;
00138 
00139     specLen = p - spec + 1;
00140 
00141     if (colon && (colon < slash || !slash)) {
00142         //
00143         // spec = <scheme>:/<the-rest>
00144         //
00145         // or
00146         //
00147         // spec = <scheme>:<authority>
00148         // spec = <scheme>:<path-no-slashes>
00149         //
00150         if (!net_IsValidScheme(spec, colon - spec) || (*(colon+1) == ':')) {
00151             NS_WARNING("malformed uri");
00152             return NS_ERROR_MALFORMED_URI;
00153         }
00154         SET_RESULT(scheme, 0, colon - spec);
00155         if (authorityLen || pathLen) {
00156             PRUint32 offset = colon + 1 - spec;
00157             ParseAfterScheme(colon + 1, specLen - offset,
00158                              authorityPos, authorityLen,
00159                              pathPos, pathLen);
00160             OFFSET_RESULT(authority, offset);
00161             OFFSET_RESULT(path, offset);
00162         }
00163     }
00164     else {
00165         // 
00166         // spec = <authority-no-port-or-password>/<path>
00167         // spec = <path>
00168         //
00169         // or
00170         //
00171         // spec = <authority-no-port-or-password>/<path-with-colon>
00172         // spec = <path-with-colon>
00173         //
00174         // or
00175         //
00176         // spec = <authority-no-port-or-password>
00177         // spec = <path-no-slashes-or-colon>
00178         //
00179         SET_RESULT(scheme, 0, -1);
00180         if (authorityLen || pathLen)
00181             ParseAfterScheme(spec, specLen,
00182                              authorityPos, authorityLen,
00183                              pathPos, pathLen);
00184     }
00185     return NS_OK;
00186 }
00187 
00188 NS_IMETHODIMP
00189 nsBaseURLParser::ParseAuthority(const char *auth, PRInt32 authLen,
00190                                 PRUint32 *usernamePos, PRInt32 *usernameLen,
00191                                 PRUint32 *passwordPos, PRInt32 *passwordLen,
00192                                 PRUint32 *hostnamePos, PRInt32 *hostnameLen,
00193                                 PRInt32 *port)
00194 {
00195     NS_PRECONDITION(auth, "null pointer");
00196 
00197     if (authLen < 0)
00198         authLen = strlen(auth);
00199 
00200     SET_RESULT(username, 0, -1);
00201     SET_RESULT(password, 0, -1);
00202     SET_RESULT(hostname, 0, authLen);
00203     if (port)
00204        *port = -1;
00205     return NS_OK;
00206 }
00207 
00208 NS_IMETHODIMP
00209 nsBaseURLParser::ParseUserInfo(const char *userinfo, PRInt32 userinfoLen,
00210                                PRUint32 *usernamePos, PRInt32 *usernameLen,
00211                                PRUint32 *passwordPos, PRInt32 *passwordLen)
00212 {
00213     SET_RESULT(username, 0, -1);
00214     SET_RESULT(password, 0, -1);
00215     return NS_OK;
00216 }
00217 
00218 NS_IMETHODIMP
00219 nsBaseURLParser::ParseServerInfo(const char *serverinfo, PRInt32 serverinfoLen,
00220                                  PRUint32 *hostnamePos, PRInt32 *hostnameLen,
00221                                  PRInt32 *port)
00222 {
00223     SET_RESULT(hostname, 0, -1);
00224     if (port)
00225        *port = -1;
00226     return NS_OK;
00227 }
00228 
00229 NS_IMETHODIMP
00230 nsBaseURLParser::ParsePath(const char *path, PRInt32 pathLen,
00231                            PRUint32 *filepathPos, PRInt32 *filepathLen,
00232                            PRUint32 *paramPos, PRInt32 *paramLen,
00233                            PRUint32 *queryPos, PRInt32 *queryLen,
00234                            PRUint32 *refPos, PRInt32 *refLen)
00235 {
00236     NS_PRECONDITION(path, "null pointer");
00237 
00238     if (pathLen < 0)
00239         pathLen = strlen(path);
00240 
00241     // path = [/]<segment1>/<segment2>/<...>/<segmentN>;<param>?<query>#<ref>
00242 
00243     // XXX PL_strnpbrk would be nice, but it's buggy
00244 
00245     // search for first occurance of either ? or #
00246     const char *query_beg = 0, *query_end = 0;
00247     const char *ref_beg = 0;
00248     const char *p = 0;
00249     for (p = path; *p; ++p) {
00250         // only match the query string if it precedes the reference fragment
00251         if (!ref_beg && !query_beg && *p == '?')
00252             query_beg = p + 1;
00253         else if (*p == '#') {
00254             ref_beg = p + 1;
00255             if (query_beg)
00256                 query_end = p;
00257             break;
00258         }
00259     }
00260 
00261     if (query_beg) {
00262         if (query_end)
00263             SET_RESULT(query, query_beg - path, query_end - query_beg);
00264         else
00265             SET_RESULT(query, query_beg - path, pathLen - (query_beg - path));
00266     }
00267     else
00268         SET_RESULT(query, 0, -1);
00269 
00270     if (ref_beg)
00271         SET_RESULT(ref, ref_beg - path, pathLen - (ref_beg - path));
00272     else
00273         SET_RESULT(ref, 0, -1);
00274 
00275     // search backwards for param
00276     const char *param_beg = 0;
00277     const char *end;
00278     if (query_beg)
00279         end = query_beg - 1;
00280     else if (ref_beg)
00281         end = ref_beg - 1;
00282     else
00283         end = path + pathLen;
00284     for (p = end - 1; p >= path && *p != '/'; --p) {
00285         if (*p == ';') {
00286             // found param
00287             param_beg = p + 1;
00288         }
00289     }
00290 
00291     if (param_beg) {
00292         // found <filepath>;<param>
00293         SET_RESULT(param, param_beg - path, end - param_beg);
00294         end = param_beg - 1;
00295     }
00296     else
00297         SET_RESULT(param, 0, -1);
00298 
00299     // an empty file path is no file path
00300     if (end != path)
00301         SET_RESULT(filepath, 0, end - path);
00302     else
00303         SET_RESULT(filepath, 0, -1);
00304     return NS_OK;
00305 }
00306 
00307 NS_IMETHODIMP
00308 nsBaseURLParser::ParseFilePath(const char *filepath, PRInt32 filepathLen,
00309                                PRUint32 *directoryPos, PRInt32 *directoryLen,
00310                                PRUint32 *basenamePos, PRInt32 *basenameLen,
00311                                PRUint32 *extensionPos, PRInt32 *extensionLen)
00312 {
00313     NS_PRECONDITION(filepath, "null pointer");
00314 
00315     if (filepathLen < 0)
00316         filepathLen = strlen(filepath);
00317 
00318     if (filepathLen == 0) {
00319         SET_RESULT(directory, 0, -1);
00320         SET_RESULT(basename, 0, 0); // assume a zero length file basename
00321         SET_RESULT(extension, 0, -1);
00322         return NS_OK;
00323     }
00324 
00325     const char *p;
00326     const char *end = filepath + filepathLen;
00327 
00328     // search backwards for filename
00329     for (p = end - 1; *p != '/' && p > filepath; --p)
00330         ;
00331     if (*p == '/') {
00332         // catch /.. and /.
00333         if ((p+1 < end && *(p+1) == '.') && 
00334            (p+2 == end || (*(p+2) == '.' && p+3 == end)))
00335             p = end - 1;
00336         // filepath = <directory><filename>.<extension>
00337         SET_RESULT(directory, 0, p - filepath + 1);
00338         ParseFileName(p + 1, end - (p + 1),
00339                       basenamePos, basenameLen,
00340                       extensionPos, extensionLen);
00341         OFFSET_RESULT(basename, p + 1 - filepath);
00342         OFFSET_RESULT(extension, p + 1 - filepath);
00343     }
00344     else {
00345         // filepath = <filename>.<extension>
00346         SET_RESULT(directory, 0, -1);
00347         ParseFileName(filepath, filepathLen,
00348                       basenamePos, basenameLen,
00349                       extensionPos, extensionLen);
00350     }
00351     return NS_OK;
00352 }
00353 
00354 nsresult
00355 nsBaseURLParser::ParseFileName(const char *filename, PRInt32 filenameLen,
00356                                PRUint32 *basenamePos, PRInt32 *basenameLen,
00357                                PRUint32 *extensionPos, PRInt32 *extensionLen)
00358 {
00359     NS_PRECONDITION(filename, "null pointer");
00360 
00361     if (filenameLen < 0)
00362         filenameLen = strlen(filename);
00363 
00364     // no extension if filename ends with a '.'
00365     if (filename[filenameLen-1] != '.') {
00366         // ignore '.' at the beginning
00367         for (const char *p = filename + filenameLen - 1; p > filename; --p) {
00368             if (*p == '.') {
00369                 // filename = <basename.extension>
00370                 SET_RESULT(basename, 0, p - filename);
00371                 SET_RESULT(extension, p + 1 - filename, filenameLen - (p - filename + 1));
00372                 return NS_OK;
00373             }
00374         }
00375     }
00376     // filename = <basename>
00377     SET_RESULT(basename, 0, filenameLen);
00378     SET_RESULT(extension, 0, -1);
00379     return NS_OK;
00380 }
00381 
00382 //----------------------------------------------------------------------------
00383 // nsNoAuthURLParser implementation
00384 //----------------------------------------------------------------------------
00385 
00386 void
00387 nsNoAuthURLParser::ParseAfterScheme(const char *spec, PRInt32 specLen,
00388                                     PRUint32 *authPos, PRInt32 *authLen,
00389                                     PRUint32 *pathPos, PRInt32 *pathLen)
00390 {
00391     NS_PRECONDITION(specLen >= 0, "unexpected");
00392 
00393     // everything is the path
00394     PRUint32 pos = 0;
00395     switch (CountConsecutiveSlashes(spec, specLen)) {
00396     case 0:
00397     case 1:
00398         break;
00399     case 2:
00400         {
00401             const char *p = nsnull;
00402             if (specLen > 2) {
00403                 // looks like there is an authority section
00404 #if defined(XP_WIN) || defined(XP_OS2)
00405                 // if the authority looks like a drive number then we
00406                 // really want to treat it as part of the path
00407                 if ((specLen > 3) && (spec[3] == ':' || spec[3] == '|') &&
00408                     nsCRT::IsAsciiAlpha(spec[2]) &&
00409                     ((specLen == 4) || (spec[4] == '/') || (spec[4] == '\\'))) {
00410                     pos = 1;
00411                     break;  
00412                 } 
00413 #endif
00414                 p = (const char *) memchr(spec + 2, '/', specLen - 2);
00415             }
00416             if (p) {
00417                 SET_RESULT(auth, 2, p - (spec + 2));
00418                 SET_RESULT(path, p - spec, specLen - (p - spec));
00419             }
00420             else {
00421                 SET_RESULT(auth, 2, specLen - 2);
00422                 SET_RESULT(path, 0, -1);
00423             }
00424             return;
00425         }
00426     default:
00427         pos = 2;
00428         break;
00429     }
00430     SET_RESULT(auth, pos, 0);
00431     SET_RESULT(path, pos, specLen - pos);
00432 }
00433 
00434 #if defined(XP_WIN) || defined(XP_OS2)
00435 NS_IMETHODIMP
00436 nsNoAuthURLParser::ParseFilePath(const char *filepath, PRInt32 filepathLen,
00437                                  PRUint32 *directoryPos, PRInt32 *directoryLen,
00438                                  PRUint32 *basenamePos, PRInt32 *basenameLen,
00439                                  PRUint32 *extensionPos, PRInt32 *extensionLen)
00440 {
00441     NS_PRECONDITION(filepath, "null pointer");
00442 
00443     if (filepathLen < 0)
00444         filepathLen = strlen(filepath);
00445 
00446     // look for a filepath consisting of only a drive number, which may or
00447     // may not have a leading slash.
00448     if (filepathLen > 1 && filepathLen < 4) {
00449         const char *end = filepath + filepathLen;
00450         const char *p = filepath;
00451         if (*p == '/')
00452             p++;
00453         if ((end-p == 2) && (p[1]==':' || p[1]=='|') && nsCRT::IsAsciiAlpha(*p)) {
00454             // filepath = <drive-number>:
00455             SET_RESULT(directory, 0, filepathLen);
00456             SET_RESULT(basename, 0, -1);
00457             SET_RESULT(extension, 0, -1);
00458             return NS_OK;
00459         }
00460     }
00461 
00462     // otherwise fallback on common implementation
00463     return nsBaseURLParser::ParseFilePath(filepath, filepathLen,
00464                                           directoryPos, directoryLen,
00465                                           basenamePos, basenameLen,
00466                                           extensionPos, extensionLen);
00467 }
00468 #endif
00469 
00470 //----------------------------------------------------------------------------
00471 // nsAuthURLParser implementation
00472 //----------------------------------------------------------------------------
00473 
00474 NS_IMETHODIMP
00475 nsAuthURLParser::ParseAuthority(const char *auth, PRInt32 authLen,
00476                                 PRUint32 *usernamePos, PRInt32 *usernameLen,
00477                                 PRUint32 *passwordPos, PRInt32 *passwordLen,
00478                                 PRUint32 *hostnamePos, PRInt32 *hostnameLen,
00479                                 PRInt32 *port)
00480 {
00481     nsresult rv;
00482 
00483     NS_PRECONDITION(auth, "null pointer");
00484 
00485     if (authLen < 0)
00486         authLen = strlen(auth);
00487 
00488     if (authLen == 0) {
00489         SET_RESULT(username, 0, -1);
00490         SET_RESULT(password, 0, -1);
00491         SET_RESULT(hostname, 0, 0);
00492         if (port)
00493             *port = -1;
00494         return NS_OK;
00495     }
00496 
00497     // search backwards for @
00498     const char *p = auth + authLen - 1;
00499     for (; (*p != '@') && (p > auth); --p);
00500     if ( *p == '@' ) {
00501         // auth = <user-info@server-info>
00502         rv = ParseUserInfo(auth, p - auth,
00503                            usernamePos, usernameLen,
00504                            passwordPos, passwordLen);
00505         if (NS_FAILED(rv)) return rv;
00506         rv = ParseServerInfo(p + 1, authLen - (p - auth + 1),
00507                              hostnamePos, hostnameLen,
00508                              port);
00509         if (NS_FAILED(rv)) return rv;
00510         OFFSET_RESULT(hostname, p + 1 - auth);
00511     }
00512     else {
00513         // auth = <server-info>
00514         SET_RESULT(username, 0, -1);
00515         SET_RESULT(password, 0, -1);
00516         rv = ParseServerInfo(auth, authLen,
00517                              hostnamePos, hostnameLen,
00518                              port);
00519         if (NS_FAILED(rv)) return rv;
00520     }
00521     return NS_OK;
00522 }
00523 
00524 NS_IMETHODIMP
00525 nsAuthURLParser::ParseUserInfo(const char *userinfo, PRInt32 userinfoLen,
00526                                PRUint32 *usernamePos, PRInt32 *usernameLen,
00527                                PRUint32 *passwordPos, PRInt32 *passwordLen)
00528 {
00529     NS_PRECONDITION(userinfo, "null pointer");
00530 
00531     if (userinfoLen < 0)
00532         userinfoLen = strlen(userinfo);
00533 
00534     if (userinfoLen == 0) {
00535         SET_RESULT(username, 0, -1);
00536         SET_RESULT(password, 0, -1);
00537         return NS_OK;
00538     }
00539 
00540     const char *p = (const char *) memchr(userinfo, ':', userinfoLen);
00541     if (p) {
00542         // userinfo = <username:password>
00543         if (p == userinfo) {
00544             // must have a username!
00545             return NS_ERROR_MALFORMED_URI;
00546         }
00547         SET_RESULT(username, 0, p - userinfo);
00548         SET_RESULT(password, p - userinfo + 1, userinfoLen - (p - userinfo + 1));
00549     }
00550     else {
00551         // userinfo = <username>
00552         SET_RESULT(username, 0, userinfoLen);
00553         SET_RESULT(password, 0, -1);
00554     }
00555     return NS_OK;
00556 }
00557 
00558 NS_IMETHODIMP
00559 nsAuthURLParser::ParseServerInfo(const char *serverinfo, PRInt32 serverinfoLen,
00560                                  PRUint32 *hostnamePos, PRInt32 *hostnameLen,
00561                                  PRInt32 *port)
00562 {
00563     NS_PRECONDITION(serverinfo, "null pointer");
00564 
00565     if (serverinfoLen < 0)
00566         serverinfoLen = strlen(serverinfo);
00567 
00568     if (serverinfoLen == 0) {
00569         SET_RESULT(hostname, 0, 0);
00570         if (port)
00571             *port = -1;
00572         return NS_OK;
00573     }
00574 
00575     // search backwards for a ':' but stop on ']' (IPv6 address literal
00576     // delimiter).  check for illegal characters in the hostname.
00577     const char *p = serverinfo + serverinfoLen - 1;
00578     const char *colon = nsnull, *bracket = nsnull;
00579     for (; p > serverinfo; --p) {
00580         switch (*p) {
00581             case ']':
00582                 bracket = p;
00583                 break;
00584             case ':':
00585                 if (bracket == nsnull)
00586                     colon = p;
00587                 break;
00588             case ' ':
00589                 // hostname must not contain a space
00590                 NS_WARNING("malformed hostname");
00591                 return NS_ERROR_MALFORMED_URI;
00592         }
00593     }
00594 
00595     if (colon) {
00596         // serverinfo = <hostname:port>
00597         SET_RESULT(hostname, 0, colon - serverinfo);
00598         if (port) {
00599             // XXX unfortunately ToInteger is not defined for substrings
00600             nsCAutoString buf(colon+1, serverinfoLen - (colon + 1 - serverinfo));
00601             PRInt32 err;
00602            *port = buf.ToInteger(&err);
00603             if (NS_FAILED(err))
00604                *port = -1;
00605         }
00606     }
00607     else {
00608         // serverinfo = <hostname>
00609         SET_RESULT(hostname, 0, serverinfoLen);
00610         if (port)
00611            *port = -1;
00612     }
00613     return NS_OK;
00614 }
00615 
00616 void
00617 nsAuthURLParser::ParseAfterScheme(const char *spec, PRInt32 specLen,
00618                                   PRUint32 *authPos, PRInt32 *authLen,
00619                                   PRUint32 *pathPos, PRInt32 *pathLen)
00620 {
00621     NS_PRECONDITION(specLen >= 0, "unexpected");
00622 
00623     PRUint32 nslash = CountConsecutiveSlashes(spec, specLen);
00624 
00625     // search for the end of the authority section
00626     const char *end = spec + specLen;
00627     const char *p;
00628     for (p = spec + nslash; p < end; ++p) {
00629         if (*p == '/' || *p == '?' || *p == '#' || *p == ';')
00630             break;
00631     }
00632     if (p < end) {
00633         // spec = [/]<auth><path>
00634         SET_RESULT(auth, nslash, p - (spec + nslash));
00635         SET_RESULT(path, p - spec, specLen - (p - spec));
00636     }
00637     else {
00638         // spec = [/]<auth>
00639         SET_RESULT(auth, nslash, specLen - nslash);
00640         SET_RESULT(path, 0, -1);
00641     }
00642 }
00643 
00644 //----------------------------------------------------------------------------
00645 // nsStdURLParser implementation
00646 //----------------------------------------------------------------------------
00647 
00648 void
00649 nsStdURLParser::ParseAfterScheme(const char *spec, PRInt32 specLen,
00650                                  PRUint32 *authPos, PRInt32 *authLen,
00651                                  PRUint32 *pathPos, PRInt32 *pathLen)
00652 {
00653     NS_PRECONDITION(specLen >= 0, "unexpected");
00654 
00655     PRUint32 nslash = CountConsecutiveSlashes(spec, specLen);
00656 
00657     // search for the end of the authority section
00658     const char *end = spec + specLen;
00659     const char *p;
00660     for (p = spec + nslash; p < end; ++p) {
00661         if (strchr("/?#;", *p))
00662             break;
00663     }
00664     switch (nslash) {
00665     case 0:
00666     case 2:
00667         if (p < end) {
00668             // spec = (//)<auth><path>
00669             SET_RESULT(auth, nslash, p - (spec + nslash));
00670             SET_RESULT(path, p - spec, specLen - (p - spec));
00671         }
00672         else {
00673             // spec = (//)<auth>
00674             SET_RESULT(auth, nslash, specLen - nslash);
00675             SET_RESULT(path, 0, -1);
00676         }
00677         break;
00678     case 1:
00679         // spec = /<path>
00680         SET_RESULT(auth, 0, -1);
00681         SET_RESULT(path, 0, specLen);
00682         break;
00683     default:
00684         // spec = ///[/]<path>
00685         SET_RESULT(auth, 2, 0);
00686         SET_RESULT(path, 2, specLen - 2);
00687     }
00688 }