FORS Pipeline Reference Manual 4.9.9
|
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_9_9 $ 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 }