df9c4605116328ce24ff7a02a958cdca9325c433
1 #include <cxxtest/TestSuite.h>
2 #include "libnr/n-art-bpath.h"
3 #include "svg/svg.h"
4 #include "2geom/coord.h"
5 #include "prefs-utils.h"
6 #include "streq.h"
7 #include <string>
8 #include <vector>
9 #include <glib/gmem.h>
11 class SvgPathNRTest : public CxxTest::TestSuite
12 {
13 private:
14 std::vector<std::string> rectanglesAbsoluteClosed;
15 std::vector<std::string> rectanglesRelativeClosed;
16 std::vector<std::string> rectanglesAbsoluteOpen;
17 std::vector<std::string> rectanglesRelativeOpen;
18 NArtBpath rectangleBpath[5+1];
19 public:
20 SvgPathNRTest() {
21 // Lots of ways to define the same rectangle
22 rectanglesAbsoluteClosed.push_back("M 1,2 L 4,2 L 4,8 L 1,8 L 1,2 Z");
23 rectanglesAbsoluteClosed.push_back("M 1,2 L 4,2 L 4,8 L 1,8 z");
24 rectanglesAbsoluteClosed.push_back("M 1,2 4,2 4,8 1,8 z");
25 rectanglesAbsoluteClosed.push_back("M 1,2 H 4 V 8 H 1 z");
26 rectanglesRelativeClosed.push_back("m 1,2 l 3,0 l 0,6 l -3,0 z");
27 rectanglesRelativeClosed.push_back("m 1,2 3,0 0,6 -3,0 z");
28 rectanglesRelativeClosed.push_back("m 1,2 h 3 v 6 h -3 z");
29 rectanglesAbsoluteOpen.push_back("M 1,2 L 4,2 L 4,8 L 1,8 L 1,2");
30 rectanglesAbsoluteOpen.push_back("M 1,2 4,2 4,8 1,8 1,2");
31 rectanglesAbsoluteOpen.push_back("M 1,2 H 4 V 8 H 1 V 2");
32 rectanglesRelativeOpen.push_back("m 1,2 l 3,0 l 0,6 l -3,0 l 0,-6");
33 rectanglesRelativeOpen.push_back("m 1,2 3,0 0,6 -3,0 0,-6");
34 rectanglesRelativeOpen.push_back("m 1,2 h 3 v 6 h -3 v -6");
35 rectangleBpath[0].code = NR_MOVETO;
36 rectangleBpath[0].x3 = 1;
37 rectangleBpath[0].y3 = 2;
38 rectangleBpath[1].code = NR_LINETO;
39 rectangleBpath[1].x3 = 4;
40 rectangleBpath[1].y3 = 2;
41 rectangleBpath[2].code = NR_LINETO;
42 rectangleBpath[2].x3 = 4;
43 rectangleBpath[2].y3 = 8;
44 rectangleBpath[3].code = NR_LINETO;
45 rectangleBpath[3].x3 = 1;
46 rectangleBpath[3].y3 = 8;
47 rectangleBpath[4].code = NR_LINETO;
48 rectangleBpath[4].x3 = 1;
49 rectangleBpath[4].y3 = 2;
50 rectangleBpath[5].code = NR_END;
51 // TODO: Also test some (smooth) cubic/quadratic beziers and elliptical arcs
52 }
54 // createSuite and destroySuite get us per-suite setup and teardown
55 // without us having to worry about static initialization order, etc.
56 static SvgPathNRTest *createSuite() { return new SvgPathNRTest(); }
57 static void destroySuite( SvgPathNRTest *suite ) { delete suite; }
59 void testReadRectanglesAbsoluteClosed()
60 {
61 rectangleBpath[0].code = NR_MOVETO;
62 for(size_t i=0; i<rectanglesAbsoluteClosed.size(); i++) {
63 NArtBpath * bpath = sp_svg_read_path(rectanglesAbsoluteClosed[i].c_str());
64 TS_ASSERT(bpathEqual(bpath,rectangleBpath));
65 g_free(bpath);
66 }
67 }
69 void testReadRectanglesRelativeClosed()
70 {
71 rectangleBpath[0].code = NR_MOVETO;
72 for(size_t i=0; i<rectanglesRelativeClosed.size(); i++) {
73 NArtBpath * bpath = sp_svg_read_path(rectanglesRelativeClosed[i].c_str());
74 TS_ASSERT(bpathEqual(bpath,rectangleBpath));
75 g_free(bpath);
76 }
77 }
79 void testReadRectanglesAbsoluteOpen()
80 {
81 rectangleBpath[0].code = NR_MOVETO_OPEN;
82 for(size_t i=0; i<rectanglesAbsoluteOpen.size(); i++) {
83 NArtBpath * bpath = sp_svg_read_path(rectanglesAbsoluteOpen[i].c_str());
84 TS_ASSERT(bpathEqual(bpath,rectangleBpath));
85 g_free(bpath);
86 }
87 }
89 void testReadRectanglesRelativeOpen()
90 {
91 rectangleBpath[0].code = NR_MOVETO_OPEN;
92 for(size_t i=0; i<rectanglesRelativeOpen.size(); i++) {
93 NArtBpath * bpath = sp_svg_read_path(rectanglesRelativeOpen[i].c_str());
94 TS_ASSERT(bpathEqual(bpath,rectangleBpath));
95 g_free(bpath);
96 }
97 }
99 void testReadConcatenatedPaths()
100 {
101 NArtBpath bpath_good[4*5+1];
102 for(size_t i=0; i<4; i++) {
103 memcpy(bpath_good+i*5,rectangleBpath,sizeof(rectangleBpath[0])*5);
104 }
105 bpath_good[0*5].code = NR_MOVETO;
106 bpath_good[1*5].code = NR_MOVETO_OPEN;
107 bpath_good[2*5].code = NR_MOVETO;
108 bpath_good[3*5].code = NR_MOVETO_OPEN;
109 bpath_good[4*5].code = NR_END;
110 for(size_t i=0; i<5; i++) {
111 bpath_good[1*5+i].x3 += bpath_good[0*5+4].x3;
112 bpath_good[1*5+i].y3 += bpath_good[0*5+4].y3;
113 }
114 for(size_t i=0; i<5; i++) {
115 bpath_good[2*5+i].x3 += bpath_good[1*5+4].x3;
116 bpath_good[2*5+i].y3 += bpath_good[1*5+4].y3;
117 }
118 std::string path_str = rectanglesAbsoluteClosed[0] + rectanglesRelativeOpen[0] + rectanglesRelativeClosed[0] + rectanglesAbsoluteOpen[0];
119 NArtBpath * bpath = sp_svg_read_path(path_str.c_str());
120 TS_ASSERT(bpathEqual(bpath,bpath_good));
121 g_free(bpath);
122 }
124 void testReadZeroLengthSubpaths() {
125 // Per the SVG 1.1 specification (section F5) zero-length subpaths are relevant
126 NArtBpath bpath_good[8+1];
127 bpath_good[0].code = NR_MOVETO_OPEN;
128 bpath_good[0].x3 = bpath_good[0].y3 = 0;
129 bpath_good[1].code = NR_MOVETO_OPEN;
130 bpath_good[1].x3 = bpath_good[1].y3 = 1;
131 bpath_good[2].code = NR_LINETO;
132 bpath_good[2].x3 = bpath_good[2].y3 = 2;
133 bpath_good[3].code = NR_MOVETO;
134 bpath_good[3].x3 = bpath_good[3].y3 = 3;
135 bpath_good[4].code = NR_MOVETO;
136 bpath_good[4].x3 = bpath_good[4].y3 = 4;
137 bpath_good[5].code = NR_LINETO;
138 bpath_good[5].x3 = bpath_good[5].y3 = 5;
139 bpath_good[6].code = NR_LINETO;
140 bpath_good[6].x3 = bpath_good[6].y3 = 4;
141 bpath_good[7].code = NR_MOVETO_OPEN;
142 bpath_good[7].x3 = bpath_good[7].y3 = 6;
143 bpath_good[8].code = NR_END;
144 { // Test absolute version
145 char const * path_str = "M 0,0 M 1,1 L 2,2 M 3,3 z M 4,4 L 5,5 z M 6,6";
146 NArtBpath * bpath = sp_svg_read_path(path_str);
147 TS_ASSERT(bpathEqual(bpath,bpath_good));
148 g_free(bpath);
149 }
150 { // Test relative version
151 char const * path_str = "m 0,0 m 1,1 l 1,1 m 1,1 z m 1,1 l 1,1 z m 2,2";
152 NArtBpath * bpath = sp_svg_read_path(path_str);
153 TS_ASSERT(bpathEqual(bpath,bpath_good));
154 g_free(bpath);
155 }
156 }
158 void testReadImplicitMoveto() {
159 NArtBpath bpath_good[6+1];
160 bpath_good[0].code = NR_MOVETO;
161 bpath_good[0].x3 = bpath_good[0].y3 = 1;
162 bpath_good[1].code = NR_LINETO;
163 bpath_good[1].x3 = bpath_good[1].y3 = 2;
164 bpath_good[2].code = NR_LINETO;
165 bpath_good[2].x3 = bpath_good[2].y3 = 1;
166 bpath_good[3].code = NR_MOVETO;
167 bpath_good[3].x3 = bpath_good[3].y3 = 1;
168 bpath_good[4].code = NR_LINETO;
169 bpath_good[4].x3 = bpath_good[4].y3 = 3;
170 bpath_good[5].code = NR_LINETO;
171 bpath_good[5].x3 = bpath_good[5].y3 = 1;
172 bpath_good[6].code = NR_END;
173 { // Test absolute version
174 char const * path_str = "M 1,1 L 2,2 z L 3,3 z";
175 NArtBpath * bpath = sp_svg_read_path(path_str);
176 TS_ASSERT(bpathEqual(bpath,bpath_good));
177 g_free(bpath);
178 }
179 { // Test relative version
180 char const * path_str = "M 1,1 L 2,2 z L 3,3 z";
181 NArtBpath * bpath = sp_svg_read_path(path_str);
182 TS_ASSERT(bpathEqual(bpath,bpath_good));
183 g_free(bpath);
184 }
185 }
187 void testReadFloatingPoint() {
188 NArtBpath bpath_good[5+1];
189 bpath_good[0].code = NR_MOVETO;
190 bpath_good[0].x3 = .01;
191 bpath_good[0].y3 = .02;
192 bpath_good[1].code = NR_LINETO;
193 bpath_good[1].x3 = .04;
194 bpath_good[1].y3 = .02;
195 bpath_good[2].code = NR_LINETO;
196 bpath_good[2].x3 = 1.5;
197 bpath_good[2].y3 = 1.6;
198 bpath_good[3].code = NR_LINETO;
199 bpath_good[3].x3 = .01;
200 bpath_good[3].y3 = .08;
201 bpath_good[4].code = NR_LINETO;
202 bpath_good[4].x3 = .01;
203 bpath_good[4].y3 = .02;
204 bpath_good[5].code = NR_END;
205 { // Test decimals
206 char const * path_str = "M .01,.02 L.04.02 L1.5,1.6L0.01,0.08 .01.02 z";
207 NArtBpath * bpath = sp_svg_read_path(path_str);
208 TS_ASSERT(bpathEqual(bpath,bpath_good));
209 g_free(bpath);
210 }
211 { // Test exponent
212 char const * path_str = "M 1e-2,.2e-1 L 0.004e1,0.0002e+2 L0150E-2,1.6e0L1.0e-2,80e-3 z";
213 NArtBpath * bpath = sp_svg_read_path(path_str);
214 TS_ASSERT(bpathEqual(bpath,bpath_good));
215 g_free(bpath);
216 }
217 }
219 void testReadImplicitSeparation() {
220 // Coordinates need not be separated by whitespace if they can still be read unambiguously
221 NArtBpath bpath_good[5+1];
222 bpath_good[0].code = NR_MOVETO;
223 bpath_good[0].x3 = .1;
224 bpath_good[0].y3 = .2;
225 bpath_good[1].code = NR_LINETO;
226 bpath_good[1].x3 = .4;
227 bpath_good[1].y3 = .2;
228 bpath_good[2].code = NR_LINETO;
229 bpath_good[2].x3 = .4;
230 bpath_good[2].y3 = .8;
231 bpath_good[3].code = NR_LINETO;
232 bpath_good[3].x3 = .1;
233 bpath_good[3].y3 = .8;
234 bpath_good[4].code = NR_LINETO;
235 bpath_good[4].x3 = .1;
236 bpath_good[4].y3 = .2;
237 bpath_good[5].code = NR_END;
238 { // Test absolute
239 char const * path_str = "M .1.2+0.4.2e0.4e0+8e-1.1.8 z";
240 NArtBpath * bpath = sp_svg_read_path(path_str);
241 TS_ASSERT(bpathEqual(bpath,bpath_good));
242 g_free(bpath);
243 }
244 { // Test relative
245 char const * path_str = "m .1.2+0.3.0e0.0e0+6e-1-.3.0 z";
246 NArtBpath * bpath = sp_svg_read_path(path_str);
247 TS_ASSERT(bpathEqual(bpath,bpath_good));
248 g_free(bpath);
249 }
250 }
252 void testReadErrorMisplacedCharacter() {
253 char const * path_str;
254 NArtBpath * bpath;
255 NArtBpath * bpath_good = rectangleBpath;
256 bpath_good[0].code = NR_MOVETO;
257 // Comma in the wrong place (commas may only appear between parameters)
258 path_str = "M 1,2 4,2 4,8 1,8 z , m 13,15";
259 bpath = sp_svg_read_path(path_str);
260 TS_ASSERT(bpathEqual(bpath,bpath_good));
261 g_free(bpath);
262 // Comma in the wrong place (commas may only appear between parameters)
263 path_str = "M 1,2 4,2 4,8 1,8 z m,13,15";
264 bpath = sp_svg_read_path(path_str);
265 TS_ASSERT(bpathEqual(bpath,bpath_good));
266 g_free(bpath);
267 // Period in the wrong place (no numbers after a 'z')
268 path_str = "M 1,2 4,2 4,8 1,8 z . m 13,15";
269 bpath = sp_svg_read_path(path_str);
270 TS_ASSERT(bpathEqual(bpath,bpath_good));
271 g_free(bpath);
272 // Sign in the wrong place (no numbers after a 'z')
273 path_str = "M 1,2 4,2 4,8 1,8 z + - m 13,15";
274 bpath = sp_svg_read_path(path_str);
275 TS_ASSERT(bpathEqual(bpath,bpath_good));
276 g_free(bpath);
277 // Digit in the wrong place (no numbers after a 'z')
278 path_str = "M 1,2 4,2 4,8 1,8 z 9809 m 13,15";
279 bpath = sp_svg_read_path(path_str);
280 TS_ASSERT(bpathEqual(bpath,bpath_good));
281 g_free(bpath);
282 // Digit in the wrong place (no numbers after a 'z')
283 path_str = "M 1,2 4,2 4,8 1,8 z 9809 876 m 13,15";
284 bpath = sp_svg_read_path(path_str);
285 TS_ASSERT(bpathEqual(bpath,bpath_good));
286 g_free(bpath);
287 }
289 void testReadErrorUnrecognizedCharacter() {
290 char const * path_str;
291 NArtBpath * bpath;
292 NArtBpath * bpath_good = rectangleBpath;
293 bpath_good[0].code = NR_MOVETO;
294 // Unrecognized character
295 path_str = "M 1,2 4,2 4,8 1,8 z&m 13,15";
296 bpath = sp_svg_read_path(path_str);
297 TS_ASSERT(bpathEqual(bpath,bpath_good));
298 g_free(bpath);
299 // Unrecognized character
300 path_str = "M 1,2 4,2 4,8 1,8 z m &13,15";
301 bpath = sp_svg_read_path(path_str);
302 TS_ASSERT(bpathEqual(bpath,bpath_good));
303 g_free(bpath);
304 }
306 void testReadErrorTypo() {
307 char const * path_str;
308 NArtBpath * bpath;
309 NArtBpath * bpath_good = rectangleBpath;
310 bpath_good[0].code = NR_MOVETO;
311 // Typo
312 path_str = "M 1,2 4,2 4,8 1,8 z j 13,15";
313 bpath = sp_svg_read_path(path_str);
314 TS_ASSERT(bpathEqual(bpath,bpath_good));
315 g_free(bpath);
317 bpath_good[0].code = NR_MOVETO_OPEN;
318 // Typo
319 path_str = "M 1,2 4,2 4,8 1,8 L 1,2 x m 13,15";
320 bpath = sp_svg_read_path(path_str);
321 TS_ASSERT(bpathEqual(bpath,bpath_good));
322 g_free(bpath);
323 }
325 void testReadErrorIllformedNumbers() {
326 char const * path_str;
327 NArtBpath * bpath;
328 NArtBpath * bpath_good = rectangleBpath;
329 bpath_good[0].code = NR_MOVETO;
330 // Double exponent
331 path_str = "M 1,2 4,2 4,8 1,8 z m 13e4e5,15";
332 bpath = sp_svg_read_path(path_str);
333 TS_ASSERT(bpathEqual(bpath,bpath_good));
334 g_free(bpath);
335 // Double sign
336 path_str = "M 1,2 4,2 4,8 1,8 z m +-13,15";
337 bpath = sp_svg_read_path(path_str);
338 TS_ASSERT(bpathEqual(bpath,bpath_good));
339 g_free(bpath);
340 // Double sign
341 path_str = "M 1,2 4,2 4,8 1,8 z m 13e+-12,15";
342 bpath = sp_svg_read_path(path_str);
343 TS_ASSERT(bpathEqual(bpath,bpath_good));
344 g_free(bpath);
345 // No digit
346 path_str = "M 1,2 4,2 4,8 1,8 z m .e12,15";
347 bpath = sp_svg_read_path(path_str);
348 TS_ASSERT(bpathEqual(bpath,bpath_good));
349 g_free(bpath);
350 // No digit
351 path_str = "M 1,2 4,2 4,8 1,8 z m .,15";
352 bpath = sp_svg_read_path(path_str);
353 TS_ASSERT(bpathEqual(bpath,bpath_good));
354 g_free(bpath);
355 // No digit
356 path_str = "M 1,2 4,2 4,8 1,8 z m +,15";
357 bpath = sp_svg_read_path(path_str);
358 TS_ASSERT(bpathEqual(bpath,bpath_good));
359 g_free(bpath);
360 // No digit
361 path_str = "M 1,2 4,2 4,8 1,8 z m +.e+,15";
362 bpath = sp_svg_read_path(path_str);
363 TS_ASSERT(bpathEqual(bpath,bpath_good));
364 g_free(bpath);
365 }
367 void testReadErrorJunk() {
368 char const * path_str;
369 NArtBpath * bpath;
370 NArtBpath * bpath_good = rectangleBpath;
371 bpath_good[0].code = NR_MOVETO;
372 // Junk
373 path_str = "M 1,2 4,2 4,8 1,8 z j 357 hkjh.,34e34 90ih6kj4 h5k6vlh4N.,6,45wikuyi3yere..3487 m 13,23";
374 bpath = sp_svg_read_path(path_str);
375 TS_ASSERT(bpathEqual(bpath,bpath_good));
376 g_free(bpath);
377 }
379 void testReadErrorStopReading() {
380 char const * path_str;
381 NArtBpath * bpath;
382 NArtBpath * bpath_good = rectangleBpath;
383 bpath_good[0].code = NR_MOVETO;
384 // Unrecognized parameter
385 path_str = "M 1,2 4,2 4,8 1,8 z m #$%,23,34";
386 bpath = sp_svg_read_path(path_str);
387 TS_ASSERT(bpathEqual(bpath,bpath_good));
388 g_free(bpath);
389 // Invalid parameter
390 path_str = "M 1,2 4,2 4,8 1,8 z m #$%,23,34";
391 bpath = sp_svg_read_path(path_str);
392 TS_ASSERT(bpathEqual(bpath,bpath_good));
393 g_free(bpath);
394 // Illformed parameter
395 path_str = "M 1,2 4,2 4,8 1,8 z m +-12,23,34";
396 bpath = sp_svg_read_path(path_str);
397 TS_ASSERT(bpathEqual(bpath,bpath_good));
398 g_free(bpath);
400 bpath_good[0].code = NR_MOVETO_OPEN;
401 // "Third" parameter
402 path_str = "M 1,2 4,2 4,8 1,8 1,2,3 M 12,23";
403 bpath = sp_svg_read_path(path_str);
404 TS_ASSERT(bpathEqual(bpath,bpath_good));
405 g_free(bpath);
406 }
408 void testRoundTrip() {
409 // This is the easiest way to (also) test writing path data, as a path can be written in more than one way.
410 NArtBpath * bpath;
411 NArtBpath * new_bpath;
412 char * path_str;
413 // Rectangle (closed)
414 bpath = sp_svg_read_path(rectanglesAbsoluteClosed[0].c_str());
415 path_str = sp_svg_write_path(bpath);
416 new_bpath = sp_svg_read_path(path_str);
417 TS_ASSERT(bpathEqual(bpath,new_bpath));
418 g_free(bpath); g_free(path_str); g_free(new_bpath);
419 // Rectangle (open)
420 bpath = sp_svg_read_path(rectanglesAbsoluteOpen[0].c_str());
421 path_str = sp_svg_write_path(bpath);
422 new_bpath = sp_svg_read_path(path_str);
423 TS_ASSERT(bpathEqual(bpath,new_bpath));
424 g_free(bpath); g_free(path_str); g_free(new_bpath);
425 // Concatenated rectangles
426 bpath = sp_svg_read_path((rectanglesAbsoluteClosed[0] + rectanglesRelativeOpen[0] + rectanglesRelativeClosed[0] + rectanglesAbsoluteOpen[0]).c_str());
427 path_str = sp_svg_write_path(bpath);
428 new_bpath = sp_svg_read_path(path_str);
429 TS_ASSERT(bpathEqual(bpath,new_bpath));
430 g_free(bpath); g_free(path_str); g_free(new_bpath);
431 // Zero-length subpaths
432 bpath = sp_svg_read_path("M 0,0 M 1,1 L 2,2 M 3,3 z M 4,4 L 5,5 z M 6,6");
433 path_str = sp_svg_write_path(bpath);
434 new_bpath = sp_svg_read_path(path_str);
435 TS_ASSERT(bpathEqual(bpath,new_bpath));
436 g_free(bpath); g_free(path_str); g_free(new_bpath);
437 // Floating-point
438 bpath = sp_svg_read_path("M .01,.02 L 0.04,0.02 L.04,.08L0.01,0.08 z""M 1e-2,.2e-1 L 0.004e1,0.0002e+2 L04E-2,.08e0L1.0e-2,80e-3 z");
439 path_str = sp_svg_write_path(bpath);
440 new_bpath = sp_svg_read_path(path_str);
441 TS_ASSERT(bpathEqual(bpath, new_bpath, 1e-17));
442 g_free(bpath); g_free(path_str); g_free(new_bpath);
443 }
445 void testMinexpPrecision() {
446 NArtBpath * bpath;
447 char * path_str;
448 // Default values
449 prefs_set_int_attribute("options.svgoutput", "allowrelativecoordinates", 1);
450 prefs_set_int_attribute("options.svgoutput", "forcerepeatcommands", 0);
451 prefs_set_int_attribute("options.svgoutput", "numericprecision", 8);
452 prefs_set_int_attribute("options.svgoutput", "minimumexponent", -8);
453 bpath = sp_svg_read_path("M 123456781,1.23456781e-8 L 123456782,1.23456782e-8 L 123456785,1.23456785e-8 L 10123456400,1.23456785e-8 L 123456789,1.23456789e-8 L 123456789,101.234564e-8 L 123456789,1.23456789e-8");
454 path_str = sp_svg_write_path(bpath);
455 TS_ASSERT_RELATION( streq_rel , "m 123456780,1.2345678e-8 0,0 10,1e-15 9999999210,0 -9999999210,0 0,9.99999921e-7 0,-9.99999921e-7" , path_str );
456 g_free(bpath); g_free(path_str);
457 }
459 private:
460 bool bpathEqual(NArtBpath const * a, NArtBpath const * b, double eps = 1e-16) {
461 while(a->code != NR_END && b->code == a->code) {
462 switch(a->code) {
463 case NR_MOVETO:
464 case NR_MOVETO_OPEN:
465 case NR_LINETO:
466 if (!Geom::are_near(a->x3,b->x3, eps) || !Geom::are_near(a->y3,b->y3, eps)) return false;
467 break;
468 case NR_CURVETO:
469 if (!Geom::are_near(a->x1,b->x1, eps) || !Geom::are_near(a->y1,b->y1, eps)) return false;
470 if (!Geom::are_near(a->x2,b->x2, eps) || !Geom::are_near(a->y2,b->y2, eps)) return false;
471 if (!Geom::are_near(a->x3,b->x3, eps) || !Geom::are_near(a->y3,b->y3, eps)) return false;
472 break;
473 default:
474 TS_FAIL("Unknown path code!");
475 }
476 a++;
477 b++;
478 }
479 return a->code == b->code;
480 }
481 };
484 /*
485 Local Variables:
486 mode:c++
487 c-file-style:"stroustrup"
488 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
489 indent-tabs-mode:nil
490 fill-column:99
491 End:
492 */
493 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :