/[thuban]/branches/WIP-pyshapelib-Unicode/thuban/libraries/pyshapelib/shapelibmodule.c
ViewVC logotype

Annotation of /branches/WIP-pyshapelib-Unicode/thuban/libraries/pyshapelib/shapelibmodule.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2742 - (hide annotations)
Wed Mar 14 16:26:14 2007 UTC (17 years, 11 months ago) by bramz
Original Path: branches/WIP-pyshapelib-bramz/libraries/pyshapelib/shapelibmodule.c
File MIME type: text/plain
File size: 15365 byte(s)
pyshapelib: rewritten dbflib to use hand-crafted Python bindings instead of SWIG generated ones.

1 bramz 2735 #include "pyshapelib_common.h"
2 jan 1611
3 bramz 2735 /* --- SHPObject ----------------------------------------------------------------------------------------------------- */
4 jan 1611
5 bramz 2735 typedef struct
6     {
7     PyObject_HEAD
8     SHPObject* shpObject;
9     }
10 bramz 2742 SHPObjectObject;
11 jan 1611
12 bramz 2741 /* allocator
13     */
14 bramz 2742 static PyObject* shpobject_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
15 bramz 2735 {
16 bramz 2742 SHPObjectObject* self;
17     self = (SHPObjectObject*) type->tp_alloc(type, 0);
18 bramz 2735 self->shpObject = NULL;
19     return (PyObject*) self;
20     }
21 jan 1611
22 bramz 2741 /* deallocator
23     */
24 bramz 2742 static void shpobject_dealloc(SHPObjectObject* self)
25 bramz 2735 {
26     SHPDestroyObject(self->shpObject);
27     self->shpObject = NULL;
28     self->ob_type->tp_free((PyObject*)self);
29     }
30 jan 1611
31 bramz 2735 /* The constructor of SHPObject. parts is a list of lists of tuples
32     * describing the parts and their vertices just likethe output of the
33     * vertices() method. part_type_list is the list of part-types and may
34     * be NULL. For the meaning of the part-types and their default value
35     * see the Shaplib documentation.
36     */
37 bramz 2742 static int shpobject_init(SHPObjectObject* self, PyObject* args, PyObject* kwds)
38 bramz 2735 {
39     int type;
40     int id;
41     PyObject* parts = NULL;
42     PyObject* part_type_list = NULL;
43    
44     int num_parts;
45     int num_vertices;
46     int part_start;
47    
48     double* xs = NULL;
49     double* ys = NULL;
50     int* part_starts = NULL;
51     int* part_types = NULL;
52    
53     int i;
54     int return_code = -1;
55    
56     /* first, unpack parameters */
57     if (kwds != NULL && PyDict_Size(kwds) > 0)
58     {
59     PyErr_Format(PyExc_TypeError, "shapelib.SHPObject.__init__ takes no keyword arguments");
60     return -1;
61     }
62     if (!PyArg_ParseTuple(args, "iiO|O", &type, &id, &parts, &part_type_list)) return -1;
63    
64     if (!PySequence_Check(parts))
65     {
66     PyErr_SetString(PyExc_TypeError, "parts is not a sequence");
67     return -1;
68     }
69     num_parts = PySequence_Length(parts);
70     if (num_parts < 0)
71     {
72     PyErr_SetString(PyExc_TypeError, "cannot determine length of parts");
73     return -1;
74     }
75    
76     /* parts and part_types have to have the same lengths */
77 bramz 2741 if (part_type_list == Py_None)
78     {
79     Py_DECREF(part_type_list);
80     part_type_list = NULL;
81     }
82 bramz 2735 if (part_type_list)
83     {
84     if (!PySequence_Check(parts))
85     {
86     PyErr_SetString(PyExc_TypeError, "part_type_list is not a sequence");
87     return -1;
88     }
89     if (PySequence_Length(part_type_list) != num_parts)
90     {
91     PyErr_SetString(PyExc_TypeError, "parts and part_types have to have the same lengths");
92     return -1;
93     }
94     }
95    
96     /* determine how many vertices there are altogether */
97     num_vertices = 0;
98     for (i = 0; i < num_parts; ++i)
99     {
100     PyObject* part = PySequence_ITEM(parts, i);
101     if (!PySequence_Check(part))
102     {
103     PyErr_SetString(PyExc_TypeError, "at least one item in parts is not a sequence");
104     Py_DECREF(part);
105     return -1;
106     }
107     num_vertices += PySequence_Length(part);
108     Py_DECREF(part);
109     }
110    
111     /* allocate the memory for the various arrays and check for memory errors */
112     xs = malloc(num_vertices * sizeof(double));
113     ys = malloc(num_vertices * sizeof(double));
114     part_starts = malloc(num_parts * sizeof(int));
115     part_types = part_type_list ? malloc(num_parts * sizeof(int)) : 0;
116    
117     if (!xs || !ys || !part_starts || (part_type_list && !part_types))
118     {
119     PyErr_NoMemory();
120     goto exit;
121     }
122    
123     /* convert the part types */
124     if (part_type_list)
125     {
126     for (i = 0; i < num_parts; i++)
127     {
128     PyObject* otype = PySequence_ITEM(part_type_list, i);
129     part_types[i] = PyInt_AsLong(otype);
130     Py_DECREF(otype);
131     if (part_types[i] < 0)
132     {
133     PyErr_SetString(PyExc_TypeError, "at least one item in part_type_list is not an integer or is negative");
134     goto exit;
135     }
136     }
137     }
138    
139     /* convert the list of parts */
140     part_start = 0;
141     for (i = 0; i < num_parts; ++i)
142     {
143     int j, length;
144    
145     PyObject* part = PySequence_ITEM(parts, i);
146     length = PySequence_Length(part);
147     part_starts[i] = part_start;
148    
149     for (j = 0; j < length; ++j)
150     {
151     PyObject* vertex = PySequence_ITEM(part, j);
152     if (!PyArg_ParseTuple(vertex, "dd", xs + part_start + j, ys + part_start + j))
153     {
154     PyErr_SetString(PyExc_TypeError, "at least one part contains an vertex that's not a tuple of two doubles");
155     Py_DECREF(vertex);
156     Py_DECREF(part);
157     goto exit;
158     }
159     Py_DECREF(vertex);
160     }
161     Py_DECREF(part);
162     part_start += length;
163     }
164    
165     self->shpObject = SHPCreateObject(type, id, num_parts, part_starts, part_types, num_vertices, xs, ys, NULL, NULL);
166     return_code = 0;
167    
168     exit:
169     free(xs);
170     free(ys);
171     free(part_starts);
172     free(part_types);
173     return return_code;
174     }
175    
176 jan 1611 /*
177 bramz 2735 * The extents() method of SHPObject.
178     *
179     * Return the extents as a tuple of two 4-element lists with the min.
180     * and max. values of x, y, z, m.
181     */
182 bramz 2742 static PyObject* shpobject_extents(SHPObjectObject* self)
183 jan 1611 {
184 bramz 2735 SHPObject* object = self->shpObject;
185     return Py_BuildValue("(dddd)(dddd)",
186     object->dfXMin, object->dfYMin, object->dfZMin, object->dfMMin,
187     object->dfXMax, object->dfYMax, object->dfZMax, object->dfMMax);
188 jan 1611 }
189    
190    
191     /*
192 bramz 2735 * The vertices() method of SHPObject.
193     *
194     * Return the x and y coords of the vertices as a list of lists of
195     * tuples.
196     */
197 jan 1611
198     static PyObject* build_vertex_list(SHPObject *object, int index, int length);
199    
200 bramz 2742 static PyObject* shpobject_vertices(SHPObjectObject* self)
201 jan 1611 {
202 bramz 2735 PyObject *result = NULL;
203     PyObject *part = NULL;
204     int part_idx, vertex_idx;
205     int length = 0;
206     SHPObject* object = self->shpObject;
207 jan 1611
208 bramz 2735 if (object->nParts > 0)
209     {
210     /* A multipart shape. Usual for SHPT_ARC and SHPT_POLYGON */
211 jan 1611
212 bramz 2735 result = PyList_New(object->nParts);
213 bramz 2741 if (!result)
214     return NULL;
215 jan 1611
216 bramz 2741 for (part_idx = 0, vertex_idx = 0; part_idx < object->nParts; part_idx++)
217     {
218     if (part_idx < object->nParts - 1)
219     length = (object->panPartStart[part_idx + 1]
220     - object->panPartStart[part_idx]);
221     else
222     length = object->nVertices - object->panPartStart[part_idx];
223    
224     part = build_vertex_list(object, vertex_idx, length);
225     if (!part)
226     goto fail;
227 jan 1611
228 bramz 2741 if (PyList_SetItem(result, part_idx, part) < 0)
229     goto fail;
230 jan 1611
231 bramz 2741 vertex_idx += length;
232     }
233 jan 1611 }
234 bramz 2735 else
235     {
236 bramz 2741 /* only one part. usual for SHPT_POINT */
237     result = build_vertex_list(object, 0, object->nVertices);
238 bramz 2735 }
239 jan 1611
240 bramz 2735 return result;
241 jan 1611
242 bramz 2735 fail:
243     Py_XDECREF(part);
244     Py_DECREF(result);
245     return NULL;
246 jan 1611 }
247    
248    
249     /* Return the length coordinates of the shape object starting at vertex
250 bramz 2735 * index as a Python-list of tuples. Helper function for
251     * SHPObject_vertices.
252     */
253     static PyObject* build_vertex_list(SHPObject *object, int index, int length)
254 jan 1611 {
255 bramz 2735 int i;
256     PyObject * list;
257     PyObject * vertex = NULL;
258 jan 1611
259 bramz 2735 list = PyList_New(length);
260     if (!list)
261 bramz 2741 return NULL;
262 jan 1611
263 bramz 2735 for (i = 0; i < length; i++, index++)
264     {
265 bramz 2741 vertex = Py_BuildValue("dd", object->padfX[index],
266     object->padfY[index]);
267     if (!vertex)
268     goto fail;
269     if (PyList_SetItem(list, i, vertex) < 0)
270     goto fail;
271 bramz 2735 }
272 jan 1611
273 bramz 2735 return list;
274 jan 1611
275 bramz 2735 fail:
276     Py_XDECREF(vertex);
277     Py_DECREF(list);
278     return NULL;
279 jan 1611 }
280    
281 bramz 2741
282    
283 bramz 2742 static PyObject* shpobject_part_types(SHPObjectObject* self)
284 bramz 2741 {
285     int i;
286     PyObject* result = NULL;
287     SHPObject* object = self->shpObject;
288    
289     if (object->nParts == 0 || object->panPartType == 0)
290     {
291     Py_RETURN_NONE;
292     }
293    
294     result = PyTuple_New(object->nParts);
295     if (!result) return NULL;
296    
297     for (i = 0; i < object->nParts; ++i)
298     {
299     /* PyTuple_SetItem steals a reference */
300     PyObject* part_type = PyInt_FromLong((long)object->panPartType[i]);
301     if (!part_type || PyTuple_SetItem(result, i, part_type) < 0) goto fail;
302     }
303     return result;
304    
305     fail:
306     Py_DECREF(result);
307     return NULL;
308     }
309    
310    
311    
312 bramz 2742 static PyObject* shpobject_type(SHPObjectObject* self, void* closure)
313 bramz 2735 {
314     return PyInt_FromLong(self->shpObject->nSHPType);
315     }
316 jan 1611
317 bramz 2741
318    
319 bramz 2742 static PyObject* shpobject_id(SHPObjectObject* self, void* closure)
320 bramz 2735 {
321     return PyInt_FromLong(self->shpObject->nShapeId);
322     }
323 jan 1611
324 bramz 2741
325    
326     /* return a string that can be feeded to eval() to reconstruct the object,
327     * assuming a proper context
328     */
329 bramz 2742 static PyObject* shpobject_repr(SHPObjectObject* self)
330 bramz 2741 {
331     PyObject* format = NULL;
332     PyObject* args = NULL;
333     PyObject* result = NULL;
334    
335     format = PyString_FromString("shapelib.SHPObject(%i, %i, %s, %s)");
336     if (!format) return NULL;
337    
338     args = Py_BuildValue("iiNN",
339     self->shpObject->nSHPType,
340     self->shpObject->nShapeId,
341 bramz 2742 shpobject_vertices(self),
342     shpobject_part_types(self));
343 bramz 2741 if (!args)
344     {
345     Py_DECREF(format);
346     return NULL;
347     }
348    
349     result = PyString_Format(format, args);
350     Py_DECREF(args);
351     Py_DECREF(format);
352     return result;
353     }
354    
355    
356    
357 bramz 2742 static struct PyMethodDef shpobject_methods[] =
358 bramz 2735 {
359 bramz 2742 {"extents", (PyCFunction)shpobject_extents, METH_NOARGS, NULL},
360     {"vertices", (PyCFunction)shpobject_vertices, METH_NOARGS, NULL},
361     {"part_types", (PyCFunction)shpobject_part_types, METH_NOARGS, NULL},
362 bramz 2735 {NULL}
363     };
364 jan 1611
365 bramz 2742 static struct PyGetSetDef shpobject_getsetters[] =
366 jan 1611 {
367 bramz 2742 {"type", (getter)shpobject_type, NULL, NULL },
368     {"id", (getter)shpobject_id, NULL, NULL },
369 bramz 2735 {NULL}
370     };
371 jan 1611
372 bramz 2742 static PyTypeObject SHPObjectType = PYSHAPELIB_DEFINE_TYPE(SHPObjectObject, shpobject, "shapelib.SHPObject", 0);
373 jan 1611
374    
375 bramz 2735 /* --- ShapeFile ----------------------------------------------------------------------------------------------------- */
376 jan 1611
377 bramz 2735 typedef struct
378     {
379     PyObject_HEAD
380     SHPHandle handle;
381     }
382 bramz 2742 ShapeFileObject;
383 jan 1611
384 bramz 2741 /* allocator
385     */
386 bramz 2742 static PyObject* shapefile_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
387 bramz 2735 {
388 bramz 2742 ShapeFileObject* self;
389     self = (ShapeFileObject*) type->tp_alloc(type, 0);
390 bramz 2735 self->handle = NULL;
391     return (PyObject*) self;
392     }
393 jan 1611
394 bramz 2742 /* destructor
395     */
396     static void shapefile_dealloc(ShapeFileObject* self)
397     {
398     SHPClose(self->handle);
399     self->ob_type->tp_free((PyObject*)self);
400     }
401    
402 bramz 2741 /* constructor
403     */
404 bramz 2742 static int shapefile_init(ShapeFileObject* self, PyObject* args, PyObject* kwds)
405 bramz 2735 {
406     char* file;
407     char* mode = "rb";
408     if (kwds != NULL && PyDict_Size(kwds) > 0)
409     {
410     PyErr_Format(PyExc_TypeError, "shapelib.ShapeFile.__init__ takes no keyword arguments");
411     return -1;
412     }
413     if (!PyArg_ParseTuple(args, "s|s", &file, &mode)) return -1;
414    
415     self->handle = SHPOpen(file, mode);
416     return self->handle ? 0 : -1;
417     }
418 jan 1611
419 bramz 2742 static PyObject* shapefile_close(ShapeFileObject* self)
420 bramz 2735 {
421     SHPClose(self->handle);
422     self->handle = NULL;
423     Py_RETURN_NONE;
424     }
425 jan 1611
426 bramz 2742 static PyObject* shapefile_info(ShapeFileObject* self)
427 bramz 2735 {
428     SHPHandle handle = self->handle;
429     return Py_BuildValue("ii(dddd)(dddd)",
430     handle->nRecords, handle->nShapeType,
431     handle->adBoundsMin[0], handle->adBoundsMin[1], handle->adBoundsMin[2], handle->adBoundsMin[3],
432     handle->adBoundsMax[0], handle->adBoundsMax[1], handle->adBoundsMax[2], handle->adBoundsMax[3]);
433     }
434 jan 1611
435 bramz 2742 static PyObject* shapefile_read_object(ShapeFileObject* self, PyObject* args)
436 bramz 2735 {
437     int index;
438     SHPObject* object;
439 bramz 2742 SHPObjectObject* result;
440 bramz 2735
441     if (!PyArg_ParseTuple(args, "i", &index)) return NULL;
442    
443     object = SHPReadObject(self->handle, index);
444     if (!object)
445 jan 1611 {
446 bramz 2735 PyErr_SetString(PyExc_RuntimeError, "failed to read object");
447 jan 1611 return NULL;
448     }
449 bramz 2735
450 bramz 2742 result = PyObject_New(SHPObjectObject, &SHPObjectType);
451 bramz 2735 if (!result)
452 jan 1611 {
453 bramz 2735 return PyErr_NoMemory();
454 jan 1611 }
455 bramz 2735
456     result->shpObject = object;
457     return (PyObject*) result;
458 jan 1611 }
459    
460 bramz 2742 static PyObject* shapefile_write_object(ShapeFileObject* self, PyObject* args)
461 bramz 2735 {
462     int index, result;
463     PyObject* object;
464    
465     if (!PyArg_ParseTuple(args, "iO", &index, &object)) return NULL;
466    
467 bramz 2742 if (!PyObject_IsInstance(object, (PyObject*)&SHPObjectType))
468 bramz 2735 {
469     PyErr_SetString(PyExc_TypeError, "object is not a SHPObject");
470     return NULL;
471     }
472    
473 bramz 2742 result = SHPWriteObject(self->handle, index, ((SHPObjectObject*)object)->shpObject);
474 bramz 2735 if (result < 0)
475     {
476     PyErr_SetString(PyExc_RuntimeError, "failed to write object");
477     return NULL;
478     }
479     return PyInt_FromLong((long)result);
480 jan 1611 }
481    
482 bramz 2742 static PyObject* shapefile_cobject(ShapeFileObject* self)
483 bramz 2735 {
484     return PyCObject_FromVoidPtr(self->handle, NULL);
485 jan 1611 }
486    
487 bramz 2742 static PyObject* shapefile_repr(ShapeFileObject* self)
488 bramz 2741 {
489     /* TODO: it would be nice to do something like "shapelib.ShapeFile(filename, mode)" instead */
490     return PyString_FromFormat("<shapelib.ShapeFile object at %p>", self->handle);
491     }
492    
493 bramz 2742 static struct PyMethodDef shapefile_methods[] =
494 bramz 2735 {
495 bramz 2742 {"close", (PyCFunction)shapefile_close, METH_NOARGS, "close the shape file" },
496     {"info", (PyCFunction)shapefile_info, METH_NOARGS,
497 bramz 2735 "Return a tuple (NUM_SHAPES, TYPE, MIN, MAX) where NUM_SHAPES is the number of shapes in the file, TYPE is the "
498     "shape type and MIN and MAX are 4-element tuples with the min. and max. values of the data." },
499 bramz 2742 {"read_object", (PyCFunction)shapefile_read_object, METH_VARARGS, "Return object number i" },
500     {"write_object", (PyCFunction)shapefile_write_object, METH_VARARGS, "Write an object"},
501     {"cobject", (PyCFunction)shapefile_cobject, METH_NOARGS, "Return the shapelib SHPHandle as a Python CObject"},
502 bramz 2735 {NULL}
503     };
504 jan 1611
505 bramz 2742 static struct PyGetSetDef shapefile_getsetters[] =
506 bramz 2735 {
507     {NULL}
508     };
509 jan 1611
510 bramz 2742 static PyTypeObject ShapeFileType = PYSHAPELIB_DEFINE_TYPE(ShapeFileObject, shapefile, "shapelib.ShapeFile", 0);
511 jan 1611
512 bramz 2735 /* --- shapelib ------------------------------------------------------------------------------------------------------ */
513 jan 1611
514 bramz 2735 static PyObject* shapelib_open(PyObject* module, PyObject* args)
515     {
516 bramz 2742 return PyObject_CallObject((PyObject*)&ShapeFileType, args);
517 jan 1611 }
518    
519 bramz 2735 static PyObject* shapelib_create(PyObject* module, PyObject* args)
520 jan 1611 {
521 bramz 2735 char* file;
522     int type;
523 bramz 2742 ShapeFileObject* result;
524 bramz 2735
525     if (!PyArg_ParseTuple(args, "si", &file, &type)) return NULL;
526    
527 bramz 2742 result = PyObject_New(ShapeFileObject, &ShapeFileType);
528 bramz 2735 if (!result)
529     {
530     return PyErr_NoMemory();
531 jan 1611 }
532 bramz 2735
533     result->handle = SHPCreate(file, type);
534     if (!result->handle)
535     {
536     PyObject_Del((PyObject*)result);
537     PyErr_SetString(PyExc_RuntimeError, "Failed to create ShapeFile");
538     return NULL;
539 jan 1611 }
540 bramz 2735
541     return (PyObject*) result;
542     }
543    
544     static PyShapeLibAPI shapelib_the_api =
545     {
546 jan 1611 SHPReadObject,
547     SHPDestroyObject,
548     SHPCreateTree,
549     SHPDestroyTree,
550     SHPTreeFindLikelyShapes
551 bramz 2735 };
552 jan 1611
553 bramz 2735 static PyObject* shapelib_c_api(PyObject* module)
554     {
555     return PyCObject_FromVoidPtr(&shapelib_the_api, NULL);
556     }
557 jan 1611
558 bramz 2735 static PyObject* shapelib_type_name(PyObject* module, PyObject* args)
559     {
560     int type;
561     if (!PyArg_ParseTuple(args, "i", &type)) return NULL;
562     return PyString_FromString(SHPTypeName(type));
563     }
564 jan 1611
565 bramz 2735 static PyObject* shapelib_part_type_name(PyObject* module, PyObject* args)
566     {
567     int type;
568     if (!PyArg_ParseTuple(args, "i", &type)) return NULL;
569     return PyString_FromString(SHPPartTypeName(type));
570     }
571 jan 1611
572 bramz 2742 static struct PyMethodDef shapelib_methods[] =
573 bramz 2735 {
574     {"open", (PyCFunction)shapelib_open, METH_VARARGS, "open a ShapeFile" },
575     {"create", (PyCFunction)shapelib_create, METH_VARARGS, "create a ShapeFile" },
576     {"c_api", (PyCFunction)shapelib_c_api, METH_NOARGS, "get C API of shapelib" },
577     {"type_name", (PyCFunction)shapelib_type_name, METH_VARARGS, "return type as string" },
578     {"part_type_name", (PyCFunction)shapelib_part_type_name, METH_VARARGS, "return part type as string" },
579     {NULL}
580     };
581 jan 1611
582 bramz 2735 PyMODINIT_FUNC initshapelib(void)
583     {
584     PyObject* module = Py_InitModule("shapelib", shapelib_methods);
585     if (!module) return;
586    
587 bramz 2742 PYSHAPELIB_ADD_TYPE(SHPObjectType, "SHPObject");
588     PYSHAPELIB_ADD_TYPE(ShapeFileType, "ShapeFile");
589 bramz 2735
590     PYSHAPELIB_ADD_CONSTANT(SHPT_NULL);
591     PYSHAPELIB_ADD_CONSTANT(SHPT_POINT);
592     PYSHAPELIB_ADD_CONSTANT(SHPT_ARC);
593     PYSHAPELIB_ADD_CONSTANT(SHPT_POLYGON);
594     PYSHAPELIB_ADD_CONSTANT(SHPT_MULTIPOINT);
595     PYSHAPELIB_ADD_CONSTANT(SHPT_POINTZ);
596     PYSHAPELIB_ADD_CONSTANT(SHPT_ARCZ);
597     PYSHAPELIB_ADD_CONSTANT(SHPT_POLYGONZ);
598     PYSHAPELIB_ADD_CONSTANT(SHPT_MULTIPOINTZ);
599     PYSHAPELIB_ADD_CONSTANT(SHPT_POINTM);
600     PYSHAPELIB_ADD_CONSTANT(SHPT_ARCM);
601     PYSHAPELIB_ADD_CONSTANT(SHPT_POLYGONM);
602     PYSHAPELIB_ADD_CONSTANT(SHPT_MULTIPOINTM);
603     PYSHAPELIB_ADD_CONSTANT(SHPT_MULTIPATCH);
604     PYSHAPELIB_ADD_CONSTANT(SHPP_TRISTRIP);
605     PYSHAPELIB_ADD_CONSTANT(SHPP_TRIFAN);
606     PYSHAPELIB_ADD_CONSTANT(SHPP_OUTERRING);
607     PYSHAPELIB_ADD_CONSTANT(SHPP_INNERRING);
608     PYSHAPELIB_ADD_CONSTANT(SHPP_FIRSTRING);
609     PYSHAPELIB_ADD_CONSTANT(SHPP_RING);
610     }
611 jan 1611

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26