source: Sophya/trunk/SophyaExt/FitsIOServer/fitsinoutfile.cc@ 3067

Last change on this file since 3067 was 3047, checked in by ansari, 19 years ago

Ajout FitsInOutFile::SkipEmptyFirstHDU() , positionnement sur HDU 2 si HDU 1 vide ds operateur >> lisant des tables + autres petites corrections , Reza 11/08/2006

  • Property svn:executable set to *
File size: 18.0 KB
RevLine 
[2820]1#include "sopnamsp.h"
2#include "machdefs.h"
3#include "segdatablock.h"
4#include "fitsinoutfile.h"
[2932]5#include "strutil.h"
[2820]6#include <stdio.h>
7#include <string.h>
8#include <iostream>
9
[2843]10string FitsTypes::ImageTypeToTypeString(int ityp)
11{
12 switch (ityp) {
13 case BYTE_IMG :
14 return "uint_1";
15 break;
16 case SHORT_IMG :
17 return "int_2";
18 break;
19 case LONG_IMG :
20 return "int_4";
21 break;
22 case FLOAT_IMG :
23 return "r_4";
24 break;
25 case DOUBLE_IMG :
26 return "r_8";
27 break;
28 default:
29 return "???" ;
30 break;
31 }
32 return "";
33}
34string FitsTypes::DataTypeToTypeString(int ityp)
35{
36 switch (ityp) {
37 case TBYTE :
38 return "uint_1";
39 break;
40 case TSHORT :
41 return "int_2";
42 break;
43 case TUSHORT :
44 return "uint_2";
45 break;
46 case TINT :
47 if (sizeof(int) == 4) return "int_4";
48 else if (sizeof(int) == 8) return "int_8";
49 else if (sizeof(int) == 2) return "int_2";
50 break;
51 case TUINT :
52 if (sizeof(int) == 4) return "uint_4";
53 else if (sizeof(int) == 8) return "uint_8";
54 else if (sizeof(int) == 2) return "uint_2";
55 break;
56 case TLONG :
57 if (sizeof(long) == 4) return "int_4";
58 else if (sizeof(long) == 8) return "int_8";
59 else if (sizeof(long) == 2) return "int_2";
60 break;
61 case TULONG :
62 if (sizeof(long) == 4) return "uint_4";
63 else if (sizeof(long) == 8) return "uint_8";
64 else if (sizeof(long) == 2) return "uint_2";
65 break;
66#ifdef TLONGLONG
67 case TLONGLONG :
68 return "int_8";
69 break;
70#endif
71 case TFLOAT :
72 return "r_4";
73 break;
74 case TDOUBLE :
75 return "r_8";
76 break;
77 case TCOMPLEX :
78 return "complex< r_4 >";
79 break;
80 case TDBLCOMPLEX :
81 return "complex< r_8 >";
82 break;
83 case TSTRING :
84 return "string";
85 break;
86 default:
87 return "???" ;
88 break;
89 }
90 return "";
91}
[2820]92
93/*!
94 \class SOPHYA::FitsInOutFile
95 \ingroup FitsIOServer
96 \brief Wrapper class for cfitsio library functions
97*/
98
99/*-- Methode --*/
100//! Default constructor - The file should be opened subsequently using Open
101FitsInOutFile::FitsInOutFile()
102{
103 fptr_ = NULL;
[2864]104 mode_ = Fits_RO;
[2820]105 SetDef_BinTable();
[2843]106 SetDef_StrColWidth();
[2820]107}
108
109/*-- Methode --*/
110//! Constructor with specification of file name and access mode
111FitsInOutFile::FitsInOutFile(string const & name, FitsIOMode mode)
112{
113 // cout << " DBG - FitsInOutFile(string name= " << name << ")" << endl;
114 fptr_ = NULL;
[2860]115 ownfptr = true;
[2820]116 SetDef_BinTable();
[2843]117 SetDef_StrColWidth();
[2820]118 Open(name.c_str(), mode);
119}
120
121/*-- Methode --*/
122//! Constructor with specification of file name and access mode
123FitsInOutFile::FitsInOutFile(const char * name, FitsIOMode mode)
124{
125 // cout << " DBG - FitsInOutFile(char* name= " << name << ")" << endl;
126 fptr_ = NULL;
[2860]127 ownfptr = true;
[2820]128 SetDef_BinTable();
[2843]129 SetDef_StrColWidth();
[2820]130 Open(name, mode);
131}
132
[2860]133/*! \brief Copy constructor
[2864]134 \warning The fits file pointer is owned by the original FitsInOutFile object and
135 should not be closed as long as the new object is being used.
[2860]136*/
137FitsInOutFile::FitsInOutFile(FitsInOutFile const& fios)
138{
139 fptr_ = fios.fptr_;
140 fname_ = fios.fname_;
141 mode_ = fios.mode_;
142 ownfptr = false;
143 SetDef_BinTable();
144 SetDef_StrColWidth();
145}
146
[2820]147/* -- Fonction utilitaire pour verifier fichier ouvert (pointeur non null) -- */
148static inline void CheckFitsPtr(fitsfile* fptr) {
149 if (fptr == NULL) throw FitsIOException("FitsInOutFile/Error - NULL fitsfile pointer ");
150 return;
151}
152/* -- Fonction utilitaire pour verifier le code d'erreur fitsio -- */
153static inline bool FitsCheckStatus(int st, char * emsg = NULL) {
154 if (st) {
155 fits_report_error(stderr, st);
156 if (emsg) {
157 char buff[FLEN_ERRMSG];
158 fits_get_errstatus(st, buff);
159 string msg = emsg;
160 msg += buff;
161 throw FitsIOException(msg);
162 }
163 else return true;
164 }
165 else return false;
166}
167
168/*-- Methode --*/
169//! Destructor - Closes the fits file (if opened)
170FitsInOutFile::~FitsInOutFile()
171{
172 Close();
173}
174
175/*-- Methode --*/
176//! Opens the named fits file (see cfitsio routines fits_open_file and fits_create_file)
177void FitsInOutFile::Open(const char* name, FitsIOMode mode)
178{
179 if (fptr_ != NULL)
180 throw FitsIOException("FitsInOutFile::Open() /Error - file already opened ");
181 int status = 0;
182 fptr_ = NULL;
183 switch ( mode ) {
184 case Fits_RO :
185 fits_open_file(&fptr_, name, READONLY, &status);
186 break;
187 case Fits_RW :
188 fits_open_file(&fptr_, name, READWRITE, &status);
189 break;
190 case Fits_Create :
191 fits_create_file(&fptr_, name, &status);
192 break;
193 }
194 FitsCheckStatus(status, "FitsInOutFile::Open() Error: ");
195 fname_ = name;
196 mode_ = mode;
[2860]197 ownfptr = true;
[2820]198 return;
199}
200
201/*-- Methode --*/
202//! Opens the named fits file (see cfitsio routines fits_open_file and fits_create_file)
203void FitsInOutFile::Close()
204{
[2860]205 if (ownfptr == false) return;
[2820]206 if (fptr_ == NULL) return;
207 int status = 0;
208 if (mode_ == Fits_Create) {
209 status = 0;
210 int hdutyp;
211 fits_movabs_hdu(FitsPtr() , 1, &hdutyp, &status);
212 status = 0;
[2843]213 float sfv = Version();
[2820]214 fits_write_key(FitsPtr(), TFLOAT, "SOPHYAFV", &sfv,
215 "SOPHYA FitsIOServer module version", &status);
216 fits_write_date(FitsPtr(), &status);
217 status = 0;
218 fits_write_comment(FitsPtr(), "------------- SOPHYA (http://www.sophya.org) -------------", &status);
219 fits_write_comment(FitsPtr(), " (C) LAL/IN2P3-CNRS Orsay , (C) DAPNIA/CEA Saclay (FRANCE)", &status);
220 fits_write_comment(FitsPtr(), "-----------------------------------------------------------", &status);
221 }
222 MoveAbsToHDU(1);
223 status = 0;
224 if (fptr_) fits_close_file(fptr_, &status);
225 if (status) {
226 cerr << " FitsInOutFile::Close - Error closing fits file !" << endl;
227 fits_report_error(stderr, status);
228 }
229}
230
[2864]231/*! \brief Closes the current fits file and uses \b fios file for subsequent operations.
232 \warning The fits file pointer is owned by the original FitsInOutFile object and
233 should not be closed as long as the current object (this) is being used.
234*/
[2820]235
[2864]236void FitsInOutFile::ShareFitsPtr(FitsInOutFile const& fios)
237{
238 Close();
239 fptr_ = fios.fptr_;
240 fname_ = fios.fname_;
241 mode_ = fios.mode_;
242 ownfptr = false;
243}
244
[2820]245/*-- Methode --*/
246float FitsInOutFile::cfitsioVersion()
247{
248 float ver;
249 fits_get_version(&ver);
250 return ver;
251}
252
253/*-- Methode --*/
254int FitsInOutFile::NbHDUs() const
255{
256 int status = 0;
257 int nbhdu = 0;
258 fits_get_num_hdus(FitsPtr() , &nbhdu, &status);
259 FitsCheckStatus(status, "FitsInOutFile::NbHDUs() Error: ");
260 return nbhdu;
261}
262
263/*-- Methode --*/
264int FitsInOutFile::CurrentHDU() const
265{
266 int status = 0;
267 int curhdu = 0;
268 fits_get_hdu_num(FitsPtr() , &curhdu);
269 return curhdu;
270}
271
272/*-- Methode --*/
273int FitsInOutFile::CurrentHDUType() const
274{
275 int status = 0;
276 int hdutyp = 0;
277 fits_get_hdu_type(FitsPtr() , &hdutyp, &status);
278 FitsCheckStatus(status, "FitsInOutFile::CurrentHDUType() Error: ");
279 return hdutyp;
280}
281
282/*-- Methode --*/
283string FitsInOutFile::CurrentHDUTypeStr() const
284{
285 int status = 0;
286 int hdutyp = 0;
287 fits_get_hdu_type(FitsPtr() , &hdutyp, &status);
288 if ( FitsCheckStatus(status, "FitsInOutFile::CurrentHDUTypeStr() Error: ") )
289 return "Unknown";
290 else {
291 if (hdutyp == IMAGE_HDU) return "IMAGE_HDU";
292 else if (hdutyp == BINARY_TBL) return "BINARY_TBL";
293 else if (hdutyp == ASCII_TBL) return "ASCII_TBL";
294 else return "Unknown";
295 }
296}
297
298/*-- Methode --*/
299int FitsInOutFile::MoveAbsToHDU(int hdunum)
300{
301 int status = 0;
302 int hdutyp = 0;
303 fits_movabs_hdu(FitsPtr() , hdunum, &hdutyp, &status);
304 FitsCheckStatus(status, "FitsInOutFile::MoveAbsToHDU Error: ");
305 return hdutyp;
306}
307
308/*-- Methode --*/
309int FitsInOutFile::MoveRelToHDU(int hdunum)
310{
311 int status = 0;
312 int hdutyp = 0;
313 fits_movrel_hdu(FitsPtr() , hdunum, &hdutyp, &status);
314 FitsCheckStatus(status, "FitsInOutFile::MoveRelToHDU Error: ");
315 return hdutyp;
316}
317
318/*-- Methode --*/
319int FitsInOutFile::MoveToNextHDU()
320{
321 if (CurrentHDU() < NbHDUs())
322 return MoveRelToHDU(1);
323 else return -1;
324}
325
326/*-- Methode --*/
[3047]327/*!
328 Skip HDU 1 if NAXIS=0 (no data), on a file opened for reading.
329 return true if moved to HDU No 2
330*/
331bool FitsInOutFile::SkipEmptyFirstHDU()
332{
333 if (fptr_ == NULL) return false;
334 if (mode_ == Fits_Create) return false;
335 if (CurrentHDU() != 1) return false;
336 int naxis = 2;
337 long naxes[2];
338 GetImageHDUInfo(naxis, naxes);
339 if ((naxis == 0) && (NbHDUs() > 1)) {
340 MoveRelToHDU(1);
341 return true;
342 }
343 return false;
344}
345
346/*-- Methode --*/
[2820]347void FitsInOutFile::CreateImageHDU(int bitpix, int naxis, long* naxes)
348{
349 int status = 0;
350 fits_create_img(fptr_, bitpix, naxis, naxes, &status);
351 FitsCheckStatus(status,"FitsInOutFile::CreateImageHDU() Error: ");
352 return;
353}
354
355/*-- Methode --*/
356 /*!
357 See cfitsio function fits_get_img_param() for more information
358 naxis : input=max naxes dimension / out=image dimension
359 Rc : return the image type (bitpix)
360 */
361int FitsInOutFile::GetImageHDUInfo(int& naxis, long* naxes) const
362{
363 int status = 0;
364 int maxdim = naxis;
365 int bitpix = 0;
366 fits_get_img_param(fptr_, maxdim, &bitpix, &naxis, naxes, &status);
367 FitsCheckStatus(status, "FitsInOutFile::GetImageHDUInfo() Error: ");
368 return bitpix;
369}
370
371/*-- Methode --*/
372long FitsInOutFile::GetNbRows() const
373{
374 int status = 0;
375 long nbrow = 0;
376 fits_get_num_rows(FitsPtr() , &nbrow, &status);
377 FitsCheckStatus(status, "FitsInOutFile::GetNbRows() Error: " );
378 return nbrow;
379}
380
381/*-- Methode --*/
382int FitsInOutFile::GetNbCols() const
383{
384 int status = 0;
385 int nbcol = 0;
386 fits_get_num_cols(FitsPtr() , &nbcol, &status);
387 FitsCheckStatus(status, "FitsInOutFile::GetNbCols() Error: ");
388 return nbcol;
389}
390
391/*-- Methode --*/
392/*!
393 Create a binary or ascii table - See cfitsio routine fits_create_tbl for more information.
394 \param extname : extension name. NextExtensionName() will be used if extname == NULL or extname == "".
395 \param ncols : Number of columns
396 \param ttype : Column names
397 \param tform : Column data types J / V / K / E / D ...
398 \param tunit : Column units
399*/
400void FitsInOutFile::CreateTable(int tbltyp, const char * extname, int ncols,
401 char * ttype[], char * tform[],
402 char * tunit[], long ininr)
403{
404 int status = 0;
405
406 char * extn;
407 if ( (extname != NULL) && (extname[0] != '\0') ) extn = const_cast<char *>(extname);
408 else extn = const_cast<char *>(next_extname_.c_str());
409
410 fits_create_tbl(FitsPtr(), tbltyp, ininr, ncols, ttype,
411 tform, tunit, extn, &status);
412 next_extname_ = "";
413 FitsCheckStatus(status, "FitsInOutFile::CreateTable() Error: ");
414 return;
415}
416
417/*-- Methode --*/
418/*!
419 Create a binary or ascii table - See cfitsio routine fits_create_tbl for more information.
420 number of columns specified by colnames.size()
421 \param extname : extension name
422 \param colnames : Column names
423 \param tform : Column data types J / V / K / E / D ...
424 \param tunit : Column units
425*/
426void FitsInOutFile::CreateTable(int tbltyp, const string & extname,
427 const vector<string> & colnames,
428 const vector<string> & tform,
429 const vector<string> & tunit,
430 long ininr)
431{
432 if ( (colnames.size() != tform.size() ) ||
433 (colnames.size() != tunit.size() ) )
434 throw SzMismatchError("FitsInOutFile::CreateTable(): different sizes for colnames,tform,tunit");
435
436 // On utilise les SegDataBlock<T> pour eviter d'avoir a gerer les new/delete
437 // en plus avec les exceptions ...
438 size_t kk;
439 int ncols = colnames.size();
440
441 SegDataBlock<const char *> colnm(colnames.size(), 1);
442 for(kk=0; kk<colnames.size(); kk++) colnm[kk] = colnames[kk].c_str();
443 SegDataBlock<const char *> tfm(tform.size(), 1);
444 for(kk=0; kk<tform.size(); kk++) tfm[kk] = tform[kk].c_str();
445 SegDataBlock<const char *> tun(tunit.size(), 1);
446 for(kk=0; kk<tunit.size(); kk++) tun[kk] = tunit[kk].c_str();
447
448 CreateTable(tbltyp, const_cast<char *>(extname.c_str()), ncols,
449 const_cast<char **>(colnm.GetSegment(0)),
450 const_cast<char **>(tfm.GetSegment(0)),
451 const_cast<char **>(tun.GetSegment(0)),
452 ininr);
453}
454
455/*-- Methode --*/
456/*!
457 Return number of columns in table (See fits_get_colname and fits_get_coltype for more information)
458 \param colnames : Column names
459 \param coltypes : Column data types ( TSTRING / TSHORT / TFLOAT / ... )
460 \param repcnt : Repeat count (for columns with vector data)
461 \param width : The width (in bytes) of a single element in a column
462
463*/
464long FitsInOutFile::GetColInfo(vector<string> & colnames,
465 vector<int> & coltypes,
466 vector<long> & repcnt,
467 vector<long> & width)
468{
469
470 int status = 0;
471
472 colnames.clear();
473 coltypes.clear();
474 width.clear();
475 repcnt.clear();
476
477 int colnum, typecode;
478 long repeat, colw;
479 int ncols = 0;
480 char colname[128]; // longueur max d'un nom de colonne
481
482 while (status != COL_NOT_FOUND) {
483 fits_get_colname(FitsPtr(), CASEINSEN, "*", colname, &colnum, &status);
484 if (status == COL_NOT_FOUND) break;
[2844]485 if ( (status != COL_NOT_UNIQUE) && (status != 0) ) {
[2820]486 char buff[32];
487 fits_get_errstatus(status, buff);
488 string msg = "FitsInOutFile::GetColInfo() Error(1): " ;
489 msg += buff;
490 throw FitsIOException(msg);
491 }
492 int sta2 = 0;
493 fits_get_coltype(FitsPtr(), colnum, &typecode, &repeat, &colw, &sta2);
494 FitsCheckStatus(sta2, "FitsInOutFile::GetColInfo() Error(2): ");
495
496 colnames.push_back(colname);
497 coltypes.push_back(typecode);
498 repcnt.push_back(repeat);
499 width.push_back(colw);
[2844]500 if (status == 0) break;
[2820]501 }
502 return colnames.size();
503}
504
505/*-- Methode --*/
506void FitsInOutFile::InsertColumn(int numcol, const char* colname, const char* fmt)
507{
508 int status = 0;
509 fits_insert_col(FitsPtr(), numcol, const_cast<char *>(colname),
510 const_cast<char *>(fmt), &status);
511 FitsCheckStatus(status, "FitsInOutFile::AddColumn() Error: ");
512
513 return;
514}
515
516/*-- Methode --*/
[2843]517/*!
518 Return the value associated to the keyword \b key in the header as a string.
519 If the keyword is not found in the fits header, an empty string is returned
520 and the \b nosk flag is set to true.
521*/
522string FitsInOutFile::KeyValue(string const & key, bool& nosk)
[2820]523{
[2843]524 nosk = false;
[2820]525 int status = 0;
526 char value[FLEN_VALUE], comm[FLEN_COMMENT];
527 fits_read_key(FitsPtr(), TSTRING, const_cast<char *>(key.c_str()), value, comm, &status);
[2843]528 if (status == KEY_NO_EXIST) {
529 nosk = true;
530 return "";
531 }
[2820]532 FitsCheckStatus(status, "FitsInOutFile::KeyValue() Error: ");
533 return value;
534}
535
536/*-- Methode --*/
[2932]537/*!
538 Read the current fits header information as pairs of '(keyword,value)' appended
539 to the DVList object \b dvl.
540 \param dvl : DVList object containing filled with (keyword,value) pairs.
541 \param stripkw : if true (default), remove leading and trailing spaces from keyword
[2937]542 \param keepstkey : if true , keep keys of type TYP_STRUC_KEY
[2932]543*/
544int FitsInOutFile::GetHeaderRecords(DVList& dvl, bool stripkw, bool keepstkey)
[2820]545{
546 int status = 0;
547 int nkeys = 0;
548 fits_get_hdrspace(FitsPtr(), &nkeys, NULL, &status);
549 FitsCheckStatus(status, "FitsInOutFile::GetHeaderRecords() Error(1): ");
550
551 char record[FLEN_CARD], value[FLEN_VALUE], comm[FLEN_COMMENT];
552 string comment;
553 int nok = 0;
554 for(int kk=1; kk<=nkeys; kk++) {
555 status = 0;
556 fits_read_record(FitsPtr(), kk, record, &status);
557 FitsCheckStatus(status, "FitsInOutFile::GetHeaderRecords() Error(2): ");
558 int kclas = fits_get_keyclass(record);
[2932]559 if ( (!keepstkey && (kclas == TYP_STRUC_KEY))
560 || (kclas == TYP_NULL_KEY) ) continue;
[2820]561 int len = 0;
562 status = 0;
563 fits_get_keyname(record, value, &len, &status);
564 if (status) continue;
[2932]565 if (stripkw) strip(value, 'B', ' ');
[2820]566 string keyname = value;
567 status = 0;
568 fits_parse_value(record, value, comm, &status);
569 if (status) continue;
570 if (kclas == TYP_COMM_KEY) {
571 if (comment.length() > 0) comment += '\n';
572 comment += value;
573 continue;
574 }
575 char ktyp;
576 status = 0;
577 fits_get_keytype(value, &ktyp, &status);
578 if (status) continue;
579 switch (ktyp) {
580 case 'C' :
581 case 'L' : // Il faudra traiter le cas des nb complexes
582 case 'X' : // idem pour les valeurs logiques
[2932]583 dvl.SetS(keyname, value);
[2820]584 break;
585 case 'I' :
586 dvl.SetI(keyname, atoi(value));
587 break;
588 case 'F' :
589 dvl.SetD(keyname, atof(value));
590 break;
591 }
592 if (strlen(comm) > 0) {
593 string scom = comm;
594 dvl.SetComment(keyname, scom);
595 }
596 nok++;
597 }
598 if (comment.length() > 0) dvl.Comment() = comment;
599 return nok;
600}
601
602/*-- Methode --*/
603void FitsInOutFile::WriteKey(const char * kname, MuTyV const & mtv,
604 const char *comment)
605{
606 CheckFitsPtr(FitsPtr());
607 char keyname[FLEN_KEYWORD], comm[FLEN_COMMENT], sval[FLEN_VALUE];
608 long lval;
609 double dval;
610 string s;
611
612 keyname[0] = '\0';
613 strncpy(keyname, kname, FLEN_KEYWORD); keyname[FLEN_KEYWORD-1] = '\0';
614 comm[0] = '\0';
615 if (comm != NULL)
616 strncpy(comm, comment, FLEN_COMMENT); comm[FLEN_COMMENT-1] = '\0';
617 int status = 0;
618 switch (mtv.Type()) {
619 case MuTyV::MTVInteger :
620 lval = mtv.GetIntPart();
621 fits_write_key(FitsPtr(), TLONG, keyname, &lval, comm, &status);
622 break;
623 case MuTyV::MTVFloat :
624 dval = mtv.GetRealPart();
625 fits_write_key(FitsPtr(), TDOUBLE, keyname, &dval, comm, &status);
626 break;
627 case MuTyV::MTVString :
628 strncpy(sval, mtv.GetStringPointer()->c_str(), FLEN_VALUE);
629 keyname[FLEN_VALUE-1] = '\0';
630 fits_write_key(FitsPtr(), TSTRING, keyname, sval, comm, &status);
631 break;
632 default :
633 s = (string)mtv;
634 strncpy(sval, s.c_str(), FLEN_VALUE);
635 keyname[FLEN_VALUE-1] = '\0';
636 fits_write_key(FitsPtr(), TSTRING, keyname, sval, comm, &status);
637 break;
638 }
639 FitsCheckStatus(status, "FitsInOutFile::WriteKey() Error: ");
640 return;
641}
642
643/*-- Methode --*/
644int FitsInOutFile::WriteHeaderRecords(DVList & dvl)
645{
646
647 CheckFitsPtr(FitsPtr());
648 int status = 0;
649 DVList::ValList::const_iterator it;
650 for(it = dvl.Begin(); it != dvl.End(); it++)
651 WriteKey( (*it).first, (*it).second.elval, (*it).second.elcomm);
652 // Ecriture commentaires
653 return 0;
654
655}
656
657/*-- Methode --*/
658void FitsInOutFile::Print(ostream& os, int lev) const
659{
660 string mode;
661 if (mode_ == Fits_Create) mode = "Create";
662 else if (mode_ == Fits_RO) mode = "ReadOnly";
663 else mode = "ReadWrite";
664 os << " FitsInOutFile(FileName= " << fname_ << " Mode="
665 << mode << ") fitsioVers= " << cfitsioVersion() << endl;
666 os << " TotalNumberHDU= " << NbHDUs() << " CurrentHDU: Num= "
667 << CurrentHDU() << " Type= " << CurrentHDUTypeStr() << endl;
668
669 return;
670}
671
672
Note: See TracBrowser for help on using the repository browser.