void nco_hst_att_cat /* [fnc] Add command line, date stamp to history attribute */ (const int out_id, /* I [id] netCDF output-file ID */ const char * const hst_sng) /* I [sng] String to add to history attribute */ { /* Purpose: Add command line and date stamp to existing history attribute, if any, and write them to specified output file */ /* Length of string + NUL required to hold output of ctime() */ #define TIME_STAMP_SNG_LNG 25 char att_nm[NC_MAX_NAME]; char *ctime_sng; char *history_crr=NULL; char *history_new; char time_stamp_sng[TIME_STAMP_SNG_LNG]; const char sng_history[]="history"; /* [sng] Possible name of history attribute */ int idx; int glb_att_nbr; long att_sz=0; nc_type att_typ; time_t time_crr_time_t; /* Create timestamp string */ time_crr_time_t=time((time_t *)NULL); ctime_sng=ctime(&time_crr_time_t); /* NUL-terminate time_stamp_sng */ time_stamp_sng[TIME_STAMP_SNG_LNG-1]='\0'; /* Get rid of carriage return in ctime_sng */ (void)strncpy(time_stamp_sng,ctime_sng,TIME_STAMP_SNG_LNG-1); /* Get number of global attributes in file */ (void)nco_inq(out_id,(int *)NULL,(int *)NULL,&glb_att_nbr,(int *)NULL); for(idx=0;idx<glb_att_nbr;idx++){ (void)nco_inq_attname(out_id,NC_GLOBAL,idx,att_nm); if(!strcasecmp(att_nm,sng_history)) break; } /* end loop over att */ /* Fill in history string */ if(idx == glb_att_nbr){ /* Global attribute "[hH]istory" does not yet exist */ /* Add 3 for formatting characters */ history_new=(char *)nco_malloc((strlen(hst_sng)+strlen(time_stamp_sng)+3)*sizeof(char)); (void)sprintf(history_new,"%s: %s",time_stamp_sng,hst_sng); /* Set attribute name to default */ (void)strcpy(att_nm,sng_history); }else{ /* Global attribute "[hH]istory" currently exists */ /* NB: ncattinq(), unlike strlen(), counts terminating NUL for stored NC_CHAR arrays */ (void)nco_inq_att(out_id,NC_GLOBAL,att_nm,&att_typ,&att_sz); if(att_typ != NC_CHAR){ if(dbg_lvl_get() >= nco_dbg_std) (void)fprintf(stderr,"%s: WARNING the \"%s\" global attribute is type %s, not %s. Therefore current command line will not be appended to %s in output file.\n",prg_nm_get(),att_nm,nco_typ_sng(att_typ),nco_typ_sng(NC_CHAR),att_nm); return; } /* end if */ /* Allocate and NUL-terminate space for current history attribute If history attribute is of size zero then ensure strlen(history_crr) = 0 */ history_crr=(char *)nco_malloc((att_sz+1)*sizeof(char)); history_crr[att_sz]='\0'; if(att_sz > 0) (void)nco_get_att(out_id,NC_GLOBAL,att_nm,(void *)history_crr,NC_CHAR); /* Add 4 for formatting characters */ history_new=(char *)nco_malloc((strlen(history_crr)+strlen(hst_sng)+strlen(time_stamp_sng)+4)*sizeof(char)); (void)sprintf(history_new,"%s: %s\n%s",time_stamp_sng,hst_sng,history_crr); } /* endif history global attribute currently exists */ (void)nco_put_att(out_id,NC_GLOBAL,att_nm,NC_CHAR,(long)(strlen(history_new)+1UL),(void *)history_new); history_crr=(char *)nco_free(history_crr); history_new=(char *)nco_free(history_new); return; /* 20050109: fxm added return to void function to squelch erroneous gcc-3.4.2 warning */ } /* end nco_hst_att_cat() */
nm_id_sct * /* O [sct] Extraction list */ nco_cnv_cf_crd_add /* [fnc] Add coordinates defined by CF convention */ (const int nc_id, /* I netCDF file ID */ nm_id_sct *xtr_lst, /* I/O current extraction list (destroyed) */ int * const xtr_nbr) /* I/O number of variables in current extraction list */ { /* Purpose: Detect coordinates specified by CF convention and add them to extraction list http://www.cgd.ucar.edu/cms/eaton/cf-metadata/CF-1.0.html#grid_ex2 */ const char dlm_sng[]=" "; /* [sng] Delimiter string */ const char fnc_nm[]="nco_cnv_cf_crd_add()"; /* [sng] Function name */ char **crd_lst; /* [sng] 1D array of list elements */ char *att_val; char att_nm[NC_MAX_NAME]; int crd_id; int idx_att; int idx_crd; int idx_var; int idx_var2; int nbr_att; int nbr_crd; /* [nbr] Number of coordinates specified in "coordinates" attribute */ int rcd=NC_NOERR; /* [rcd] Return code */ int var_id; long att_sz; nc_type att_typ; /* ...for each variable in extraction list... */ for(idx_var=0;idx_var<*xtr_nbr;idx_var++){ /* Eschew indirection */ var_id=xtr_lst[idx_var].id; /* Find number of attributes */ (void)nco_inq_varnatts(nc_id,var_id,&nbr_att); for(idx_att=0;idx_att<nbr_att;idx_att++){ (void)nco_inq_attname(nc_id,var_id,idx_att,att_nm); /* Is attribute part of CF convention? */ if(!strcmp(att_nm,"coordinates")){ /* Yes, get list of specified attributes */ (void)nco_inq_att(nc_id,var_id,att_nm,&att_typ,&att_sz); if(att_typ != NC_CHAR){ (void)fprintf(stderr,"%s: WARNING the \"%s\" attribute for variable %s is type %s, not %s. This violates the CF convention for specifying additional attributes. Therefore %s will skip this attribute.\n",nco_prg_nm_get(),att_nm,xtr_lst[idx_var].nm,nco_typ_sng(att_typ),nco_typ_sng(NC_CHAR),fnc_nm); return xtr_lst; } /* end if */ att_val=(char *)nco_malloc((att_sz+1L)*sizeof(char)); if(att_sz > 0) (void)nco_get_att(nc_id,var_id,att_nm,(void *)att_val,NC_CHAR); /* NUL-terminate attribute */ att_val[att_sz]='\0'; /* Split list into separate coordinate names */ /* using nco_lst_prs_sgl_2D() and not nco_lst_prs_2D */ /* see TODO 944 */ crd_lst=nco_lst_prs_sgl_2D(att_val,dlm_sng,&nbr_crd); /* ...for each coordinate in "coordinates" attribute... */ for(idx_crd=0;idx_crd<nbr_crd;idx_crd++){ /* Verify "coordinate" exists in input file */ rcd=nco_inq_varid_flg(nc_id,crd_lst[idx_crd],&crd_id); /* NB: Do not check that dimension by this name exists CF files often use "coordinates" convention to identify two-dimensional (or greater) variables which serve as coordinates. In other words, we want to allow N-D variables to work as coordinates for the purpose of adding them to the extraction list only. */ if(rcd == NC_NOERR){ /* idx_var2 labels inner loop over variables */ /* Is "coordinate" already on extraction list? */ for(idx_var2=0;idx_var2<*xtr_nbr;idx_var2++){ if(crd_id == xtr_lst[idx_var2].id) break; } /* end loop over idx_var2 */ if(idx_var2 == *xtr_nbr){ /* Add coordinate to list */ xtr_lst=(nm_id_sct *)nco_realloc((void *)xtr_lst,(*xtr_nbr+1)*sizeof(nm_id_sct)); xtr_lst[*xtr_nbr].nm=(char *)strdup(crd_lst[idx_crd]); xtr_lst[*xtr_nbr].id=crd_id; (*xtr_nbr)++; /* NB: Changes size of current loop! */ /* Continue to next coordinate in loop */ continue; } /* end if coordinate was not already in list */ } /* end if named coordinate exists in input file */ } /* end loop over idx_crd */ /* Free allocated memory */ att_val=(char *)nco_free(att_val); crd_lst=nco_sng_lst_free(crd_lst,nbr_crd); } /* !coordinates */ } /* end loop over attributes */ } /* end loop over idx_var */ return xtr_lst; } /* end nco_cnv_cf_crd_add() */
void nco_att_cpy /* [fnc] Copy attributes from input netCDF file to output netCDF file */ (const int in_id, /* I [id] netCDF input-file ID */ const int out_id, /* I [id] netCDF output-file ID */ const int var_in_id, /* I [id] netCDF input-variable ID */ const int var_out_id, /* I [id] netCDF output-variable ID */ const nco_bool PCK_ATT_CPY) /* I [flg] Copy attributes "scale_factor", "add_offset" */ { /* Purpose: Copy attributes from input netCDF file to output netCDF file If var_in_id == NC_GLOBAL, then copy global attributes Otherwise copy only indicated variable's attributes When PCK_ATT_CPY is false, copy all attributes except "scale_factor", "add_offset" */ char att_nm[NC_MAX_NAME]; char var_nm[NC_MAX_NAME]; int idx; int nbr_att; int rcd; /* [enm] Return code */ if(var_in_id == NC_GLOBAL){ (void)nco_inq_natts(in_id,&nbr_att); }else{ (void)nco_inq_varnatts(in_id,var_in_id,&nbr_att); } /* end else */ /* Jump back to here if current attribute is treated specially */ for(idx=0;idx<nbr_att;idx++){ (void)nco_inq_attname(in_id,var_in_id,idx,att_nm); /* Look for same attribute in output variable in output file */ rcd=nco_inq_att_flg(out_id,var_out_id,att_nm,(nc_type *)NULL,(long *)NULL); /* If instructed not to copy packing attributes... */ if(!PCK_ATT_CPY) /* ...and attribute is "scale_factor" or "add_offset" ... */ if(!strcmp(att_nm,"scale_factor") || !strcmp(att_nm,"add_offset")) /* ...then skip remainder of loop, thereby skipping attribute copy... */ continue; /* Inform user when copy will overwrite an existing attribute */ if(dbg_lvl_get() >= nco_dbg_std){ if(rcd == NC_NOERR){ if(var_out_id == NC_GLOBAL){ (void)fprintf(stderr,"%s: INFO Overwriting global attribute %s\n",prg_nm_get(),att_nm); }else{ (void)nco_inq_varname(out_id,var_out_id,var_nm); (void)fprintf(stderr,"%s: INFO Overwriting attribute %s for output variable %s\n",prg_nm_get(),att_nm,var_nm); } /* end else */ } /* end if */ } /* end if dbg */ if(strcmp(att_nm,nco_mss_val_sng_get())){ /* Copy all attributes except _FillValue with fast library routine */ (void)nco_copy_att(in_id,var_in_id,att_nm,out_id,var_out_id); }else{ /* Convert "_FillValue" attribute to unpacked type then copy Impose NCO convention that _FillValue is same type as variable, whether variable is packed or not */ aed_sct aed; long att_sz; size_t att_lng_in; nc_type att_typ_in; nc_type att_typ_out; ptr_unn mss_tmp; (void)nco_inq_att(in_id,var_in_id,att_nm,&att_typ_in,&att_sz); if(att_sz != 1L){ (void)fprintf(stderr,"%s: ERROR input \"%s\" attribute has %li elements, but nco_att_cpy() only works for size of 1\n",prg_nm_get(),att_nm,att_sz); nco_exit(EXIT_FAILURE); } /* end if */ /* Convert "_FillValue" to unpacked type before copying */ aed.att_nm=att_nm; /* Name of attribute */ if(var_out_id == NC_GLOBAL){ aed.var_nm=NULL; }else{ (void)nco_inq_varname(out_id,var_out_id,var_nm); aed.var_nm=var_nm; /* Name of variable, or NULL for global attribute */ } /* end if */ aed.id=out_id; /* Variable ID or NC_GLOBAL ( = -1) for global attribute */ aed.sz=att_sz; /* Number of elements in attribute */ /* Do not convert global attributes or PCK_ATT_CPY */ if(PCK_ATT_CPY || var_out_id==NC_GLOBAL) att_typ_out=att_typ_in; else (void)nco_inq_vartype(out_id,var_out_id,&att_typ_out); if(att_typ_out==att_typ_in){ aed.type=att_typ_out; /* Type of attribute */ aed.val.vp=(void *)nco_malloc(nco_typ_lng(aed.type)); /* Pointer to attribute value */ (void)nco_get_att(in_id,var_in_id,att_nm,aed.val.vp,att_typ_out); }else{ /* att_typ_out!=att_typ_in */ /* Convert type */ aed.type=att_typ_out; /* Type of attribute */ aed.val.vp=(void *)nco_malloc(nco_typ_lng(aed.type)); /* Pointer to attribute value */ att_lng_in=att_sz*nco_typ_lng(att_typ_in); mss_tmp.vp=(void *)nco_malloc(att_lng_in); (void)nco_get_att(in_id,var_in_id,att_nm,mss_tmp.vp,att_typ_in); (void)nco_val_cnf_typ(att_typ_in,mss_tmp,att_typ_out,aed.val); mss_tmp.vp=nco_free(mss_tmp.vp); } /* att_typ_out!=att_typ_in */ /* Overwrite mode causes problems with netCDF4 and "_FillValue" Use create mode instead */ aed.mode=aed_create; (void)nco_aed_prc(out_id,var_out_id,aed); /* Release temporary memory */ aed.val.vp=nco_free(aed.val.vp); } /* endif copying _FillValue */ } /* end loop over attributes */ } /* end nco_att_cpy() */