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