Example #1
0
/* Create a group. It's ncid is returned in the new_ncid pointer. */
int
NC4_def_grp(int parent_ncid, const char *name, int *new_ncid)
{
   NC_GRP_INFO_T *grp, *g;
   NC_HDF5_FILE_INFO_T *h5;
   char norm_name[NC_MAX_NAME + 1];
   int retval;

   LOG((2, "%s: parent_ncid 0x%x name %s", __func__, parent_ncid, name));

   /* Find info for this file and group, and set pointer to each. */
   if ((retval = nc4_find_grp_h5(parent_ncid, &grp, &h5)))
      return retval;
   if (!h5)
      return NC_ENOTNC4;

   /* Check and normalize the name. */
   if ((retval = nc4_check_name(name, norm_name)))
      return retval;

   /* Check that this name is not in use as a var, grp, or type. */
   if ((retval = nc4_check_dup_name(grp, norm_name)))
      return retval;

   /* No groups in netcdf-3! */
   if (h5->cmode & NC_CLASSIC_MODEL)
      return NC_ESTRICTNC3;

   /* If it's not in define mode, switch to define mode. */
   if (!(h5->flags & NC_INDEF))
      if ((retval = NC4_redef(parent_ncid)))
	 return retval;

   /* Update internal lists to reflect new group. The actual HDF5
    * group creation will be done when metadata is written by a
    * sync. */
   if ((retval = nc4_grp_list_add(&(grp->children), h5->next_nc_grpid, 
				  grp, grp->nc4_info->controller, norm_name, &g)))
      return retval;
   if (new_ncid)
      *new_ncid = grp->nc4_info->controller->ext_ncid | h5->next_nc_grpid;
   h5->next_nc_grpid++;
   
   return NC_NOERR;
}
Example #2
0
/* Rename a group. */
int
NC4_rename_grp(int grpid, const char *name)
{
   NC_GRP_INFO_T *grp, *g;
   NC_HDF5_FILE_INFO_T *h5;
   char norm_name[NC_MAX_NAME + 1];
   int retval;

   LOG((2, "nc_rename_grp: grpid 0x%x name %s", grpid, name));

   /* Find info for this file and group, and set pointer to each. */
   if ((retval = nc4_find_grp_h5(grpid, &grp, &h5)))
      return retval;
   if (!h5)
      return NC_ENOTNC4;

   if (h5->no_write)
      return NC_EPERM; /* attempt to write to a read-only file */

   /* Do not allow renaming the root group */
   if(grp->parent == NULL)
	return NC_EBADGRPID;

   /* Check and normalize the name. */
   if ((retval = nc4_check_name(name, norm_name)))
      return retval;

   /* Check that this name is not in use as a var, grp, or type. */
   if ((retval = nc4_check_dup_name(grp, norm_name)))
      return retval;

   /* If it's not in define mode, switch to define mode. */
   if (!(h5->flags & NC_INDEF))
      if ((retval = NC4_redef(grpid)))
	 return retval;

   /* Rename the group, if it exists in the file */
   if (grp->hdf_grpid)
   {
      /* Close the group */
      if (H5Gclose(grp->hdf_grpid) < 0)
         return NC_EHDFERR;
      grp->hdf_grpid = 0;

      /* Attempt to rename & re-open the group, if the parent group is open */
      if (grp->parent->hdf_grpid)
      {
         /* Rename the group */
         if (H5Gmove(grp->parent->hdf_grpid, grp->name, name) < 0)
            return NC_EHDFERR;

         /* Reopen the group, with the new name */
         if ((grp->hdf_grpid = H5Gopen2(grp->parent->hdf_grpid, name, H5P_DEFAULT)) < 0)
            return NC_EHDFERR;
      }
   }

   /* Give the group its new name in metadata. UTF8 normalization
    * has been done. */
   free(grp->name);
   if (!(grp->name = malloc((strlen(norm_name) + 1) * sizeof(char))))
      return NC_ENOMEM;
   strcpy(grp->name, norm_name);

   return NC_NOERR;
}
Example #3
0
/* Dimensions are defined in attributes attached to the appropriate
   group in the data file. */
int
NC4_def_dim(int ncid, const char *name, size_t len, int *idp)
{
    NC *nc;
    NC_GRP_INFO_T *grp;
    NC_HDF5_FILE_INFO_T *h5;
    NC_DIM_INFO_T *dim;
    char norm_name[NC_MAX_NAME + 1];
    int retval = NC_NOERR;

    LOG((2, "nc_def_dim: ncid 0x%x name %s len %d", ncid, name,
         (int)len));

    /* Find our global metadata structure. */
    if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
        return retval;

    assert(h5 && nc /*& grp*/);

    /* If the file is read-only, return an error. */
    if (h5->no_write)
        return NC_EPERM;

#ifdef USE_PNETCDF
    /* Take care of files created/opened with parallel-netcdf library. */
    if (h5->pnetcdf_file)
        return ncmpi_def_dim(nc->int_ncid, name, len, idp);
#endif /* USE_PNETCDF */

    /* Check some stuff if strict nc3 rules are in effect. */
    if (h5->cmode & NC_CLASSIC_MODEL)
    {
        /* Only one limited dimenson for strict nc3. */
        if (len == NC_UNLIMITED)
            for (dim = grp->dim; dim; dim = dim->next)
                if (dim->unlimited)
                    return NC_EUNLIMIT;

        /* Must be in define mode for stict nc3. */
        if (!(h5->flags & NC_INDEF))
            return NC_ENOTINDEFINE;
    }

    /* If it's not in define mode, enter define mode. */
    if (!(h5->flags & NC_INDEF))
        if ((retval = NC4_redef(ncid)))
            return retval;

    /* Make sure this is a valid netcdf name. */
    if ((retval = nc4_check_name(name, norm_name)))
        return retval;

    /* For classic model: dim length has to fit in a 32-bit unsigned
     * int, as permitted for 64-bit offset format. */
    if (h5->cmode & NC_CLASSIC_MODEL)
        if(len > X_UINT_MAX) /* Backward compat */
            return NC_EDIMSIZE;

    /* Make sure the name is not already in use. */
    for (dim = grp->dim; dim; dim = dim->next)
        if (!strncmp(dim->name, norm_name, NC_MAX_NAME))
            return NC_ENAMEINUSE;

    /* Add a dimension to the list. The ID must come from the file
     * information, since dimids are visible in more than one group. */
    nc4_dim_list_add(&grp->dim);
    grp->dim->dimid = grp->nc4_info->next_dimid++;

    /* Initialize the metadata for this dimension. */
    if (!(grp->dim->name = malloc((strlen(norm_name) + 1) * sizeof(char))))
        return NC_ENOMEM;
    strcpy(grp->dim->name, norm_name);
    grp->dim->len = len;
    grp->dim->dirty++;
    if (len == NC_UNLIMITED)
        grp->dim->unlimited++;

    /* Pass back the dimid. */
    if (idp)
        *idp = grp->dim->dimid;

    return retval;
}
Example #4
0
/**
 * @internal
 * Write an attribute to a netCDF-4/HDF5 file, converting
 * data type if necessary.
 *
 * @param ncid File and group ID.
 * @param varid Variable ID.
 * @param name Name of attribute.
 * @param file_type Type of the attribute data in file.
 * @param len Number of elements in attribute array.
 * @param data Attribute data.
 * @param mem_type Type of data in memory.
 * @param force write even if the attribute is special
 *
 * @return ::NC_NOERR No error.
 * @return ::NC_EINVAL Invalid parameters.
 * @return ::NC_EBADID Bad ncid.
 * @return ::NC_ENOTVAR Variable not found.
 * @return ::NC_EBADNAME Name contains illegal characters.
 * @return ::NC_ENAMEINUSE Name already in use.
 * @author Ed Hartnett, Dennis Heimbigner
 */
int
nc4_put_att(NC_GRP_INFO_T* grp, int varid, const char *name, nc_type file_type,
            size_t len, const void *data, nc_type mem_type, int force)
{
   NC* nc;
   NC_FILE_INFO_T *h5;
   NC_VAR_INFO_T *var = NULL;
   NCindex* attlist = NULL;
   NC_ATT_INFO_T* att;
   char norm_name[NC_MAX_NAME + 1];
   nc_bool_t new_att = NC_FALSE;
   int retval = NC_NOERR, range_error = 0;
   size_t type_size;
   int i;
   int ret;
   int ncid;

   h5 = grp->nc4_info;
   nc = h5->controller;
   assert(nc && grp && h5);

   ncid = nc->ext_ncid | grp->hdr.id;

   /* Find att, if it exists. (Must check varid first or nc_test will
    * break.) */
   if ((ret = getattlist(grp, varid, &var, &attlist)))
      return ret;

   /* The length needs to be positive (cast needed for braindead
      systems with signed size_t). */
   if((unsigned long) len > X_INT_MAX)
      return NC_EINVAL;

   /* Check name before LOG statement. */
   if (!name || strlen(name) > NC_MAX_NAME)
      return NC_EBADNAME;

   LOG((1, "%s: ncid 0x%x varid %d name %s file_type %d mem_type %d len %d",
        __func__,ncid, varid, name, file_type, mem_type, len));

   /* If len is not zero, then there must be some data. */
   if (len && !data)
      return NC_EINVAL;

   /* If the file is read-only, return an error. */
   if (h5->no_write)
      return NC_EPERM;

   /* Check and normalize the name. */
   if ((retval = nc4_check_name(name, norm_name)))
      return retval;

   /* Check that a reserved att name is not being used improperly */
   const NC_reservedatt* ra = NC_findreserved(name);
   if(ra != NULL && !force) {
      /* case 1: grp=root, varid==NC_GLOBAL, flags & READONLYFLAG */
      if (nc->ext_ncid == ncid && varid == NC_GLOBAL && grp->parent == NULL
          && (ra->flags & READONLYFLAG))
         return NC_ENAMEINUSE;
      /* case 2: grp=NA, varid!=NC_GLOBAL, flags & DIMSCALEFLAG */
      if (varid != NC_GLOBAL && (ra->flags & DIMSCALEFLAG))
         return NC_ENAMEINUSE;
   }

   /* See if there is already an attribute with this name. */
   att = (NC_ATT_INFO_T*)ncindexlookup(attlist,norm_name);

   LOG((1, "%s: ncid 0x%x varid %d name %s file_type %d mem_type %d len %d",
        __func__, ncid, varid, name, file_type, mem_type, len));

   if (!att)
   {
      /* If this is a new att, require define mode. */
      if (!(h5->flags & NC_INDEF))
      {
         if (h5->cmode & NC_CLASSIC_MODEL)
            return NC_ENOTINDEFINE;
         if ((retval = NC4_redef(ncid)))
            BAIL(retval);
      }
      new_att = NC_TRUE;
   }
   else
   {
      /* For an existing att, if we're not in define mode, the len
         must not be greater than the existing len for classic model. */
      if (!(h5->flags & NC_INDEF) &&
          len * nc4typelen(file_type) > (size_t)att->len * nc4typelen(att->nc_typeid))
      {
         if (h5->cmode & NC_CLASSIC_MODEL)
            return NC_ENOTINDEFINE;
         if ((retval = NC4_redef(ncid)))
            BAIL(retval);
      }
   }

   /* We must have two valid types to continue. */
   if (file_type == NC_NAT || mem_type == NC_NAT)
      return NC_EBADTYPE;

   /* Get information about this type. */
   if ((retval = nc4_get_typelen_mem(h5, file_type, &type_size)))
      return retval;

   /* No character conversions are allowed. */
   if (file_type != mem_type &&
       (file_type == NC_CHAR || mem_type == NC_CHAR ||
        file_type == NC_STRING || mem_type == NC_STRING))
      return NC_ECHAR;

   /* For classic mode file, only allow atts with classic types to be
    * created. */
   if (h5->cmode & NC_CLASSIC_MODEL && file_type > NC_DOUBLE)
      return NC_ESTRICTNC3;

   /* Add to the end of the attribute list, if this att doesn't
      already exist. */
   if (new_att)
   {
      LOG((3, "adding attribute %s to the list...", norm_name));
      if ((ret = nc4_att_list_add(attlist, norm_name, &att)))
         BAIL(ret);

      /* Allocate storage for the HDF5 specific att info. */
      if (!(att->format_att_info = calloc(1, sizeof(NC_HDF5_ATT_INFO_T))))
         BAIL(NC_ENOMEM);
   }

   /* Now fill in the metadata. */
   att->dirty = NC_TRUE;
   att->nc_typeid = file_type;

   /* If this att has vlen or string data, release it before we lose the length value. */
   if (att->stdata)
   {
      for (i = 0; i < att->len; i++)
         if(att->stdata[i])
            free(att->stdata[i]);
      free(att->stdata);
      att->stdata = NULL;
   }
   if (att->vldata)
   {
      for (i = 0; i < att->len; i++)
         nc_free_vlen(&att->vldata[i]); /* FIX: see warning of nc_free_vlen */
      free(att->vldata);
      att->vldata = NULL;
   }

   att->len = len;

   /* If this is the _FillValue attribute, then we will also have to
    * copy the value to the fill_vlue pointer of the NC_VAR_INFO_T
    * struct for this var. (But ignore a global _FillValue
    * attribute). */
   if (!strcmp(att->hdr.name, _FillValue) && varid != NC_GLOBAL)
   {
      int size;

      /* Fill value must be same type and have exactly one value */
      if (att->nc_typeid != var->type_info->hdr.id)
         return NC_EBADTYPE;
      if (att->len != 1)
         return NC_EINVAL;

      /* If we already wrote to the dataset, then return an error. */
      if (var->written_to)
         return NC_ELATEFILL;

      /* Get the length of the veriable data type. */
      if ((retval = nc4_get_typelen_mem(grp->nc4_info, var->type_info->hdr.id,
                                        &type_size)))
         return retval;

      /* Already set a fill value? Now I'll have to free the old
       * one. Make up your damn mind, would you? */
      if (var->fill_value)
      {
         if (var->type_info->nc_type_class == NC_VLEN)
         {
            if ((retval = nc_free_vlen(var->fill_value)))
               return retval;
         }
         else if (var->type_info->nc_type_class == NC_STRING)
         {
            if (*(char **)var->fill_value)
               free(*(char **)var->fill_value);
         }
         free(var->fill_value);
      }

      /* Determine the size of the fill value in bytes. */
      if (var->type_info->nc_type_class == NC_VLEN)
         size = sizeof(hvl_t);
      else if (var->type_info->nc_type_class == NC_STRING)
         size = sizeof(char *);
      else
         size = type_size;

      /* Allocate space for the fill value. */
      if (!(var->fill_value = calloc(1, size)))
         return NC_ENOMEM;

      /* Copy the fill_value. */
      LOG((4, "Copying fill value into metadata for variable %s", var->hdr.name));
      if (var->type_info->nc_type_class == NC_VLEN)
      {
         nc_vlen_t *in_vlen = (nc_vlen_t *)data, *fv_vlen = (nc_vlen_t *)(var->fill_value);
         NC_TYPE_INFO_T* basetype;
	 size_t basetypesize = 0;

	 /* get the basetype and its size */
	 basetype = var->type_info;
         if ((retval = nc4_get_typelen_mem(grp->nc4_info, basetype->hdr.id, &basetypesize)))
             return retval;
	 /* shallow clone the content of the vlen; shallow because it has only a temporary existence */
         fv_vlen->len = in_vlen->len;
         if (!(fv_vlen->p = malloc(basetypesize * in_vlen->len)))
            return NC_ENOMEM;
         memcpy(fv_vlen->p, in_vlen->p, in_vlen->len * basetypesize);
      }
      else if (var->type_info->nc_type_class == NC_STRING)
      {
         if (*(char **)data)
         {
            if (!(*(char **)(var->fill_value) = malloc(strlen(*(char **)data) + 1)))
               return NC_ENOMEM;
            strcpy(*(char **)var->fill_value, *(char **)data);
         }
         else
            *(char **)var->fill_value = NULL;
      }
      else
         memcpy(var->fill_value, data, type_size);

      /* Indicate that the fill value was changed, if the variable has already
       * been created in the file, so the dataset gets deleted and re-created. */
      if (var->created)
         var->fill_val_changed = NC_TRUE;
   }

   /* Copy the attribute data, if there is any. VLENs and string
    * arrays have to be handled specially. */
   if (att->len)
   {
      nc_type type_class;    /* Class of attribute's type */

      /* Get class for this type. */
      if ((retval = nc4_get_typeclass(h5, file_type, &type_class)))
         return retval;

      assert(data);
      if (type_class == NC_VLEN)
      {
         const hvl_t *vldata1;
         NC_TYPE_INFO_T *vltype;
         size_t base_typelen;

         /* Get the type object for the attribute's type */
         if ((retval = nc4_find_type(h5, file_type, &vltype)))
            BAIL(retval);

         /* Retrieve the size of the base type */
         if ((retval = nc4_get_typelen_mem(h5, vltype->u.v.base_nc_typeid, &base_typelen)))
            BAIL(retval);

         vldata1 = data;
         if (!(att->vldata = (nc_vlen_t*)malloc(att->len * sizeof(hvl_t))))
            BAIL(NC_ENOMEM);
         for (i = 0; i < att->len; i++)
         {
            att->vldata[i].len = vldata1[i].len;
	    /* Warning, this only works for cases described for nc_free_vlen() */
            if (!(att->vldata[i].p = malloc(base_typelen * att->vldata[i].len)))
               BAIL(NC_ENOMEM);
            memcpy(att->vldata[i].p, vldata1[i].p, base_typelen * att->vldata[i].len);
         }
      }
      else if (type_class == NC_STRING)
      {
         LOG((4, "copying array of NC_STRING"));
         if (!(att->stdata = malloc(sizeof(char *) * att->len))) {
            BAIL(NC_ENOMEM);
         }

         /* If we are overwriting an existing attribute,
            specifically an NC_CHAR, we need to clean up
            the pre-existing att->data. */
         if (!new_att && att->data) {
            free(att->data);
            att->data = NULL;
         }

         for (i = 0; i < att->len; i++)
         {
            if(NULL != ((char **)data)[i]) {
               LOG((5, "copying string %d of size %d", i, strlen(((char **)data)[i]) + 1));
               if (!(att->stdata[i] = strdup(((char **)data)[i])))
                  BAIL(NC_ENOMEM);
            }
            else
               att->stdata[i] = ((char **)data)[i];
         }
      }
      else
      {
         /* [Re]allocate memory for the attribute data */
         if (!new_att)
            free (att->data);
         if (!(att->data = malloc(att->len * type_size)))
            BAIL(NC_ENOMEM);

         /* Just copy the data, for non-atomic types */
         if (type_class == NC_OPAQUE || type_class == NC_COMPOUND || type_class == NC_ENUM)
            memcpy(att->data, data, len * type_size);
         else
         {
            /* Data types are like religions, in that one can convert.  */
            if ((retval = nc4_convert_type(data, att->data, mem_type, file_type,
                                           len, &range_error, NULL,
                                           (h5->cmode & NC_CLASSIC_MODEL))))
               BAIL(retval);
         }
      }
   }
   att->dirty = NC_TRUE;
   att->created = NC_FALSE;

   /* Mark attributes on variable dirty, so they get written */
   if(var)
      var->attr_dirty = NC_TRUE;

exit:
   /* If there was an error return it, otherwise return any potential
      range error value. If none, return NC_NOERR as usual.*/
   if (retval)
      return retval;
   if (range_error)
      return NC_ERANGE;
   return NC_NOERR;
}
Example #5
0
/**
 * @internal Delete an att. Rub it out. Push the button on
 * it. Liquidate it. Bump it off. Take it for a one-way
 * ride. Terminate it.
 *
 * @param ncid File and group ID.
 * @param varid Variable ID.
 * @param name Name of attribute to delete.
 *
 * @return ::NC_NOERR No error.
 * @return ::NC_EBADID Bad ncid.
 * @return ::NC_ENOTATT Attribute not found.
 * @return ::NC_EINVAL No name provided.
 * @return ::NC_EPERM File is read only.
 * @return ::NC_ENOTINDEFINE Classic model not in define mode.
 * @return ::NC_EINTERNAL Could not rebuild list.
 * @author Ed Hartnett, Dennis Heimbigner
 */
int
NC4_del_att(int ncid, int varid, const char *name)
{
   NC_GRP_INFO_T *grp;
   NC_VAR_INFO_T *var;
   NC_FILE_INFO_T *h5;
   NC_ATT_INFO_T *att;
   NCindex* attlist = NULL;
   hid_t locid = 0;
   int i;
   size_t deletedid;
   int retval;

   /* Name must be provided. */
   if (!name)
      return NC_EINVAL;

   LOG((2, "nc_del_att: ncid 0x%x varid %d name %s", ncid, varid, name));

   /* Find info for this file, group, and h5 info. */
   if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
      return retval;
   assert(h5 && grp);

   /* If the file is read-only, return an error. */
   if (h5->no_write)
      return NC_EPERM;

   /* If file is not in define mode, return error for classic model
    * files, otherwise switch to define mode. */
   if (!(h5->flags & NC_INDEF))
   {
      if (h5->cmode & NC_CLASSIC_MODEL)
         return NC_ENOTINDEFINE;
      if ((retval = NC4_redef(ncid)))
         return retval;
   }

   /* Get either the global or a variable attribute list. */
   if ((retval = getattlist(grp, varid, &var, &attlist)))
      return retval;

   /* Determine the location id in the HDF5 file. */
   if (varid == NC_GLOBAL)
      locid = ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid;
   else if (var->created)
      locid = var->hdf_datasetid;

   /* Now find the attribute by name. */
   if (!(att = (NC_ATT_INFO_T*)ncindexlookup(attlist, name)))
      return NC_ENOTATT;

   /* Delete it from the HDF5 file, if it's been created. */
   if (att->created)
   {
      assert(locid);
      if (H5Adelete(locid, att->hdr.name) < 0)
         return NC_EATTMETA;
   }

   deletedid = att->hdr.id;

   /* Remove this attribute in this list */
   if ((retval = nc4_att_list_del(attlist, att)))
      return retval;

   /* Renumber all attributes with higher indices. */
   for (i = 0; i < ncindexsize(attlist); i++)
   {
      NC_ATT_INFO_T *a;
      if (!(a = (NC_ATT_INFO_T *)ncindexith(attlist, i)))
         continue;
      if (a->hdr.id > deletedid)
         a->hdr.id--;
   }

   /* Rebuild the index. */
   if (!ncindexrebuild(attlist))
      return NC_EINTERNAL;

   return NC_NOERR;
}
Example #6
0
/* Delete an att. Rub it out. Push the button on it. Liquidate
   it. Bump it off. Take it for a one-way ride. Terminate it. Drop the
   bomb on it. You get the idea.
   Ed Hartnett, 10/1/3
*/
int
NC4_del_att(int ncid, int varid, const char *name)
{
   NC *nc;
   NC_GRP_INFO_T *grp;
   NC_HDF5_FILE_INFO_T *h5;
   NC_ATT_INFO_T *att, *natt;
   NC_VAR_INFO_T *var;
   NC_ATT_INFO_T **attlist = NULL;
   hid_t locid = 0, datasetid = 0;
   int retval = NC_NOERR;

   if (!name)
      return NC_EINVAL;

   LOG((2, "nc_del_att: ncid 0x%x varid %d name %s",
	ncid, varid, name));

   /* Find metadata for this file. */
   if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
      return retval;

   assert(h5 && grp);

   /* If the file is read-only, return an error. */
   if (h5->no_write)
      return NC_EPERM;

#if 0 /*def USE_PNETCDF*/
   /* Take care of files created/opened with parallel-netcdf library. */
   if (h5->pnetcdf_file)
      return ncmpi_del_att(nc->int_ncid, varid, name);
#endif /* USE_PNETCDF */

   /* If it's not in define mode, forget it. */
   if (!(h5->flags & NC_INDEF))
   {
      if (h5->cmode & NC_CLASSIC_MODEL)
	 return NC_ENOTINDEFINE;
      if ((retval = NC4_redef(ncid)))
	 BAIL(retval);
   }

   /* Get either the global or a variable attribute list. Also figure
      out the HDF5 location it's attached to. */
   if (varid == NC_GLOBAL)
   {
      attlist = &grp->att;
      locid = grp->hdf_grpid;
   }
   else
   {
      for(var = grp->var; var; var = var->l.next)
      {
	 if (var->varid == varid)
	 {
	    attlist = &var->att;
	    break;
	 }
      }
      if (!var)
	 return NC_ENOTVAR;
      if (var->created)
	 locid = var->hdf_datasetid;
   }

   /* Now find the attribute by name or number. */
   for (att = *attlist; att; att = att->l.next)
      if (!strcmp(att->name, name))
	 break;

   /* If att is NULL, we couldn't find the attribute. */
   if (!att)
      BAIL_QUIET(NC_ENOTATT);

   /* Delete it from the HDF5 file, if it's been created. */
   if (att->created)
   {
      assert(locid);

      if(H5Adelete(locid, att->name) < 0)
	 BAIL(NC_EATTMETA);
   }

   /* Renumber all following attributes. */
   for (natt = att->l.next; natt; natt = natt->l.next)
      natt->attnum--;

   /* Delete this attribute from this list. */
   if ((retval = nc4_att_list_del(attlist, att)))
      BAIL(retval);

 exit:
   if (datasetid > 0) H5Dclose(datasetid);
   return retval;
}
Example #7
0
/* Put attribute metadata into our global metadata. */
static int
nc4_put_att(int ncid, NC *nc, int varid, const char *name,
	    nc_type file_type, nc_type mem_type, size_t len, int is_long,
	    const void *data)
{
   NC_GRP_INFO_T *grp;
   NC_HDF5_FILE_INFO_T *h5;
   NC_VAR_INFO_T *var = NULL;
   NC_ATT_INFO_T *att, **attlist = NULL;
   char norm_name[NC_MAX_NAME + 1];
   nc_bool_t new_att = NC_FALSE;
   int retval = NC_NOERR, range_error = 0;
   size_t type_size;
   int i;
   int res;

   if (!name)
      return NC_EBADNAME;
   assert(nc && NC4_DATA(nc));

   LOG((1, "nc4_put_att: ncid 0x%x varid %d name %s "
	"file_type %d mem_type %d len %d", ncid, varid,
	name, file_type, mem_type, len));

   /* If len is not zero, then there must be some data. */
   if (len && !data)
      return NC_EINVAL;

   /* Find info for this file and group, and set pointer to each. */
   h5 = NC4_DATA(nc);
   if (!(grp = nc4_rec_find_grp(h5->root_grp, (ncid & GRP_ID_MASK))))
      return NC_EBADGRPID;

   /* If the file is read-only, return an error. */
   if (h5->no_write)
     return NC_EPERM;

   /* Check and normalize the name. */
   if ((retval = nc4_check_name(name, norm_name)))
      return retval;

   /* Find att, if it exists. */
   if (varid == NC_GLOBAL)
      attlist = &grp->att;
   else
   {
      for (var = grp->var; var; var = var->l.next)
	 if (var->varid == varid)
	 {
	    attlist = &var->att;
	    break;
	 }
      if (!var)
	 return NC_ENOTVAR;
   }
   for (att = *attlist; att; att = att->l.next)
      if (!strcmp(att->name, norm_name))
	 break;

   if (!att)
   {
      /* If this is a new att, require define mode. */
      if (!(h5->flags & NC_INDEF))
      {
	 if (h5->cmode & NC_CLASSIC_MODEL)
	    return NC_EINDEFINE;
	 if ((retval = NC4_redef(ncid)))
	    BAIL(retval);
      }
      new_att = NC_TRUE;
   }
   else
   {
      /* For an existing att, if we're not in define mode, the len
	 must not be greater than the existing len for classic model. */
      if (!(h5->flags & NC_INDEF) &&
	  len * nc4typelen(file_type) > (size_t)att->len * nc4typelen(att->nc_typeid))
      {
	 if (h5->cmode & NC_CLASSIC_MODEL)
	    return NC_EINDEFINE;
	 if ((retval = NC4_redef(ncid)))
	    BAIL(retval);
      }
   }

   /* We must have two valid types to continue. */
   if (file_type == NC_NAT || mem_type == NC_NAT)
      return NC_EBADTYPE;

   /* Get information about this type. */
   if ((retval = nc4_get_typelen_mem(h5, file_type, is_long, &type_size)))
      return retval;

   /* No character conversions are allowed. */
   if (file_type != mem_type &&
       (file_type == NC_CHAR || mem_type == NC_CHAR ||
	file_type == NC_STRING || mem_type == NC_STRING))
      return NC_ECHAR;

   /* For classic mode file, only allow atts with classic types to be
    * created. */
   if (h5->cmode & NC_CLASSIC_MODEL && file_type > NC_DOUBLE)
      return NC_ESTRICTNC3;

   /* Add to the end of the attribute list, if this att doesn't
      already exist. */
   if (new_att)
   {
      LOG((3, "adding attribute %s to the list...", norm_name));
      if ((res = nc4_att_list_add(attlist, &att)))
	 BAIL (res);
      if (!(att->name = strdup(norm_name)))
         return NC_ENOMEM;
   }

   /* Now fill in the metadata. */
   att->dirty = NC_TRUE;
   att->nc_typeid = file_type;

   /* If this att has vlen or string data, release it before we lose the length value. */
   if (att->stdata)
   {
      for (i = 0; i < att->len; i++)
         if(att->stdata[i])
	    free(att->stdata[i]);
      free(att->stdata);
      att->stdata = NULL;
   }
   if (att->vldata)
   {
      for (i = 0; i < att->len; i++)
	 nc_free_vlen(&att->vldata[i]);
      free(att->vldata);
      att->vldata = NULL;
   }

   att->len = len;
   if (att->l.prev)
      att->attnum = ((NC_ATT_INFO_T *)att->l.prev)->attnum + 1;
   else
      att->attnum = 0;

   /* If this is the _FillValue attribute, then we will also have to
    * copy the value to the fill_vlue pointer of the NC_VAR_INFO_T
    * struct for this var. (But ignore a global _FillValue
    * attribute). */
   if (!strcmp(att->name, _FillValue) && varid != NC_GLOBAL)
   {
      NC_ATT_INFO_T *varatt;
      int size;

      /* Fill value must be same type and have exactly one value */
      if (att->nc_typeid != var->type_info->nc_typeid)
	 return NC_EBADTYPE;
      if (att->len != 1)
	 return NC_EINVAL;

      /* If we already wrote to the dataset, then return an error. */
      if (var->written_to)
	 return NC_ELATEFILL;

      /* If fill value hasn't been set, allocate space. Of course,
       * vlens have to be different... */
      if ((retval = nc4_get_typelen_mem(grp->nc4_info, var->type_info->nc_typeid, 0,
					&type_size)))
	 return retval;

      /* Already set a fill value? Now I'll have to free the old
       * one. Make up your damn mind, would you? */
      if (var->fill_value)
      {
         if (var->type_info->nc_type_class == NC_VLEN)
         {
            if ((retval = nc_free_vlen(var->fill_value)))
               return retval;
         }
         else if (var->type_info->nc_type_class == NC_STRING)
         {
            if (*(char **)var->fill_value)
               free(*(char **)var->fill_value);
         }
	 free(var->fill_value);
      }

      /* Allocate space for the fill value. */
      if (var->type_info->nc_type_class == NC_VLEN)
	 size = sizeof(hvl_t);
      else if (var->type_info->nc_type_class == NC_STRING)
	 size = sizeof(char *);
      else
	 size = type_size;

      if (!(var->fill_value = calloc(1, size)))
	 return NC_ENOMEM;

      /* Copy the fill_value. */
      LOG((4, "Copying fill value into metadata for variable %s", var->name));
      if (var->type_info->nc_type_class == NC_VLEN)
      {
	 nc_vlen_t *in_vlen = (nc_vlen_t *)data, *fv_vlen = (nc_vlen_t *)(var->fill_value);

	 fv_vlen->len = in_vlen->len;
	 if (!(fv_vlen->p = malloc(size * in_vlen->len)))
	    return NC_ENOMEM;
	 memcpy(fv_vlen->p, in_vlen->p, in_vlen->len * size);
      }
      else if (var->type_info->nc_type_class == NC_STRING)
      {
         if(NULL != (*(char **)data))
         {
            if (!(*(char **)(var->fill_value) = malloc(strlen(*(char **)data) + 1)))
               return NC_ENOMEM;
            strcpy(*(char **)var->fill_value, *(char **)data);
         }
         else
            *(char **)var->fill_value = NULL;
      }
      else
	 memcpy(var->fill_value, data, type_size);
      
      /* Indicate that the fill value was changed, if the variable has already
       * been created in the file, so the dataset gets deleted and re-created. */
      if (var->created)
         var->fill_val_changed = NC_TRUE;
   }

   /* Copy the attribute data, if there is any. VLENs and string
    * arrays have to be handled specially. */
   if(att->len)
   {
      nc_type type_class;    /* Class of attribute's type */

      /* Get class for this type. */
      if ((retval = nc4_get_typeclass(h5, file_type, &type_class)))
         return retval;

      assert(data);
      if (type_class == NC_VLEN)
      {
         const hvl_t *vldata1;
	 NC_TYPE_INFO_T *type;
	 size_t base_typelen;

         /* Get the type object for the attribute's type */
	 if ((retval = nc4_find_type(h5, file_type, &type)))
	   BAIL(retval);

         /* Retrieve the size of the base type */
         if ((retval = nc4_get_typelen_mem(h5, type->u.v.base_nc_typeid, 0, &base_typelen)))
            BAIL(retval);

         vldata1 = data;
         if (!(att->vldata = (nc_vlen_t*)malloc(att->len * sizeof(hvl_t))))
            BAIL(NC_ENOMEM);
         for (i = 0; i < att->len; i++)
         {
            att->vldata[i].len = vldata1[i].len;
            if (!(att->vldata[i].p = malloc(base_typelen * att->vldata[i].len)))
               BAIL(NC_ENOMEM);
            memcpy(att->vldata[i].p, vldata1[i].p, base_typelen * att->vldata[i].len);
         }
      }
      else if (type_class == NC_STRING)
      {
         LOG((4, "copying array of NC_STRING"));
         if (!(att->stdata = malloc(sizeof(char *) * att->len)))
            BAIL(NC_ENOMEM);
         for (i = 0; i < att->len; i++)
         {
            if(NULL != ((char **)data)[i]) {
               LOG((5, "copying string %d of size %d", i, strlen(((char **)data)[i]) + 1));
               if (!(att->stdata[i] = strdup(((char **)data)[i])))
                  BAIL(NC_ENOMEM);
           }
           else
               att->stdata[i] = ((char **)data)[i];
         }
      }
      else
      {
         /* [Re]allocate memory for the attribute data */
         if (!new_att)
            free (att->data);
         if (!(att->data = malloc(att->len * type_size)))
            BAIL(NC_ENOMEM);

         /* Just copy the data, for non-atomic types */
         if (type_class == NC_OPAQUE || type_class == NC_COMPOUND || type_class == NC_ENUM)
            memcpy(att->data, data, len * type_size);
         else
         {
            /* Data types are like religions, in that one can convert.  */
            if ((retval = nc4_convert_type(data, att->data, mem_type, file_type,
                                           len, &range_error, NULL,
                                           (h5->cmode & NC_CLASSIC_MODEL), is_long, 0)))
               BAIL(retval);
         }
      }
   }
   att->dirty = NC_TRUE;
   att->created = NC_FALSE;

   /* Mark attributes on variable dirty, so they get written */
   if(var)
       var->attr_dirty = NC_TRUE;

 exit:
   /* If there was an error return it, otherwise return any potential
      range error value. If none, return NC_NOERR as usual.*/
   if (retval)
      return retval;
   if (range_error)
      return NC_ERANGE;
   return NC_NOERR;
}