Beispiel #1
0
ZIP_EXTERN int
zip_file_rename(struct zip *za, zip_uint64_t idx, const char *name, zip_flags_t flags)
{
    const char *old_name;
    int old_is_dir, new_is_dir;
    
    if (idx >= za->nentry || (name != NULL && strlen(name) > ZIP_UINT16_MAX)) {
	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	return -1;
    }

    if (ZIP_IS_RDONLY(za)) {
	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
	return -1;
    }

    if ((old_name=zip_get_name(za, idx, 0)) == NULL)
	return -1;
								    
    new_is_dir = (name != NULL && name[strlen(name)-1] == '/');
    old_is_dir = (old_name[strlen(old_name)-1] == '/');

    if (new_is_dir != old_is_dir) {
	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	return -1;
    }

    return _zip_set_name(za, idx, name, flags);
}
Beispiel #2
0
zip_set_archive_comment(struct zip *za, const char *comment, int len)
{
    char *tmpcom;

    if (len < 0 || len > MAXCOMLEN
	|| (len > 0 && comment == NULL)) {
	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	return -1;
    }

    if (ZIP_IS_RDONLY(za)) {
	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
	return -1;
    }

    if (len > 0) {
	if ((tmpcom=(char *)_zip_memdup(comment, len, &za->error)) == NULL)
	    return -1;
    }
    else
	tmpcom = NULL;

    free(za->ch_comment);
    za->ch_comment = tmpcom;
    za->ch_comment_len = len;
    
    return 0;
}
ZIP_EXTERN int
zip_file_extra_field_delete_by_id(struct zip *za, zip_uint64_t idx, zip_uint16_t ef_id, zip_uint16_t ef_idx, zip_flags_t flags)
{
    struct zip_dirent *de;

    if ((flags & ZIP_EF_BOTH) == 0) {
	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	return -1;
    }

    if (((flags & ZIP_EF_BOTH) == ZIP_EF_BOTH) && (ef_idx != ZIP_EXTRA_FIELD_ALL)) {
	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	return -1;
    }
    
    if (_zip_get_dirent(za, idx, 0, NULL) == NULL)
	return -1;

    if (ZIP_IS_RDONLY(za)) {
	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
	return -1;
    }
    
    if (_zip_file_extra_field_prepare_for_change(za, idx) < 0)
        return -1;
    
    de = za->entry[idx].changes;

    de->extra_fields = _zip_ef_delete_by_id(de->extra_fields, ef_id, ef_idx, flags);
    return 0;
}
ZIP_EXTERN int
zip_set_file_extra(struct zip *za, zip_uint64_t idx,
		   const char *extra, int len)
{
    char *tmpext;

    if (idx >= za->nentry
	|| len < 0 || len > MAXEXTLEN
	|| (len > 0 && extra == NULL)) {
	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	return -1;
    }

    if (ZIP_IS_RDONLY(za)) {
	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
	return -1;
    }

    if (len > 0) {
	if ((tmpext=(char *)_zip_memdup(extra, len, &za->error)) == NULL)
	    return -1;
    }
    else
	tmpext = NULL;

    if (za->entry[idx].changes.valid & ZIP_DIRENT_EXTRAFIELD)
	free(za->entry[idx].changes.extrafield);
    za->entry[idx].changes.extrafield = tmpext;
    za->entry[idx].changes.extrafield_len = len;
    za->entry[idx].changes.valid |= ZIP_DIRENT_EXTRAFIELD;

    return 0;
}
ZIP_EXTERN int
zip_set_archive_flag(struct zip *za, unsigned int flag, int value)
{
    unsigned int new_flags;
    
    if (value)
	new_flags = za->ch_flags | flag;
    else
	new_flags = za->ch_flags & ~flag;

    if (new_flags == za->ch_flags)
	return 0;

    if (ZIP_IS_RDONLY(za)) {
	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
	return -1;
    }

    if ((flag & ZIP_AFL_RDONLY) && value
	&& (za->ch_flags & ZIP_AFL_RDONLY) == 0) {
	if (_zip_changed(za, NULL)) {
	    _zip_error_set(&za->error, ZIP_ER_CHANGED, 0);
	    return -1;
	}
    }

    za->ch_flags = new_flags;

    return 0;
}
zip_int64_t
_zip_replace(struct zip *za, zip_uint64_t idx, const char *name,
	     struct zip_source *source)
{
    zip_uint64_t za_nentry_prev;
    if (ZIP_IS_RDONLY(za)) {
	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
	return -1;
    }

    za_nentry_prev = za->nentry;
    if (idx == ZIP_UINT64_MAX) {
	if (_zip_entry_new(za) == NULL)
	    return -1;
	
	idx = za->nentry - 1;
	za->entry[idx].changes.valid |= ZIP_DIRENT_COMP_METHOD;
	za->entry[idx].changes.comp_method = ZIP_CM_DEFLATE; /* XXX: default */
    }
    
    if (name && _zip_set_name(za, idx, name) != 0) {
	za->nentry = za_nentry_prev;
	return -1;
    }

    /* does not change any name related data, so we can do it here;
     * needed for a double add of the same file name */
    _zip_unchange_data(za->entry+idx);

    za->entry[idx].state = ((za->cdir == NULL || idx >= (zip_uint64_t)za->cdir->nentry)
			    ? ZIP_ST_ADDED : ZIP_ST_REPLACED);
    za->entry[idx].source = source;

    return idx;
}
zip_int64_t
_zip_file_replace(struct zip *za, zip_uint64_t idx, const char *name, struct zip_source *source, zip_flags_t flags)
{
    zip_uint64_t za_nentry_prev;
    
    if (ZIP_IS_RDONLY(za)) {
	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
	return -1;
    }

    za_nentry_prev = za->nentry;
    if (idx == ZIP_UINT64_MAX) {
	zip_int64_t i = -1;
	
	if (flags & ZIP_FL_OVERWRITE)
	    i = _zip_name_locate(za, name, flags, NULL);

	if (i == -1) {
	    /* create and use new entry, used by zip_add */
	    if ((i=_zip_add_entry(za)) < 0)
		return -1;
	}
	idx = (zip_uint64_t)i;
    }
    
    if (name && _zip_set_name(za, idx, name, flags) != 0) {
	if (za->nentry != za_nentry_prev) {
	    _zip_entry_finalize(za->entry+idx);
	    za->nentry = za_nentry_prev;
	}
	return -1;
    }

    /* does not change any name related data, so we can do it here;
     * needed for a double add of the same file name */
    _zip_unchange_data(za->entry+idx);

    if (za->entry[idx].orig != NULL && (za->entry[idx].changes == NULL || (za->entry[idx].changes->changed & ZIP_DIRENT_COMP_METHOD) == 0)) {
        if (za->entry[idx].changes == NULL) {
            if ((za->entry[idx].changes=_zip_dirent_clone(za->entry[idx].orig)) == NULL) {
                _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
                return -1;
            }
        }

        za->entry[idx].changes->comp_method = ZIP_CM_REPLACED_DEFAULT;
        za->entry[idx].changes->changed |= ZIP_DIRENT_COMP_METHOD;
    }
	
    za->entry[idx].source = source;

    return (zip_int64_t)idx;
}
ZIP_EXTERN int
zip_set_file_compression(struct zip *za, zip_uint64_t idx,
			 zip_int32_t method, zip_uint32_t flags)
{
    struct zip_entry *e;
	zip_int32_t old_method;

    if (idx >= za->nentry) {
	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	return -1;
    }

    if (ZIP_IS_RDONLY(za)) {
	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
	return -1;
    }

    if (method != ZIP_CM_DEFAULT && method != ZIP_CM_STORE && method != ZIP_CM_DEFLATE) {
	_zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
	return -1;
    }

    e = za->entry+idx;
    
    //zip_int32_t old_method = (e->orig == NULL ? ZIP_CM_DEFAULT : e->orig->comp_method);
    old_method = (e->orig == NULL ? ZIP_CM_DEFAULT : e->orig->comp_method);
    
    /* XXX: revisit this when flags are supported, since they may require a recompression */
    
    if (method == old_method) {
	if (e->changes) {
	    e->changes->changed &= ~ZIP_DIRENT_COMP_METHOD;
	    if (e->changes->changed == 0) {
		_zip_dirent_free(e->changes);
		e->changes = NULL;
	    }
	}
    }
    else {
        if (e->changes == NULL) {
            if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) {
                _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
                return -1;
            }
        }

        e->changes->comp_method = method;
        e->changes->changed |= ZIP_DIRENT_COMP_METHOD;
    }
    
    return 0;
}
Beispiel #9
0
ZIP_EXTERN zip_int64_t
zip_dir_add(struct zip *za, const char *name, zip_flags_t flags)
{
    size_t len;
    zip_int64_t idx;
    char *s;
    struct zip_source *source;

    if (ZIP_IS_RDONLY(za)) {
	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
	return -1;
    }

    if (name == NULL) {
	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	return -1;
    }

    s = NULL;
    len = strlen(name);

    if (name[len-1] != '/') {
	if ((s=(char *)malloc(len+2)) == NULL) {
	    _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
	    return -1;
	}
	strcpy(s, name);
	s[len] = '/';
	s[len+1] = '\0';
    }

    if ((source=zip_source_buffer(za, NULL, 0, 0)) == NULL) {
	free(s);
	return -1;
    }
	
    idx = _zip_file_replace(za, ZIP_UINT64_MAX, s ? s : name, source, flags);

    free(s);

    if (idx < 0)
	zip_source_free(source);
    else {
	if (zip_file_set_external_attributes(za, (zip_uint64_t)idx, 0, ZIP_OPSYS_DEFAULT, ZIP_EXT_ATTRIB_DEFAULT_DIR) < 0) {
	    zip_delete(za, (zip_uint64_t)idx);
	    return -1;
	}
    }

    return idx;
}
int
zip_file_set_external_attributes(struct zip *za, zip_uint64_t idx, zip_flags_t flags, zip_uint8_t opsys, zip_uint32_t attributes)
{
    struct zip_entry *e;
    int changed;
    zip_uint8_t unchanged_opsys;
    zip_uint32_t unchanged_attributes;

    if (_zip_get_dirent(za, idx, 0, NULL) == NULL)
	return -1;

    if (ZIP_IS_RDONLY(za)) {
	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
	return -1;
    }

    e = za->entry+idx;

    unchanged_opsys = e->orig ? e->orig->version_madeby>>8 : ZIP_OPSYS_DEFAULT;
    unchanged_attributes = e->orig ? e->orig->ext_attrib : ZIP_EXT_ATTRIB_DEFAULT;

    changed = (opsys != unchanged_opsys || attributes != unchanged_attributes);

    if (changed) {
        if (e->changes == NULL) {
            if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) {
                _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
                return -1;
            }
        }
        e->changes->version_madeby = (opsys << 8) | (e->changes->version_madeby & 0xff);
	e->changes->ext_attrib = attributes;
        e->changes->changed |= ZIP_DIRENT_ATTRIBUTES;
    }
    else if (e->changes) {
	e->changes->changed &= ~ZIP_DIRENT_ATTRIBUTES;
	if (e->changes->changed == 0) {
	    _zip_dirent_free(e->changes);
	    e->changes = NULL;
	}
	else {
	    e->changes->version_madeby = (unchanged_opsys << 8) | (e->changes->version_madeby & 0xff);
	    e->changes->ext_attrib = unchanged_attributes;
	}
    }

    return 0;
}
ZIP_EXTERN zip_int64_t
zip_dir_add(struct zip *za, const char *name, zip_flags_t flags)
{
    size_t len;
    zip_int64_t ret;
    char *s;
    struct zip_source *source;

    if (ZIP_IS_RDONLY(za)) {
	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
	return -1;
    }

    if (name == NULL) {
	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	return -1;
    }

    s = NULL;
    len = strlen(name);

    if (name[len-1] != '/') {
	if ((s=(char *)malloc(len+2)) == NULL) {
	    _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
	    return -1;
	}
	strcpy(s, name);
	s[len] = '/';
	s[len+1] = '\0';
    }

    if ((source=zip_source_buffer(za, NULL, 0, 0)) == NULL) {
	free(s);
	return -1;
    }
	
    ret = _zip_file_replace(za, ZIP_UINT64_MAX, s ? s : name, source, flags);

    free(s);
    if (ret < 0)
	zip_source_free(source);

    return ret;
}
ZIP_EXTERN int
zip_set_archive_comment(struct zip *za, const char *comment, zip_uint16_t len)
{
    struct zip_string *cstr;

    if (ZIP_IS_RDONLY(za)) {
	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
	return -1;
    }

    if (len < 0 || len > MAXCOMLEN
	|| (len > 0 && comment == NULL)) {
	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	return -1;
    }

    if (len > 0) {
	if ((cstr=_zip_string_new((const zip_uint8_t *)comment, len, ZIP_FL_ENC_GUESS, &za->error)) == NULL)
	    return -1;

	if (_zip_guess_encoding(cstr, ZIP_ENCODING_UNKNOWN) == ZIP_ENCODING_CP437) {
	    _zip_string_free(cstr);
	    _zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	    return -1;
	}
    }
    else
	cstr = NULL;

    _zip_string_free(za->comment_changes);
    za->comment_changes = NULL;

    if (((za->comment_orig && _zip_string_equal(za->comment_orig, cstr))
	 || (za->comment_orig == NULL && cstr == NULL))) {
	_zip_string_free(cstr);
	za->comment_changed = 0;
    }
    else {
	za->comment_changes = cstr;
	za->comment_changed = 1;
    }
    
    return 0;
}
Beispiel #13
0
zip_delete(struct zip *za, zip_uint64_t idx)
{
    if (idx >= za->nentry) {
	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	return -1;
    }

    if (ZIP_IS_RDONLY(za)) {
	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
	return -1;
    }

    /* allow duplicate file names, because the file will
     * be removed directly afterwards */
    if (_zip_unchange(za, idx, 1) != 0)
	return -1;

    za->entry[idx].state = ZIP_ST_DELETED;

    return 0;
}
Beispiel #14
0
ZIP_EXTERN int zip_file_set_mtime(zip_t *za, zip_uint64_t idx, time_t mtime, zip_flags_t flags)
{
    zip_entry_t *e;
    int changed;

    if (_zip_get_dirent(za, idx, 0, NULL) == NULL)
        return -1;

    if (ZIP_IS_RDONLY(za)) {
        zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
        return -1;
    }

    e = za->entry+idx;

    changed = e->orig == NULL || mtime != e->orig->last_mod;

    if (changed) {
        if (e->changes == NULL) {
            if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) {
                zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
                return -1;
            }
        }
        e->changes->last_mod = mtime;
        e->changes->changed |= ZIP_DIRENT_LAST_MOD;
    }
    else {
        if (e->changes) {
            e->changes->changed &= ~ZIP_DIRENT_LAST_MOD;
            if (e->changes->changed == 0) {
		_zip_dirent_free(e->changes);
                e->changes = NULL;
            }
        }
    }

    return 0;
}
Beispiel #15
0
int
_zip_set_name(zip_t *za, zip_uint64_t idx, const char *name, zip_flags_t flags)
{
    zip_entry_t *e;
    zip_string_t *str;
    bool same_as_orig;
    zip_int64_t i;
    const zip_uint8_t *old_name, *new_name;
    zip_string_t *old_str;

    if (idx >= za->nentry) {
	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	return -1;
    }

    if (ZIP_IS_RDONLY(za)) {
	zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
	return -1;
    }

    if (name && name[0] != '\0') {
        /* TODO: check for string too long */
	if ((str=_zip_string_new((const zip_uint8_t *)name, (zip_uint16_t)strlen(name), flags, &za->error)) == NULL)
	    return -1;
	if ((flags & ZIP_FL_ENCODING_ALL) == ZIP_FL_ENC_GUESS && _zip_guess_encoding(str, ZIP_ENCODING_UNKNOWN) == ZIP_ENCODING_UTF8_GUESSED)
	    str->encoding = ZIP_ENCODING_UTF8_KNOWN;
    }
    else
	str = NULL;

    /* TODO: encoding flags needed for CP437? */
    if ((i=_zip_name_locate(za, name, 0, NULL)) >= 0 && (zip_uint64_t)i != idx) {
	_zip_string_free(str);
	zip_error_set(&za->error, ZIP_ER_EXISTS, 0);
	return -1;
    }

    /* no effective name change */
    if (i>=0 && (zip_uint64_t)i == idx) {
	_zip_string_free(str);
	return 0;
    }

    e = za->entry+idx;

    if (e->orig)
	same_as_orig = _zip_string_equal(e->orig->filename, str);
    else
	same_as_orig = false;

    if (!same_as_orig && e->changes == NULL) {
	if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) {
	    zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
	    _zip_string_free(str);
	    return -1;
	}
    }

    if ((new_name = _zip_string_get(same_as_orig ? e->orig->filename : str, NULL, 0, &za->error)) == NULL) {
	_zip_string_free(str);
	return -1;
    }

    if (e->changes) {
	old_str = e->changes->filename;
    }
    else if (e->orig) {
	old_str = e->orig->filename;
    }
    else {
	old_str = NULL;
    }
    
    if (old_str) {
	if ((old_name = _zip_string_get(old_str, NULL, 0, &za->error)) == NULL) {
	    _zip_string_free(str);
	    return -1;
	}
    }
    else {
	old_name = NULL;
    }

    if (_zip_hash_add(za->names, new_name, idx, 0, &za->error) == false) {
	_zip_string_free(str);
	return -1;
    }
    if (old_name) {
	_zip_hash_delete(za->names, old_name, NULL);
    }

    if (same_as_orig) {
	if (e->changes) {
	    if (e->changes->changed & ZIP_DIRENT_FILENAME) {
		_zip_string_free(e->changes->filename);
		e->changes->changed &= ~ZIP_DIRENT_FILENAME;
		if (e->changes->changed == 0) {
		    _zip_dirent_free(e->changes);
		    e->changes = NULL;
		}
		else {
		    /* TODO: what if not cloned? can that happen? */
		    e->changes->filename = e->orig->filename;
		}
	    }
	}
	_zip_string_free(str);
    }
    else {
	if (e->changes->changed & ZIP_DIRENT_FILENAME) {
	    _zip_string_free(e->changes->filename);
	}
	e->changes->changed |= ZIP_DIRENT_FILENAME;
	e->changes->filename = str;
    }

    return 0;
}
ZIP_EXTERN int
zip_file_set_comment(struct zip *za, zip_uint64_t idx,
		     const char *comment, zip_uint16_t len, zip_flags_t flags)
{
    struct zip_entry *e;
    struct zip_string *cstr;
    struct zip_dirent *de;
    int changed;

    if ((de=_zip_get_dirent(za, idx, 0, NULL)) == NULL)
	return -1;

    if (ZIP_IS_RDONLY(za)) {
	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
	return -1;
    }

    if (len > 0 && comment == NULL) {
	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	return -1;
    }

    if (len > 0) {
	if ((cstr=_zip_string_new((const zip_uint8_t *)comment, len, flags, &za->error)) == NULL)
	    return -1;
	if ((flags & ZIP_FL_ENCODING_ALL) == ZIP_FL_ENC_GUESS && _zip_guess_encoding(cstr, ZIP_ENCODING_UNKNOWN) == ZIP_ENCODING_UTF8_GUESSED)
	    cstr->encoding = ZIP_ENCODING_UTF8_KNOWN;
    }
    else
	cstr = NULL;

    e = za->entry+idx;

    if (e->changes) {
	_zip_string_free(e->changes->comment);
	e->changes->comment = NULL;
	e->changes->changed &= ~ZIP_DIRENT_COMMENT;
    }

    if (e->orig && e->orig->comment)
	changed = !_zip_string_equal(e->orig->comment, cstr);
    else
	changed = (cstr != NULL);
	
    if (changed) {
        if (e->changes == NULL) {
            if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) {
                _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
                return -1;
            }
        }
        e->changes->comment = cstr;
        e->changes->changed |= ZIP_DIRENT_COMMENT;
    }
    else {
	_zip_string_free(cstr);
	if (e->changes && e->changes->changed == 0) {
	    _zip_dirent_free(e->changes);
	    e->changes = NULL;
	}
    }

    return 0;
}
Beispiel #17
0
int
_zip_set_name(struct zip *za, zip_uint64_t idx, const char *name, zip_flags_t flags)
{
    struct zip_entry *e;
    struct zip_string *str;
    int changed;
    zip_int64_t i;

    if (idx >= za->nentry) {
        _zip_error_set(&za->error, ZIP_ER_INVAL, 0);
        return -1;
    }

    if (ZIP_IS_RDONLY(za)) {
        _zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
        return -1;
    }

    if (name && strlen(name) > 0) {
        /* XXX: check for string too long */
        if ((str=_zip_string_new((const zip_uint8_t *)name, (zip_uint16_t)strlen(name), flags, &za->error)) == NULL)
            return -1;
        if ((flags & ZIP_FL_ENCODING_ALL) == ZIP_FL_ENC_GUESS && _zip_guess_encoding(str, ZIP_ENCODING_UNKNOWN) == ZIP_ENCODING_UTF8_GUESSED)
            str->encoding = ZIP_ENCODING_UTF8_KNOWN;
    }
    else
        str = NULL;

    /* XXX: encoding flags needed for CP437? */
    if ((i=_zip_name_locate(za, name, 0, NULL)) >= 0 && (zip_uint64_t)i != idx) {
        _zip_string_free(str);
        _zip_error_set(&za->error, ZIP_ER_EXISTS, 0);
        return -1;
    }

    /* no effective name change */
    if (i>=0 && (zip_uint64_t)i == idx) {
        _zip_string_free(str);
        return 0;
    }

    e = za->entry+idx;

    if (e->changes) {
        _zip_string_free(e->changes->filename);
        e->changes->filename = NULL;
        e->changes->changed &= ~ZIP_DIRENT_FILENAME;
    }

    if (e->orig)
        changed = !_zip_string_equal(e->orig->filename, str);
    else
        changed = 1;

    if (changed) {
        if (e->changes == NULL) {
            if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) {
                _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
                return -1;
            }
        }
        e->changes->filename = str;
        e->changes->changed |= ZIP_DIRENT_FILENAME;
    }
    else {
        _zip_string_free(str);
        if (e->changes && e->changes->changed == 0) {
            _zip_dirent_free(e->changes);
            e->changes = NULL;
        }
    }

    return 0;
}
ZIP_EXTERN int
zip_file_extra_field_set(struct zip *za, zip_uint64_t idx, zip_uint16_t ef_id, zip_uint16_t ef_idx, const zip_uint8_t *data, zip_uint16_t len, zip_flags_t flags)
{
    struct zip_dirent *de;
    zip_uint16_t ls, cs;
    struct zip_extra_field *ef, *ef_prev, *ef_new;
    int i, found, new_len;

    if ((flags & ZIP_EF_BOTH) == 0) {
	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	return -1;
    }

    if (_zip_get_dirent(za, idx, 0, NULL) == NULL)
	return -1;
    
    if (ZIP_IS_RDONLY(za)) {
	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
	return -1;
    }
    
    if (ZIP_EF_IS_INTERNAL(ef_id)) {
	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	return -1;
    }

    if (_zip_file_extra_field_prepare_for_change(za, idx) < 0)
        return -1;
    
    de = za->entry[idx].changes;

    ef = de->extra_fields;
    ef_prev = NULL;
    i = 0;
    found = 0;

    for (; ef; ef=ef->next) {
	if (ef->id == ef_id && (ef->flags & flags & ZIP_EF_BOTH)) {
	    if (i == ef_idx) {
		found = 1;
		break;
	    }
	    i++;
	}
	ef_prev = ef;
    }

    if (i < ef_idx && ef_idx != ZIP_EXTRA_FIELD_NEW) {
	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	return -1;
    }

    if (flags & ZIP_EF_LOCAL)
	ls = _zip_ef_size(de->extra_fields, ZIP_EF_LOCAL);
    else
	ls = 0;
    if (flags & ZIP_EF_CENTRAL)
	cs = _zip_ef_size(de->extra_fields, ZIP_EF_CENTRAL);
    else
	cs = 0;

    new_len = ls > cs ? ls : cs;
    if (found)
	new_len -= ef->size + 4;
    new_len += len + 4;

    if (new_len > ZIP_UINT16_MAX) {
	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
	return -1;
    }
    
    if ((ef_new=_zip_ef_new(ef_id, len, data, flags)) == NULL) {
	_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
	return -1;
    }

    if (found) {
	if ((ef->flags & ZIP_EF_BOTH) == (flags & ZIP_EF_BOTH)) {
	    ef_new->next = ef->next;
	    ef->next = NULL;
	    _zip_ef_free(ef);
	    if (ef_prev)
		ef_prev->next = ef_new;
	    else
		de->extra_fields = ef_new;
	}
	else {
	    ef->flags &= ~(flags & ZIP_EF_BOTH);
	    ef_new->next = ef->next;
	    ef->next = ef_new;
	}	    
    }
    else if (ef_prev) {
	ef_new->next = ef_prev->next;
	ef_prev->next = ef_new;
    }
    else
	de->extra_fields = ef_new;
    
    return 0;
}