]> git.pld-linux.org Git - packages/cvsspam.git/blame - cvsspam-textdiff.patch
drop ruby_mod_ver_requires_eq
[packages/cvsspam.git] / cvsspam-textdiff.patch
CommitLineData
8b9b7edf 1--- cvsspam.rb 2005-07-11 16:53:29.000000000 +0100
d0da94c0
ER
2+++ cvsspam.rb 2007-09-21 11:08:17.000000000 +0100
3@@ -590,6 +590,42 @@
4 end
5 end
6
7+# outputs commit log comment text supplied by LogReader as preformatted Text
8+class TextCommentHandler < LineConsumer
9+ def initialize
10+ @lastComment = nil
11+ end
12+
13+ def setup
14+ @haveBlank = false
15+ @comment = ""
16+ end
17+
18+ def consume(line)
19+ if line =~ /^\s*$/
20+ @haveBlank = true
21+ else
22+ if @haveBlank
23+ @comment += "\n"
24+ @haveBlank = false
25+ end
26+ $mailSubject = line unless $mailSubject.length > 0
27+ @comment += line += "\n"
28+ end
29+ end
30+
31+ def teardown
32+ unless @comment == @lastComment
33+ #println("<pre class=\"comment\">")
34+ encoded = @comment
35+ $commentEncoder.gsub!(encoded)
36+ println(encoded)
37+ #println("</pre>")
38+ @lastComment = @comment
39+ end
40+ end
41+end
42+
43
44 # Handle lines from LogReader that represent the name of the branch tag for
45 # the next file in the log. When files are committed to the trunk, the log
46@@ -649,6 +685,31 @@
47 end
48 end
49
50+# Reads a line giving the path and name of the current file being considered
51+# from our log of all files changed in this commit. Subclasses make different
52+# records depending on whether this commit adds, removes, or just modifies this
53+# file
54+class TextFileHandler < LineConsumer
55+ def setTagHandler(handler)
56+ @tagHandler = handler
57+ end
58+
59+ def consume(line)
60+ $file = FileEntry.new(line)
61+ if $diff_output_limiter.choose_to_limit?
62+ $file.has_diff = false
63+ end
64+ #$fileEntries << $file
65+ $file.tag = getTag
66+ handleFile($file)
67+ end
68+
69+ protected
70+ def getTag
71+ @tagHandler.getLastTag
72+ end
73+end
74+
75 # A do-nothing superclass for objects that know how to create hyperlinks to
76 # web CVS interfaces (e.g. CVSweb). Subclasses overide these methods to
77 # wrap HTML link tags arround the text that this classes methods generate.
78@@ -659,6 +720,11 @@
79 htmlEncode(path)
80 end
81
82+ # text path
83+ def textpath(path, tag)
84+ path
85+ end
86+
87 # Just returns the value of the 'version' argument. Subclasses should change
88 # this into a link to the given version of the file.
89 def version(path, version)
90@@ -670,6 +736,11 @@
91 def diff(file)
92 '-&gt;'
93 end
94+
95+ # text diff
96+ def textdiff(file)
97+ '->'
98+ end
99 end
100
101 # Superclass for objects that can link to CVS frontends on the web (ViewCVS,
102@@ -810,6 +881,31 @@
103 end
104 end
105
106+# Note when LogReader finds record of a file that was added in this commit
107+class TextAddedFileHandler < TextFileHandler
108+ def handleFile(file)
109+ file.type="A"
110+ file.toVer=$toVer
111+ end
112+end
113+
114+# Note when LogReader finds record of a file that was removed in this commit
115+class TextRemovedFileHandler < TextFileHandler
116+ def handleFile(file)
117+ file.type="R"
118+ file.fromVer=$fromVer
119+ end
120+end
121+
122+# Note when LogReader finds record of a file that was modified in this commit
123+class TextModifiedFileHandler < TextFileHandler
124+ def handleFile(file)
125+ file.type="M"
126+ file.fromVer=$fromVer
127+ file.toVer=$toVer
128+ end
129+end
130+
131
132 # Used by UnifiedDiffHandler to record the number of added and removed lines
133 # appearing in a unidiff.
134@@ -1030,6 +1126,160 @@
135 end
136 end
137
138+# Used by TextUnifiedDiffHandler to produce an Text
139+class TextUnifiedDiffColouriser < LineConsumer
140+ def initialize
141+ @currentState = "@"
142+ @currentStyle = "info"
143+ @lineJustDeleted = nil
144+ @lineJustDeletedSuperlong = false
145+ @truncatedLineCount = 0
146+ end
147+
148+ def output=(io)
149+ @emailIO = io
150+ end
151+
152+ def consume(line)
153+ initial = line[0,1]
154+ superlong_line = false
155+ if $maxDiffLineLength && line.length > $maxDiffLineLength+1
156+ line = line[0, $maxDiffLineLength+1]
157+ superlong_line = true
158+ @truncatedLineCount += 1
159+ end
160+ if initial != @currentState
161+ prefixLen = 1
162+ suffixLen = 0
163+ if initial=="+" && @currentState=="-" && @lineJustDeleted!=nil
164+ # may be an edit, try to highlight the changes part of the line
165+ a = line[1,line.length-1]
166+ b = @lineJustDeleted[1,@lineJustDeleted.length-1]
167+ prefixLen = commonPrefixLength(a, b)+1
168+ suffixLen = commonPrefixLength(a.reverse, b.reverse)
169+ # prevent prefix/suffux having overlap,
170+ suffixLen = min(suffixLen, min(line.length,@lineJustDeleted.length)-prefixLen)
171+ deleteInfixSize = @lineJustDeleted.length - (prefixLen+suffixLen)
172+ addInfixSize = line.length - (prefixLen+suffixLen)
173+ oversize_change = deleteInfixSize*100/@lineJustDeleted.length>33 || addInfixSize*100/line.length>33
174+
175+ if prefixLen==1 && suffixLen==0 || deleteInfixSize<=0 || oversize_change
176+ print(@lineJustDeleted)
177+ else
178+ print(@lineJustDeleted[0,prefixLen])
179+ print(@lineJustDeleted[prefixLen,deleteInfixSize])
180+ print(@lineJustDeleted[@lineJustDeleted.length-suffixLen,suffixLen])
181+ end
182+ if superlong_line
183+ println("[...]")
184+ else
185+ println("")
186+ end
187+ @lineJustDeleted = nil
188+ end
189+ if initial=="-"
190+ @lineJustDeleted=line
191+ @lineJustDeletedSuperlong = superlong_line
192+ shift(initial)
193+ # we'll print it next time (fingers crossed)
194+ return
195+ elsif @lineJustDeleted!=nil
196+ print(@lineJustDeleted)
197+ if @lineJustDeletedSuperlong
198+ println("[...]")
199+ else
200+ println("")
201+ end
202+ @lineJustDeleted = nil
203+ end
204+ shift(initial)
205+ if prefixLen==1 && suffixLen==0 || addInfixSize<=0 || oversize_change
206+ encoded = line
207+ else
208+ encoded = line[0,prefixLen] +
209+ line[prefixLen,addInfixSize] +
210+ line[line.length-suffixLen,suffixLen]
211+ end
212+ else
213+ encoded = line
214+ end
215+ if initial=="-"
216+ unless @lineJustDeleted==nil
217+ print(@lineJustDeleted)
218+ if @lineJustDeletedSuperlong
219+ println("[...]")
220+ else
221+ println("")
222+ end
223+ @lineJustDeleted=nil
224+ end
225+ end
226+ print(encoded)
227+ if superlong_line
228+ println("[...]")
229+ else
230+ println("")
231+ end
232+ end
233+
234+ def teardown
235+ unless @lineJustDeleted==nil
236+ print(@lineJustDeleted)
237+ if @lineJustDeletedSuperlong
238+ println("[...]")
239+ else
240+ println("")
241+ end
242+ @lineJustDeleted = nil
243+ end
244+ shift(nil)
245+ if @truncatedLineCount>0
246+ println("[Note: Some over-long lines of diff output only partialy shown]")
247+ end
248+ end
249+
250+ # start the diff output, using the given lines as the 'preamble' bit
251+ def start_output(*lines)
252+ println("--------------------------------------------------------------------------------")
253+ case $file.type
254+ when "A"
255+ print($frontend.textpath($file.basedir, $file.tag))
256+ println("\n")
257+ println("#{$file.file} added at #{$frontend.version($file.path,$file.toVer)}")
258+ when "R"
259+ print($frontend.textpath($file.basedir, $file.tag))
260+ println("\n")
261+ println("#{$file.file} removed after #{$frontend.version($file.path,$file.fromVer)}")
262+ when "M"
263+ print($frontend.textpath($file.basedir, $file.tag))
264+ println("\n")
265+ println("#{$file.file} #{$frontend.version($file.path,$file.fromVer)} #{$frontend.textdiff($file)} #{$frontend.version($file.path,$file.toVer)}")
266+ end
267+ lines.each do |line|
268+ println(line)
269+ end
270+ end
271+
272+ private
273+
274+ def formatChange(text)
275+ return '^M' if text=="\r"
276+ end
277+
278+ def shift(nextState)
279+ @currentState = nextState
280+ end
281+
282+ def commonPrefixLength(a, b)
283+ length = 0
284+ a.each_byte do |char|
285+ break unless b[length]==char
286+ length = length + 1
287+ end
288+ return length
289+ end
290+end
291+
292
293 # Handle lines from LogReader that are the output from 'cvs diff -u' for the
294 # particular file under consideration
295@@ -1084,6 +1334,57 @@
296 end
297 end
298
299+# Handle lines from LogReader that are the output from 'cvs diff -u' for the
300+# particular file under consideration for Text
301+class TextUnifiedDiffHandler < LineConsumer
302+ def setup
303+ @stats = UnifiedDiffStats.new
304+ @colour = TextUnifiedDiffColouriser.new
305+ @colour.output = @emailIO
306+ @lookahead = nil
307+ end
308+
309+ def consume(line)
310+ case lineno()
311+ when 1
312+ @diffline = line
313+ when 2
314+ @lookahead = line
315+ when 3
316+ if $file.wants_diff_in_mail?
317+ @colour.start_output(@diffline, @lookahead, line)
318+ end
319+ else
320+ @stats.consume(line)
321+ if $file.wants_diff_in_mail?
322+ if $maxLinesPerDiff.nil? || @stats.diffLines < $maxLinesPerDiff
323+ @colour.consume(line)
324+ elsif @stats.diffLines == $maxLinesPerDiff
325+ @colour.consume(line)
326+ @colour.teardown
327+ end
328+ end
329+ end
330+ end
331+
332+ def teardown
333+ if @lookahead == nil
334+ $file.isEmpty = true
335+ elsif @lookahead =~ /Binary files .* and .* differ/
336+ $file.isBinary = true
337+ else
338+ if $file.wants_diff_in_mail?
339+ if $maxLinesPerDiff && @stats.diffLines > $maxLinesPerDiff
340+ println("[truncated at #{$maxLinesPerDiff} lines; #{@stats.diffLines-$maxLinesPerDiff} more skipped]")
341+ else
342+ @colour.teardown
343+ end
344+ $file.has_diff = true
345+ end
346+ end
347+ end
348+end
349+
350
351 # a filter that counts the number of characters output to the underlying object
352 class OutputCounter
353@@ -1381,6 +1682,18 @@
354 $handlers["R"].setTagHandler(tagHandler)
355 $handlers["M"].setTagHandler(tagHandler)
356
357+$texthandlers = Hash[">" => TextCommentHandler.new,
358+ "U" => TextUnifiedDiffHandler.new,
359+ "T" => tagHandler,
360+ "A" => TextAddedFileHandler.new,
361+ "R" => TextRemovedFileHandler.new,
362+ "M" => TextModifiedFileHandler.new,
363+ "V" => VersionHandler.new]
364+
365+$texthandlers["A"].setTagHandler(tagHandler)
366+$texthandlers["R"].setTagHandler(tagHandler)
367+$texthandlers["M"].setTagHandler(tagHandler)
368+
369 $fileEntries = Array.new
370 $task_list = Array.new
371 $allTags = Hash.new
372@@ -1403,6 +1716,24 @@
373
374 end
375
376+File.open("#{$logfile}.emailtexttmp", File::RDWR|File::CREAT|File::TRUNC) do |mail|
377+
378+ $diff_output_limiter = OutputSizeLimiter.new(mail, $mail_size_limit)
379+
380+ File.open($logfile) do |log|
381+ reader = LogReader.new(log)
382+
383+ until reader.eof
384+ handler = $texthandlers[reader.currentLineCode]
385+ if handler == nil
386+ raise "No handler file lines marked '##{reader.currentLineCode}'"
387+ end
388+ handler.handleLines(reader.getLines, $diff_output_limiter)
389+ end
390+ end
391+
392+end
393+
394 if $subjectPrefix == nil
395 $subjectPrefix = "[CVS #{Repository.array.join(',')}]"
396 end
397@@ -1432,7 +1763,10 @@
398
399 # generate the email header (and footer) having already generated the diffs
400 # for the email body to a temp file (which is simply included in the middle)
401-def make_html_email(mail)
402+def make_html_email(mail, boundary)
403+ mail.puts("--#{boundary}")
404+ mail.puts("Content-Type: text/html;" + ($charset.nil? ? "" : "; charset=\"#{$charset}\""))
405+ mail.puts("Content-Disposition: inline\n\n");
406 mail.puts(<<HEAD)
407 <html>
408 <head>
409@@ -1660,7 +1994,7 @@
410 mail.puts("<center><small><a href=\"http://www.badgers-in-foil.co.uk/projects/cvsspam/\" title=\"commit -&gt; email\">CVSspam</a> #{$version}</small></center>")
411
412 mail.puts("</body></html>")
413-
414+ mail.puts("\n\n--#{boundary}--\n\n")
415 end
416
417 # Tries to look up an 'alias' email address for the given string in the
418@@ -1818,11 +2152,188 @@
419
420 $from_address = sender_alias($from_address) unless $from_address.nil?
421
422+def rand_string(len)
423+ chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
424+ newpass = ""
425+ 1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
426+ return newpass
427+end
428+
429+def make_text_email(mail, boundary)
430+ mail.puts(<<HEAD)
431+--#{boundary}
432+Content-Type: text/plain; charset=us-ascii
433+Content-Disposition: inline
434+
435+HEAD
436+
437+ haveTags = false
438+ Repository.each do |repository|
439+ haveTags |= repository.has_multiple_tags
440+ end
441+
442+ filesAdded = 0
443+ filesRemoved = 0
444+ filesModified = 0
445+ totalLinesAdded = 0
446+ totalLinesRemoved = 0
447+ file_count = 0
448+ lastPath = ""
449+ last_repository = nil
450+ $fileEntries.each do |file|
451+ unless file.repository == last_repository
452+ last_repository = file.repository
453+ if last_repository.has_multiple_tags
454+ mail.print("Mixed-tag commit")
455+ else
456+ mail.print("Commit")
457+ end
458+ mail.print(" in #{last_repository.common_prefix}")
459+ if last_repository.trunk_only?
460+ mail.print(" on MAIN")
461+ else
462+ mail.print(" on ")
463+ tagCount = 0
464+ last_repository.each_tag do |tag|
465+ tagCount += 1
466+ if tagCount > 1
467+ mail.print tagCount<last_repository.tag_count ? ", " : " & "
468+ end
469+ mail.print tag ? tag : "MAIN"
470+ end
471+ end
472+ mail.puts("\n")
473+ end
474+ file_count += 1
475+ if file.addition?
476+ filesAdded += 1
477+ elsif file.removal?
478+ filesRemoved += 1
479+ elsif file.modification?
480+ filesModified += 1
481+ end
482+ name = file.name_after_common_prefix
483+ slashPos = name.rindex("/")
484+ if slashPos==nil
485+ prefix = ""
486+ else
487+ thisPath = name[0,slashPos]
488+ name = name[slashPos+1,name.length]
489+ if thisPath == lastPath
490+ prefix = " "*(slashPos) + "/"
491+ else
492+ prefix = thisPath + "/"
493+ end
494+ lastPath = thisPath
495+ end
496+ if file.addition?
497+ name = "#{name}"
498+ elsif file.removal?
499+ name = "#{name}"
500+ end
501+ if file.has_diff?
502+ mail.print("#{prefix}#{name} ")
503+ else
504+ mail.print("#{prefix}#{name} ")
505+ end
506+ if file.isEmpty
507+ mail.print("[empty] ")
508+ elsif file.isBinary
509+ mail.print("[binary] ")
510+ else
511+ if file.lineAdditions>0
512+ totalLinesAdded += file.lineAdditions
513+ mail.print("+#{file.lineAdditions} ")
514+ end
515+ if file.lineRemovals>0
516+ totalLinesRemoved += file.lineRemovals
517+ mail.print("-#{file.lineRemovals} ")
518+ end
519+ end
520+ if last_repository.has_multiple_tags
521+ if file.tag
522+ mail.print("#{file.tag} ")
523+ else
524+ mail.print("MAIN ")
525+ end
526+ end
527+ if file.addition?
528+ mail.print("added #{$frontend.version(file.path,file.toVer)} ")
529+ elsif file.removal?
530+ mail.print("#{$frontend.version(file.path,file.fromVer)} removed ")
531+ elsif file.modification?
532+ mail.print("#{$frontend.version(file.path,file.fromVer)} #{$frontend.textdiff(file)} #{$frontend.version(file.path,file.toVer)} ")
533+ end
534+
535+ mail.puts("\n")
536+ end
537+ if $fileEntries.size>1 && (totalLinesAdded+totalLinesRemoved)>0
538+ if totalLinesAdded>0
539+ mail.print("+#{totalLinesAdded} ")
540+ end
541+ if totalLinesRemoved>0
542+ mail.print("-#{totalLinesRemoved} ")
543+ end
544+ mail.puts("\n")
545+ end
546+
547+ totalFilesChanged = filesAdded+filesRemoved+filesModified
548+ if totalFilesChanged > 1
549+ changeKind = 0
550+ if filesAdded>0
551+ mail.print("#{filesAdded} added")
552+ changeKind += 1
553+ end
554+ if filesRemoved>0
555+ mail.print(" + ") if changeKind>0
556+ mail.print("#{filesRemoved} removed")
557+ changeKind += 1
558+ end
559+ if filesModified>0
560+ mail.print(" + ") if changeKind>0
561+ mail.print("#{filesModified} modified")
562+ changeKind += 1
563+ end
564+ mail.print(", total #{totalFilesChanged}") if changeKind > 1
565+ mail.puts(" files\n")
566+ end
567+
568+ if $task_list.size > 0
569+ task_count = 0
570+ $task_list.each do |item|
571+ task_count += 1
572+ item = htmlEncode(item)
573+ mail.puts("* #{item}\n")
574+ end
575+ end
576+
577+
578+ File.open("#{$logfile}.emailtexttmp") do |input|
579+ input.each do |line|
580+ mail.puts(line.chomp)
581+ end
582+ end
583+ if $diff_output_limiter.choose_to_limit?
584+ mail.puts("[Reached #{$diff_output_limiter.written_count} bytes of diffs.")
585+ mail.puts("Since the limit is about #{$mail_size_limit} bytes,")
586+ mail.puts("a further #{$diff_output_limiter.total_count-$diff_output_limiter.written_count} were skipped.]")
587+ end
588+ if $debug
589+ blah("leaving file #{$logfile}.emailtexttmp")
590+ else
591+ File.unlink("#{$logfile}.emailtexttmp")
592+ end
593+
594+ mail.puts("CVSspam #{$version}")
595+end
596+
597 mailer.send($from_address, $recipients) do |mail|
598 mail.header("Subject", mailSubject)
599 inject_threading_headers(mail)
600 mail.header("MIME-Version", "1.0")
601- mail.header("Content-Type", "text/html" + ($charset.nil? ? "" : "; charset=\"#{$charset}\""))
602+ boundary = rand_string(32)
603+ mail.header("Content-Type", "multipart/alternative; boundary=\"#{boundary}\"")
604+ mail.header("Content-Disposition", "inline")
605 if ENV['REMOTE_HOST']
606 # TODO: I think this will always be an IP address. If a hostname is
607 # possible, it may need encoding of some kind,
608@@ -1836,6 +2347,7 @@
609 mail.header("X-Mailer", "CVSspam #{$version} <http://www.badgers-in-foil.co.uk/projects/cvsspam/>")
610
611 mail.body do |body|
612- make_html_email(body)
613+ make_text_email(body, boundary)
614+ make_html_email(body, boundary)
615 end
616 end
8b9b7edf
ER
617--- cvsspam.rb 2009-03-05 02:14:14.149660640 +0200
618+++ cvsspam.rb 2009-03-05 02:06:57.409693131 +0200
619@@ -1943,8 +1943,9 @@
620 # generate the email header (and footer) having already generated the diffs
621 # for the email body to a temp file (which is simply included in the middle)
622 def make_html_email(mail, boundary)
623+
624 mail.puts("--#{boundary}")
625- mail.puts("Content-Type: text/html;" + ($charset.nil? ? "" : "; charset=\"#{$charset}\""))
626+ mail.puts("Content-Type: text/html" + ($charset.nil? ? "" : "; charset=\"#{$charset}\""))
627 mail.puts("Content-Disposition: inline\n\n");
628 mail.puts(<<HEAD)
629 <html>
630@@ -2378,12 +2379,10 @@
631 end
632
633 def make_text_email(mail, boundary)
634- mail.puts(<<HEAD)
635---#{boundary}
636-Content-Type: text/plain; charset=us-ascii
637-Content-Disposition: inline
638-
639-HEAD
640+
641+ mail.puts("--#{boundary}")
642+ mail.puts("Content-Type: text/plain" + ($charset.nil? ? "" : "; charset=\"#{$charset}\""))
643+ mail.puts("Content-Disposition: inline\n\n");
644
645 haveTags = false
646 Repository.each do |repository|
39d8f878
ER
647--- cvsspam.rb 2009-03-05 02:40:37.616133405 +0200
648+++ cvsspam.rb 2009-03-05 02:38:55.616141228 +0200
649@@ -1375,15 +1375,15 @@
650 when "A"
651 print($frontend.textpath($file.basedir, $file.tag))
652 println("\n")
653- println("#{$file.file} added at #{$frontend.version($file.path,$file.toVer)}")
654+ println("#{$file.file} added at #{$file.toVer}")
655 when "R"
656 print($frontend.textpath($file.basedir, $file.tag))
657 println("\n")
658- println("#{$file.file} removed after #{$frontend.version($file.path,$file.fromVer)}")
659+ println("#{$file.file} removed after #{$file.fromVer}")
660 when "M"
661 print($frontend.textpath($file.basedir, $file.tag))
662 println("\n")
663- println("#{$file.file} #{$frontend.version($file.path,$file.fromVer)} #{$frontend.textdiff($file)} #{$frontend.version($file.path,$file.toVer)}")
664+ println("#{$file.file} #{$file.fromVer} #{$frontend.textdiff($file)} #{$file.toVer}")
665 end
666 lines.each do |line|
667 println(line)
668@@ -2475,11 +2475,11 @@
669 end
670 end
671 if file.addition?
672- mail.print("added #{$frontend.version(file.path,file.toVer)} ")
673+ mail.print("added #{file.toVer} ")
674 elsif file.removal?
675- mail.print("#{$frontend.version(file.path,file.fromVer)} removed ")
676+ mail.print("#{file.fromVer} removed ")
677 elsif file.modification?
678- mail.print("#{$frontend.version(file.path,file.fromVer)} #{$frontend.textdiff(file)} #{$frontend.version(file.path,file.toVer)} ")
679+ mail.print("#{file.fromVer} #{$frontend.textdiff(file)} #{file.toVer} ")
680 end
681
682 mail.puts("\n")
f3fbd1ca
ER
683--- cvsspam.rb 2009-03-05 19:26:38.950116565 +0200
684+++ cvsspam.rb 2009-03-05 16:29:57.947827866 +0200
685@@ -1862,12 +1862,16 @@
686 "T" => tagHandler,
687 "A" => TextAddedFileHandler.new,
688 "R" => TextRemovedFileHandler.new,
689+ "C" => CopiedFileHandler.new,
690 "M" => TextModifiedFileHandler.new,
691+ "P" => ModifiedPropsFileHandler.new,
692 "V" => VersionHandler.new]
693
694 $texthandlers["A"].setTagHandler(tagHandler)
695 $texthandlers["R"].setTagHandler(tagHandler)
696+$texthandlers["C"].setTagHandler(tagHandler)
697 $texthandlers["M"].setTagHandler(tagHandler)
698+$texthandlers["P"].setTagHandler(tagHandler)
699
700 $fileEntries = Array.new
701 $task_list = Array.new
This page took 0.115347 seconds and 4 git commands to generate.