7 AWK is great. All hail AWK!
9 First, some utility functions. parsehdr extracts the number of lines
10 (old or new) in the given hunk header line.
12 function parsehdr(s) {
13 s = gensub(".*,", "", 1, s)
14 s = gensub("^-", "", 1, s)
18 filename extracts the name of the file from a "+++ path" or "--- path"
21 function filename(s) {
22 s = gensub("^... ", "", 1, s)
24 These lines have an optional tab followed by extra informations (the
25 date for example) that needs to be removed too.
27 s = gensub("\t.*", "", 1, s)
31 Switches the current file to the one provided. It's a great place where
32 accumulate part of the summary showed at the end and to reset the
35 function switchfile(newfile) {
37 summary = sprintf("%s%4d+ %4d-\t%s\n",
38 summary, add, rem, file)
46 Now, the real "parser". Initialize the state to "out" since we're
47 looking for the start of a diff.
53 Parse the changed file.
55 state == "out" && /^\+\+\+ / {
57 if (nfile == "/dev/null") {
59 When deleting a file, the name will be "/dev/null", but it's not a great
60 name for the stats. Let's use the "old" name instead.
69 Similarly, extract the "old" file name for when it's needed.
71 state == "out" && /^--- / {
72 delfile = filename($0)
75 Match the start of a hunk
77 state == "out" && /^@@ / {
79 This part is a bit complicated, but all it does is extracting the number
80 of "new" and "old" lines showed in the hunk. A hunk header looks like this
81 (except for the initial '#' character)
83 # @@ -55,7 +55,19 @@ ...
85 So first extract the text inside the pair of "@@"
87 s = gensub("@@ ", "", 1)
88 s = gensub(" @@.*", "", 1, s)
90 and then parse each number.
92 old = gensub(" .*", "", 1, s)
95 new = gensub(".* ", "", 1, s)
98 Don't forget to switch the state of the parser, now we're reading a
104 Keep count of the added and removed line. Also, decrement the "old" and
105 "new" lines when needed, to know when we're done with the hunk.
107 state == "in" && /^ / {
112 state == "in" && /^-/ {
118 state == "in" && /^\+/ {
124 When there are no more "new" and "old" lines to read, go back to the
125 "out" state, ready to read another hunk or another file.
127 state == "in" && old <= 0 && new <= 0 {
131 Don't be a sink! Continue the pipeline so we can further save or apply
136 At the end, print the stats.
140 It's better to flush the output here, otherwise the stats (printed to
141 stderr and unbuffered) may be interleaved with the output (on stdout,
146 Generate the stat summary for the last processed file
150 Print the stat to the standard error, to avoid "changing" the patch.
152 Unfortunately, there doesn't seem to be a "built-in" way of printing to
153 stderr other than using the pseudo-device "/dev/stderr".
155 printf("%s", summary) > "/dev/stderr"
156 printf("%4d+ %4d-\ttotal\n", totadd, totrem) > "/dev/stderr"
161 * cvs -q diff | diffstat > /tmp/diff
162 * got diff | diffstat | ssh foo 'cd xyz && got patch'