#! /usr/bin/python
#  -*- Python -*-

"""Kernel synchronization utility

This script allows synchronization between ALSA CVS repository and 
standard kernel sources stored in local BK repository.

This tool is intended mainly for internal use of Jaroslav Kysela.

Usage:
	%(PROGRAM)s [options] command

Where options is:

	-h
	--help
		Print this help

	-C <path>
	--cvsroot=<path>
		Set root of ALSA CVS repository

	-G <path>
	--gitroot=<path>
		Set root of Linux kernel GIT repository

Where command is:

	diffall
		Diff between ALSA and Linux kernel repositories
	diffall -r
		Reverse diff between ALSA and Linux kernel repositories

"""

import os
import sys
import string
import time
import dircache
import getopt
import re

# define for documentation
PROGRAM = sys.argv[0]

# define working directories
CVSROOT = '~/alsa'
GITROOT = '~/git/repos/linux-2.6'
PATCH_UPDATE = '~/alsa/alsa-kernel/scripts/kchanges/update'
GIT_COMMITER_NAME = 'Jaroslav Kysela'
GIT_COMMITER_EMAIL = 'perex@suse.cz'

# exclude some files or directories
ALSA_EXCLUDE = ['/include/version.h']
ALSA_EXCLUDE_DIR = ['/scripts', '/oss', '/kbuild']
KERNEL_EXCLUDE = ['/COPYING', '/CREDITS',
		  '/MAINTAINERS','/Makefile',
		  '/README','/REPORTING-BUGS',
		  '/include/sound/version.h']
KERNEL_EXCLUDE_DIR = ['/BitKeeper',
		      '/Documentation',
		      '/arch', '/drivers', '/crypto', '/fs',
		      '/include', '/init', '/ipc',
		      '/kernel', '/lib', '/mm', '/usr',
		      '/net', '/scripts', '/security',
		      '/sound/oss']
# force to process
ALSA_FORCE = []
ALSA_FORCE_DIR = ['/']
KERNEL_FORCE = []
KERNEL_FORCE_DIR = ['/Documentation/sound/alsa', '/include/sound']

# Directory mapping
ALSA_MAP = {'/':'/sound',
	    '/Documentation':'/Documentation/sound/alsa',
	    '/include':'/include/sound'}
KERNEL_MAP = {}		# Initialized from ALSA_MAP

# Comment mapping
COMMENT_MAP_DRIVER = [
		('/include/mpu401.h'	,'MPU401 UART'),
		('/drivers/mpu401'	,'MPU401 UART'),
	 	('/include/asound_fm.h' ,'Raw OPL FM'),
		('/include/opl3.h'	,'OPL3'),
		('/drivers/opl3'	,'OPL3'),
		('/drivers/opl4'	,'OPL4'),
		('/include/vx_core.h'	,'Digigram VX core'),
		('/drivers/vx'		,'Digigram VX core'),
		('/drivers'		,'Generic drivers'),
		('/include/uda1341.h'	,'UDA1341'),
		('/arm/Kconfig'		,'ARM'),
		('/arm/devdma.[ch]'	,'ARM DMA routines'),
		('/arm/aaci.[ch]'	,'ARM AACI PL041 driver'),
		('/arm/sa11xx-uda1341.c','SA11xx UDA1341 driver'),
		('/arm'			,'ERROR'),
		('/isa/Kconfig'		,'ISA'),
		('/isa/ad1816a'		,'AD1816A driver'),
		('/include/ad1848.h'	,'AD1848 driver'),
		('/isa/ad1848'		,'AD1848 driver'),
		('/include/cs4231.h'	,'CS4231 driver'),
		('/isa/cs423x/cs4231.*'	,'CS4231 driver'),
		('/isa/cs423x/cs4236.*'	,'CS4236+ driver'),
		('/isa/cs423x/.*pc98.*'	,'PC98(CS423x) driver'),
		('/isa/cs423x'		,'CS423x drivers'),
		('/include/es1688.h'	,'ES1688 driver'),
		('/isa/es1688'		,'ES1688 driver'),
		('/include/gus.h'	,'GUS Library'),
		('/isa/gus/gus_.*'	,'GUS Library'),
		('/isa/gus/gusclassic.c','GUS Classic driver'),
		('/isa/gus/gusextreme.c','GUS Extreme driver'),
		('/isa/gus/gusmax.c'	,'GUS MAX driver'),
		('/isa/gus/interwave.*'	,'AMD InterWave driver'),
		('/isa/gus'		,'GUS drivers'),
		('/include/sb.h'	,'SB drivers'),
		('/isa/sb/es968.c'	,'ES968 driver'),
		('/include/sfnt_info.h' ,'SoundFont'),
		('/include/soundfont.h' ,'SoundFont'),
		('/include/emu8000.h'	,'EMU8000 driver'),
		('/isa/sb/emu8000.*'	,'EMU8000 driver'),
		('/isa/sb/sb16.*'	,'SB16/AWE driver'),
		('/isa/sb/sb8.*'	,'SB8 driver'),
		('/isa/sb'		,'SB drivers'),
		('/isa/opti9xx'		,'Opti9xx drivers'),
		('/include/yss225.h'	,'Wavefront drivers'),
		('/include/snd_wavefront.h', 'Wavefront drivers'),
		('/isa/wavefront'	,'Wavefront drivers'),
		('/isa/als100.c' 	,'ALS100 driver'),
		('/isa/azt2320.c'	,'AZT2320 driver'),
		('/isa/cmi8330.c'	,'CMI8330 driver'),
		('/isa/dt019x.c' 	,'DT019x driver'),
		('/isa/es18xx.c' 	,'ES18xx driver'),
		('/isa/opl3sa2.c'	,'OPL3SA2 driver'),
		('/isa/sgalaxy.c'	,'Sound Galaxy driver'),
		('/include/sscape_ioctl.h','Sound Scape driver'),
		('/isa/sscape.c'	,'Sound Scape driver'),
		('/isa'			,'ERROR'),
		('/include/ak4531_codec.h','AK4531 codec'),
		('/pci/ac97/ak4531_codec.c','AK4531 codec'),
		('/include/ac97_codec.h','AC97 Codec'),
		('/pci/ac97'		,'AC97 Codec'),
		('/pci/ali5451'		,'ALI5451 driver'),
		('/include/emu10k1.h'	,'EMU10K1/EMU10K2 driver'),
		('/pci/emu10k1'		,'EMU10K1/EMU10K2 driver'),
		('/pci/au88x0'		,'au88x0 driver'),
		('/pci/ice1712/envy24ht.h','ICE1724 driver'),
		('/pci/ice1712/revo.(c|h)','ICE1724 driver'),
		('/pci/ice1712/amp.(c|h)','ICE1724 driver'),
		('/pci/ice1712/ice1724.c','ICE1724 driver'),
		('/pci/ice1712'		,'ICE1712 driver'),
		('/pci/korg1212'	,'KORG1212 driver'),
		('/pci/mixart'		,'MIXART driver'),
		('/pci/nm256'		,'NM256 driver'),
		('/include/hdsp.h'	,'RME HDSP driver'),
		('/pci/rme9652/hdsp.c'	,'RME HDSP driver'),
		('/pci/rme9652'		,'RME9652 driver'),
		('/include/ymfpci.h'	,'YMFPCI driver'),
		('/pci/ymfpci'		,'YMFPCI driver'),
		('/include/trident.h'	,'Trident driver'),
		('/pci/trident'		,'Trident driver'),
		('/pci/vx222'		,'Digigram VX222 driver'),
		('/include/cs46xx.h'	,'CS46xx driver'),
		('/pci/cs46xx'		,'CS46xx driver'),
		('/pci/als4000.c'	,'ALS4000 driver'),
		('/pci/azt3328.c'	,'AZT3328 driver'),
		('/pci/cmipci.c' 	,'CMIPCI driver'),
		('/pci/cs4281.c' 	,'CS4281 driver'),
		('/pci/ens1370.c'	,'ENS1370/1+ driver'),
		('/pci/ens1371.c'	,'ENS371+ driver'),
		('/pci/es1938.c' 	,'ES1938 driver'),
		('/pci/es1968.c' 	,'ES1968 driver'),
		('/pci/fm801.c'  	,'FM801 driver'),
		('/pci/intel8x0.c'	,'Intel8x0 driver'),
		('/pci/maestro3.c'	,'Maestro3 driver'),
		('/pci/rme32.c'		,'RME32 driver'),
		('/pci/rme96.c'		,'RME96 driver'),
		('/pci/sonicvibes.c'	,'SonicVibes driver'),
		('/pci/via82xx.c'	,'VIA82xx driver'),
		('/pci/bt87x.c'		,'BT87x driver'),
		('/pci/atiixp.c'	,'ATIIXP driver'),
		('/pci/atiixp_modem.c'	,'ATIIXP-modem driver'),
		('/pci/intel8x0m.c'	,'Intel8x0-modem driver'),
		('/pci/via82xx_modem.c'	,'VIA82xx-modem driver'),
		('/pci/Kconfig'		,'PCI drivers'),
		('/pci/Makefile'	,'PCI drivers'),
		('/pci/ca0106/.*'	,'CA0106 driver'),
		('/pci/hda/hda_codec.[ch]', 'HDA Codec driver'),
		('/pci/hda/hda_patch.[ch]', 'HDA Codec driver'),
		('/pci/hda/patch_.*'	,'HDA Codec driver'),
		('/pci/hda/hda_intel.c'	,'HDA Intel driver'),
		('/pci/hda/.*'		,'HDA generic driver'),
		('/include/hdspm.h'	,'HDSPM driver'),
		('/pci'			,'ERROR'),
		('/ppc/Makefile'	,'PPC'),
		('/ppc/Kconfig'		,'PPC'),
		('/ppc/awacs.(c|h)'	,'PPC AWACS driver'),
		('/ppc/burgundy.(c|h)'	,'PPC Burgundy driver'),
		('/ppc/daca.c'		,'PPC DACA driver'),
		('/ppc/keywest.c'	,'PPC Keywest driver'),
		('/ppc/pmac.(c|h)'	,'PPC PMAC driver'),
		('/ppc/powermac.c'	,'PPC PowerMac driver'),
		('/ppc/tumbler.(c|h)'	,'PPC Tumbler driver'),
		('/ppc/beep.c'		,'PPC Beep'),
		('/ppc/toonie.c'	,'PPC Toonie'),
		('/ppc'			,'ERROR'),
		('/i2c/l3'		,'L3 drivers'),
		('/include/tea575x-tuner.h','TEA575x tuner'),
		('/i2c/other/tea575x-tuner.c','TEA575x tuner'),
		('/include/ak4114.h'	,'AK4114 receiver'),
		('/i2c/other/ak4114.c'  ,'AK4114 receiver'),
		('/include/ak4117.h'	,'AK4117 receiver'),
		('/i2c/other/ak4117.c'  ,'AK4117 receiver'),
		('/include/ak4xxx-adda.h','AK4XXX AD/DA converters'),
		('/i2c/other/ak4xxx-adda.c','AK4XXX AD/DA converters'),
		('/i2c/other'		,'Serial BUS drivers'),
		('/include/i2c.h'	,'I2C lib core'),
		('/i2c/i2c.c'		,'I2C lib core'),
		('/include/cs8427.h'	,'I2C cs8427'),
		('/i2c/cs8427.c'	,'I2C cs8427'),
		('/i2c/tea6330t.c'	,'I2C tea6330t'),
		('/i2c'			,'ERROR'),
		('/parisc/harmony.c'	,'PARISC Harmony driver'),
		('/parisc/Kconfig'	,'PARISC'),
		('/parisc'		,'ERROR'),
		('/sparc/amd7930.c'	,'SPARC AMD7930 driver'),
		('/sparc/cs4231.c'	,'SPARC cs4231 driver'),
		('/sparc/Kconfig'	,'SPARC'),
		('/sparc'		,'ERROR'),
		('/include/emux_synth.h','Common EMU synth'),
		('/synth/emux'		,'Common EMU synth'),
		('/include/soundmem.h'  ,'Synth'),
		('/synth/Makefile'	,'Synth'),
		('/synth/util_mem.c'	,'Synth'),
		('/pcmcia/vx'		,'Digigram VX Pocket driver'),
		('/pcmcia/pdaudiocf'	,'PDAudioCF driver'),
		('/pcmcia/Kconfig'	,'PCMCIA Kconfig'),
		('/pcmcia/Makefile'	,'PCMCIA'),
		('/pcmcia'		,'ERROR'),
		('/usb/usx2y/.*'	,'USB USX2Y'),
		('/usb/Kconfig'		,'USB'),
		('/usb/Makefile'	,'USB'),
		('/usb/usbaudio.(c|h)'	,'USB generic driver'),
		('/usb/usbmidi.(c|h)'	,'USB generic driver'),
		('/usb/usbmixer.(c|h)'	,'USB generic driver'),
		('/usb/usbquirks.(c|h)' ,'USB generic driver'),
		('/usb/usbmixer_maps.c' ,'USB generic driver'),
		('/usb'			,'ERROR'),
		('/core/ioctl32'	,'IOCTL32 emulation'),
		('/include/pcm_oss.h'	,'ALSA<-OSS emulation'),
		('/include/mixer_oss.h'	,'ALSA<-OSS emulation'),
		('/core/oss'		,'ALSA<-OSS emulation'),
		('/core/seq/oss'	,'ALSA<-OSS sequencer'),
		('/include/ainstr.*'	,'Instrument layer'),
		('/core/seq/instr'	,'Instrument layer'),
		('/include/seq_kernel.h','ALSA sequencer'),
		('/include/seq_midi_emul.h','ALSA sequencer'),
		('/include/seq_midi_event.h','ALSA sequencer'),
		('/include/asequencer.h','ALSA sequencer'),
		('/include/seq_virmidi.h','Virtual Midi'),
		('/core/seq'		,'ALSA sequencer'),
		('/core/Kconfig'	,'ALSA Core'),
		('/core/memalloc.*'	,'Memalloc module'),
		('/core/sgbuf.*'	,'Memalloc module'),
		('/core/rtctimer.*'	,'RTC timer driver'),
		('/include/timer.h'	,'Timer Midlevel'),
		('/core/timer.*'	,'Timer Midlevel'),
		('/include/rawmidi.*'	,'RawMidi Midlevel'),
		('/core/rawmidi.*'	,'RawMidi Midlevel'),
		('/include/pcm.*'	,'PCM Midlevel'),
		('/core/pcm.*'		,'PCM Midlevel'),
		('/include/hwdep.*'	,'HWDEP Midlevel'),
		('/core/hwdep.*'	,'HWDEP Midlevel'),
		('/include/control.*'	,'Control Midlevel'),
		('/core/control.*'	,'Control Midlevel'),
		('/include/adriver.h'	,'ALSA Core'),
		('/include/asound.h'	,'ALSA Core'),
		('/include/asoundef.h'	,'ALSA Core'),
		('/include/driver.h'	,'ALSA Core'),
		('/include/version.h'	,'ALSA Version'),
		('/include/initval.h'	,'ALSA Core'),
		('/include/minors.h'	,'ALSA Minor Numbers'),
		('/include/sndmagic.h'	,'ALSA Core'),
		('/include/info.h'	,'ALSA Core'),
		('/include/core.h'	,'ALSA Core'),
		('/include/memalloc.h'  ,'ALSA Core'),
		('/include/config.h.in' ,'ALSA Core'),
		('/include/firmware_compat.h','ALSA Core'),
		('/core/info.*'		,'ALSA Core'),
		('/core/control.*'	,'ALSA Core'),
		('/core/init.*'		,'ALSA Core'),
		('/core/sound.*'	,'ALSA Core'),
		('/core/device.*'	,'ALSA Core'),
		('/core/memory.*'	,'ALSA Core'),
		('/core/misc.*'		,'ALSA Core'),
		('/core/wrappers.*'	,'ALSA Core'),
		('/core/Makefile'	,'ALSA Core'),
		('/core'		,'ERROR'),
		('/Documentation/.*'	,'Documentation'),
		('/doc/.*'		,'Documentation'),
		('/include'		,'ERROR'),
		('/sound_core.c'	,'OSS device core'),
		('/scripts'		,'IGNORE'),
		('/Kconfig'		,'Sound Core'),
		('/Makefile'		,'Sound Core'),
		('/INSTALL'		,'Sound Core'),
		('/Rules.make'		,'Sound Core'),
		('/configure.in'	,'Sound Core'),
		('/'			,'ERROR')
	       ]
COMMENT_MAP_LIB = [
		('/include/sound/.*'	,'Kernel Headers'),
		('/include/local.*'	,'Core'),
		('/include/global.*'	,'Core'),
		('/include/control.h'	,'Control API'),
		('/src/control/.*'	,'Control API'),
		('/src/mixer/.*'	,'Mixer API'),
		('/include/pcm.h'	,'PCM API'),
		('/src/pcm/.*'		,'PCM API'),
		('/src/rawmidi/.*'	,'RawMidi API'),
		('/src/timer/.*'	,'Timer API'),
		('/src/seq/.*'		,'Sequencer API'),
		('/src/instr/.*'	,'Instrument API'),
		('/src/input.*'		,'I/O subsystem'),
		('/src/output.*'	,'I/O subsystem'),
		('/src/conf.*'		,'Configuration'),
		('/src/async.*'		,'Async helpers'),
		('/src/error.*'		,'Error handler'),
		('/src/socket.*'	,'Socket helpers'),
		('/src/userfile.*'	,'Filename helpers'),
		('/src/Versions'	,'Core'),
		('/doc/.*'		,'Documentation'),
		('/configure.in'	,'Core'),
		('/NOTES'		,'Documentation'),
		('/'			,'ERROR')
	       ]
COMMENT_MAP_UTILS = [
		('/amixer/.*'		,'amixer'),
		('/alsamixer/.*'	,'alsamixer'),
		('/aplay/.*'		,'aplay/arecord'),
		('/alsaconf/.*'		,'alsaconf'),
		('/speaker-test/.*'	,'Speaker Test'),
		('/alsactl/.*'		,'ALSA Control (alsactl)'),
		('/configure.in'	,'Core'),
		('/Makefile.am'		,'Core'),
		('/Makefile.am'		,'Core'),
		('/m4/.*'		,'IGNORE'),
		('/'			,'ERROR')
	       ]
COMMENT_MAP_TOOLS = [
		('/envy24control/.*'	,'Envy24 Control'),
		('/rmedigicontrol/.*'	,'RME Digi Control'),
		('/pcxhrloader/.*'	,'Digigram PCXHR Loader'),
		('/echomixer/.*'	,'Digigram Echo Mixer'),
		('/Makefile'		,'Core'),
		('/'			,'ERROR')
	       ]
COMMENT_MAP_FIRMWARE = [
		('/vxloader'		,'Digigram Vx Loader'),
		('/mixartloader'	,'Digigram MixArt Loader'),
		('/pcxhrloader'		,'Digigram PCXHR Loader'),
		('/echoaudio'		,'Digigram Echo Audio Loader'),
		('/hdsploader'		,'RME HDSP Loader'),
		('/configure.in'	,'Core'),
		('/Makefile.am'		,'Core'),
		('/README'		,'Core'),
		('/'			,'ERROR')
	       ]
COMMENT_MAP_OSS = [
		('/alsa/pcm.c'		,'PCM Emulation'),
		('/alsa/Makefile.am'	,'Core'),
		('/alsa/aoss.1'		,'Manual Page'),
		('/configure.in'	,'Core'),
		('/'			,'ERROR')
	       ]
COMMENT_MAP = {
	'alsa-driver': COMMENT_MAP_DRIVER,
	'alsa-lib': COMMENT_MAP_LIB,
	'alsa-utils': COMMENT_MAP_UTILS,
	'alsa-tools': COMMENT_MAP_TOOLS,
	'alsa-firmware': COMMENT_MAP_FIRMWARE,
	'alsa-oss': COMMENT_MAP_OSS
}

# Global variables
LAST_CVS_TIME = 0
FATAL_LSYNC_ERROR = 0

# Translation tables for CVS user IDs
CVS_USER = {'perex':'Jaroslav Kysela <perex@suse.cz>',
	    'uid53661':'Jaroslav Kysela <perex@suse.cz>',	# seems SF CVS malfunction
	    'tiwai':'Takashi Iwai <tiwai@suse.de>',
	    'abramo':'Abramo Bagnara <abramo@alsa-project.org>',
	    'cladisch':'Clemens Ladisch <clemens@ladisch.de>',
	    'jcdutton':'James Courtier-Dutton <James@superbug.co.uk>'}

def usage(code, msg=''):
	print __doc__ % globals()
	if msg:
		print msg
	sys.exit(code)

def get_cvs_root():
	return os.path.expanduser(CVSROOT + '/alsa-kernel')

def get_cvs_root_top():
	return os.path.expanduser(CVSROOT)

def get_git_root(root=''):
	if root == '':
		root = GITROOT
	return os.path.expanduser(root)

def my_popen(cmd):
	fp = os.popen(cmd)
	lines = fp.readlines()
	fp.close()
	return lines

def my_popen2(cmd):
	fp = os.popen('{ ' + cmd + '; } 2>&1', 'r')
	lines = fp.readlines()
	sts = fp.close()
	if sts == None:
		sts = 0
	return sts, lines

def print_file(fp, lines):
	for line in lines:
		fp.write(line)

def print_file_comment(fp, lines, space=''):
	for line in lines:
		fp.write('# %s' % space)
		fp.write(line)

def extract_name(full):
	x, y = string.split(full, '<')
	return x[:-1]

def extract_email(full):
	x, y = string.split(full, '<')
	return y[1:-1]

def modify_version_file():
	filename = get_cvs_root() + '/include/version.h'
	fp = open(filename)
	lines = fp.readlines()
	fp.close()
	fp = open(filename + '.new', 'w')
	for line in lines:
		str = '#define CONFIG_SND_DATE'
		if line[0:len(str)] == str:
			t = time.gmtime(LAST_CVS_TIME)
			ts = time.strftime('%a %b %d %H:%M:%S %Y', t)
			str = str + ' " (%s UTC)"\n' % ts
			fp.write(str)
		else:
			fp.write(line)
	fp.close()
	os.rename(filename + '.new', filename)

def update_cvs_file(file, destfile = '', params = ''):
	path, file = os.path.split(file)
	try:
		os.chdir(get_cvs_root() + '/' + path)
	except OSError, msg:
		os.mkdir(get_cvs_root() + '/' + path)
		pass
	if destfile == '':
		cmd = 'cvs -z3 update %s' % file
	else:
		cmd = 'cvs -z3 update -p %s %s' % (params, file)
	fp = os.popen(cmd)
	lines = fp.readlines()
	fp.close()
	if destfile == '':
		print_file(sys.stderr, lines)
	else:
		fp = open(destfile, 'w')
		print_file(fp, lines)
		fp.close()

def update_git_file(file, root=''):
	# empty
	root =''

def change_diff_line(line, file):
	if line[:6] == 'Index:':
		return 'Index: ' + file + '\n'
	if line[:7] == 'diff -u':
		return 'diff -u ' + file + '.old ' + file + '\n'
	if line[:4] == '--- ':
		file = file + '.old'
	if line[:4] == '--- ' or line[:4] == '+++ ':
		idx = string.find(line[4:], '\t')
		nline = line[:4] + file + line[4+idx:]
		return nline
	return line

def change_diff_lines(lines, file):
	if len(lines) > 1:
		lines[0] = change_diff_line(lines[0], file)
		lines[1] = change_diff_line(lines[1], file)
	return lines

def update_last_cvs_time(stime):
	global LAST_CVS_TIME
	# mktime returns local time, and we need to convert
	# the result to GMT (UTC) time, so substract the difference
	# in seconds between local time and GMT (time.timezone)	
	gm_time = time.mktime(time.strptime(stime)) - time.timezone
	if (LAST_CVS_TIME < gm_time):
		LAST_CVS_TIME = gm_time

def get_cvs_files(base, dir):
	dlist = []
	flist = []

	# Read all entries
	fp = open(base + dir + 'CVS/Entries')
	entries = fp.readlines()
	fp.close()

	# Process files entries
	for e in entries:
		try:
			flags, name, rev, time, unk1, unk2 = string.split(e, '/')
			if string.count(flags, 'D') <= 0:
				update_last_cvs_time(time)
				flist.append(dir + name);
		except ValueError, msg:
			pass

	# Process directory entries
	for e in entries:
		try:
			flags, name, rev, time, unk1, unk2 = string.split(e, '/')
			if string.count(flags, 'D') > 0 and not ALSA_EXCLUDE_DIR.count(dir + name):
				dlist1, flist1 = get_cvs_files(base, dir + name + '/');
				dlist.append(dir + name);
				dlist = dlist + dlist1
				flist = flist + flist1
		except ValueError, msg:
			pass

	return dlist, flist

def get_git_files(base, dir):
	dlist = []
	flist = []

	# merge files and directories
	dir1 = base + dir
	l = dircache.listdir(dir1)
	for f in l:
		if f[:-1] == '~' or f == '.git':
			continue
		if os.path.isdir(dir1 + f):
			if not KERNEL_EXCLUDE_DIR.count(dir + f):
				dlist1, flist1 = get_git_files(base, dir + f + '/')
				dlist.append(dir + f)
				dlist = dlist + dlist1
				flist = flist + flist1
		else:
			flist.append(dir + f)
	del l
	return dlist, flist

def remap_engine(dir, dict):
	comp = string.split(dir, '/')
	add = ''
	while len(comp) > 0:
		tmp = string.join(comp, '/');
		if tmp == '':
			tmp = '/'
		if dict.has_key(tmp):
			if add != '' and dict[tmp] != '/':
				add = '/' + add
			return dict[tmp] + add;
		if add == '':
			add = comp.pop();
		else:
			add = comp.pop() + '/' + add;
	return dir

def remap_alsa(dir):
	return remap_engine(dir, ALSA_MAP);

def remap_kernel(dir):
	return remap_engine(dir, KERNEL_MAP);

def remap_alsa_file(file):
	comp = string.split(file, '/')
	path = string.join(comp[:len(comp)-1],'/')
	if path == '':
		path = '/'
	path = remap_alsa(path)
	if path[-1] != '/':
		path = path + '/'
	return path + comp[-1]

def remap_kernel_file(file):
	comp = string.split(file, '/')
	path = string.join(comp[:len(comp)-1],'/')
	if path == '':
		path = '/'
	path = remap_kernel(path)
	if path[-1] != '/':
		path = path + '/'
	return path + comp[-1]

def get_cvs_date_param(from_cvs_time, to_cvs_time):
	if from_cvs_time:
		xtime1 = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(from_cvs_time))
	if to_cvs_time:
		xtime2 = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(to_cvs_time))
	if from_cvs_time and not to_cvs_time:
		return "-d'%s<'" % xtime1
	elif not from_cvs_time and to_cvs_time:
		return "-d'%s>'" % xtime2
	else:
		return "-d'%s<%s'" % (xtime1, xtime2)

def get_cvs_date_param1(to_cvs_time):
	if to_cvs_time:
		xtime1 = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(to_cvs_time))
		return "-D'%s'" % xtime1
	else:
		return ''

def collect_cvs_logs(file, from_cvs_time, to_cvs_time):
	path, file = os.path.split(file)
	os.chdir(get_cvs_root() + '/' + path)
	cmd = "cvs -z3 log %s %s" % (get_cvs_date_param(from_cvs_time, to_cvs_time), file)
	fp = os.popen(cmd)
	lines = fp.readlines()
	fp.close()
	i = 0
	fdead = 1
	dead = 0
	next_is_comment = 0
	result = ''
	while i < len(lines):
		if lines[i][0:11] == 'revision ':
			revision = line[i][11:]
			i = i + 1
		elif lines[i][0:6] == 'date: ':
			date, author, state, xlines = string.split(lines[i], ';')
			date = date[6:]
			author = string.lstrip(author)[8:]
			state = string.lstrip(state)[7:]
			if state == 'dead' and fdead:
				dead = 1
			i = i + 1
			next_is_comment = 1
			fdead = 0
		elif next_is_comment:
			next_is_comment = 0
			comment = ''
			while i < len(lines) and lines[i][0:6] != '------' and lines[i][0:6] != '======':
				if lines[i][0:9] == 'branches:' or lines[i][0:11] == '*BK_IGNORE*':
					while i < len(lines) and lines[i][0:6] != '------' and lines[i][0:6] != '======':
						i = i + 1
				else:
					comment = comment + '  ' + lines[i]
					i = i + 1
			result = result + '%s, %s :\n' % (CVS_USER[author], date) + comment
		else:
			i = i + 1
	return result, dead

def write_to_patch_update_file(lines):
	global PATCH_UPDATE
	fp = open(os.path.expanduser(PATCH_UPDATE), 'a+')
	print_file(fp, lines)
	fp.close()

def do_alsa_kernel_diff(alsa, kernel, from_cvs_time, to_cvs_time, ofile=sys.stdout):
	global FATAL_LSYNC_ERROR
	ncvsflag = 0
	ngitflag = 0
	lsyncflag = 0
	if from_cvs_time or to_cvs_time:
		lsyncflag = 1
#		ncvsflag = 1
#		afile = '/tmp/ksync_kernel_diff'
#		params = get_cvs_date_param1(to_cvs_time)
#		update_cvs_file(alsa, afile, params)
#	else:
	afile = get_cvs_root() + alsa;
	if not os.path.exists(afile):
		update_cvs_file(alsa)
		if not os.path.exists(afile):
			ncvsflag = 1
			os.system('touch %s' % afile)
	kfile = get_git_root() + kernel;
	if not os.path.exists(kfile):
		update_git_file(kfile)
		if not os.path.exists(kfile):
			ngitflag = 1
			os.system('touch %s' % kfile)
	if lsyncflag > 0:
		cmd = 'diff -uN %s %s' % (afile, kfile)
	else:
		cmd = 'diff -uN %s %s' % (kfile, afile)
	fp = os.popen(cmd)
	lines = fp.readlines()
	fp.close()
	lines = change_diff_lines(lines, "linux" + kernel)
	if len(lines) > 1:
		if lsyncflag > 0:
			collected_logs, dead = collect_cvs_logs(alsa, from_cvs_time, to_cvs_time)
			os.chdir(get_git_root())
			print 'Updating file %s' % kfile
			if not ngitflag and not dead:
				if os.system('git edit %s' % kfile):
					sys.exit(1)
			if ngitflag:
				if os.system('cp -avf %s %s' % (afile, kfile)):
					sys.exit(1)
				if os.system('cg-add %s' % kfile):
					sys.exit(1)
				ngitflag = 0
			elif dead:
				if os.system('cg-rm %s' % kfile):
					sys.exit(1)
			else:
				if os.system('cp -avf %s %s' % (afile, kfile)):
					sys.exit(1)
			tmpfile = '/tmp/ksyncABCD1234'
			fp = open(tmpfile, 'w+')
			if alsa == '/include/version.h':
				collected_logs = 'Updated date/time/version from the ALSA CVS tree'
			fp.write(collected_logs)
			fp.close()
			os.remove(tmpfile)
		else:
			print_file(ofile, lines)
	if ncvsflag:
		os.remove(afile)
	if ngitflag:
		os.remove(kfile)

def do_kernel_alsa_diff(kernel, alsa):
	afile = get_cvs_root() + alsa;
	kfile = get_git_root() + kernel;
	if not os.path.exists(afile):
		update_cvs_file(alsa)
	if not os.path.exists(kfile):
		update_git_file(kfile)
	cmd = 'diff -uN %s %s' % (afile, kfile)
	fp = os.popen(cmd)
	lines = fp.readlines()
	fp.close()
	lines = change_diff_lines(lines, alsa)
	print_file(sys.stdout, lines)

def diffall(reverse, from_cvs_time = 0, to_cvs_time = 0):
	lsyncflag = 0
	if from_cvs_time or to_cvs_time:
		lsyncflag = 1
	cdirs, cfiles = get_cvs_files(get_cvs_root(), '/')
	bdirs, bfiles = get_git_files(get_git_root(), '/')
	print 'GITROOT = %s' % GITROOT
	for d in ALSA_FORCE_DIR:
		if len(d) == 0:
			continue
		if cdirs.count(d) == 0:
			cdirs.append(d)
			if d[-1] != '/':
				d = d + '/'
			if d != '/':
				cdirs1, cfiles1 = get_cvs_files(get_cvs_root(), d)
				cdirs = cdirs + cdirs1
				cfiles = cfiles + cfiles1
	for d in KERNEL_FORCE_DIR:
		if len(d) == 0:
			continue
		if bdirs.count(d) == 0:
			bdirs.append(d)
			if d[-1] != '/':
				d = d + '/'
			if d != '/':
				bdirs1, bfiles1 = get_git_files(get_git_root(), d)
				bdirs = bdirs + bdirs1
				bfiles = bfiles + bfiles1
	cfiles = cfiles + ALSA_FORCE
	bfiles = bfiles + KERNEL_FORCE
	for f in ALSA_EXCLUDE:
		cfiles.remove(f)
	for f in KERNEL_EXCLUDE:
		bfiles.remove(f)
	first = 1
	for d in cdirs:
		dm = d
		d = remap_alsa(dm)
		if not bdirs.count(d):
			if first:
				print '# Added directories to Kernel Tree:'
				first = 0
			print '#   %s (%s)' % (dm, d)
	first = 1
	for d in bdirs:
		dm = d
		d = remap_kernel(dm)
		if not cdirs.count(d):
			if first:
				print '# Removed directories from Kernel Tree:'
				first = 0
			print '#   %s (%s)' % (dm, d)
	first = 1
	for f in cfiles:
		df = f
		f = remap_alsa_file(df)
		if not bfiles.count(f):
			if first:
				print '# Added files to Kernel Tree:'
				first = 0
			print '#   %s (%s)' % (df, f)
	first = 1
	for f in bfiles:
		df = f
		f = remap_kernel_file(df)
		if not cfiles.count(f):
			if first:
				print '# Removed files from Kernel Tree:'
				first = 0
			print '#   %s (%s)' % (df, f)
	print '\n'
	for f in cfiles:
		df = f
		f = remap_alsa_file(df)
		if not reverse:
			do_alsa_kernel_diff(df, f, from_cvs_time, to_cvs_time)
		else:
			do_kernel_alsa_diff(f, df)
		if bfiles.count(f):
			bfiles.remove(f)
	for f in bfiles:
		df = f
		f = remap_kernel_file(df)
		if not reverse:
			do_alsa_kernel_diff(f, df, from_cvs_time, to_cvs_time)
		else:
			do_kernel_alsa_diff(df, f)
		if cfiles.count(f):
			cfiles.remove(f)
	update_cvs_file('include/version.h')
	modify_version_file()
	if not reverse:
		do_alsa_kernel_diff('/include/version.h', '/include/sound/version.h', from_cvs_time, to_cvs_time)
	else:
		do_kernel_alsa_diff('/include/sound/version.h', '/include/version.h')
	os.remove(get_cvs_root() + '/include/version.h')
	update_cvs_file('include/version.h')

def get_last_lsync_time():
	os.chdir(get_git_root())
	os.system('bk get -q include/sound/version.h')
	fp = open('include/sound/version.h', 'r')
	lines = fp.readlines()
	fp.close()
	if lines[2][0:24] != '#define CONFIG_SND_DATE ':
		print 'Error, unexpected include/sound/version.h in git directory'
		print lines[2]
		sys.exit(1)
	str = lines[2][27:-7]
	t = time.mktime(time.strptime(str, '%a %b %d %H:%M:%S %Y'))
	return t + time.timezone

def get_last_changeset():
	os.chdir(get_git_root())
	os.system('bk get -q Makefile')
	fp = open('Makefile', 'r')
	lines = fp.readlines()
	fp.close()
	kernel1 = '?'
	kernel2 = '?'
	kernel3 = '?'
	kernel4 = ''
	for line in lines:
		if line[0:10] == 'VERSION = ':
			kernel1 = line[10:-1]
		elif line[0:13] == 'PATCHLEVEL = ':
			kernel2 = line[13:-1]
		elif line[0:11] == 'SUBLEVEL = ':
			kernel3 = line[11:-1]
		elif line[0:15] == 'EXTRAVERSION = ':
			kernel4 = line[15:-1]
	kernel = kernel1 + '.' + kernel2 + '.' + kernel3 + kernel4
	cmd = 'bk prs -r+ ChangeSet'
	fp = os.popen(cmd)
	lines = fp.readlines()
	fp.close()
	cs = '0.0'
	for line in lines:
		line = line[:-1]
		words = string.split(line, ' ')
		if words[0] == 'D':
			cs = words[1]
		elif words[0] == 'S':
			kernel = words[1][1:]
	return cs, kernel

def filename(ver = 1):
	cs, kernel = get_last_changeset()
        print 'alsa-%s-%s-linux-%s-cs%s.patch' % (time.strftime("%Y-%m-%d", time.gmtime(time.time())), ver, kernel, cs)	

def parse_time(str):
	args = string.split(str, '/')
	if len(args) < 3:
		args.insert(0, time.strftime("%Y", time.gmtime(time.time())))
	if len(args) != 3:
		print 'Bad date syntax'
		sys.exit(0)
	str = args[0] + ' ' + args[1] + ' ' + args[2]
	t = time.mktime(time.strptime(str, '%Y %m %d'))
	return t - time.timezone

def cvsps(changeset):
	os.chdir(get_cvs_root())
	lines = my_popen("cvsps -Z 3 -u | grep PatchSet | tail -n 1")
	dummy, endchangeset = string.split(lines[0][0:-2], ' ')
	changeset = int(changeset)
	endchangeset = int(endchangeset)
	print 'Generating CVS changesets %s..%s' % (changeset, endchangeset)
	try:
		os.mkdir(get_cvs_root() + '/scripts/changesets')
	except OSError, msg:
		a = 0
	while changeset <= endchangeset:
		print 'ChangeSet %s' % changeset
		fp = open(get_cvs_root() + '/scripts/changesets/%s.patch' % changeset, 'w+')
		lines = my_popen('cvsps -Z 3 -g -s %s' % changeset)
		idx = 0
		bkfile = ''
		for i in lines:
			if i[0:6] == 'Index:':
				bkfile = remap_alsa_file(i[string.find(i, '/'):-1])
				fp.write(change_diff_line(i, bkfile))
				idx = 1
			elif i[0:13] == '--- /dev/null':
				prevline = i
				idx = 100
			elif idx > 0 and idx < 4:
				fp.write(change_diff_line(i, bkfile))
				idx += 1
			elif idx == 100:
				str = string.expandtabs(i[string.find(i, '/'):-1])
				str = str[0:string.find(str, ' ')]
				bkfile = remap_alsa_file(str)
				fp.write('Index: %s\n' % bkfile)
				fp.write('diff -u %s.old %s\n' % (bkfile, bkfile))
				fp.write(change_diff_line(prevline, bkfile))
				fp.write(change_diff_line(i, bkfile))
				idx = 3
			else:
				fp.write(i)
		fp.close()
		changeset += 1				

def cvsps_merge_members1(file, module):
	global COMMENT_MAP
	map = COMMENT_MAP[module]
	if module == 'alsa-driver' and file[:6] == '/acore':
		file = '/' + file[2:]
	for i in map:
		if re.compile("^" + i[0]).search(file):
			if i[1] == 'ERROR':
				break
			return i[1]
	if file[-11:] == '/.cvsignore':
		return 'IGNORE'
	if file[-9:] == '/Makefile':
		return file
	return 'ERROR'

def cvsps_merge_members(members, module='alsa-driver'):
	changes = []
	result = []
	for member in members:
		file, other = string.split(member, ':')
		file = "/" + file
		while file != '':
			result1 = cvsps_merge_members1(file, module)
			if result1 == 'ERROR':
				print 'Cannot identify file "%s"' % file
				sys.exit(1)
			if result1 != '':
				file = ''
				changes.append(result1)
			else:
				i = string.rfind(file, '/')
				if i < 0:
					file = ''
				else:
					file = file[0:i]
	i = 0
	while i < len(changes):
		j = 0
		while j < len(changes):
			if i != j and changes[i] == changes[j]:
				del changes[j]
				i = -1
				break
			j += 1
		i += 1
	xresult = ''
	for i in changes:
		if len(i) + len(xresult) > 70:
			result.append(xresult)
			xresult = ''
		if xresult == '':
			xresult = i
		else :
			xresult = xresult + ',' + i
	if xresult != '':
		result.append(xresult)
	return result

def cvsps_merge1(f,t=0):
	if not t:
		print 'Trying merge patch ' + f
	ff = get_cvs_root() + '/scripts/changesets/' + f
	fp = open(ff, 'r')
	diffs = fp.readlines()
	fp.close()
	files = []
	pline = ''
	for line in diffs:
		if line[:14] == "+++ /dev/null\t":
			continue
		if line[0:3] == '+++':
			xx1 = string.split(line, ' ')
			xx1 = string.split(xx1[1], '\t')
			xx2 = string.split(xx1[0], '/')
			a = 0
			file = ''
			for i in xx2:
				a += 1
				if a < 2:
					continue
				if file != '':
					file = file + '/' + i
				else:
					file = i
			files.append(file)
		pline = line
	summary = ''
	signed = []
	log = []
	members = []
	ok = 0
	idx = 0
	author = ''
	date = ''
	level = 'Devel'
	for line in diffs:
		if line[:-1] == 'Log:':
			ok = 1
		elif line[:-1] == 'Members: ':
			ok = 2
		elif line[:6] == 'Index:':
			ok = 0
		elif line[:3] == '---':
			ok = 0
		elif line[:5] == 'Date:':
			date = line[6:-1]
		elif line[:7] == 'Author:':
			author = CVS_USER[line[8:-1]]
		elif line[:8] == 'Members:':
			ok = 2
		elif ok == 1:
			if line[:14] == "Signed-off-by:":
				signed.append(line)
			elif line[:14] == "Signed-Off-By:":
				signed.append(line)
			elif line[:9] == "Summary: ":
				summary = line[9:]
			elif line[:8] == "Summary:":
				summary = line[8:]
			elif line[:9] == "Subject: ":
				summary = line[9:]
			elif line[:8] == "Subject:":
				summary = line[8:]
			elif line[:13] == "Patch-level: ":
				level = line[13:-1]
			elif line[:13] == "Patch-Level: ":
				level = line[13:-1]
			else:
				log.append(line)
		elif ok == 2:
			if line != '\n':
				members.append(line[1:])
	while len(log) > 1 and log[0] == '\n':
		del log[0]
	if level == 'Low':
		level = 'Devel'
	elif level == 'High':
		level = 'ASAP'
	if level == 'merged':
		level = 'Merged'
	while len(log) > 1 and log[len(log)-1] == '\n':
		del log[len(log)-1]
	if summary != '':
		lines = '[ALSA] ' + summary + '\n'
	else:
		lines = 'ALSA CVS update\n'
	nlines = lines
	if date != '':
		lines = lines + 'D:' + date + '\n'
	changes = cvsps_merge_members(members)
	for i in changes:
		lines = lines + 'C:' + i + '\n'
	#if author != '':
	#	lines = lines + 'A:' + author + '\n'
	for i in changes:
		nlines = nlines + i + '\n'
	for i in members:
		lines = lines + 'F:' + i
	for i in log:
		lines = lines + 'L:' + i
		nlines = nlines + i
	signed_flag = 0
	for i in signed:
		if not signed_flag:
			nlines = nlines + '\n'
			signed_flag = 1
		lines = lines + i
		nlines = nlines + i
	if not signed_flag:
		nlines = nlines + '\n'
		signed_flag = 1
	lines = lines + 'Signed-off-by: %s\n' % author
	nlines = nlines + 'Signed-off-by: %s\n' % author
	if t:
		print lines
		if level == 'Merged':
			print '^^^^^^ Already merged to mainstream...'
			print
		return

	if level == 'Merged':
		print 'Already merged to mainstream...'
		return

	nlines = string.replace(nlines, '"', "'")
	nlines = string.replace(nlines, '`', "'")
	lines = string.replace(lines, '"', "'")
	lines = string.replace(lines, '`', "'")

	nfiles = []
	for file in files:
		if not os.path.exists(file):
			nfiles.append(file)
	print 'New files: %s' % nfiles

	while 1:
		sts, out = my_popen2('cat ' + ff + ' | cg-patch')
		for o in out:
			print o[:-1]
			if string.find(o, 'FAILED') > 0:
				sts = 1
		if sts == 0:
			break
		print 'Patch failed...'
		os.system('cg-cancel')
		sys.exit(1)
	for file in nfiles:
		print 'Adding file %s...' % file
		os.system('cg-add %s' % file)
	pline = ''
	for line in diffs:
		if line[:14] == "+++ /dev/null\t" and pline != '':
			file = pline[4:string.find(pline, '\t')]
			file1 = remap_alsa_file(file[11:])[1:]
			print 'Removing file %s...' % file1
			os.system('cg-rm %s' % file1)
		pline = line

	print 'Commiting...'
	os.environ['AUTHOR_NAME'] = extract_name(author)
	os.environ['AUTHOR_EMAIL'] = extract_email(author)
	os.environ['AUTHOR_DATE'] = date
	os.environ['COMMIT_AUTHOR_NAME'] = GIT_COMMITER_NAME
	os.environ['COMMIT_AUTHOR_EMAIL'] = GIT_COMMITER_EMAIL
	cmd = 'echo "' + nlines + '" | cg-commit'
	while os.system(cmd):
		print 'cg-commit failed'
		os.system('cg-cancel')
		sys.exit(1)
	if False:
		for file in files:
			os.system('bk get %s' % file)
			if not os.path.exists(file):
				continue
			if os.system('bk comment -y"' + lines + '" %s' % file):
				print 'BK comment change failed'
				sys.exit(1)
	#sys.exit(1)
	
def cvsps_merge(start):
	os.chdir(get_git_root())
	l = dircache.listdir(get_cvs_root() + '/scripts/changesets')
	for f in l:
		if string.find(f, '.') > 0:
			file, suff = string.split(f, '.')
			if suff != 'patch':
				continue
			y = int(file)
			if y < start:
				continue
			cvsps_merge1(f, t=1)
	print 'Is this ok? Press Ctrl-C to abort...'
	sys.stdin.readline()
	get_cvs_files(get_cvs_root(), '/')
	update_cvs_file('include/version.h')
	modify_version_file()
	do_alsa_kernel_diff('/include/version.h', '/include/sound/version.h', 0, 0)
	os.remove(get_cvs_root() + '/include/version.h')
	update_cvs_file('include/version.h')
	os.chdir(get_git_root())
	print 'Is this ok? Press Ctrl-C to abort...'
	sys.stdin.readline()
	for f in l:
		if string.find(f, '.') > 0:
			file, suff = string.split(f, '.')
			if suff != 'patch':
				continue
			y = int(file)
			if y < start:
				continue
			cvsps_merge1(f, t=0)
	del l

def cvsps_changes1(changes, lines, module):
	if module == 'alsa-kernel':
		module = 'alsa-driver'
	idx = 0
	delim = '---------------------\n'
	while idx < len(lines):
		while idx < len(lines) and lines[idx] != delim:
			idx += 1
		idx += 1
		patchset = 0
		date = ''
		author = ''
		log = []
		logflag = False
		memflag = False
		members = []
		while idx < len(lines) and lines[idx] != delim:
			line = lines[idx]
			# print line
			if line[:8] == 'Members:':
				logflag = False
				memflag = True
			elif logflag:
				if len(line) > 1:
					log.append(line[:-1])
			elif memflag:
				if len(line) > 2:
					members.append(line[1:-1])
			elif line[:9] == 'PatchSet ':
				patchset = long(line[9:-1])
				#print 'patchset: %s' % patchset
			elif line[:6] == 'Date: ':
				date = line[6:-1]
				#print 'date: %s' % date
			elif line[:8] == 'Author: ':
				author = line[8:-1]
				#print 'author: %s' % author
			elif line[:4] == 'Log:':
				logflag = True
			idx += 1
		a = {}
		a['patchset'] = patchset
		a['date'] = date
		a['author'] = author
		a['log'] = log
		a['members'] = members
		a['module'] = module
		already = False
		idx1 = 0
		for change in changes:
			if a['date'] == change['date'] and \
			   a['author'] == change['author'] and \
			   a['log'] == change['log']:
				# print 'SAME!!!'
				already = True
				break
			if a['date'] < change['date']:
				changes.insert(idx1, a)
				# print 'INSERTED!!!'
				already = True
				break
			idx1 += 1
		if not already:
			changes.append(a)
		del log
		del members

def cvsps_changes2(changes):
	res = {}
	for change in changes:
		module = change['module']
		if not res.has_key(module):
			res[module] = {}
		members = cvsps_merge_members(change['members'], module)[0]
		mems = string.split(members, ',')
		for mem in mems:
			if mem == 'IGNORE':
				continue
			if not res[module].has_key(mem):
				res[module][mem] = []
			res[module][mem].append(change)
	return res

def cvsps_changes3(allitems):
	items = []
	idx = 0
	for item in ['Sound Core', 'ALSA Core']:
		items.append([item])
		idx += 1
	core = idx
	items.append([])	# Core
	midlevel = idx + 1
	items.append([])	# Midlevel
	all = idx + 2
	items.append(allitems)
	items[all].sort()
	for item in items[all]:
		if string.find(item, 'Core') >= 0:
			items[core].append(item)
		if string.find(item, 'Midlevel') >= 0:
			items[midlevel].append(item)
		if string.find(item, 'API') >= 0:
			items[midlevel].append(item)
	idx1 = core
	while idx1 < all:
		for item in items[idx1]:
			items[all].remove(item)
		idx1 += 1
	for items1 in items[:idx]:
		for item in items1:
			idx1 = idx
			while idx1 < len(items):
				if item in items[idx1]:
					items[idx1].remove(item)
				idx1 += 1
	return items

def cvsps_changes(rev1, rev2):
	changes = []
	os.chdir(get_cvs_root_top())
	fullset = ['alsa-driver', 'alsa-kernel', 'alsa-lib',
		   'alsa-utils', 'alsa-tools', 'alsa-firmware',
		   'alsa-oss']
	for module in fullset:
	#for module in ['alsa-oss']:
		os.chdir(get_cvs_root_top() + '/' + module)
		lines = my_popen("cvsps -Z 3 -r %s -r %s" % (rev1, rev2))
		#lines = my_popen("cvsps -Z 3 -u -r %s -r %s" % (rev1, rev2))
		cvsps_changes1(changes, lines, module)
	res = cvsps_changes2(changes)
	modules1 = res.keys()
	modules = []
	for module in fullset:
		if module in modules1:
			modules.append(module)
	print
	print
	print 'Changelog'
	print '*********'
	print
	for module in modules:
		print '* %s' % module
		items = cvsps_changes3(res[module].keys())
		for items1 in items:
			for b in items1:
				if not res[module].has_key(b):
					continue
				print '  + %s' % b
				for a in res[module][b]:
					log = a['log'][0]
					if log[:9] == 'Summary: ':
						log = log[9:]
					elif log[:8] == 'Summary:':
						log = log[8:]
					print '    - %s' % log
	print
	print
	print 'Detailed changelog'
	print '******************'
	print
	for module in modules:
		print '* %s' % module
		items = cvsps_changes3(res[module].keys())
		for items1 in items:
			for b in items1:
				if not res[module].has_key(b):
					continue
				print '  + %s' % b
				for a in res[module][b]:
					log = a['log']
					first = "-"
					for l in log:
						print '    %s %s' % (first, l)
						first = " "

def main():
	global CVSROOT, GITROOT, PATCH_UPDATE, ALSA_MAP, KERNEL_MAP
	try:
		opts, args = getopt.getopt(sys.argv[1:], 'hB:C:',
					   ['cvsroot=', 'bkroot=', 'help']);
	except getopt.error, msg:
		usage(1, msg)

	cwd = os.getcwd()

	# parse the options
	for opt, arg in opts:
		if opt in ('-h', '--help'):
			usage(0)
		elif opt == '--cvsroot':
			CVSROOT = arg
		elif opt == '--gitroot':
			GITROOT = arg

	for k in ALSA_MAP.keys():
		KERNEL_MAP[ALSA_MAP[k]] = k

	if not args:
		print 'Command not specified'
		sys.exit(1)
	if args[0] == 'diffall' or args[0] == 'work':
		reverse=0
		if args[0] == 'work':
			GITROOT = '~/git/repos/work'
		if len(args) > 1:
			if args[1] == '-r':
				reverse=1
			else:
				print 'Ignoring extra arguments %s' % args[1:]
		diffall(reverse)
	elif args[0] == 'cvsps':
		reverse=0
		if len(args) < 2:
			print 'Please, give a starting changeset'
			return
		cvsps(args[1])
	elif args[0] == 'cvsps-merge':
		GITROOT = '~/git/repos/work'
		if len(args) < 2:
			x = 0
		else:
			x = int(args[1])
		cvsps_merge(x)
	elif args[0] == 'cvsps-changes':
		if len(args) < 3:
			print 'Please, give a start and end revision'
			print 'Example: ksync cvsps-changes v1-0-7 v1-0-8'
			return
		cvsps_changes(args[1], args[2])
	elif args[0] == 'lsync':
		to_cvs_time = 0
		patch = ''
		if len(args) > 1:
			if args[1] == "last":
				BKROOT = '~/bk/linux-sound/linux-sound'
				xtime = get_last_lsync_time()
				print time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(xtime))
				return
			to_cvs_time = parse_time(args[1])
		if len(args) > 2:
			patch = os.path.abspath(os.path.expanduser(args[2]))
		else:
			npatch = os.path.abspath('kchanges/%s' % time.strftime("%Y-%m-%d", time.gmtime(to_cvs_time)))
			if os.path.exists(npatch):
				print 'Using kernel patch %s' % npatch
				patch = npatch

		if to_cvs_time > 0:
			CVSROOT = '~/alsa.local'
			os.chdir(os.path.expanduser('~/'))
			os.system('rm -rf alsa.local')
			os.system('mkdir alsa.local')
			os.chdir(os.path.expanduser('~/alsa.local'))
			os.system('cvs -d/home/src/alsa co -P %s alsa-driver alsa-kernel' % get_cvs_date_param1(to_cvs_time))
			if os.path.exists(patch):
				os.chdir(os.path.expanduser('~/alsa.local/alsa-kernel'))
				os.system('patch -p2 < %s' % patch)
				print 'Is this ok? Press Ctrl-C to abort...'
				sys.stdin.readline()

		BKROOT = '~/bk/linux-sound/work'
		os.chdir(os.path.expanduser('~/bk/linux-sound'))
		os.system('rm -rf work')
		os.system('bk clone -l linux-sound work')

		if os.path.exists(os.path.expanduser(PATCH_UPDATE)):
			os.remove(os.path.expanduser(PATCH_UPDATE))

		from_cvs_time = get_last_lsync_time()
		if to_cvs_time > 0 and to_cvs_time <= from_cvs_time:
			print 'Time < last_lsync_time (%s)' % time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(from_cvs_time))
			sys.exit(1)
		diffall(0, from_cvs_time, to_cvs_time)
		if FATAL_LSYNC_ERROR > 0:
			print 'Error during merge, see %s file...' % PATCH_UPDATE
	elif args[0] == 'cvslocal':
		if len(args) <= 1:
			print 'Specify time, please'
			sys.exit(1)
		to_cvs_time = parse_time(args[1])
		CVSROOT = '~/alsa.local'
		os.chdir(os.path.expanduser('~/'))
		os.system('rm -rf alsa.local')
		os.system('mkdir alsa.local')
		os.chdir(os.path.expanduser('~/alsa.local'))
		os.system('cvs -d/home/src/alsa co -P %s alsa-driver alsa-kernel' % get_cvs_date_param1(to_cvs_time))
	elif args[0] == 'filename':
		ver = 1
		if len(args) > 1:
			ver = args[1]
		filename(ver)
	else:
		print 'Unknown command %s' % args[0]
		sys.exit(1)

if __name__ == '__main__':
	main()
	sys.exit(0)
