Changeset 7589 for trunk/psModules/src/camera/pmFPAfile.c
- Timestamp:
- Jun 16, 2006, 3:50:43 PM (20 years ago)
- File:
-
- 1 edited
-
trunk/psModules/src/camera/pmFPAfile.c (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/psModules/src/camera/pmFPAfile.c
r7529 r7589 5 5 #include "pmHDU.h" 6 6 #include "pmFPA.h" 7 #include "pmFPAMaskWeight.h"7 // #include "pmFPAMaskWeight.h" 8 8 #include "pmFPAConstruct.h" 9 9 #include "pmFPAview.h" 10 10 #include "pmFPAfile.h" 11 11 #include "pmFPACopy.h" 12 #include "pmFPARead.h"13 #include "pmFPAWrite.h"14 #include "pmPeaks.h"15 #include "pmMoments.h"16 #include "pmModel.h"17 #include "pmSource.h"18 #include "pmSourceIO.h"19 #include "pmGrowthCurve.h"20 #include "pmPSF.h"21 #include "pmPSF_IO.h"22 #include "pmFPA_JPEG.h"12 // #include "pmFPARead.h" 13 // #include "pmFPAWrite.h" 14 // #include "pmPeaks.h" 15 // #include "pmMoments.h" 16 // #include "pmModel.h" 17 // #include "pmSource.h" 18 // #include "pmSourceIO.h" 19 // #include "pmGrowthCurve.h" 20 // #include "pmPSF.h" 21 // #include "pmPSF_IO.h" 22 // #include "pmFPA_JPEG.h" 23 23 24 24 static void pmFPAfileFree(pmFPAfile *file) … … 33 33 psFree (file->names); 34 34 35 psFree (file->camera); 35 36 psFree (file->format); 36 37 psFree (file->name); … … 48 49 psFree (file->extname); 49 50 50 // these are just views ??51 // psFree (file->phu);52 // psFree (file->header);53 54 51 return; 55 52 } … … 68 65 file->names = psMetadataAlloc(); 69 66 67 file->camera = NULL; 70 68 file->format = NULL; 71 69 file->name = NULL; … … 86 84 } 87 85 88 static const char *depthEnumToName(pmFPAdepth depth)89 {90 const char *val = NULL;91 92 switch (depth) {93 case PM_FPA_DEPTH_NONE:94 val = "NONE";95 break;96 case PM_FPA_DEPTH_FPA:97 val = "FPA";98 break;99 case PM_FPA_DEPTH_CHIP:100 val = "CHIP";101 break;102 case PM_FPA_DEPTH_CELL:103 val = "CELL";104 break;105 case PM_FPA_DEPTH_READOUT:106 val = "READOUT";107 break;108 default:109 psAbort(PS_FILE_LINE, "You can't get here; depth = %d", depth);110 }111 112 return val;113 }114 115 static pmFPAdepth depthNameToEnum(const char *name)116 {117 pmFPAdepth val;118 119 if (name == NULL) {120 val = PM_FPA_DEPTH_NONE;121 } else if (!strcasecmp(name, "FPA")) {122 val = PM_FPA_DEPTH_FPA;123 } else if (!strcasecmp(name, "CHIP")) {124 val = PM_FPA_DEPTH_CHIP;125 } else if (!strcasecmp(name, "CELL")) {126 val = PM_FPA_DEPTH_CELL;127 } else if (!strcasecmp(name, "READOUT")) {128 val = PM_FPA_DEPTH_READOUT;129 } else {130 val = PM_FPA_DEPTH_NONE;131 }132 133 return val;134 }135 136 // define a pmFPAfile, bind to the optional fpa if supplied137 pmFPAfile *pmFPAfileDefine(psMetadata *files, psMetadata *camera, pmFPA *fpa, char *name)138 {139 PS_ASSERT_PTR_NON_NULL(files, NULL);140 PS_ASSERT_PTR_NON_NULL(camera, NULL);141 PS_ASSERT_PTR_NON_NULL(name, NULL);142 PS_ASSERT_INT_POSITIVE(strlen(name), NULL);143 144 bool status;145 char *type;146 147 // select the FILERULES from the camera config148 psMetadata *filerules = psMetadataLookupPtr (&status, camera, "FILERULES");149 if (filerules == NULL) {150 psError(PS_ERR_IO, true, "Can't find FILERULES in the CAMERA configuration!");151 return NULL;152 }153 154 // select the name from the FILERULES155 // check for alias name (type == STR, name is aliased name)156 char *realname = psMetadataLookupStr (&status, filerules, name);157 if (!realname || strlen(realname) == 0) {158 realname = name;159 }160 161 psMetadata *data = psMetadataLookupPtr (&status, filerules, realname);162 if (data == NULL) {163 psError(PS_ERR_IO, true, "Can't find file concept %s!", name);164 return NULL;165 }166 167 pmFPAfile *file = pmFPAfileAlloc ();168 169 // save the name of this pmFPAfile170 file->name = psStringCopy (name);171 172 file->filerule = psMemIncrRefCounter(psMetadataLookupStr (&status, data, "FILENAME.RULE"));173 file->filextra = psMemIncrRefCounter(psMetadataLookupStr (&status, data, "FILENAME.XTRA"));174 file->extrule = psMemIncrRefCounter(psMetadataLookupStr (&status, data, "EXTNAME.RULE"));175 file->extxtra = psMemIncrRefCounter(psMetadataLookupStr (&status, data, "EXTNAME.XTRA"));176 177 file->fileDepth = depthNameToEnum(psMetadataLookupStr(&status, data, "FILE.DEPTH"));178 if (file->fileDepth == PM_FPA_DEPTH_NONE) {179 psError(PS_ERR_IO, true, "FILE.DEPTH is not set for %s\n", name);180 return NULL;181 }182 183 file->dataDepth = depthNameToEnum(psMetadataLookupStr (&status, data, "DATA.DEPTH"));184 if (file->dataDepth == PM_FPA_DEPTH_NONE) {185 psError(PS_ERR_IO, true, "DATA.DEPTH is not set for %s\n", name);186 return NULL;187 }188 189 file->type = PM_FPA_FILE_NONE;190 type = psMetadataLookupStr (&status, data, "FILE.TYPE");191 if (type != NULL) {192 if (!strcasecmp (type, "SX")) {193 file->type = PM_FPA_FILE_SX;194 }195 if (!strcasecmp (type, "OBJ")) {196 file->type = PM_FPA_FILE_OBJ;197 }198 if (!strcasecmp (type, "CMP")) {199 file->type = PM_FPA_FILE_CMP;200 }201 if (!strcasecmp (type, "CMF")) {202 file->type = PM_FPA_FILE_CMF;203 }204 if (!strcasecmp (type, "RAW")) {205 file->type = PM_FPA_FILE_RAW;206 }207 if (!strcasecmp (type, "IMAGE")) {208 file->type = PM_FPA_FILE_IMAGE;209 }210 if (!strcasecmp (type, "PSF")) {211 file->type = PM_FPA_FILE_PSF;212 }213 if (!strcasecmp (type, "JPEG")) {214 file->type = PM_FPA_FILE_JPEG;215 }216 }217 if (file->type == PM_FPA_FILE_NONE) {218 psError(PS_ERR_IO, true, "FILE.TYPE is not defined for %s\n", name);219 return NULL;220 }221 222 file->mode = PM_FPA_MODE_NONE;223 type = psMetadataLookupStr (&status, data, "FILE.MODE");224 if (type != NULL) {225 if (!strcasecmp (type, "READ")) {226 file->mode = PM_FPA_MODE_READ;227 }228 if (!strcasecmp (type, "WRITE")) {229 file->mode = PM_FPA_MODE_WRITE;230 }231 }232 if (file->mode == PM_FPA_MODE_NONE) {233 psError(PS_ERR_IO, true, "FILE.MODE is not defined for %s\n", name);234 return NULL;235 }236 237 if (fpa != NULL) {238 file->fpa = psMemIncrRefCounter(fpa);239 }240 241 // for WRITE type of data, the output format needs to be determined242 // this is only needed if the output file is not identical in structure to the input243 char *formatName = psMetadataLookupStr (&status, data, "FILE.FORMAT");244 if (formatName && strcasecmp (formatName, "NONE")) {245 psMetadata *formats = psMetadataLookupMD(&status, camera, "FORMATS"); // List of formats246 char *formatFile = psMetadataLookupStr (&status, formats, formatName);247 if (!formatFile) {248 psError(PS_ERR_IO, false, "format %s for %s not defined", formatName, name);249 psFree (file);250 return NULL;251 }252 readConfig (&file->format, formatFile, formatName);253 }254 255 psMetadataAddPtr (files, PS_LIST_TAIL, name, PS_DATA_UNKNOWN, "", file);256 257 // we free this copy of file, but 'files' still has a copy258 psFree (file);259 260 // the returned value is a view into the version on 'files'261 return (file);262 }263 264 /*265 pmFPAfile *pmFPAfileConstruct (psMetadata *files, psMetadata *format, psMetadata *camera, char *name)266 {267 pmFPA *fpa = pmFPAConstruct (camera);268 pmFPAfile *file = pmFPAfileDefine (files, format, fpa, name);269 psFree (fpa);270 if (!file) {271 psErrorStackPrint(stderr, "file %s not defined\n", name);272 return NULL;273 }274 return file;275 }276 */277 278 // open file (if not already opened)279 bool pmFPAfileOpen (pmFPAfile *file, const pmFPAview *view)280 {281 PS_ASSERT_PTR_NON_NULL(file, false);282 PS_ASSERT_PTR_NON_NULL(view, false);283 284 bool status;285 char *extra;286 char *mode = NULL;287 char *readMode = "r";288 char *writeMode = "w";289 290 if (file->state & PM_FPA_STATE_INACTIVE) {291 psTrace("pmFPAfile", 6, "skip open for %s, files is inactive", file->name);292 return true;293 }294 295 if (file->state == PM_FPA_STATE_OPEN) {296 return true;297 }298 299 if (file->mode == PM_FPA_MODE_NONE) {300 psError(PS_ERR_IO, true, "File is mode PM_FPA_MODE_NONE");301 return false;302 }303 if (file->mode == PM_FPA_MODE_INTERNAL) {304 psError(PS_ERR_IO, true, "File is mode PM_FPA_MODE_INTERNAL");305 return false;306 }307 if (file->mode == PM_FPA_MODE_READ) {308 mode = readMode;309 }310 if (file->mode == PM_FPA_MODE_WRITE) {311 mode = writeMode;312 }313 314 // determine the file name315 // free a name allocated earlier316 psFree (file->filename);317 file->filename = pmFPAfileNameFromRule (file->filerule, file, view);318 if (file->filename == NULL) {319 psError(PS_ERR_IO, true, "Filename is NULL");320 return false;321 }322 323 // indirect filenames324 if (!strcasecmp (file->filename, "@FILES")) {325 psFree (file->filename);326 extra = pmFPAfileNameFromRule (file->filextra, file, view);327 file->filename = psMetadataLookupStr (&status, file->names, extra);328 psFree (extra);329 if (file->filename == NULL) {330 psError(PS_ERR_IO, true, "filename lookup error (@FILES) for %s : %s\n", file->filextra, extra);331 return false;332 }333 // psMetadataLookupStr just returns a view, file->filename must be protected334 psMemIncrRefCounter (file->filename);335 }336 if (!strcasecmp (file->filename, "@DETDB")) {337 psFree (file->filename);338 extra = pmFPAfileNameFromRule (file->filextra, file, view);339 // file->filename = pmDetrendSelect (extra);340 psFree (extra);341 if (file->filename == NULL) {342 psError(PS_ERR_IO, true, "filename lookup error (@DETBD) for %s : %s\n", file->filextra, extra);343 return false;344 }345 psMemIncrRefCounter (file->filename);346 }347 348 switch (file->type) {349 // open the FITS types:350 case PM_FPA_FILE_IMAGE:351 case PM_FPA_FILE_CMF:352 psTrace ("pmFPAfile", 5, "opening %s (type: %d)\n", file->filename, file->type);353 file->fits = psFitsOpen (file->filename, mode);354 if (file->fits == NULL) {355 psError(PS_ERR_IO, false, "error opening file %s\n", file->filename);356 return false;357 }358 file->state = PM_FPA_STATE_OPEN;359 break;360 361 // defer opening TEXT types:362 case PM_FPA_FILE_SX:363 case PM_FPA_FILE_OBJ:364 case PM_FPA_FILE_CMP:365 case PM_FPA_FILE_RAW:366 case PM_FPA_FILE_PSF:367 case PM_FPA_FILE_JPEG:368 psTrace ("pmFPAfile", 5, "defer opening %s\n", file->filename);369 break;370 371 default:372 psError(PS_ERR_IO, true, "type mismatch for %s : %d\n", file->filename, file->type);373 return false;374 }375 return true;376 }377 378 bool pmFPAfileRead(pmFPAfile *file, const pmFPAview *view)379 {380 PS_ASSERT_PTR_NON_NULL(file, false);381 PS_ASSERT_PTR_NON_NULL(view, false);382 383 if (file->state & PM_FPA_STATE_INACTIVE) {384 psTrace("pmFPAfile", 6, "skip read for %s, files is inactive", file->name);385 return true;386 }387 388 if (file->mode != PM_FPA_MODE_READ) {389 psTrace("pmFPAfile", 6, "skip read for %s, mode is not READ", file->name);390 return true;391 }392 393 // get the current depth394 pmFPAdepth depth = pmFPAviewDepth (view);395 396 // do we need to open this file?397 if (depth == file->fileDepth) {398 if (!pmFPAfileOpen (file, view)) {399 psError(PS_ERR_IO, false, "failed to open file %s", file->name);400 return false;401 }402 }403 404 // do we need to read this file?405 if (depth != file->dataDepth) {406 psTrace("pmFPAfile", 6, "skip reading of %s at this depth %s: dataDepth is %s",407 file->name, depthEnumToName(depth), depthEnumToName(file->dataDepth));408 return true;409 }410 411 switch (file->type) {412 case PM_FPA_FILE_IMAGE:413 if (pmFPAviewReadFitsImage (view, file)) {414 psTrace ("pmFPAfile", 5, "reading %s (type: %d)\n", file->filename, file->type);415 } else {416 psError(PS_ERR_UNKNOWN, false, "skipping %s (type: %d)\n", file->filename, file->type);417 return false;418 }419 break;420 421 case PM_FPA_FILE_SX:422 case PM_FPA_FILE_RAW:423 case PM_FPA_FILE_OBJ:424 case PM_FPA_FILE_CMP:425 case PM_FPA_FILE_CMF:426 pmFPAviewReadObjects (view, file);427 psTrace ("pmFPAfile", 5, "reading %s (type: %d)\n", file->filename, file->type);428 break;429 430 case PM_FPA_FILE_PSF:431 pmFPAviewReadPSFmodel (view, file);432 psTrace ("pmFPAfile", 5, "reading %s (type: %d)\n", file->filename, file->type);433 break;434 435 case PM_FPA_FILE_JPEG:436 break;437 438 default:439 psError(PS_ERR_IO, true, "warning: type mismatch; saw type %d", file->type);440 return false;441 }442 return true;443 }444 445 bool pmFPAfileFreeData(pmFPAfile *file, const pmFPAview *view)446 {447 PS_ASSERT_PTR_NON_NULL(file, false);448 PS_ASSERT_PTR_NON_NULL(view, false);449 450 if (file->state & PM_FPA_STATE_INACTIVE) {451 psTrace("pmFPAfile", 6, "skip free for %s, files is inactive", file->name);452 return true;453 }454 455 // get the current depth456 pmFPAdepth depth = pmFPAviewDepth (view);457 458 // do we need to read this file?459 if (depth != file->dataDepth) {460 psTrace("pmFPAfile", 6, "skip free of %s at this depth %s: dataDepth is %s",461 file->name, depthEnumToName(depth), depthEnumToName(file->dataDepth));462 return true;463 }464 465 switch (file->type) {466 case PM_FPA_FILE_IMAGE:467 if (pmFPAviewFreeFitsImage (view, file)) {468 psTrace ("pmFPAfile", 5, "freed %s (type: %d)\n", file->filename, file->type);469 } else {470 psError(PS_ERR_UNKNOWN, false, "skipping %s (type: %d)\n", file->filename, file->type);471 return false;472 }473 break;474 475 case PM_FPA_FILE_SX:476 case PM_FPA_FILE_RAW:477 case PM_FPA_FILE_OBJ:478 case PM_FPA_FILE_CMP:479 case PM_FPA_FILE_CMF:480 // pmFPAviewFreeObjects (view, file);481 psTrace ("pmFPAfile", 5, "NOT freeing %s (type: %d)\n", file->filename, file->type);482 break;483 484 // XXX not certain what I need to free here485 case PM_FPA_FILE_PSF:486 case PM_FPA_FILE_JPEG:487 break;488 489 default:490 psError(PS_ERR_IO, true, "warning: type mismatch; saw type %d", file->type);491 return false;492 }493 return true;494 }495 496 bool pmFPAfileWrite(pmFPAfile *file, const pmFPAview *view)497 {498 PS_ASSERT_PTR_NON_NULL(file, false);499 PS_ASSERT_PTR_NON_NULL(view, false);500 501 if (file->state & PM_FPA_STATE_INACTIVE) {502 psTrace("pmFPAfile", 6, "skip write for %s, files is inactive", file->name);503 return true;504 }505 506 if (file->mode != PM_FPA_MODE_WRITE) {507 psTrace("pmFPAfile", 6, "skip write for %s, mode is not WRITE", file->name);508 return true;509 }510 511 // get the current depth512 pmFPAdepth depth = pmFPAviewDepth (view);513 514 // do we need to write this file?515 if (depth != file->dataDepth) {516 psTrace("pmFPAfile", 6, "skip writing of %s at this depth %s: dataDepth is %s",517 file->name, depthEnumToName(depth), depthEnumToName(file->dataDepth));518 return true;519 }520 521 // do we need to open this file?522 if (depth >= file->fileDepth) {523 if (!pmFPAfileOpen (file, view)) {524 psError(PS_ERR_IO, false, "failed to open %s", file->filename);525 return false;526 }527 }528 529 switch (file->type) {530 case PM_FPA_FILE_IMAGE:531 pmFPAviewWriteFitsImage (view, file);532 psTrace ("pmFPAfile", 5, "wrote image %s (fpa: %p)\n", file->filename, file->fpa);533 break;534 535 case PM_FPA_FILE_SX:536 case PM_FPA_FILE_RAW:537 case PM_FPA_FILE_OBJ:538 case PM_FPA_FILE_CMP:539 case PM_FPA_FILE_CMF:540 psTrace ("pmFPAfile", 5, "writing object %s (fpa: %p)\n", file->filename, file->fpa);541 if (!pmFPAviewWriteObjects (view, file)) {542 psError(PS_ERR_IO, false, "Failed to write object %s", file->filename);543 return false;544 }545 546 break;547 548 case PM_FPA_FILE_PSF:549 pmFPAviewWritePSFmodel (view, file);550 psTrace ("pmFPAfile", 5, "wrote PSF %s (fpa: %p)\n", file->filename, file->fpa);551 break;552 553 case PM_FPA_FILE_JPEG:554 pmFPAviewWriteJPEG (view, file);555 psTrace ("pmFPAfile", 5, "wrote PSF %s (fpa: %p)\n", file->filename, file->fpa);556 break;557 558 default:559 fprintf (stderr, "warning: type mismatch\n");560 return false;561 }562 return true;563 }564 565 // create the data elements (headers, images) appropriate for this view566 bool pmFPAfileCreate (pmFPAfile *file, const pmFPAview *view)567 {568 PS_ASSERT_PTR_NON_NULL(file, false);569 PS_ASSERT_PTR_NON_NULL(view, false);570 571 // these are not error conditions; these are state tests572 if (file->state & PM_FPA_STATE_INACTIVE) {573 psTrace("pmFPAfile", 6, "skip create for inactive file %s", file->name);574 return true;575 }576 if (file->mode != PM_FPA_MODE_WRITE) {577 psTrace("pmFPAfile", 6, "skip create for non-write file %s", file->name);578 return true;579 }580 581 // get the current depth582 pmFPAdepth depth = pmFPAviewDepth (view);583 584 // don't create the file if the src FPA is not defined585 if (file->src == NULL) {586 psTrace("pmFPAfile", 6, "skip create for FPA without src FPA for %s", file->name);587 return true;588 }589 590 // do we need to write this file?591 if (depth != file->dataDepth) {592 psTrace("pmFPAfile", 6, "skip creation of %s at this depth %s: dataDepth is %s",593 file->name, depthEnumToName(depth), depthEnumToName(file->dataDepth));594 return true;595 }596 597 switch (file->type) {598 case PM_FPA_FILE_IMAGE:599 /* create a PHU for thie file, if it does not exist */600 pmFPAfileCopyStructureView (file->fpa, file->src, file->format, file->xBin, file->yBin, view);601 psTrace ("pmFPAfile", 5, "created fpa data elements for %s (fpa: %p)\n", file->name, file->fpa);602 break;603 604 case PM_FPA_FILE_SX:605 case PM_FPA_FILE_RAW:606 case PM_FPA_FILE_OBJ:607 case PM_FPA_FILE_CMP:608 case PM_FPA_FILE_CMF:609 case PM_FPA_FILE_PSF:610 case PM_FPA_FILE_JPEG:611 break;612 613 default:614 psError(PS_ERR_IO, true, "Unsupported type for %s: %d", file->name, file->type);615 return false;616 }617 return true;618 }619 620 bool pmFPAfileClose (pmFPAfile *file, const pmFPAview *view)621 {622 PS_ASSERT_PTR_NON_NULL(file, false);623 PS_ASSERT_PTR_NON_NULL(view, false);624 625 if (file->state & PM_FPA_STATE_INACTIVE) {626 psTrace("pmFPAfile", 6, "skip close for %s, files is inactive", file->name);627 return true;628 }629 if (file->state == PM_FPA_STATE_CLOSED) {630 return true;631 }632 633 // is current depth == open depth?634 pmFPAdepth depth = pmFPAviewDepth (view);635 if (file->fileDepth != depth) {636 psTrace("pmFPAfile", 6, "skip closing of %s at this depth %s: dataDepth is %s",637 file->name, depthEnumToName(depth), depthEnumToName(file->dataDepth));638 return true;639 }640 641 // check if we are actually open642 switch (file->type) {643 // check the FITS types644 case PM_FPA_FILE_IMAGE:645 case PM_FPA_FILE_CMF:646 psFitsClose (file->fits);647 psTrace ("pmFPAfile", 5, "closing %s (type: %d)\n", file->filename, file->type);648 file->fits = NULL;649 file->phu = NULL;650 file->header = NULL;651 file->state = PM_FPA_STATE_CLOSED;652 break;653 654 // ignore the TEXT types655 case PM_FPA_FILE_SX:656 case PM_FPA_FILE_RAW:657 case PM_FPA_FILE_OBJ:658 case PM_FPA_FILE_CMP:659 case PM_FPA_FILE_PSF:660 case PM_FPA_FILE_JPEG:661 break;662 663 default:664 psError(PS_ERR_IO, true, "type mismatch: %d", file->type);665 return false;666 }667 return true;668 }669 670 // set the state of the specified pmFPAfile to active (state == true) or inactive671 // if name is NULL, set the state for all pmFPAfiles672 bool pmFPAfileActivate (psMetadata *files, bool state, char *name)673 {674 PS_ASSERT_PTR_NON_NULL(files, false);675 676 if (!name) {677 psMetadataItem *item = NULL;678 psMetadataIterator *iter = psMetadataIteratorAlloc (files, PS_LIST_HEAD, NULL);679 while ((item = psMetadataGetAndIncrement (iter)) != NULL) {680 pmFPAfile *file = item->data.V;681 if (state) {682 file->state &= NOT_U8(PM_FPA_STATE_INACTIVE);683 } else {684 file->state |= PM_FPA_STATE_INACTIVE;685 }686 }687 psFree (iter);688 return true;689 }690 691 bool status = false;692 pmFPAfile *file = psMetadataLookupPtr (&status, files, name);693 if (!status) {694 psTrace("pmFPAfile", 6, "%s is not a defined IO file", name);695 return false;696 }697 if (!file) {698 psError(PS_ERR_IO, true, "file %s is NULL", name);699 return false;700 }701 if (state) {702 file->state &= NOT_U8(PM_FPA_STATE_INACTIVE);703 } else {704 file->state |= PM_FPA_STATE_INACTIVE;705 }706 return true;707 }708 709 // attempt create, read, write, close, or free pmFPAfiles available in files710 // files are automatically opened before they are read711 bool pmFPAfileIOChecks (psMetadata *files, const pmFPAview *view, pmFPAfilePlace place)712 {713 PS_ASSERT_PTR_NON_NULL(files, false);714 PS_ASSERT_PTR_NON_NULL(view, false);715 716 bool status = true;717 718 // attempt to perform all create, read, write719 psMetadataItem *item = NULL;720 psMetadataIterator *iter = psMetadataIteratorAlloc (files, PS_LIST_HEAD, NULL);721 while ((item = psMetadataGetAndIncrement (iter)) != NULL) {722 pmFPAfile *file = item->data.V;723 724 switch (place) {725 case PM_FPA_BEFORE:726 if (!pmFPAfileRead (file, view)) {727 psError(PS_ERR_IO, false, "failed READ in FPA_BEFORE block for %s", file->name);728 status = false;729 }730 if (!pmFPAfileCreate(file, view)) {731 psError(PS_ERR_IO, false, "failed CREATE in FPA_BEFORE block for %s", file->name);732 status = false;733 }734 break;735 case PM_FPA_AFTER:736 if (!pmFPAfileWrite (file, view)) {737 psError(PS_ERR_IO, false, "failed WRITE in FPA_AFTER block for %s", file->name);738 status = false;739 }740 if (!pmFPAfileClose(file, view)) {741 psError(PS_ERR_IO, false, "failed CLOSE in FPA_AFTER block for %s", file->name);742 status = false;743 }744 break;745 default:746 psAbort(PS_FILE_LINE, "You can't get here");747 }748 }749 750 // attempt to free data that is no longer needed751 psMetadataIteratorSet (iter, PS_LIST_HEAD);752 while ((item = psMetadataGetAndIncrement (iter)) != NULL) {753 pmFPAfile *file = item->data.V;754 755 switch (place) {756 case PM_FPA_BEFORE:757 break;758 case PM_FPA_AFTER:759 if (!pmFPAfileFreeData(file, view)) {760 psError(PS_ERR_IO, false, "failed FREE in FPA_AFTER block for %s", file->name);761 status = false;762 }763 break;764 default:765 psAbort(PS_FILE_LINE, "You can't get here");766 }767 }768 psFree (iter);769 return status;770 }771 772 // create a file with the given name, assign it type "INTERNAL", and supply it with an image773 // of the requested dimensions. (image only, mask and weight are ignored)774 pmReadout *pmFPAfileCreateInternal (psMetadata *files, char *name, int Nx, int Ny, int type)775 {776 PS_ASSERT_PTR_NON_NULL(files, false);777 PS_ASSERT_PTR_NON_NULL(name, false);778 PS_ASSERT_INT_POSITIVE(strlen(name), false);779 780 pmReadout *readout = pmReadoutAlloc(NULL);781 readout->image = psImageAlloc(Nx, Ny, type);782 783 // I want an image from the784 pmFPAfile *file = pmFPAfileAlloc();785 file->mode = PM_FPA_MODE_INTERNAL;786 file->name = psStringCopy (name);787 788 file->readout = readout;789 psMetadataAddPtr(files, PS_LIST_TAIL, name, PS_DATA_UNKNOWN, "", file);790 psFree(file);791 // we free this copy of file, but 'files' still has a copy792 793 return readout;794 }795 796 bool pmFPAfileDropInternal(psMetadata *files, char *name)797 {798 PS_ASSERT_PTR_NON_NULL(files, false);799 PS_ASSERT_PTR_NON_NULL(name, false);800 PS_ASSERT_INT_POSITIVE(strlen(name), false);801 802 bool status = false;803 804 pmFPAfile *file = psMetadataLookupPtr (&status, files, name);805 if (!status) {806 psTrace("pmFPAfile", 6, "Internal File %s not in file list", name);807 return true;808 }809 if (file == NULL) {810 psError(PS_ERR_IO, true, "file %s is NULL", name);811 return false;812 }813 if (file->mode != PM_FPA_MODE_INTERNAL) {814 psTrace("pmFPAfile", 6, "FPA File %s not Internal, not dropping", name);815 return true;816 }817 818 psMetadataRemoveKey (files, name);819 return true;820 }821 822 86 // select the readout from the named pmFPAfile; if the named file does not exist, 823 87 pmReadout *pmFPAfileThisReadout (psMetadata *files, const pmFPAview *view, const char *name) … … 844 108 } 845 109 846 // given an already-opened fits file, read the components corresponding 847 // to the specified view 848 bool pmFPAviewReadFitsImage (const pmFPAview *view, pmFPAfile *file) 849 { 850 PS_ASSERT_PTR_NON_NULL(view, false); 851 PS_ASSERT_PTR_NON_NULL(file, false); 852 853 bool status; 854 pmFPA *fpa = file->fpa; 855 psFits *fits = file->fits; 856 857 if (view->chip == -1) { 858 status = pmFPARead (fpa, fits, NULL); 859 return status; 860 } 861 862 if (view->chip >= fpa->chips->n) { 863 psError(PS_ERR_IO, true, "Requested chip == %d >= fpa->chips->n == %d", view->chip, fpa->chips->n); 864 return false; 865 } 866 pmChip *chip = fpa->chips->data[view->chip]; 867 868 if (view->cell == -1) { 869 status = pmChipRead (chip, fits, NULL); 870 return status; 871 } 872 873 if (view->cell >= chip->cells->n) { 874 psError(PS_ERR_IO, true, "Requested cell == %d >= chip->cells->n == %d", view->cell, chip->cells->n); 875 return false; 876 } 877 pmCell *cell = chip->cells->data[view->cell]; 878 879 if (view->readout == -1) { 880 status = pmCellRead (cell, fits, NULL); 881 return status; 882 } 883 psError(PS_ERR_UNKNOWN, true, "Returning false"); 884 return false; 885 886 // XXX pmReadoutRead, pmReadoutReadSegement disabled for now 887 #if 0 888 889 if (view->readout >= cell->readouts->n) { 890 psError(PS_ERR_IO, true, "Requested readout == %d >= cell->readouts->n == %d", 891 view->readout, cell->readouts->n); 892 return false; 893 } 894 pmReadout *readout = cell->readouts->data[view->readout]; 895 896 if (view->nRows == 0) { 897 pmReadoutRead (readout, fits, NULL); 898 } else { 899 pmReadoutReadSegment (readout, fits, view->nRows, view->iRows, NULL, NULL); 900 } 901 return true; 902 #endif 903 } 904 905 // given an already-opened fits file, read the components corresponding 906 // to the specified view 907 bool pmFPAviewFreeFitsImage (const pmFPAview *view, pmFPAfile *file) 908 { 909 PS_ASSERT_PTR_NON_NULL(view, false); 910 PS_ASSERT_PTR_NON_NULL(file, false); 911 912 pmFPA *fpa = file->fpa; 913 914 if (view->chip == -1) { 915 psTrace ("pmFPAfile", 5, "freeing fpa for %s\n", file->filename); 916 psFree (fpa); 917 file->fpa = NULL; 918 return true; 919 } 920 921 if (view->chip >= fpa->chips->n) { 922 psError(PS_ERR_IO, true, "Requested chip == %d >= fpa->chips->n == %d", view->chip, fpa->chips->n); 923 return false; 924 } 925 pmChip *chip = fpa->chips->data[view->chip]; 926 927 if (view->cell == -1) { 928 psTrace ("pmFPAfile", 5, "freeing chip %d for %s\n", view->chip, file->filename); 929 psFree (chip); 930 fpa->chips->data[view->chip] = NULL; 931 return true; 932 } 933 934 if (view->cell >= chip->cells->n) { 935 psError(PS_ERR_IO, true, "Requested cell == %d >= chip->cells->n == %d", view->cell, chip->cells->n); 936 return false; 937 } 938 pmCell *cell = chip->cells->data[view->cell]; 939 940 if (view->readout == -1) { 941 psTrace ("pmFPAfile", 5, "freeing cell %d for %s\n", view->cell, file->filename); 942 psFree (cell); 943 chip->cells->data[view->cell] = NULL; 944 return true; 945 } 946 psError(PS_ERR_UNKNOWN, true, "Returning false"); 947 return false; 948 949 // XXX pmReadoutRead, pmReadoutReadSegement disabled for now 950 #if 0 951 952 if (view->readout >= cell->readouts->n) { 953 psError(PS_ERR_IO, true, "Requested readout == %d >= cell->readouts->n == %d", 954 view->readout, cell->readouts->n); 955 return false; 956 } 957 pmReadout *readout = cell->readouts->data[view->readout]; 958 959 if (view->nRows == 0) { 960 pmReadoutRead (readout, fits, NULL); 961 } else { 962 pmReadoutReadSegment (readout, fits, view->nRows, view->iRows, NULL, NULL); 963 } 964 return true; 965 #endif 966 } 967 968 // given an already-opened fits file, write the components corresponding 969 // to the specified view 970 bool pmFPAviewWriteFitsImage (const pmFPAview *view, pmFPAfile *file) 971 { 972 PS_ASSERT_PTR_NON_NULL(view, false); 973 PS_ASSERT_PTR_NON_NULL(file, false); 974 975 pmFPA *fpa = file->fpa; 976 psFits *fits = file->fits; 977 978 // pmFPAWrite takes care of all PHUs as needed 979 if (view->chip == -1) { 980 pmFPAWrite(fpa, fits, NULL, true, true); 981 return true; 982 } 983 984 if (view->chip >= fpa->chips->n) { 985 psError(PS_ERR_IO, true, "Requested chip == %d >= fpa->chips->n == %d", view->chip, fpa->chips->n); 986 return false; 987 } 988 pmChip *chip = fpa->chips->data[view->chip]; 989 990 // do we need to write out a PHU for this entry? 991 if (file->phu == NULL) { 992 pmHDU *hdu = pmFPAviewThisHDU (view, file->fpa); 993 pmHDU *phu = pmFPAviewThisPHU (view, file->fpa); 994 // if this hdu is the phu, the write function below will create the phu 995 if (hdu != phu) { 996 // we assume that the PHU is just a header 997 psMetadata *outhead = psMetadataCopy (NULL, phu->header); 998 psMetadataAdd (outhead, PS_LIST_TAIL, "EXTEND", PS_DATA_BOOL | PS_META_REPLACE, "this file has extensions", true); 999 psFitsWriteBlank (file->fits, outhead); 1000 file->phu = phu->header; 1001 psTrace ("pmFPAfile", 5, "wrote phu %s (type: %d)\n", file->filename, file->type); 1002 psFree (outhead); 1003 } 1004 } 1005 1006 if (view->cell == -1) { 1007 pmChipWrite (chip, fits, NULL, true, true); 1008 return true; 1009 } 1010 1011 if (view->cell >= chip->cells->n) { 1012 psError(PS_ERR_IO, true, "Requested cell == %d >= chip->cells->n == %d", view->cell, chip->cells->n); 1013 return false; 1014 } 1015 pmCell *cell = chip->cells->data[view->cell]; 1016 1017 if (view->readout == -1) { 1018 return pmCellWrite (cell, fits, NULL, true); 1019 } 1020 psError(PS_ERR_UNKNOWN, true, "Returning false"); 1021 return false; 1022 1023 // XXX disable readout write for now 1024 #if 0 1025 1026 if (view->readout >= cell->readouts->n) { 1027 psError(PS_ERR_IO, true, "Requested readout == %d >= cell->readouts->n == %d", 1028 view->readout, cell->readouts->n); 1029 return false; 1030 } 1031 pmReadout *readout = cell->readouts->data[view->readout]; 1032 1033 if (view->nRows == 0) { 1034 pmReadoutWrite (readout, fits, NULL, NULL); 1035 } else { 1036 pmReadoutWriteSegment (readout, fits, view->nRows, view->iRows, NULL, NULL); 1037 } 1038 return true; 1039 #endif 1040 } 1041 1042 // look for the given name on the argument list. 1043 // returns the file (a view to the one saved on config->files) 1044 pmFPAfile *pmFPAfileFromArgs (bool *found, pmConfig *config, char *filename, char *argname) 1045 { 1046 PS_ASSERT_PTR_NON_NULL(config, false); 1047 PS_ASSERT_PTR_NON_NULL(filename, false); 1048 PS_ASSERT_INT_POSITIVE(strlen(filename), false); 1049 PS_ASSERT_PTR_NON_NULL(argname, false); 1050 PS_ASSERT_INT_POSITIVE(strlen(argname), false); 1051 1052 bool status; 1053 pmFPA *fpa = NULL; 1054 psFits *fits = NULL; 1055 pmFPAfile *file = NULL; 1056 psMetadata *phu = NULL; 1057 psMetadata *format = NULL; 1058 1059 if (*found) { 1060 return NULL; 1061 } 1062 1063 // we search the argument data for the named fileset (argname) 1064 psArray *infiles = psMetadataLookupPtr(&status, config->arguments, argname); 1065 if (!status) { 1066 psTrace("pmFPAfile", 5, "Failed to find %s in argument list", argname); 1067 return NULL; 1068 } 1069 if (infiles->n < 1) { 1070 psError(PS_ERR_IO, false, "Found n == %d files in %s in arguments\n", infiles->n, argname); 1071 return NULL; 1072 } 1073 1074 // determine the current format from the header 1075 // if no camera has been specified, use the first image as a template for the rest. 1076 fits = psFitsOpen (infiles->data[0], "r"); 1077 phu = psFitsReadHeader (NULL, fits); 1078 format = pmConfigCameraFormatFromHeader (config, phu); 1079 psFitsClose (fits); // don't close phu; we'll use it below 1080 if (!format) { 1081 psError(PS_ERR_IO, false, "Failed to read CCD format from %s\n", infiles->data[0]); 1082 psFree(phu); 1083 return NULL; 1084 } 1085 1086 // build the template fpa, set up the basic view 1087 fpa = pmFPAConstruct (config->camera); 1088 if (!fpa) { 1089 psError(PS_ERR_IO, false, "Failed to construct FPA from %s", infiles->data[0]); 1090 return NULL; 1091 } 1092 1093 // load the given filerule (from config->camera) and associate it with the fpa 1094 // the output file is just a view to the file on config->files 1095 file = pmFPAfileDefine (config->files, config->camera, fpa, filename); 1096 if (!file) { 1097 psError(PS_ERR_IO, false, "file %s not defined", filename); 1098 psFree(phu); 1099 psFree (fpa); 1100 psFree (format); 1101 return NULL; 1102 } 1103 1104 // this file is (by virtue of being supplied in the argument list) coming from the fileset 1105 psFree (file->filerule); 1106 psFree (file->filextra); 1107 1108 // adjust the rules to identify these files in the file->names data 1109 file->filerule = psStringCopy ("@FILES"); 1110 // XXX this rule does not work in general 1111 file->filextra = psStringCopy ("{CHIP.NAME}.{CELL.NAME}"); 1112 1113 // examine the list of input files and validate their cameras 1114 for (int i = 0; i < infiles->n; i++) { 1115 if (i > 0) { 1116 fits = psFitsOpen (infiles->data[i], "r"); 1117 phu = psFitsReadHeader (NULL, fits); 1118 pmConfigValidateCameraFormat (format, phu); 1119 psFitsClose (fits); 1120 } 1121 1122 // set the view to the corresponding entry for this phu 1123 pmFPAview *view = pmFPAAddSourceFromHeader (fpa, phu, format); 1124 if (!view) { 1125 psError(PS_ERR_IO, false, "Failed to set view from file %s", infiles->data[i]); 1126 psFree(phu); 1127 psFree (fpa); 1128 psFree (format); 1129 return NULL; 1130 } 1131 1132 // XXX is this the correct psMD to save the filename? 1133 char *name = pmFPAfileNameFromRule (file->filextra, file, view); 1134 psMetadataAddStr (file->names, PS_LIST_TAIL, name, 0, "", infiles->data[i]); 1135 1136 psFree (view); 1137 psFree (name); 1138 psFree (phu); 1139 } 1140 psFree (fpa); 1141 psFree (format); 1142 *found = true; 1143 1144 return file; 1145 } 1146 1147 // XXX this this function through, then finish 1148 #if 0 1149 // look for the given name on the argument list. 1150 // returns the file (a view to the one saved on config->files) 1151 // in this case, each file should correspond to the same view 1152 // (except at the readout level), of multiple FPAs 1153 // XXX think this through a bit more: 1154 // - do we create multiple input fpas, or assign the same files to the single fpa? 1155 // - do we save the multiple pmFPAfiles in the config->files psMD? 1156 // - do we save the multiple filenames in the file->names psMD? 1157 // - if we have a single FPA and multiple names, we need a method to 1158 // turn on a specific name for the I/O actions. probably true if we 1159 // have multiple FPAs as well. 1160 pmFPAfile *pmFPAfileSetFromArgs (bool *found, pmConfig *config, char *filename, char *argname) 1161 { 1162 PS_ASSERT_PTR_NON_NULL(config, false); 1163 PS_ASSERT_PTR_NON_NULL(filename, false); 1164 PS_ASSERT_INT_POSITIVE(strlen(filename), false); 1165 PS_ASSERT_PTR_NON_NULL(argname, false); 1166 PS_ASSERT_INT_POSITIVE(strlen(argname), false); 1167 1168 bool status; 1169 pmFPA *fpa = NULL; 1170 psFits *fits = NULL; 1171 pmFPAfile *file = NULL; 1172 psMetadata *phu = NULL; 1173 psMetadata *format = NULL; 1174 1175 if (*found) 1176 return NULL; 1177 1178 // we search the argument data for the named fileset (argname) 1179 psArray *infiles = psMetadataLookupPtr(&status, config->arguments, argname); 1180 if (!status) { 1181 return NULL; 1182 } 1183 if (infiles->n < 1) { 1184 return NULL; 1185 } 1186 1187 // determine the current format from the header 1188 // if no camera has been specified, use the first image as a template for the rest. 1189 fits = psFitsOpen (infiles->data[0], "r"); 1190 phu = psFitsReadHeader (NULL, fits); 1191 format = pmConfigCameraFormatFromHeader (config, phu); 1192 psFitsClose (fits); 1193 1194 // build the template fpa, set up the basic view 1195 fpa = pmFPAConstruct (config->camera); 1196 1197 // load the given filerule (from config->camera) and associate it with the fpa 1198 // the output file is just a view to the file on config->files 1199 // XXX the filenames placed on config->files should include the seq number 1200 file = pmFPAfileDefine (config->files, config->camera, fpa, filename); 1201 if (!file) { 1202 psError(PS_ERR_IO, false, "file %s not defined", filename); 1203 psFree (fpa); 1204 psFree (format); 1205 return NULL; 1206 } 1207 1208 // this file is (by virtue of being supplied in the argument list) coming from the fileset 1209 psFree (file->filerule); 1210 psFree (file->filextra); 1211 1212 // adjust the rules to identify these files in the file->names data 1213 file->filerule = psStringCopy ("@FILES"); 1214 // XXX this rule does not work in general 1215 file->filextra = psStringCopy ("{CHIP.NAME}.{CELL.NAME}"); 1216 1217 // examine the list of input files and validate their cameras 1218 for (int i = 0; i < infiles->n; i++) { 1219 if (i > 0) { 1220 fits = psFitsOpen (infiles->data[i], "r"); 1221 phu = psFitsReadHeader (NULL, fits); 1222 pmConfigValidateCameraFormat (format, phu); 1223 psFitsClose (fits); 1224 } 1225 1226 // set the view to the corresponding entry for this phu 1227 pmFPAview *view = pmFPAAddSourceFromHeader (fpa, phu, format); 1228 1229 // XXX is this the correct psMD to save the filename? 1230 char *name = pmFPAfileNameFromRule (file->filextra, file, view); 1231 psMetadataAddStr (file->names, PS_LIST_TAIL, name, 0, "", infiles->data[i]); 1232 1233 psFree (view); 1234 psFree (name); 1235 psFree (phu); 1236 } 1237 psFree (fpa); 1238 psFree (format); 1239 *found = true; 1240 1241 return file; 1242 } 1243 #endif 1244 1245 // create a new output pmFPAfile based on an existing FPA 1246 pmFPAfile *pmFPAfileFromFPA (pmConfig *config, pmFPA *src, int xBin, int yBin, char *filename) 1247 { 1248 PS_ASSERT_PTR_NON_NULL(config, false); 1249 PS_ASSERT_PTR_NON_NULL(src, false); 1250 PS_ASSERT_PTR_NON_NULL(filename, false); 1251 PS_ASSERT_INT_POSITIVE(strlen(filename), false); 1252 1253 // XXX pmFPAConstruct has many leaks (6919) 1254 pmFPA *fpa = pmFPAConstruct (config->camera); 1255 pmFPAfile *file = pmFPAfileDefine (config->files, config->camera, fpa, filename); 1256 if (!file) { 1257 psErrorStackPrint(stderr, "file %s not defined\n", filename); 1258 return NULL; 1259 } 1260 file->src = src; // inherit output elements from this source pmFPA 1261 file->xBin = xBin; 1262 file->yBin = yBin; 1263 1264 psFree (fpa); 1265 return file; 1266 } 1267 1268 // look for the given name on the argument list. 1269 pmFPAfile *pmFPAfileFromConf (bool *found, pmConfig *config, char *filename, pmFPA *input) 1270 { 1271 PS_ASSERT_PTR_NON_NULL(config, false); 1272 PS_ASSERT_PTR_NON_NULL(filename, false); 1273 PS_ASSERT_INT_POSITIVE(strlen(filename), false); 1274 PS_ASSERT_PTR_NON_NULL(input, false); 1275 1276 psFits *fits = NULL; 1277 pmFPAfile *file = NULL; 1278 psMetadata *phu = NULL; 1279 psMetadata *format = NULL; 1280 psArray *infiles = NULL; 1281 1282 if (*found) { 1283 return NULL; 1284 } 1285 1286 // a camera config is needed (as source of file rule) 1287 if (config->camera == NULL) { 1288 psErrorStackPrint (stderr, "camera is not defined\n"); 1289 return NULL; 1290 } 1291 1292 // expect @DETDB in the config filerule 1293 file = pmFPAfileDefine (config->files, config->camera, NULL, filename); 1294 if (!file) { 1295 psError(PS_ERR_IO, false, "file %s not defined\n", filename); 1296 return NULL; 1297 } 1298 1299 // image names come from the file->name list? 1300 if (!strcasecmp (file->filerule, "@FILES")) { 1301 psAbort ("pmFPAfileFromConfig", "programming error"); 1302 } 1303 1304 // image needs to come from the detrend database 1305 if (!strcasecmp (file->filerule, "@DETDB")) { 1306 // char *extra = pmFPAfileNameFromRule (file->filextra, file, view); 1307 // psArray *infiles = pmDetrendSelect (extra, input); 1308 psAbort ("pmFPAfileFromConfig", "programming error: @DETDB not yet defined"); 1309 } else { 1310 infiles = psArrayAlloc(1); 1311 infiles->n = 1; 1312 infiles->data[0] = psStringCopy (file->filerule); 1313 } 1314 if (infiles == NULL) { 1315 return NULL; 1316 } 1317 if (infiles->n < 1) { 1318 psFree (infiles); 1319 return NULL; 1320 } 1321 1322 // determine the current format from the header 1323 // if no camera has been specified, use the first image as a template for the rest. 1324 fits = psFitsOpen (infiles->data[0], "r"); 1325 phu = psFitsReadHeader (NULL, fits); 1326 format = pmConfigCameraFormatFromHeader (config, phu); 1327 psFitsClose (fits); 1328 1329 // build the template fpa, set up the basic view 1330 file->fpa = pmFPAConstruct (config->camera); 1331 1332 // this file is (by virtue of being supplied in the argument list) coming from the fileset 1333 psFree (file->filerule); 1334 psFree (file->filextra); 1335 1336 // adjust the rules to identify these files in the file->names data 1337 file->filerule = psStringCopy ("@FILES"); 1338 // XXX this rule does not work in general 1339 file->filextra = psStringCopy ("{CHIP.NAME}.{CELL.NAME}"); 1340 1341 // examine the list of input files and validate their cameras 1342 for (int i = 0; i < infiles->n; i++) { 1343 if (i > 0) { 1344 fits = psFitsOpen (infiles->data[i], "r"); 1345 phu = psFitsReadHeader (NULL, fits); 1346 pmConfigValidateCameraFormat (format, phu); 1347 psFitsClose (fits); 1348 } 1349 1350 // set the view to the corresponding entry for this phu 1351 pmFPAview *view = pmFPAAddSourceFromHeader (file->fpa, phu, format); 1352 if (!view) { 1353 psError(PS_ERR_IO, false, "Unable to determine source for %s", file->name); 1354 return NULL; 1355 } 1356 1357 // XXX is this the correct psMD to save the filename? 1358 char *name = pmFPAfileNameFromRule (file->filextra, file, view); 1359 psMetadataAddStr (file->names, PS_LIST_TAIL, name, 0, "", infiles->data[i]); 1360 1361 psFree (view); 1362 psFree (name); 1363 psFree (phu); 1364 } 1365 psFree (format); 1366 psFree (infiles); 1367 *found = true; 1368 return file; 1369 } 1370 110 // XXX reconsider this function name / concept 1371 111 bool pmFPAfileAddFileNames (psMetadata *files, char *name, char *value, int mode) 1372 112 {
Note:
See TracChangeset
for help on using the changeset viewer.
