80{
81
82# define NUM_PIECES 9
83# define WORK_SIZE 1024
85 int current_piece = 0;
86 int is_negative = 0;
87
88 double degrees = 0.0;
89 double minutes = 0.0;
90 double seconds = 0.0;
91
92 int compass_dir_piece = -1;
93
94 int reading_deg = 0;
95 int deg_digits = 0;
96 int deg_has_decpoint = 0;
97 int deg_dec_digits = 0;
98 int deg_piece = -1;
99
100 int reading_min = 0;
101 int min_digits = 0;
102 int min_has_decpoint = 0;
103 int min_dec_digits = 0;
104 int min_piece = -1;
105
106 int reading_sec = 0;
107 int sec_digits = 0;
108 int sec_has_decpoint = 0;
109 int sec_dec_digits = 0;
110 int sec_piece = -1;
111
112 int round_pow = 0;
113
114 int format_length = ((NULL == format) ? 0 : strlen(format));
115
117
118 int index, following_byte_index;
119 int multibyte_char_width = 1;
120
121
122
123
125 {
126 pieces[index][0] = '\0';
127 }
128
129
130 if (0 == format_length)
131 {
132
133 format = "D\xC2\xB0""M'S.SSS\"C";
134 format_length = strlen(format);
135 }
137 {
138
139
141 }
142
143 for (index = 0; index < format_length; index++)
144 {
145 char next_char = format[index];
146 switch (next_char)
147 {
148 case 'D':
149 if (reading_deg)
150 {
151
152 deg_has_decpoint ? deg_dec_digits++ : deg_digits++;
153 }
154 else
155 {
156
157 current_piece++;
158 deg_piece = current_piece;
159 if (deg_digits > 0)
160 {
161 lwerror(
"Bad format, cannot include degrees (DD.DDD) more than once.");
162 }
163 reading_deg = 1;
164 reading_min = 0;
165 reading_sec = 0;
166 deg_digits++;
167 }
168 break;
169 case 'M':
170 if (reading_min)
171 {
172
173 min_has_decpoint ? min_dec_digits++ : min_digits++;
174 }
175 else
176 {
177
178 current_piece++;
179 min_piece = current_piece;
180 if (min_digits > 0)
181 {
182 lwerror(
"Bad format, cannot include minutes (MM.MMM) more than once.");
183 }
184 reading_deg = 0;
185 reading_min = 1;
186 reading_sec = 0;
187 min_digits++;
188 }
189 break;
190 case 'S':
191 if (reading_sec)
192 {
193
194 sec_has_decpoint ? sec_dec_digits++ : sec_digits++;
195 }
196 else
197 {
198
199 current_piece++;
200 sec_piece = current_piece;
201 if (sec_digits > 0)
202 {
203 lwerror(
"Bad format, cannot include seconds (SS.SSS) more than once.");
204 }
205 reading_deg = 0;
206 reading_min = 0;
207 reading_sec = 1;
208 sec_digits++;
209 }
210 break;
211 case 'C':
212
213 if (reading_deg || reading_min || reading_sec)
214 {
215
216 reading_deg = 0;
217 reading_min = 0;
218 reading_sec = 0;
219 }
220 current_piece++;
221
222 if (compass_dir_piece >= 0)
223 {
224 lwerror(
"Bad format, cannot include compass dir (C) more than once.");
225 }
226
227 compass_dir_piece = current_piece;
228 current_piece++;
229 break;
230 case '.':
231
232 if (reading_deg)
233 {
234 deg_has_decpoint = 1;
235 }
236 else if (reading_min)
237 {
238 min_has_decpoint = 1;
239 }
240 else if (reading_sec)
241 {
242 sec_has_decpoint = 1;
243 }
244 else
245 {
246
247 strncat(pieces[current_piece], &next_char, 1);
248 }
249 break;
250 default:
251
252 if (reading_deg || reading_min || reading_sec)
253 {
254
255 current_piece++;
256 reading_deg = 0;
257 reading_min = 0;
258 reading_sec = 0;
259 }
260
261
262 multibyte_char_width = 1;
263 if (next_char & 0x80)
264 {
265 if ((next_char & 0xF8) == 0xF0)
266 {
267 multibyte_char_width += 3;
268 }
269 else if ((next_char & 0xF0) == 0xE0)
270 {
271 multibyte_char_width += 2;
272 }
273 else if ((next_char & 0xE0) == 0xC0)
274 {
275 multibyte_char_width += 1;
276 }
277 else
278 {
279 lwerror(
"Bad format, invalid high-order byte found first, format string may not be UTF-8.");
280 }
281 }
282 if (multibyte_char_width > 1)
283 {
284 if (index + multibyte_char_width >= format_length)
285 {
286 lwerror(
"Bad format, UTF-8 character first byte found with insufficient following bytes, format string may not be UTF-8.");
287 }
288 for (following_byte_index = (index + 1); following_byte_index < (index + multibyte_char_width); following_byte_index++)
289 {
290 if ((format[following_byte_index] & 0xC0) != 0x80)
291 {
292 lwerror(
"Bad format, invalid byte found following leading byte of multibyte character, format string may not be UTF-8.");
293 }
294 }
295 }
296
297 strncat(pieces[current_piece], &(format[index]), multibyte_char_width);
298
299 index += multibyte_char_width - 1;
300 break;
301 }
303 {
304 lwerror(
"Internal error, somehow needed more pieces than it should.");
305 }
306 }
307 if (deg_piece < 0)
308 {
309 lwerror(
"Bad format, degrees (DD.DDD) must be included.");
310 }
311
312
313 if (val < 0)
314 {
315 val *= -1;
316 is_negative = 1;
317 }
318 degrees = val;
319 if (min_digits > 0)
320 {
321
322 minutes = modf(val, °rees) * 60;
323 }
324 if (sec_digits > 0)
325 {
326 if (0 == min_digits)
327 {
328 lwerror(
"Bad format, cannot include seconds (SS.SSS) without including minutes (MM.MMM).");
329 }
330 seconds = modf(minutes, &minutes) * 60;
331 if (sec_piece >= 0)
332 {
333
334 round_pow = pow(10, sec_dec_digits);
335 if (lround(seconds * round_pow) >= 60 * round_pow)
336 {
337 minutes += 1;
338 seconds = 0;
339
340 if (lround(minutes * round_pow) >= 60 * round_pow)
341 {
342 degrees += 1;
343 minutes = 0;
344 }
345 }
346 }
347 }
348
349
350 if (compass_dir_piece >= 0)
351 {
352 strcpy(pieces[compass_dir_piece], is_negative ? neg_dir_symbol : pos_dir_symbol);
353 }
354 else if (is_negative)
355 {
356 degrees *= -1;
357 }
358
359
360 if (deg_digits + deg_dec_digits + 2 >
WORK_SIZE)
361 {
362 lwerror(
"Bad format, degrees (DD.DDD) number of digits was greater than our working limit.");
363 }
364 if(deg_piece >= 0)
365 {
366 snprintf(pieces[deg_piece],
WORK_SIZE,
"%*.*f", deg_digits, deg_dec_digits, degrees);
367 }
368
369 if (min_piece >= 0)
370 {
371
372 if (min_digits + min_dec_digits + 2 >
WORK_SIZE)
373 {
374 lwerror(
"Bad format, minutes (MM.MMM) number of digits was greater than our working limit.");
375 }
376 snprintf(pieces[min_piece],
WORK_SIZE,
"%*.*f", min_digits, min_dec_digits, minutes);
377 }
378 if (sec_piece >= 0)
379 {
380
381 if (sec_digits + sec_dec_digits + 2 >
WORK_SIZE)
382 {
383 lwerror(
"Bad format, seconds (SS.SSS) number of digits was greater than our working limit.");
384 }
385 snprintf(pieces[sec_piece],
WORK_SIZE,
"%*.*f", sec_digits, sec_dec_digits, seconds);
386 }
387
388
391
392
393 strcpy(
result, pieces[0]);
395 {
396 strcat(
result, pieces[index]);
397 }
398
400}
char result[OUT_DOUBLE_BUFFER_SIZE]
void * lwalloc(size_t size)
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.