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