Print this page
4389 webrev should fetch preparer info from git if available
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/tools/scripts/webrev.sh
+++ new/usr/src/tools/scripts/webrev.sh
1 1 #!/usr/bin/ksh93 -p
2 2 #
3 3 # CDDL HEADER START
4 4 #
5 5 # The contents of this file are subject to the terms of the
6 6 # Common Development and Distribution License (the "License").
7 7 # You may not use this file except in compliance with the License.
8 8 #
9 9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 10 # or http://www.opensolaris.org/os/licensing.
11 11 # See the License for the specific language governing permissions
12 12 # and limitations under the License.
13 13 #
14 14 # When distributing Covered Code, include this CDDL HEADER in each
15 15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 16 # If applicable, add the following below this CDDL HEADER, with the
17 17 # fields enclosed by brackets "[]" replaced with your own identifying
18 18 # information: Portions Copyright [yyyy] [name of copyright owner]
↓ open down ↓ |
18 lines elided |
↑ open up ↑ |
19 19 #
20 20 # CDDL HEADER END
21 21 #
22 22
23 23 #
24 24 # Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
25 25 #
26 26
27 27 # Copyright 2008, 2010, Richard Lowe
28 28 # Copyright 2012 Marcel Telka <marcel@telka.sk>
29 +# Copyright 2013 Ivan Richwalski <ivan@seppuku.net>
29 30
30 31 #
31 32 # This script takes a file list and a workspace and builds a set of html files
32 33 # suitable for doing a code review of source changes via a web page.
33 34 # Documentation is available via the manual page, webrev.1, or just
34 35 # type 'webrev -h'.
35 36 #
36 37 # Acknowledgements to contributors to webrev are listed in the webrev(1)
37 38 # man page.
38 39 #
39 40
40 41 REMOVED_COLOR=brown
41 42 CHANGED_COLOR=blue
42 43 NEW_COLOR=blue
43 44
44 45 HTML='<?xml version="1.0"?>
45 46 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
46 47 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
47 48 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
48 49
49 50 FRAMEHTML='<?xml version="1.0"?>
50 51 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
51 52 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
52 53 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
53 54
54 55 STDHEAD='<meta http-equiv="cache-control" content="no-cache"></meta>
55 56 <meta http-equiv="Pragma" content="no-cache"></meta>
56 57 <meta http-equiv="Expires" content="-1"></meta>
57 58 <!--
58 59 Note to customizers: the body of the webrev is IDed as SUNWwebrev
59 60 to allow easy overriding by users of webrev via the userContent.css
60 61 mechanism available in some browsers.
61 62
62 63 For example, to have all "removed" information be red instead of
63 64 brown, set a rule in your userContent.css file like:
64 65
65 66 body#SUNWwebrev span.removed { color: red ! important; }
66 67 -->
67 68 <style type="text/css" media="screen">
68 69 body {
69 70 background-color: #eeeeee;
70 71 }
71 72 hr {
72 73 border: none 0;
73 74 border-top: 1px solid #aaa;
74 75 height: 1px;
75 76 }
76 77 div.summary {
77 78 font-size: .8em;
78 79 border-bottom: 1px solid #aaa;
79 80 padding-left: 1em;
80 81 padding-right: 1em;
81 82 }
82 83 div.summary h2 {
83 84 margin-bottom: 0.3em;
84 85 }
85 86 div.summary table th {
86 87 text-align: right;
87 88 vertical-align: top;
88 89 white-space: nowrap;
89 90 }
90 91 span.lineschanged {
91 92 font-size: 0.7em;
92 93 }
93 94 span.oldmarker {
94 95 color: red;
95 96 font-size: large;
96 97 font-weight: bold;
97 98 }
98 99 span.newmarker {
99 100 color: green;
100 101 font-size: large;
101 102 font-weight: bold;
102 103 }
103 104 span.removed {
104 105 color: brown;
105 106 }
106 107 span.changed {
107 108 color: blue;
108 109 }
109 110 span.new {
110 111 color: blue;
111 112 font-weight: bold;
112 113 }
113 114 span.chmod {
114 115 font-size: 0.7em;
115 116 color: #db7800;
116 117 }
117 118 a.print { font-size: x-small; }
118 119 a:hover { background-color: #ffcc99; }
119 120 </style>
120 121
121 122 <style type="text/css" media="print">
122 123 pre { font-size: 0.8em; font-family: courier, monospace; }
123 124 span.removed { color: #444; font-style: italic }
124 125 span.changed { font-weight: bold; }
125 126 span.new { font-weight: bold; }
126 127 span.newmarker { font-size: 1.2em; font-weight: bold; }
127 128 span.oldmarker { font-size: 1.2em; font-weight: bold; }
128 129 a.print {display: none}
129 130 hr { border: none 0; border-top: 1px solid #aaa; height: 1px; }
130 131 </style>
131 132 '
132 133
133 134 #
134 135 # UDiffs need a slightly different CSS rule for 'new' items (we don't
135 136 # want them to be bolded as we do in cdiffs or sdiffs).
136 137 #
137 138 UDIFFCSS='
138 139 <style type="text/css" media="screen">
139 140 span.new {
140 141 color: blue;
141 142 font-weight: normal;
142 143 }
143 144 </style>
144 145 '
145 146
146 147 #
147 148 # Display remote target with prefix and trailing slash.
148 149 #
149 150 function print_upload_header
150 151 {
151 152 typeset -r prefix=$1
152 153 typeset display_target
153 154
154 155 if [[ -z $tflag ]]; then
155 156 display_target=${prefix}${remote_target}
156 157 else
157 158 display_target=${remote_target}
158 159 fi
159 160
160 161 if [[ ${display_target} != */ ]]; then
161 162 display_target=${display_target}/
162 163 fi
163 164
164 165 print " Upload to: ${display_target}\n" \
165 166 " Uploading: \c"
166 167 }
167 168
168 169 #
169 170 # Upload the webrev via rsync. Return 0 on success, 1 on error.
170 171 #
171 172 function rsync_upload
172 173 {
173 174 if (( $# != 2 )); then
174 175 print "\nERROR: rsync_upload: wrong usage ($#)"
175 176 exit 1
176 177 fi
177 178
178 179 typeset -r dst=$1
179 180 integer -r print_err_msg=$2
180 181
181 182 print_upload_header ${rsync_prefix}
182 183 print "rsync ... \c"
183 184 typeset -r err_msg=$( $MKTEMP /tmp/rsync_err.XXXXXX )
184 185 if [[ -z $err_msg ]]; then
185 186 print "\nERROR: rsync_upload: cannot create temporary file"
186 187 return 1
187 188 fi
188 189 #
189 190 # The source directory must end with a slash in order to copy just
190 191 # directory contents, not the whole directory.
191 192 #
192 193 typeset src_dir=$WDIR
193 194 if [[ ${src_dir} != */ ]]; then
194 195 src_dir=${src_dir}/
195 196 fi
196 197 $RSYNC -r -q ${src_dir} $dst 2>$err_msg
197 198 if (( $? != 0 )); then
198 199 if (( ${print_err_msg} > 0 )); then
199 200 print "Failed.\nERROR: rsync failed"
200 201 print "src dir: '${src_dir}'\ndst dir: '$dst'"
201 202 print "error messages:"
202 203 $SED 's/^/> /' $err_msg
203 204 rm -f $err_msg
204 205 fi
205 206 return 1
206 207 fi
207 208
208 209 rm -f $err_msg
209 210 print "Done."
210 211 return 0
211 212 }
212 213
213 214 #
214 215 # Create directories on remote host using SFTP. Return 0 on success,
215 216 # 1 on failure.
216 217 #
217 218 function remote_mkdirs
218 219 {
219 220 typeset -r dir_spec=$1
220 221 typeset -r host_spec=$2
221 222
222 223 #
223 224 # If the supplied path is absolute we assume all directories are
224 225 # created, otherwise try to create all directories in the path
225 226 # except the last one which will be created by scp.
226 227 #
227 228 if [[ "${dir_spec}" == */* && "${dir_spec}" != /* ]]; then
228 229 print "mkdirs \c"
229 230 #
230 231 # Remove the last directory from directory specification.
231 232 #
232 233 typeset -r dirs_mk=${dir_spec%/*}
233 234 typeset -r batch_file_mkdir=$( $MKTEMP \
234 235 /tmp/webrev_mkdir.XXXXXX )
235 236 if [[ -z $batch_file_mkdir ]]; then
236 237 print "\nERROR: remote_mkdirs:" \
237 238 "cannot create temporary file for batch file"
238 239 return 1
239 240 fi
240 241 OLDIFS=$IFS
241 242 IFS=/
242 243 typeset dir
243 244 for dir in ${dirs_mk}; do
244 245 #
245 246 # Use the '-' prefix to ignore mkdir errors in order
246 247 # to avoid an error in case the directory already
247 248 # exists. We check the directory with chdir to be sure
248 249 # there is one.
249 250 #
250 251 print -- "-mkdir ${dir}" >> ${batch_file_mkdir}
251 252 print "chdir ${dir}" >> ${batch_file_mkdir}
252 253 done
253 254 IFS=$OLDIFS
254 255 typeset -r sftp_err_msg=$( $MKTEMP /tmp/webrev_scp_err.XXXXXX )
255 256 if [[ -z ${sftp_err_msg} ]]; then
256 257 print "\nERROR: remote_mkdirs:" \
257 258 "cannot create temporary file for error messages"
258 259 return 1
259 260 fi
260 261 $SFTP -b ${batch_file_mkdir} ${host_spec} 2>${sftp_err_msg} 1>&2
261 262 if (( $? != 0 )); then
262 263 print "\nERROR: failed to create remote directories"
263 264 print "error messages:"
264 265 $SED 's/^/> /' ${sftp_err_msg}
265 266 rm -f ${sftp_err_msg} ${batch_file_mkdir}
266 267 return 1
267 268 fi
268 269 rm -f ${sftp_err_msg} ${batch_file_mkdir}
269 270 fi
270 271
271 272 return 0
272 273 }
273 274
274 275 #
275 276 # Upload the webrev via SSH. Return 0 on success, 1 on error.
276 277 #
277 278 function ssh_upload
278 279 {
279 280 if (( $# != 1 )); then
280 281 print "\nERROR: ssh_upload: wrong number of arguments"
281 282 exit 1
282 283 fi
283 284
284 285 typeset dst=$1
285 286 typeset -r host_spec=${dst%%:*}
286 287 typeset -r dir_spec=${dst#*:}
287 288
288 289 #
289 290 # Display the upload information before calling delete_webrev
290 291 # because it will also print its progress.
291 292 #
292 293 print_upload_header ${ssh_prefix}
293 294
294 295 #
295 296 # If the deletion was explicitly requested there is no need
296 297 # to perform it again.
297 298 #
298 299 if [[ -z $Dflag ]]; then
299 300 #
300 301 # We do not care about return value because this might be
301 302 # the first time this directory is uploaded.
302 303 #
303 304 delete_webrev 0
304 305 fi
305 306
306 307 #
307 308 # Create remote directories. Any error reporting will be done
308 309 # in remote_mkdirs function.
309 310 #
310 311 remote_mkdirs ${dir_spec} ${host_spec}
311 312 if (( $? != 0 )); then
312 313 return 1
313 314 fi
314 315
315 316 print "upload ... \c"
316 317 typeset -r scp_err_msg=$( $MKTEMP /tmp/scp_err.XXXXXX )
317 318 if [[ -z ${scp_err_msg} ]]; then
318 319 print "\nERROR: ssh_upload:" \
319 320 "cannot create temporary file for error messages"
320 321 return 1
321 322 fi
322 323 $SCP -q -C -B -o PreferredAuthentications=publickey -r \
323 324 $WDIR $dst 2>${scp_err_msg}
324 325 if (( $? != 0 )); then
325 326 print "Failed.\nERROR: scp failed"
326 327 print "src dir: '$WDIR'\ndst dir: '$dst'"
327 328 print "error messages:"
328 329 $SED 's/^/> /' ${scp_err_msg}
329 330 rm -f ${scp_err_msg}
330 331 return 1
331 332 fi
332 333
333 334 rm -f ${scp_err_msg}
334 335 print "Done."
335 336 return 0
336 337 }
337 338
338 339 #
339 340 # Delete webrev at remote site. Return 0 on success, 1 or exit code from sftp
340 341 # on failure. If first argument is 1 then perform the check of sftp return
341 342 # value otherwise ignore it. If second argument is present it means this run
342 343 # only performs deletion.
343 344 #
344 345 function delete_webrev
345 346 {
346 347 if (( $# < 1 )); then
347 348 print "delete_webrev: wrong number of arguments"
348 349 exit 1
349 350 fi
350 351
351 352 integer -r check=$1
352 353 integer delete_only=0
353 354 if (( $# == 2 )); then
354 355 delete_only=1
355 356 fi
356 357
357 358 #
358 359 # Strip the transport specification part of remote target first.
359 360 #
360 361 typeset -r stripped_target=${remote_target##*://}
361 362 typeset -r host_spec=${stripped_target%%:*}
362 363 typeset -r dir_spec=${stripped_target#*:}
363 364 typeset dir_rm
364 365
365 366 #
366 367 # Do not accept an absolute path.
367 368 #
368 369 if [[ ${dir_spec} == /* ]]; then
369 370 return 1
370 371 fi
371 372
372 373 #
373 374 # Strip the ending slash.
374 375 #
375 376 if [[ ${dir_spec} == */ ]]; then
376 377 dir_rm=${dir_spec%%/}
377 378 else
378 379 dir_rm=${dir_spec}
379 380 fi
380 381
381 382 if (( ${delete_only} > 0 )); then
382 383 print " Removing: \c"
383 384 else
384 385 print "rmdir \c"
385 386 fi
386 387 if [[ -z "$dir_rm" ]]; then
387 388 print "\nERROR: empty directory for removal"
388 389 return 1
389 390 fi
390 391
391 392 #
392 393 # Prepare batch file.
393 394 #
394 395 typeset -r batch_file_rm=$( $MKTEMP /tmp/webrev_remove.XXXXXX )
395 396 if [[ -z $batch_file_rm ]]; then
396 397 print "\nERROR: delete_webrev: cannot create temporary file"
397 398 return 1
398 399 fi
399 400 print "rename $dir_rm $TRASH_DIR/removed.$$" > $batch_file_rm
400 401
401 402 #
402 403 # Perform remote deletion and remove the batch file.
403 404 #
404 405 typeset -r sftp_err_msg=$( $MKTEMP /tmp/webrev_scp_err.XXXXXX )
405 406 if [[ -z ${sftp_err_msg} ]]; then
406 407 print "\nERROR: delete_webrev:" \
407 408 "cannot create temporary file for error messages"
408 409 return 1
409 410 fi
410 411 $SFTP -b $batch_file_rm $host_spec 2>${sftp_err_msg} 1>&2
411 412 integer -r ret=$?
412 413 rm -f $batch_file_rm
413 414 if (( $ret != 0 && $check > 0 )); then
414 415 print "Failed.\nERROR: failed to remove remote directories"
415 416 print "error messages:"
416 417 $SED 's/^/> /' ${sftp_err_msg}
417 418 rm -f ${sftp_err_msg}
418 419 return $ret
419 420 fi
420 421 rm -f ${sftp_err_msg}
421 422 if (( ${delete_only} > 0 )); then
422 423 print "Done."
423 424 fi
424 425
425 426 return 0
426 427 }
427 428
428 429 #
429 430 # Upload webrev to remote site
430 431 #
431 432 function upload_webrev
432 433 {
433 434 integer ret
434 435
435 436 if [[ ! -d "$WDIR" ]]; then
436 437 print "\nERROR: webrev directory '$WDIR' does not exist"
437 438 return 1
438 439 fi
439 440
440 441 #
441 442 # Perform a late check to make sure we do not upload closed source
442 443 # to remote target when -n is used. If the user used custom remote
443 444 # target he probably knows what he is doing.
444 445 #
445 446 if [[ -n $nflag && -z $tflag ]]; then
446 447 $FIND $WDIR -type d -name closed \
447 448 | $GREP closed >/dev/null
448 449 if (( $? == 0 )); then
449 450 print "\nERROR: directory '$WDIR' contains" \
450 451 "\"closed\" directory"
451 452 return 1
452 453 fi
453 454 fi
454 455
455 456
456 457 #
457 458 # We have the URI for remote destination now so let's start the upload.
458 459 #
459 460 if [[ -n $tflag ]]; then
460 461 if [[ "${remote_target}" == ${rsync_prefix}?* ]]; then
461 462 rsync_upload ${remote_target##$rsync_prefix} 1
462 463 ret=$?
463 464 return $ret
464 465 elif [[ "${remote_target}" == ${ssh_prefix}?* ]]; then
465 466 ssh_upload ${remote_target##$ssh_prefix}
466 467 ret=$?
467 468 return $ret
468 469 fi
469 470 else
470 471 #
471 472 # Try rsync first and fallback to SSH in case it fails.
472 473 #
473 474 rsync_upload ${remote_target} 0
474 475 ret=$?
475 476 if (( $ret != 0 )); then
476 477 print "Failed. (falling back to SSH)"
477 478 ssh_upload ${remote_target}
478 479 ret=$?
479 480 fi
480 481 return $ret
481 482 fi
482 483 }
483 484
484 485 #
485 486 # input_cmd | url_encode | output_cmd
486 487 #
487 488 # URL-encode (percent-encode) reserved characters as defined in RFC 3986.
488 489 #
489 490 # Reserved characters are: :/?#[]@!$&'()*+,;=
490 491 #
491 492 # While not a reserved character itself, percent '%' is reserved by definition
492 493 # so encode it first to avoid recursive transformation, and skip '/' which is
493 494 # a path delimiter.
494 495 #
495 496 # The quotation character is deliberately not escaped in order to make
496 497 # the substitution work with GNU sed.
497 498 #
498 499 function url_encode
499 500 {
500 501 $SED -e "s|%|%25|g" -e "s|:|%3A|g" -e "s|\&|%26|g" \
501 502 -e "s|?|%3F|g" -e "s|#|%23|g" -e "s|\[|%5B|g" \
502 503 -e "s|*|%2A|g" -e "s|@|%40|g" -e "s|\!|%21|g" \
503 504 -e "s|=|%3D|g" -e "s|;|%3B|g" -e "s|\]|%5D|g" \
504 505 -e "s|(|%28|g" -e "s|)|%29|g" -e "s|'|%27|g" \
505 506 -e "s|+|%2B|g" -e "s|\,|%2C|g" -e "s|\\\$|%24|g"
506 507 }
507 508
508 509 #
509 510 # input_cmd | html_quote | output_cmd
510 511 # or
511 512 # html_quote filename | output_cmd
512 513 #
513 514 # Make a piece of source code safe for display in an HTML <pre> block.
514 515 #
515 516 html_quote()
516 517 {
517 518 $SED -e "s/&/\&/g" -e "s/</\</g" -e "s/>/\>/g" "$@" | expand
518 519 }
519 520
520 521 #
521 522 # Trim a digest-style revision to a conventionally readable yet useful length
522 523 #
523 524 trim_digest()
524 525 {
525 526 typeset digest=$1
526 527
527 528 echo $digest | $SED -e 's/\([0-9a-f]\{12\}\).*/\1/'
528 529 }
529 530
530 531 #
531 532 # input_cmd | its2url | output_cmd
532 533 #
533 534 # Scan for information tracking system references and insert <a> links to the
534 535 # relevant databases.
535 536 #
536 537 its2url()
537 538 {
538 539 $SED -f ${its_sed_script}
539 540 }
540 541
541 542 #
542 543 # strip_unchanged <infile> | output_cmd
543 544 #
544 545 # Removes chunks of sdiff documents that have not changed. This makes it
545 546 # easier for a code reviewer to find the bits that have changed.
546 547 #
547 548 # Deleted lines of text are replaced by a horizontal rule. Some
548 549 # identical lines are retained before and after the changed lines to
549 550 # provide some context. The number of these lines is controlled by the
550 551 # variable C in the $AWK script below.
551 552 #
552 553 # The script detects changed lines as any line that has a "<span class="
553 554 # string embedded (unchanged lines have no particular class and are not
554 555 # part of a <span>). Blank lines (without a sequence number) are also
555 556 # detected since they flag lines that have been inserted or deleted.
556 557 #
557 558 strip_unchanged()
558 559 {
559 560 $AWK '
560 561 BEGIN { C = c = 20 }
561 562 NF == 0 || /<span class="/ {
562 563 if (c > C) {
563 564 c -= C
564 565 inx = 0
565 566 if (c > C) {
566 567 print "\n</pre><hr></hr><pre>"
567 568 inx = c % C
568 569 c = C
569 570 }
570 571
571 572 for (i = 0; i < c; i++)
572 573 print ln[(inx + i) % C]
573 574 }
574 575 c = 0;
575 576 print
576 577 next
577 578 }
578 579 { if (c >= C) {
579 580 ln[c % C] = $0
580 581 c++;
581 582 next;
582 583 }
583 584 c++;
584 585 print
585 586 }
586 587 END { if (c > (C * 2)) print "\n</pre><hr></hr>" }
587 588
588 589 ' $1
589 590 }
590 591
591 592 #
592 593 # sdiff_to_html
593 594 #
594 595 # This function takes two files as arguments, obtains their diff, and
595 596 # processes the diff output to present the files as an HTML document with
596 597 # the files displayed side-by-side, differences shown in color. It also
597 598 # takes a delta comment, rendered as an HTML snippet, as the third
598 599 # argument. The function takes two files as arguments, then the name of
599 600 # file, the path, and the comment. The HTML will be delivered on stdout,
600 601 # e.g.
601 602 #
602 603 # $ sdiff_to_html old/usr/src/tools/scripts/webrev.sh \
603 604 # new/usr/src/tools/scripts/webrev.sh \
604 605 # webrev.sh usr/src/tools/scripts \
605 606 # '<a href="http://monaco.sfbay.sun.com/detail.jsp?cr=1234567">
606 607 # 1234567</a> my bugid' > <file>.html
607 608 #
608 609 # framed_sdiff() is then called which creates $2.frames.html
609 610 # in the webrev tree.
610 611 #
611 612 # FYI: This function is rather unusual in its use of awk. The initial
612 613 # diff run produces conventional diff output showing changed lines mixed
613 614 # with editing codes. The changed lines are ignored - we're interested in
614 615 # the editing codes, e.g.
615 616 #
616 617 # 8c8
617 618 # 57a61
618 619 # 63c66,76
619 620 # 68,93d80
620 621 # 106d90
621 622 # 108,110d91
622 623 #
623 624 # These editing codes are parsed by the awk script and used to generate
624 625 # another awk script that generates HTML, e.g the above lines would turn
625 626 # into something like this:
626 627 #
627 628 # BEGIN { printf "<pre>\n" }
628 629 # function sp(n) {for (i=0;i<n;i++)printf "\n"}
629 630 # function wl(n) {printf "<font color=%s>%4d %s </font>\n", n, NR, $0}
630 631 # NR==8 {wl("#7A7ADD");next}
631 632 # NR==54 {wl("#7A7ADD");sp(3);next}
632 633 # NR==56 {wl("#7A7ADD");next}
633 634 # NR==57 {wl("black");printf "\n"; next}
634 635 # : :
635 636 #
636 637 # This script is then run on the original source file to generate the
637 638 # HTML that corresponds to the source file.
638 639 #
639 640 # The two HTML files are then combined into a single piece of HTML that
640 641 # uses an HTML table construct to present the files side by side. You'll
641 642 # notice that the changes are color-coded:
642 643 #
643 644 # black - unchanged lines
644 645 # blue - changed lines
645 646 # bold blue - new lines
646 647 # brown - deleted lines
647 648 #
648 649 # Blank lines are inserted in each file to keep unchanged lines in sync
649 650 # (side-by-side). This format is familiar to users of sdiff(1) or
650 651 # Teamware's filemerge tool.
651 652 #
652 653 sdiff_to_html()
653 654 {
654 655 diff -b $1 $2 > /tmp/$$.diffs
655 656
656 657 TNAME=$3
657 658 TPATH=$4
658 659 COMMENT=$5
659 660
660 661 #
661 662 # Now we have the diffs, generate the HTML for the old file.
662 663 #
663 664 $AWK '
664 665 BEGIN {
665 666 printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
666 667 printf "function removed() "
667 668 printf "{printf \"<span class=\\\"removed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
668 669 printf "function changed() "
669 670 printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
670 671 printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
671 672 }
672 673 /^</ {next}
673 674 /^>/ {next}
674 675 /^---/ {next}
675 676
676 677 {
677 678 split($1, a, /[cad]/) ;
678 679 if (index($1, "a")) {
679 680 if (a[1] == 0) {
680 681 n = split(a[2], r, /,/);
681 682 if (n == 1)
682 683 printf "BEGIN\t\t{sp(1)}\n"
683 684 else
684 685 printf "BEGIN\t\t{sp(%d)}\n",\
685 686 (r[2] - r[1]) + 1
686 687 next
687 688 }
688 689
689 690 printf "NR==%s\t\t{", a[1]
690 691 n = split(a[2], r, /,/);
691 692 s = r[1];
692 693 if (n == 1)
693 694 printf "bl();printf \"\\n\"; next}\n"
694 695 else {
695 696 n = r[2] - r[1]
696 697 printf "bl();sp(%d);next}\n",\
697 698 (r[2] - r[1]) + 1
698 699 }
699 700 next
700 701 }
701 702 if (index($1, "d")) {
702 703 n = split(a[1], r, /,/);
703 704 n1 = r[1]
704 705 n2 = r[2]
705 706 if (n == 1)
706 707 printf "NR==%s\t\t{removed(); next}\n" , n1
707 708 else
708 709 printf "NR==%s,NR==%s\t{removed(); next}\n" , n1, n2
709 710 next
710 711 }
711 712 if (index($1, "c")) {
712 713 n = split(a[1], r, /,/);
713 714 n1 = r[1]
714 715 n2 = r[2]
715 716 final = n2
716 717 d1 = 0
717 718 if (n == 1)
718 719 printf "NR==%s\t\t{changed();" , n1
719 720 else {
720 721 d1 = n2 - n1
721 722 printf "NR==%s,NR==%s\t{changed();" , n1, n2
722 723 }
723 724 m = split(a[2], r, /,/);
724 725 n1 = r[1]
725 726 n2 = r[2]
726 727 if (m > 1) {
727 728 d2 = n2 - n1
728 729 if (d2 > d1) {
729 730 if (n > 1) printf "if (NR==%d)", final
730 731 printf "sp(%d);", d2 - d1
731 732 }
732 733 }
733 734 printf "next}\n" ;
734 735
735 736 next
736 737 }
737 738 }
738 739
739 740 END { printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
740 741 ' /tmp/$$.diffs > /tmp/$$.file1
741 742
742 743 #
743 744 # Now generate the HTML for the new file
744 745 #
745 746 $AWK '
746 747 BEGIN {
747 748 printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
748 749 printf "function new() "
749 750 printf "{printf \"<span class=\\\"new\\\">%%4d %%s</span>\\n\", NR, $0}\n"
750 751 printf "function changed() "
751 752 printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
752 753 printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
753 754 }
754 755
755 756 /^</ {next}
756 757 /^>/ {next}
757 758 /^---/ {next}
758 759
759 760 {
760 761 split($1, a, /[cad]/) ;
761 762 if (index($1, "d")) {
762 763 if (a[2] == 0) {
763 764 n = split(a[1], r, /,/);
764 765 if (n == 1)
765 766 printf "BEGIN\t\t{sp(1)}\n"
766 767 else
767 768 printf "BEGIN\t\t{sp(%d)}\n",\
768 769 (r[2] - r[1]) + 1
769 770 next
770 771 }
771 772
772 773 printf "NR==%s\t\t{", a[2]
773 774 n = split(a[1], r, /,/);
774 775 s = r[1];
775 776 if (n == 1)
776 777 printf "bl();printf \"\\n\"; next}\n"
777 778 else {
778 779 n = r[2] - r[1]
779 780 printf "bl();sp(%d);next}\n",\
780 781 (r[2] - r[1]) + 1
781 782 }
782 783 next
783 784 }
784 785 if (index($1, "a")) {
785 786 n = split(a[2], r, /,/);
786 787 n1 = r[1]
787 788 n2 = r[2]
788 789 if (n == 1)
789 790 printf "NR==%s\t\t{new() ; next}\n" , n1
790 791 else
791 792 printf "NR==%s,NR==%s\t{new() ; next}\n" , n1, n2
792 793 next
793 794 }
794 795 if (index($1, "c")) {
795 796 n = split(a[2], r, /,/);
796 797 n1 = r[1]
797 798 n2 = r[2]
798 799 final = n2
799 800 d2 = 0;
800 801 if (n == 1) {
801 802 final = n1
802 803 printf "NR==%s\t\t{changed();" , n1
803 804 } else {
804 805 d2 = n2 - n1
805 806 printf "NR==%s,NR==%s\t{changed();" , n1, n2
806 807 }
807 808 m = split(a[1], r, /,/);
808 809 n1 = r[1]
809 810 n2 = r[2]
810 811 if (m > 1) {
811 812 d1 = n2 - n1
812 813 if (d1 > d2) {
813 814 if (n > 1) printf "if (NR==%d)", final
814 815 printf "sp(%d);", d1 - d2
815 816 }
816 817 }
817 818 printf "next}\n" ;
818 819 next
819 820 }
820 821 }
821 822 END { printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
822 823 ' /tmp/$$.diffs > /tmp/$$.file2
823 824
824 825 #
825 826 # Post-process the HTML files by running them back through $AWK
826 827 #
827 828 html_quote < $1 | $AWK -f /tmp/$$.file1 > /tmp/$$.file1.html
828 829
829 830 html_quote < $2 | $AWK -f /tmp/$$.file2 > /tmp/$$.file2.html
830 831
831 832 #
832 833 # Now combine into a valid HTML file and side-by-side into a table
833 834 #
834 835 print "$HTML<head>$STDHEAD"
835 836 print "<title>$WNAME Sdiff $TPATH/$TNAME</title>"
836 837 print "</head><body id=\"SUNWwebrev\">"
837 838 print "<a class=\"print\" href=\"javascript:print()\">Print this page</a>"
838 839 print "<pre>$COMMENT</pre>\n"
839 840 print "<table><tr valign=\"top\">"
840 841 print "<td><pre>"
841 842
842 843 strip_unchanged /tmp/$$.file1.html
843 844
844 845 print "</pre></td><td><pre>"
845 846
846 847 strip_unchanged /tmp/$$.file2.html
847 848
848 849 print "</pre></td>"
849 850 print "</tr></table>"
850 851 print "</body></html>"
851 852
852 853 framed_sdiff $TNAME $TPATH /tmp/$$.file1.html /tmp/$$.file2.html \
853 854 "$COMMENT"
854 855 }
855 856
856 857
857 858 #
858 859 # framed_sdiff <filename> <filepath> <lhsfile> <rhsfile> <comment>
859 860 #
860 861 # Expects lefthand and righthand side html files created by sdiff_to_html.
861 862 # We use insert_anchors() to augment those with HTML navigation anchors,
862 863 # and then emit the main frame. Content is placed into:
863 864 #
864 865 # $WDIR/DIR/$TNAME.lhs.html
865 866 # $WDIR/DIR/$TNAME.rhs.html
866 867 # $WDIR/DIR/$TNAME.frames.html
867 868 #
868 869 # NOTE: We rely on standard usage of $WDIR and $DIR.
869 870 #
870 871 function framed_sdiff
871 872 {
872 873 typeset TNAME=$1
873 874 typeset TPATH=$2
874 875 typeset lhsfile=$3
875 876 typeset rhsfile=$4
876 877 typeset comments=$5
877 878 typeset RTOP
878 879
879 880 # Enable html files to access WDIR via a relative path.
880 881 RTOP=$(relative_dir $TPATH $WDIR)
881 882
882 883 # Make the rhs/lhs files and output the frameset file.
883 884 print "$HTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.lhs.html
884 885
885 886 cat >> $WDIR/$DIR/$TNAME.lhs.html <<-EOF
886 887 <script type="text/javascript" src="${RTOP}ancnav.js"></script>
887 888 </head>
888 889 <body id="SUNWwebrev" onkeypress="keypress(event);">
889 890 <a name="0"></a>
890 891 <pre>$comments</pre><hr></hr>
891 892 EOF
892 893
893 894 cp $WDIR/$DIR/$TNAME.lhs.html $WDIR/$DIR/$TNAME.rhs.html
894 895
895 896 insert_anchors $lhsfile >> $WDIR/$DIR/$TNAME.lhs.html
896 897 insert_anchors $rhsfile >> $WDIR/$DIR/$TNAME.rhs.html
897 898
898 899 close='</body></html>'
899 900
900 901 print $close >> $WDIR/$DIR/$TNAME.lhs.html
901 902 print $close >> $WDIR/$DIR/$TNAME.rhs.html
902 903
903 904 print "$FRAMEHTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.frames.html
904 905 print "<title>$WNAME Framed-Sdiff " \
905 906 "$TPATH/$TNAME</title> </head>" >> $WDIR/$DIR/$TNAME.frames.html
906 907 cat >> $WDIR/$DIR/$TNAME.frames.html <<-EOF
907 908 <frameset rows="*,60">
908 909 <frameset cols="50%,50%">
909 910 <frame src="$TNAME.lhs.html" scrolling="auto" name="lhs"></frame>
910 911 <frame src="$TNAME.rhs.html" scrolling="auto" name="rhs"></frame>
911 912 </frameset>
912 913 <frame src="${RTOP}ancnav.html" scrolling="no" marginwidth="0"
913 914 marginheight="0" name="nav"></frame>
914 915 <noframes>
915 916 <body id="SUNWwebrev">
916 917 Alas 'frames' webrev requires that your browser supports frames
917 918 and has the feature enabled.
918 919 </body>
919 920 </noframes>
920 921 </frameset>
921 922 </html>
922 923 EOF
923 924 }
924 925
925 926
926 927 #
927 928 # fix_postscript
928 929 #
929 930 # Merge codereview output files to a single conforming postscript file, by:
930 931 # - removing all extraneous headers/trailers
931 932 # - making the page numbers right
932 933 # - removing pages devoid of contents which confuse some
933 934 # postscript readers.
934 935 #
935 936 # From Casper.
936 937 #
937 938 function fix_postscript
938 939 {
939 940 infile=$1
940 941
941 942 cat > /tmp/$$.crmerge.pl << \EOF
942 943
943 944 print scalar(<>); # %!PS-Adobe---
944 945 print "%%Orientation: Landscape\n";
945 946
946 947 $pno = 0;
947 948 $doprint = 1;
948 949
949 950 $page = "";
950 951
951 952 while (<>) {
952 953 next if (/^%%Pages:\s*\d+/);
953 954
954 955 if (/^%%Page:/) {
955 956 if ($pno == 0 || $page =~ /\)S/) {
956 957 # Header or single page containing text
957 958 print "%%Page: ? $pno\n" if ($pno > 0);
958 959 print $page;
959 960 $pno++;
960 961 } else {
961 962 # Empty page, skip it.
962 963 }
963 964 $page = "";
964 965 $doprint = 1;
965 966 next;
966 967 }
967 968
968 969 # Skip from %%Trailer of one document to Endprolog
969 970 # %%Page of the next
970 971 $doprint = 0 if (/^%%Trailer/);
971 972 $page .= $_ if ($doprint);
972 973 }
973 974
974 975 if ($page =~ /\)S/) {
975 976 print "%%Page: ? $pno\n";
976 977 print $page;
977 978 } else {
978 979 $pno--;
979 980 }
980 981 print "%%Trailer\n%%Pages: $pno\n";
981 982 EOF
982 983
983 984 $PERL /tmp/$$.crmerge.pl < $infile
984 985 }
985 986
986 987
987 988 #
988 989 # input_cmd | insert_anchors | output_cmd
989 990 #
990 991 # Flag blocks of difference with sequentially numbered invisible
991 992 # anchors. These are used to drive the frames version of the
992 993 # sdiffs output.
993 994 #
994 995 # NOTE: Anchor zero flags the top of the file irrespective of changes,
995 996 # an additional anchor is also appended to flag the bottom.
996 997 #
997 998 # The script detects changed lines as any line that has a "<span
998 999 # class=" string embedded (unchanged lines have no class set and are
999 1000 # not part of a <span>. Blank lines (without a sequence number)
1000 1001 # are also detected since they flag lines that have been inserted or
1001 1002 # deleted.
1002 1003 #
1003 1004 function insert_anchors
1004 1005 {
1005 1006 $AWK '
1006 1007 function ia() {
1007 1008 printf "<a name=\"%d\" id=\"anc%d\"></a>", anc, anc++;
1008 1009 }
1009 1010
1010 1011 BEGIN {
1011 1012 anc=1;
1012 1013 inblock=1;
1013 1014 printf "<pre>\n";
1014 1015 }
1015 1016 NF == 0 || /^<span class=/ {
1016 1017 if (inblock == 0) {
1017 1018 ia();
1018 1019 inblock=1;
1019 1020 }
1020 1021 print;
1021 1022 next;
1022 1023 }
1023 1024 {
1024 1025 inblock=0;
1025 1026 print;
1026 1027 }
1027 1028 END {
1028 1029 ia();
1029 1030
1030 1031 printf "<b style=\"font-size: large; color: red\">";
1031 1032 printf "--- EOF ---</b>"
1032 1033 for(i=0;i<8;i++) printf "\n\n\n\n\n\n\n\n\n\n";
1033 1034 printf "</pre>"
1034 1035 printf "<form name=\"eof\">";
1035 1036 printf "<input name=\"value\" value=\"%d\" " \
1036 1037 "type=\"hidden\"></input>", anc - 1;
1037 1038 printf "</form>";
1038 1039 }
1039 1040 ' $1
1040 1041 }
1041 1042
1042 1043
1043 1044 #
1044 1045 # relative_dir
1045 1046 #
1046 1047 # Print a relative return path from $1 to $2. For example if
1047 1048 # $1=/tmp/myreview/raw_files/usr/src/tools/scripts and $2=/tmp/myreview,
1048 1049 # this function would print "../../../../".
1049 1050 #
1050 1051 # In the event that $1 is not in $2 a warning is printed to stderr,
1051 1052 # and $2 is returned-- the result of this is that the resulting webrev
1052 1053 # is not relocatable.
1053 1054 #
1054 1055 function relative_dir
1055 1056 {
1056 1057 typeset cur="${1##$2?(/)}"
1057 1058
1058 1059 #
1059 1060 # If the first path was specified absolutely, and it does
1060 1061 # not start with the second path, it's an error.
1061 1062 #
1062 1063 if [[ "$cur" = "/${1#/}" ]]; then
1063 1064 # Should never happen.
1064 1065 print -u2 "\nWARNING: relative_dir: \"$1\" not relative "
1065 1066 print -u2 "to \"$2\". Check input paths. Framed webrev "
1066 1067 print -u2 "will not be relocatable!"
1067 1068 print $2
1068 1069 return
1069 1070 fi
1070 1071
1071 1072 #
1072 1073 # This is kind of ugly. The sed script will do the following:
1073 1074 #
1074 1075 # 1. Strip off a leading "." or "./": this is important to get
1075 1076 # the correct arcnav links for files in $WDIR.
1076 1077 # 2. Strip off a trailing "/": this is not strictly necessary,
1077 1078 # but is kind of nice, since it doesn't end up in "//" at
1078 1079 # the end of a relative path.
1079 1080 # 3. Replace all remaining sequences of non-"/" with "..": the
1080 1081 # assumption here is that each dirname represents another
1081 1082 # level of relative separation.
1082 1083 # 4. Append a trailing "/" only for non-empty paths: this way
1083 1084 # the caller doesn't need to duplicate this logic, and does
1084 1085 # not end up using $RTOP/file for files in $WDIR.
1085 1086 #
1086 1087 print $cur | $SED -e '{
1087 1088 s:^\./*::
1088 1089 s:/$::
1089 1090 s:[^/][^/]*:..:g
1090 1091 s:^\(..*\)$:\1/:
1091 1092 }'
1092 1093 }
1093 1094
1094 1095 #
1095 1096 # frame_nav_js
1096 1097 #
1097 1098 # Emit javascript for frame navigation
1098 1099 #
1099 1100 function frame_nav_js
1100 1101 {
1101 1102 cat << \EOF
1102 1103 var myInt;
1103 1104 var scrolling=0;
1104 1105 var sfactor = 3;
1105 1106 var scount=10;
1106 1107
1107 1108 function scrollByPix() {
1108 1109 if (scount<=0) {
1109 1110 sfactor*=1.2;
1110 1111 scount=10;
1111 1112 }
1112 1113 parent.lhs.scrollBy(0,sfactor);
1113 1114 parent.rhs.scrollBy(0,sfactor);
1114 1115 scount--;
1115 1116 }
1116 1117
1117 1118 function scrollToAnc(num) {
1118 1119
1119 1120 // Update the value of the anchor in the form which we use as
1120 1121 // storage for this value. setAncValue() will take care of
1121 1122 // correcting for overflow and underflow of the value and return
1122 1123 // us the new value.
1123 1124 num = setAncValue(num);
1124 1125
1125 1126 // Set location and scroll back a little to expose previous
1126 1127 // lines.
1127 1128 //
1128 1129 // Note that this could be improved: it is possible although
1129 1130 // complex to compute the x and y position of an anchor, and to
1130 1131 // scroll to that location directly.
1131 1132 //
1132 1133 parent.lhs.location.replace(parent.lhs.location.pathname + "#" + num);
1133 1134 parent.rhs.location.replace(parent.rhs.location.pathname + "#" + num);
1134 1135
1135 1136 parent.lhs.scrollBy(0,-30);
1136 1137 parent.rhs.scrollBy(0,-30);
1137 1138 }
1138 1139
1139 1140 function getAncValue()
1140 1141 {
1141 1142 return (parseInt(parent.nav.document.diff.real.value));
1142 1143 }
1143 1144
1144 1145 function setAncValue(val)
1145 1146 {
1146 1147 if (val <= 0) {
1147 1148 val = 0;
1148 1149 parent.nav.document.diff.real.value = val;
1149 1150 parent.nav.document.diff.display.value = "BOF";
1150 1151 return (val);
1151 1152 }
1152 1153
1153 1154 //
1154 1155 // The way we compute the max anchor value is to stash it
1155 1156 // inline in the left and right hand side pages-- it's the same
1156 1157 // on each side, so we pluck from the left.
1157 1158 //
1158 1159 maxval = parent.lhs.document.eof.value.value;
1159 1160 if (val < maxval) {
1160 1161 parent.nav.document.diff.real.value = val;
1161 1162 parent.nav.document.diff.display.value = val.toString();
1162 1163 return (val);
1163 1164 }
1164 1165
1165 1166 // this must be: val >= maxval
1166 1167 val = maxval;
1167 1168 parent.nav.document.diff.real.value = val;
1168 1169 parent.nav.document.diff.display.value = "EOF";
1169 1170 return (val);
1170 1171 }
1171 1172
1172 1173 function stopScroll() {
1173 1174 if (scrolling==1) {
1174 1175 clearInterval(myInt);
1175 1176 scrolling=0;
1176 1177 }
1177 1178 }
1178 1179
1179 1180 function startScroll() {
1180 1181 stopScroll();
1181 1182 scrolling=1;
1182 1183 myInt=setInterval("scrollByPix()",10);
1183 1184 }
1184 1185
1185 1186 function handlePress(b) {
1186 1187
1187 1188 switch (b) {
1188 1189 case 1 :
1189 1190 scrollToAnc(-1);
1190 1191 break;
1191 1192 case 2 :
1192 1193 scrollToAnc(getAncValue() - 1);
1193 1194 break;
1194 1195 case 3 :
1195 1196 sfactor=-3;
1196 1197 startScroll();
1197 1198 break;
1198 1199 case 4 :
1199 1200 sfactor=3;
1200 1201 startScroll();
1201 1202 break;
1202 1203 case 5 :
1203 1204 scrollToAnc(getAncValue() + 1);
1204 1205 break;
1205 1206 case 6 :
1206 1207 scrollToAnc(999999);
1207 1208 break;
1208 1209 }
1209 1210 }
1210 1211
1211 1212 function handleRelease(b) {
1212 1213 stopScroll();
1213 1214 }
1214 1215
1215 1216 function keypress(ev) {
1216 1217 var keynum;
1217 1218 var keychar;
1218 1219
1219 1220 if (window.event) { // IE
1220 1221 keynum = ev.keyCode;
1221 1222 } else if (ev.which) { // non-IE
1222 1223 keynum = ev.which;
1223 1224 }
1224 1225
1225 1226 keychar = String.fromCharCode(keynum);
1226 1227
1227 1228 if (keychar == "k") {
1228 1229 handlePress(2);
1229 1230 return (0);
1230 1231 } else if (keychar == "j" || keychar == " ") {
1231 1232 handlePress(5);
1232 1233 return (0);
1233 1234 }
1234 1235 return (1);
1235 1236 }
1236 1237
1237 1238 function ValidateDiffNum(){
1238 1239 val = parent.nav.document.diff.display.value;
1239 1240 if (val == "EOF") {
1240 1241 scrollToAnc(999999);
1241 1242 return;
1242 1243 }
1243 1244
1244 1245 if (val == "BOF") {
1245 1246 scrollToAnc(0);
1246 1247 return;
1247 1248 }
1248 1249
1249 1250 i=parseInt(val);
1250 1251 if (isNaN(i)) {
1251 1252 parent.nav.document.diff.display.value = getAncValue();
1252 1253 } else {
1253 1254 scrollToAnc(i);
1254 1255 }
1255 1256 return false;
1256 1257 }
1257 1258
1258 1259 EOF
1259 1260 }
1260 1261
1261 1262 #
1262 1263 # frame_navigation
1263 1264 #
1264 1265 # Output anchor navigation file for framed sdiffs.
1265 1266 #
1266 1267 function frame_navigation
1267 1268 {
1268 1269 print "$HTML<head>$STDHEAD"
1269 1270
1270 1271 cat << \EOF
1271 1272 <title>Anchor Navigation</title>
1272 1273 <meta http-equiv="Content-Script-Type" content="text/javascript">
1273 1274 <meta http-equiv="Content-Type" content="text/html">
1274 1275
1275 1276 <style type="text/css">
1276 1277 div.button td { padding-left: 5px; padding-right: 5px;
1277 1278 background-color: #eee; text-align: center;
1278 1279 border: 1px #444 outset; cursor: pointer; }
1279 1280 div.button a { font-weight: bold; color: black }
1280 1281 div.button td:hover { background: #ffcc99; }
1281 1282 </style>
1282 1283 EOF
1283 1284
1284 1285 print "<script type=\"text/javascript\" src=\"ancnav.js\"></script>"
1285 1286
1286 1287 cat << \EOF
1287 1288 </head>
1288 1289 <body id="SUNWwebrev" bgcolor="#eeeeee" onload="document.diff.real.focus();"
1289 1290 onkeypress="keypress(event);">
1290 1291 <noscript lang="javascript">
1291 1292 <center>
1292 1293 <p><big>Framed Navigation controls require Javascript</big><br></br>
1293 1294 Either this browser is incompatable or javascript is not enabled</p>
1294 1295 </center>
1295 1296 </noscript>
1296 1297 <table width="100%" border="0" align="center">
1297 1298 <tr>
1298 1299 <td valign="middle" width="25%">Diff navigation:
1299 1300 Use 'j' and 'k' for next and previous diffs; or use buttons
1300 1301 at right</td>
1301 1302 <td align="center" valign="top" width="50%">
1302 1303 <div class="button">
1303 1304 <table border="0" align="center">
1304 1305 <tr>
1305 1306 <td>
1306 1307 <a onMouseDown="handlePress(1);return true;"
1307 1308 onMouseUp="handleRelease(1);return true;"
1308 1309 onMouseOut="handleRelease(1);return true;"
1309 1310 onClick="return false;"
1310 1311 title="Go to Beginning Of file">BOF</a></td>
1311 1312 <td>
1312 1313 <a onMouseDown="handlePress(3);return true;"
1313 1314 onMouseUp="handleRelease(3);return true;"
1314 1315 onMouseOut="handleRelease(3);return true;"
1315 1316 title="Scroll Up: Press and Hold to accelerate"
1316 1317 onClick="return false;">Scroll Up</a></td>
1317 1318 <td>
1318 1319 <a onMouseDown="handlePress(2);return true;"
1319 1320 onMouseUp="handleRelease(2);return true;"
1320 1321 onMouseOut="handleRelease(2);return true;"
1321 1322 title="Go to previous Diff"
1322 1323 onClick="return false;">Prev Diff</a>
1323 1324 </td></tr>
1324 1325
1325 1326 <tr>
1326 1327 <td>
1327 1328 <a onMouseDown="handlePress(6);return true;"
1328 1329 onMouseUp="handleRelease(6);return true;"
1329 1330 onMouseOut="handleRelease(6);return true;"
1330 1331 onClick="return false;"
1331 1332 title="Go to End Of File">EOF</a></td>
1332 1333 <td>
1333 1334 <a onMouseDown="handlePress(4);return true;"
1334 1335 onMouseUp="handleRelease(4);return true;"
1335 1336 onMouseOut="handleRelease(4);return true;"
1336 1337 title="Scroll Down: Press and Hold to accelerate"
1337 1338 onClick="return false;">Scroll Down</a></td>
1338 1339 <td>
1339 1340 <a onMouseDown="handlePress(5);return true;"
1340 1341 onMouseUp="handleRelease(5);return true;"
1341 1342 onMouseOut="handleRelease(5);return true;"
1342 1343 title="Go to next Diff"
1343 1344 onClick="return false;">Next Diff</a></td>
1344 1345 </tr>
1345 1346 </table>
1346 1347 </div>
1347 1348 </td>
1348 1349 <th valign="middle" width="25%">
1349 1350 <form action="" name="diff" onsubmit="return ValidateDiffNum();">
1350 1351 <input name="display" value="BOF" size="8" type="text"></input>
1351 1352 <input name="real" value="0" size="8" type="hidden"></input>
1352 1353 </form>
1353 1354 </th>
1354 1355 </tr>
1355 1356 </table>
1356 1357 </body>
1357 1358 </html>
1358 1359 EOF
1359 1360 }
1360 1361
1361 1362
1362 1363
1363 1364 #
1364 1365 # diff_to_html <filename> <filepath> { U | C } <comment>
1365 1366 #
1366 1367 # Processes the output of diff to produce an HTML file representing either
1367 1368 # context or unified diffs.
1368 1369 #
1369 1370 diff_to_html()
1370 1371 {
1371 1372 TNAME=$1
1372 1373 TPATH=$2
1373 1374 DIFFTYPE=$3
1374 1375 COMMENT=$4
1375 1376
1376 1377 print "$HTML<head>$STDHEAD"
1377 1378 print "<title>$WNAME ${DIFFTYPE}diff $TPATH</title>"
1378 1379
1379 1380 if [[ $DIFFTYPE == "U" ]]; then
1380 1381 print "$UDIFFCSS"
1381 1382 fi
1382 1383
1383 1384 cat <<-EOF
1384 1385 </head>
1385 1386 <body id="SUNWwebrev">
1386 1387 <a class="print" href="javascript:print()">Print this page</a>
1387 1388 <pre>$COMMENT</pre>
1388 1389 <pre>
1389 1390 EOF
1390 1391
1391 1392 html_quote | $AWK '
1392 1393 /^--- new/ { next }
1393 1394 /^\+\+\+ new/ { next }
1394 1395 /^--- old/ { next }
1395 1396 /^\*\*\* old/ { next }
1396 1397 /^\*\*\*\*/ { next }
1397 1398 /^-------/ { printf "<center><h1>%s</h1></center>\n", $0; next }
1398 1399 /^\@\@.*\@\@$/ { printf "</pre><hr></hr><pre>\n";
1399 1400 printf "<span class=\"newmarker\">%s</span>\n", $0;
1400 1401 next}
1401 1402
1402 1403 /^\*\*\*/ { printf "<hr></hr><span class=\"oldmarker\">%s</span>\n", $0;
1403 1404 next}
1404 1405 /^---/ { printf "<span class=\"newmarker\">%s</span>\n", $0;
1405 1406 next}
1406 1407 /^\+/ {printf "<span class=\"new\">%s</span>\n", $0; next}
1407 1408 /^!/ {printf "<span class=\"changed\">%s</span>\n", $0; next}
1408 1409 /^-/ {printf "<span class=\"removed\">%s</span>\n", $0; next}
1409 1410 {printf "%s\n", $0; next}
1410 1411 '
1411 1412
1412 1413 print "</pre></body></html>\n"
1413 1414 }
1414 1415
1415 1416
1416 1417 #
1417 1418 # source_to_html { new | old } <filename>
1418 1419 #
1419 1420 # Process a plain vanilla source file to transform it into an HTML file.
1420 1421 #
1421 1422 source_to_html()
1422 1423 {
1423 1424 WHICH=$1
1424 1425 TNAME=$2
1425 1426
1426 1427 print "$HTML<head>$STDHEAD"
1427 1428 print "<title>$WNAME $WHICH $TNAME</title>"
1428 1429 print "<body id=\"SUNWwebrev\">"
1429 1430 print "<pre>"
1430 1431 html_quote | $AWK '{line += 1 ; printf "%4d %s\n", line, $0 }'
1431 1432 print "</pre></body></html>"
1432 1433 }
1433 1434
1434 1435 #
1435 1436 # comments_from_teamware {text|html} parent-file child-file
1436 1437 #
1437 1438 # Find the first delta in the child that's not in the parent. Get the
1438 1439 # newest delta from the parent, get all deltas from the child starting
1439 1440 # with that delta, and then get all info starting with the second oldest
1440 1441 # delta in that list (the first delta unique to the child).
1441 1442 #
1442 1443 # This code adapted from Bill Shannon's "spc" script
1443 1444 #
1444 1445 comments_from_teamware()
1445 1446 {
1446 1447 fmt=$1
1447 1448 pfile=$PWS/$2
1448 1449 cfile=$CWS/$3
1449 1450
1450 1451 if [[ ! -f $PWS/${2%/*}/SCCS/s.${2##*/} && -n $RWS ]]; then
1451 1452 pfile=$RWS/$2
1452 1453 fi
1453 1454
1454 1455 if [[ -f $pfile ]]; then
1455 1456 psid=$($SCCS prs -d:I: $pfile 2>/dev/null)
1456 1457 else
1457 1458 psid=1.1
1458 1459 fi
1459 1460
1460 1461 set -A sids $($SCCS prs -l -r$psid -d:I: $cfile 2>/dev/null)
1461 1462 N=${#sids[@]}
1462 1463
1463 1464 nawkprg='
1464 1465 /^COMMENTS:/ {p=1; continue}
1465 1466 /^D [0-9]+\.[0-9]+/ {printf "--- %s ---\n", $2; p=0; }
1466 1467 NF == 0u { continue }
1467 1468 {if (p==0) continue; print $0 }'
1468 1469
1469 1470 if [[ $N -ge 2 ]]; then
1470 1471 sid1=${sids[$((N-2))]} # Gets 2nd to last sid
1471 1472
1472 1473 if [[ $fmt == "text" ]]; then
1473 1474 $SCCS prs -l -r$sid1 $cfile 2>/dev/null | \
1474 1475 $AWK "$nawkprg"
1475 1476 return
1476 1477 fi
1477 1478
1478 1479 $SCCS prs -l -r$sid1 $cfile 2>/dev/null | \
1479 1480 html_quote | its2url | $AWK "$nawkprg"
1480 1481 fi
1481 1482 }
1482 1483
1483 1484 #
1484 1485 # comments_from_wx {text|html} filepath
1485 1486 #
1486 1487 # Given the pathname of a file, find its location in a "wx" active
1487 1488 # file list and print the following comment. Output is either text or
1488 1489 # HTML; if the latter, embedded bugids (sequence of 5 or more digits)
1489 1490 # are turned into URLs.
1490 1491 #
1491 1492 # This is also used with Mercurial and the file list provided by hg-active.
1492 1493 #
1493 1494 comments_from_wx()
1494 1495 {
1495 1496 typeset fmt=$1
1496 1497 typeset p=$2
1497 1498
1498 1499 comm=`$AWK '
1499 1500 $1 == "'$p'" {
1500 1501 do getline ; while (NF > 0)
1501 1502 getline
1502 1503 while (NF > 0) { print ; getline }
1503 1504 exit
1504 1505 }' < $wxfile`
1505 1506
1506 1507 if [[ -z $comm ]]; then
1507 1508 comm="*** NO COMMENTS ***"
1508 1509 fi
1509 1510
1510 1511 if [[ $fmt == "text" ]]; then
1511 1512 print -- "$comm"
1512 1513 return
1513 1514 fi
1514 1515
1515 1516 print -- "$comm" | html_quote | its2url
1516 1517
1517 1518 }
1518 1519
1519 1520 #
1520 1521 # getcomments {text|html} filepath parentpath
1521 1522 #
1522 1523 # Fetch the comments depending on what SCM mode we're in.
1523 1524 #
1524 1525 getcomments()
1525 1526 {
1526 1527 typeset fmt=$1
1527 1528 typeset p=$2
1528 1529 typeset pp=$3
1529 1530
1530 1531 if [[ -n $Nflag ]]; then
1531 1532 return
1532 1533 fi
1533 1534 #
1534 1535 # Mercurial support uses a file list in wx format, so this
1535 1536 # will be used there, too
1536 1537 #
1537 1538 if [[ -n $wxfile ]]; then
1538 1539 comments_from_wx $fmt $p
1539 1540 else
1540 1541 if [[ $SCM_MODE == "teamware" ]]; then
1541 1542 comments_from_teamware $fmt $pp $p
1542 1543 fi
1543 1544 fi
1544 1545 }
1545 1546
1546 1547 #
1547 1548 # printCI <total-changed> <inserted> <deleted> <modified> <unchanged>
1548 1549 #
1549 1550 # Print out Code Inspection figures similar to sccs-prt(1) format.
1550 1551 #
1551 1552 function printCI
1552 1553 {
1553 1554 integer tot=$1 ins=$2 del=$3 mod=$4 unc=$5
1554 1555 typeset str
1555 1556 if (( tot == 1 )); then
1556 1557 str="line"
1557 1558 else
1558 1559 str="lines"
1559 1560 fi
1560 1561 printf '%d %s changed: %d ins; %d del; %d mod; %d unchg\n' \
1561 1562 $tot $str $ins $del $mod $unc
1562 1563 }
1563 1564
1564 1565
1565 1566 #
1566 1567 # difflines <oldfile> <newfile>
1567 1568 #
1568 1569 # Calculate and emit number of added, removed, modified and unchanged lines,
1569 1570 # and total lines changed, the sum of added + removed + modified.
1570 1571 #
1571 1572 function difflines
1572 1573 {
1573 1574 integer tot mod del ins unc err
1574 1575 typeset filename
1575 1576
1576 1577 eval $( diff -e $1 $2 | $AWK '
1577 1578 # Change range of lines: N,Nc
1578 1579 /^[0-9]*,[0-9]*c$/ {
1579 1580 n=split(substr($1,1,length($1)-1), counts, ",");
1580 1581 if (n != 2) {
1581 1582 error=2
1582 1583 exit;
1583 1584 }
1584 1585 #
1585 1586 # 3,5c means lines 3 , 4 and 5 are changed, a total of 3 lines.
1586 1587 # following would be 5 - 3 = 2! Hence +1 for correction.
1587 1588 #
1588 1589 r=(counts[2]-counts[1])+1;
1589 1590
1590 1591 #
1591 1592 # Now count replacement lines: each represents a change instead
1592 1593 # of a delete, so increment c and decrement r.
1593 1594 #
1594 1595 while (getline != /^\.$/) {
1595 1596 c++;
1596 1597 r--;
1597 1598 }
1598 1599 #
1599 1600 # If there were more replacement lines than original lines,
1600 1601 # then r will be negative; in this case there are no deletions,
1601 1602 # but there are r changes that should be counted as adds, and
1602 1603 # since r is negative, subtract it from a and add it to c.
1603 1604 #
1604 1605 if (r < 0) {
1605 1606 a-=r;
1606 1607 c+=r;
1607 1608 }
1608 1609
1609 1610 #
1610 1611 # If there were more original lines than replacement lines, then
1611 1612 # r will be positive; in this case, increment d by that much.
1612 1613 #
1613 1614 if (r > 0) {
1614 1615 d+=r;
1615 1616 }
1616 1617 next;
1617 1618 }
1618 1619
1619 1620 # Change lines: Nc
1620 1621 /^[0-9].*c$/ {
1621 1622 # The first line is a replacement; any more are additions.
1622 1623 if (getline != /^\.$/) {
1623 1624 c++;
1624 1625 while (getline != /^\.$/) a++;
1625 1626 }
1626 1627 next;
1627 1628 }
1628 1629
1629 1630 # Add lines: both Na and N,Na
1630 1631 /^[0-9].*a$/ {
1631 1632 while (getline != /^\.$/) a++;
1632 1633 next;
1633 1634 }
1634 1635
1635 1636 # Delete range of lines: N,Nd
1636 1637 /^[0-9]*,[0-9]*d$/ {
1637 1638 n=split(substr($1,1,length($1)-1), counts, ",");
1638 1639 if (n != 2) {
1639 1640 error=2
1640 1641 exit;
1641 1642 }
1642 1643 #
1643 1644 # 3,5d means lines 3 , 4 and 5 are deleted, a total of 3 lines.
1644 1645 # following would be 5 - 3 = 2! Hence +1 for correction.
1645 1646 #
1646 1647 r=(counts[2]-counts[1])+1;
1647 1648 d+=r;
1648 1649 next;
1649 1650 }
1650 1651
1651 1652 # Delete line: Nd. For example 10d says line 10 is deleted.
1652 1653 /^[0-9]*d$/ {d++; next}
1653 1654
1654 1655 # Should not get here!
1655 1656 {
1656 1657 error=1;
1657 1658 exit;
1658 1659 }
1659 1660
1660 1661 # Finish off - print results
1661 1662 END {
1662 1663 printf("tot=%d;mod=%d;del=%d;ins=%d;err=%d\n",
1663 1664 (c+d+a), c, d, a, error);
1664 1665 }' )
1665 1666
1666 1667 # End of $AWK, Check to see if any trouble occurred.
1667 1668 if (( $? > 0 || err > 0 )); then
1668 1669 print "Unexpected Error occurred reading" \
1669 1670 "\`diff -e $1 $2\`: \$?=$?, err=" $err
1670 1671 return
1671 1672 fi
1672 1673
1673 1674 # Accumulate totals
1674 1675 (( TOTL += tot ))
1675 1676 (( TMOD += mod ))
1676 1677 (( TDEL += del ))
1677 1678 (( TINS += ins ))
1678 1679 # Calculate unchanged lines
1679 1680 unc=`wc -l < $1`
1680 1681 if (( unc > 0 )); then
1681 1682 (( unc -= del + mod ))
1682 1683 (( TUNC += unc ))
1683 1684 fi
1684 1685 # print summary
1685 1686 print "<span class=\"lineschanged\">"
1686 1687 printCI $tot $ins $del $mod $unc
1687 1688 print "</span>"
1688 1689 }
1689 1690
1690 1691
1691 1692 #
1692 1693 # flist_from_wx
1693 1694 #
1694 1695 # Sets up webrev to source its information from a wx-formatted file.
1695 1696 # Sets the global 'wxfile' variable.
1696 1697 #
1697 1698 function flist_from_wx
1698 1699 {
1699 1700 typeset argfile=$1
1700 1701 if [[ -n ${argfile%%/*} ]]; then
1701 1702 #
1702 1703 # If the wx file pathname is relative then make it absolute
1703 1704 # because the webrev does a "cd" later on.
1704 1705 #
1705 1706 wxfile=$PWD/$argfile
1706 1707 else
1707 1708 wxfile=$argfile
1708 1709 fi
1709 1710
1710 1711 $AWK '{ c = 1; print;
1711 1712 while (getline) {
1712 1713 if (NF == 0) { c = -c; continue }
1713 1714 if (c > 0) print
1714 1715 }
1715 1716 }' $wxfile > $FLIST
1716 1717
1717 1718 print " Done."
1718 1719 }
1719 1720
1720 1721 #
1721 1722 # flist_from_teamware [ <args-to-putback-n> ]
1722 1723 #
1723 1724 # Generate the file list by extracting file names from a putback -n. Some
1724 1725 # names may come from the "update/create" messages and others from the
1725 1726 # "currently checked out" warning. Renames are detected here too. Extract
1726 1727 # values for CODEMGR_WS and CODEMGR_PARENT from the output of the putback
1727 1728 # -n as well, but remove them if they are already defined.
1728 1729 #
1729 1730 function flist_from_teamware
1730 1731 {
1731 1732 if [[ -n $codemgr_parent && -z $parent_webrev ]]; then
1732 1733 if [[ ! -d $codemgr_parent/Codemgr_wsdata ]]; then
1733 1734 print -u2 "parent $codemgr_parent doesn't look like a" \
1734 1735 "valid teamware workspace"
1735 1736 exit 1
1736 1737 fi
1737 1738 parent_args="-p $codemgr_parent"
1738 1739 fi
1739 1740
1740 1741 print " File list from: 'putback -n $parent_args $*' ... \c"
1741 1742
1742 1743 putback -n $parent_args $* 2>&1 |
1743 1744 $AWK '
1744 1745 /^update:|^create:/ {print $2}
1745 1746 /^Parent workspace:/ {printf("CODEMGR_PARENT=%s\n",$3)}
1746 1747 /^Child workspace:/ {printf("CODEMGR_WS=%s\n",$3)}
1747 1748 /^The following files are currently checked out/ {p = 1; continue}
1748 1749 NF == 0 {p=0 ; continue}
1749 1750 /^rename/ {old=$3}
1750 1751 $1 == "to:" {print $2, old}
1751 1752 /^"/ {continue}
1752 1753 p == 1 {print $1}' |
1753 1754 sort -r -k 1,1 -u | sort > $FLIST
1754 1755
1755 1756 print " Done."
1756 1757 }
1757 1758
1758 1759 #
1759 1760 # Call hg-active to get the active list output in the wx active list format
1760 1761 #
1761 1762 function hg_active_wxfile
1762 1763 {
1763 1764 typeset child=$1
1764 1765 typeset parent=$2
1765 1766
1766 1767 TMPFLIST=/tmp/$$.active
1767 1768 $HG_ACTIVE -w $child -p $parent -o $TMPFLIST
1768 1769 wxfile=$TMPFLIST
1769 1770 }
1770 1771
1771 1772 #
1772 1773 # flist_from_mercurial
1773 1774 # Call hg-active to get a wx-style active list, and hand it off to
1774 1775 # flist_from_wx
1775 1776 #
1776 1777 function flist_from_mercurial
1777 1778 {
1778 1779 typeset child=$1
1779 1780 typeset parent=$2
1780 1781
1781 1782 print " File list from: hg-active -p $parent ...\c"
1782 1783 if [[ ! -x $HG_ACTIVE ]]; then
1783 1784 print # Blank line for the \c above
1784 1785 print -u2 "Error: hg-active tool not found. Exiting"
1785 1786 exit 1
1786 1787 fi
1787 1788 hg_active_wxfile $child $parent
1788 1789
1789 1790 # flist_from_wx prints the Done, so we don't have to.
1790 1791 flist_from_wx $TMPFLIST
1791 1792 }
1792 1793
1793 1794 #
1794 1795 # Transform a specified 'git log' output format into a wx-like active list.
1795 1796 #
1796 1797 function git_wxfile
1797 1798 {
1798 1799 typeset child="$1"
1799 1800 typeset parent="$2"
1800 1801
1801 1802 TMPFLIST=/tmp/$$.active
1802 1803 $PERL -e 'my (%files, %realfiles, $msg);
1803 1804 my $branch = $ARGV[0];
1804 1805
1805 1806 open(F, "git diff -M --name-status $branch |");
1806 1807 while (<F>) {
1807 1808 chomp;
1808 1809 if (/^R(\d+)\s+([^ ]+)\s+([^ ]+)/) { # rename
1809 1810 if ($1 >= 75) { # Probably worth treating as a rename
1810 1811 $realfiles{$3} = $2;
1811 1812 } else {
1812 1813 $realfiles{$3} = $3;
1813 1814 $realfiles{$2} = $2;
1814 1815 }
1815 1816 } else {
1816 1817 my $f = (split /\s+/, $_)[1];
1817 1818 $realfiles{$f} = $f;
1818 1819 }
1819 1820 }
1820 1821 close(F);
1821 1822
1822 1823 my $state = 1; # 0|comments, 1|files
1823 1824 open(F, "git whatchanged --pretty=format:%B $branch.. |");
1824 1825 while (<F>) {
1825 1826 chomp;
1826 1827 if (/^:[0-9]{6}/) {
1827 1828 my $fname = (split /\t/, $_)[1];
1828 1829 next if !defined($realfiles{$fname}); # No real change
1829 1830 $state = 1;
1830 1831 chomp $msg;
1831 1832 $files{$fname} .= $msg;
1832 1833 } else {
1833 1834 if ($state == 1) {
1834 1835 $state = 0;
1835 1836 $msg = /^\n/ ? "" : "\n";
1836 1837 }
1837 1838 $msg .= "$_\n" if ($_);
1838 1839 }
1839 1840 }
1840 1841 close(F);
1841 1842
1842 1843 for (sort keys %files) {
1843 1844 if ($realfiles{$_} ne $_) {
1844 1845 print "$_ $realfiles{$_}\n$files{$_}\n\n";
1845 1846 } else {
1846 1847 print "$_\n$files{$_}\n\n"
1847 1848 }
1848 1849 }' ${parent} > $TMPFLIST
1849 1850
1850 1851 wxfile=$TMPFLIST
1851 1852 }
1852 1853
1853 1854 #
1854 1855 # flist_from_git
1855 1856 # Build a wx-style active list, and hand it off to flist_from_wx
1856 1857 #
1857 1858 function flist_from_git
1858 1859 {
1859 1860 typeset child=$1
1860 1861 typeset parent=$2
1861 1862
1862 1863 print " File list from: git ...\c"
1863 1864 git_wxfile "$child" "$parent";
1864 1865
1865 1866 # flist_from_wx prints the Done, so we don't have to.
1866 1867 flist_from_wx $TMPFLIST
1867 1868 }
1868 1869
1869 1870 #
1870 1871 # flist_from_subversion
1871 1872 #
1872 1873 # Generate the file list by extracting file names from svn status.
1873 1874 #
1874 1875 function flist_from_subversion
1875 1876 {
1876 1877 CWS=$1
1877 1878 OLDPWD=$2
1878 1879
1879 1880 cd $CWS
1880 1881 print -u2 " File list from: svn status ... \c"
1881 1882 svn status | $AWK '/^[ACDMR]/ { print $NF }' > $FLIST
1882 1883 print -u2 " Done."
1883 1884 cd $OLDPWD
1884 1885 }
1885 1886
1886 1887 function env_from_flist
1887 1888 {
1888 1889 [[ -r $FLIST ]] || return
1889 1890
1890 1891 #
1891 1892 # Use "eval" to set env variables that are listed in the file
1892 1893 # list. Then copy those into our local versions of those
1893 1894 # variables if they have not been set already.
1894 1895 #
1895 1896 eval `$SED -e "s/#.*$//" $FLIST | $GREP = `
1896 1897
1897 1898 if [[ -z $codemgr_ws && -n $CODEMGR_WS ]]; then
1898 1899 codemgr_ws=$CODEMGR_WS
1899 1900 export CODEMGR_WS
1900 1901 fi
1901 1902
1902 1903 #
1903 1904 # Check to see if CODEMGR_PARENT is set in the flist file.
1904 1905 #
1905 1906 if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then
1906 1907 codemgr_parent=$CODEMGR_PARENT
1907 1908 export CODEMGR_PARENT
1908 1909 fi
1909 1910 }
1910 1911
1911 1912 function look_for_prog
1912 1913 {
1913 1914 typeset path
1914 1915 typeset ppath
1915 1916 typeset progname=$1
1916 1917
1917 1918 ppath=$PATH
1918 1919 ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin
1919 1920 ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin
1920 1921 ppath=$ppath:/opt/onbld/bin/`uname -p`
1921 1922
1922 1923 PATH=$ppath prog=`whence $progname`
1923 1924 if [[ -n $prog ]]; then
1924 1925 print $prog
1925 1926 fi
1926 1927 }
1927 1928
1928 1929 function get_file_mode
1929 1930 {
1930 1931 $PERL -e '
1931 1932 if (@stat = stat($ARGV[0])) {
1932 1933 $mode = $stat[2] & 0777;
1933 1934 printf "%03o\n", $mode;
1934 1935 exit 0;
1935 1936 } else {
1936 1937 exit 1;
1937 1938 }
1938 1939 ' $1
1939 1940 }
1940 1941
1941 1942 function build_old_new_teamware
1942 1943 {
1943 1944 typeset olddir="$1"
1944 1945 typeset newdir="$2"
1945 1946
1946 1947 # If the child's version doesn't exist then
1947 1948 # get a readonly copy.
1948 1949
1949 1950 if [[ ! -f $CWS/$DIR/$F && -f $CWS/$DIR/SCCS/s.$F ]]; then
1950 1951 $SCCS get -s -p $CWS/$DIR/$F > $CWS/$DIR/$F
1951 1952 fi
1952 1953
1953 1954 # The following two sections propagate file permissions the
1954 1955 # same way SCCS does. If the file is already under version
1955 1956 # control, always use permissions from the SCCS/s.file. If
1956 1957 # the file is not under SCCS control, use permissions from the
1957 1958 # working copy. In all cases, the file copied to the webrev
1958 1959 # is set to read only, and group/other permissions are set to
1959 1960 # match those of the file owner. This way, even if the file
1960 1961 # is currently checked out, the webrev will display the final
1961 1962 # permissions that would result after check in.
1962 1963
1963 1964 #
1964 1965 # Snag new version of file.
1965 1966 #
1966 1967 rm -f $newdir/$DIR/$F
1967 1968 cp $CWS/$DIR/$F $newdir/$DIR/$F
1968 1969 if [[ -f $CWS/$DIR/SCCS/s.$F ]]; then
1969 1970 chmod `get_file_mode $CWS/$DIR/SCCS/s.$F` \
1970 1971 $newdir/$DIR/$F
1971 1972 fi
1972 1973 chmod u-w,go=u $newdir/$DIR/$F
1973 1974
1974 1975 #
1975 1976 # Get the parent's version of the file. First see whether the
1976 1977 # child's version is checked out and get the parent's version
1977 1978 # with keywords expanded or unexpanded as appropriate.
1978 1979 #
1979 1980 if [[ -f $PWS/$PDIR/$PF && ! -f $PWS/$PDIR/SCCS/s.$PF && \
1980 1981 ! -f $PWS/$PDIR/SCCS/p.$PF ]]; then
1981 1982 # Parent is not a real workspace, but just a raw
1982 1983 # directory tree - use the file that's there as
1983 1984 # the old file.
1984 1985
1985 1986 rm -f $olddir/$PDIR/$PF
1986 1987 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
1987 1988 else
1988 1989 if [[ -f $PWS/$PDIR/SCCS/s.$PF ]]; then
1989 1990 real_parent=$PWS
1990 1991 else
1991 1992 real_parent=$RWS
1992 1993 fi
1993 1994
1994 1995 rm -f $olddir/$PDIR/$PF
1995 1996
1996 1997 if [[ -f $real_parent/$PDIR/$PF ]]; then
1997 1998 if [ -f $CWS/$DIR/SCCS/p.$F ]; then
1998 1999 $SCCS get -s -p -k $real_parent/$PDIR/$PF > \
1999 2000 $olddir/$PDIR/$PF
2000 2001 else
2001 2002 $SCCS get -s -p $real_parent/$PDIR/$PF > \
2002 2003 $olddir/$PDIR/$PF
2003 2004 fi
2004 2005 chmod `get_file_mode $real_parent/$PDIR/SCCS/s.$PF` \
2005 2006 $olddir/$PDIR/$PF
2006 2007 fi
2007 2008 fi
2008 2009 if [[ -f $olddir/$PDIR/$PF ]]; then
2009 2010 chmod u-w,go=u $olddir/$PDIR/$PF
2010 2011 fi
2011 2012 }
2012 2013
2013 2014 function build_old_new_mercurial
2014 2015 {
2015 2016 typeset olddir="$1"
2016 2017 typeset newdir="$2"
2017 2018 typeset old_mode=
2018 2019 typeset new_mode=
2019 2020 typeset file
2020 2021
2021 2022 #
2022 2023 # Get old file mode, from the parent revision manifest entry.
2023 2024 # Mercurial only stores a "file is executable" flag, but the
2024 2025 # manifest will display an octal mode "644" or "755".
2025 2026 #
2026 2027 if [[ "$PDIR" == "." ]]; then
2027 2028 file="$PF"
2028 2029 else
2029 2030 file="$PDIR/$PF"
2030 2031 fi
2031 2032 file=`echo $file | $SED 's#/#\\\/#g'`
2032 2033 # match the exact filename, and return only the permission digits
2033 2034 old_mode=`$SED -n -e "/^\\(...\\) . ${file}$/s//\\1/p" \
2034 2035 < $HG_PARENT_MANIFEST`
2035 2036
2036 2037 #
2037 2038 # Get new file mode, directly from the filesystem.
2038 2039 # Normalize the mode to match Mercurial's behavior.
2039 2040 #
2040 2041 new_mode=`get_file_mode $CWS/$DIR/$F`
2041 2042 if [[ -n "$new_mode" ]]; then
2042 2043 if [[ "$new_mode" = *[1357]* ]]; then
2043 2044 new_mode=755
2044 2045 else
2045 2046 new_mode=644
2046 2047 fi
2047 2048 fi
2048 2049
2049 2050 #
2050 2051 # new version of the file.
2051 2052 #
2052 2053 rm -rf $newdir/$DIR/$F
2053 2054 if [[ -e $CWS/$DIR/$F ]]; then
2054 2055 cp $CWS/$DIR/$F $newdir/$DIR/$F
2055 2056 if [[ -n $new_mode ]]; then
2056 2057 chmod $new_mode $newdir/$DIR/$F
2057 2058 else
2058 2059 # should never happen
2059 2060 print -u2 "ERROR: set mode of $newdir/$DIR/$F"
2060 2061 fi
2061 2062 fi
2062 2063
2063 2064 #
2064 2065 # parent's version of the file
2065 2066 #
2066 2067 # Note that we get this from the last version common to both
2067 2068 # ourselves and the parent. References are via $CWS since we have no
2068 2069 # guarantee that the parent workspace is reachable via the filesystem.
2069 2070 #
2070 2071 if [[ -n $parent_webrev && -e $PWS/$PDIR/$PF ]]; then
2071 2072 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
2072 2073 elif [[ -n $HG_PARENT ]]; then
2073 2074 hg cat -R $CWS -r $HG_PARENT $CWS/$PDIR/$PF > \
2074 2075 $olddir/$PDIR/$PF 2>/dev/null
2075 2076
2076 2077 if (( $? != 0 )); then
2077 2078 rm -f $olddir/$PDIR/$PF
2078 2079 else
2079 2080 if [[ -n $old_mode ]]; then
2080 2081 chmod $old_mode $olddir/$PDIR/$PF
2081 2082 else
2082 2083 # should never happen
2083 2084 print -u2 "ERROR: set mode of $olddir/$PDIR/$PF"
2084 2085 fi
2085 2086 fi
2086 2087 fi
2087 2088 }
2088 2089
2089 2090 function build_old_new_git
2090 2091 {
2091 2092 typeset olddir="$1"
2092 2093 typeset newdir="$2"
2093 2094 typeset o_mode=
2094 2095 typeset n_mode=
2095 2096 typeset o_object=
2096 2097 typeset n_object=
2097 2098 typeset OWD=$PWD
2098 2099 typeset file
2099 2100 typeset type
2100 2101
2101 2102 cd $CWS
2102 2103
2103 2104 #
2104 2105 # Get old file and its mode from the git object tree
2105 2106 #
2106 2107 if [[ "$PDIR" == "." ]]; then
2107 2108 file="$PF"
2108 2109 else
2109 2110 file="$PDIR/$PF"
2110 2111 fi
2111 2112
2112 2113 if [[ -n $parent_webrev && -e $PWS/$PDIR/$PF ]]; then
2113 2114 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
2114 2115 else
2115 2116 $GIT ls-tree $GIT_PARENT $file | read o_mode type o_object junk
2116 2117 $GIT cat-file $type $o_object > $olddir/$file 2>/dev/null
2117 2118
2118 2119 if (( $? != 0 )); then
2119 2120 rm -f $olddir/$file
2120 2121 elif [[ -n $o_mode ]]; then
2121 2122 # Strip the first 3 digits, to get a regular octal mode
2122 2123 o_mode=${o_mode/???/}
2123 2124 chmod $o_mode $olddir/$file
2124 2125 else
2125 2126 # should never happen
2126 2127 print -u2 "ERROR: set mode of $olddir/$file"
2127 2128 fi
2128 2129 fi
2129 2130
2130 2131 #
2131 2132 # new version of the file.
2132 2133 #
2133 2134 if [[ "$DIR" == "." ]]; then
2134 2135 file="$F"
2135 2136 else
2136 2137 file="$DIR/$F"
2137 2138 fi
2138 2139 rm -rf $newdir/$file
2139 2140
2140 2141 if [[ -e $CWS/$DIR/$F ]]; then
2141 2142 cp $CWS/$DIR/$F $newdir/$DIR/$F
2142 2143 chmod $(get_file_mode $CWS/$DIR/$F) $newdir/$DIR/$F
2143 2144 fi
2144 2145 cd $OWD
2145 2146 }
2146 2147
2147 2148 function build_old_new_subversion
2148 2149 {
2149 2150 typeset olddir="$1"
2150 2151 typeset newdir="$2"
2151 2152
2152 2153 # Snag new version of file.
2153 2154 rm -f $newdir/$DIR/$F
2154 2155 [[ -e $CWS/$DIR/$F ]] && cp $CWS/$DIR/$F $newdir/$DIR/$F
2155 2156
2156 2157 if [[ -n $PWS && -e $PWS/$PDIR/$PF ]]; then
2157 2158 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
2158 2159 else
2159 2160 # Get the parent's version of the file.
2160 2161 svn status $CWS/$DIR/$F | read stat file
2161 2162 if [[ $stat != "A" ]]; then
2162 2163 svn cat -r BASE $CWS/$DIR/$F > $olddir/$PDIR/$PF
2163 2164 fi
2164 2165 fi
2165 2166 }
2166 2167
2167 2168 function build_old_new_unknown
2168 2169 {
2169 2170 typeset olddir="$1"
2170 2171 typeset newdir="$2"
2171 2172
2172 2173 #
2173 2174 # Snag new version of file.
2174 2175 #
2175 2176 rm -f $newdir/$DIR/$F
2176 2177 [[ -e $CWS/$DIR/$F ]] && cp $CWS/$DIR/$F $newdir/$DIR/$F
2177 2178
2178 2179 #
2179 2180 # Snag the parent's version of the file.
2180 2181 #
2181 2182 if [[ -f $PWS/$PDIR/$PF ]]; then
2182 2183 rm -f $olddir/$PDIR/$PF
2183 2184 cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF
2184 2185 fi
2185 2186 }
2186 2187
2187 2188 function build_old_new
2188 2189 {
2189 2190 typeset WDIR=$1
2190 2191 typeset PWS=$2
2191 2192 typeset PDIR=$3
2192 2193 typeset PF=$4
2193 2194 typeset CWS=$5
2194 2195 typeset DIR=$6
2195 2196 typeset F=$7
2196 2197
2197 2198 typeset olddir="$WDIR/raw_files/old"
2198 2199 typeset newdir="$WDIR/raw_files/new"
2199 2200
2200 2201 mkdir -p $olddir/$PDIR
2201 2202 mkdir -p $newdir/$DIR
2202 2203
2203 2204 if [[ $SCM_MODE == "teamware" ]]; then
2204 2205 build_old_new_teamware "$olddir" "$newdir"
2205 2206 elif [[ $SCM_MODE == "mercurial" ]]; then
2206 2207 build_old_new_mercurial "$olddir" "$newdir"
2207 2208 elif [[ $SCM_MODE == "git" ]]; then
2208 2209 build_old_new_git "$olddir" "$newdir"
2209 2210 elif [[ $SCM_MODE == "subversion" ]]; then
2210 2211 build_old_new_subversion "$olddir" "$newdir"
2211 2212 elif [[ $SCM_MODE == "unknown" ]]; then
2212 2213 build_old_new_unknown "$olddir" "$newdir"
2213 2214 fi
2214 2215
2215 2216 if [[ ! -f $olddir/$PDIR/$PF && ! -f $newdir/$DIR/$F ]]; then
2216 2217 print "*** Error: file not in parent or child"
2217 2218 return 1
2218 2219 fi
2219 2220 return 0
2220 2221 }
2221 2222
2222 2223
2223 2224 #
2224 2225 # Usage message.
2225 2226 #
2226 2227 function usage
2227 2228 {
2228 2229 print 'Usage:\twebrev [common-options]
2229 2230 webrev [common-options] ( <file> | - )
2230 2231 webrev [common-options] -w <wx file>
2231 2232
2232 2233 Options:
2233 2234 -C <filename>: Use <filename> for the information tracking configuration.
2234 2235 -D: delete remote webrev
2235 2236 -i <filename>: Include <filename> in the index.html file.
2236 2237 -I <filename>: Use <filename> for the information tracking registry.
2237 2238 -n: do not generate the webrev (useful with -U)
2238 2239 -O: Print bugids/arc cases suitable for OpenSolaris.
2239 2240 -o <outdir>: Output webrev to specified directory.
2240 2241 -p <compare-against>: Use specified parent wkspc or basis for comparison
2241 2242 -t <remote_target>: Specify remote destination for webrev upload
2242 2243 -U: upload the webrev to remote destination
2243 2244 -w <wxfile>: Use specified wx active file.
2244 2245
2245 2246 Environment:
2246 2247 WDIR: Control the output directory.
2247 2248 WEBREV_TRASH_DIR: Set directory for webrev delete.
2248 2249
2249 2250 SCM Specific Options:
2250 2251 TeamWare: webrev [common-options] -l [arguments to 'putback']
2251 2252
2252 2253 SCM Environment:
2253 2254 CODEMGR_WS: Workspace location.
2254 2255 CODEMGR_PARENT: Parent workspace location.
2255 2256 '
2256 2257
2257 2258 exit 2
2258 2259 }
2259 2260
2260 2261 #
2261 2262 #
2262 2263 # Main program starts here
2263 2264 #
2264 2265 #
2265 2266
2266 2267 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
2267 2268
2268 2269 set +o noclobber
2269 2270
2270 2271 PATH=$(/bin/dirname "$(whence $0)"):$PATH
2271 2272
2272 2273 [[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff`
2273 2274 [[ -z $WX ]] && WX=`look_for_prog wx`
2274 2275 [[ -z $HG_ACTIVE ]] && HG_ACTIVE=`look_for_prog hg-active`
2275 2276 [[ -z $GIT ]] && GIT=`look_for_prog git`
2276 2277 [[ -z $WHICH_SCM ]] && WHICH_SCM=`look_for_prog which_scm`
2277 2278 [[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview`
2278 2279 [[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf`
2279 2280 [[ -z $PERL ]] && PERL=`look_for_prog perl`
2280 2281 [[ -z $RSYNC ]] && RSYNC=`look_for_prog rsync`
2281 2282 [[ -z $SCCS ]] && SCCS=`look_for_prog sccs`
2282 2283 [[ -z $AWK ]] && AWK=`look_for_prog nawk`
2283 2284 [[ -z $AWK ]] && AWK=`look_for_prog gawk`
2284 2285 [[ -z $AWK ]] && AWK=`look_for_prog awk`
2285 2286 [[ -z $SCP ]] && SCP=`look_for_prog scp`
2286 2287 [[ -z $SED ]] && SED=`look_for_prog sed`
2287 2288 [[ -z $SFTP ]] && SFTP=`look_for_prog sftp`
2288 2289 [[ -z $SORT ]] && SORT=`look_for_prog sort`
2289 2290 [[ -z $MKTEMP ]] && MKTEMP=`look_for_prog mktemp`
2290 2291 [[ -z $GREP ]] && GREP=`look_for_prog grep`
2291 2292 [[ -z $FIND ]] && FIND=`look_for_prog find`
2292 2293
2293 2294 # set name of trash directory for remote webrev deletion
2294 2295 TRASH_DIR=".trash"
2295 2296 [[ -n $WEBREV_TRASH_DIR ]] && TRASH_DIR=$WEBREV_TRASH_DIR
2296 2297
2297 2298 if [[ ! -x $PERL ]]; then
2298 2299 print -u2 "Error: No perl interpreter found. Exiting."
2299 2300 exit 1
2300 2301 fi
2301 2302
2302 2303 if [[ ! -x $WHICH_SCM ]]; then
2303 2304 print -u2 "Error: Could not find which_scm. Exiting."
2304 2305 exit 1
2305 2306 fi
2306 2307
2307 2308 #
2308 2309 # These aren't fatal, but we want to note them to the user.
2309 2310 # We don't warn on the absence of 'wx' until later when we've
2310 2311 # determined that we actually need to try to invoke it.
2311 2312 #
2312 2313 [[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found."
2313 2314 [[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found."
2314 2315 [[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found."
2315 2316
2316 2317 # Declare global total counters.
2317 2318 integer TOTL TINS TDEL TMOD TUNC
2318 2319
2319 2320 # default remote host for upload/delete
2320 2321 typeset -r DEFAULT_REMOTE_HOST="cr.opensolaris.org"
2321 2322 # prefixes for upload targets
2322 2323 typeset -r rsync_prefix="rsync://"
2323 2324 typeset -r ssh_prefix="ssh://"
2324 2325
2325 2326 Cflag=
2326 2327 Dflag=
2327 2328 flist_mode=
2328 2329 flist_file=
2329 2330 iflag=
2330 2331 Iflag=
2331 2332 lflag=
2332 2333 Nflag=
2333 2334 nflag=
2334 2335 Oflag=
2335 2336 oflag=
2336 2337 pflag=
2337 2338 tflag=
2338 2339 uflag=
2339 2340 Uflag=
2340 2341 wflag=
2341 2342 remote_target=
2342 2343
2343 2344 #
2344 2345 # NOTE: when adding/removing options it is necessary to sync the list
2345 2346 # with usr/src/tools/onbld/hgext/cdm.py
2346 2347 #
2347 2348 while getopts "C:Di:I:lnNo:Op:t:Uw" opt
2348 2349 do
2349 2350 case $opt in
2350 2351 C) Cflag=1
2351 2352 ITSCONF=$OPTARG;;
2352 2353
2353 2354 D) Dflag=1;;
2354 2355
2355 2356 i) iflag=1
2356 2357 INCLUDE_FILE=$OPTARG;;
2357 2358
2358 2359 I) Iflag=1
2359 2360 ITSREG=$OPTARG;;
2360 2361
2361 2362 #
2362 2363 # If -l has been specified, we need to abort further options
2363 2364 # processing, because subsequent arguments are going to be
2364 2365 # arguments to 'putback -n'.
2365 2366 #
2366 2367 l) lflag=1
2367 2368 break;;
2368 2369
2369 2370 N) Nflag=1;;
2370 2371
2371 2372 n) nflag=1;;
2372 2373
2373 2374 O) Oflag=1;;
2374 2375
2375 2376 o) oflag=1
2376 2377 # Strip the trailing slash to correctly form remote target.
2377 2378 WDIR=${OPTARG%/};;
2378 2379
2379 2380 p) pflag=1
2380 2381 codemgr_parent=$OPTARG;;
2381 2382
2382 2383 t) tflag=1
2383 2384 remote_target=$OPTARG;;
2384 2385
2385 2386 U) Uflag=1;;
2386 2387
2387 2388 w) wflag=1;;
2388 2389
2389 2390 ?) usage;;
2390 2391 esac
2391 2392 done
2392 2393
2393 2394 FLIST=/tmp/$$.flist
2394 2395
2395 2396 if [[ -n $wflag && -n $lflag ]]; then
2396 2397 usage
2397 2398 fi
2398 2399
2399 2400 # more sanity checking
2400 2401 if [[ -n $nflag && -z $Uflag ]]; then
2401 2402 print "it does not make sense to skip webrev generation" \
2402 2403 "without -U"
2403 2404 exit 1
2404 2405 fi
2405 2406
2406 2407 if [[ -n $tflag && -z $Uflag && -z $Dflag ]]; then
2407 2408 echo "remote target has to be used only for upload or delete"
2408 2409 exit 1
2409 2410 fi
2410 2411
2411 2412 #
2412 2413 # For the invocation "webrev -n -U" with no other options, webrev will assume
2413 2414 # that the webrev exists in ${CWS}/webrev, but will upload it using the name
2414 2415 # $(basename ${CWS}). So we need to get CWS set before we skip any remaining
2415 2416 # logic.
2416 2417 #
2417 2418 $WHICH_SCM | read SCM_MODE junk || exit 1
2418 2419 if [[ $SCM_MODE == "teamware" ]]; then
2419 2420 #
2420 2421 # Teamware priorities:
2421 2422 # 1. CODEMGR_WS from the environment
2422 2423 # 2. workspace name
2423 2424 #
2424 2425 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
2425 2426 if [[ -n $codemgr_ws && ! -d $codemgr_ws ]]; then
2426 2427 print -u2 "$codemgr_ws: no such workspace"
2427 2428 exit 1
2428 2429 fi
2429 2430 [[ -z $codemgr_ws ]] && codemgr_ws=$(workspace name)
2430 2431 codemgr_ws=$(cd $codemgr_ws;print $PWD)
2431 2432 CODEMGR_WS=$codemgr_ws
2432 2433 CWS=$codemgr_ws
2433 2434 elif [[ $SCM_MODE == "mercurial" ]]; then
2434 2435 #
2435 2436 # Mercurial priorities:
2436 2437 # 1. hg root from CODEMGR_WS environment variable
2437 2438 # 1a. hg root from CODEMGR_WS/usr/closed if we're somewhere under
2438 2439 # usr/closed when we run webrev
2439 2440 # 2. hg root from directory of invocation
2440 2441 #
2441 2442 if [[ ${PWD} =~ "usr/closed" ]]; then
2442 2443 testparent=${CODEMGR_WS}/usr/closed
2443 2444 # If we're in OpenSolaris mode, we enforce a minor policy:
2444 2445 # help to make sure the reviewer doesn't accidentally publish
2445 2446 # source which is under usr/closed
2446 2447 if [[ -n "$Oflag" ]]; then
2447 2448 print -u2 "OpenSolaris output not permitted with" \
2448 2449 "usr/closed changes"
2449 2450 exit 1
2450 2451 fi
2451 2452 else
2452 2453 testparent=${CODEMGR_WS}
2453 2454 fi
2454 2455 [[ -z $codemgr_ws && -n $testparent ]] && \
2455 2456 codemgr_ws=$(hg root -R $testparent 2>/dev/null)
2456 2457 [[ -z $codemgr_ws ]] && codemgr_ws=$(hg root 2>/dev/null)
2457 2458 CWS=$codemgr_ws
2458 2459 elif [[ $SCM_MODE == "git" ]]; then
2459 2460 #
2460 2461 # Git priorities:
2461 2462 # 1. git rev-parse --git-dir from CODEMGR_WS environment variable
2462 2463 # 2. git rev-parse --git-dir from directory of invocation
2463 2464 #
2464 2465 [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && \
2465 2466 codemgr_ws=$($GIT --git-dir=$CODEMGR_WS/.git rev-parse --git-dir \
2466 2467 2>/dev/null)
2467 2468 [[ -z $codemgr_ws ]] && \
2468 2469 codemgr_ws=$($GIT rev-parse --git-dir 2>/dev/null)
2469 2470
2470 2471 if [[ "$codemgr_ws" == ".git" ]]; then
2471 2472 codemgr_ws="${PWD}/${codemgr_ws}"
2472 2473 fi
2473 2474
2474 2475 codemgr_ws=$(dirname $codemgr_ws) # Lose the '/.git'
2475 2476 CWS="$codemgr_ws"
2476 2477 elif [[ $SCM_MODE == "subversion" ]]; then
2477 2478 #
2478 2479 # Subversion priorities:
2479 2480 # 1. CODEMGR_WS from environment
2480 2481 # 2. Relative path from current directory to SVN repository root
2481 2482 #
2482 2483 if [[ -n $CODEMGR_WS && -d $CODEMGR_WS/.svn ]]; then
2483 2484 CWS=$CODEMGR_WS
2484 2485 else
2485 2486 svn info | while read line; do
2486 2487 if [[ $line == "URL: "* ]]; then
2487 2488 url=${line#URL: }
2488 2489 elif [[ $line == "Repository Root: "* ]]; then
2489 2490 repo=${line#Repository Root: }
2490 2491 fi
2491 2492 done
2492 2493
2493 2494 rel=${url#$repo}
2494 2495 CWS=${PWD%$rel}
2495 2496 fi
2496 2497 fi
2497 2498
2498 2499 #
2499 2500 # If no SCM has been determined, take either the environment setting
2500 2501 # setting for CODEMGR_WS, or the current directory if that wasn't set.
2501 2502 #
2502 2503 if [[ -z ${CWS} ]]; then
2503 2504 CWS=${CODEMGR_WS:-.}
2504 2505 fi
2505 2506
2506 2507 #
2507 2508 # If the command line options indicate no webrev generation, either
2508 2509 # explicitly (-n) or implicitly (-D but not -U), then there's a whole
2509 2510 # ton of logic we can skip.
2510 2511 #
2511 2512 # Instead of increasing indentation, we intentionally leave this loop
2512 2513 # body open here, and exit via break from multiple points within.
2513 2514 # Search for DO_EVERYTHING below to find the break points and closure.
2514 2515 #
2515 2516 for do_everything in 1; do
2516 2517
2517 2518 # DO_EVERYTHING: break point
2518 2519 if [[ -n $nflag || ( -z $Uflag && -n $Dflag ) ]]; then
2519 2520 break
2520 2521 fi
2521 2522
2522 2523 #
2523 2524 # If this manually set as the parent, and it appears to be an earlier webrev,
2524 2525 # then note that fact and set the parent to the raw_files/new subdirectory.
2525 2526 #
2526 2527 if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then
2527 2528 parent_webrev=$(readlink -f "$codemgr_parent")
2528 2529 codemgr_parent=$(readlink -f "$codemgr_parent/raw_files/new")
2529 2530 fi
2530 2531
2531 2532 if [[ -z $wflag && -z $lflag ]]; then
2532 2533 shift $(($OPTIND - 1))
2533 2534
2534 2535 if [[ $1 == "-" ]]; then
2535 2536 cat > $FLIST
2536 2537 flist_mode="stdin"
2537 2538 flist_done=1
2538 2539 shift
2539 2540 elif [[ -n $1 ]]; then
2540 2541 if [[ ! -r $1 ]]; then
2541 2542 print -u2 "$1: no such file or not readable"
2542 2543 usage
2543 2544 fi
2544 2545 cat $1 > $FLIST
2545 2546 flist_mode="file"
2546 2547 flist_file=$1
2547 2548 flist_done=1
2548 2549 shift
2549 2550 else
2550 2551 flist_mode="auto"
2551 2552 fi
2552 2553 fi
2553 2554
2554 2555 #
2555 2556 # Before we go on to further consider -l and -w, work out which SCM we think
2556 2557 # is in use.
2557 2558 #
2558 2559 case "$SCM_MODE" in
2559 2560 teamware|mercurial|git|subversion)
2560 2561 ;;
2561 2562 unknown)
2562 2563 if [[ $flist_mode == "auto" ]]; then
2563 2564 print -u2 "Unable to determine SCM in use and file list not specified"
2564 2565 print -u2 "See which_scm(1) for SCM detection information."
2565 2566 exit 1
2566 2567 fi
2567 2568 ;;
2568 2569 *)
2569 2570 if [[ $flist_mode == "auto" ]]; then
2570 2571 print -u2 "Unsupported SCM in use ($SCM_MODE) and file list not specified"
2571 2572 exit 1
2572 2573 fi
2573 2574 ;;
2574 2575 esac
2575 2576
2576 2577 print -u2 " SCM detected: $SCM_MODE"
2577 2578
2578 2579 if [[ -n $lflag ]]; then
2579 2580 #
2580 2581 # If the -l flag is given instead of the name of a file list,
2581 2582 # then generate the file list by extracting file names from a
2582 2583 # putback -n.
2583 2584 #
2584 2585 shift $(($OPTIND - 1))
2585 2586 if [[ $SCM_MODE == "teamware" ]]; then
2586 2587 flist_from_teamware "$*"
2587 2588 else
2588 2589 print -u2 -- "Error: -l option only applies to TeamWare"
2589 2590 exit 1
2590 2591 fi
2591 2592 flist_done=1
2592 2593 shift $#
2593 2594 elif [[ -n $wflag ]]; then
2594 2595 #
2595 2596 # If the -w is given then assume the file list is in Bonwick's "wx"
2596 2597 # command format, i.e. pathname lines alternating with SCCS comment
2597 2598 # lines with blank lines as separators. Use the SCCS comments later
2598 2599 # in building the index.html file.
2599 2600 #
2600 2601 shift $(($OPTIND - 1))
2601 2602 wxfile=$1
2602 2603 if [[ -z $wxfile && -n $CODEMGR_WS ]]; then
2603 2604 if [[ -r $CODEMGR_WS/wx/active ]]; then
2604 2605 wxfile=$CODEMGR_WS/wx/active
2605 2606 fi
2606 2607 fi
2607 2608
2608 2609 [[ -z $wxfile ]] && print -u2 "wx file not specified, and could not " \
2609 2610 "be auto-detected (check \$CODEMGR_WS)" && exit 1
2610 2611
2611 2612 if [[ ! -r $wxfile ]]; then
2612 2613 print -u2 "$wxfile: no such file or not readable"
2613 2614 usage
2614 2615 fi
2615 2616
2616 2617 print -u2 " File list from: wx 'active' file '$wxfile' ... \c"
2617 2618 flist_from_wx $wxfile
2618 2619 flist_done=1
2619 2620 if [[ -n "$*" ]]; then
2620 2621 shift
2621 2622 fi
2622 2623 elif [[ $flist_mode == "stdin" ]]; then
2623 2624 print -u2 " File list from: standard input"
2624 2625 elif [[ $flist_mode == "file" ]]; then
2625 2626 print -u2 " File list from: $flist_file"
2626 2627 fi
2627 2628
2628 2629 if [[ $# -gt 0 ]]; then
2629 2630 print -u2 "WARNING: unused arguments: $*"
2630 2631 fi
2631 2632
2632 2633 #
2633 2634 # Before we entered the DO_EVERYTHING loop, we should have already set CWS
2634 2635 # and CODEMGR_WS as needed. Here, we set the parent workspace.
2635 2636 #
2636 2637
2637 2638 if [[ $SCM_MODE == "teamware" ]]; then
2638 2639
2639 2640 #
2640 2641 # Teamware priorities:
2641 2642 #
2642 2643 # 1) via -p command line option
2643 2644 # 2) in the user environment
2644 2645 # 3) in the flist
2645 2646 # 4) automatically based on the workspace
2646 2647 #
2647 2648
2648 2649 #
2649 2650 # For 1, codemgr_parent will already be set. Here's 2:
2650 2651 #
2651 2652 [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
2652 2653 codemgr_parent=$CODEMGR_PARENT
2653 2654 if [[ -n $codemgr_parent && ! -d $codemgr_parent ]]; then
2654 2655 print -u2 "$codemgr_parent: no such directory"
2655 2656 exit 1
2656 2657 fi
2657 2658
2658 2659 #
2659 2660 # If we're in auto-detect mode and we haven't already gotten the file
2660 2661 # list, then see if we can get it by probing for wx.
2661 2662 #
2662 2663 if [[ -z $flist_done && $flist_mode == "auto" && -n $codemgr_ws ]]; then
2663 2664 if [[ ! -x $WX ]]; then
2664 2665 print -u2 "WARNING: wx not found!"
2665 2666 fi
2666 2667
2667 2668 #
2668 2669 # We need to use wx list -w so that we get renamed files, etc.
2669 2670 # but only if a wx active file exists-- otherwise wx will
2670 2671 # hang asking us to initialize our wx information.
2671 2672 #
2672 2673 if [[ -x $WX && -f $codemgr_ws/wx/active ]]; then
2673 2674 print -u2 " File list from: 'wx list -w' ... \c"
2674 2675 $WX list -w > $FLIST
2675 2676 $WX comments > /tmp/$$.wx_comments
2676 2677 wxfile=/tmp/$$.wx_comments
2677 2678 print -u2 "done"
2678 2679 flist_done=1
2679 2680 fi
2680 2681 fi
2681 2682
2682 2683 #
2683 2684 # If by hook or by crook we've gotten a file list by now (perhaps
2684 2685 # from the command line), eval it to extract environment variables from
2685 2686 # it: This is method 3 for finding the parent.
2686 2687 #
2687 2688 if [[ -z $flist_done ]]; then
2688 2689 flist_from_teamware
2689 2690 fi
2690 2691 env_from_flist
2691 2692
2692 2693 #
2693 2694 # (4) If we still don't have a value for codemgr_parent, get it
2694 2695 # from workspace.
2695 2696 #
2696 2697 [[ -z $codemgr_parent ]] && codemgr_parent=`workspace parent`
2697 2698 if [[ ! -d $codemgr_parent ]]; then
2698 2699 print -u2 "$CODEMGR_PARENT: no such parent workspace"
2699 2700 exit 1
2700 2701 fi
2701 2702
2702 2703 PWS=$codemgr_parent
2703 2704
2704 2705 [[ -n $parent_webrev ]] && RWS=$(workspace parent $CWS)
2705 2706
2706 2707 elif [[ $SCM_MODE == "mercurial" ]]; then
2707 2708 #
2708 2709 # Parent can either be specified with -p
2709 2710 # Specified with CODEMGR_PARENT in the environment
2710 2711 # or taken from hg's default path.
2711 2712 #
2712 2713
2713 2714 if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then
2714 2715 codemgr_parent=$CODEMGR_PARENT
2715 2716 fi
2716 2717
2717 2718 if [[ -z $codemgr_parent ]]; then
2718 2719 codemgr_parent=`hg path -R $codemgr_ws default 2>/dev/null`
2719 2720 fi
2720 2721
2721 2722 PWS=$codemgr_parent
2722 2723
2723 2724 #
2724 2725 # If the parent is a webrev, we want to do some things against
2725 2726 # the natural workspace parent (file list, comments, etc)
2726 2727 #
2727 2728 if [[ -n $parent_webrev ]]; then
2728 2729 real_parent=$(hg path -R $codemgr_ws default 2>/dev/null)
2729 2730 else
2730 2731 real_parent=$PWS
2731 2732 fi
2732 2733
2733 2734 #
2734 2735 # If hg-active exists, then we run it. In the case of no explicit
2735 2736 # flist given, we'll use it for our comments. In the case of an
2736 2737 # explicit flist given we'll try to use it for comments for any
2737 2738 # files mentioned in the flist.
2738 2739 #
2739 2740 if [[ -z $flist_done ]]; then
2740 2741 flist_from_mercurial $CWS $real_parent
2741 2742 flist_done=1
2742 2743 fi
2743 2744
2744 2745 #
2745 2746 # If we have a file list now, pull out any variables set
2746 2747 # therein. We do this now (rather than when we possibly use
2747 2748 # hg-active to find comments) to avoid stomping specifications
2748 2749 # in the user-specified flist.
2749 2750 #
2750 2751 if [[ -n $flist_done ]]; then
2751 2752 env_from_flist
2752 2753 fi
2753 2754
2754 2755 #
2755 2756 # Only call hg-active if we don't have a wx formatted file already
2756 2757 #
2757 2758 if [[ -x $HG_ACTIVE && -z $wxfile ]]; then
2758 2759 print " Comments from: hg-active -p $real_parent ...\c"
2759 2760 hg_active_wxfile $CWS $real_parent
2760 2761 print " Done."
2761 2762 fi
2762 2763
2763 2764 #
2764 2765 # At this point we must have a wx flist either from hg-active,
2765 2766 # or in general. Use it to try and find our parent revision,
2766 2767 # if we don't have one.
2767 2768 #
2768 2769 if [[ -z $HG_PARENT ]]; then
2769 2770 eval `$SED -e "s/#.*$//" $wxfile | $GREP HG_PARENT=`
2770 2771 fi
2771 2772
2772 2773 #
2773 2774 # If we still don't have a parent, we must have been given a
2774 2775 # wx-style active list with no HG_PARENT specification, run
2775 2776 # hg-active and pull an HG_PARENT out of it, ignore the rest.
2776 2777 #
2777 2778 if [[ -z $HG_PARENT && -x $HG_ACTIVE ]]; then
2778 2779 $HG_ACTIVE -w $codemgr_ws -p $real_parent | \
2779 2780 eval `$SED -e "s/#.*$//" | $GREP HG_PARENT=`
2780 2781 elif [[ -z $HG_PARENT ]]; then
2781 2782 print -u2 "Error: Cannot discover parent revision"
2782 2783 exit 1
2783 2784 fi
2784 2785
2785 2786 pnode=$(trim_digest $HG_PARENT)
2786 2787 PRETTY_PWS="${PWS} (at ${pnode})"
2787 2788 cnode=$(hg parent -R $codemgr_ws --template '{node|short}' \
2788 2789 2>/dev/null)
2789 2790 PRETTY_CWS="${CWS} (at ${cnode})"}
2790 2791 elif [[ $SCM_MODE == "git" ]]; then
2791 2792 #
2792 2793 # Parent can either be specified with -p, or specified with
2793 2794 # CODEMGR_PARENT in the environment.
2794 2795 #
2795 2796
2796 2797 if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then
2797 2798 codemgr_parent=$CODEMGR_PARENT
2798 2799 fi
2799 2800
2800 2801 # Try to figure out the parent based on the branch the current
2801 2802 # branch is tracking, if we fail, use origin/master
2802 2803 this_branch=$($GIT branch | nawk '$1 == "*" { print $2 }')
2803 2804 par_branch="origin/master"
2804 2805
2805 2806 # If we're not on a branch there's nothing we can do
2806 2807 if [[ $this_branch != "(no branch)" ]]; then
2807 2808 $GIT for-each-ref \
2808 2809 --format='%(refname:short) %(upstream:short)' refs/heads/ | \
2809 2810 while read local remote; do \
2810 2811 [[ "$local" == "$this_branch" ]] && par_branch="$remote"; \
2811 2812 done
2812 2813 fi
2813 2814
2814 2815 if [[ -z $codemgr_parent ]]; then
2815 2816 codemgr_parent=$par_branch
2816 2817 fi
2817 2818 PWS=$codemgr_parent
2818 2819
2819 2820 #
2820 2821 # If the parent is a webrev, we want to do some things against
2821 2822 # the natural workspace parent (file list, comments, etc)
2822 2823 #
2823 2824 if [[ -n $parent_webrev ]]; then
2824 2825 real_parent=$par_branch
2825 2826 else
2826 2827 real_parent=$PWS
2827 2828 fi
2828 2829
2829 2830 if [[ -z $flist_done ]]; then
2830 2831 flist_from_git "$CWS" "$real_parent"
2831 2832 flist_done=1
2832 2833 fi
2833 2834
2834 2835 #
2835 2836 # If we have a file list now, pull out any variables set
2836 2837 # therein.
2837 2838 #
2838 2839 if [[ -n $flist_done ]]; then
2839 2840 env_from_flist
2840 2841 fi
2841 2842
2842 2843 #
2843 2844 # If we don't have a wx-format file list, build one we can pull change
2844 2845 # comments from.
2845 2846 #
2846 2847 if [[ -z $wxfile ]]; then
2847 2848 print " Comments from: git...\c"
2848 2849 git_wxfile "$CWS" "$real_parent"
2849 2850 print " Done."
2850 2851 fi
2851 2852
2852 2853 if [[ -z $GIT_PARENT ]]; then
2853 2854 GIT_PARENT=$($GIT merge-base "$real_parent" HEAD)
2854 2855 fi
2855 2856 if [[ -z $GIT_PARENT ]]; then
2856 2857 print -u2 "Error: Cannot discover parent revision"
2857 2858 exit 1
2858 2859 fi
2859 2860
2860 2861 pnode=$(trim_digest $GIT_PARENT)
2861 2862
2862 2863 if [[ $real_parent == */* ]]; then
2863 2864 origin=$(echo $real_parent | cut -d/ -f1)
2864 2865 origin=$($GIT remote -v | \
2865 2866 $AWK '$1 == "'$origin'" { print $2; exit }')
2866 2867 PRETTY_PWS="${PWS} (${origin} at ${pnode})"
2867 2868 else
2868 2869 PRETTY_PWS="${PWS} (at ${pnode})"
2869 2870 fi
2870 2871
2871 2872 cnode=$($GIT --git-dir=${codemgr_ws}/.git rev-parse --short=12 HEAD \
2872 2873 2>/dev/null)
2873 2874 PRETTY_CWS="${CWS} (at ${cnode})"
2874 2875 elif [[ $SCM_MODE == "subversion" ]]; then
2875 2876
2876 2877 #
2877 2878 # We only will have a real parent workspace in the case one
2878 2879 # was specified (be it an older webrev, or another checkout).
2879 2880 #
2880 2881 [[ -n $codemgr_parent ]] && PWS=$codemgr_parent
2881 2882
2882 2883 if [[ -z $flist_done && $flist_mode == "auto" ]]; then
2883 2884 flist_from_subversion $CWS $OLDPWD
2884 2885 fi
2885 2886 else
2886 2887 if [[ $SCM_MODE == "unknown" ]]; then
2887 2888 print -u2 " Unknown type of SCM in use"
2888 2889 else
2889 2890 print -u2 " Unsupported SCM in use: $SCM_MODE"
2890 2891 fi
2891 2892
2892 2893 env_from_flist
2893 2894
2894 2895 if [[ -z $CODEMGR_WS ]]; then
2895 2896 print -u2 "SCM not detected/supported and CODEMGR_WS not specified"
2896 2897 exit 1
2897 2898 fi
2898 2899
2899 2900 if [[ -z $CODEMGR_PARENT ]]; then
2900 2901 print -u2 "SCM not detected/supported and CODEMGR_PARENT not specified"
2901 2902 exit 1
2902 2903 fi
2903 2904
2904 2905 CWS=$CODEMGR_WS
2905 2906 PWS=$CODEMGR_PARENT
2906 2907 fi
2907 2908
2908 2909 #
2909 2910 # If the user didn't specify a -i option, check to see if there is a
2910 2911 # webrev-info file in the workspace directory.
2911 2912 #
2912 2913 if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then
2913 2914 iflag=1
2914 2915 INCLUDE_FILE="$CWS/webrev-info"
2915 2916 fi
2916 2917
2917 2918 if [[ -n $iflag ]]; then
2918 2919 if [[ ! -r $INCLUDE_FILE ]]; then
2919 2920 print -u2 "include file '$INCLUDE_FILE' does not exist or is" \
2920 2921 "not readable."
2921 2922 exit 1
2922 2923 else
2923 2924 #
2924 2925 # $INCLUDE_FILE may be a relative path, and the script alters
2925 2926 # PWD, so we just stash a copy in /tmp.
2926 2927 #
2927 2928 cp $INCLUDE_FILE /tmp/$$.include
2928 2929 fi
2929 2930 fi
2930 2931
2931 2932 # DO_EVERYTHING: break point
2932 2933 if [[ -n $Nflag ]]; then
2933 2934 break
2934 2935 fi
2935 2936
2936 2937 typeset -A itsinfo
2937 2938 typeset -r its_sed_script=/tmp/$$.its_sed
2938 2939 valid_prefixes=
2939 2940 if [[ -z $nflag ]]; then
2940 2941 DEFREGFILE="$(/bin/dirname "$(whence $0)")/../etc/its.reg"
2941 2942 if [[ -n $Iflag ]]; then
2942 2943 REGFILE=$ITSREG
2943 2944 elif [[ -r $HOME/.its.reg ]]; then
2944 2945 REGFILE=$HOME/.its.reg
2945 2946 else
2946 2947 REGFILE=$DEFREGFILE
2947 2948 fi
2948 2949 if [[ ! -r $REGFILE ]]; then
2949 2950 print "ERROR: Unable to read database registry file $REGFILE"
2950 2951 exit 1
2951 2952 elif [[ $REGFILE != $DEFREGFILE ]]; then
2952 2953 print " its.reg from: $REGFILE"
2953 2954 fi
2954 2955
2955 2956 $SED -e '/^#/d' -e '/^[ ]*$/d' $REGFILE | while read LINE; do
2956 2957
2957 2958 name=${LINE%%=*}
2958 2959 value="${LINE#*=}"
2959 2960
2960 2961 if [[ $name == PREFIX ]]; then
2961 2962 p=${value}
2962 2963 valid_prefixes="${p} ${valid_prefixes}"
2963 2964 else
2964 2965 itsinfo["${p}_${name}"]="${value}"
2965 2966 fi
2966 2967 done
2967 2968
2968 2969
2969 2970 DEFCONFFILE="$(/bin/dirname "$(whence $0)")/../etc/its.conf"
2970 2971 CONFFILES=$DEFCONFFILE
2971 2972 if [[ -r $HOME/.its.conf ]]; then
2972 2973 CONFFILES="${CONFFILES} $HOME/.its.conf"
2973 2974 fi
2974 2975 if [[ -n $Cflag ]]; then
2975 2976 CONFFILES="${CONFFILES} ${ITSCONF}"
2976 2977 fi
2977 2978 its_domain=
2978 2979 its_priority=
2979 2980 for cf in ${CONFFILES}; do
2980 2981 if [[ ! -r $cf ]]; then
2981 2982 print "ERROR: Unable to read database configuration file $cf"
2982 2983 exit 1
2983 2984 elif [[ $cf != $DEFCONFFILE ]]; then
2984 2985 print " its.conf: reading $cf"
2985 2986 fi
2986 2987 $SED -e '/^#/d' -e '/^[ ]*$/d' $cf | while read LINE; do
2987 2988 eval "${LINE}"
2988 2989 done
2989 2990 done
2990 2991
2991 2992 #
2992 2993 # If an information tracking system is explicitly identified by prefix,
2993 2994 # we want to disregard the specified priorities and resolve it accordingly.
2994 2995 #
2995 2996 # To that end, we'll build a sed script to do each valid prefix in turn.
2996 2997 #
2997 2998 for p in ${valid_prefixes}; do
2998 2999 #
2999 3000 # When an informational URL was provided, translate it to a
3000 3001 # hyperlink. When omitted, simply use the prefix text.
3001 3002 #
3002 3003 if [[ -z ${itsinfo["${p}_INFO"]} ]]; then
3003 3004 itsinfo["${p}_INFO"]=${p}
3004 3005 else
3005 3006 itsinfo["${p}_INFO"]="<a href=\\\"${itsinfo["${p}_INFO"]}\\\">${p}</a>"
3006 3007 fi
3007 3008
3008 3009 #
3009 3010 # Assume that, for this invocation of webrev, all references
3010 3011 # to this information tracking system should resolve through
3011 3012 # the same URL.
3012 3013 #
3013 3014 # If the caller specified -O, then always use EXTERNAL_URL.
3014 3015 #
3015 3016 # Otherwise, look in the list of domains for a matching
3016 3017 # INTERNAL_URL.
3017 3018 #
3018 3019 [[ -z $Oflag ]] && for d in ${its_domain}; do
3019 3020 if [[ -n ${itsinfo["${p}_INTERNAL_URL_${d}"]} ]]; then
3020 3021 itsinfo["${p}_URL"]="${itsinfo[${p}_INTERNAL_URL_${d}]}"
3021 3022 break
3022 3023 fi
3023 3024 done
3024 3025 if [[ -z ${itsinfo["${p}_URL"]} ]]; then
3025 3026 itsinfo["${p}_URL"]="${itsinfo[${p}_EXTERNAL_URL]}"
3026 3027 fi
3027 3028
3028 3029 #
3029 3030 # Turn the destination URL into a hyperlink
3030 3031 #
3031 3032 itsinfo["${p}_URL"]="<a href=\\\"${itsinfo[${p}_URL]}\\\">&</a>"
3032 3033
3033 3034 # The character class below contains a literal tab
3034 3035 print "/^${p}[: ]/ {
3035 3036 s;${itsinfo[${p}_REGEX]};${itsinfo[${p}_URL]};g
3036 3037 s;^${p};${itsinfo[${p}_INFO]};
3037 3038 }" >> ${its_sed_script}
3038 3039 done
3039 3040
3040 3041 #
3041 3042 # The previous loop took care of explicit specification. Now use
3042 3043 # the configured priorities to attempt implicit translations.
3043 3044 #
3044 3045 for p in ${its_priority}; do
3045 3046 print "/^${itsinfo[${p}_REGEX]}[ ]/ {
3046 3047 s;^${itsinfo[${p}_REGEX]};${itsinfo[${p}_URL]};g
3047 3048 }" >> ${its_sed_script}
3048 3049 done
3049 3050 fi
3050 3051
3051 3052 #
3052 3053 # Search for DO_EVERYTHING above for matching "for" statement
3053 3054 # and explanation of this terminator.
3054 3055 #
3055 3056 done
3056 3057
3057 3058 #
3058 3059 # Output directory.
3059 3060 #
3060 3061 WDIR=${WDIR:-$CWS/webrev}
3061 3062
3062 3063 #
3063 3064 # Name of the webrev, derived from the workspace name or output directory;
3064 3065 # in the future this could potentially be an option.
3065 3066 #
3066 3067 if [[ -n $oflag ]]; then
3067 3068 WNAME=${WDIR##*/}
3068 3069 else
3069 3070 WNAME=${CWS##*/}
3070 3071 fi
3071 3072
3072 3073 # Make sure remote target is well formed for remote upload/delete.
3073 3074 if [[ -n $Dflag || -n $Uflag ]]; then
3074 3075 #
3075 3076 # If remote target is not specified, build it from scratch using
3076 3077 # the default values.
3077 3078 #
3078 3079 if [[ -z $tflag ]]; then
3079 3080 remote_target=${DEFAULT_REMOTE_HOST}:${WNAME}
3080 3081 else
3081 3082 #
3082 3083 # Check upload target prefix first.
3083 3084 #
3084 3085 if [[ "${remote_target}" != ${rsync_prefix}* &&
3085 3086 "${remote_target}" != ${ssh_prefix}* ]]; then
3086 3087 print "ERROR: invalid prefix of upload URI" \
3087 3088 "($remote_target)"
3088 3089 exit 1
3089 3090 fi
3090 3091 #
3091 3092 # If destination specification is not in the form of
3092 3093 # host_spec:remote_dir then assume it is just remote hostname
3093 3094 # and append a colon and destination directory formed from
3094 3095 # local webrev directory name.
3095 3096 #
3096 3097 typeset target_no_prefix=${remote_target##*://}
3097 3098 if [[ ${target_no_prefix} == *:* ]]; then
3098 3099 if [[ "${remote_target}" == *: ]]; then
3099 3100 remote_target=${remote_target}${WNAME}
3100 3101 fi
3101 3102 else
3102 3103 if [[ ${target_no_prefix} == */* ]]; then
3103 3104 print "ERROR: badly formed upload URI" \
3104 3105 "($remote_target)"
3105 3106 exit 1
3106 3107 else
3107 3108 remote_target=${remote_target}:${WNAME}
3108 3109 fi
3109 3110 fi
3110 3111 fi
3111 3112
3112 3113 #
3113 3114 # Strip trailing slash. Each upload method will deal with directory
3114 3115 # specification separately.
3115 3116 #
3116 3117 remote_target=${remote_target%/}
3117 3118 fi
3118 3119
3119 3120 #
3120 3121 # Option -D by itself (option -U not present) implies no webrev generation.
3121 3122 #
3122 3123 if [[ -z $Uflag && -n $Dflag ]]; then
3123 3124 delete_webrev 1 1
3124 3125 exit $?
3125 3126 fi
3126 3127
3127 3128 #
3128 3129 # Do not generate the webrev, just upload it or delete it.
3129 3130 #
3130 3131 if [[ -n $nflag ]]; then
3131 3132 if [[ -n $Dflag ]]; then
3132 3133 delete_webrev 1 1
3133 3134 (( $? == 0 )) || exit $?
3134 3135 fi
3135 3136 if [[ -n $Uflag ]]; then
3136 3137 upload_webrev
3137 3138 exit $?
3138 3139 fi
3139 3140 fi
3140 3141
3141 3142 if [ "${WDIR%%/*}" ]; then
3142 3143 WDIR=$PWD/$WDIR
3143 3144 fi
3144 3145
3145 3146 if [[ ! -d $WDIR ]]; then
3146 3147 mkdir -p $WDIR
3147 3148 (( $? != 0 )) && exit 1
3148 3149 fi
3149 3150
3150 3151 #
3151 3152 # Summarize what we're going to do.
3152 3153 #
3153 3154 print " Workspace: ${PRETTY_CWS:-$CWS}"
3154 3155 if [[ -n $parent_webrev ]]; then
3155 3156 print "Compare against: webrev at $parent_webrev"
3156 3157 else
3157 3158 print "Compare against: ${PRETTY_PWS:-$PWS}"
3158 3159 fi
3159 3160
3160 3161 [[ -n $INCLUDE_FILE ]] && print " Including: $INCLUDE_FILE"
3161 3162 print " Output to: $WDIR"
3162 3163
3163 3164 #
3164 3165 # Save the file list in the webrev dir
3165 3166 #
3166 3167 [[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list
3167 3168
3168 3169 rm -f $WDIR/$WNAME.patch
3169 3170 rm -f $WDIR/$WNAME.ps
3170 3171 rm -f $WDIR/$WNAME.pdf
3171 3172
3172 3173 touch $WDIR/$WNAME.patch
3173 3174
3174 3175 print " Output Files:"
3175 3176
3176 3177 #
3177 3178 # Clean up the file list: Remove comments, blank lines and env variables.
3178 3179 #
3179 3180 $SED -e "s/#.*$//" -e "/=/d" -e "/^[ ]*$/d" $FLIST > /tmp/$$.flist.clean
3180 3181 FLIST=/tmp/$$.flist.clean
3181 3182
3182 3183 #
3183 3184 # For Mercurial, create a cache of manifest entries.
3184 3185 #
3185 3186 if [[ $SCM_MODE == "mercurial" ]]; then
3186 3187 #
3187 3188 # Transform the FLIST into a temporary sed script that matches
3188 3189 # relevant entries in the Mercurial manifest as follows:
3189 3190 # 1) The script will be used against the parent revision manifest,
3190 3191 # so for FLIST lines that have two filenames (a renamed file)
3191 3192 # keep only the old name.
3192 3193 # 2) Escape all forward slashes the filename.
3193 3194 # 3) Change the filename into another sed command that matches
3194 3195 # that file in "hg manifest -v" output: start of line, three
3195 3196 # octal digits for file permissions, space, a file type flag
3196 3197 # character, space, the filename, end of line.
3197 3198 # 4) Eliminate any duplicate entries. (This can occur if a
3198 3199 # file has been used as the source of an hg cp and it's
3199 3200 # also been modified in the same changeset.)
3200 3201 #
3201 3202 SEDFILE=/tmp/$$.manifest.sed
3202 3203 $SED '
3203 3204 s#^[^ ]* ##
3204 3205 s#/#\\\/#g
3205 3206 s#^.*$#/^... . &$/p#
3206 3207 ' < $FLIST | $SORT -u > $SEDFILE
3207 3208
3208 3209 #
3209 3210 # Apply the generated script to the output of "hg manifest -v"
3210 3211 # to get the relevant subset for this webrev.
3211 3212 #
3212 3213 HG_PARENT_MANIFEST=/tmp/$$.manifest
3213 3214 hg -R $CWS manifest -v -r $HG_PARENT |
3214 3215 $SED -n -f $SEDFILE > $HG_PARENT_MANIFEST
3215 3216 fi
3216 3217
3217 3218 #
3218 3219 # First pass through the files: generate the per-file webrev HTML-files.
3219 3220 #
3220 3221 cat $FLIST | while read LINE
3221 3222 do
3222 3223 set - $LINE
3223 3224 P=$1
3224 3225
3225 3226 #
3226 3227 # Normally, each line in the file list is just a pathname of a
3227 3228 # file that has been modified or created in the child. A file
3228 3229 # that is renamed in the child workspace has two names on the
3229 3230 # line: new name followed by the old name.
3230 3231 #
3231 3232 oldname=""
3232 3233 oldpath=""
3233 3234 rename=
3234 3235 if [[ $# -eq 2 ]]; then
3235 3236 PP=$2 # old filename
3236 3237 if [[ -f $PP ]]; then
3237 3238 oldname=" (copied from $PP)"
3238 3239 else
3239 3240 oldname=" (renamed from $PP)"
3240 3241 fi
3241 3242 oldpath="$PP"
3242 3243 rename=1
3243 3244 PDIR=${PP%/*}
3244 3245 if [[ $PDIR == $PP ]]; then
3245 3246 PDIR="." # File at root of workspace
3246 3247 fi
3247 3248
3248 3249 PF=${PP##*/}
3249 3250
3250 3251 DIR=${P%/*}
3251 3252 if [[ $DIR == $P ]]; then
3252 3253 DIR="." # File at root of workspace
3253 3254 fi
3254 3255
3255 3256 F=${P##*/}
3256 3257
3257 3258 else
3258 3259 DIR=${P%/*}
3259 3260 if [[ "$DIR" == "$P" ]]; then
3260 3261 DIR="." # File at root of workspace
3261 3262 fi
3262 3263
3263 3264 F=${P##*/}
3264 3265
3265 3266 PP=$P
3266 3267 PDIR=$DIR
3267 3268 PF=$F
3268 3269 fi
3269 3270
3270 3271 COMM=`getcomments html $P $PP`
3271 3272
3272 3273 print "\t$P$oldname\n\t\t\c"
3273 3274
3274 3275 # Make the webrev mirror directory if necessary
3275 3276 mkdir -p $WDIR/$DIR
3276 3277
3277 3278 #
3278 3279 # We stash old and new files into parallel directories in $WDIR
3279 3280 # and do our diffs there. This makes it possible to generate
3280 3281 # clean looking diffs which don't have absolute paths present.
3281 3282 #
3282 3283
3283 3284 build_old_new "$WDIR" "$PWS" "$PDIR" "$PF" "$CWS" "$DIR" "$F" || \
3284 3285 continue
3285 3286
3286 3287 #
3287 3288 # Keep the old PWD around, so we can safely switch back after
3288 3289 # diff generation, such that build_old_new runs in a
3289 3290 # consistent environment.
3290 3291 #
3291 3292 OWD=$PWD
3292 3293 cd $WDIR/raw_files
3293 3294 ofile=old/$PDIR/$PF
3294 3295 nfile=new/$DIR/$F
3295 3296
3296 3297 mv_but_nodiff=
3297 3298 cmp $ofile $nfile > /dev/null 2>&1
3298 3299 if [[ $? == 0 && $rename == 1 ]]; then
3299 3300 mv_but_nodiff=1
3300 3301 fi
3301 3302
3302 3303 #
3303 3304 # If we have old and new versions of the file then run the appropriate
3304 3305 # diffs. This is complicated by a couple of factors:
3305 3306 #
3306 3307 # - renames must be handled specially: we emit a 'remove'
3307 3308 # diff and an 'add' diff
3308 3309 # - new files and deleted files must be handled specially
3309 3310 # - Solaris patch(1m) can't cope with file creation
3310 3311 # (and hence renames) as of this writing.
3311 3312 # - To make matters worse, gnu patch doesn't interpret the
3312 3313 # output of Solaris diff properly when it comes to
3313 3314 # adds and deletes. We need to do some "cleansing"
3314 3315 # transformations:
3315 3316 # [to add a file] @@ -1,0 +X,Y @@ --> @@ -0,0 +X,Y @@
3316 3317 # [to del a file] @@ -X,Y +1,0 @@ --> @@ -X,Y +0,0 @@
3317 3318 #
3318 3319 cleanse_rmfile="$SED 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'"
3319 3320 cleanse_newfile="$SED 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'"
3320 3321
3321 3322 rm -f $WDIR/$DIR/$F.patch
3322 3323 if [[ -z $rename ]]; then
3323 3324 if [ ! -f "$ofile" ]; then
3324 3325 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
3325 3326 > $WDIR/$DIR/$F.patch
3326 3327 elif [ ! -f "$nfile" ]; then
3327 3328 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
3328 3329 > $WDIR/$DIR/$F.patch
3329 3330 else
3330 3331 diff -u $ofile $nfile > $WDIR/$DIR/$F.patch
3331 3332 fi
3332 3333 else
3333 3334 diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
3334 3335 > $WDIR/$DIR/$F.patch
3335 3336
3336 3337 diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
3337 3338 >> $WDIR/$DIR/$F.patch
3338 3339 fi
3339 3340
3340 3341 #
3341 3342 # Tack the patch we just made onto the accumulated patch for the
3342 3343 # whole wad.
3343 3344 #
3344 3345 cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch
3345 3346
3346 3347 print " patch\c"
3347 3348
3348 3349 if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
3349 3350
3350 3351 ${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff
3351 3352 diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
3352 3353 > $WDIR/$DIR/$F.cdiff.html
3353 3354 print " cdiffs\c"
3354 3355
3355 3356 ${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff
3356 3357 diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
3357 3358 > $WDIR/$DIR/$F.udiff.html
3358 3359
3359 3360 print " udiffs\c"
3360 3361
3361 3362 if [[ -x $WDIFF ]]; then
3362 3363 $WDIFF -c "$COMM" \
3363 3364 -t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \
3364 3365 $WDIR/$DIR/$F.wdiff.html 2>/dev/null
3365 3366 if [[ $? -eq 0 ]]; then
3366 3367 print " wdiffs\c"
3367 3368 else
3368 3369 print " wdiffs[fail]\c"
3369 3370 fi
3370 3371 fi
3371 3372
3372 3373 sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
3373 3374 > $WDIR/$DIR/$F.sdiff.html
3374 3375 print " sdiffs\c"
3375 3376
3376 3377 print " frames\c"
3377 3378
3378 3379 rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
3379 3380
3380 3381 difflines $ofile $nfile > $WDIR/$DIR/$F.count
3381 3382
3382 3383 elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
3383 3384 # renamed file: may also have differences
3384 3385 difflines $ofile $nfile > $WDIR/$DIR/$F.count
3385 3386 elif [[ -f $nfile ]]; then
3386 3387 # new file: count added lines
3387 3388 difflines /dev/null $nfile > $WDIR/$DIR/$F.count
3388 3389 elif [[ -f $ofile ]]; then
3389 3390 # old file: count deleted lines
3390 3391 difflines $ofile /dev/null > $WDIR/$DIR/$F.count
3391 3392 fi
3392 3393
3393 3394 #
3394 3395 # Now we generate the postscript for this file. We generate diffs
3395 3396 # only in the event that there is delta, or the file is new (it seems
3396 3397 # tree-killing to print out the contents of deleted files).
3397 3398 #
3398 3399 if [[ -f $nfile ]]; then
3399 3400 ocr=$ofile
3400 3401 [[ ! -f $ofile ]] && ocr=/dev/null
3401 3402
3402 3403 if [[ -z $mv_but_nodiff ]]; then
3403 3404 textcomm=`getcomments text $P $PP`
3404 3405 if [[ -x $CODEREVIEW ]]; then
3405 3406 $CODEREVIEW -y "$textcomm" \
3406 3407 -e $ocr $nfile \
3407 3408 > /tmp/$$.psfile 2>/dev/null &&
3408 3409 cat /tmp/$$.psfile >> $WDIR/$WNAME.ps
3409 3410 if [[ $? -eq 0 ]]; then
3410 3411 print " ps\c"
3411 3412 else
3412 3413 print " ps[fail]\c"
3413 3414 fi
3414 3415 fi
3415 3416 fi
3416 3417 fi
3417 3418
3418 3419 if [[ -f $ofile ]]; then
3419 3420 source_to_html Old $PP < $ofile > $WDIR/$DIR/$F-.html
3420 3421 print " old\c"
3421 3422 fi
3422 3423
3423 3424 if [[ -f $nfile ]]; then
3424 3425 source_to_html New $P < $nfile > $WDIR/$DIR/$F.html
3425 3426 print " new\c"
3426 3427 fi
3427 3428
3428 3429 cd $OWD
3429 3430
3430 3431 print
3431 3432 done
3432 3433
3433 3434 frame_nav_js > $WDIR/ancnav.js
3434 3435 frame_navigation > $WDIR/ancnav.html
3435 3436
3436 3437 if [[ ! -f $WDIR/$WNAME.ps ]]; then
3437 3438 print " Generating PDF: Skipped: no output available"
3438 3439 elif [[ -x $CODEREVIEW && -x $PS2PDF ]]; then
3439 3440 print " Generating PDF: \c"
3440 3441 fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf
3441 3442 print "Done."
3442 3443 else
3443 3444 print " Generating PDF: Skipped: missing 'ps2pdf' or 'codereview'"
3444 3445 fi
3445 3446
3446 3447 # If we're in OpenSolaris mode and there's a closed dir under $WDIR,
3447 3448 # delete it - prevent accidental publishing of closed source
3448 3449
3449 3450 if [[ -n "$Oflag" ]]; then
3450 3451 $FIND $WDIR -type d -name closed -exec /bin/rm -rf {} \;
3451 3452 fi
3452 3453
3453 3454 # Now build the index.html file that contains
3454 3455 # links to the source files and their diffs.
3455 3456
3456 3457 cd $CWS
3457 3458
3458 3459 # Save total changed lines for Code Inspection.
3459 3460 print "$TOTL" > $WDIR/TotalChangedLines
3460 3461
3461 3462 print " index.html: \c"
3462 3463 INDEXFILE=$WDIR/index.html
3463 3464 exec 3<&1 # duplicate stdout to FD3.
3464 3465 exec 1<&- # Close stdout.
3465 3466 exec > $INDEXFILE # Open stdout to index file.
3466 3467
3467 3468 print "$HTML<head>$STDHEAD"
3468 3469 print "<title>$WNAME</title>"
3469 3470 print "</head>"
3470 3471 print "<body id=\"SUNWwebrev\">"
3471 3472 print "<div class=\"summary\">"
3472 3473 print "<h2>Code Review for $WNAME</h2>"
↓ open down ↓ |
3434 lines elided |
↑ open up ↑ |
3473 3474
3474 3475 print "<table>"
3475 3476
3476 3477 #
3477 3478 # Get the preparer's name:
3478 3479 #
3479 3480 # If the SCM detected is Mercurial, and the configuration property
3480 3481 # ui.username is available, use that, but be careful to properly escape
3481 3482 # angle brackets (HTML syntax characters) in the email address.
3482 3483 #
3484 +# For git, use the user.name and user.email properties if they are set.
3485 +#
3483 3486 # Otherwise, use the current userid in the form "John Doe (jdoe)", but
3484 3487 # to maintain compatibility with passwd(4), we must support '&' substitutions.
3485 3488 #
3486 3489 preparer=
3487 3490 if [[ "$SCM_MODE" == mercurial ]]; then
3488 3491 preparer=`hg showconfig ui.username 2>/dev/null`
3489 - if [[ -n "$preparer" ]]; then
3490 - preparer="$(echo "$preparer" | html_quote)"
3492 +elif [[ "$SCM_MODE" == git ]]; then
3493 + preparer=$( git config user.name 2>/dev/null )
3494 + prepmail=$( git config user.email 2>/dev/null )
3495 + if [[ -n "$prepmail" ]]; then
3496 + preparer="$preparer <$prepmail>"
3491 3497 fi
3492 3498 fi
3499 +if [[ -n "$preparer" ]]; then
3500 + preparer="$(echo "$preparer" | html_quote)"
3501 +fi
3493 3502 if [[ -z "$preparer" ]]; then
3494 3503 preparer=$(
3495 3504 $PERL -e '
3496 3505 ($login, $pw, $uid, $gid, $quota, $cmt, $gcos) = getpwuid($<);
3497 3506 if ($login) {
3498 3507 $gcos =~ s/\&/ucfirst($login)/e;
3499 3508 printf "%s (%s)\n", $gcos, $login;
3500 3509 } else {
3501 3510 printf "(unknown)\n";
3502 3511 }
3503 3512 ')
3504 3513 fi
3505 3514
3506 3515 PREPDATE=$(LC_ALL=C /usr/bin/date +%Y-%b-%d\ %R\ %z\ %Z)
3507 3516 print "<tr><th>Prepared by:</th><td>$preparer on $PREPDATE</td></tr>"
3508 3517 print "<tr><th>Workspace:</th><td>${PRETTY_CWS:-$CWS}"
3509 3518 print "</td></tr>"
3510 3519 print "<tr><th>Compare against:</th><td>"
3511 3520 if [[ -n $parent_webrev ]]; then
3512 3521 print "webrev at $parent_webrev"
3513 3522 else
3514 3523 print "${PRETTY_PWS:-$PWS}"
3515 3524 fi
3516 3525 print "</td></tr>"
3517 3526 print "<tr><th>Summary of changes:</th><td>"
3518 3527 printCI $TOTL $TINS $TDEL $TMOD $TUNC
3519 3528 print "</td></tr>"
3520 3529
3521 3530 if [[ -f $WDIR/$WNAME.patch ]]; then
3522 3531 wpatch_url="$(print $WNAME.patch | url_encode)"
3523 3532 print "<tr><th>Patch of changes:</th><td>"
3524 3533 print "<a href=\"$wpatch_url\">$WNAME.patch</a></td></tr>"
3525 3534 fi
3526 3535 if [[ -f $WDIR/$WNAME.pdf ]]; then
3527 3536 wpdf_url="$(print $WNAME.pdf | url_encode)"
3528 3537 print "<tr><th>Printable review:</th><td>"
3529 3538 print "<a href=\"$wpdf_url\">$WNAME.pdf</a></td></tr>"
3530 3539 fi
3531 3540
3532 3541 if [[ -n "$iflag" ]]; then
3533 3542 print "<tr><th>Author comments:</th><td><div>"
3534 3543 cat /tmp/$$.include
3535 3544 print "</div></td></tr>"
3536 3545 fi
3537 3546 print "</table>"
3538 3547 print "</div>"
3539 3548
3540 3549 #
3541 3550 # Second pass through the files: generate the rest of the index file
3542 3551 #
3543 3552 cat $FLIST | while read LINE
3544 3553 do
3545 3554 set - $LINE
3546 3555 P=$1
3547 3556
3548 3557 if [[ $# == 2 ]]; then
3549 3558 PP=$2
3550 3559 oldname="$PP"
3551 3560 else
3552 3561 PP=$P
3553 3562 oldname=""
3554 3563 fi
3555 3564
3556 3565 mv_but_nodiff=
3557 3566 cmp $WDIR/raw_files/old/$PP $WDIR/raw_files/new/$P > /dev/null 2>&1
3558 3567 if [[ $? == 0 && -n "$oldname" ]]; then
3559 3568 mv_but_nodiff=1
3560 3569 fi
3561 3570
3562 3571 DIR=${P%/*}
3563 3572 if [[ $DIR == $P ]]; then
3564 3573 DIR="." # File at root of workspace
3565 3574 fi
3566 3575
3567 3576 # Avoid processing the same file twice.
3568 3577 # It's possible for renamed files to
3569 3578 # appear twice in the file list
3570 3579
3571 3580 F=$WDIR/$P
3572 3581
3573 3582 print "<p>"
3574 3583
3575 3584 # If there's a diffs file, make diffs links
3576 3585
3577 3586 if [[ -f $F.cdiff.html ]]; then
3578 3587 cdiff_url="$(print $P.cdiff.html | url_encode)"
3579 3588 udiff_url="$(print $P.udiff.html | url_encode)"
3580 3589 print "<a href=\"$cdiff_url\">Cdiffs</a>"
3581 3590 print "<a href=\"$udiff_url\">Udiffs</a>"
3582 3591
3583 3592 if [[ -f $F.wdiff.html && -x $WDIFF ]]; then
3584 3593 wdiff_url="$(print $P.wdiff.html | url_encode)"
3585 3594 print "<a href=\"$wdiff_url\">Wdiffs</a>"
3586 3595 fi
3587 3596
3588 3597 sdiff_url="$(print $P.sdiff.html | url_encode)"
3589 3598 print "<a href=\"$sdiff_url\">Sdiffs</a>"
3590 3599
3591 3600 frames_url="$(print $P.frames.html | url_encode)"
3592 3601 print "<a href=\"$frames_url\">Frames</a>"
3593 3602 else
3594 3603 print " ------ ------ ------"
3595 3604
3596 3605 if [[ -x $WDIFF ]]; then
3597 3606 print " ------"
3598 3607 fi
3599 3608
3600 3609 print " ------"
3601 3610 fi
3602 3611
3603 3612 # If there's an old file, make the link
3604 3613
3605 3614 if [[ -f $F-.html ]]; then
3606 3615 oldfile_url="$(print $P-.html | url_encode)"
3607 3616 print "<a href=\"$oldfile_url\">Old</a>"
3608 3617 else
3609 3618 print " ---"
3610 3619 fi
3611 3620
3612 3621 # If there's an new file, make the link
3613 3622
3614 3623 if [[ -f $F.html ]]; then
3615 3624 newfile_url="$(print $P.html | url_encode)"
3616 3625 print "<a href=\"$newfile_url\">New</a>"
3617 3626 else
3618 3627 print " ---"
3619 3628 fi
3620 3629
3621 3630 if [[ -f $F.patch ]]; then
3622 3631 patch_url="$(print $P.patch | url_encode)"
3623 3632 print "<a href=\"$patch_url\">Patch</a>"
3624 3633 else
3625 3634 print " -----"
3626 3635 fi
3627 3636
3628 3637 if [[ -f $WDIR/raw_files/new/$P ]]; then
3629 3638 rawfiles_url="$(print raw_files/new/$P | url_encode)"
3630 3639 print "<a href=\"$rawfiles_url\">Raw</a>"
3631 3640 else
3632 3641 print " ---"
3633 3642 fi
3634 3643
3635 3644 print "<b>$P</b>"
3636 3645
3637 3646 # For renamed files, clearly state whether or not they are modified
3638 3647 if [[ -f "$oldname" ]]; then
3639 3648 if [[ -n "$mv_but_nodiff" ]]; then
3640 3649 print "<i>(copied from $oldname)</i>"
3641 3650 else
3642 3651 print "<i>(copied and modified from $oldname)</i>"
3643 3652 fi
3644 3653 elif [[ -n "$oldname" ]]; then
3645 3654 if [[ -n "$mv_but_nodiff" ]]; then
3646 3655 print "<i>(renamed from $oldname)</i>"
3647 3656 else
3648 3657 print "<i>(renamed and modified from $oldname)</i>"
3649 3658 fi
3650 3659 fi
3651 3660
3652 3661 # If there's an old file, but no new file, the file was deleted
3653 3662 if [[ -f $F-.html && ! -f $F.html ]]; then
3654 3663 print " <i>(deleted)</i>"
3655 3664 fi
3656 3665
3657 3666 #
3658 3667 # Check for usr/closed and deleted_files/usr/closed
3659 3668 #
3660 3669 if [ ! -z "$Oflag" ]; then
3661 3670 if [[ $P == usr/closed/* || \
3662 3671 $P == deleted_files/usr/closed/* ]]; then
3663 3672 print " <i>Closed source: omitted from" \
3664 3673 "this review</i>"
3665 3674 fi
3666 3675 fi
3667 3676
3668 3677 print "</p>"
3669 3678 # Insert delta comments
3670 3679
3671 3680 print "<blockquote><pre>"
3672 3681 getcomments html $P $PP
3673 3682 print "</pre>"
3674 3683
3675 3684 # Add additional comments comment
3676 3685
3677 3686 print "<!-- Add comments to explain changes in $P here -->"
3678 3687
3679 3688 # Add count of changes.
3680 3689
3681 3690 if [[ -f $F.count ]]; then
3682 3691 cat $F.count
3683 3692 rm $F.count
3684 3693 fi
3685 3694
3686 3695 if [[ $SCM_MODE == "teamware" ||
3687 3696 $SCM_MODE == "mercurial" ||
3688 3697 $SCM_MODE == "unknown" ]]; then
3689 3698
3690 3699 # Include warnings for important file mode situations:
3691 3700 # 1) New executable files
3692 3701 # 2) Permission changes of any kind
3693 3702 # 3) Existing executable files
3694 3703
3695 3704 old_mode=
3696 3705 if [[ -f $WDIR/raw_files/old/$PP ]]; then
3697 3706 old_mode=`get_file_mode $WDIR/raw_files/old/$PP`
3698 3707 fi
3699 3708
3700 3709 new_mode=
3701 3710 if [[ -f $WDIR/raw_files/new/$P ]]; then
3702 3711 new_mode=`get_file_mode $WDIR/raw_files/new/$P`
3703 3712 fi
3704 3713
3705 3714 if [[ -z "$old_mode" && "$new_mode" = *[1357]* ]]; then
3706 3715 print "<span class=\"chmod\">"
3707 3716 print "<p>new executable file: mode $new_mode</p>"
3708 3717 print "</span>"
3709 3718 elif [[ -n "$old_mode" && -n "$new_mode" &&
3710 3719 "$old_mode" != "$new_mode" ]]; then
3711 3720 print "<span class=\"chmod\">"
3712 3721 print "<p>mode change: $old_mode to $new_mode</p>"
3713 3722 print "</span>"
3714 3723 elif [[ "$new_mode" = *[1357]* ]]; then
3715 3724 print "<span class=\"chmod\">"
3716 3725 print "<p>executable file: mode $new_mode</p>"
3717 3726 print "</span>"
3718 3727 fi
3719 3728 fi
3720 3729
3721 3730 print "</blockquote>"
3722 3731 done
3723 3732
3724 3733 print
3725 3734 print
3726 3735 print "<hr></hr>"
3727 3736 print "<p style=\"font-size: small\">"
3728 3737 print "This code review page was prepared using <b>$0</b>."
3729 3738 print "Webrev is maintained by the <a href=\"http://www.illumos.org\">"
3730 3739 print "illumos</a> project. The latest version may be obtained"
3731 3740 print "<a href=\"http://src.illumos.org/source/xref/illumos-gate/usr/src/tools/scripts/webrev.sh\">here</a>.</p>"
3732 3741 print "</body>"
3733 3742 print "</html>"
3734 3743
3735 3744 exec 1<&- # Close FD 1.
3736 3745 exec 1<&3 # dup FD 3 to restore stdout.
3737 3746 exec 3<&- # close FD 3.
3738 3747
3739 3748 print "Done."
3740 3749
3741 3750 #
3742 3751 # If remote deletion was specified and fails do not continue.
3743 3752 #
3744 3753 if [[ -n $Dflag ]]; then
3745 3754 delete_webrev 1 1
3746 3755 (( $? == 0 )) || exit $?
3747 3756 fi
3748 3757
3749 3758 if [[ -n $Uflag ]]; then
3750 3759 upload_webrev
3751 3760 exit $?
3752 3761 fi
↓ open down ↓ |
250 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX