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