LAL  7.5.0.1-08ee4f4
StrToGPS.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2007 Kipp Cannon
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
12  * Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 
20 #include <ctype.h>
21 #include <errno.h>
22 #include <locale.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <lal/Date.h>
26 #include <lal/LALDatatypes.h>
27 #include <lal/LALMalloc.h>
28 #include <lal/XLALError.h>
29 
30 
31 /*
32  * Check for a base 10 or base 16 number.
33  */
34 
35 
36 static int isbase10(const char *s, int radix)
37 {
38  if(*s == radix)
39  s++;
40  return isdigit(*s);
41 }
42 
43 
44 static int isbase16(const char *s, int radix)
45 {
46  if(*s == '0') {
47  s++;
48  if(*s == 'X' || *s == 'x') {
49  s++;
50  if(*s == radix)
51  s++;
52  return isxdigit(*s);
53  }
54  }
55  return 0;
56 }
57 
58 
59 /*
60  * Check that a string contains an exponent.
61  */
62 
63 
64 static int isdecimalexp(const char *s)
65 {
66  if(*s == 'E' || *s == 'e') {
67  s++;
68  if(*s == '+' || *s == '-')
69  s++;
70  return isdigit(*s);
71  }
72  return 0;
73 }
74 
75 
76 static int isbinaryexp(const char *s)
77 {
78  if(*s == 'P' || *s == 'p') {
79  s++;
80  if(*s == '+' || *s == '-')
81  s++;
82  return isdigit(*s);
83  }
84  return 0;
85 }
86 
87 
88 /**
89  * Parse an ASCII string into a LIGOTimeGPS structure.
90  */
91 int XLALStrToGPS(LIGOTimeGPS *t, const char *nptr, char **endptr)
92 {
93  union { char *s; const char *cs; } pconv; /* this is bad */
94  int olderrno;
95  int radix;
96  char *digits;
97  int len=0;
98  int sign;
99  int base;
100  int radixpos;
101  int exppart;
102 
103  /* save and clear C library errno so we can check for failures */
104  olderrno = errno;
105  errno = 0;
106 
107  /* retrieve the radix character */
108  radix = localeconv()->decimal_point[0];
109 
110  /* this is bad ... there is a reason for warnings! */
111  pconv.cs = nptr;
112 
113  /* consume leading white space */
114  while(isspace(*(pconv.cs)))
115  (pconv.cs)++;
116  if(endptr)
117  *endptr = pconv.s;
118 
119  /* determine the sign */
120  if(*(pconv.cs) == '-') {
121  sign = -1;
122  (pconv.cs)++;
123  } else if(*(pconv.cs) == '+') {
124  sign = +1;
125  (pconv.cs)++;
126  } else
127  sign = +1;
128 
129  /* determine the base */
130  if(isbase16((pconv.cs), radix)) {
131  base = 16;
132  (pconv.cs) += 2;
133  } else if(isbase10((pconv.cs), radix)) {
134  base = 10;
135  } else {
136  /* this isn't a recognized number */
137  XLALGPSSet(t, 0, 0);
138  return 0;
139  }
140 
141  /* count the number of digits including the radix but not including
142  * the exponent. */
143  radixpos = -1;
144  switch(base) {
145  case 10:
146  for(len = 0; 1; len++) {
147  if(isdigit((pconv.cs)[len]))
148  continue;
149  if((pconv.cs)[len] == radix && radixpos < 0) {
150  radixpos = len;
151  continue;
152  }
153  break;
154  }
155  break;
156 
157  case 16:
158  for(len = 0; 1; len++) {
159  if(isxdigit((pconv.cs)[len]))
160  continue;
161  if((pconv.cs)[len] == radix && radixpos < 0) {
162  radixpos = len;
163  continue;
164  }
165  break;
166  }
167  break;
168  }
169 
170  /* copy the digits into a scratch space, removing the radix character
171  * if one was found */
172  if(radixpos >= 0) {
173  digits = malloc(len + 1);
174  memcpy(digits, (pconv.cs), radixpos);
175  memcpy(digits + radixpos, (pconv.cs) + radixpos + 1, len - radixpos - 1);
176  digits[len - 1] = '\0';
177  (pconv.cs) += len;
178  len--;
179  } else {
180  digits = malloc(len + 2);
181  memcpy(digits, (pconv.cs), len);
182  digits[len] = '\0';
183  radixpos = len;
184  (pconv.cs) += len;
185  }
186 
187  /* check for and parse an exponent, performing an adjustment of the
188  * radix position */
189  exppart = 1;
190  switch(base) {
191  case 10:
192  /* exponent is the number of powers of 10 */
193  if(isdecimalexp((pconv.cs)))
194  radixpos += strtol((pconv.cs) + 1, &pconv.s, 10);
195  break;
196 
197  case 16:
198  /* exponent is the number of powers of 2 */
199  if(isbinaryexp((pconv.cs))) {
200  exppart = strtol((pconv.cs) + 1, &pconv.s, 10);
201  radixpos += exppart / 4;
202  exppart %= 4;
203  if(exppart < 0) {
204  radixpos--;
205  exppart += 4;
206  }
207  exppart = 1 << exppart;
208  }
209  break;
210  }
211 
212  /* save end of converted characters */
213  if(endptr)
214  *endptr = pconv.s;
215 
216  /* insert the radix character, padding the scratch digits with zeroes
217  * if needed */
218  if(radixpos < 2) {
219  digits = realloc(digits, len + 2 + (2 - radixpos));
220  memmove(digits + (2 - radixpos) + 1, digits, len + 1);
221  memset(digits, '0', (2 - radixpos) + 1);
222  if(radixpos == 1)
223  digits[1] = digits[2];
224  radixpos = 2;
225  } else if(radixpos > len) {
226  digits = realloc(digits, radixpos + 2);
227  memset(digits + len, '0', radixpos - len);
228  digits[radixpos + 1] = '\0';
229  } else {
230  memmove(digits + radixpos + 1, digits + radixpos, len - radixpos + 1);
231  }
232  digits[radixpos] = radix;
233 
234  /* parse the integer part */
235  XLALINT8NSToGPS(t, sign * strtol(digits, NULL, base) * exppart * XLAL_BILLION_INT8);
236 
237  /* parse the fractional part */
238  if(errno != ERANGE) {
239  switch(base) {
240  case 10:
241  break;
242 
243  case 16:
244  digits[radixpos - 2] = '0';
245  digits[radixpos - 1] = 'x';
246  radixpos -= 2;
247  break;
248  }
249  XLALGPSAdd(t, sign * strtod(digits + radixpos, NULL) * exppart);
250  }
251 
252  /* free the scratch space */
253  free(digits);
254 
255  /* check for failures and restore errno if there weren't any */
256  if(errno == ERANGE)
258  errno = olderrno;
259 
260  /* success */
261  return 0;
262 }
263 
264 
265 /**
266  * \ingroup Date_h
267  * \brief Return a string containing the ASCII base 10 representation of a
268  * LIGOTimeGPS. If s is not NULL, then the string is written to that
269  * location which must be large enough to hold the string plus a 0
270  * terminator. If s is NULL then a new buffer is allocated, and the string
271  * written to it; free the buffer with XLALFree(). The return value is
272  * the address of the string or NULL on failure.
273  */
274 char *XLALGPSToStr(char *s, const LIGOTimeGPS *t)
275 {
276  char *end;
277  /* so we can play with it */
278  LIGOTimeGPS copy = *t;
279 
280  /* make sure we've got a buffer */
281 
282  if(!s) {
283  /* 22 = 9 digits to the right of the decimal point +
284  * decimal point + upto 10 digits to the left of the
285  * decimal point plus an optional sign + a null */
286  s = XLALMalloc(22 * sizeof(*s));
287  if(!s)
289  }
290 
291  /* normalize the fractional part */
292 
293  while(labs(copy.gpsNanoSeconds) >= XLAL_BILLION_INT4) {
294  if(copy.gpsNanoSeconds < 0) {
295  copy.gpsSeconds -= 1;
297  } else {
298  copy.gpsSeconds += 1;
300  }
301  }
302 
303  /* if both components are non-zero, make sure they have the same
304  * sign */
305 
306  if(copy.gpsSeconds > 0 && copy.gpsNanoSeconds < 0) {
307  copy.gpsSeconds -= 1;
309  } else if(copy.gpsSeconds < 0 && copy.gpsNanoSeconds > 0) {
310  copy.gpsSeconds += 1;
312  }
313 
314  /* print */
315 
316  if(copy.gpsSeconds < 0 || copy.gpsNanoSeconds < 0)
317  /* number is negative */
318  end = s + sprintf(s, "-%ld.%09ld", labs(copy.gpsSeconds), labs(copy.gpsNanoSeconds));
319  else
320  /* number is non-negative */
321  end = s + sprintf(s, "%ld.%09ld", (long) copy.gpsSeconds, (long) copy.gpsNanoSeconds);
322 
323  /* remove trailing 0s and decimal point */
324 
325  while(*(--end) == '0')
326  *end = 0;
327  if(*end == '.')
328  *end = 0;
329 
330  /* done */
331 
332  return s;
333 }
#define XLAL_BILLION_INT4
Definition: Date.h:39
#define XLAL_BILLION_INT8
Definition: Date.h:40
static int sign(int s)
Definition: LALStringTest.c:27
static int isbinaryexp(const char *s)
Definition: StrToGPS.c:76
static int isbase16(const char *s, int radix)
Definition: StrToGPS.c:44
static int isbase10(const char *s, int radix)
Definition: StrToGPS.c:36
int XLALStrToGPS(LIGOTimeGPS *t, const char *nptr, char **endptr)
Parse an ASCII string into a LIGOTimeGPS structure.
Definition: StrToGPS.c:91
static int isdecimalexp(const char *s)
Definition: StrToGPS.c:64
char * XLALGPSToStr(char *s, const LIGOTimeGPS *t)
Return a string containing the ASCII base 10 representation of a LIGOTimeGPS.
Definition: StrToGPS.c:274
#define XLALMalloc(n)
Definition: LALMalloc.h:44
#define XLAL_ERROR_NULL(...)
Macro to invoke a failure from a XLAL routine returning a pointer.
Definition: XLALError.h:713
#define XLAL_ERROR(...)
Macro to invoke a failure from a XLAL routine returning an integer.
Definition: XLALError.h:700
@ XLAL_ERANGE
Output range error.
Definition: XLALError.h:411
@ XLAL_EFUNC
Internal function call failed bit: "or" this with existing error number.
Definition: XLALError.h:462
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
Adds a double to a GPS time.
Definition: XLALTime.c:131
LIGOTimeGPS * XLALGPSSet(LIGOTimeGPS *epoch, INT4 gpssec, INT8 gpsnan)
Sets GPS time given GPS integer seconds and residual nanoseconds.
Definition: XLALTime.c:63
LIGOTimeGPS * XLALINT8NSToGPS(LIGOTimeGPS *epoch, INT8 ns)
Converts nano seconds stored as an INT8 to GPS time.
Definition: XLALTime.c:46
Epoch relative to GPS epoch, see LIGOTimeGPS type for more details.
Definition: LALDatatypes.h:458
INT4 gpsSeconds
Seconds since 0h UTC 6 Jan 1980.
Definition: LALDatatypes.h:459
INT4 gpsNanoSeconds
Residual nanoseconds.
Definition: LALDatatypes.h:460