Routino SVN Repository Browser

Check out the latest version of Routino: svn co http://routino.org/svn/trunk routino

ViewVC logotype

Contents of /trunk/src/waysx.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1145 - (show annotations) (download) (as text)
Sat Nov 17 13:31:14 2012 UTC (12 years, 4 months ago) by amb
File MIME type: text/x-csrc
File size: 19188 byte(s)
Fix applying changes for ways (highways that have been modified to be
non-highways were not added to the database so the original remains).

1 /***************************************
2 Extended Way data type functions.
3
4 Part of the Routino routing software.
5 ******************/ /******************
6 This file Copyright 2008-2012 Andrew M. Bishop
7
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU Affero General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ***************************************/
21
22
23 #include <assert.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "types.h"
28 #include "ways.h"
29
30 #include "typesx.h"
31 #include "segmentsx.h"
32 #include "waysx.h"
33
34 #include "files.h"
35 #include "logging.h"
36 #include "sorting.h"
37
38
39 /* Global variables */
40
41 /*+ The command line '--tmpdir' option or its default value. +*/
42 extern char *option_tmpdirname;
43
44 /* Local variables */
45
46 /*+ Temporary file-local variables for use by the sort functions. +*/
47 static WaysX *sortwaysx;
48 static SegmentsX *sortsegmentsx;
49
50 /* Local functions */
51
52 static int sort_by_id(WayX *a,WayX *b);
53 static int sort_by_name(WayX *a,WayX *b);
54 static int sort_by_name_and_prop_and_id(WayX *a,WayX *b);
55
56 static int delete_unused(WayX *wayx,index_t index);
57 static int deduplicate_by_id(WayX *wayx,index_t index);
58 static int index_by_id(WayX *wayx,index_t index);
59 static int deduplicate_and_index_by_compact_id(WayX *wayx,index_t index);
60
61
62 /*++++++++++++++++++++++++++++++++++++++
63 Allocate a new way list (create a new file or open an existing one).
64
65 WaysX *NewWayList Returns the way list.
66
67 int append Set to 1 if the file is to be opened for appending.
68
69 int readonly Set to 1 if the file is to be opened for reading.
70
71 int index Set to 1 if the file is to be indexed (requires append=1).
72 ++++++++++++++++++++++++++++++++++++++*/
73
74 WaysX *NewWayList(int append,int readonly,int index)
75 {
76 WaysX *waysx;
77
78 waysx=(WaysX*)calloc(1,sizeof(WaysX));
79
80 assert(waysx); /* Check calloc() worked */
81
82 waysx->filename =(char*)malloc(strlen(option_tmpdirname)+32);
83 waysx->filename_tmp=(char*)malloc(strlen(option_tmpdirname)+32);
84
85 sprintf(waysx->filename ,"%s/waysx.parsed.mem",option_tmpdirname);
86 sprintf(waysx->filename_tmp,"%s/waysx.%p.tmp" ,option_tmpdirname,(void*)waysx);
87
88 if(append || readonly)
89 if(ExistsFile(waysx->filename))
90 {
91 off_t size,position=0;
92 int fd;
93
94 size=SizeFile(waysx->filename);
95
96 fd=ReOpenFile(waysx->filename);
97
98 while(position<size)
99 {
100 FILESORT_VARINT waysize;
101
102 SeekReadFile(fd,&waysize,FILESORT_VARSIZE,position);
103
104 waysx->number++;
105 position+=waysize+FILESORT_VARSIZE;
106 }
107
108 CloseFile(fd);
109
110 if(append && index)
111 {
112 /* Allocate the array of indexes */
113
114 waysx->idata=(way_t*)malloc(waysx->number*sizeof(way_t));
115
116 assert(waysx->idata); /* Check malloc() worked */
117
118 /* Read through the file again */
119
120 fd=ReOpenFile(waysx->filename);
121
122 position=0;
123
124 while(position<size)
125 {
126 FILESORT_VARINT waysize;
127 WayX wayx;
128
129 SeekReadFile(fd,&waysize,FILESORT_VARSIZE,position);
130
131 SeekReadFile(fd,&wayx,sizeof(WayX),position+2);
132
133 waysx->idata[waysx->inumber]=wayx.id;
134
135 waysx->inumber++;
136 position+=waysize+FILESORT_VARSIZE;
137 }
138
139 CloseFile(fd);
140 }
141
142 RenameFile(waysx->filename,waysx->filename_tmp);
143 }
144
145 if(append)
146 waysx->fd=OpenFileAppend(waysx->filename_tmp);
147 else if(!readonly)
148 waysx->fd=OpenFileNew(waysx->filename_tmp);
149 else
150 waysx->fd=-1;
151
152
153 waysx->nfilename_tmp=(char*)malloc(strlen(option_tmpdirname)+32);
154
155 sprintf(waysx->nfilename_tmp,"%s/waynames.%p.tmp",option_tmpdirname,(void*)waysx);
156
157 return(waysx);
158 }
159
160
161 /*++++++++++++++++++++++++++++++++++++++
162 Free a way list.
163
164 WaysX *waysx The set of ways to be freed.
165 ++++++++++++++++++++++++++++++++++++++*/
166
167 void FreeWayList(WaysX *waysx)
168 {
169 DeleteFile(waysx->filename_tmp);
170
171 free(waysx->filename);
172 free(waysx->filename_tmp);
173
174 if(waysx->idata)
175 free(waysx->idata);
176
177 if(waysx->cdata)
178 free(waysx->cdata);
179
180 DeleteFile(waysx->nfilename_tmp);
181
182 free(waysx->nfilename_tmp);
183
184 free(waysx);
185 }
186
187
188 /*++++++++++++++++++++++++++++++++++++++
189 Append a single way to an unsorted way list.
190
191 WaysX *waysx The set of ways to process.
192
193 way_t id The ID of the way.
194
195 Way *way The way data itself.
196
197 const char *name The name or reference of the way.
198 ++++++++++++++++++++++++++++++++++++++*/
199
200 void AppendWay(WaysX *waysx,way_t id,Way *way,const char *name)
201 {
202 WayX wayx;
203 FILESORT_VARINT size;
204
205 wayx.id=id;
206 wayx.way=*way;
207
208 size=sizeof(WayX)+strlen(name)+1;
209
210 WriteFile(waysx->fd,&size,FILESORT_VARSIZE);
211 WriteFile(waysx->fd,&wayx,sizeof(WayX));
212 WriteFile(waysx->fd,name,strlen(name)+1);
213
214 waysx->number++;
215
216 assert(waysx->number!=0); /* Zero marks the high-water mark for ways. */
217 }
218
219
220 /*++++++++++++++++++++++++++++++++++++++
221 Finish appending ways and change the filename over.
222
223 WaysX *waysx The ways that have been appended.
224
225 int preserve If set then the results file is to be preserved.
226 ++++++++++++++++++++++++++++++++++++++*/
227
228 void FinishWayList(WaysX *waysx,int preserve)
229 {
230 if(waysx->idata)
231 {free(waysx->idata);waysx->idata=NULL;}
232
233 /* Close the file (finished appending) */
234
235 if(waysx->fd!=-1)
236 waysx->fd=CloseFile(waysx->fd);
237
238 /* Rename the file if keeping it */
239
240 if(preserve)
241 RenameFile(waysx->filename_tmp,waysx->filename);
242 }
243
244
245 /*++++++++++++++++++++++++++++++++++++++
246 Sort the list of ways.
247
248 WaysX *waysx The set of ways to process.
249 ++++++++++++++++++++++++++++++++++++++*/
250
251 void SortWayList(WaysX *waysx)
252 {
253 index_t xnumber;
254 int fd;
255
256 /* Print the start message */
257
258 printf_first("Sorting Ways");
259
260 /* Re-open the file read-only and a new file writeable */
261
262 waysx->fd=ReOpenFile(waysx->filename_tmp);
263
264 DeleteFile(waysx->filename_tmp);
265
266 fd=OpenFileNew(waysx->filename_tmp);
267
268 /* Sort the ways by ID and index them */
269
270 xnumber=waysx->number;
271
272 waysx->number=filesort_vary(waysx->fd,fd,NULL,
273 (int (*)(const void*,const void*))sort_by_id,
274 (int (*)(void*,index_t))deduplicate_by_id);
275
276 /* Close the files */
277
278 waysx->fd=CloseFile(waysx->fd);
279 CloseFile(fd);
280
281 /* Print the final message */
282
283 printf_last("Sorted Ways: Ways=%"Pindex_t" Duplicates=%"Pindex_t,xnumber,xnumber-waysx->number);
284 }
285
286
287 /*++++++++++++++++++++++++++++++++++++++
288 Extract the way names from the ways and reference the list of names from the ways.
289
290 WaysX *waysx The set of ways to process.
291
292 int preserve If set to 1 then keep the old data file otherwise delete it.
293 ++++++++++++++++++++++++++++++++++++++*/
294
295 void ExtractWayNames(WaysX *waysx,int preserve)
296 {
297 index_t i;
298 int fd;
299 char *names[2]={NULL,NULL};
300 int namelen[2]={0,0};
301 int nnames=0;
302 uint32_t lastlength=0;
303
304 /* Print the start message */
305
306 printf_first("Sorting Ways by Name");
307
308 /* Re-open the file read-only and a new file writeable */
309
310 waysx->fd=ReOpenFile(waysx->filename_tmp);
311
312 if(preserve)
313 RenameFile(waysx->filename_tmp,waysx->filename);
314 else
315 DeleteFile(waysx->filename_tmp);
316
317 fd=OpenFileNew(waysx->filename_tmp);
318
319 /* Sort the ways to allow separating the names */
320
321 filesort_vary(waysx->fd,fd,NULL,
322 (int (*)(const void*,const void*))sort_by_name,
323 NULL);
324
325 /* Close the files */
326
327 waysx->fd=CloseFile(waysx->fd);
328 CloseFile(fd);
329
330 /* Print the final message */
331
332 printf_last("Sorted Ways by Name: Ways=%"Pindex_t,waysx->number);
333
334
335 /* Print the start message */
336
337 printf_first("Separating Way Names: Ways=0 Names=0");
338
339 /* Re-open the file read-only and new files writeable */
340
341 waysx->fd=ReOpenFile(waysx->filename_tmp);
342
343 DeleteFile(waysx->filename_tmp);
344
345 fd=OpenFileNew(waysx->filename_tmp);
346
347 waysx->nfd=OpenFileNew(waysx->nfilename_tmp);
348
349 /* Copy from the single file into two files */
350
351 for(i=0;i<waysx->number;i++)
352 {
353 WayX wayx;
354 FILESORT_VARINT size;
355
356 ReadFile(waysx->fd,&size,FILESORT_VARSIZE);
357
358 if(namelen[nnames%2]<size)
359 names[nnames%2]=(char*)realloc((void*)names[nnames%2],namelen[nnames%2]=size);
360
361 ReadFile(waysx->fd,&wayx,sizeof(WayX));
362 ReadFile(waysx->fd,names[nnames%2],size-sizeof(WayX));
363
364 if(nnames==0 || strcmp(names[0],names[1]))
365 {
366 WriteFile(waysx->nfd,names[nnames%2],size-sizeof(WayX));
367
368 lastlength=waysx->nlength;
369 waysx->nlength+=size-sizeof(WayX);
370
371 nnames++;
372 }
373
374 wayx.way.name=lastlength;
375
376 WriteFile(fd,&wayx,sizeof(WayX));
377
378 if(!((i+1)%1000))
379 printf_middle("Separating Way Names: Ways=%"Pindex_t" Names=%"Pindex_t,i+1,nnames);
380 }
381
382 if(names[0]) free(names[0]);
383 if(names[1]) free(names[1]);
384
385 /* Close the files */
386
387 waysx->fd=CloseFile(waysx->fd);
388 CloseFile(fd);
389
390 waysx->nfd=CloseFile(waysx->nfd);
391
392 /* Print the final message */
393
394 printf_last("Separated Way Names: Ways=%"Pindex_t" Names=%"Pindex_t,waysx->number,nnames);
395
396
397 /* Print the start message */
398
399 printf_first("Sorting Ways");
400
401 /* Re-open the file read-only and a new file writeable */
402
403 waysx->fd=ReOpenFile(waysx->filename_tmp);
404
405 DeleteFile(waysx->filename_tmp);
406
407 fd=OpenFileNew(waysx->filename_tmp);
408
409 /* Allocate the array of indexes */
410
411 waysx->idata=(way_t*)malloc(waysx->number*sizeof(way_t));
412
413 assert(waysx->idata); /* Check malloc() worked */
414
415 waysx->inumber=waysx->number;
416
417 /* Sort the ways by ID */
418
419 sortwaysx=waysx;
420
421 filesort_fixed(waysx->fd,fd,sizeof(WayX),NULL,
422 (int (*)(const void*,const void*))sort_by_id,
423 (int (*)(void*,index_t))index_by_id);
424
425 /* Close the files */
426
427 waysx->fd=CloseFile(waysx->fd);
428 CloseFile(fd);
429
430 /* Print the final message */
431
432 printf_last("Sorted Ways: Ways=%"Pindex_t,waysx->number);
433 }
434
435
436 /*++++++++++++++++++++++++++++++++++++++
437 Compact the way list, removing duplicated ways and unused ways.
438
439 WaysX *waysx The set of ways to process.
440
441 SegmentsX *segmentsx The set of segments to check.
442 ++++++++++++++++++++++++++++++++++++++*/
443
444 void CompactWayList(WaysX *waysx,SegmentsX *segmentsx)
445 {
446 int fd;
447 index_t cnumber;
448
449 /* Print the start message */
450
451 printf_first("Sorting Ways and Compacting");
452
453 /* Allocate the array of indexes */
454
455 waysx->cdata=(index_t*)malloc(waysx->number*sizeof(index_t));
456
457 assert(waysx->cdata); /* Check malloc() worked */
458
459 /* Re-open the file read-only and a new file writeable */
460
461 waysx->fd=ReOpenFile(waysx->filename_tmp);
462
463 DeleteFile(waysx->filename_tmp);
464
465 fd=OpenFileNew(waysx->filename_tmp);
466
467 /* Sort the ways to allow compacting according to the properties */
468
469 sortwaysx=waysx;
470 sortsegmentsx=segmentsx;
471
472 cnumber=filesort_fixed(waysx->fd,fd,sizeof(WayX),(int (*)(void*,index_t))delete_unused,
473 (int (*)(const void*,const void*))sort_by_name_and_prop_and_id,
474 (int (*)(void*,index_t))deduplicate_and_index_by_compact_id);
475
476 /* Close the files */
477
478 waysx->fd=CloseFile(waysx->fd);
479 CloseFile(fd);
480
481 /* Print the final message */
482
483 printf_last("Sorted and Compacted Ways: Ways=%"Pindex_t" Unique=%"Pindex_t,waysx->number,cnumber);
484 waysx->number=cnumber;
485
486 free(segmentsx->usedway);
487 segmentsx->usedway=NULL;
488 }
489
490
491 /*++++++++++++++++++++++++++++++++++++++
492 Sort the ways into id order.
493
494 int sort_by_id Returns the comparison of the id fields.
495
496 WayX *a The first extended way.
497
498 WayX *b The second extended way.
499 ++++++++++++++++++++++++++++++++++++++*/
500
501 static int sort_by_id(WayX *a,WayX *b)
502 {
503 way_t a_id=a->id;
504 way_t b_id=b->id;
505
506 if(a_id<b_id)
507 return(-1);
508 else if(a_id>b_id)
509 return(1);
510 else
511 return(-FILESORT_PRESERVE_ORDER(a,b)); /* latest version first */
512 }
513
514
515 /*++++++++++++++++++++++++++++++++++++++
516 Sort the ways into name order and then id order.
517
518 int sort_by_name Returns the comparison of the name fields.
519
520 WayX *a The first extended Way.
521
522 WayX *b The second extended Way.
523 ++++++++++++++++++++++++++++++++++++++*/
524
525 static int sort_by_name(WayX *a,WayX *b)
526 {
527 int compare;
528 char *a_name=(char*)a+sizeof(WayX);
529 char *b_name=(char*)b+sizeof(WayX);
530
531 compare=strcmp(a_name,b_name);
532
533 if(compare)
534 return(compare);
535 else
536 return(FILESORT_PRESERVE_ORDER(a,b));
537 }
538
539
540 /*++++++++++++++++++++++++++++++++++++++
541 Sort the ways into name, properties and id order.
542
543 int sort_by_name_and_prop_and_id Returns the comparison of the name, properties and id fields.
544
545 WayX *a The first extended Way.
546
547 WayX *b The second extended Way.
548 ++++++++++++++++++++++++++++++++++++++*/
549
550 static int sort_by_name_and_prop_and_id(WayX *a,WayX *b)
551 {
552 int compare;
553 index_t a_name=a->way.name;
554 index_t b_name=b->way.name;
555
556 if(a_name<b_name)
557 return(-1);
558 else if(a_name>b_name)
559 return(1);
560
561 compare=WaysCompare(&a->way,&b->way);
562
563 if(compare)
564 return(compare);
565
566 return(sort_by_id(a,b));
567 }
568
569
570 /*++++++++++++++++++++++++++++++++++++++
571 Discard duplicate ways.
572
573 int deduplicate_by_id Return 1 if the value is to be kept, otherwise 0.
574
575 WayX *wayx The extended way.
576
577 index_t index The number of sorted ways that have already been written to the output file.
578 ++++++++++++++++++++++++++++++++++++++*/
579
580 static int deduplicate_by_id(WayX *wayx,index_t index)
581 {
582 static way_t previd=NO_WAY_ID;
583
584 if(wayx->id!=previd)
585 {
586 previd=wayx->id;
587
588 if(wayx->way.type==WAY_DELETED)
589 return(0);
590 else
591 return(1);
592 }
593 else
594 {
595 logerror("Way %"Pway_t" is duplicated.\n",wayx->id);
596
597 return(0);
598 }
599 }
600
601
602 /*++++++++++++++++++++++++++++++++++++++
603 Create the index of identifiers.
604
605 int index_by_id Return 1 if the value is to be kept, otherwise 0.
606
607 WayX *wayx The extended way.
608
609 index_t index The number of sorted ways that have already been written to the output file.
610 ++++++++++++++++++++++++++++++++++++++*/
611
612 static int index_by_id(WayX *wayx,index_t index)
613 {
614 sortwaysx->idata[index]=wayx->id;
615
616 return(1);
617 }
618
619
620 /*++++++++++++++++++++++++++++++++++++++
621 Delete the ways that are no longer being used.
622
623 int delete_unused Return 1 if the value is to be kept, otherwise 0.
624
625 WayX *wayx The extended way.
626
627 index_t index The number of unsorted ways that have been read from the input file.
628 ++++++++++++++++++++++++++++++++++++++*/
629
630 static int delete_unused(WayX *wayx,index_t index)
631 {
632 if(sortsegmentsx && !IsBitSet(sortsegmentsx->usedway,index))
633 {
634 sortwaysx->cdata[index]=NO_WAY;
635
636 return(0);
637 }
638 else
639 {
640 wayx->id=index;
641
642 return(1);
643 }
644 }
645
646
647 /*++++++++++++++++++++++++++++++++++++++
648 Create the index of compacted Way identifiers and ignore Ways with duplicated properties.
649
650 int deduplicate_and_index_by_compact_id Return 1 if the value is to be kept, otherwise 0.
651
652 WayX *wayx The extended way.
653
654 index_t index The number of sorted ways that have already been written to the output file.
655 ++++++++++++++++++++++++++++++++++++++*/
656
657 static int deduplicate_and_index_by_compact_id(WayX *wayx,index_t index)
658 {
659 static Way lastway;
660
661 if(index==0 || wayx->way.name!=lastway.name || WaysCompare(&lastway,&wayx->way))
662 {
663 lastway=wayx->way;
664
665 sortwaysx->cdata[wayx->id]=index;
666
667 wayx->id=index;
668
669 return(1);
670 }
671 else
672 {
673 sortwaysx->cdata[wayx->id]=index-1;
674
675 return(0);
676 }
677 }
678
679
680 /*++++++++++++++++++++++++++++++++++++++
681 Find a particular way index.
682
683 index_t IndexWayX Returns the index of the extended way with the specified id.
684
685 WaysX *waysx The set of ways to process.
686
687 way_t id The way id to look for.
688 ++++++++++++++++++++++++++++++++++++++*/
689
690 index_t IndexWayX(WaysX *waysx,way_t id)
691 {
692 index_t start=0;
693 index_t end=waysx->inumber-1;
694 index_t mid;
695
696 /* Binary search - search key exact match only is required.
697 *
698 * # <- start | Check mid and move start or end if it doesn't match
699 * # |
700 * # | Since an exact match is wanted we can set end=mid-1
701 * # <- mid | or start=mid+1 because we know that mid doesn't match.
702 * # |
703 * # | Eventually either end=start or end=start+1 and one of
704 * # <- end | start or end is the wanted one.
705 */
706
707 if(end<start) /* There are no ways */
708 return(NO_WAY);
709 else if(id<waysx->idata[start]) /* Check key is not before start */
710 return(NO_WAY);
711 else if(id>waysx->idata[end]) /* Check key is not after end */
712 return(NO_WAY);
713 else
714 {
715 do
716 {
717 mid=(start+end)/2; /* Choose mid point */
718
719 if(waysx->idata[mid]<id) /* Mid point is too low */
720 start=mid+1;
721 else if(waysx->idata[mid]>id) /* Mid point is too high */
722 end=mid?(mid-1):mid;
723 else /* Mid point is correct */
724 return(mid);
725 }
726 while((end-start)>1);
727
728 if(waysx->idata[start]==id) /* Start is correct */
729 return(start);
730
731 if(waysx->idata[end]==id) /* End is correct */
732 return(end);
733 }
734
735 return(NO_WAY);
736 }
737
738
739 /*++++++++++++++++++++++++++++++++++++++
740 Save the way list to a file.
741
742 WaysX *waysx The set of ways to save.
743
744 const char *filename The name of the file to save.
745 ++++++++++++++++++++++++++++++++++++++*/
746
747 void SaveWayList(WaysX *waysx,const char *filename)
748 {
749 index_t i;
750 int fd;
751 int position=0;
752 WayX wayx;
753 WaysFile waysfile={0};
754 highways_t highways=0;
755 transports_t allow=0;
756 properties_t props=0;
757
758 /* Print the start message */
759
760 printf_first("Writing Ways: Ways=0");
761
762 /* Re-open the files */
763
764 waysx->fd=ReOpenFile(waysx->filename_tmp);
765 waysx->nfd=ReOpenFile(waysx->nfilename_tmp);
766
767 /* Write out the ways data */
768
769 fd=OpenFileNew(filename);
770
771 SeekFile(fd,sizeof(WaysFile));
772
773 for(i=0;i<waysx->number;i++)
774 {
775 ReadFile(waysx->fd,&wayx,sizeof(WayX));
776
777 highways|=HIGHWAYS(wayx.way.type);
778 allow |=wayx.way.allow;
779 props |=wayx.way.props;
780
781 WriteFile(fd,&wayx.way,sizeof(Way));
782
783 if(!((i+1)%1000))
784 printf_middle("Writing Ways: Ways=%"Pindex_t,i+1);
785 }
786
787 /* Write out the ways names */
788
789 SeekFile(fd,sizeof(WaysFile)+(off_t)waysx->number*sizeof(Way));
790
791 while(position<waysx->nlength)
792 {
793 int len=1024;
794 char temp[1024];
795
796 if((waysx->nlength-position)<1024)
797 len=waysx->nlength-position;
798
799 ReadFile(waysx->nfd,temp,len);
800
801 WriteFile(fd,temp,len);
802
803 position+=len;
804 }
805
806 /* Close the files */
807
808 waysx->fd=CloseFile(waysx->fd);
809 waysx->nfd=CloseFile(waysx->nfd);
810
811 /* Write out the header structure */
812
813 waysfile.number =waysx->number;
814
815 waysfile.highways=highways;
816 waysfile.allow =allow;
817 waysfile.props =props;
818
819 SeekFile(fd,0);
820 WriteFile(fd,&waysfile,sizeof(WaysFile));
821
822 CloseFile(fd);
823
824 /* Print the final message */
825
826 printf_last("Wrote Ways: Ways=%"Pindex_t,waysx->number);
827 }

Properties

Name Value
cvs:description Extended ways functions.