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         }
345     newUri.normalize();
346     return newUri;
350 /**
351  *  This follows the Java URI algorithm:
352  *   1. All "." segments are removed.
353  *   2. If a ".." segment is preceded by a non-".." segment
354  *          then both of these segments are removed. This step
355  *          is repeated until it is no longer applicable.
356  *   3. If the path is relative, and if its first segment
357  *          contains a colon character (':'), then a "." segment
358  *          is prepended. This prevents a relative URI with a path
359  *          such as "a:b/c/d" from later being re-parsed as an
360  *          opaque URI with a scheme of "a" and a scheme-specific
361  *          part of "b/c/d". (Deviation from RFC 2396)
362  */
363 void URI::normalize()
365     std::vector<DOMString> segments;
367     //## Collect segments
368     if (path.size()<2)
369         return;
370     bool abs = false;
371     unsigned int pos=0;
372     if (path[0]=='/')
373         {
374         abs = true;
375         pos++;
376         }
377     while (pos < path.size())
378         {
379         unsigned int pos2 = path.find('/', pos);
380         if (pos2==path.npos)
381             {
382             DOMString seg = path.substr(pos);
383             //printf("last segment:%s\n", seg.c_str());
384             segments.push_back(seg);
385             break;
386             }
387         if (pos2>pos)
388             {
389             DOMString seg = path.substr(pos, pos2-pos);
390             //printf("segment:%s\n", seg.c_str());
391             segments.push_back(seg);
392             }
393         pos = pos2;
394         pos++;
395         }
397     //## Clean up (normalize) segments
398     bool edited = false;
399     std::vector<DOMString>::iterator iter;
400     for (iter=segments.begin() ; iter!=segments.end() ; )
401         {
402         DOMString s = *iter;
403         if (s == ".")
404             {
405             iter = segments.erase(iter);
406             edited = true;
407             }
408         else if (s == ".." &&
409                  iter != segments.begin() &&
410                  *(iter-1) != "..")
411             {
412             iter--; //back up, then erase two entries
413             iter = segments.erase(iter);
414             iter = segments.erase(iter);
415             edited = true;
416             }
417         else
418             iter++;
419         }
421     //## Rebuild path, if necessary
422     if (edited)
423         {
424         path.clear();
425         if (abs)
426             {
427             path.append("/");
428             }
429         std::vector<DOMString>::iterator iter;
430         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
431             {
432             if (iter != segments.begin())
433                 path.append("/");
434             path.append(*iter);
435             }
436         }
442 //#########################################################################
443 //# M E S S A G E S
444 //#########################################################################
446 void URI::error(const char *fmt, ...)
448     va_list args;
449     fprintf(stderr, "URI error: ");
450     va_start(args, fmt);
451     vfprintf(stderr, fmt, args);
452     va_end(args);
453     fprintf(stderr, "\n");
456 void URI::trace(const char *fmt, ...)
458     va_list args;
459     fprintf(stdout, "URI: ");
460     va_start(args, fmt);
461     vfprintf(stdout, fmt, args);
462     va_end(args);
463     fprintf(stdout, "\n");
468 //#########################################################################
469 //# P A R S I N G
470 //#########################################################################
474 int URI::peek(int p)
476     if (p<0 || p>=parselen)
477         return -1;
478     return parsebuf[p];
483 int URI::match(int p0, char *key)
485     int p = p0;
486     while (p < parselen)
487         {
488         if (*key == '\0')
489             return p;
490         else if (*key != parsebuf[p])
491             break;
492         p++; key++;
493         }
494     return p0;
497 //#########################################################################
498 //#  Parsing is performed according to:
499 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
500 //#########################################################################
502 int URI::parseScheme(int p0)
504     int p = p0;
505     for (LookupEntry *entry = schemes; entry->sval ; entry++)
506         {
507         int p2 = match(p, entry->sval);
508         if (p2 > p)
509             {
510             schemeStr = entry->sval;
511             scheme    = entry->ival;
512             port      = entry->port;
513             p = p2;
514             return p;
515             }
516         }
518     return p;
522 int URI::parseHierarchicalPart(int p0)
524     int p = p0;
525     int ch;
527     //# Authority field (host and port, for example)
528     int p2 = match(p, "//");
529     if (p2 > p)
530         {
531         p = p2;
532         portSpecified = false;
533         DOMString portStr;
534         while (p < parselen)
535             {
536             ch = peek(p);
537             if (ch == '/')
538                 break;
539             else if (ch == ':')
540                 portSpecified = true;
541             else if (portSpecified)
542                 portStr.push_back((XMLCh)ch);
543             else
544                 authority.push_back((XMLCh)ch);
545             p++;
546             }
547         if (portStr.size() > 0)
548             {
549             char *pstr = (char *)portStr.c_str();
550             char *endStr;
551             long val = strtol(pstr, &endStr, 10);
552             if (endStr > pstr) //successful parse?
553                 port = val;
554             }
555         }
557     //# Are we absolute?
558     ch = peek(p);
559     if (isLetter(ch) && peek(p+1)==':')
560         {
561         absolute = true;
562         path.push_back((XMLCh)'/');
563         }
564     else if (ch == '/')
565         {
566         absolute = true;
567         if (p>p0) //in other words, if '/' is not the first char
568             opaque = true;
569         path.push_back((XMLCh)ch);
570         p++;
571         }
573     while (p < parselen)
574         {
575         ch = peek(p);
576         if (ch == '?' || ch == '#')
577             break;
578         path.push_back((XMLCh)ch);
579         p++;
580         }
582     return p;
585 int URI::parseQuery(int p0)
587     int p = p0;
588     int ch = peek(p);
589     if (ch != '?')
590         return p0;
592     p++;
593     while (p < parselen)
594         {
595         ch = peek(p);
596         if (ch == '#')
597             break;
598         query.push_back((XMLCh)ch);
599         p++;
600         }
603     return p;
606 int URI::parseFragment(int p0)
609     int p = p0;
610     int ch = peek(p);
611     if (ch != '#')
612         return p0;
614     p++;
615     while (p < parselen)
616         {
617         ch = peek(p);
618         if (ch == '?')
619             break;
620         fragment.push_back((XMLCh)ch);
621         p++;
622         }
625     return p;
629 int URI::parse(int p0)
632     int p = p0;
634     int p2 = parseScheme(p);
635     if (p2 < 0)
636         {
637         error("Scheme");
638         return -1;
639         }
640     p = p2;
643     p2 = parseHierarchicalPart(p);
644     if (p2 < 0)
645         {
646         error("Hierarchical part");
647         return -1;
648         }
649     p = p2;
651     p2 = parseQuery(p);
652     if (p2 < 0)
653         {
654         error("Query");
655         return -1;
656         }
657     p = p2;
660     p2 = parseFragment(p);
661     if (p2 < 0)
662         {
663         error("Fragment");
664         return -1;
665         }
666     p = p2;
668     return p;
674 bool URI::parse(const DOMString &str)
677     parselen = str.size();
679     DOMString tmp;
680     for (unsigned int i=0 ; i<str.size() ; i++)
681         {
682         XMLCh ch = (XMLCh) str[i];
683         if (ch == '\\')
684             tmp.push_back((XMLCh)'/');
685         else
686             tmp.push_back(ch);
687         }
688     parsebuf = (char *) tmp.c_str();
691     int p = parse(0);
692     normalize();
694     if (p < 0)
695         {
696         error("Syntax error");
697         return false;
698         }
700     //printf("uri:%s\n", toString().c_str());
701     //printf("path:%s\n", path.c_str());
703     return true;
711 }  //namespace dom
712 }  //namespace w3c
713 }  //namespace org
714 //#########################################################################
715 //# E N D    O F    F I L E
716 //#########################################################################