fors_paf.c

00001 /* $Id: fors_paf.c,v 1.6 2013-08-14 13:28:56 cgarcia Exp $
00002  *
00003  * This file is part of the FORS Library
00004  * Copyright (C) 2002-2010 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00019  */
00020 
00021 /*
00022  * $Author: cgarcia $
00023  * $Date: 2013-08-14 13:28:56 $
00024  * $Revision: 1.6 $
00025  * $Name: not supported by cvs2svn $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 #include <stdlib.h>
00033 #include <stdio.h>
00034 #include <string.h>
00035 #include <ctype.h>
00036 #include <unistd.h>
00037 #include <pwd.h>
00038 #include <sys/types.h>
00039 #include <time.h>
00040 #include <assert.h>
00041 
00042 #include <cpl.h>
00043 #include <fors_paf.h>
00044 
00045 
00046 /*
00047  * Reserved PAF header keywords
00048  */
00049 
00050 #define PAF_HDR_START      "PAF.HDR.START"
00051 #define PAF_TYPE           "PAF.TYPE"
00052 #define PAF_ID             "PAF.ID"
00053 #define PAF_NAME           "PAF.NAME"
00054 #define PAF_DESC           "PAF.DESC"
00055 #define PAF_CRTE_NAME      "PAF.CRTE.NAME"
00056 #define PAF_CRTE_TIME      "PAF.CRTE.DAYTIM"
00057 #define PAF_LCHG_NAME      "PAF.LCHG.NAME"
00058 #define PAF_LCHG_TIME      "PAF.LCHG.DAYTIM"
00059 #define PAF_CHCK_NAME      "PAF.CHCK.NAME"
00060 #define PAF_CHCK_TIME      "PAF.CHCK.DAYTIM"
00061 #define PAF_CHCK_CHECKSUM  "PAF.CHCK.CHECKSUM"
00062 #define PAF_HDR_END        "PAF.HDR.END"
00063 
00064 
00065 /*
00066  * Value and comment field start position
00067  */
00068 
00069 #define PAF_FIELD_OFFSET_VALUE    20
00070 #define PAF_FIELD_OFFSET_COMMENT  45
00071 
00072 
00081 /*
00082  * PAF record definition. This corresponds to one line of a parameter file.
00083  */
00084 
00085 struct _FORS_PAF_RECORD_ {
00086     char *name;
00087     char *comment;
00088     ForsPAFType type;
00089 
00090     union {
00091         int *bval;
00092         int *ival;
00093         double *dval;
00094         char *sval;
00095     } data;
00096 };
00097 
00098 typedef struct _FORS_PAF_RECORD_ ForsPAFRecord;
00099 
00100 
00101 /*
00102  * The PAF object
00103  */
00104 
00105 struct _FORS_PAF_ {
00106     char *name;
00107     int nh;
00108     int nr;
00109     ForsPAFRecord **header;
00110     ForsPAFRecord **records;
00111 };
00112 
00113 
00114 /*
00115  * @brief
00116  *   Get current date and time in ISO8601 format.
00117  *
00118  * @return Pointer to a statically allocated string in the function
00119  *   (no need to free it). In case of failure, returns a @c NULL.
00120  *
00121  * This private function just returns the current time in ISO8601 format.
00122  */
00123 
00124 #define TIME_ISO8601_LENGTH (20)
00125 
00126 static char *getTimeISO8601(void)
00127 {
00128 
00129     static char timeISO8601[TIME_ISO8601_LENGTH];
00130     time_t      seconds = time((time_t *)0);
00131 
00132     if (strftime(timeISO8601, TIME_ISO8601_LENGTH,
00133                     "%Y-%m-%dT%T", localtime(&seconds)) == 0)
00134         strcpy(timeISO8601, "0000-00-00T00:00:00");
00135 
00136     return timeISO8601;
00137 
00138 }
00139 
00140 
00141 /*
00142  * Compute record type size in bytes.
00143  */
00144 
00145 inline static size_t
00146 _forsPAFValueSize(ForsPAFType type, const void *value)
00147 {
00148     size_t sz;
00149 
00150     switch (type) {
00151         case PAF_TYPE_BOOL:
00152             sz = sizeof(int);
00153             break;
00154             
00155         case PAF_TYPE_INT:
00156             sz = sizeof(int);
00157             break;
00158 
00159         case PAF_TYPE_DOUBLE:
00160             sz = sizeof(double);
00161             break;
00162 
00163         case PAF_TYPE_STRING:
00164             sz = (strlen((char *)value) + 1) * sizeof(char);
00165             break;
00166 
00167         default:
00168                 sz = 0;
00169                 break;
00170         }
00171 
00172     return sz;
00173 
00174 }
00175 
00176 
00177 /*
00178  * Destroy a PAF record
00179  */
00180 
00181 inline static void
00182 _forsPAFRecordDestroy(ForsPAFRecord *record)
00183 {
00184 
00185     if (record) {
00186         cpl_free(record->name);
00187         cpl_free((void *)record->data.sval);
00188         cpl_free(record->comment);
00189         cpl_free(record);
00190     }
00191 
00192     return;
00193 
00194 }
00195 
00196 
00197 /*
00198  * Create a new PAF record
00199  */
00200 
00201 inline static ForsPAFRecord *
00202 _forsPAFRecordCreate(const char *name, ForsPAFType type, const void *value,
00203                     const char *comment)
00204 {
00205 
00206     size_t sz;
00207 
00208     ForsPAFRecord *record = cpl_malloc(sizeof(ForsPAFRecord));
00209 
00210 
00211     record->name = cpl_strdup(name);
00212     record->comment = comment ? cpl_strdup(comment) : NULL;
00213     record->type = type;
00214 
00215     sz = _forsPAFValueSize(type, value);
00216 
00217     if (sz == 0) {
00218         record->data.sval = NULL;
00219     }
00220     else {
00221         record->data.sval = (char *)cpl_malloc(sz);
00222     }
00223 
00224     memcpy(record->data.sval, value, sz);
00225 
00226     return record;
00227 
00228 }
00229 
00230 /*
00231  * Set name, value and comment of a PAF record
00232  */
00233 
00234 inline static void
00235 _forsPAFRecordSet(ForsPAFRecord *record, const char *name, ForsPAFType type,
00236                  const void *value, const char *comment)
00237 {
00238 
00239     if (name) {
00240         cpl_free(record->name);
00241         record->name = cpl_strdup(name);
00242     }
00243 
00244     if (comment) {
00245         cpl_free(record->comment);
00246         record->comment = cpl_strdup(comment);
00247     }
00248 
00249     if (value) {
00250         size_t sz = _forsPAFValueSize(type, value);
00251 
00252         if (record->data.sval) {
00253             size_t size = _forsPAFValueSize(record->type, record->data.sval);
00254 
00255             if (sz != size)
00256                 record->data.sval = (char *)cpl_realloc(record->data.sval, sz);
00257         }
00258         else
00259             record->data.sval = (char *)cpl_malloc(sz);
00260 
00261         memcpy(record->data.sval, value, sz);
00262         record->type = type;
00263     }
00264 
00265     return;
00266 
00267 }
00268 
00269 
00270 /*
00271  * Create a record from name, value and comment and append the record to
00272  * the PAF header or record list.
00273  */
00274 
00275 inline static int
00276 _forsPAFAppend(ForsPAFRecord ***list, int *pos, const char *name, 
00277               ForsPAFType type, const void *value, const char *comment)
00278 {
00279 
00280     ForsPAFRecord *record;
00281 
00282 
00283     record = _forsPAFRecordCreate(name, type, value, comment);
00284     if (!record)
00285         return 1;
00286 
00287     if (pos[0] == 0) {
00288         *list = cpl_malloc(sizeof(ForsPAFRecord *));
00289     }
00290     else {
00291         *list = cpl_realloc(*list, (pos[0]+1) * sizeof(ForsPAFRecord *));
00292     }
00293 
00294     (*list)[pos[0]] = record;
00295     pos[0]++;
00296 
00297     return 0;
00298 
00299 }
00300 
00301 
00302 /*
00303  * Create a new PAF header.
00304  */
00305 
00306 inline static ForsPAFRecord **
00307 _forsPAFHeaderCreate(const char *name, const char *type, const char *id,
00308                     const char *desc, int *pos)
00309 {
00310 
00311     ForsPAFRecord **hdr;
00312     const char *user, *timestamp;
00313 #if defined HAVE_GETUID && defined HAVE_GETPWUID
00314     struct passwd *pw;
00315 #endif
00316 
00317     /* Get user id */
00318 
00319 #if defined HAVE_GETUID && defined HAVE_GETPWUID
00320     pw = getpwuid(getuid());
00321 
00322     if (!pw)
00323         return NULL;
00324 
00325     user = pw->pw_name;
00326 #else
00327     user = getenv("USER");
00328     user = user == NULL ? getenv("LOGNAME") : user;
00329 
00330     if (!user)
00331     {
00332         return NULL;
00333     }
00334 #endif
00335 
00336     /* Get timestamp */
00337 
00338     timestamp = getTimeISO8601();
00339 
00340     pos[0] = 0;
00341 
00342     _forsPAFAppend(&hdr, pos, PAF_HDR_START, PAF_TYPE_NONE, NULL, NULL);
00343     _forsPAFAppend(&hdr, pos, PAF_TYPE, PAF_TYPE_STRING, type,
00344                   "Type of parameter file");
00345 
00346     if (id) {
00347         _forsPAFAppend(&hdr, pos, PAF_ID, PAF_TYPE_STRING, id, NULL);
00348     }
00349     else {
00350         _forsPAFAppend(&hdr, pos, PAF_ID, PAF_TYPE_STRING, "", NULL);
00351     }
00352 
00353     _forsPAFAppend(&hdr, pos, PAF_NAME, PAF_TYPE_STRING, name, "Name of PAF");
00354 
00355     if (desc)
00356         _forsPAFAppend(&hdr, pos, PAF_DESC, PAF_TYPE_STRING, desc,
00357                       "Short description of PAF");
00358     else
00359         _forsPAFAppend(&hdr, pos, PAF_DESC, PAF_TYPE_STRING, "",
00360                       "Short description of PAF");
00361 
00362     _forsPAFAppend(&hdr, pos, PAF_CRTE_NAME, PAF_TYPE_STRING, user,
00363                   "Name of creator");
00364     _forsPAFAppend(&hdr, pos, PAF_CRTE_TIME, PAF_TYPE_STRING, timestamp,
00365                   "Civil time for creation");
00366     _forsPAFAppend(&hdr, pos, PAF_LCHG_NAME, PAF_TYPE_STRING, user,
00367                   "Author of par. file");
00368     _forsPAFAppend(&hdr, pos, PAF_LCHG_TIME, PAF_TYPE_STRING, timestamp,
00369                   "Timestamp for last change");
00370     _forsPAFAppend(&hdr, pos, PAF_CHCK_NAME, PAF_TYPE_STRING, "",
00371                   "Name of appl. checking");
00372     _forsPAFAppend(&hdr, pos, PAF_CHCK_TIME, PAF_TYPE_STRING, "",
00373                   "Time for checking");
00374     _forsPAFAppend(&hdr, pos, PAF_CHCK_CHECKSUM, PAF_TYPE_STRING, "",
00375                   "Checksum for the PAF");
00376     _forsPAFAppend(&hdr, pos, PAF_HDR_END, PAF_TYPE_NONE, NULL, NULL);
00377 
00378     return hdr;
00379 
00380 }
00381 
00382 
00383 /*
00384  * Format a record so that it can be written to a parameter file on disk.
00385  * The formatted record is written to the given output buffer.
00386  */
00387 
00388 inline static const char *
00389 _forsPAFFormatRecord(ForsPAFRecord *record)
00390 {
00391 
00392     static char buffer[PAF_RECORD_MAX + 1];
00393     char value[PAF_RECORD_MAX + 1];
00394 
00395     int pos, sz;
00396 
00397 
00398     memset(buffer, ' ', PAF_RECORD_MAX);
00399 
00400 
00401     /*
00402      * Verify that the record name fits into the buffer. The extra
00403      * character is for the semicolon which has to be present.
00404      */
00405 
00406     if (strlen(record->name) + 1 > PAF_RECORD_MAX)
00407         return NULL;
00408 
00409 
00410     /*
00411      * Build the formatted string from the record structure
00412      */
00413 
00414     sz = strlen(record->name);
00415     strncpy(buffer, record->name, sz);
00416 
00417     pos = sz;
00418     if (record->data.sval) {
00419         if (pos < PAF_FIELD_OFFSET_VALUE)
00420             pos = PAF_FIELD_OFFSET_VALUE;
00421         else
00422             pos++;
00423 
00424         switch (record->type) {
00425             case PAF_TYPE_BOOL:
00426                 snprintf(value, PAF_RECORD_MAX, "%c",
00427                          *record->data.bval ? 'T' : 'F');
00428                 break;
00429 
00430             case PAF_TYPE_INT:
00431                 snprintf(value, PAF_RECORD_MAX, "%d", *record->data.ival);
00432                 break;
00433 
00434             case PAF_TYPE_DOUBLE:
00435                 snprintf(value, PAF_RECORD_MAX, "%.15G", *record->data.dval);
00436                 if (!strchr(value, '.')) {
00437                     if (strchr(value, 'E'))
00438                         snprintf(value, PAF_RECORD_MAX, "%.1E",
00439                                  *record->data.dval);
00440                     else
00441                         strcat(value, ".");
00442                 }
00443                 break;
00444 
00445             case PAF_TYPE_STRING:
00446                 snprintf(value, PAF_RECORD_MAX, "\"%s\"", record->data.sval);
00447                 break;
00448 
00449             case PAF_TYPE_NONE:
00450 
00451                 /* 
00452                  * Should not reach this point. If type is PAF_TYPE_NONE
00453                  * the data pointer should always be NULL.
00454                  */
00455                 
00456                 break;
00457         }
00458 
00459         sz = strlen(value);
00460 
00461         /* 
00462          * Verify that writing the value string does not overflow the buffer.
00463          */
00464         
00465         if (sz > PAF_RECORD_MAX - pos + 1)
00466             return NULL;
00467 
00468         strncpy(&buffer[pos], value, sz);
00469         pos += sz;
00470     }
00471 
00472     buffer[pos++] = ';';
00473 
00474 
00475     /*
00476      * Comments are not printed if there is room in the buffer for at least 3
00477      * characters, so that not only the hash and/or the following blank
00478      * could be stored because of the finite record size.
00479      */
00480 
00481     if (record->comment && (PAF_RECORD_MAX - pos) >= 2) {
00482         if (pos < PAF_FIELD_OFFSET_COMMENT)
00483             pos = PAF_FIELD_OFFSET_COMMENT;
00484         else
00485             pos++;
00486 
00487         strncpy(&buffer[pos], "# ", 2);
00488         pos += 2;
00489         sz = strlen(record->comment);
00490         strncpy(&buffer[pos], record->comment, sz);
00491         pos += sz;
00492     }
00493 
00494     buffer[pos] = '\0';
00495     
00496     return buffer;
00497 }
00498 
00499 
00511 inline void deleteForsPAF(ForsPAF *paf)
00512 {
00513 
00514     int i;
00515 
00516     if (paf) {
00517         for (i = 0; i < paf->nh; i++)
00518             _forsPAFRecordDestroy(paf->header[i]);
00519         for (i = 0; i < paf->nr; i++)
00520             _forsPAFRecordDestroy(paf->records[i]);
00521         cpl_free(paf->header);
00522         cpl_free(paf->records);
00523         cpl_free(paf->name);
00524         cpl_free(paf);
00525     }
00526 
00527     return;
00528 
00529 }
00530 
00531 
00550 ForsPAF *newForsPAF(const char *name, const char *type, const char *id,
00551                   const char *desc)
00552 {
00553 
00554     ForsPAF *paf;
00555     int     pos = 0;
00556 
00557 
00558     if (!name || !type)
00559         return NULL;
00560 
00561     paf = (ForsPAF *)cpl_malloc(sizeof(ForsPAF));
00562     if (paf) {
00563         paf->header = _forsPAFHeaderCreate(name, type, id, desc, &pos);
00564         if(paf->header == NULL)
00565         {
00566             cpl_free(paf);
00567             return NULL;
00568         }
00569         paf->records = NULL;
00570         paf->nh = pos;
00571         paf->nr = 0;
00572         paf->name = cpl_strdup(name);
00573     }
00574     
00575     return paf;
00576 
00577 }
00578 
00579 
00593 int
00594 forsPAFIsEmpty(const ForsPAF *paf)
00595 {
00596 
00597     assert(paf != NULL);
00598 
00599     return paf->nr == 0 ? 1 : 0;
00600 
00601 }
00602 
00603 
00618 size_t forsPAFGetSize(const ForsPAF *paf)
00619 {
00620     assert(paf != NULL);
00621 
00622     return (size_t)paf->nr;
00623 
00624 }
00625 
00626 
00641 inline int
00642 forsPAFIsValidName(const char *name)
00643 {
00644 
00645     register size_t i, sz;
00646 
00647 
00648     assert(name != NULL);
00649 
00650     if (strchr(name, ' '))
00651         return 0;
00652 
00653     sz = strlen(name);
00654     for (i = 0; i <sz; i++) {
00655         char c = name[i];
00656 
00657         /*
00658          * Names may be composed from uppercase letters, digits, the dot
00659          * and the underscore only.
00660          */
00661 
00662         /*
00663          * Note: The characer class functions have to be enclosed in
00664          *   parantheses to use the actual function on HP-UX where these
00665          *   functions are also provided as macros, which are taken by
00666          *   default and may lead to compiler warnings.
00667          */
00668 
00669         if (!(isupper)(c) && !(isdigit)(c) && c != '.' && c != '_' && c != '-')
00670             return 0;
00671     }
00672 
00673     return 1;
00674 
00675 }
00676 
00677 
00694 inline int
00695 forsPAFAppendBool(ForsPAF *paf, const char *name, int value, const char *comment)
00696 {
00697     assert(paf != NULL);
00698     assert(name != NULL);
00699     
00700     if (!forsPAFIsValidName(name) && name[0] != '#' && name [0] != '\0')
00701         return EXIT_FAILURE;
00702 
00703     if (_forsPAFAppend(&(paf->records), &(paf->nr), name, PAF_TYPE_BOOL, 
00704                       &value, comment))
00705         return EXIT_FAILURE;
00706 
00707     return EXIT_SUCCESS;
00708 
00709 }
00710 
00711 
00728 inline int
00729 forsPAFAppendInt(ForsPAF *paf, const char *name, int value, const char *comment)
00730 {
00731 
00732     assert(paf != NULL);
00733     assert(name != NULL);
00734     
00735     if (!forsPAFIsValidName(name) && name[0] != '#' && name [0] != '\0')
00736         return EXIT_FAILURE;
00737 
00738     if (_forsPAFAppend(&(paf->records), &(paf->nr), name, PAF_TYPE_INT, 
00739                       &value, comment))
00740         return EXIT_FAILURE;
00741 
00742     return EXIT_SUCCESS;
00743 
00744 }
00745 
00746 
00763 inline int
00764 forsPAFAppendDouble(ForsPAF *paf, const char *name, double value,
00765                    const char *comment)
00766 {
00767 
00768     assert(paf != NULL);
00769     assert(name != NULL);
00770     
00771     if (!forsPAFIsValidName(name) && name[0] != '#' && name [0] != '\0')
00772         return EXIT_FAILURE;
00773 
00774     if (_forsPAFAppend(&(paf->records), &(paf->nr), name, PAF_TYPE_DOUBLE, 
00775                       &value, comment))
00776         return EXIT_FAILURE;
00777 
00778     return EXIT_SUCCESS;
00779 
00780 }
00781 
00782 
00799 inline int
00800 forsPAFAppendString(ForsPAF *paf, const char *name, const char *value,
00801                    const char *comment)
00802 {
00803 
00804     assert(paf != NULL);
00805     assert(name != NULL);
00806     
00807     if (!forsPAFIsValidName(name) && name[0] != '#' && name [0] != '\0')
00808         return EXIT_FAILURE;
00809 
00810     if (_forsPAFAppend(&(paf->records), &(paf->nr), name, PAF_TYPE_STRING, 
00811                       value, comment))
00812         return EXIT_FAILURE;
00813 
00814     return EXIT_SUCCESS;
00815 
00816 }
00817 
00818 
00833 int
00834 forsPAFWrite(ForsPAF *paf)
00835 {
00836 
00837     const char *record;
00838     FILE *stream;
00839     int i;
00840 
00841 
00842     if (!paf)
00843         return EXIT_FAILURE;
00844 
00845     assert(paf->header != NULL);
00846 
00847 
00848     /*
00849      * Create output file
00850      */
00851 
00852     stream = fopen(paf->name, "wb");
00853     if (!stream)
00854         return EXIT_FAILURE;
00855 
00856 
00857     for (i = 0; i < paf->nh; i++) {
00858         record = _forsPAFFormatRecord(paf->header[i]);
00859         if (!record) {
00860             fclose(stream);
00861             return EXIT_FAILURE;
00862         }
00863             
00864         fprintf(stream, "%s\n", record);
00865     }
00866 
00867 
00868     if (paf->nr) {
00869         char buffer[PAF_RECORD_MAX];
00870 
00871         buffer[0] = '#';
00872         memset(&buffer[1], '-', 78);
00873         buffer[79] = '\0';
00874         fprintf(stream, "%s\n", buffer);
00875     }
00876 
00877     for (i = 0; i < paf->nr; i++) {
00878         record = _forsPAFFormatRecord(paf->records[i]);
00879         if (!record) {
00880             fclose(stream);
00881             return EXIT_FAILURE;
00882         }
00883 
00884         fprintf(stream, "%s\n", record);
00885     }
00886 
00887     fclose(stream);
00888 
00889     return EXIT_SUCCESS;
00890     
00891 }

Generated on 12 Feb 2016 for FORS Pipeline Reference Manual by  doxygen 1.6.1