1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
/*
*******************************************************************************
*
* Copyright (C) 2003, International Business Machines
* Corporation and others. All Rights Reserved.
*
*******************************************************************************
* file name: ucdmerge.c
* encoding: US-ASCII
* tab size: 8 (not used)
* indentation:4
*
* created on: 2003feb20
* created by: Markus W. Scherer
*
* Simple tool for Unicode Character Database files with semicolon-delimited fields.
* Merges adjacent, identical per-code point data lines into one line with range syntax.
*
* To compile, just call a C compiler/linker with this source file.
* On Windows: cl ucdmerge.c
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static const char *
skipWhitespace(const char *s) {
while(*s==' ' || *s=='\t') {
++s;
}
return s;
}
/* return the first character position after the end of the data */
static char *
endOfData(const char *l) {
char *end;
char c;
end=strchr(l, '#');
if(end!=NULL) {
/* ignore whitespace before the comment */
while(l!=end && ((c=*(end-1))==' ' || c=='\t')) {
--end;
}
} else {
end=strchr(l, 0);
}
return end;
}
static int
sameData(const char *l1, const char *l2) {
char *end1, *end2;
int length;
/* find the first semicolon in each line - there must be one */
l1=strchr(l1, ';')+1;
l2=strchr(l2, ';')+1;
/* find the end of data: end of string or start of comment */
end1=endOfData(l1);
end2=endOfData(l2);
/* compare the line data portions */
length=end1-l1;
return length==(end2-l2) && 0==memcmp(l1, l2, length);
}
extern int
main(int argc, const char *argv[]) {
static char line[2000], firstLine[2000], lastLine[2000];
char *end;
long first, last, c;
int finished;
first=last=-1;
finished=0;
for(;;) {
if(gets(line)!=NULL) {
/* parse the initial code point, if any */
c=strtol(line, &end, 16);
if(end!=line && *skipWhitespace(end)==';') {
/* single code point followed by semicolon and data, keep c */
} else {
c=-1;
}
} else {
line[0]=0;
c=-1;
finished=1;
}
if(last>=0 && (c!=(last+1) || !sameData(firstLine, line))) {
/* output the current range */
if(first==last) {
/* there was no range, just output the one line we found */
puts(firstLine);
} else {
/* there was a real range, merge their lines */
end=strchr(lastLine, '#');
if(end==NULL) {
/* no comment in second line */
printf("%04lX..%04lX%s\n",
first, last, /* code point range */
strchr(firstLine, ';'));/* first line starting from the first ; */
} else if(strchr(firstLine, '#')==NULL) {
/* no comment in first line */
printf("%04lX..%04lX%s%s\n",
first, last, /* code point range */
strchr(firstLine, ';'), /* first line starting from the first ; */
end); /* comment from second line */
} else {
/* merge comments from both lines */
printf("%04lX..%04lX%s..%s\n",
first, last, /* code point range */
strchr(firstLine, ';'), /* first line starting from the first ; */
skipWhitespace(end+1)); /* comment from second line, after # and spaces */
}
}
first=last=-1;
}
if(c<0) {
if(finished) {
break;
}
/* no data on this line, output as is */
puts(line);
} else {
/* data on this line, store for possible range compaction */
if(last<0) {
/* set as the first line in a possible range */
first=last=c;
strcpy(firstLine, line);
lastLine[0]=0;
} else /* must be c==(last+1) && sameData() because of previous conditions */ {
/* continue with the current range */
last=c;
strcpy(lastLine, line);
}
}
}
return 0;
}
|