libs/lmo: fix whitespace handling in string hashing, optimize code
[project/luci.git] / libs / lmo / src / lmo_po2lmo.c
1 /*
2  * lmo - Lua Machine Objects - PO to LMO conversion tool
3  *
4  *   Copyright (C) 2009-2011 Jo-Philipp Wich <xm@subsignal.org>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */
18
19 #include "lmo.h"
20
21 static void die(const char *msg)
22 {
23         fprintf(stderr, "Error: %s\n", msg);
24         exit(1);
25 }
26
27 static void usage(const char *name)
28 {
29         fprintf(stderr, "Usage: %s input.po output.lmo\n", name);
30         exit(1);
31 }
32
33 static void print(const void *ptr, size_t size, size_t nmemb, FILE *stream)
34 {
35         if( fwrite(ptr, size, nmemb, stream) == 0 )
36                 die("Failed to write stdout");
37 }
38
39 static int extract_string(const char *src, char *dest, int len)
40 {
41         int pos = 0;
42         int esc = 0;
43         int off = -1;
44
45         for( pos = 0; (pos < strlen(src)) && (pos < len); pos++ )
46         {
47                 if( (off == -1) && (src[pos] == '"') )
48                 {
49                         off = pos + 1;
50                 }
51                 else if( off >= 0 )
52                 {
53                         if( esc == 1 )
54                         {
55                                 switch (src[pos])
56                                 {
57                                 case '"':
58                                 case '\\':
59                                         off++;
60                                         break;
61                                 }
62                                 dest[pos-off] = src[pos];
63                                 esc = 0;
64                         }
65                         else if( src[pos] == '\\' )
66                         {
67                                 dest[pos-off] = src[pos];
68                                 esc = 1;
69                         }
70                         else if( src[pos] != '"' )
71                         {
72                                 dest[pos-off] = src[pos];
73                         }
74                         else
75                         {
76                                 dest[pos-off] = '\0';
77                                 break;
78                         }
79                 }
80         }
81
82         return (off > -1) ? strlen(dest) : -1;
83 }
84
85 int main(int argc, char *argv[])
86 {
87         char line[4096];
88         char key[4096];
89         char val[4096];
90         char tmp[4096];
91         int state  = 0;
92         int offset = 0;
93         int length = 0;
94         uint32_t key_id, val_id;
95
96         FILE *in;
97         FILE *out;
98
99         lmo_entry_t *head  = NULL;
100         lmo_entry_t *entry = NULL;
101
102         if( (argc != 3) || ((in = fopen(argv[1], "r")) == NULL) || ((out = fopen(argv[2], "w")) == NULL) )
103                 usage(argv[0]);
104
105         memset(line, 0, sizeof(key));
106         memset(key, 0, sizeof(val));
107         memset(val, 0, sizeof(val));
108
109         while( (NULL != fgets(line, sizeof(line), in)) || (state >= 2 && feof(in)) )
110         {
111                 if( state == 0 && strstr(line, "msgid \"") == line )
112                 {
113                         switch(extract_string(line, key, sizeof(key)))
114                         {
115                                 case -1:
116                                         die("Syntax error in msgid");
117                                 case 0:
118                                         state = 1;
119                                         break;
120                                 default:
121                                         state = 2;
122                         }
123                 }
124                 else if( state == 1 || state == 2 )
125                 {
126                         if( strstr(line, "msgstr \"") == line || state == 2 )
127                         {
128                                 switch(extract_string(line, val, sizeof(val)))
129                                 {
130                                         case -1:
131                                                 state = 4;
132                                                 break;
133                                         default:
134                                                 state = 3;
135                                 }
136                         }
137                         else
138                         {
139                                 switch(extract_string(line, tmp, sizeof(tmp)))
140                                 {
141                                         case -1:
142                                                 state = 2;
143                                                 break;
144                                         default:
145                                                 strcat(key, tmp);
146                                 }
147                         }
148                 }
149                 else if( state == 3 )
150                 {
151                         switch(extract_string(line, tmp, sizeof(tmp)))
152                         {
153                                 case -1:
154                                         state = 4;
155                                         break;
156                                 default:
157                                         strcat(val, tmp);
158                         }
159                 }
160
161                 if( state == 4 )
162                 {
163                         if( strlen(key) > 0 && strlen(val) > 0 )
164                         {
165                                 key_id = sfh_hash(key, strlen(key));
166                                 val_id = sfh_hash(val, strlen(val));
167
168                                 if( key_id != val_id )
169                                 {
170                                         if( (entry = (lmo_entry_t *) malloc(sizeof(lmo_entry_t))) != NULL )
171                                         {
172                                                 memset(entry, 0, sizeof(entry));
173                                                 length = strlen(val) + ((4 - (strlen(val) % 4)) % 4);
174
175                                                 entry->key_id = htonl(key_id);
176                                                 entry->val_id = htonl(val_id);
177                                                 entry->offset = htonl(offset);
178                                                 entry->length = htonl(strlen(val));
179
180                                                 print(val, length, 1, out);
181                                                 offset += length;
182
183                                                 entry->next = head;
184                                                 head = entry;
185                                         }
186                                         else
187                                         {
188                                                 die("Out of memory");
189                                         }
190                                 }
191                         }
192
193                         state = 0;
194                         memset(key, 0, sizeof(key));
195                         memset(val, 0, sizeof(val));
196                 }
197
198                 memset(line, 0, sizeof(line));
199         }
200
201         entry = head;
202         while( entry != NULL )
203         {
204                 print(&entry->key_id, sizeof(uint32_t), 1, out);
205                 print(&entry->val_id, sizeof(uint32_t), 1, out);
206                 print(&entry->offset, sizeof(uint32_t), 1, out);
207                 print(&entry->length, sizeof(uint32_t), 1, out);
208                 entry = entry->next;
209         }
210
211         if( offset > 0 )
212         {
213                 offset = htonl(offset);
214                 print(&offset, sizeof(uint32_t), 1, out);
215                 fsync(fileno(out));
216                 fclose(out);
217         }
218         else
219         {
220                 fclose(out);
221                 unlink(argv[2]);
222         }
223
224         fclose(in);
225         return(0);
226 }