Code

Native path support
[inkscape.git] / src / dom / uri.cpp
1 /**
2  * Phoebe DOM Implementation.
3  *
4  * This is a C++ approximation of the W3C DOM model, which follows
5  * fairly closely the specifications in the various .idl files, copies of
6  * which are provided for reference.  Most important is this one:
7  *
8  * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html
9  *
10  * Authors:
11  *   Bob Jamison
12  *
13  * Copyright (C) 2005 Bob Jamison
14  *
15  *  This library is free software; you can redistribute it and/or
16  *  modify it under the terms of the GNU Lesser General Public
17  *  License as published by the Free Software Foundation; either
18  *  version 2.1 of the License, or (at your option) any later version.
19  *
20  *  This library is distributed in the hope that it will be useful,
21  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23  *  Lesser General Public License for more details.
24  *
25  *  You should have received a copy of the GNU Lesser General Public
26  *  License along with this library; if not, write to the Free Software
27  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
28  */
33 #include "uri.h"
34 #include "charclass.h"
36 #include <stdio.h>
37 #include <stdarg.h>
41 namespace org
42 {
43 namespace w3c
44 {
45 namespace dom
46 {
49 typedef struct
50 {
51     int  ival;
52     char *sval;
53     int  port;
54 } LookupEntry;
56 LookupEntry schemes[] =
57 {
58     { URI::SCHEME_DATA,   "data:",    0 },
59     { URI::SCHEME_HTTP,   "http:",   80 },
60     { URI::SCHEME_HTTPS,  "https:", 443 },
61     { URI::SCHEME_FTP,    "ftp",     12 },
62     { URI::SCHEME_FILE,   "file:",    0 },
63     { URI::SCHEME_LDAP,   "ldap:",  123 },
64     { URI::SCHEME_MAILTO, "mailto:", 25 },
65     { URI::SCHEME_NEWS,   "news:",  117 },
66     { URI::SCHEME_TELNET, "telnet:", 23 },
67     { 0,                  NULL,       0 }
68 };
72 //#########################################################################
73 //# C O N S T R U C T O R
74 //#########################################################################
76 /**
77  *
78  */
79 URI::URI()
80 {
81     init();
82 }
84 /**
85  *
86  */
87 URI::URI(const DOMString &str)
88 {
89     init();
90     parse(str);
91 }
94 /**
95  *
96  */
97 URI::URI(const char *str)
98 {
99     init();
100     DOMString domStr = str;
101     parse(domStr);
105 /**
106  *
107  */
108 URI::URI(const URI &other)
110     init();
111     assign(other);
115 /**
116  *
117  */
118 URI &URI::operator=(const URI &other)
120     init();
121     assign(other);
122     return *this;
126 /**
127  *
128  */
129 URI::~URI()
137 /**
138  *
139  */
140 void URI::init()
142     parsebuf  = NULL;
143     parselen  = 0;
144     scheme    = SCHEME_NONE;
145     schemeStr = "";
146     port      = 0;
147     authority = "";
148     path      = "";
149     absolute  = false;
150     opaque    = false;
151     query     = "";
152     fragment  = "";
156 /**
157  *
158  */
159 void URI::assign(const URI &other)
161     scheme    = other.scheme;
162     schemeStr = other.schemeStr;
163     authority = other.authority;
164     port      = other.port;
165     path      = other.path;
166     absolute  = other.absolute;
167     opaque    = other.opaque;
168     query     = other.query;
169     fragment  = other.fragment;
173 //#########################################################################
174 //#A T T R I B U T E S
175 //#########################################################################
177 DOMString URI::toString() const
179     DOMString str = schemeStr;
180     if (authority.size() > 0)
181         {
182         str.append("//");
183         str.append(authority);
184         }
185     str.append(path);
186     if (query.size() > 0)
187         {
188         str.append("?");
189         str.append(query);
190         }
191     if (fragment.size() > 0)
192         {
193         str.append("#");
194         str.append(fragment);
195         }
196     return str;
200 int URI::getScheme() const
202     return scheme;
205 DOMString URI::getSchemeStr() const
207     return schemeStr;
211 DOMString URI::getAuthority() const
213     DOMString ret = authority;
214     if (portSpecified && port>=0)
215         {
216         char buf[7];
217         snprintf(buf, 6, ":%6d", port);
218         ret.append(buf);
219         }
220     return ret;
223 DOMString URI::getHost() const
225     return authority;
228 int URI::getPort() const
230     return port;
234 DOMString URI::getPath() const
236     return path;
239 DOMString URI::getNativePath() const
241     DOMString npath;
242 #ifdef __WIN32__
243     unsigned int firstChar = 0;
244     if (path.size() >= 3)
245         {
246         if (path[0] == '/' &&
247             isLetter(path[1]) &&
248             path[2] == ':')
249             firstChar++;
250          }
251     for (unsigned int i=firstChar ; i<path.size() ; i++)
252         {
253         XMLCh ch = (XMLCh) path[i];
254         if (ch == '/')
255             npath.push_back((XMLCh)'\\');
256         else
257             npath.push_back(ch);
258         }
259 #else
260     npath = path;
261 #endif
262     return npath;
266 bool URI::isAbsolute() const
268     return absolute;
271 bool URI::isOpaque() const
273     return opaque;
277 DOMString URI::getQuery() const
279     return query;
283 DOMString URI::getFragment() const
285     return fragment;
289 URI URI::resolve(const URI &other) const
291     //### According to w3c, this is handled in 3 cases
293     //## 1
294     if (opaque || other.isAbsolute())
295         return other;
297     //## 2
298     if (other.fragment.size()  >  0 &&
299         other.path.size()      == 0 &&
300         other.scheme           == SCHEME_NONE &&
301         other.authority.size() == 0 &&
302         other.query.size()     == 0 )
303         {
304         URI fragUri = *this;
305         fragUri.fragment = other.fragment;
306         return fragUri;
307         }
309     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
310     URI newUri;
311     //# 3.1
312     newUri.scheme    = scheme;
313     newUri.schemeStr = schemeStr;
314     newUri.query     = other.query;
315     newUri.fragment  = other.fragment;
316     if (other.authority.size() > 0)
317         {
318         //# 3.2
319         if (absolute || other.absolute)
320             newUri.absolute = true;
321         newUri.authority = other.authority;
322         newUri.port      = other.port;//part of authority
323         newUri.path      = other.path;
324         }
325     else
326         {
327         //# 3.3
328         if (other.absolute)
329             {
330             newUri.absolute = true;
331             newUri.path     = other.path;
332             }
333         else
334             {
335             unsigned int pos = path.rfind('/');
336             if (pos != path.npos)
337                 {
338                 DOMString tpath = path.substr(0, pos+1);
339                 tpath.append(other.path);
340                 newUri.path = tpath;
341                 }
342             }
343         }
344     newUri.normalize();
345     return newUri;
349 /**
350  *  This follows the Java URI algorithm:
351  *   1. All "." segments are removed.
352  *   2. If a ".." segment is preceded by a non-".." segment
353  *          then both of these segments are removed. This step
354  *          is repeated until it is no longer applicable.
355  *   3. If the path is relative, and if its first segment
356  *          contains a colon character (':'), then a "." segment
357  *          is prepended. This prevents a relative URI with a path
358  *          such as "a:b/c/d" from later being re-parsed as an
359  *          opaque URI with a scheme of "a" and a scheme-specific
360  *          part of "b/c/d". (Deviation from RFC 2396)
361  */
362 void URI::normalize()
364     std::vector<DOMString> segments;
366     //## Collect segments
367     if (path.size()<2)
368         return;
369     bool abs = false;
370     unsigned int pos=0;
371     if (path[0]=='/')
372         {
373         abs = true;
374         pos++;
375         }
376     while (pos < path.size())
377         {
378         unsigned int pos2 = path.find('/', pos);
379         if (pos2==path.npos)
380             {
381             DOMString seg = path.substr(pos);
382             //printf("last segment:%s\n", seg.c_str());
383             segments.push_back(seg);
384             break;
385             }
386         if (pos2>pos)
387             {
388             DOMString seg = path.substr(pos, pos2-pos);
389             //printf("segment:%s\n", seg.c_str());
390             segments.push_back(seg);
391             }
392         pos = pos2;
393         pos++;
394         }
396     //## Clean up (normalize) segments
397     bool edited = false;
398     std::vector<DOMString>::iterator iter;
399     for (iter=segments.begin() ; iter!=segments.end() ; )
400         {
401         DOMString s = *iter;
402         if (s == ".")
403             {
404             iter = segments.erase(iter);
405             edited = true;
406             }
407         else if (s == ".." &&
408                  iter != segments.begin() &&
409                  *(iter-1) != "..")
410             {
411             iter--; //back up, then erase two entries
412             iter = segments.erase(iter);
413             iter = segments.erase(iter);
414             edited = true;
415             }
416         else
417             iter++;
418         }
420     //## Rebuild path, if necessary
421     if (edited)
422         {
423         path.clear();
424         if (abs)
425             {
426             path.append("/");
427             }
428         std::vector<DOMString>::iterator iter;
429         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
430             {
431             if (iter != segments.begin())
432                 path.append("/");
433             path.append(*iter);
434             }
435         }
441 //#########################################################################
442 //# M E S S A G E S
443 //#########################################################################
445 void URI::error(const char *fmt, ...)
447     va_list args;
448     fprintf(stderr, "URI error: ");
449     va_start(args, fmt);
450     vfprintf(stderr, fmt, args);
451     va_end(args);
452     fprintf(stderr, "\n");
455 void URI::trace(const char *fmt, ...)
457     va_list args;
458     fprintf(stdout, "URI: ");
459     va_start(args, fmt);
460     vfprintf(stdout, fmt, args);
461     va_end(args);
462     fprintf(stdout, "\n");
467 //#########################################################################
468 //# P A R S I N G
469 //#########################################################################
473 int URI::peek(int p)
475     if (p<0 || p>=parselen)
476         return -1;
477     return parsebuf[p];
482 int URI::match(int p0, char *key)
484     int p = p0;
485     while (p < parselen)
486         {
487         if (*key == '\0')
488             return p;
489         else if (*key != parsebuf[p])
490             break;
491         p++; key++;
492         }
493     return p0;
496 //#########################################################################
497 //#  Parsing is performed according to:
498 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
499 //#########################################################################
501 int URI::parseScheme(int p0)
503     int p = p0;
504     for (LookupEntry *entry = schemes; entry->sval ; entry++)
505         {
506         int p2 = match(p, entry->sval);
507         if (p2 > p)
508             {
509             schemeStr = entry->sval;
510             scheme    = entry->ival;
511             port      = entry->port;
512             p = p2;
513             return p;
514             }
515         }
517     return p;
521 int URI::parseHierarchicalPart(int p0)
523     int p = p0;
524     int ch;
526     //# Authority field (host and port, for example)
527     int p2 = match(p, "//");
528     if (p2 > p)
529         {
530         p = p2;
531         portSpecified = false;
532         DOMString portStr;
533         while (p < parselen)
534             {
535             ch = peek(p);
536             if (ch == '/')
537                 break;
538             else if (ch == ':')
539                 portSpecified = true;
540             else if (portSpecified)
541                 portStr.push_back((XMLCh)ch);
542             else
543                 authority.push_back((XMLCh)ch);
544             p++;
545             }
546         if (portStr.size() > 0)
547             {
548             char *pstr = (char *)portStr.c_str();
549             char *endStr;
550             long val = strtol(pstr, &endStr, 10);
551             if (endStr > pstr) //successful parse?
552                 port = val;
553             }
554         }
556     //# Are we absolute?
557     ch = peek(p);
558     if (isLetter(ch) && peek(p+1)==':')
559         {
560         absolute = true;
561         path.push_back((XMLCh)'/');
562         }
563     else if (ch == '/')
564         {
565         absolute = true;
566         if (p>p0) //in other words, if '/' is not the first char
567             opaque = true;
568         path.push_back((XMLCh)ch);
569         p++;
570         }
572     while (p < parselen)
573         {
574         ch = peek(p);
575         if (ch == '?' || ch == '#')
576             break;
577         path.push_back((XMLCh)ch);
578         p++;
579         }
581     return p;
584 int URI::parseQuery(int p0)
586     int p = p0;
587     int ch = peek(p);
588     if (ch != '?')
589         return p0;
591     p++;
592     while (p < parselen)
593         {
594         ch = peek(p);
595         if (ch == '#')
596             break;
597         query.push_back((XMLCh)ch);
598         p++;
599         }
602     return p;
605 int URI::parseFragment(int p0)
608     int p = p0;
609     int ch = peek(p);
610     if (ch != '#')
611         return p0;
613     p++;
614     while (p < parselen)
615         {
616         ch = peek(p);
617         if (ch == '?')
618             break;
619         fragment.push_back((XMLCh)ch);
620         p++;
621         }
624     return p;
628 int URI::parse(int p0)
631     int p = p0;
633     int p2 = parseScheme(p);
634     if (p2 < 0)
635         {
636         error("Scheme");
637         return -1;
638         }
639     p = p2;
642     p2 = parseHierarchicalPart(p);
643     if (p2 < 0)
644         {
645         error("Hierarchical part");
646         return -1;
647         }
648     p = p2;
650     p2 = parseQuery(p);
651     if (p2 < 0)
652         {
653         error("Query");
654         return -1;
655         }
656     p = p2;
659     p2 = parseFragment(p);
660     if (p2 < 0)
661         {
662         error("Fragment");
663         return -1;
664         }
665     p = p2;
667     return p;
673 bool URI::parse(const DOMString &str)
676     parselen = str.size();
678     DOMString tmp;
679     for (unsigned int i=0 ; i<str.size() ; i++)
680         {
681         XMLCh ch = (XMLCh) str[i];
682         if (ch == '\\')
683             tmp.push_back((XMLCh)'/');
684         else
685             tmp.push_back(ch);
686         }
687     parsebuf = (char *) tmp.c_str();
690     int p = parse(0);
691     normalize();
693     if (p < 0)
694         {
695         error("Syntax error");
696         return false;
697         }
699     //printf("uri:%s\n", toString().c_str());
700     //printf("path:%s\n", path.c_str());
702     return true;
710 }  //namespace dom
711 }  //namespace w3c
712 }  //namespace org
713 //#########################################################################
714 //# E N D    O F    F I L E
715 //#########################################################################