#!/usr/bin/env python

# interactively apply each hunk of a patch.
# 061120:
#	Missing hunks when hitting --- lines while building hunks
#	improve visual
#

import sys;
import re;
import string;
import os;

def usage():
	print "ipatch.py [-o \"<patch options>\"] [-u <unused file>] <patch file>"
	print
	print "\tInteractively apply patch hunk by hunk";
	print
	print "\t-o : options passed to 'patch' command.  Don't pass 'dry-run'."
	print "\t-u : unapplied patch hunks are put into this file."

def error(msg):
	print "Error : %s" % (msg);
	sys.exit(-1);

def spit_hunk(file, hunk, no) :
	global patch_opts;
	global unused_file;

	f = open("/tmp/ipatch-temp.patch", "w");
	f.write(file + hunk);
	f.close();

	print
	print "==============================================================";
	print file + hunk;

	while 1==1:
		print
		print "-------------------";
		print "Press 'y' to apply, 't' to try, or <return> to skip the hunk ..."

		c = sys.stdin.readline();
		c=string.strip(c);
		print

		if c == 'y':
			sys.stdout.flush();
			cmd = "patch " + patch_opts + " < /tmp/ipatch-temp.patch";
			ret=os.system(cmd);
			sys.stdout.flush();
			if ret!=0 :
				print "patching hunk #" + `no` + " failed ...";
				if no == 1:
					unused_file.write(file + hunk);
				else:
					unused_file.write(hunk);
			print "-------------------";
			print "Press <return> to continue ...";
			c = sys.stdin.readline();
			break;
		elif c == 't':
			sys.stdout.flush();
			cmd = "patch " + patch_opts + " --dry-run < /tmp/ipatch-temp.patch";
			os.system(cmd);
			sys.stdout.flush();
		else:
			print "skipping hunk #" + `no` + " ...";
			if no == 1:
				unused_file.write(file + hunk);
			else:
				unused_file.write(hunk);
			break;
		

if (len(sys.argv)%2 !=0) or sys.argv[1] == "-h":
	usage();
	sys.exit();

# check options
patch_opts="";
unused_fname="/tmp/ipatch-unused.patch";
for i in (1, 3):
	if len(sys.argv) == i+1:
		break;

	if sys.argv[i] == "-o":
		patch_opts = sys.argv[i+1];
	elif sys.argv[i] == "-u":
		unused_fname = sys.argv[i+1];

file=open(sys.argv[len(sys.argv)-1], 'r');
unused_file=open(unused_fname, 'w');

state="need_file";
file_header="";
num_lines=0;

for l in file.readlines():
	num_lines+=1;
	if state == "need_file" and (re.match(r"^--- ", l) or re.match(r"^\+\+\+ ", l)) :
		file_header = l;
		state = "need_file_1";
	elif state == "need_file_1" :
		if (re.match(r"^--- ", l) or re.match(r"^\+\+\+ ", l)) :
			file_header = file_header + l;
			state = "need_hunk";
			num_hunks=0;
		else :
			msg = "Unexpected line at line " + `num_lines` + ".  State is need_file_1";
			error(msg);
	elif state == "need_hunk" :
		if re.match(r"^@@ .* @@", l):
			hunk = l;
			state = "build_hunk";
	elif state == "build_hunk" :
		if re.match(r"^@@ .* @@", l):
			num_hunks +=1;
			spit_hunk(file_header, hunk, num_hunks);
			hunk = l;
			state = "build_hunk";
		elif (re.match(r"^--- ", l) or re.match(r"^\+\+\+ ", l)) :
			num_hunks +=1;
			spit_hunk(file_header, hunk, num_hunks);
			file_header = l;
			state = "need_file_1";
		elif (not re.match(r"^diff ", l)) :
			# skip auxilillary diff lines
			hunk += l;

if state == "build_hunk":
	num_hunks +=1;
	spit_hunk(file_header, hunk, num_hunks);

file.close();
unused_file.close();
