PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
tzparser.c File Reference
#include "postgres.h"
#include <ctype.h>
#include "miscadmin.h"
#include "storage/fd.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/tzparser.h"
Include dependency graph for tzparser.c:

Go to the source code of this file.

Macros

#define WHITESPACE   " \t\n\r"
 

Functions

static bool validateTzEntry (tzEntry *tzentry)
 
static bool splitTzLine (const char *filename, int lineno, char *line, tzEntry *tzentry)
 
static int addToArray (tzEntry **base, int *arraysize, int n, tzEntry *entry, bool override)
 
static int ParseTzFile (const char *filename, int depth, tzEntry **base, int *arraysize, int n)
 
TimeZoneAbbrevTableload_tzoffsets (const char *filename)
 

Macro Definition Documentation

#define WHITESPACE   " \t\n\r"

Definition at line 34 of file tzparser.c.

Referenced by ParseTzFile(), and splitTzLine().

Function Documentation

static int addToArray ( tzEntry **  base,
int *  arraysize,
int  n,
tzEntry entry,
bool  override 
)
static

Definition at line 188 of file tzparser.c.

References tzEntry::abbrev, cmp(), tzEntry::filename, GUC_check_errdetail, GUC_check_errmsg, tzEntry::is_dst, tzEntry::lineno, memmove, NULL, tzEntry::offset, repalloc(), and tzEntry::zone.

Referenced by ParseTzFile().

190 {
191  tzEntry *arrayptr;
192  int low;
193  int high;
194 
195  /*
196  * Search the array for a duplicate; as a useful side effect, the array is
197  * maintained in sorted order. We use strcmp() to ensure we match the
198  * sort order datetime.c expects.
199  */
200  arrayptr = *base;
201  low = 0;
202  high = n - 1;
203  while (low <= high)
204  {
205  int mid = (low + high) >> 1;
206  tzEntry *midptr = arrayptr + mid;
207  int cmp;
208 
209  cmp = strcmp(entry->abbrev, midptr->abbrev);
210  if (cmp < 0)
211  high = mid - 1;
212  else if (cmp > 0)
213  low = mid + 1;
214  else
215  {
216  /*
217  * Found a duplicate entry; complain unless it's the same.
218  */
219  if ((midptr->zone == NULL && entry->zone == NULL &&
220  midptr->offset == entry->offset &&
221  midptr->is_dst == entry->is_dst) ||
222  (midptr->zone != NULL && entry->zone != NULL &&
223  strcmp(midptr->zone, entry->zone) == 0))
224  {
225  /* return unchanged array */
226  return n;
227  }
228  if (override)
229  {
230  /* same abbrev but something is different, override */
231  midptr->zone = entry->zone;
232  midptr->offset = entry->offset;
233  midptr->is_dst = entry->is_dst;
234  return n;
235  }
236  /* same abbrev but something is different, complain */
237  GUC_check_errmsg("time zone abbreviation \"%s\" is multiply defined",
238  entry->abbrev);
239  GUC_check_errdetail("Entry in time zone file \"%s\", line %d, conflicts with entry in file \"%s\", line %d.",
240  midptr->filename, midptr->lineno,
241  entry->filename, entry->lineno);
242  return -1;
243  }
244  }
245 
246  /*
247  * No match, insert at position "low".
248  */
249  if (n >= *arraysize)
250  {
251  *arraysize *= 2;
252  *base = (tzEntry *) repalloc(*base, *arraysize * sizeof(tzEntry));
253  }
254 
255  arrayptr = *base + low;
256 
257  memmove(arrayptr + 1, arrayptr, (n - low) * sizeof(tzEntry));
258 
259  memcpy(arrayptr, entry, sizeof(tzEntry));
260 
261  return n + 1;
262 }
#define GUC_check_errdetail
Definition: guc.h:407
int offset
Definition: tzparser.h:29
#define GUC_check_errmsg
Definition: guc.h:403
char * zone
Definition: tzparser.h:27
int lineno
Definition: tzparser.h:32
bool is_dst
Definition: tzparser.h:30
const char * filename
Definition: tzparser.h:33
#define memmove(d, s, c)
Definition: c.h:1058
char * abbrev
Definition: tzparser.h:26
#define NULL
Definition: c.h:229
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:963
static int cmp(const chr *x, const chr *y, size_t len)
Definition: regc_locale.c:742
TimeZoneAbbrevTable* load_tzoffsets ( const char *  filename)

Definition at line 438 of file tzparser.c.

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate(), ConvertTimeZoneAbbrevs(), CurrentMemoryContext, GUC_check_errmsg, MemoryContextDelete(), MemoryContextSwitchTo(), NULL, palloc(), ParseTzFile(), and result.

Referenced by check_timezone_abbreviations().

439 {
441  MemoryContext tmpContext;
442  MemoryContext oldContext;
443  tzEntry *array;
444  int arraysize;
445  int n;
446 
447  /*
448  * Create a temp memory context to work in. This makes it easy to clean
449  * up afterwards.
450  */
452  "TZParserMemory",
454  oldContext = MemoryContextSwitchTo(tmpContext);
455 
456  /* Initialize array at a reasonable size */
457  arraysize = 128;
458  array = (tzEntry *) palloc(arraysize * sizeof(tzEntry));
459 
460  /* Parse the file(s) */
461  n = ParseTzFile(filename, 0, &array, &arraysize, 0);
462 
463  /* If no errors so far, let datetime.c allocate memory & convert format */
464  if (n >= 0)
465  {
466  result = ConvertTimeZoneAbbrevs(array, n);
467  if (!result)
468  GUC_check_errmsg("out of memory");
469  }
470 
471  /* Clean up */
472  MemoryContextSwitchTo(oldContext);
473  MemoryContextDelete(tmpContext);
474 
475  return result;
476 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:200
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:175
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define GUC_check_errmsg
Definition: guc.h:403
return result
Definition: formatting.c:1633
static int ParseTzFile(const char *filename, int depth, tzEntry **base, int *arraysize, int n)
Definition: tzparser.c:276
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:322
#define NULL
Definition: c.h:229
static char * filename
Definition: pg_dumpall.c:89
void * palloc(Size size)
Definition: mcxt.c:849
TimeZoneAbbrevTable * ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs, int n)
Definition: datetime.c:4519
static int ParseTzFile ( const char *  filename,
int  depth,
tzEntry **  base,
int *  arraysize,
int  n 
)
static

Definition at line 276 of file tzparser.c.

References addToArray(), AllocateDir(), AllocateFile(), FreeDir(), FreeFile(), get_share_path(), GUC_check_errhint, GUC_check_errmsg, MAXPGPATH, my_exec_path, NULL, pg_strncasecmp(), pstrdup(), share_path, snprintf(), splitTzLine(), validateTzEntry(), and WHITESPACE.

Referenced by load_tzoffsets().

278 {
279  char share_path[MAXPGPATH];
280  char file_path[MAXPGPATH];
281  FILE *tzFile;
282  char tzbuf[1024];
283  char *line;
284  tzEntry tzentry;
285  int lineno = 0;
286  bool override = false;
287  const char *p;
288 
289  /*
290  * We enforce that the filename is all alpha characters. This may be
291  * overly restrictive, but we don't want to allow access to anything
292  * outside the timezonesets directory, so for instance '/' *must* be
293  * rejected.
294  */
295  for (p = filename; *p; p++)
296  {
297  if (!isalpha((unsigned char) *p))
298  {
299  /* at level 0, just use guc.c's regular "invalid value" message */
300  if (depth > 0)
301  GUC_check_errmsg("invalid time zone file name \"%s\"",
302  filename);
303  return -1;
304  }
305  }
306 
307  /*
308  * The maximal recursion depth is a pretty arbitrary setting. It is hard
309  * to imagine that someone needs more than 3 levels so stick with this
310  * conservative setting until someone complains.
311  */
312  if (depth > 3)
313  {
314  GUC_check_errmsg("time zone file recursion limit exceeded in file \"%s\"",
315  filename);
316  return -1;
317  }
318 
319  get_share_path(my_exec_path, share_path);
320  snprintf(file_path, sizeof(file_path), "%s/timezonesets/%s",
321  share_path, filename);
322  tzFile = AllocateFile(file_path, "r");
323  if (!tzFile)
324  {
325  /*
326  * Check to see if the problem is not the filename but the directory.
327  * This is worth troubling over because if the installation share/
328  * directory is missing or unreadable, this is likely to be the first
329  * place we notice a problem during postmaster startup.
330  */
331  int save_errno = errno;
332  DIR *tzdir;
333 
334  snprintf(file_path, sizeof(file_path), "%s/timezonesets",
335  share_path);
336  tzdir = AllocateDir(file_path);
337  if (tzdir == NULL)
338  {
339  GUC_check_errmsg("could not open directory \"%s\": %m",
340  file_path);
341  GUC_check_errhint("This may indicate an incomplete PostgreSQL installation, or that the file \"%s\" has been moved away from its proper location.",
342  my_exec_path);
343  return -1;
344  }
345  FreeDir(tzdir);
346  errno = save_errno;
347 
348  /*
349  * otherwise, if file doesn't exist and it's level 0, guc.c's
350  * complaint is enough
351  */
352  if (errno != ENOENT || depth > 0)
353  GUC_check_errmsg("could not read time zone file \"%s\": %m",
354  filename);
355 
356  return -1;
357  }
358 
359  while (!feof(tzFile))
360  {
361  lineno++;
362  if (fgets(tzbuf, sizeof(tzbuf), tzFile) == NULL)
363  {
364  if (ferror(tzFile))
365  {
366  GUC_check_errmsg("could not read time zone file \"%s\": %m",
367  filename);
368  return -1;
369  }
370  /* else we're at EOF after all */
371  break;
372  }
373  if (strlen(tzbuf) == sizeof(tzbuf) - 1)
374  {
375  /* the line is too long for tzbuf */
376  GUC_check_errmsg("line is too long in time zone file \"%s\", line %d",
377  filename, lineno);
378  return -1;
379  }
380 
381  /* skip over whitespace */
382  line = tzbuf;
383  while (*line && isspace((unsigned char) *line))
384  line++;
385 
386  if (*line == '\0') /* empty line */
387  continue;
388  if (*line == '#') /* comment line */
389  continue;
390 
391  if (pg_strncasecmp(line, "@INCLUDE", strlen("@INCLUDE")) == 0)
392  {
393  /* pstrdup so we can use filename in result data structure */
394  char *includeFile = pstrdup(line + strlen("@INCLUDE"));
395 
396  includeFile = strtok(includeFile, WHITESPACE);
397  if (!includeFile || !*includeFile)
398  {
399  GUC_check_errmsg("@INCLUDE without file name in time zone file \"%s\", line %d",
400  filename, lineno);
401  return -1;
402  }
403  n = ParseTzFile(includeFile, depth + 1,
404  base, arraysize, n);
405  if (n < 0)
406  return -1;
407  continue;
408  }
409 
410  if (pg_strncasecmp(line, "@OVERRIDE", strlen("@OVERRIDE")) == 0)
411  {
412  override = true;
413  continue;
414  }
415 
416  if (!splitTzLine(filename, lineno, line, &tzentry))
417  return -1;
418  if (!validateTzEntry(&tzentry))
419  return -1;
420  n = addToArray(base, arraysize, n, &tzentry, override);
421  if (n < 0)
422  return -1;
423  }
424 
425  FreeFile(tzFile);
426 
427  return n;
428 }
char * pstrdup(const char *in)
Definition: mcxt.c:1077
#define GUC_check_errmsg
Definition: guc.h:403
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
static char * share_path
Definition: initdb.c:118
static bool validateTzEntry(tzEntry *tzentry)
Definition: tzparser.c:51
int pg_strncasecmp(const char *s1, const char *s2, size_t n)
Definition: pgstrcasecmp.c:69
Definition: dirent.c:25
static int addToArray(tzEntry **base, int *arraysize, int n, tzEntry *entry, bool override)
Definition: tzparser.c:188
#define MAXPGPATH
static int ParseTzFile(const char *filename, int depth, tzEntry **base, int *arraysize, int n)
Definition: tzparser.c:276
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2094
DIR * AllocateDir(const char *dirname)
Definition: fd.c:2335
char my_exec_path[MAXPGPATH]
Definition: globals.c:64
#define NULL
Definition: c.h:229
int FreeFile(FILE *file)
Definition: fd.c:2277
static char * filename
Definition: pg_dumpall.c:89
#define GUC_check_errhint
Definition: guc.h:411
#define WHITESPACE
Definition: tzparser.c:34
static bool splitTzLine(const char *filename, int lineno, char *line, tzEntry *tzentry)
Definition: tzparser.c:98
void get_share_path(const char *my_exec_path, char *ret_path)
Definition: path.c:704
int FreeDir(DIR *dir)
Definition: fd.c:2444
static bool splitTzLine ( const char *  filename,
int  lineno,
char *  line,
tzEntry tzentry 
)
static

Definition at line 98 of file tzparser.c.

References tzEntry::abbrev, tzEntry::filename, filename, GUC_check_errmsg, tzEntry::is_dst, tzEntry::lineno, NULL, tzEntry::offset, pg_strcasecmp(), pstrdup(), WHITESPACE, and tzEntry::zone.

Referenced by ParseTzFile().

99 {
100  char *abbrev;
101  char *offset;
102  char *offset_endptr;
103  char *remain;
104  char *is_dst;
105 
106  tzentry->lineno = lineno;
107  tzentry->filename = filename;
108 
109  abbrev = strtok(line, WHITESPACE);
110  if (!abbrev)
111  {
112  GUC_check_errmsg("missing time zone abbreviation in time zone file \"%s\", line %d",
113  filename, lineno);
114  return false;
115  }
116  tzentry->abbrev = pstrdup(abbrev);
117 
118  offset = strtok(NULL, WHITESPACE);
119  if (!offset)
120  {
121  GUC_check_errmsg("missing time zone offset in time zone file \"%s\", line %d",
122  filename, lineno);
123  return false;
124  }
125 
126  /* We assume zone names don't begin with a digit or sign */
127  if (isdigit((unsigned char) *offset) || *offset == '+' || *offset == '-')
128  {
129  tzentry->zone = NULL;
130  tzentry->offset = strtol(offset, &offset_endptr, 10);
131  if (offset_endptr == offset || *offset_endptr != '\0')
132  {
133  GUC_check_errmsg("invalid number for time zone offset in time zone file \"%s\", line %d",
134  filename, lineno);
135  return false;
136  }
137 
138  is_dst = strtok(NULL, WHITESPACE);
139  if (is_dst && pg_strcasecmp(is_dst, "D") == 0)
140  {
141  tzentry->is_dst = true;
142  remain = strtok(NULL, WHITESPACE);
143  }
144  else
145  {
146  /* there was no 'D' dst specifier */
147  tzentry->is_dst = false;
148  remain = is_dst;
149  }
150  }
151  else
152  {
153  /*
154  * Assume entry is a zone name. We do not try to validate it by
155  * looking up the zone, because that would force loading of a lot of
156  * zones that probably will never be used in the current session.
157  */
158  tzentry->zone = pstrdup(offset);
159  tzentry->offset = 0;
160  tzentry->is_dst = false;
161  remain = strtok(NULL, WHITESPACE);
162  }
163 
164  if (!remain) /* no more non-whitespace chars */
165  return true;
166 
167  if (remain[0] != '#') /* must be a comment */
168  {
169  GUC_check_errmsg("invalid syntax in time zone file \"%s\", line %d",
170  filename, lineno);
171  return false;
172  }
173  return true;
174 }
char * pstrdup(const char *in)
Definition: mcxt.c:1077
int offset
Definition: tzparser.h:29
#define GUC_check_errmsg
Definition: guc.h:403
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
char * zone
Definition: tzparser.h:27
int lineno
Definition: tzparser.h:32
bool is_dst
Definition: tzparser.h:30
const char * filename
Definition: tzparser.h:33
char * abbrev
Definition: tzparser.h:26
#define NULL
Definition: c.h:229
static char * filename
Definition: pg_dumpall.c:89
#define WHITESPACE
Definition: tzparser.c:34
static bool validateTzEntry ( tzEntry tzentry)
static

Definition at line 51 of file tzparser.c.

References tzEntry::abbrev, tzEntry::filename, GUC_check_errmsg, tzEntry::lineno, tzEntry::offset, pg_tolower(), and TOKMAXLEN.

Referenced by ParseTzFile().

52 {
53  unsigned char *p;
54 
55  /*
56  * Check restrictions imposed by datetkntbl storage format (see
57  * datetime.c)
58  */
59  if (strlen(tzentry->abbrev) > TOKMAXLEN)
60  {
61  GUC_check_errmsg("time zone abbreviation \"%s\" is too long (maximum %d characters) in time zone file \"%s\", line %d",
62  tzentry->abbrev, TOKMAXLEN,
63  tzentry->filename, tzentry->lineno);
64  return false;
65  }
66 
67  /*
68  * Sanity-check the offset: shouldn't exceed 14 hours
69  */
70  if (tzentry->offset > 14 * 60 * 60 ||
71  tzentry->offset < -14 * 60 * 60)
72  {
73  GUC_check_errmsg("time zone offset %d is out of range in time zone file \"%s\", line %d",
74  tzentry->offset,
75  tzentry->filename, tzentry->lineno);
76  return false;
77  }
78 
79  /*
80  * Convert abbrev to lowercase (must match datetime.c's conversion)
81  */
82  for (p = (unsigned char *) tzentry->abbrev; *p; p++)
83  *p = pg_tolower(*p);
84 
85  return true;
86 }
unsigned char pg_tolower(unsigned char ch)
Definition: pgstrcasecmp.c:122
int offset
Definition: tzparser.h:29
#define GUC_check_errmsg
Definition: guc.h:403
int lineno
Definition: tzparser.h:32
const char * filename
Definition: tzparser.h:33
char * abbrev
Definition: tzparser.h:26
#define TOKMAXLEN
Definition: datetime.h:207