/* COPYRIGHT (c) 1995 Kapteyn Astronomical Institute University of Groningen, The Netherlands All Rights Reserved. #> history.dc1 Program: HISTORY Purpose: read, or update history or comment in set header Category: HEADER, UTILITY File: history.c Author: M.G.R. Vogelaar Keywords: INSET= Give set, subsets: Maximum number of subsets is 1024. ***STRING= Enter string to add to hist./comm.: [interactive mode] You can use HISTORY in an automatic or interactive mode. If you start HISTORY with a STRING= specification, then this string will be added to the history or comments depending on ITEM=. Otherwise, the interactive mode is started. ITEM= Select COMMENT or HISTORY items: C/[H] MODE= Do you want to EDIT or READ history items? E/[R] Description: In automatic mode: Add string in STRING= to history or comments items (ITEM=) for all subsets. If STRING= is empty, goto interactive mode. In interactive mode: List (or edit) all available information in the header of INSET= stored under the commentary (FITS) keywords COMMENT or HISTORY. One of these is selected with ITEM= If you want a list of the stored information, use MODE=R (the default). If you want to edit the infor- mation, use MODE=E In this mode, you enter the editor (specified with the environment variable EDITOR) and edit or create the file. If you save the changes in the editor, then also the header information will be updated. "History text should contain a history of steps and procedures associated with the processing of the associated data (NOST)". INSET= could also select subsets (e.g. INSET=AURORA FREQ 3:10). Read and edit actions are repeated for each subset separately. Before starting an edit action, the edit prompt will display which subset is used. Usually you will read/edit history and comment at top level. Then you need only to give the name of the set (e.g. INSET=AURORA) If you cancel an edit action, or leave the editor without writing the changes, the header information will not be changed. Notes: Use Hermes debug mode ( S) for debug information. Updates: Apr 25, 1995: VOG, Document created. Apr 25, 1996: VOG, STRING= added (automatic mode) #< */ /* history.c: include files */ #include "stdio.h" /* Defines ANSI C input and output utilities */ #include "stdlib.h" /* Defines the ANSI C functions for number */ /* conversion, storage allocation, and similar tasks.*/ #include "string.h" /* Declares the ANSI C string functions*/ /* like:strcpy, strcat etc.*/ #include "math.h" /* Declares the mathematical functions and macros.*/ #include "cmain.h" /* Defines the main body of a C program with */ /* MAIN_PROGRAM_ENTRY and IDENTIFICATION */ #include "gipsyc.h" /* Defines the ANSI-F77 types for Fortran to C intface */ /* including def. of char2str,str2char,tofchar,zadd */ /* and macros tobool and toflog */ #include "float.h" /* Definition of FLT_MAX etc.*/ #include "ctype.h" /* Declares ANSI C functions for testing characters */ /* like: isalpha, isdigit etc. also tolower, toupper.*/ /* Common includes */ #include "init.h" /* Declare task running to HERMES and initialize.*/ #include "finis.h" /* Informs HERMES that servant quits and cleans up the mess.*/ #include "anyout.h" /* General character output routine for GIPSY programs.*/ #include "error.h" /* User error handling routine. */ #include "myname.h" /* Obtain the name under which a GIPSY task is being run.*/ #include "nelc.h" /* Characters in F-string discarding trailing blanks.*/ #include "editfile.h" /* User action interface routine to edit a text file. */ /* User input routines */ #include "userfio.h" /* Easy-C companions for user interface routines.*/ #include "userint.h" /* User input interface routines.*/ #include "userlog.h" #include "userreal.h" #include "userdble.h" #include "usertext.h" #include "usercharu.h" #include "reject.h" /* Reject user input.*/ #include "cancel.h" /* Remove user input from table maintained by HERMES.*/ /* Input of sets */ #include "gdsinp.h" /* Input of set, subsets, return # subsets.*/ #include "gdspos.h" /* Define a position in a subset.*/ #include "gdsbox.h" /* Define a box inside/around a subset.*/ #include "gdsd_rewind.h" /* Set current read position at beginning of descriptor item.*/ #include "gdsd_delete.h" /* Delete descriptor item.*/ #include "gdsd_wvar.h" /* Write variable length record to descriptor item.*/ #include "gdsd_rvar.h" /* Read variable length record from descriptor item.*/ #include "gdsc_range.h" /* Return lower left and upper right corner of a subset.*/ #include "gdsc_name.h" /* Return the name of an axis.*/ #include "gdsc_ndims.h" /* Return the dimensionality of a coordinate word.*/ #include "gdsc_grid.h" /* Extract grid value.*/ #include "gdsc_fill.h" /* return coordinate word filled with a grid */ /* value for each axis.*/ #include "gdsi_read.h" /* Reads data from (part of) a set.*/ #include "gds_errstr.h" /* Obtain the message string associated with a GDS error code. */ /* DEFINITIONS: */ /* Initialize Fortran compatible string with macro 'fmake' */ #define fmake(fchr,size) { \ static char buff[size+1]; \ int i; \ for (i = 0; i < size; buff[i++] = ' '); \ buff[i] = 0; \ fchr.a = buff; \ fchr.l = size; \ } /* Malloc version of 'fmake. Strings allocated with' */ /* finit, must be freed with free( fc.a ) */ #define finit( fc , len ) { fc.a = malloc( ( len + 1 ) * sizeof( char ) ) ; \ fc.a[ len ] = '\0' ; \ fc.l = len ; } #define MYMAX(a,b) ( (a) > (b) ? (a) : (b) ) #define MYMIN(a,b) ( (a) > (b) ? (b) : (a) ) #define NINT(a) ( (a) < 0 ? (int)((a)-.5) : (int)((a)+.5) ) #define ABS(a) ( (a) < 0 ? (-(a)) : (a) ) #define PI 3.141592653589793 #define RAD(a) ( a * 0.017453292519943295769237 ) #define DEG(a) ( a * 57.295779513082320876798155 ) #define RELEASE "1.0" /* Version number */ #define MAXAXES 10 /* Max. axes in a set */ #define MAXSUBSETS 1024 /* Max. allowed subsets */ #define MAXBUF 4096 /* Buffer size for I/O */ #define STRLEN 256 /* Max length of strings */ #define FILENAMELEN 256 /* Max length of file names */ #define FITSLEN 20 /* Max length of header items etc.*/ #define EMPTYREAD -3 #define NOMOREDATA -4 #define EMPTYREWIND -7 #define OFFSET 17 #define NONE 0 /* Default levels in userxxx routines */ #define REQUEST 1 #define HIDDEN 2 #define EXACT 4 #define YES 1 /* C versions of .TRUE. and .FALSE. */ #define NO 0 /*--------------------------------------------------*/ /* The HISTORY and COMMENT keyword shall have no */ /* associated value; columns 9-80 may contain any */ /* ASCII text. NOST, FITS */ /*--------------------------------------------------*/ #define MAXRECLEN 80-9 /* Defines for in/output routines etc.*/ #define KEY_INSET tofchar("INSET=") #define MES_INSET tofchar("Give input set (, subsets):") /* Variables for input */ static fint subin[MAXSUBSETS]; /* Subset coordinate words */ static fint axnum[MAXAXES]; /* Array of size MAXAXES containing the */ /* axes numbers. The first elements (upto */ /* the dimension of the subset) contain the */ /* axes numbers of the subset, the other */ /* ones ontain the axes numbers outside the */ /* the subset ordered ccording to the */ /* specification by the user. */ static fint axcount[MAXAXES]; /* Array of size MAXAXES containing the */ /* number of grids along an axes as */ /* specified by the user. The first elements */ /* (upto the dimension of the subset) contain */ /* the length of the subset axes, the other */ /* ones contain the the number of grids along */ /* an axes outside the subset. */ /* the operation for each subset, Class 2 */ /* is for applications for which the operation */ /* requires an interaction between the different */ /* subsets. */ /* Miscellaneous */ static char message[STRLEN]; /* All purpose character buffer. */ static void editorerr( int err ) /*-----------------------------------------------------------*/ /* PURPOSE: Print error message for editor action. */ /*-----------------------------------------------------------*/ { if (err == -1) anyoutf( 1, "Warning: user canceled the edit" ); else if (err == -2) anyoutf( 1, "Warning: cannot start editor" ); else if (err == -3) anyoutf( 1, "Warning: editor process exited with error status"); else if (err == -666) anyoutf( 1, "Warning: this version of Hermes does not support EDITTFILE" ); } static void printGDSerror( int err ) /*-----------------------------------------------------------*/ /* PURPOSE: Print an error message associated with this error*/ /*-----------------------------------------------------------*/ { fchar Errstr; fint r = (fint) err; fmake( Errstr, STRLEN ); gds_errstr_c( Errstr, &r ); anyoutf( 1, "Application has GDS error: %.*s", nelc_c(Errstr), Errstr.a ); } static int makeFITSstr( char *str ) /*-----------------------------------------------------------*/ /* PURPOSE: Adjust string 'str' according to FITS standard. */ /* A string read from file will have a newline character */ /* which must be replaced by a 0. Check the string whether it*/ /* smaller than the FITS length defined in 'MAXRECLEN'. */ /* Return the length of the manipulated string. */ /*-----------------------------------------------------------*/ { int i; int len = strlen(str); if (len > MAXRECLEN) { str[MAXRECLEN-1] = '\0'; len = MAXRECLEN; } for (i = len-1; i >= 0; i--) { if (str[i] == '\n') { str[i] = '\0'; break; } } return( nelc_c(tofchar(str)) ); } static char *showsub( char *buf, fchar Setin, fint subset, fint *axnum ) /*--------------------------------------------------------------*/ /* PURPOSE: Put subset info in char. buffer. */ /* Generate name of subset axis and give the subset. */ /*--------------------------------------------------------------*/ { fint setdim, subdim; int n; fchar Axisname; fint r1, r2; fint grid; fint setlevel = 0; char buf2[STRLEN]; setdim = gdsc_ndims_c( Setin, &setlevel ); /* dimension of set */ subdim = gdsc_ndims_c( Setin, &subset ); if (setdim == subdim) { strcpy( buf, "Set level" ); return( buf ); } strcpy( buf, " Subset:" ); fmake( Axisname, FITSLEN ); for (n = subdim; n < setdim; n++) { r2 = r1 = 0; gdsc_name_c( Axisname, Setin, &axnum[n], &r1 ); grid = gdsc_grid_c( Setin, &axnum[n], &subset, &r2 ); if ( (n + 1) == setdim) sprintf( buf2, "%s=%d ", strtok(Axisname.a, " -"), grid ); else sprintf( buf2, "%s=%d,", strtok(Axisname.a, " -"), grid ); strcat( buf, buf2 ); } return( buf ); } MAIN_PROGRAM_ENTRY /*-------------------------------------------------------------------------*/ /* The macro MAIN_PROGRAM_ENTRY replaces the C-call main() to start the */ /* main body of your GIPSY application. Variables defined as 'fchar' start */ /* with a capital. */ /*-------------------------------------------------------------------------*/ { fint maxsubs = MAXSUBSETS; fint maxaxes = MAXAXES; /* Max num. of axes the program can deal with.*/ fint class = 1; /* Class 1 is for applications which repeat */ fint showdev = 1; fint nsubs; /* Number of input subsets */ fint dfault; /* Default option for input etc */ fint nitems; fint r1; /* Result values for different routines. */ bool readhist; bool history; bool comment; bool edit; fchar Setin; /* Name of input set */ fchar Option; fchar Descriptor; fchar Addtxt; fint subnr; /* Counter for subset loop. */ fint subdim; int status; int addtxt; /* Add text automatically to H/C */ init_c(); /* contact Hermes */ /* Task identification */ { static fchar Task; /* Name of current task */ fmake( Task, 20 ); /* Macro 'fmake' must be available */ myname_c( Task ); /* Get task name */ Task.a[nelc_c(Task)] = '\0'; /* Terminate task name with null char. */ IDENTIFICATION( Task.a, RELEASE ); /* Show task and version */ } /*--------------------------------------------------*/ /* Get the input set. Documentation can be found in */ /* $gip_sub/gdsinp.dc2 */ /*--------------------------------------------------*/ { fmake( Setin, STRLEN ); dfault = NONE; showdev = 16; /* Show set info only in debug mode */ subdim = 0; /* Allow only n-dim structures */ nsubs = gdsinp_c( Setin, /* Name of input set. */ subin, /* Array containing subsets coordinate words. */ &maxsubs, /* Maximum number of subsets in 'subin'.*/ &dfault, /* Default code as is USERxxx. */ KEY_INSET, /* Keyword prompt. */ MES_INSET, /* Keyword message for the user. */ &showdev, /* Device number (as in ANYOUT). */ axnum, /* Array of size 'maxaxes' containing the axes numbers. */ /* The first elements (upto the dimension of the subset) */ /* contain the axes numbers of the subset, */ /* the other ones contain the axes numbers */ /* outside the subset ordered according to the */ /* specification by the user. */ axcount, /* Number of grids on axes in 'axnum' */ &maxaxes, /* Max. number of axes. */ /* the operation for each subset. */ &class, /* Class 1 is for applications which repeat */ &subdim ); /* Dimensionality of the subsets for class 1 */ } fmake( Option, 1 ); (void) str2char( "H", Option ); dfault = REQUEST; nitems = 1; r1 = usercharu_c( Option, &nitems, &dfault, tofchar("ITEM="), tofchar("Select COMMENT or HISTORY items: C/[H]") ); history = (Option.a[0] == 'H'); if (Option.a[0] != 'H' && Option.a[0] != 'C') { anyoutf( 1, "No such option ==> exit program!" ); finis_c(); return(EXIT_SUCCESS); } comment = !history; fmake( Addtxt, MAXRECLEN ); dfault = HIDDEN; nitems = 1; if (history) strcpy( message, "Enter string to add to history: [interactive mode]" ); else strcpy( message, "Enter string to add to comments: [interactive mode]" ); r1 = usertext_c( Addtxt, &dfault, tofchar("STRING="), tofchar(message) ); addtxt = (r1 > 0); if (!addtxt) { (void) str2char( "R", Option ); if (history) strcpy( message, "Do you want to EDIT or READ history items? E/[R]"); else strcpy( message, "Do you want to EDIT or READ comment items? E/[R]"); dfault = REQUEST; nitems = 1; r1 = usercharu_c( Option, &nitems, &dfault, tofchar("MODE="), tofchar(message) ); readhist = (Option.a[0] == 'R'); edit = !readhist; if (Option.a[0] != 'R' && Option.a[0] != 'E') { anyoutf( 1, "No reading or appending ==> exit program!" ); finis_c(); return(EXIT_SUCCESS); } } fmake( Descriptor, FITSLEN ); if (history) (void) str2char( "HISTORY", Descriptor ); else (void) str2char( "COMMENT", Descriptor ); if (addtxt) { makeFITSstr( Addtxt.a ); anyoutf( 16, "Writing: %s", Addtxt.a ); for(subnr = 0; subnr < nsubs; subnr++) { r1 = 0; gdsd_wvar_c( Setin, Descriptor, &subin[subnr], Addtxt, &r1 ); if (r1 < 0) printGDSerror( r1 ); } finis_c(); return( EXIT_SUCCESS ); } if (readhist) /*--------------------------------------------------*/ /* User wants to list all history and comment items.*/ /*--------------------------------------------------*/ { char item[10]; char border[STRLEN]; if (history) strcpy( item, "HISTORY" ); else strcpy( item, "COMMENT" ); r1 = sprintf( message, "%s from header of set [%.*s]", item, nelc_c(Setin), Setin.a ); memset( border, '=', r1 ); border[r1] = '\0'; anyoutf( 1, " " ); anyoutf( 1, "%*s%s", OFFSET, " ", border ); anyoutf( 1, "%*s%s", OFFSET, " ", message ); anyoutf( 1, "%*s%s", OFFSET, " ", border ); for(subnr = 0; subnr < nsubs; subnr++) /* Loop over all subsets */ { r1 = 0; gdsd_rewind_c( Setin, /* Rewind var.recs. before read action */ Descriptor, &subin[subnr], &r1 ); if (r1 < 0) /* Something wrong */ { if (r1 == EMPTYREWIND) anyoutf( 1, "No items found" ); else printGDSerror( r1 ); } else { do { fchar Headerstr; fmake( Headerstr, STRLEN ); /* Read variable length record from descriptor item */ r1 = 0; gdsd_rvar_c( Setin, Descriptor, &subin[subnr], Headerstr, &r1 ); if (r1 < 0 && r1 != NOMOREDATA) /* Something wrong, but not end of data */ printGDSerror( r1 ); else if (r1 != NOMOREDATA) anyoutf( 1, "%.*s", nelc_c(Headerstr), Headerstr.a ); } while (r1 >= 0); /* No records anymore */ } memset( border, '-', 80 ); border[80] = '\0'; anyoutf( 1, border ); } } else /*--------------------------------------------------*/ /* Read items and store in Ascii file. Edit file and*/ /* store new items from that file in header. */ /*--------------------------------------------------*/ { char tmpfile[STRLEN]; FILE *fp; for(subnr = 0; subnr < nsubs; subnr++) { tmpnam( tmpfile ); /* Temp. file for editor */ fp = fopen( tmpfile, "w" ); if (fp == NULL) errorf( 4, "Could not open tmp file: %s", tmpfile ); r1 = 0; gdsd_rewind_c( Setin, Descriptor, &subin[subnr], &r1 ); anyoutf( 16, "Error return code in gdsd_rewind = %d", r1 ); status = r1; if (r1 < 0 && r1 != EMPTYREWIND) /* Something wrong */ printGDSerror( r1 ); else /*--------------------------------------------------*/ /* There is data. Read the data and write to the */ /* temp. file. */ /*--------------------------------------------------*/ { if (status != EMPTYREWIND) { do { fchar Headerstr; fmake( Headerstr, STRLEN ); /* Read variable length record from descriptor item */ r1 = 0; gdsd_rvar_c( Setin, Descriptor, &subin[subnr], Headerstr, &r1 ); anyoutf( 16, "Error return code in gdsd_rvar = %d", r1 ); if (r1 < 0 && r1 != NOMOREDATA) /* Something wrong, but not end of data */ printGDSerror( r1 ); else fprintf( fp, "%.*s\n", nelc_c(Headerstr), Headerstr.a ); } while (r1 >= 0); /* No records anymore */ } fclose( fp ); /* Close tmp file in any case */ if (subin[subnr] != 0) /* If not top level ==> show */ { showsub( message, Setin, subin[subnr], axnum ); strcat( message, " (^C to abort edit)" ); } else strcpy( message, "Use ^C to abort edit" ); r1 = editfile_c( tofchar(tmpfile), tofchar(message) ); if (r1 != 0) editorerr( r1 ); else /*--------------------------------------------------*/ /* Delete the history items on this level. Start */ /* reading from tmp. file and add new items. */ /*--------------------------------------------------*/ { fp = fopen( tmpfile, "r" ); if (fp == NULL) errorf( 4, "Could not read tmp file: %s", tmpfile ); if (status != EMPTYREWIND) { anyoutf( 16, "Deleting %.*s item(s)", nelc_c(Descriptor), Descriptor.a ); r1 = 0; gdsd_delete_c( Setin, Descriptor, &subin[subnr], &r1 ); if (r1 < 0) printGDSerror( r1 ); } /*--------------------------------------------------*/ /* Read from file. But process string first. Its */ /* length may not exceed a certain length, and the */ /* newline character must be replaced by a 0. */ /*--------------------------------------------------*/ while (!feof(fp)) { if ( fgets(message, STRLEN, fp) ); { if (!feof(fp)) /* Extra check on end of file */ { if ( makeFITSstr(message) ) { anyoutf( 16, "Writing: %s", message ); r1 = 0; gdsd_wvar_c( Setin, Descriptor, &subin[subnr], tofchar(message), &r1 ); if (r1 < 0) printGDSerror( r1 ); } } } } fclose( fp ); r1 = remove( tmpfile ); /* Remove tmp file */ } } } } finis_c(); return(EXIT_SUCCESS); /* Dummy return */ }