FORS Pipeline Reference Manual  4.12.5
fors_paf.c
1 /* $Id: fors_paf.c,v 1.6 2013-08-14 13:28:56 cgarcia Exp $
2  *
3  * This file is part of the FORS Library
4  * Copyright (C) 2002-2010 European Southern Observatory
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /*
22  * $Author: cgarcia $
23  * $Date: 2013-08-14 13:28:56 $
24  * $Revision: 1.6 $
25  * $Name: not supported by cvs2svn $
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include <unistd.h>
37 #include <pwd.h>
38 #include <sys/types.h>
39 #include <time.h>
40 #include <assert.h>
41 
42 #include <cpl.h>
43 #include <fors_paf.h>
44 
45 
46 /*
47  * Reserved PAF header keywords
48  */
49 
50 #define PAF_HDR_START "PAF.HDR.START"
51 #define PAF_TYPE "PAF.TYPE"
52 #define PAF_ID "PAF.ID"
53 #define PAF_NAME "PAF.NAME"
54 #define PAF_DESC "PAF.DESC"
55 #define PAF_CRTE_NAME "PAF.CRTE.NAME"
56 #define PAF_CRTE_TIME "PAF.CRTE.DAYTIM"
57 #define PAF_LCHG_NAME "PAF.LCHG.NAME"
58 #define PAF_LCHG_TIME "PAF.LCHG.DAYTIM"
59 #define PAF_CHCK_NAME "PAF.CHCK.NAME"
60 #define PAF_CHCK_TIME "PAF.CHCK.DAYTIM"
61 #define PAF_CHCK_CHECKSUM "PAF.CHCK.CHECKSUM"
62 #define PAF_HDR_END "PAF.HDR.END"
63 
64 
65 /*
66  * Value and comment field start position
67  */
68 
69 #define PAF_FIELD_OFFSET_VALUE 20
70 #define PAF_FIELD_OFFSET_COMMENT 45
71 
72 
81 /*
82  * PAF record definition. This corresponds to one line of a parameter file.
83  */
84 
86  char *name;
87  char *comment;
88  ForsPAFType type;
89 
90  union {
91  int *bval;
92  int *ival;
93  double *dval;
94  char *sval;
95  } data;
96 };
97 
98 typedef struct _FORS_PAF_RECORD_ ForsPAFRecord;
99 
100 
101 /*
102  * The PAF object
103  */
104 
105 struct _FORS_PAF_ {
106  char *name;
107  int nh;
108  int nr;
109  ForsPAFRecord **header;
110  ForsPAFRecord **records;
111 };
112 
113 
114 /*
115  * @brief
116  * Get current date and time in ISO8601 format.
117  *
118  * @return Pointer to a statically allocated string in the function
119  * (no need to free it). In case of failure, returns a @c NULL.
120  *
121  * This private function just returns the current time in ISO8601 format.
122  */
123 
124 #define TIME_ISO8601_LENGTH (20)
125 
126 static char *getTimeISO8601(void)
127 {
128 
129  static char timeISO8601[TIME_ISO8601_LENGTH];
130  time_t seconds = time((time_t *)0);
131 
132  if (strftime(timeISO8601, TIME_ISO8601_LENGTH,
133  "%Y-%m-%dT%T", localtime(&seconds)) == 0)
134  strcpy(timeISO8601, "0000-00-00T00:00:00");
135 
136  return timeISO8601;
137 
138 }
139 
140 
141 /*
142  * Compute record type size in bytes.
143  */
144 
145 inline static size_t
146 _forsPAFValueSize(ForsPAFType type, const void *value)
147 {
148  size_t sz;
149 
150  switch (type) {
151  case PAF_TYPE_BOOL:
152  sz = sizeof(int);
153  break;
154 
155  case PAF_TYPE_INT:
156  sz = sizeof(int);
157  break;
158 
159  case PAF_TYPE_DOUBLE:
160  sz = sizeof(double);
161  break;
162 
163  case PAF_TYPE_STRING:
164  sz = (strlen((char *)value) + 1) * sizeof(char);
165  break;
166 
167  default:
168  sz = 0;
169  break;
170  }
171 
172  return sz;
173 
174 }
175 
176 
177 /*
178  * Destroy a PAF record
179  */
180 
181 inline static void
182 _forsPAFRecordDestroy(ForsPAFRecord *record)
183 {
184 
185  if (record) {
186  cpl_free(record->name);
187  cpl_free((void *)record->data.sval);
188  cpl_free(record->comment);
189  cpl_free(record);
190  }
191 
192  return;
193 
194 }
195 
196 
197 /*
198  * Create a new PAF record
199  */
200 
201 inline static ForsPAFRecord *
202 _forsPAFRecordCreate(const char *name, ForsPAFType type, const void *value,
203  const char *comment)
204 {
205 
206  size_t sz;
207 
208  ForsPAFRecord *record = cpl_malloc(sizeof(ForsPAFRecord));
209 
210 
211  record->name = cpl_strdup(name);
212  record->comment = comment ? cpl_strdup(comment) : NULL;
213  record->type = type;
214 
215  sz = _forsPAFValueSize(type, value);
216 
217  if (sz == 0) {
218  record->data.sval = NULL;
219  }
220  else {
221  record->data.sval = (char *)cpl_malloc(sz);
222  }
223 
224  memcpy(record->data.sval, value, sz);
225 
226  return record;
227 
228 }
229 
230 /*
231  * Set name, value and comment of a PAF record
232  */
233 
234 inline static void
235 _forsPAFRecordSet(ForsPAFRecord *record, const char *name, ForsPAFType type,
236  const void *value, const char *comment)
237 {
238 
239  if (name) {
240  cpl_free(record->name);
241  record->name = cpl_strdup(name);
242  }
243 
244  if (comment) {
245  cpl_free(record->comment);
246  record->comment = cpl_strdup(comment);
247  }
248 
249  if (value) {
250  size_t sz = _forsPAFValueSize(type, value);
251 
252  if (record->data.sval) {
253  size_t size = _forsPAFValueSize(record->type, record->data.sval);
254 
255  if (sz != size)
256  record->data.sval = (char *)cpl_realloc(record->data.sval, sz);
257  }
258  else
259  record->data.sval = (char *)cpl_malloc(sz);
260 
261  memcpy(record->data.sval, value, sz);
262  record->type = type;
263  }
264 
265  return;
266 
267 }
268 
269 
270 /*
271  * Create a record from name, value and comment and append the record to
272  * the PAF header or record list.
273  */
274 
275 inline static int
276 _forsPAFAppend(ForsPAFRecord ***list, int *pos, const char *name,
277  ForsPAFType type, const void *value, const char *comment)
278 {
279 
280  ForsPAFRecord *record;
281 
282 
283  record = _forsPAFRecordCreate(name, type, value, comment);
284  if (!record)
285  return 1;
286 
287  if (pos[0] == 0) {
288  *list = cpl_malloc(sizeof(ForsPAFRecord *));
289  }
290  else {
291  *list = cpl_realloc(*list, (pos[0]+1) * sizeof(ForsPAFRecord *));
292  }
293 
294  (*list)[pos[0]] = record;
295  pos[0]++;
296 
297  return 0;
298 
299 }
300 
301 
302 /*
303  * Create a new PAF header.
304  */
305 
306 inline static ForsPAFRecord **
307 _forsPAFHeaderCreate(const char *name, const char *type, const char *id,
308  const char *desc, int *pos)
309 {
310 
311  ForsPAFRecord **hdr;
312  const char *user, *timestamp;
313 #if defined HAVE_GETUID && defined HAVE_GETPWUID
314  struct passwd *pw;
315 #endif
316 
317  /* Get user id */
318 
319 #if defined HAVE_GETUID && defined HAVE_GETPWUID
320  pw = getpwuid(getuid());
321 
322  if (!pw)
323  return NULL;
324 
325  user = pw->pw_name;
326 #else
327  user = getenv("USER");
328  user = user == NULL ? getenv("LOGNAME") : user;
329 
330  if (!user)
331  {
332  return NULL;
333  }
334 #endif
335 
336  /* Get timestamp */
337 
338  timestamp = getTimeISO8601();
339 
340  pos[0] = 0;
341 
342  _forsPAFAppend(&hdr, pos, PAF_HDR_START, PAF_TYPE_NONE, NULL, NULL);
343  _forsPAFAppend(&hdr, pos, PAF_TYPE, PAF_TYPE_STRING, type,
344  "Type of parameter file");
345 
346  if (id) {
347  _forsPAFAppend(&hdr, pos, PAF_ID, PAF_TYPE_STRING, id, NULL);
348  }
349  else {
350  _forsPAFAppend(&hdr, pos, PAF_ID, PAF_TYPE_STRING, "", NULL);
351  }
352 
353  _forsPAFAppend(&hdr, pos, PAF_NAME, PAF_TYPE_STRING, name, "Name of PAF");
354 
355  if (desc)
356  _forsPAFAppend(&hdr, pos, PAF_DESC, PAF_TYPE_STRING, desc,
357  "Short description of PAF");
358  else
359  _forsPAFAppend(&hdr, pos, PAF_DESC, PAF_TYPE_STRING, "",
360  "Short description of PAF");
361 
362  _forsPAFAppend(&hdr, pos, PAF_CRTE_NAME, PAF_TYPE_STRING, user,
363  "Name of creator");
364  _forsPAFAppend(&hdr, pos, PAF_CRTE_TIME, PAF_TYPE_STRING, timestamp,
365  "Civil time for creation");
366  _forsPAFAppend(&hdr, pos, PAF_LCHG_NAME, PAF_TYPE_STRING, user,
367  "Author of par. file");
368  _forsPAFAppend(&hdr, pos, PAF_LCHG_TIME, PAF_TYPE_STRING, timestamp,
369  "Timestamp for last change");
370  _forsPAFAppend(&hdr, pos, PAF_CHCK_NAME, PAF_TYPE_STRING, "",
371  "Name of appl. checking");
372  _forsPAFAppend(&hdr, pos, PAF_CHCK_TIME, PAF_TYPE_STRING, "",
373  "Time for checking");
374  _forsPAFAppend(&hdr, pos, PAF_CHCK_CHECKSUM, PAF_TYPE_STRING, "",
375  "Checksum for the PAF");
376  _forsPAFAppend(&hdr, pos, PAF_HDR_END, PAF_TYPE_NONE, NULL, NULL);
377 
378  return hdr;
379 
380 }
381 
382 
383 /*
384  * Format a record so that it can be written to a parameter file on disk.
385  * The formatted record is written to the given output buffer.
386  */
387 
388 inline static const char *
389 _forsPAFFormatRecord(ForsPAFRecord *record)
390 {
391 
392  static char buffer[PAF_RECORD_MAX + 1];
393  char value[PAF_RECORD_MAX + 1];
394 
395  int pos, sz;
396 
397 
398  memset(buffer, ' ', PAF_RECORD_MAX);
399 
400 
401  /*
402  * Verify that the record name fits into the buffer. The extra
403  * character is for the semicolon which has to be present.
404  */
405 
406  if (strlen(record->name) + 1 > PAF_RECORD_MAX)
407  return NULL;
408 
409 
410  /*
411  * Build the formatted string from the record structure
412  */
413 
414  sz = strlen(record->name);
415  strncpy(buffer, record->name, sz);
416 
417  pos = sz;
418  if (record->data.sval) {
419  if (pos < PAF_FIELD_OFFSET_VALUE)
420  pos = PAF_FIELD_OFFSET_VALUE;
421  else
422  pos++;
423 
424  switch (record->type) {
425  case PAF_TYPE_BOOL:
426  snprintf(value, PAF_RECORD_MAX, "%c",
427  *record->data.bval ? 'T' : 'F');
428  break;
429 
430  case PAF_TYPE_INT:
431  snprintf(value, PAF_RECORD_MAX, "%d", *record->data.ival);
432  break;
433 
434  case PAF_TYPE_DOUBLE:
435  snprintf(value, PAF_RECORD_MAX, "%.15G", *record->data.dval);
436  if (!strchr(value, '.')) {
437  if (strchr(value, 'E'))
438  snprintf(value, PAF_RECORD_MAX, "%.1E",
439  *record->data.dval);
440  else
441  strcat(value, ".");
442  }
443  break;
444 
445  case PAF_TYPE_STRING:
446  snprintf(value, PAF_RECORD_MAX, "\"%s\"", record->data.sval);
447  break;
448 
449  case PAF_TYPE_NONE:
450 
451  /*
452  * Should not reach this point. If type is PAF_TYPE_NONE
453  * the data pointer should always be NULL.
454  */
455 
456  break;
457  }
458 
459  sz = strlen(value);
460 
461  /*
462  * Verify that writing the value string does not overflow the buffer.
463  */
464 
465  if (sz > PAF_RECORD_MAX - pos + 1)
466  return NULL;
467 
468  strncpy(&buffer[pos], value, sz);
469  pos += sz;
470  }
471 
472  buffer[pos++] = ';';
473 
474 
475  /*
476  * Comments are not printed if there is room in the buffer for at least 3
477  * characters, so that not only the hash and/or the following blank
478  * could be stored because of the finite record size.
479  */
480 
481  if (record->comment && (PAF_RECORD_MAX - pos) >= 2) {
482  if (pos < PAF_FIELD_OFFSET_COMMENT)
483  pos = PAF_FIELD_OFFSET_COMMENT;
484  else
485  pos++;
486 
487  strncpy(&buffer[pos], "# ", 2);
488  pos += 2;
489  sz = strlen(record->comment);
490  strncpy(&buffer[pos], record->comment, sz);
491  pos += sz;
492  }
493 
494  buffer[pos] = '\0';
495 
496  return buffer;
497 }
498 
499 
511 inline void deleteForsPAF(ForsPAF *paf)
512 {
513 
514  int i;
515 
516  if (paf) {
517  for (i = 0; i < paf->nh; i++)
518  _forsPAFRecordDestroy(paf->header[i]);
519  for (i = 0; i < paf->nr; i++)
520  _forsPAFRecordDestroy(paf->records[i]);
521  cpl_free(paf->header);
522  cpl_free(paf->records);
523  cpl_free(paf->name);
524  cpl_free(paf);
525  }
526 
527  return;
528 
529 }
530 
531 
550 ForsPAF *newForsPAF(const char *name, const char *type, const char *id,
551  const char *desc)
552 {
553 
554  ForsPAF *paf;
555  int pos = 0;
556 
557 
558  if (!name || !type)
559  return NULL;
560 
561  paf = (ForsPAF *)cpl_malloc(sizeof(ForsPAF));
562  if (paf) {
563  paf->header = _forsPAFHeaderCreate(name, type, id, desc, &pos);
564  if(paf->header == NULL)
565  {
566  cpl_free(paf);
567  return NULL;
568  }
569  paf->records = NULL;
570  paf->nh = pos;
571  paf->nr = 0;
572  paf->name = cpl_strdup(name);
573  }
574 
575  return paf;
576 
577 }
578 
579 
593 int
595 {
596 
597  assert(paf != NULL);
598 
599  return paf->nr == 0 ? 1 : 0;
600 
601 }
602 
603 
618 size_t forsPAFGetSize(const ForsPAF *paf)
619 {
620  assert(paf != NULL);
621 
622  return (size_t)paf->nr;
623 
624 }
625 
626 
641 inline int
642 forsPAFIsValidName(const char *name)
643 {
644 
645  register size_t i, sz;
646 
647 
648  assert(name != NULL);
649 
650  if (strchr(name, ' '))
651  return 0;
652 
653  sz = strlen(name);
654  for (i = 0; i <sz; i++) {
655  char c = name[i];
656 
657  /*
658  * Names may be composed from uppercase letters, digits, the dot
659  * and the underscore only.
660  */
661 
662  /*
663  * Note: The characer class functions have to be enclosed in
664  * parantheses to use the actual function on HP-UX where these
665  * functions are also provided as macros, which are taken by
666  * default and may lead to compiler warnings.
667  */
668 
669  if (!(isupper)(c) && !(isdigit)(c) && c != '.' && c != '_' && c != '-')
670  return 0;
671  }
672 
673  return 1;
674 
675 }
676 
677 
694 inline int
695 forsPAFAppendBool(ForsPAF *paf, const char *name, int value, const char *comment)
696 {
697  assert(paf != NULL);
698  assert(name != NULL);
699 
700  if (!forsPAFIsValidName(name) && name[0] != '#' && name [0] != '\0')
701  return EXIT_FAILURE;
702 
703  if (_forsPAFAppend(&(paf->records), &(paf->nr), name, PAF_TYPE_BOOL,
704  &value, comment))
705  return EXIT_FAILURE;
706 
707  return EXIT_SUCCESS;
708 
709 }
710 
711 
728 inline int
729 forsPAFAppendInt(ForsPAF *paf, const char *name, int value, const char *comment)
730 {
731 
732  assert(paf != NULL);
733  assert(name != NULL);
734 
735  if (!forsPAFIsValidName(name) && name[0] != '#' && name [0] != '\0')
736  return EXIT_FAILURE;
737 
738  if (_forsPAFAppend(&(paf->records), &(paf->nr), name, PAF_TYPE_INT,
739  &value, comment))
740  return EXIT_FAILURE;
741 
742  return EXIT_SUCCESS;
743 
744 }
745 
746 
763 inline int
764 forsPAFAppendDouble(ForsPAF *paf, const char *name, double value,
765  const char *comment)
766 {
767 
768  assert(paf != NULL);
769  assert(name != NULL);
770 
771  if (!forsPAFIsValidName(name) && name[0] != '#' && name [0] != '\0')
772  return EXIT_FAILURE;
773 
774  if (_forsPAFAppend(&(paf->records), &(paf->nr), name, PAF_TYPE_DOUBLE,
775  &value, comment))
776  return EXIT_FAILURE;
777 
778  return EXIT_SUCCESS;
779 
780 }
781 
782 
799 inline int
800 forsPAFAppendString(ForsPAF *paf, const char *name, const char *value,
801  const char *comment)
802 {
803 
804  assert(paf != NULL);
805  assert(name != NULL);
806 
807  if (!forsPAFIsValidName(name) && name[0] != '#' && name [0] != '\0')
808  return EXIT_FAILURE;
809 
810  if (_forsPAFAppend(&(paf->records), &(paf->nr), name, PAF_TYPE_STRING,
811  value, comment))
812  return EXIT_FAILURE;
813 
814  return EXIT_SUCCESS;
815 
816 }
817 
818 
833 int
835 {
836 
837  const char *record;
838  FILE *stream;
839  int i;
840 
841 
842  if (!paf)
843  return EXIT_FAILURE;
844 
845  assert(paf->header != NULL);
846 
847 
848  /*
849  * Create output file
850  */
851 
852  stream = fopen(paf->name, "wb");
853  if (!stream)
854  return EXIT_FAILURE;
855 
856 
857  for (i = 0; i < paf->nh; i++) {
858  record = _forsPAFFormatRecord(paf->header[i]);
859  if (!record) {
860  fclose(stream);
861  return EXIT_FAILURE;
862  }
863 
864  fprintf(stream, "%s\n", record);
865  }
866 
867 
868  if (paf->nr) {
869  char buffer[PAF_RECORD_MAX];
870 
871  buffer[0] = '#';
872  memset(&buffer[1], '-', 78);
873  buffer[79] = '\0';
874  fprintf(stream, "%s\n", buffer);
875  }
876 
877  for (i = 0; i < paf->nr; i++) {
878  record = _forsPAFFormatRecord(paf->records[i]);
879  if (!record) {
880  fclose(stream);
881  return EXIT_FAILURE;
882  }
883 
884  fprintf(stream, "%s\n", record);
885  }
886 
887  fclose(stream);
888 
889  return EXIT_SUCCESS;
890 
891 }
int forsPAFAppendInt(ForsPAF *paf, const char *name, int value, const char *comment)
Append a integer value to a PAF object.
Definition: fors_paf.c:729
int forsPAFIsEmpty(const ForsPAF *paf)
Check whether a PAF object is empty.
Definition: fors_paf.c:594
int forsPAFIsValidName(const char *name)
Verify that the given string is a valid PAF keyword.
Definition: fors_paf.c:642
void deleteForsPAF(ForsPAF *paf)
Destroy a PAF object.
Definition: fors_paf.c:511
int forsPAFAppendDouble(ForsPAF *paf, const char *name, double value, const char *comment)
Append a double value to a PAF object.
Definition: fors_paf.c:764
Definition: list.c:74
int forsPAFWrite(ForsPAF *paf)
Write a PAF object to a disk file.
Definition: fors_paf.c:834
int forsPAFAppendBool(ForsPAF *paf, const char *name, int value, const char *comment)
Append a boolean value to a PAF object.
Definition: fors_paf.c:695
int forsPAFAppendString(ForsPAF *paf, const char *name, const char *value, const char *comment)
Append a string value to a PAF object.
Definition: fors_paf.c:800
size_t forsPAFGetSize(const ForsPAF *paf)
Get the actual size of the given PAF object.
Definition: fors_paf.c:618
ForsPAF * newForsPAF(const char *name, const char *type, const char *id, const char *desc)
Create a new PAF object.
Definition: fors_paf.c:550