Code

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