fors_paf.c

00001 /* $Id: fors_paf.c,v 1.3 2010/09/14 07:49:30 cizzo 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: cizzo $
00023  * $Date: 2010/09/14 07:49:30 $
00024  * $Revision: 1.3 $
00025  * $Name: fors-4_8_6 $
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         return NULL;
00332 #endif
00333 
00334     /* Get timestamp */
00335 
00336     timestamp = getTimeISO8601();
00337 
00338     pos[0] = 0;
00339 
00340     _forsPAFAppend(&hdr, pos, PAF_HDR_START, PAF_TYPE_NONE, NULL, NULL);
00341     _forsPAFAppend(&hdr, pos, PAF_TYPE, PAF_TYPE_STRING, type,
00342                   "Type of parameter file");
00343 
00344     if (id) {
00345         _forsPAFAppend(&hdr, pos, PAF_ID, PAF_TYPE_STRING, id, NULL);
00346     }
00347     else {
00348         _forsPAFAppend(&hdr, pos, PAF_ID, PAF_TYPE_STRING, "", NULL);
00349     }
00350 
00351     _forsPAFAppend(&hdr, pos, PAF_NAME, PAF_TYPE_STRING, name, "Name of PAF");
00352 
00353     if (desc)
00354         _forsPAFAppend(&hdr, pos, PAF_DESC, PAF_TYPE_STRING, desc,
00355                       "Short description of PAF");
00356     else
00357         _forsPAFAppend(&hdr, pos, PAF_DESC, PAF_TYPE_STRING, "",
00358                       "Short description of PAF");
00359 
00360     _forsPAFAppend(&hdr, pos, PAF_CRTE_NAME, PAF_TYPE_STRING, user,
00361                   "Name of creator");
00362     _forsPAFAppend(&hdr, pos, PAF_CRTE_TIME, PAF_TYPE_STRING, timestamp,
00363                   "Civil time for creation");
00364     _forsPAFAppend(&hdr, pos, PAF_LCHG_NAME, PAF_TYPE_STRING, user,
00365                   "Author of par. file");
00366     _forsPAFAppend(&hdr, pos, PAF_LCHG_TIME, PAF_TYPE_STRING, timestamp,
00367                   "Timestamp for last change");
00368     _forsPAFAppend(&hdr, pos, PAF_CHCK_NAME, PAF_TYPE_STRING, "",
00369                   "Name of appl. checking");
00370     _forsPAFAppend(&hdr, pos, PAF_CHCK_TIME, PAF_TYPE_STRING, "",
00371                   "Time for checking");
00372     _forsPAFAppend(&hdr, pos, PAF_CHCK_CHECKSUM, PAF_TYPE_STRING, "",
00373                   "Checksum for the PAF");
00374     _forsPAFAppend(&hdr, pos, PAF_HDR_END, PAF_TYPE_NONE, NULL, NULL);
00375 
00376     return hdr;
00377 
00378 }
00379 
00380 
00381 /*
00382  * Format a record so that it can be written to a parameter file on disk.
00383  * The formatted record is written to the given output buffer.
00384  */
00385 
00386 inline static const char *
00387 _forsPAFFormatRecord(ForsPAFRecord *record)
00388 {
00389 
00390     static char buffer[PAF_RECORD_MAX + 1];
00391     char value[PAF_RECORD_MAX + 1];
00392 
00393     int pos, sz;
00394 
00395 
00396     memset(buffer, ' ', PAF_RECORD_MAX);
00397 
00398 
00399     /*
00400      * Verify that the record name fits into the buffer. The extra
00401      * character is for the semicolon which has to be present.
00402      */
00403 
00404     if (strlen(record->name) + 1 > PAF_RECORD_MAX)
00405         return NULL;
00406 
00407 
00408     /*
00409      * Build the formatted string from the record structure
00410      */
00411 
00412     sz = strlen(record->name);
00413     strncpy(buffer, record->name, sz);
00414 
00415     pos = sz;
00416     if (record->data.sval) {
00417         if (pos < PAF_FIELD_OFFSET_VALUE)
00418             pos = PAF_FIELD_OFFSET_VALUE;
00419         else
00420             pos++;
00421 
00422         switch (record->type) {
00423             case PAF_TYPE_BOOL:
00424                 snprintf(value, PAF_RECORD_MAX, "%c",
00425                          *record->data.bval ? 'T' : 'F');
00426                 break;
00427 
00428             case PAF_TYPE_INT:
00429                 snprintf(value, PAF_RECORD_MAX, "%d", *record->data.ival);
00430                 break;
00431 
00432             case PAF_TYPE_DOUBLE:
00433                 snprintf(value, PAF_RECORD_MAX, "%.15G", *record->data.dval);
00434                 if (!strchr(value, '.')) {
00435                     if (strchr(value, 'E'))
00436                         snprintf(value, PAF_RECORD_MAX, "%.1E",
00437                                  *record->data.dval);
00438                     else
00439                         strcat(value, ".");
00440                 }
00441                 break;
00442 
00443             case PAF_TYPE_STRING:
00444                 snprintf(value, PAF_RECORD_MAX, "\"%s\"", record->data.sval);
00445                 break;
00446 
00447             case PAF_TYPE_NONE:
00448 
00449                 /* 
00450                  * Should not reach this point. If type is PAF_TYPE_NONE
00451                  * the data pointer should always be NULL.
00452                  */
00453                 
00454                 break;
00455         }
00456 
00457         sz = strlen(value);
00458 
00459         /* 
00460          * Verify that writing the value string does not overflow the buffer.
00461          */
00462         
00463         if (sz > PAF_RECORD_MAX - pos + 1)
00464             return NULL;
00465 
00466         strncpy(&buffer[pos], value, sz);
00467         pos += sz;
00468     }
00469 
00470     buffer[pos++] = ';';
00471 
00472 
00473     /*
00474      * Comments are not printed if there is room in the buffer for at least 3
00475      * characters, so that not only the hash and/or the following blank
00476      * could be stored because of the finite record size.
00477      */
00478 
00479     if (record->comment && (PAF_RECORD_MAX - pos) >= 2) {
00480         if (pos < PAF_FIELD_OFFSET_COMMENT)
00481             pos = PAF_FIELD_OFFSET_COMMENT;
00482         else
00483             pos++;
00484 
00485         strncpy(&buffer[pos], "# ", 2);
00486         pos += 2;
00487         sz = strlen(record->comment);
00488         strncpy(&buffer[pos], record->comment, sz);
00489         pos += sz;
00490     }
00491 
00492     buffer[pos] = '\0';
00493     
00494     return buffer;
00495 }
00496 
00497 
00509 inline void deleteForsPAF(ForsPAF *paf)
00510 {
00511 
00512     int i;
00513 
00514     if (paf) {
00515         for (i = 0; i < paf->nh; i++)
00516             _forsPAFRecordDestroy(paf->header[i]);
00517         for (i = 0; i < paf->nr; i++)
00518             _forsPAFRecordDestroy(paf->records[i]);
00519         cpl_free(paf->header);
00520         cpl_free(paf->records);
00521         cpl_free(paf->name);
00522         cpl_free(paf);
00523     }
00524 
00525     return;
00526 
00527 }
00528 
00529 
00548 ForsPAF *newForsPAF(const char *name, const char *type, const char *id,
00549                   const char *desc)
00550 {
00551 
00552     ForsPAF *paf;
00553     int     pos = 0;
00554 
00555 
00556     if (!name || !type)
00557         return NULL;
00558 
00559     paf = (ForsPAF *)cpl_malloc(sizeof(ForsPAF));
00560     if (paf) {
00561         paf->header = _forsPAFHeaderCreate(name, type, id, desc, &pos);
00562         paf->records = NULL;
00563         paf->nh = pos;
00564         paf->nr = 0;
00565         paf->name = cpl_strdup(name);
00566     }
00567 
00568     return paf;
00569 
00570 }
00571 
00572 
00586 int
00587 forsPAFIsEmpty(const ForsPAF *paf)
00588 {
00589 
00590     assert(paf != NULL);
00591 
00592     return paf->nr == 0 ? 1 : 0;
00593 
00594 }
00595 
00596 
00611 size_t forsPAFGetSize(const ForsPAF *paf)
00612 {
00613     assert(paf != NULL);
00614 
00615     return (size_t)paf->nr;
00616 
00617 }
00618 
00619 
00634 inline int
00635 forsPAFIsValidName(const char *name)
00636 {
00637 
00638     register size_t i, sz;
00639 
00640 
00641     assert(name != NULL);
00642 
00643     if (strchr(name, ' '))
00644         return 0;
00645 
00646     sz = strlen(name);
00647     for (i = 0; i <sz; i++) {
00648         char c = name[i];
00649 
00650         /*
00651          * Names may be composed from uppercase letters, digits, the dot
00652          * and the underscore only.
00653          */
00654 
00655         /*
00656          * Note: The characer class functions have to be enclosed in
00657          *   parantheses to use the actual function on HP-UX where these
00658          *   functions are also provided as macros, which are taken by
00659          *   default and may lead to compiler warnings.
00660          */
00661 
00662         if (!(isupper)(c) && !(isdigit)(c) && c != '.' && c != '_' && c != '-')
00663             return 0;
00664     }
00665 
00666     return 1;
00667 
00668 }
00669 
00670 
00687 inline int
00688 forsPAFAppendBool(ForsPAF *paf, const char *name, int value, const char *comment)
00689 {
00690     assert(paf != NULL);
00691     assert(name != NULL);
00692     
00693     if (!forsPAFIsValidName(name) && name[0] != '#' && name [0] != '\0')
00694         return EXIT_FAILURE;
00695 
00696     if (_forsPAFAppend(&(paf->records), &(paf->nr), name, PAF_TYPE_BOOL, 
00697                       &value, comment))
00698         return EXIT_FAILURE;
00699 
00700     return EXIT_SUCCESS;
00701 
00702 }
00703 
00704 
00721 inline int
00722 forsPAFAppendInt(ForsPAF *paf, const char *name, int value, const char *comment)
00723 {
00724 
00725     assert(paf != NULL);
00726     assert(name != NULL);
00727     
00728     if (!forsPAFIsValidName(name) && name[0] != '#' && name [0] != '\0')
00729         return EXIT_FAILURE;
00730 
00731     if (_forsPAFAppend(&(paf->records), &(paf->nr), name, PAF_TYPE_INT, 
00732                       &value, comment))
00733         return EXIT_FAILURE;
00734 
00735     return EXIT_SUCCESS;
00736 
00737 }
00738 
00739 
00756 inline int
00757 forsPAFAppendDouble(ForsPAF *paf, const char *name, double value,
00758                    const char *comment)
00759 {
00760 
00761     assert(paf != NULL);
00762     assert(name != NULL);
00763     
00764     if (!forsPAFIsValidName(name) && name[0] != '#' && name [0] != '\0')
00765         return EXIT_FAILURE;
00766 
00767     if (_forsPAFAppend(&(paf->records), &(paf->nr), name, PAF_TYPE_DOUBLE, 
00768                       &value, comment))
00769         return EXIT_FAILURE;
00770 
00771     return EXIT_SUCCESS;
00772 
00773 }
00774 
00775 
00792 inline int
00793 forsPAFAppendString(ForsPAF *paf, const char *name, const char *value,
00794                    const char *comment)
00795 {
00796 
00797     assert(paf != NULL);
00798     assert(name != NULL);
00799     
00800     if (!forsPAFIsValidName(name) && name[0] != '#' && name [0] != '\0')
00801         return EXIT_FAILURE;
00802 
00803     if (_forsPAFAppend(&(paf->records), &(paf->nr), name, PAF_TYPE_STRING, 
00804                       value, comment))
00805         return EXIT_FAILURE;
00806 
00807     return EXIT_SUCCESS;
00808 
00809 }
00810 
00811 
00826 int
00827 forsPAFWrite(ForsPAF *paf)
00828 {
00829 
00830     const char *record;
00831     FILE *stream;
00832     int i;
00833 
00834 
00835     if (!paf)
00836         return EXIT_FAILURE;
00837 
00838     assert(paf->header != NULL);
00839 
00840 
00841     /*
00842      * Create output file
00843      */
00844 
00845     stream = fopen(paf->name, "wb");
00846     if (!stream)
00847         return EXIT_FAILURE;
00848 
00849 
00850     for (i = 0; i < paf->nh; i++) {
00851         record = _forsPAFFormatRecord(paf->header[i]);
00852         if (!record) {
00853             fclose(stream);
00854             return EXIT_FAILURE;
00855         }
00856             
00857         fprintf(stream, "%s\n", record);
00858     }
00859 
00860 
00861     if (paf->nr) {
00862         char buffer[PAF_RECORD_MAX];
00863 
00864         buffer[0] = '#';
00865         memset(&buffer[1], '-', 78);
00866         buffer[79] = '\0';
00867         fprintf(stream, "%s\n", buffer);
00868     }
00869 
00870     for (i = 0; i < paf->nr; i++) {
00871         record = _forsPAFFormatRecord(paf->records[i]);
00872         if (!record) {
00873             fclose(stream);
00874             return EXIT_FAILURE;
00875         }
00876 
00877         fprintf(stream, "%s\n", record);
00878     }
00879 
00880     fclose(stream);
00881 
00882     return EXIT_SUCCESS;
00883     
00884 }

Generated on Fri Mar 4 09:46:00 2011 for FORS Pipeline Reference Manual by  doxygen 1.4.7