Edit

thodg/got/lib/diffoffset.c

Branch :

  • Show log

    Commit

  • Author : Stefan Sperling
    Date : 2018-07-12 15:04:11
    Hash : c35a7943
    Message : account for line shift in blame; lots of help from tb@

  • lib/diffoffset.c
  • /*
     * Copyright (c) 2018 Stefan Sperling <stsp@openbsd.org>
     *
     * Permission to use, copy, modify, and distribute this software for any
     * purpose with or without fee is hereby granted, provided that the above
     * copyright notice and this permission notice appear in all copies.
     *
     * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     */
    
    #include <sys/queue.h>
    #include <sys/stat.h>
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <sha1.h>
    #include <zlib.h>
    
    #include "got_object.h"
    
    #include "got_error.h"
    #include "got_lib_delta.h"
    #include "got_lib_inflate.h"
    #include "got_lib_object.h"
    #include "got_lib_diffoffset.h"
    
    /*
     * A line offset between an old file and a new file, derived from diff chunk
     * header info @@ -old_lineno,old_length +new_lineno,new_length @@ in a diff
     * with zero context lines (as in diff -U0 old-file new-file).
     */
    struct got_diffoffset_chunk {
    	int lineno;	/* first line which has shifted */
    	int offset;	/* applies to subsequent lines until next chunk */
    	SIMPLEQ_ENTRY(got_diffoffset_chunk) entry;
    };
    
    static struct got_diffoffset_chunk *
    alloc_chunk(int lineno, int offset)
    {
    	struct got_diffoffset_chunk *chunk;
    
    	chunk = calloc(1, sizeof(*chunk));
    	if (chunk == NULL)
    		return NULL;
    
    	chunk->lineno = lineno;
    	chunk->offset = offset;
    
    	return chunk;
    }
    
    const struct got_error *
    got_diffoffset_alloc(struct got_diffoffset_chunks **chunks)
    {
    	const struct got_error *err = NULL;
    	struct got_diffoffset_chunk *first;
    
    	first = alloc_chunk(0, 0);
    	if (first == NULL)
    		return got_error_from_errno();
    
    	*chunks = calloc(1, sizeof(**chunks));
    	if (*chunks == NULL) {
    		err = got_error_from_errno();
    		free(first);
    		return err;
    	}
    
    	SIMPLEQ_INIT(*chunks);
    	SIMPLEQ_INSERT_HEAD(*chunks, first, entry);
    
    	return NULL;
    }
    
    void
    got_diffoffset_free(struct got_diffoffset_chunks *chunks)
    {
    	struct got_diffoffset_chunk *chunk;
    
    	while (!SIMPLEQ_EMPTY(chunks)) {
    		chunk = SIMPLEQ_FIRST(chunks);
    		SIMPLEQ_REMOVE_HEAD(chunks, entry);
    		free(chunk);
    	}
    	free(chunks);
    }
    
    const struct got_error *
    got_diffoffset_add(struct got_diffoffset_chunks *chunks,
        int old_lineno, int old_length, int new_lineno, int new_length)
    {
    	struct got_diffoffset_chunk *chunk1, *chunk2;
    
    	chunk1 = alloc_chunk(old_lineno, new_lineno - old_lineno);
    	if (chunk1 == NULL)
    		return got_error_from_errno();
    
    	chunk2 = alloc_chunk(old_lineno + old_length,
    	    new_lineno - old_lineno + new_length - old_length);
    	if (chunk2 == NULL) {
    		const struct got_error *err = got_error_from_errno();
    		free(chunk1);
    		return err;
    	}
    
    	SIMPLEQ_INSERT_TAIL(chunks, chunk1, entry);
    	SIMPLEQ_INSERT_TAIL(chunks, chunk2, entry);
    	return NULL;
    }
    
    int
    got_diffoffset_get(struct got_diffoffset_chunks *chunks, int lineno)
    {
    	struct got_diffoffset_chunk *chunk, *prev;
    
    	prev = SIMPLEQ_FIRST(chunks);
    	SIMPLEQ_FOREACH(chunk, chunks, entry) {
    		if (chunk->lineno > lineno)
    			break;
    		prev = chunk;
    	}
    
    	return lineno + prev->offset;
    }